Skip to main content

Create Token Types

This first tutorial shows you how to create new fungible and non-fungible token types, and how the type inheritance works. The examples show different kinds of predicates that are on user tokens, and how to make inputs for them.

info

This tutorial addresses the local devnet flow. To sync transactions with the public testnet, add --rpc-url flag with the following URL to wallet subcommands:

--rpc-url https://tokens-partition.testnet.alphabill.org
Important

As of version 0.4.0, the list token types command is not yet implemented on the new JSON-RPC based validator APIs. Therefore, it is the responsibility of the user to track their token types.

Prerequisites

Before you begin, make sure you have completed the following steps:

Create Fungible Token Type

  1. The CLI wallet has a separate command for interaction with the User Token Partition. Open the help output for wallet token command to view available commands and options. Run the following command from the alphabill-wallet/build directory:

    ./abwallet wallet token -h
    create and manage fungible and non-fungible tokens

    Usage:
    abwallet wallet token [command]

    Available Commands:
    collect-dust join fungible tokens into one unit
    list lists all available tokens
    list-types lists token types
    lock locks a fungible or non-fungible token
    new mint new token
    new-type create new token type
    send send a token
    unlock unlocks a fungible or non-fungible token
    update update the data field on a non-fungible token

    Flags:
    -r, --rpc-url string rpc node url (default "localhost:28866")
    -h, --help help for token
    -w, --wait-for-confirmation string waits for transaction confirmation on the blockchain, otherwise just broadcasts the transaction (default "true")

    Global Flags:
    --config string config file URL (default is $AB_HOME/config.props)
    --home string set the AB_HOME for this invocation (default is /home/user/.alphabill)
    --log-file string log file path or one of the special values: stdout, stderr, discard
    --log-format string log format, one of: text, json, console, ecs
    --log-level string logging level, one of: DEBUG, INFO, WARN, ERROR
    --logger-config string logger config file URL. Considered absolute if starts with '/'. Otherwise relative from $AB_HOME. (default "logger-config.yaml")
    --metrics string metrics exporter, disabled when not set. One of: stdout, prometheus
    -p, --password password (interactive from prompt)
    --pn string password (non-interactive from args)
    --tracing string traces exporter, disabled when not set. One of: stdout, otlptracehttp, otlptracegrpc, zipkin
    -l, --wallet-location string wallet home directory (default $AB_HOME/wallet)

    Use "abwallet wallet token [command] --help" for more information about a command.

    As seen from the command help output, you can create new token types with the new-type subcommand.

  2. Start with the fungible token type, but first see what options are under the wallet token new-type fungible subcommand:

    ./abwallet wallet token new-type fungible -h
    create new fungible token type

    Usage:
    abwallet wallet token new-type fungible [flags]

    Flags:
    --decimals uint32 token decimal (default 8)
    -h, --help help for fungible
    --icon-file string icon file name for the token type (optional)
    --inherit-bearer-clause string predicate that will be inherited by subtypes into their bearer clauses, values <true|false|ptpkh> (default "true")
    -k, --key uint which key to use for sending the transaction (default 1)
    --mint-clause string predicate to control minting of this type, values <true|false|ptpkh> (default "ptpkh")
    --name string full name of the token type (optional)
    --parent-type hex unit identifier of a parent type in hexadecimal format
    --subtype-clause string predicate to control sub typing, values <true|false|ptpkh> (default "true")
    --subtype-input strings input to satisfy the parent type creation clause (mandatory with --parent-type)
    --symbol string symbol (short name) of the token type (mandatory)

    Global Flags:
    --config string config file URL (default is $AB_HOME/config.props)
    --home string set the AB_HOME for this invocation (default is /home/user/.alphabill)
    --log-file string log file path or one of the special values: stdout, stderr, discard
    --log-format string log format, one of: text, json, console, ecs
    --log-level string logging level, one of: DEBUG, INFO, WARN, ERROR
    --logger-config string logger config file URL. Considered absolute if starts with '/'. Otherwise relative from $AB_HOME. (default "logger-config.yaml")
    --metrics string metrics exporter, disabled when not set. One of: stdout, prometheus
    -p, --password password (interactive from prompt)
    --pn string password (non-interactive from args)
    -r, --rpc-url string rpc node url (default "localhost:28866")
    --tracing string traces exporter, disabled when not set. One of: stdout, otlptracehttp, otlptracegrpc, zipkin
    -w, --wait-for-confirmation string waits for transaction confirmation on the blockchain, otherwise just broadcasts the transaction (default "true")
    -l, --wallet-location string wallet home directory (default $AB_HOME/wallet)

    The output shows that this subcommand has only one mandatory option: --symbol, short name of the token type.

    Let's take a look at some of the token type default properties:

    • --decimals: defaults to 8 decimal places.
    • --inherit-bearer-clause: defaults to true. This means no additional bearer restrictions are placed on types that inherit from this type. Just because it's a default, doesn't mean it doesn't exist⁠—this fact will come into play later.
    • --mint-clause: defaults to ptpkh. This means that only this wallet can be used to mint or create new tokens of this type. The default key is key #1, but other keys can be indicated using the syntax: ptpkh:2, for example. Mint clauses are also inherited! This means that if a token type inherits from a parent in order to mint tokens of the child type, the transaction will need to satisfy both the mint clause of the parent, first, and next of the child. This is an important concept, as you will see later. Once again, just because it's a default, doesn't mean it doesn't exist.
    • --parent-type: defaults to 00, or no parent. If you want to inherit, you need to pass this flag and indicate the ID of the type you want to inherit from. This token has no parent.
    • --subtype-clause: this flag is only required if the token is inheriting from a parent type. If you do inherit, you need to provide an input to the parent types subtype clause.
    • --symbol: mandatory, token symbol.
  3. Run the following command to create a new type, only the mandatory --symbol option provided (in this example, MYFT):

    ./abwallet wallet token new-type fungible \
    --symbol MYFT
    Example response:

    Sent request for new fungible token type with id=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A
    Paid 0.000'000'01 fees for transaction(s).

    info

    The default --rpc-url flag value is used here. Each wallet subcommand requires a connection to partition's RPC node, which in case of the User Token Partition is started on localhost:28866.

  4. After the command succeeds, list known token types:

    ./abwallet wallet token list-types
    Example response:

    ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (fungible)

