Protocol Update 9 - Protocol-Level Tokens

Effective on Testnet

TBD

Effective on Mainnet

TBD

Specification hash

1524c737e9d81fe79e4fccfa0b74d11d1cd7d3217b069b4b92f0cb3490d79ba6

Specification

                           Protocol update

Protocol Version: 9
Builds on Protocol Version: 8

                               Abstract

This document describes the changes in Concordium protocol version 9 compared to
protocol version 8.

The protocol change supports Protocol-Level Tokens (PLTs), which provide
chain-native support for tokens other than CCD, without depending on smart
contracts for the implementation.

                              Background

Prior to protocol version 9, only CCD is supported as a native token. Non-native
tokens can be implemented using smart contracts, typically following the CIS-2
token standard [1]. Smart contracts allow for significant flexibility in the
implementation and functionality of tokens.

Protocol version 9 introduces a new mechanism for token implementation:
Protocol-Level Tokens (PLTs). PLTs are implemented natively, without the use of
smart contracts, which allows for more efficient (and hence cheaper) transactions.
The PLT implementation is intended to be updated in future protocol versions to
support additional functionality.

Each PLT is initially created on the chain by a chain-governance operation.
This operation assigns a unique Token ID for the PLT that is used to identify it.
It also specifies data and metadata associated with the PLT, such as: the token
name; the URL of additional metadata; the governance account for the PLT; whether
the PLT supports an allow list, a deny list, minting of tokens, and burning of
tokens; the initial supply of the token; and the number of decimal places used in
the token representation.

The governance account for a PLT is a Concordium account that is granted
capabilities to perform administrative operations associated with the PLT. These
consist of pausing or un-pausing token balance change operations (mint, burn, and
transfer operations), adding or removing accounts from the allow and deny lists
(if they are enabled), minting and burning tokens (if enabled, and the PLT is not
paused).

PLTs may be held by Concordium accounts. An account may send an amount of a PLT to
another account with a transfer operation. If the PLT supports an allow list, both
the sender and recipient account must be on the allow list. If the PLT supports a
deny list, neither the sender nor the recipient may be on the deny list. The
transfer will also fail if the PLT is currently globally paused, or if the sender
has insufficient balance to cover the transfer. Similarly to regular CCD transfers,
PLT transfers can include a memo of up to 255 bytes and may reference the recipient
account by an alias.

Each PLT specifies the number of decimal places (d) used in its representation, from
0 to 255. PLT amounts are represented using unsigned 64-bit values, which limits the
maximum representable amount to (2^64-1) * 10^(-d) (approximately 1.84 * 10^(19-d)).
The useful range of the number of decimal places is thus more limited than the
permitted range.

                               Changes

The following behaviours are changed in protocol version 9.

1. The block state is updated in the following ways:

    (a) A list of the protocol-level tokens is maintained. Each entry contains:

        - The static configuration (Token ID, Token Module hash, number of decimals).

        - The mutable state of the PLT. This is stored in a trie datastructure that
          provides a key-value map interface.

        - The current circulating supply of the PLT.

       The list is initially empty.

    (b) Each account maintains a list of balances of PLTs held on the account.
        The balance may be 0. An account does not necessarily have a balance for
        every existing PLT. The list is initially empty.

    (c) The "chain updates" data structure adds a `CreatePLT` access structure to
        the level-2 keys and a sequence number for updates authorized by these keys.
        The initial access structure is defined by the auxiliary data provided by
        the protocol update. The initial next sequence number is 1.

2. A new level 2 update instruction (payload `CreatePLT`) is added. It is
   authorized by a new access structure on the level 2 keys: the `CreatePLT` keys.
   The update instruction is identified by payload type 24. It consists of:

    - The Token ID of the PLT being created. This consists of up to 128 characters,
      limited to a-z, A-Z, 0-9, `-`, `.` and `%`. Token IDs are matched in a
      case-insensitive manner.

    - The Token Module hash. This identifies the specific implementation used for
      a particular PLT. As of protocol version 9, only the following value is supported:
      `5c5c2645db84a7026d78f2501740f60a8ccb8fae5c166dc2428077fd9a699a4a`.

    - The number of decimals used in the representation of amounts of the new PLT.
      This must be at least 0 and at most 255.

    - CBOR-encoded parameters that are used by the Token Module to initialize the PLT.
      These include:

        - The token name.

        - The URL at which token metadata can be obtained, optionally including a SHA256
          hash of the metadata.

        - The address of the account to serve as the governance account for the PLT.

        - Optionally, whether an allow list is supported. (Defaults to not supported.)

        - Optionally, whether a deny list is supported. (Defaults to not supported.)

        - Optionally, an initial supply of tokens to be minted to the governance account.

        - Optionally, whether tokens can be minted. (Defaults to no.)

        - Optionally, whether tokens can be burnt. (Defaults to no.)

   `CreatePLT` updates are executed immediately when they occur in blocks. This differs
   from existing chain updates, which are added to update queues and executed in the
   first future block after a specified time. A `CreatePLT` update creates the new PLT
   with the specified configuration. If specified, an initial amount is minted to the
   governance account. Invalid `CreatePLT` updates cannot occur in valid blocks. A
   `CreatePLT` update may be invalid if the Token ID already exists or the configuration
   is invalid. A successful `CreatePLT` update results in a `TokenCreated` event,
   followed by a `TokenMint` event if an initial supply is specified.

