Account-based token system

Tokens are the bread & butter of blockchains, thus it is useful to demonstrate how a token system can be implemented in Rell. There are roughly two different implementation strategies:

  • Account-based tokens which maintain an updateable balance for each account (which can be associated with a key or an address)
  • UTXO-based ones (Bitcoin-style) deal with virtual “coins” which are minted and destroyed in transactions

This section details the account-based implementation. For an example of a UTXO based system see UTXO-based token system.

A minimal implementation can look like this:

class balance {
      key pubkey;
      mutable amount: integer;
}

operation transfer(from_pubkey: pubkey, to_pubkey: pubkey, xfer_amount: integer) {
          require( is_signer(from_pubkey) );
          require( xfer_amount > 0 );
          require( balance@{from_pubkey}.amount >= xfer_amount );
          update balance@{from_pubkey} (amount -= xfer_amount);
          update balance@{to_pubkey} (amount += xfer_amount);
}

There are a few items which should be highlighted in this code. First, let’s note that balance@{from_pubkey}.amount is simply a shorthand notation for balance@{from_pubkey} (amount).

update relational operator combines a relational expression specifying objects to update with a form which specifies how to update their attributes. Attributes are updateable only if they are market as mutable.

Note

We don’t need to worry about concurrency issues (i.e. that the balance can change after we checked it) because Rell applies operations within a single blockchain sequentially.

But this minimal implementation is not very useful, as there’s no mechanism for a wallet to identify payments it receives (without somehow scanning the blockchain, or asking the payer to share the transaction with the recipient). Other blockchains systems might resort to third-party tools and complex protocols to handle this (for example, the Electrum Bitcoin wallet connects to Electrum Servers which perform blockchain indexing). Rell-based blockchains can just use built-in indexing to keep track of payment history. For example, by using the additional payment class. To make things more efficient, we also wrap pubkey into user class, thus getting:

class user { key pubkey; }

class balance {
    key user;
    mutable amount: integer;
}

class payment {
      index from_user: user;
      index to_user: user;
      amount: integer;
      timestamp;
}

operation transfer(from_pubkey: pubkey, to_pubkey: pubkey, xfer_amount: integer) {
          require( is_signer(from_pubkey) );
          require( xfer_amount > 0 );
          val from_user = user@{from_pubkey};
          val to_user = user@{to_pubkey};
          require( balance@{from_user}.amount >= xfer_amount );
          update balance@{from_user} (amount -= xfer_amount);
          update balance@{to_user} (amount += xfer_amount);
          create payment (
                 from_user,
                 to_user,
                 amount=xfer_amount,
                 timestamp=op_context.last_block_time);
}

Note

In create payment (from_user, to_user, ...) Rell can figure out matching attributes from names of local variables as they match exactly. It is often the case that you can use the same name for the same concept.)

Note

In a future version of Rell it will be possible to timestamp objects automatically using the log annotation, with the added benefit that they are then linked to the corresponding transaction and block.

The example above can be easily extended to support multiple types of tokens. For example:

class asset { key asset_code; }

class balance {
      key user, asset;
      mutable amount: integer;
}

Here we use a composite key to keep track of the balance for each (user, asset) pair.