Parent Fungible Token Type

  1. Create a new type, but place some restrictions on how it can be subtyped:

    ./abwallet wallet token new-type fungible \
    --symbol PARENT \
    --subtype-clause ptpkh:2

    What is happening here is that the parent token types can only be subtyped if the transaction to create the subtype has a subtype input that can satisfy the ptpkh predicate/clause for this wallet's key #2.

    Effectively, this means that only this wallet can subtype this parent type. At first, it seems not too useful, however, you could imagine that there is an application that might allow other users to request the ability to subtype your token types, and it could facilitate you signing their transaction hashes to do it.

    This is an effective form of copyright for token types. Alternatively, the syntax also allows you to create clauses using public keys that are not in your wallet using the following syntax:

    ptpkh:0x026b2c214fdcd96c6a246e5ab13309e3d95772d50b2add414a2b48bc2bb9b8ef15

    Here the public key is prefixed by 0x as indicated. This allows one user to create a type for another user to use, which is effectively delegation of a responsibility or right.

  2. Before moving on to the next example, list known token types to get the ID of the parent type you created:

    ./abwallet wallet token list-types
    Example response:

    ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT (fungible)
    ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (fungible)

Child Fungible Token Type

  1. Next, create a subtype from the parent type. Remember to use the actual ID of your parent type:

    ./abwallet wallet token new-type fungible \
    --symbol CHILD \
    --subtype-clause false \
    --parent-type INSERT_YOUR_PARENT_TYPE_ID \
    --subtype-input ptpkh:2

    The properties of this token are:

    • Its symbol is CHILD.
    • It cannot, itself, be subtyped any further.
    • The parent type is the PARENT token created earlier.
  2. Create one more subtype. Again, use the actual ID of your parent type:

    ./abwallet wallet token new-type fungible \
    --symbol CHILD2 \
    --subtype-clause false \
    --parent-type INSERT_YOUR_PARENT_TYPE_ID \
    --subtype-input ptpkh:2 \
    --inherit-bearer-clause ptpkh:2

    The properties of this new CHILD2 type is like the previous CHILD type, except for one addition: all tokens of CHILD2 type will require a signature from this wallet's key #2 in order to be transferred.

    Again, this might not seem too useful, however, it could be used to create a permission system, where tokens can only be transferred if approved by an authority.

    While this is not what we usually think of when using cryptocurrency, it is exactly how accounting procedures at a corporation, or risk control procedures at a financial institution, work.

  3. After success, list all token types you have created so far:

    ./abwallet wallet token list-types
    Example response:

    ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2 (fungible)
    ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD (fungible)
    ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (fungible)
    ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT (fungible)

    These types are needed later when creating actual fungible tokens. Here are few things to keep in mind:

    • Both CHILD and CHILD2 types have the default mint clause, which is ptpkh:1. So creating actual tokens of either of these types will require a signature from this wallet's key #1.
    • Token type inheritance can have many levels—as many as are required.
    • Some token types might never be meant to be used to actually mint tokens from⁠—instead, they might only exist to be parents of other types.
    • true predicates mean there are no restrictions.
    • false predicates can never be satisfied.
    • ptpkh predicates could refer to keys in your wallet, or keys in someone else's wallet.
    • Future applications could make it possible to request permission to access certain rights, like subtyping between users or wallets.
    • Future work on the predicate language will make more, fully programmable, predicate types available.