3. A new transaction payload type (`TokenUpdate`) is added. It is identified by payload
   type 27. The payload consists of the Token ID of the PLT the update pertains to and
   a CBOR-encoded list of operations to perform. If the PLT does not exist, the transaction
   is rejected with a `NonExistentTokenId` reject reason.

   The Token Module is responsible for decoding and executing the list of operations.
   If the Token Module cannot decode and execute the operations, the transaction is
   rejected with a `TokenUpdateTransactionFailed`, which specifies the type of the
   failure and, potentially, CBOR-encoded details associated with the failure.
   If the list cannot be decoded successfully, the Token Module fails with a
   `deserializationFailure`. The following operations are supported:

    - `transfer`: transfer a specified amount from the sender account to the specified
      recipient account. An optional memo may be specified. To succeed, the following
      conditions must hold:

        (a) The PLT is not currently paused; otherwise fail with `operationNotPermitted`.

        (b) The recipient account exists; otherwise fail with `addressNotFound`.

        (c) If the PLT supports an allow list, the sender is on the allow list;
            otherwise fail with `operationNotPermitted`.

        (d) If the PLT supports an allow list, the recipient is on the allow list;
            otherwise fail with `operationNotPermitted`.

        (e) If the PLT supports a deny list, the sender is not on the deny list;
            otherwise fail with `operationNotPermitted`.

        (f) If the PLT supports a deny list, the recipient is not on the deny list;
            otherwise fail with `operationNotPermitted`.

        (g) The sender's balance is sufficient to cover the transfer; otherwise fail
            with `tokenBalanceInsufficient`.

       The incremental energy cost of a transfer operation is 100. If successful,
       the operation generates a `TokenTransfer` event specifying the Token ID,
       sender, receiver, amount, and (if specified) memo.

    - `mint`: mint a specified amount to the sender's account. This increases the
      total supply and the balance of the sender's account by the specified amount.
      To succeed, the following conditions must hold:

        (a) The account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT is not currently paused; otherwise fail with `operationNotPermitted`.

        (c) The PLT configuration supports minting; otherwise fail with
            `unsupportedOperation`.

        (d) The total supply of the token after minting does not exceed the maximum
            representable amount ((2^64 - 1) * 10^(-decimals)); otherwise fail with
            `mintWouldOverflow`.

       The incremental energy cost of a mint operation is 50. If successful, the
       operation generates a `TokenMint` event specifying the Token ID, sender account,
       and minted amount.

    - `burn`: burn a specified amount from the sender's account. This decreases
      the total supply and the balance of the sender's account by the specified
      amount. To succeed, the following conditions must hold:

        (a) The account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT is not currently paused; otherwise fail with `operationNotPermitted`.

        (c) The PLT configuration supports burning; otherwise fail with
            `unsupportedOperation`.

        (d) The balance of the sender account must be at least the specified amount;
            otherwise fail with `tokenBalanceInsufficient`.

      The incremental energy cost of a burn operation is 50. If successful, the
      operation generates a `TokenBurn` event specifying the Token ID, sender account,
      and burnt amount.

    - `addAllowList`: add a specified target account to the allow list. To succeed,
      the following conditions must hold:

        (a) The sender account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT configuration supports an allow list; otherwise fail with
            `unsupportedOperation`.

        (c) The target account exists; otherwise fail with `addressNotFound`.

      The incremental energy cost of a list-update operation is 50. If successful,
      the operation generates a `TokenModuleEvent` wrapping an `addAllowList` event
      that specifies the target account.

    - `removeAllowList`: remove a specified target account from the allow list. To
       succeed, the following conditions must hold:

        (a) The sender account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT configuration supports an allow list; otherwise fail with
            `unsupportedOperation`.

        (c) The target account exists; otherwise fail with `addressNotFound`.

      The incremental energy cost of a list-update operation is 50. If successful,
      the operation generates a `TokenModuleEvent` wrapping a `removeAllowList`
      event that specifies the target account.

    - `addDenyList`: add a specified target account to the deny list. To succeed,
      the following conditions must hold:

        (a) The sender account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT configuration supports a deny list; otherwise fail with
            `unsupportedOperation`.

        (c) The target account exists; otherwise fail with `addressNotFound`.

      The incremental energy cost of a list-update operation is 50. If successful,
      the operation generates a `TokenModuleEvent` wrapping an `addDenyList` event
      that specifies the target account.

    - `removeDenyList`: remove a specified target account from the deny list. To
       succeed, the following conditions must hold:

        (a) The sender account is the PLT's governance account; otherwise fail with
            `operationNotPermitted`.

        (b) The PLT configuration supports a deny list; otherwise fail with
            `unsupportedOperation`.

        (c) The target account exists; otherwise fail with `addressNotFound`.

      The incremental energy cost of a list-update operation is 50. If successful,
      the operation generates a `TokenModuleEvent` wrapping a `removeDenyList`
      event that specifies the target account.

    - `pause`: suspend all balance-changing operations (transfer, mint, and burn)
      for the PLT. To succeed, the following condition must hold:

        (a) The sender account is the PLT's governance account; otherwise fail
            with `operationNotPermitted`.

      The incremental cost of a pause operation is 50. If successful, the operation
      generates a `TokenModuleEvent` wrapping a `pause` event.

    - `unpause`: resume all balance-changing operations (transfer, mint, and burn)
      for the PLT. To succeed, the following condition must hold:

        (a) The sender account is the PLT's governance account; otherwise fail
            with `operationNotPermitted`.

      The incremental cost of an unpause operation is 50. If successful, the
      operation generates a `TokenModuleEvent` wrapping an `unpause` event.

   The energy cost for a `TokenUpdate` transaction consists of:

    - The general transaction base cost:
        100 * `number of signatures` + `total size of transaction payload and header`

    - The token update base cost: 300

    - The total incremental cost of executed operations, excluding any operations
      after the first failed operation.

   In the event that one operation in a `TokenUpdate` transaction fails, none of
   the operations has any effect. Nonetheless, energy is charged for the
   operations up to and including the first failed operation.

                     Protocol update instruction

