Create Token Types
This first tutorial demonstrates how to create new fungible and non-fungible token types and explains how token type inheritance works. It includes examples of different kinds of predicates used on tokens and provides guidance on how to supply the necessary inputs for these predicates.
The wallet token list-types
command is not yet implemented in the new JSON-RPC-based validator APIs. This feature will be introduced in a future release. For now, users need to manually track their token type IDs.
Prerequisites
Before you begin, make sure you have completed the following steps:
- You have set up an Alphabill CLI wallet.
- Your wallet is funded with testnet ALPHA, and it has enough fee credit to cover transaction fees when interacting with the User Token Partition.
Create Fungible Token Types
-
The CLI wallet has a separate command for interaction with the User Token Partition. Go to the
alphabill-wallet/build
directory and open the help output for thewallet token
command to view available subcommands and options:./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 in the help output, you can create new token types with the
wallet token new-type
subcommand. -
Start with the fungible token type, but first check the available options:
./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 totrue
. 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 toptpkh
. 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, the transaction to mint tokens of the child type must first satisfy the parent's mint clause and then the child's. This is an important concept, as you will see later. Remember, just because it's a default doesn't mean it doesn't exist.--parent-type
: defaults to00
, 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, short name of the token type.
-
Run the following command to create a new type, only the mandatory
--symbol
option provided (in this example,SIMPLE_FT
):./abwallet wallet token new-type fungible \
--symbol SIMPLE_FT \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
Sent request for new fungible token type with id=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A
Paid 0.000'000'01 fees for transaction(s). -
After the command succeeds, list known token types:
./abwallet wallet token list-types \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=SIMPLE_FT (fungible)
Parent Fungible Token Type
-
Create a new type, but place some restrictions on how it can be subtyped:
./abwallet wallet token new-type fungible \
--symbol PARENT_FT \
--subtype-clause ptpkh:2 \
--rpc-url https://tokens-partition.testnet.alphabill.orgWhat is happening here is that the parent token types can only be subtyped if the transaction to create the subtype includes an input that satisfies 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. -
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 \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT_FT (fungible)
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=SIMPLE_FT (fungible)
Child Fungible Token Type
-
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_FT \
--subtype-clause false \
--parent-type PARENT_FT_TYPE_ID \
--subtype-input ptpkh:2 \
--rpc-url https://tokens-partition.testnet.alphabill.orgThe properties of this token are:
- Its symbol is
CHILD_FT
. - It cannot, itself, be subtyped any further.
- The parent type is the
PARENT_FT
token created earlier.
- Its symbol is
-
Create one more subtype. Again, use the actual ID of your parent type:
./abwallet wallet token new-type fungible \
--symbol CHILD2_FT \
--subtype-clause false \
--parent-type PARENT_FT_TYPE_ID \
--subtype-input ptpkh:2 \
--inherit-bearer-clause ptpkh:2 \
--rpc-url https://tokens-partition.testnet.alphabill.orgThe properties of this new child token type are similar to the previous type, except for one addition: all tokens of this token 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.
-
After success, list all token types you have created so far:
./abwallet wallet token list-types \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2_FT (fungible)
ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD_FT (fungible)
ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT_FT (fungible)
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=SIMPLE_FT (fungible)These types are needed later when creating actual fungible tokens. Here are few things to keep in mind:
- Both child token types have the default mint clause, which is
ptpkh:1
. This means that creating actual tokens of either type requires a signature from the 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.
- Both child token types have the default mint clause, which is
Create Non-Fungible Token Types
Create some non-fungible token (NFT) types as well. Open the help output to see what options are available:
./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 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 totrue
.
Parent Non-Fungible Token Type
-
Again, create the parent type first and then the subtype:
./abwallet wallet token new-type non-fungible \
--symbol PARENT_NFT \
--subtype-clause ptpkh:2 \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
Sent request for new NFT type with id=56A5A46B1DD23E7684E735B056D5AF52B490AC68B7E4A14407923895E26C57ED
Paid 0.000'000'01 fees for transaction(s). -
List types to get the ID of the parent type:
./abwallet wallet token list-types \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2_FT (fungible)
ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD_FT (fungible)
ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT_FT (fungible)
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=SIMPLE_FT (fungible)
ID=56A5A46B1DD23E7684E735B056D5AF52B490AC68B7E4A14407923895E26C57ED, symbol=PARENT_NFT (nft)
Child Non-Fungible Token Type
-
Create a subtype of the parent type you just created, but remember to use the actual ID of your parent NFT type:
./abwallet wallet token new-type non-fungible \
--symbol CHILD_NFT \
--subtype-clause false \
--parent-type PARENT_NFT_TYPE_ID \
--subtype-input ptpkh:2 \
--mint-clause ptpkh:2 \
--rpc-url https://tokens-partition.testnet.alphabill.orgNotice one difference from the previous fungible examples—it has a non-default
--mint-clause
on the child type. As a result, it requires aptpkh:2
input instead of the default, which isptpkh
. -
After the transaction succeeds, list all known token types:
./abwallet wallet token list-types \
--rpc-url https://tokens-partition.testnet.alphabill.orgExample response:
ID=124F7464FDD7210C3471B0390F6E5EF4DBDE4F6E4BFA957781DE1AFB1CA3BA4A, symbol=CHILD2_FT (fungible)
ID=C1FDCF5A23666E117156979B68E46C7AD1C52DC4DFB5FE2008161418F2EBEC06, symbol=CHILD_FT (fungible)
ID=8518FC9357540BE4E04BE1334A166B223FC57305FAD9FF979B04247DD0CF11E9, symbol=PARENT_FT (fungible)
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=SIMPLE_FT (fungible)
ID=56A5A46B1DD23E7684E735B056D5AF52B490AC68B7E4A14407923895E26C57ED, symbol=PARENT_NFT (nft)
ID=41E4FA487E37E524C13F69985B4F786E053B6BA7D92EFBC20502FAC4461D65E2, symbol=CHILD_NFT (nft)infoThese examples haven't covered any non-default
--data-update-clause
usage yet. You can see it later when minting a non-fungible token. 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 account to another. Some key points to remember about token types:
- Token types are immutable—once created, their properties cannot be changed.
- Token types are not owned and cannot be transferred. This follows from the above.
- Token types can be subtyped and specify additional bearer clauses. This means that any types inheriting from them will also include these clauses.
- Non-fungible token types have a mutable data field that can be updated if the data update predicate is satisfied. This capability can be used to implement a state machine and enforce business processes or procedures.