diff --git a/bank-tutorial/contract/Bank.jsligo b/bank-tutorial/contract/Bank.jsligo
index a543a89..a07f69a 100644
--- a/bank-tutorial/contract/Bank.jsligo
+++ b/bank-tutorial/contract/Bank.jsligo
@@ -1,63 +1,163 @@
+import Tezos = Tezos.Next;
+
namespace Bank {
// Storage stores a map of addresses and how much tez they have deposited
- type map_type = big_map
;
+ export type map_type = big_map;
type return_type = [list, map_type];
- // Deposit endpoint: send a certain amount of tez to be deposited
- @entry
- const deposit = (_: unit, store : map_type): return_type => {
+ // Deposit entrypoint: send a certain amount of tez to be deposited
+ // @entry
+ const deposit = (_: unit, storage: map_type): return_type => {
// Verify that the sender sent tez
if (Tezos.get_amount() <= (0 as tez)) {
return failwith("Send some tez to this entrypoint.");
}
// Get the sender's current balance, if any
- const balance_option = Big_map.find_opt(Tezos.get_sender(), store);
+ const balance_option: option = Big_map.find_opt(Tezos.get_sender(), storage);
// Get the new balance
const new_balance =
- match(balance_option) {
+ $match(balance_option, {
// Sender has a previous balance
- when(Some(current_balance)): current_balance + Tezos.get_amount();
+ "Some": (current_balance: tez) => current_balance + Tezos.get_amount(),
// Sender has a 0 balance
- when(None()): Tezos.get_amount();
- }
- const updated_map = Big_map.update(Tezos.get_sender(), Some(new_balance), store);
+ "None": () => Tezos.get_amount(),
+ });
+ const updated_map = Big_map.update(Tezos.get_sender(), ["Some" as "Some", new_balance], storage);
- return [list([]), updated_map];
+ return [[], updated_map];
}
// Withdraw the tez that the account has deposited
- @entry
- const withdraw = (_: unit, store : map_type): return_type => {
+ // @entry
+ const withdraw = (_: unit, storage: map_type): return_type => {
// Verify that the sender did not send tez
if (Tezos.get_amount() > (0 as tez)) {
return failwith("Do not send tez to this entrypoint");
}
// Get the sender's current balance, if any
- const balance_option = Big_map.find_opt(Tezos.get_sender(), store);
- return match(balance_option) {
- when(Some(current_balance)): do {
- // Sender has a previous balance
+ const balance_option: option = Big_map.find_opt(Tezos.get_sender(), storage);
+ return $match(balance_option, {
+ // Sender has a previous balance
+ "Some": (current_balance: tez) => (() => {
// Update the map
- const updated_map = Big_map.remove(Tezos.get_sender(), store);
+ const updated_map = Big_map.remove(Tezos.get_sender(), storage);
// Send tez to the account
- const payment: operation = Tezos.transaction(unit, current_balance, Tezos.get_contract_with_error(Tezos.get_sender(), "Account not found"));
- return [list([payment]), updated_map];
- };
- // Sender has a 0 balance
- when(None()): failwith("You do not currently have a balance.");
- };
+ const payment: operation = Tezos.Operation.transaction(unit, current_balance, Tezos.get_contract_with_error(Tezos.get_sender(), "Account not found"));
+ return [[payment], updated_map];
+ })(),
+ "None": () => failwith("You do not currently have a balance."),
+ });
}
// Get the user's balance
- @view
- const balance = (account: address, store: map_type): tez => {
+ // @view
+ const balance = (account: address, storage: map_type): tez => {
// Get the sender's current balance, if any
- const balance_option = Big_map.find_opt(account, store);
- return match(balance_option) {
- when(Some(current_balance)): current_balance;
- when(None()): 0 as tez;
- };
+ const balance_option: option = Big_map.find_opt(account, storage);
+ return $match(balance_option, {
+ "Some": (current_balance) => current_balance,
+ "None": () => 0 as tez,
+ });
}
-}
\ No newline at end of file
+}
+
+import Test = Test.Next;
+
+const test = (() => {
+
+ const starting_storage: Bank.map_type = Big_map.empty;
+ const contract = Test.Originate.contract(contract_of(Bank), starting_storage, 0 as tez);
+ const user_account: address = Test.Account.address(0 as nat);
+ Test.State.set_source(user_account);
+ const other_user: address = Test.Account.address(1 as nat);
+
+ // Test deposit entrypoint
+
+ // Test failure when no tez are sent
+ const deposit1 = Test.Contract.transfer(
+ Test.Typed_address.get_entrypoint("deposit", contract.taddr),
+ unit,
+ 0 as tez
+ );
+ $match(deposit1, {
+ "Fail": _err => Test.IO.log("Deposit entrypoint correctly rejected a call without any tez"),
+ "Success": _s => failwith("Deposit entrypoint failed to reject a call without any tez"),
+ });
+
+ // Deposit
+ Test.Contract.transfer_exn(
+ Test.Typed_address.get_entrypoint("deposit", contract.taddr),
+ unit,
+ 1 as tez
+ );
+ // Check balance by getting the storage
+ const storageAfterDeposit: Bank.map_type = Test.Typed_address.get_storage(contract.taddr);
+ const entry_opt: option = Big_map.find_opt(user_account, storageAfterDeposit);
+ $match(entry_opt, {
+ "Some": (balance: tez) => Assert.assert(Test.Compare.eq(balance, 1 as tez)),
+ "None": () => failwith("Deposit did not update balance"),
+ });
+
+ // Test balance view
+ const contractAddress = Test.Typed_address.to_address(contract.taddr);
+ const viewResultOption1: option = Tezos.View.call("balance", user_account, contractAddress);
+ const viewResult1 = $match(viewResultOption1, {
+ "Some": (balance) => balance,
+ "None": () => 0 as tez,
+ });
+ Assert.assert(Test.Compare.eq(viewResult1, 1 as tez));
+ const viewResultOption2: option = Tezos.View.call("balance", other_user, contractAddress);
+ const viewResult2 = $match(viewResultOption2, {
+ "Some": (balance) => balance,
+ "None": () => 0 as tez,
+ });
+ Assert.assert(Test.Compare.eq(viewResult2, 0 as tez));
+
+ // Withdraw entrypoint
+
+ // Test that another user can't withdraw without depositing
+ Test.State.set_source(other_user);
+ const withdraw1 = Test.Contract.transfer(
+ Test.Typed_address.get_entrypoint("withdraw", contract.taddr),
+ unit,
+ 0 as tez
+ );
+ $match(withdraw1, {
+ "Fail": _err => Test.IO.log("Withdraw entrypoint successfully blocked a withdrawal from an account with no balance when there are other deposits"),
+ "Success": _s => failwith("Deposit entrypoint failed to block a withdrawal from an account with no balance"),
+ });
+ Test.State.set_source(user_account);
+
+ // Test withdraw
+ const user_balance_before = Test.Address.get_balance(user_account);
+ Test.Contract.transfer_exn(
+ Test.Typed_address.get_entrypoint("withdraw", contract.taddr),
+ unit,
+ 0 as tez
+ );
+ const user_balance_after = Test.Address.get_balance(user_account);
+ Assert.assert(Test.Compare.lt(user_balance_before, user_balance_after));
+
+ // Test withdraw failures
+ const withdraw2 = Test.Contract.transfer(
+ Test.Typed_address.get_entrypoint("withdraw", contract.taddr),
+ unit,
+ 0 as tez
+ );
+ $match(withdraw2, {
+ "Fail": _err => Test.IO.log("Withdraw entrypoint successfully blocked a withdrawal from an account with no balance"),
+ "Success": _s => failwith("Deposit entrypoint failed to block a withdrawal from an account with no balance"),
+ });
+ const withdraw3 = Test.Contract.transfer(
+ Test.Typed_address.get_entrypoint("withdraw", contract.taddr),
+ unit,
+ 1 as tez
+ );
+ $match(withdraw3, {
+ "Fail": _err => Test.IO.log("Withdraw entrypoint successfully blocked a withdrawal request that included tez"),
+ "Success": _s => failwith("Deposit entrypoint failed to block a withdrawal request that included tez"),
+ });
+
+})();