Create NFT Type

Create some non-fungible token (NFT) types as well. Open the help output to see what options are available under the new-type non-fungible subcommand:

./abwallet wallet token new-type non-fungible -h
create new non-fungible token type

Usage:
abwallet wallet token new-type non-fungible [flags]

Flags:
--data-update-clause string data update predicate, values <true|false|ptpkh> (default "true")
-h, --help help for non-fungible
--icon-file string icon file name for the token type (optional)
--inherit-bearer-clause string predicate that will be inherited by subtypes into their bearer clauses, values <true|false|ptpkh> (default "true")
-k, --key uint which key to use for sending the transaction (default 1)
--mint-clause string predicate to control minting of this type, values <true|false|ptpkh> (default "ptpkh")
--name string full name of the token type (optional)
--parent-type hex unit identifier of a parent type in hexadecimal format
--subtype-clause string predicate to control sub typing, values <true|false|ptpkh> (default "true")
--subtype-input strings input to satisfy the parent type creation clause (mandatory with --parent-type)
--symbol string symbol (short name) of the token type (mandatory)

Global Flags:
--config string config file URL (default is $AB_HOME/config.props)
--home string set the AB_HOME for this invocation (default is /home/user/.alphabill)
--log-file string log file path or one of the special values: stdout, stderr, discard
--log-format string log format, one of: text, json, console, ecs
--log-level string logging level, one of: DEBUG, INFO, WARN, ERROR
--logger-config string logger config file URL. Considered absolute if starts with '/'. Otherwise relative from $AB_HOME. (default "logger-config.yaml")
--metrics string metrics exporter, disabled when not set. One of: stdout, prometheus
-p, --password password (interactive from prompt)
--pn string password (non-interactive from args)
-r, --rpc-url string rpc node url (default "localhost:28866")
--tracing string traces exporter, disabled when not set. One of: stdout, otlptracehttp, otlptracegrpc, zipkin
-w, --wait-for-confirmation string waits for transaction confirmation on the blockchain, otherwise just broadcasts the transaction (default "true")
-l, --wallet-location string wallet home directory (default $AB_HOME/wallet)

The options are very similar to those available for fungible types, with two differences:

  • There is no --decimals option. Since these tokens are non-fungible, they cannot be divided at all.
  • There is a new --data-update-clause option. This is a new predicate which is unique to Alphabill NFTs. It defaults to true.

Parent NFT Type

  1. Again, create the parent type first and then the subtype:

    ./abwallet wallet token new-type non-fungible \
    --symbol PARENTNFT \
    --subtype-clause ptpkh:2
  2. List types to get the ID of the parent type:

    ./abwallet wallet token list-types
    Example response:

    ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2 (fungible)
    ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD (fungible)
    ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT (fungible)
    ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (fungible)
    ID=56A5A46B1DD23E7684E735B056D5AF52B490AC68B7E4A14407923895E26C57ED, symbol=PARENTNFT (nft)

Child NFT Type

  1. Create a subtype of the parent type you just created, but remember to use the actual ID of your parent type:

    ./abwallet wallet token new-type non-fungible \
    --symbol CHILDNFT \
    --subtype-clause false \
    --parent-type INSERT_YOUR_PARENTNFT_TYPE_ID \
    --subtype-input ptpkh:2 \
    --mint-clause ptpkh:2

    Notice one difference from the previous fungible examples—it has a non-default --mint-clause on the child type. As a result, it requires a ptpkh:2 input instead of the default, which is ptpkh.

  2. After the transaction succeeds, list all known token types:

    ./abwallet wallet token list-types
    Example response:

    ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2 (fungible)
    ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD (fungible)
    ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT (fungible)
    ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (fungible)
    ID=56A5A46B1DD23E7684E735B056D5AF52B490AC68B7E4A14407923895E26C57ED, symbol=PARENTNFT (nft)
    ID=41E4FA487E37E524C13F69985B4F786E053B6BA7D92EFBC20502FAC4461D65E2, symbol=CHILDNFT (nft)

    info

    These examples haven't covered any non-default --data-update-clause usage yet. You can see it later when minting an NFT. However, the full functionality of this clause will only be realized once a fully programmable predicate language is introduced in the future.

Conclusion

The token types you created will come into use when you start minting actual tokens and transfer them from one key to another. Some final points to remember about token types:

  • Token types are immutable—once they are created, nothing about them changes.
  • Token types are not owned and cannot be transferred. This follows from the above.
  • Token types can be subtyped and can specify additional bearer clauses. Meaning that types that inherit from them will also have these clauses.
  • NFT types have a mutable data field, which can be changed if the data update predicate is satisfied. This capability can be used to implement a state machine and can be used to enforce business processes or procedures.