File Listing

Including all files mentioned in this post, the layout should look like this

test.sh

#! /bin/bash

set -e

test_file="$1"

this_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
techel_lib="$this_script_dir/tests/techel.liq"

# List of all the contracts.
contracts=""
for file in `find "$this_script_dir/contracts" -iname "*.liq"` ; do
    contracts="$contracts $file"
done

# File liquidity will compile to.
target="$test_file.techel"

echo "Compiling $test_file..."
echo
liquidity --no-annot --no-simplify --no-peephole $techel_lib $contracts -o $target $test_file
echo

# Running techelson on the target.
echo "Running test $target"
echo
techelson $target

tests/

tests/basic.liq

type storage = unit

let nothing : operation list * unit = [], ()

let%entry test (_param : unit) (_storage : unit) =
    let delegate : key_hash option = None in
    let operation, address =
        Account.create
            ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
            ~delegate
            ~delegatable:true
            ~amount:13tz
    in
    (* Apply the operation so that we can interact with the account. *)
    Techel.apply_operations [operation];

    (* Contract is now live. *)
    let balance = Techel.get_balance address in
    if balance <> 13tz then (
        failwith "balance should be 13tz"
    );

    let account_contract =
        match UnitContract.at address with
        | None -> failwith "could not retrieve account"
        | Some c -> c
    in

    let operation = Contract.call ~dest:account_contract ~amount:29tz ~parameter:() in
    Techel.apply_operations [operation];

    let balance = Techel.get_balance address in
    if balance <> 42tz then (
        failwith "balance should be 42tz"
    );

    nothing

tests/basic_err.liq

type storage = unit

let nothing : operation list * unit = [], ()

let%entry test (_param : unit) (_storage : unit) =
    let delegate : key_hash option = None in
    let operation, address =
        Account.create
            ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
            ~delegate
            ~delegatable:true
            ~amount:13tz
    in
    (* Apply the operation so that we can interact with the account. *)
    Techel.apply_operations [operation];

    (* Contract is now live. *)
    let balance = Techel.get_balance address in
    if balance <> 13tz then (
        failwith "balance should be 13tz"
    );

    let account_contract =
        match UnitContract.at address with
        | None -> failwith "could not retrieve account"
        | Some c -> c
    in

    let operation = Contract.call ~dest:account_contract ~amount:29tz ~parameter:() in
    Techel.apply_operations [operation];

    let balance = Techel.get_balance address in
    if balance <> 12tz then (
        failwith "balance should be 12tz"
    );

    nothing

tests/empty.liq

type storage = unit

let nothing : operation list * unit = [], ()

let%entry test (_param : unit) (_storage : unit) =
    nothing

tests/techel.liq

external get_balance :
    [%stack: address] -> [%stack: tez]
    = "GET_BALANCE"
external get_storage :
    [%type: 'a] -> [%stack: address] -> [%stack: 'a option]
    = "GET_STORAGE"
external apply_operations :
    [%stack: operation list] -> unit
    = "APPLY_OPERATIONS"
external start_set_source :
    [%stack: address] -> unit
    = "SET_SOURCE { #"
external end_set_source :
    unit -> unit
    = "}"
external must_fail :
    [%stack: string option] -> [%stack: operation] -> [%stack: operation]
    = "MUST_FAIL string"
external print_stack :
    unit -> unit
    = "PRINT_STACK"
external step :
    unit -> unit
    = "STEP"

tests/test1.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client *)
    let bad_add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    let must_fail = Techel.must_fail None bad_add_client in
    Techel.apply_operations [ must_fail ];

    nothing

tests/test1_better.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client *)
    let bad_add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    let error_message = Some "illegal access to admin account" in
    let must_fail = Techel.must_fail error_message bad_add_client in
    Techel.apply_operations [ must_fail ];

    nothing

tests/test1_err.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client *)
    let add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    Techel.apply_operations [ add_client ];

    nothing

