A 2


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.



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.


    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.


    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 {
    match getopt(users, addr) with
    | some v ->
    match getopt(transferlists, v) with
    | some(r) -> r.unrestricted
    | none -> false
    | none -> false
    entry assertReceivers (addrs : list<address>) {
    for addr in addrs do
    if (addr <> issuer)
    then dorequire(assertReceiver(addr), "USER_RESTRICTED")
    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")
    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")
    | none -> dofailif(contains(transferlists, transferlistId), "EXISTS_TRANSFERLIST")
    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
    for a in lallowTransferlists do
    transferlists.put(transferlistId, ltransferlist)
    | none -> transferlists.remove(transferlistId)
    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)