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.
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
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:
- CLI wallet set up and a second public key added to your wallet.
- Local devnet set up and running on your machine.
- Devnet funds deposited into your wallet.
- Funds added to fee credit balance (both wallet accounts) for managing fees on User Token Partition.
Create Fungible Token Type
-
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 thealphabill-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. -
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 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 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 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, token symbol.
-
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 MYFTExample response:
Sent request for new fungible token type with id=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A
Paid 0.000'000'01 fees for transaction(s).infoThe 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 onlocalhost:28866
. -
After the command succeeds, list known token types:
./abwallet wallet token list-types
Example response:
ID=982B06B35FF5965B47F0CF28F7FAC835BD8250AC2F0E8C16999C57D2BDD9F98A, symbol=MYFT (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 \
--subtype-clause ptpkh:2What 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. -
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
-
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:2The 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.
- Its symbol is
-
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:2The properties of this new
CHILD2
type is like the previousCHILD
type, except for one addition: all tokens ofCHILD2
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
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
andCHILD2
types have the default mint clause, which isptpkh: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.
- Both
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 totrue
.
Parent NFT Type
-
Again, create the parent type first and then the subtype:
./abwallet wallet token new-type non-fungible \
--symbol PARENTNFT \
--subtype-clause ptpkh: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
-
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:2Notice 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
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)infoThese 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.