Regulated tokens

Let’s create tokens that can be traded only by certain whitelisted addresses and that cannot be sent to a non-whitelisted ones.

Model

The issuer (account) stores asset-whitelist pairs:

data: [
 {
   key: 'asset id',
   type: 'string',
   value: 'whitelist public key'
 },
 …
]

The whitelist (smart account) stores addresses with their statuses (true/false):

data: [
 {
   key: 'address',
   type: 'boolean',
   value: 'status'
 },
 …
]

The regulated token takes data from his issuer, then checks if transaction sender/recipient is in the whitelist.
The issuer can store multiple asset-whitelist pairs, and can also use one whitelist for several assets.

Code

The whitelist has following script (it just accepts data transactions signed by admin, multisignature account can be used):

let adminPublicKey = base58'adminPK'
sigVerify(tx.bodyBytes, tx.proofs[0], adminPublicKey)

The smart asset script is:

let adminPublicKey = base58'adminPK'
let adminAddress = addressFromPublicKey(adminPublicKey)
match tx {
  case allow: ExchangeTransaction | TransferTransaction =>
    let assetId = match allow {
      case e: ExchangeTransaction =>
        e.sellOrder.assetPair.amountAsset
      case t: TransferTransaction =>
        t.assetId
    }
    let assetIdString = toBase58String(extract(assetId))
    let whitelistPK = extract(getString(adminAddress, assetIdString))
    let whitelistAddress = extract(addressFromPublicKey(fromBase58String(whitelistPK)))
    let senderAddress = match allow {
      case e: ExchangeTransaction =>
        e.sellOrder.sender
      case t: TransferTransaction =>
        t.sender
    }
    let recipientAddress = match allow {
      case t: TransferTransaction =>
        addressFromRecipient(t.recipient)
      case _ => throw()
    }
    let senderAccepted = getBoolean(whitelistAddress, toBase58String(senderAddress.bytes))
    let recipientAccepted = getBoolean(whitelistAddress, toBase58String(recipientAddress.bytes))
    match allow {
      case e: ExchangeTransaction =>
        if (!isDefined(senderAccepted))
          then throw()
          else extract(senderAccepted)
      case t: TransferTransaction =>
        if (!isDefined(senderAccepted) || !isDefined(recipientAccepted))
          then throw()
          else extract(senderAccepted) && extract(recipientAccepted)
    }
  case _ => false
}

It includes the following steps:

  • obtaining the whitelist public key from the issuer’s data
  • transaction sender/recipient validation

Article
Github

1 Like

There is first (?) regulated token with whitelist on the mainnet

1 Like