tests/test2.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client and fail *)
    let bad_add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    let error_message = Some "illegal access to admin account" in
    let must_fail = Techel.must_fail error_message bad_add_client in
    (* let's really add a client now *)
    Techel.start_set_source root ;
        (* all operations created in here will appear to have been created by `root` *)
        let add_client =
            Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
                "root", "lucy", client, client_instance
            )
        in
    Techel.end_set_source () ;

    Techel.apply_operations [ must_fail ; add_client ];

    nothing

tests/test3.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client and fail *)
    let bad_add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    let error_message = Some "illegal access to admin account" in
    let must_fail = Techel.must_fail error_message bad_add_client in
    (* let's really add a client now *)
    Techel.start_set_source root ;
        (* all operations created in here will appear to have been created by `root` *)
        let add_client =
            Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
                "root", "lucy", client, client_instance
            )
        in
    Techel.end_set_source () ;

    Techel.apply_operations [ must_fail ; add_client ];

    (* lucy deposits `10tz` *)
    Techel.start_set_source client ;
        let deposit_money =
            Contract.call ~dest:main_instance ~amount:10tz ~entry:deposit ~parameter:"lucy"
        in
    Techel.end_set_source () ;
    Techel.apply_operations [ deposit_money ];
    let balance_lucy = Techel.get_balance client in
    if balance_lucy <> 5tz then (
        failwith "lucy should have 5tz now"
    );

    (* lucy walks out of the whole thing *)
    Techel.start_set_source client ;
        let drain =
            Contract.call ~dest:main_instance ~amount:0tz ~entry:drain ~parameter:"lucy"
        in
    Techel.end_set_source () ;
    Techel.apply_operations [ drain ] ;

    (* lucy should have her money back *)
    let balance_lucy = Techel.get_balance client in
    if balance_lucy <> 15tz then (
        failwith "lucy should have 15tz now"
    );

    nothing

tests/test3_err.liq

let nothing : operation list * unit = [], ()

(* Creates a storage for Multi with one administrator. *)
let one_admin (root : string) (address : address) : Multi.storage = {
    Multi.admins =
        Map.add root address (Map : (string, address) map) ;
    Multi.users =
        (Map : (string, (address * tez * UnitContract.instance)) map) ;
}

(* Deploys an account with an arbitrary manager. *)
let deploy_account_op (amount: tez) : operation * address =
    let delegate : key_hash option = None in
    Account.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~amount

(* Deploys an instance of multi with an arbitrary manager. *)
let deploy_contract_op (storage : Multi.storage) : operation * address =
    let delegate : key_hash option = None in
    Contract.create
        ~manager:tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc
        ~delegate
        ~delegatable:true
        ~spendable:false
        ~amount:0tz
        ~storage
        ~code:(contract Multi)

(* Storage of the test is irrelevant. *)
type storage = unit