The protocol update instruction is identified by the SHA256 hash of this
file. The instruction needs the following auxiliary data:

- The access structure defining the level 2 keys that are authorized to perform
  `CreatePLT` chain updates.

                             References

  [1] CIS-2: Concordium Token Standard 2
      <https://proposals.concordium.com/CIS/cis-2.html>

Commentary

                      Protocol update commentary

Protocol Version: 9
Builds on Protocol Version: 8

Protocol version 9 introduces Protocol-Level Tokens (PLTs), which enable
chain-native support for tokens other than CCD, without reliance on smart
contracts. Since they are implemented natively, PLTs can be cheaper and
more efficient than smart-contract-based tokens, which incur runtime
overheads.

Protocol-Level Tokens are intended to provide a high-assurance platform
for implementing tokens, such as stable coins. Since PLTs are built in
to the Concordium blockchain, users are not required to trust third-party
smart contract implementations.

While there are advantages to protocol-level tokens, they also come with
certain limitations. Firstly, PLTs can only be created by a
chain-governance operation. While any user can create a CIS-2 (smart
contract-based) token, PLTs can only be created if sanctioned by the
Concordium governance process.

Secondly, the functionality of PLTs is currently limited. Tokens can
only be held on accounts, and not by smart contracts. There are no
mechanisms to assign or delegate control of PLTs held on an account
(other than by transferring them), and there is no mechanism for
supporting sponsored transactions. However, future protocol updates
may introduce additional functionality, including the possibility of
upgrading PLT functionality without the need for a further protocol
update.

Since PLTs are designed with future upgradability in mind, the
interface to PLTs is built for extensibility. Core to this is the
concept of a Token Module: the specific implementation behind any
given PLT. At protocol version 9, only one Token Module implementation
is supported, but in future there may be more, either adding
general functionality to all PLTs, or providing specialized
functionality tailored to individual PLTs. To allow for extensibility,
the Token Module interacts with the outside world through CBOR-encoded
data [1]. Transactions, events, reject reasons, and state query responses
all use CBOR-encoding. This allows for future token modules to support
additional transaction types, events, etc., without requiring changes
to the Concordium node's interface. The CIS-7 standard [2] for PLTs
defines the interface.


                        References

[1] RFC 8949: Concise Binary Object Representation (CBOR),
    C. Bormann and P. Hoffman
    <https://www.rfc-editor.org/rfc/rfc8949.html>

[2] CIS-7: Protocol-level Tokens (PLTs)
    <https://proposals.concordium.com/CIS/cis-7.html>