A 2

Introduction#

This contract implements the Application 2 (A 2) specification TZIP 15 for whitelisting mechanism.

It defines transfer authorisation from users to other users. A user belongs to a list which is associated to other lists. Basically a user A can transfer to a user B if A's list is associated to the list B belongs to.

API#

Storage#

NameTypeDescription
issueraddressIssuer is a special user that can transfer (inbound and outbound) to/from anyone.
adminaddressAdmin can set the address of the issuer, and update the transfer lists data.
usersbig_map<adress, nat>A user, identified by the address, is associated to a list id.
transferlistsbig_map<nat, bool*set<nat>>A transfer list, identified by a nat id, is associated to:
  • a boolean unrestricted state
  • a set transferlists of ids
  • If unrestricted is false, then any transfer from the list or to the list is not authorised. If unrestricted is true, then transfers are allowed only to lists and from lists in the transferlists set.

    Entrypoints#

    NameParameterDescription
    assertReceiverslist<addr>Fails if one user in the parameter list belongs to a restricted or inexistant list. It also fails if a user is not listed, or if its list is not listed.
    assertTransferslist<address * list<address>>Parameter is a list of from addresses associated to a list of to addresses.

    Fails if
  • a from user is restricted
  • or one of its associated user is restricted (a user is restricted if its associated list is restricted)
  • or if one of its associated user is not in the from address transferlists
  • assertTransferListnat option<bool* set<nat>>When option parameter is none, fails if list exists.

    When option parameter is some data, fails if:
  • list does not exists
  • unrestricted state is equal to bool parameter
  • set of list ides is a subset of list's transferlists
  • updateUseraddress option<nat>Called by admin to associate a user address to a list, or remove the user from users.
    updateTransferlistnat option<(bool * list<nat> * set<nat>Called by admin to remove a list from transferlists if option parameter is none.

    It adds or updates a transfer list's data if optional parameter is some:
  • writes unrestricted state with boolean parameter
  • removes list ids from transferlists
  • adds list ids in set to transferlists
  • setAdminaddressCalled by admin to set admin's address.
    setIssueraddressCalled by admin to set issuer's address.
    getAdminGetter for admin's address.
    getIsuerGetter for issuer's address.

    Code#

    archetype a2(
    admin : address,
    issuer : address
    )
    variable users : big_map<address, nat> = []
    record transferlist {
    unrestricted : bool;
    allowedTransferlists : set<nat>;
    }
    variable transferlists : big_map<nat, transferlist> = []
    function assertReceiver(addr : address) : bool {
    return
    match getopt(users, addr) with
    | some v ->
    match getopt(transferlists, v) with
    | some(r) -> r.unrestricted
    | none -> false
    end
    | none -> false
    end
    }
    entry assertReceivers (addrs : list<address>) {
    for addr in addrs do
    if (addr <> issuer)
    then dorequire(assertReceiver(addr), "USER_RESTRICTED")
    done
    }
    entry assertTransfers (input_list : list<address * list<address>>) {
    for input_item in input_list do
    var %from = input_item[0];
    var tos = input_item[1];
    for %to in tos do
    if %from = issuer
    then dorequire(assertReceiver(%to), "TO_RESTRICTED")
    else begin
    dorequire(assertReceiver(%from), "FROM_RESTRICTED");
    dorequire(assertReceiver(%to), "TO_RESTRICTED");
    var fromid = users[%from];
    var toid = users[%to];
    var allowedlists = transferlists[fromid].allowedTransferlists;
    dorequire(contains(allowedlists, toid), "TO_NOT_ALLOWED")
    end
    done
    done
    }
    entry assertTransferlist (transferlistId : nat, input : option<transferlist>) {
    match input with
    | some tl -> begin
    dorequire(contains(transferlists, transferlistId), "TRANSFERLIST_NOT_FOUND");
    var l = transferlists[transferlistId];
    dorequire(l.unrestricted = tl.unrestricted, "INVALID_UNRESTRICTED_STATE");
    for i in tl.allowedTransferlists do
    dorequire(contains(l.allowedTransferlists, i), "IS_NOT_SUBSET")
    done
    end
    | none -> dofailif(contains(transferlists, transferlistId), "EXISTS_TRANSFERLIST")
    end
    }
    entry updateUser (user : address, transferlistId : option<nat>) {
    called by admin
    require {
    r0 otherwise "ISSUER_NOT_USER" : issuer <> user;
    }
    effect {
    users.update(user, transferlistId)
    }
    }
    entry updateTransferlist (
    transferlistId : nat,
    u : option<(bool * list<nat> * set<nat>)>) {
    called by admin
    effect {
    match u with
    | some v -> begin
    var ltransferlist = transferlists[transferlistId];
    var lunrestricted = v[0];
    var ldisallowTransferlists = v[1];
    var lallowTransferlists = v[2];
    ltransferlist.unrestricted := lunrestricted;
    for r in ldisallowTransferlists do
    ltransferlist.allowedTransferlists.remove(r)
    done;
    for a in lallowTransferlists do
    ltransferlist.allowedTransferlists.add(la)
    done;
    transferlists.put(transferlistId, ltransferlist)
    end
    | none -> transferlists.remove(transferlistId)
    end
    }
    }
    entry setAdmin (value : address) {
    called by admin
    effect {
    admin := value;
    }
    }
    entry setIssuer (value : address) {
    called by admin
    effect {
    issuer := value;
    }
    }
    getter getAdmin () : address {
    return admin
    }
    getter getIssuer () : address {
    return issuer
    }
    getter getUser (user : address) : option<nat> {
    return getopt(users, user)
    }