(* Actual test. *)
let%entry test (_param : unit) (_storage : unit) =
    let root_op, root = deploy_account_op 0tz in
    let storage = one_admin "root" root in
    let main_op, main = deploy_contract_op storage in
    (* ask techelson to apply these operations. *)
    Techel.apply_operations [ root_op ; main_op ];
    (* root and main are live now *)

    (* let's check root is an admin, and that the address is correct *)
    let storage =
        match Techel.get_storage [%type: Multi.storage] main with
        | Some storage -> storage
        | None -> failwith "can't retrieve contract's storage"
    in
    (
        match Map.find "root" storage.Multi.admins with
        | None -> failwith "no root in storage"
        | Some address -> (
            if address <> root then (
                failwith "wrong address for root"
            )
        )
    );

    let client_op, client = deploy_account_op 15tz in
    (* deploy the client *)
    Techel.apply_operations [ client_op ];
    (* client is live now *)

    (* retrieve client instance for registration *)
    let client_instance =
        match (Contract.at client : UnitContract.instance option) with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in
    (* retrieve multi's instance to call it *)
    let main_instance =
        match Multi.at main with
        | None -> failwith "could not retrieve main contract"
        | Some instance -> instance
    in

    (* let's add a client and fail *)
    let bad_add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    let error_message = Some "illegal access to admin account" in
    let must_fail = Techel.must_fail error_message bad_add_client in
    (* let's really add a client now *)
    Techel.start_set_source root ;
    (* all operations created in here will appear to have been created by `root` *)
    let add_client =
        Contract.call ~dest:main_instance ~amount:0tz ~entry:add_client ~parameter:(
            "root", "lucy", client, client_instance
        )
    in
    Techel.end_set_source () ;

    Techel.apply_operations [ must_fail ; add_client ];

    (* lucy deposits `10tz` *)
    Techel.start_set_source client ;
        let deposit_money =
            Contract.call ~dest:main_instance ~amount:10tz ~entry:deposit ~parameter:"lucy"
        in
    Techel.end_set_source () ;
    Techel.apply_operations [ deposit_money ];
    let balance_lucy = Techel.get_balance client in
    if balance_lucy <> 5tz then (
        failwith "lucy should have 5tz now"
    );

    (* lucy walks out of the whole thing *)
    Techel.start_set_source client ;
        let drain =
            Contract.call ~dest:main_instance ~amount:0tz ~entry:drain ~parameter:"lucy"
        in
    Techel.end_set_source () ;
    Techel.apply_operations [ drain ] ;

    (* lucy should have her money back *)
    let balance_lucy = Techel.get_balance client in
    if balance_lucy <> 2tz then (
        failwith "lucy should have 2tz now"
    );

    nothing

contracts/

contracts/multi.liq

type storage = {
    admins : (string, address) map ;
    users : (string, (address * tez * UnitContract.instance)) map ;
}

let admin_check (storage : storage) (name : string) (a : address) : unit =
    match Map.find name storage.admins with
    | None -> failwith "only admins can perform administrative tasks"
    | Some address ->
        if address <> a then
            failwith "illegal access to admin account"

let%entry add_admin
    (
        (admin_name, nu_admin_name, nu_admin_address) :
         string *    string *       address
    ) (
        storage : storage
    )
  : operation list * storage 
=
    admin_check storage admin_name (Current.sender ()); 
    let storage =
        storage.admins <- Map.update nu_admin_name (Some nu_admin_address) storage.admins
    in
    [], storage
  
let%entry rm_admin (admin_name, user_name : string * string) (storage : storage) =
    admin_check storage admin_name (Current.sender ());
    let storage = storage.admins <- Map.update user_name None storage.admins in
    [], storage
    
let%entry add_client (
    (admin_name, user_name, user,     c) :
     string *    string *   address * UnitContract.instance
) (storage : storage) =
    admin_check storage admin_name (Current.sender ());
    if Map.mem user_name storage.users then (
        failwith "username already taken"
    );
    let data = Some (user, 0tz, c) in
    let storage = storage.users <- Map.update user_name data storage.users in
    [], storage
  
let data_of (storage : storage) (name : string) (user : address) : tez * UnitContract.instance =
    match Map.find name storage.users with
    | None -> failwith "unknown user"
    | Some (address, tez, c) ->
        if user <> address then
            failwith "illegal access to account"
        else (tez, c)

let%entry deposit (name : string) (storage : storage) =
    let user = Current.sender () in
    let money, c = data_of storage name user in
    let amount = Current.amount () in
    let nu_data = Some (user, money + amount, c) in
    [], storage.users <- Map.update name nu_data storage.users 
          
          
let%entry withdraw (name, amount : string * tez) (storage : storage) =
    let user = Current.sender () in
    let money, c = data_of storage name user in
    if amount > money then
        failwith "insufficient balance"
    else (
        let nu_data = Some (user, money - amount, c) in
        [], storage.users <- Map.update name nu_data storage.users
    )
        
let%entry drain (name : string) (storage : storage) =
    let user = Current.sender () in
    let money, c = data_of storage name user in
    let storage = storage.users <- Map.update name None storage.users in
    let ops = [Contract.call ~dest:c ~amount:money ~parameter:()] in
    ops, storage