You can create your own CF-20 token in the Cellframe network.

If you want to create token in your own network on Cellframe platfrom, see this manual.

Actions:

This manual will show the process of token creation from declaration to emission.

Declaration

Every token in the Cellframe ecosystem must be declared before emission.

Use Node Command - TOKEN DECL to declare your token.

Command has such parameters:

net - name of the Cellframe Network chain - name of the chain where token was declared token - ticker (name) of the token total_supply - maximum sum of all token emissions in datoshi signs_total - quantity of authorized token signatures signs_emission - quantity of authorized token signatures required for its emission, must be lower than signs_emission value decimals - number accuration after comma (18 by default) certs - certificates which is used to sign token (you can use any certificate)

Note

In this example, we will use bridge certificates.

Let’s declare the token CRINGE:

Command:

cellframe-node-cli token_decl -net riemann -chain zerochain -token CRINGE -type CF20 -total_supply 10000 -signs_total 2 -signs_emission 2 -decimals 18 -certs riemann.bridge.pvt.0

total_supply 10000 means 10000 datoshi or 0.00000000000001 coin, so usually you have to specify 10000.0e+18 in case you want 10000 coins to be declared.

Despite specifying more than one cert in signs_total parameter, use just one certificate in this command, you will be able to add remaining certificates using Node Command - TOKEN DECL SIGN.

Response:

                code: 0
                message: Datum 0x80F32668F798B9A59257344010D08E9D5DF4BBA6FD4855303D27B27D2B754B5E with token CRINGE is placed in datum pool

Sign it with additional cert riemann.bridge.pvt.1:

Command:

cellframe-node-cli token_decl_sign -net riemann -chain zerochain -datum 0x80F32668F798B9A59257344010D08E9D5DF4BBA6FD4855303D27B27D2B754B5E -certs riemann.bridge.pvt.1

After signing, the hash of the datum will change.

Response:

                code: 0
                message: Datum was replaced in datum pool:
        Old: 0x80F32668F798B9A59257344010D08E9D5DF4BBA6FD4855303D27B27D2B754B5E
        New: 0xB2B3D91D0DEA507FE892103140068B6E866EF607D0996814FFC36443FBA909AE

And finally, sign it with remaining cert riemann.bridge.pvt.2:

Command:

cellframe-node-cli token_decl_sign -net riemann -chain zerochain -datum 0xB2B3D91D0DEA507FE892103140068B6E866EF607D0996814FFC36443FBA909AE -certs riemann.bridge.pvt.2

The hash of the datum has changed again.

Response:

                code: 0
                message: Datum was replaced in datum pool:
        Old: 0xB2B3D91D0DEA507FE892103140068B6E866EF607D0996814FFC36443FBA909AE
        New: 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4
 

Now, our datum with token declaration is in the mempool. We have to process it.

Use Node Command - MEMPOOL PROC and the last datum hash as a datum parameter. In this case - 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4.

Command:

cellframe-node-cli mempool_proc -net riemann -chain zerochain -datum 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4
 

Response:

 datum:
            hash: 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4
            type: DATUM_TOKEN
            ts_created:
                time_stamp: 1743058177
                str: Thu, 27 Mar 2025 06:49:37 +0000
 
            data_size: 10226
 
        verify:
            isProcessed: true
            notice: Removed datum from mempool.
 

Here is the important info:

verify:
isProcessed: true
            notice: Removed datum from mempool.

We have processed the datum, in the next few minutes it will be in the zerochain.

We can now get some information about our newly created token using Node Command - TOKEN INFO.

Command:

cellframe-node-cli token info -net riemann -name CRINGE

Response:

 TOKENS:
 
                CRINGE:
                    current state:
                        -->Token name: CRINGE
                        type: CF20
                        flags: NONE
                        description: The token description is not set
                        Supply current: 10000
                        Supply total: 10000
                        Decimals: 18
                        Auth signs valid: 2
                        Auth signs total: 3
                        Signatures public keys:
 
                                line: 0
                                hash: 0xE7D0E94E6792FE145A9691ECDFECA151BC1C7477C3E11979DA48E5752C14A2E1
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                                line: 1
                                hash: 0x0A57D6D1F3E72E9F2D1DDA6E6089AFEB0AADB953FB347E08C7B6575D65FA6D56
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                                line: 2
                                hash: 0x18A38ACF46876226F9AC130427870814503C19B00185A85F034DDC2058D699C1
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                        Total emissions: 0
 
                    declarations:
 
                            status: ACCEPTED
                            Ledger return code: 0
                            Datum:
                                === Datum Token Declaration ===:
                                hash: 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4
                                ticker: CRINGE
                                size: 10226
                                version: 2
                                type: DECL
                                subtype: CF20
                                decimals: 18
                                auth signs valid: 2
                                auth signs total: 3
                                total_supply: 10000
                                flags: NONE
                                Signatures:
                                status:
 
                                        line: 1
                                        hash: 0xE7D0E94E6792FE145A9691ECDFECA151BC1C7477C3E11979DA48E5752C14A2E1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 2
                                        hash: 0x0A57D6D1F3E72E9F2D1DDA6E6089AFEB0AADB953FB347E08C7B6575D65FA6D56
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 3
                                        hash: 0x18A38ACF46876226F9AC130427870814503C19B00185A85F034DDC2058D699C1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                    updates:

On the top of the dump is current state description, below goes declarations and then updates, for now updates are empty.

You can emit your token after declaration, but in case you want to change something about it, improve safety, increase total supply, add description, etc, you must update your token.

But in case you want to emit your token immediately, go here.

Updating

Token updating allows the user to fine tune the token.

It is provided by Node Command - TOKEN UPDATE.

Command parameters:

-net - name of the Cellframe Network. The list of networks can be found in the <Config_dir> \ etc \ network folder or received by The Cellframe-Node-CLI using command - net list
-chain - name of the chain where token was declared (optional)
-token - ticker (name) of the token
-type - type of the token (CF20 or private, CF20 by default)
-total_supply_change - changes total supply, specify “INF” to set unlimited total supply (optional)
-certs - list of certificates which were used to sign token
 
-flag_set - list of flags are being set (optional)
-flag_unset - list of flags are being unset (optional)
-total_signs_valid - sets the minimum amount of valid signatures (optional)
-description - token description written in " " (optional)
 
-tx_receiver_allowed - adds specified wallet address to the list of allowed receivers (optional)
-tx_receiver_blocked - adds specified wallet address to the list of blocked receivers (optional)
-tx_sender_allowed - adds specified wallet address to the list of allowed senders (optional)
-tx_sender_blocked - adds specified wallet address to the list of blocked senders (optional)
 
-add_cert - adds certificates to the certificates list of the token (optional)
-remove_certs - removes certificates from the certificates list using theirs public key hash (optional)

Available flags:

ALL_BLOCKED: Blocks all permissions.
ALL_ALLOWED: Allows all permissions unless they are blocked. Be careful with this mode.
ALL_FROZEN: Temporarily freezes all permissions
ALL_UNFROZEN: Unfreezes all frozen permissions
STATIC_ALL: Blocks manipulations with a token after declaration. Tokens are declared statically.
STATIC_FLAGS: Blocks manipulations with token flags after declaration.
STATIC_PERMISSIONS_ALL: Blocks all manipulations with permissions list after declaration.
STATIC_PERMISSIONS_DATUM_TYPE: Blocks all manipulations with datum permissions list after declaration.
STATIC_PERMISSIONS_TX_SENDER: Blocks all manipulations with transaction senders permissions list after declaration.
STATIC_PERMISSIONS_TX_RECEIVER: Blocks all manipulations with transaction receivers permissions list after declaration.

What do we want to modify?

Example

For example, we need to increase total_supply by 5000 and restrict all actions with flags using flag STATIC_FLAGS, after that, we can inform users about this update by changing token description.

Paste all these parameters in the command.

Command:

cellframe-node-cli token_update -net riemann -chain zerochain -token CRINGE -type CF20 -total_supply_change 15000 -certs riemann.bridge.pvt.0 -flag_set STATIC_FLAGS -description "Updated: total supply increased on 5000 and the flags were set as STATIC"

Response:

                code: 0
                message: Datum 0xF48B60D3C829D1F6280781862B9EB87ED36729E6AD75D35604053A3D0D81A4AB with token update for ticker CRINGE is placed in datum pool

This message indicates that the CRINGE was successfully updated.

Now let’s sign it with 2 more signatures.

Command:

cellframe-node-cli token_update_sign -net riemann -chain zerochain -datum 0xF48B60D3C829D1F6280781862B9EB87ED36729E6AD75D35604053A3D0D81A4AB -certs riemann.bridge.pvt.1

First one.

Response:

                code: 0
                message: Datum was replaced in datum pool:
        Old: 0xF48B60D3C829D1F6280781862B9EB87ED36729E6AD75D35604053A3D0D81A4AB
        New: 0x549730A5830CFE1EDA983B36CDA935D84B64A9977BB649F20E27938F48BA2432

Command:

 
cellframe-node-cli token_update_sign -net riemann -chain zerochain -datum 0x549730A5830CFE1EDA983B36CDA935D84B64A9977BB649F20E27938F48BA243 -certs riemann.bridge.pvt.2

And the second one.

Response:

 
                code: 0
                message: Datum was replaced in datum pool:
        Old: 0x549730A5830CFE1EDA983B36CDA935D84B64A9977BB649F20E27938F48BA2432
        New: 0x562FB0177E71EE299532467A937E181CB409714864FBC8620590CF0E62CA22C0
 
 

As it was with the declaration datum, we have to proccess updating datum too.

Command:

 
cellframe-node-cli mempool_proc -net riemann -chain zerochain -datum 0xD6B95071CFC459965DA8E0B35423D85A3D78B01B41C897B1E1F3F115406C8CDD
 

Response:

 
 
        datum:
            hash: 0xD6B95071CFC459965DA8E0B35423D85A3D78B01B41C897B1E1F3F115406C8CDD
            type: DATUM_TOKEN
            ts_created:
                time_stamp: 1743069545
                str: Thu, 27 Mar 2025 09:59:05 +0000
 
            data_size: 10287
 
        verify:
            isProcessed: true
            notice: Removed datum from mempool.
 

The datum was processed.

How token looks now:

Command:

cellframe-node-cli token info -net riemann -name CRINGE

Now the token dump contains information about declaration and all updates which led to its current state which is shown on the top.

Response:

 
 TOKENS:
 
                CRINGE:
                    current state:
                        -->Token name: CRINGE
                        type: CF20
                        flags:
                            STATIC_FLAGS
 
                        description: Updated: total supply increased on 5000 and the flags were set as STATIC
                        Supply current: 15000
                        Supply total: 15000
                        Decimals: 18
                        Auth signs valid: 2
                        Auth signs total: 3
                        Signatures public keys:
 
                                line: 0
                                hash: 0xE7D0E94E6792FE145A9691ECDFECA151BC1C7477C3E11979DA48E5752C14A2E1
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                                line: 1
                                hash: 0x0A57D6D1F3E72E9F2D1DDA6E6089AFEB0AADB953FB347E08C7B6575D65FA6D56
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                                line: 2
                                hash: 0x18A38ACF46876226F9AC130427870814503C19B00185A85F034DDC2058D699C1
                                pkey_type: DAP_PKEY_TYPE_SIGN_DILITHIUM
                                bytes: 1196
 
 
                        Total emissions: 0
 
                    declarations:
 
                            status: ACCEPTED
                            Ledger return code: 0
                            Datum:
                                === Datum Token Declaration ===:
                                hash: 0x8C21984193F7BF0746063B3B186F5C1BBB0676282713BC0C5B8D92B2488B8FF4
                                ticker: CRINGE
                                size: 10226
                                version: 2
                                type: DECL
                                subtype: CF20
                                decimals: 18
                                auth signs valid: 2
                                auth signs total: 3
                                total_supply: 10000
                                flags: NONE
                                Signatures:
                                status:
 
                                        line: 1
                                        hash: 0xE7D0E94E6792FE145A9691ECDFECA151BC1C7477C3E11979DA48E5752C14A2E1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 2
                                        hash: 0x0A57D6D1F3E72E9F2D1DDA6E6089AFEB0AADB953FB347E08C7B6575D65FA6D56
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 3
                                        hash: 0x18A38ACF46876226F9AC130427870814503C19B00185A85F034DDC2058D699C1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
 
                    updates:
 
                            status: ACCEPTED
                            Ledger return code: 0
                            Datum:
                                === Datum Token Declaration ===:
                                hash: 0x562FB0177E71EE299532467A937E181CB409714864FBC8620590CF0E62CA22C0
                                size: 10305
                                version: 2
                                total_sign: 3
                                description: Updated: total supply increased on 5000 and the flags were set as STATIC
                                Signatures:
                                status:
 
                                        line: 1
                                        hash: 0xE7D0E94E6792FE145A9691ECDFECA151BC1C7477C3E11979DA48E5752C14A2E1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 2
                                        hash: 0x0A57D6D1F3E72E9F2D1DDA6E6089AFEB0AADB953FB347E08C7B6575D65FA6D56
                                        sign_type: sig_dil
                                        bytes: 2096
 
 
                                        line: 3
                                        hash: 0x18A38ACF46876226F9AC130427870814503C19B00185A85F034DDC2058D699C1
                                        sign_type: sig_dil
                                        bytes: 2096
 
 

Finally, it’s time to execute emission.

Emission

To provide token emission, we must have a wallet in the corresponding network.

How to create a wallet - via the Cellframe Wallet or via the CLI request.

Let’s emit 10000 datoshi from our 15000 total supply.

cellframe-node-cli token_emit -token CRINGE -emission_value 10000 -addr o9z3wUTSTicckJuoznovQRfNbRKv2viuHsUtmmJFockaWLtuCx2BTU7SBMfJRCFwpLo7UnkMkdpX5VsTFBHoRFQzfQpow2S6Ddc713aE -chain_emission zerochain -net riemann -certs riemann.bridge.pvt.0

Important

Emission must be performed in the zerochain.

Datum 0xF7D26F9667EFC236EECD1001394445E9A300858CFB3CCA853ED846B6E56A9E27 with 256bit emission is placed in datum pool

Sign datum using Node Command - TOKEN EMIT SIGN, and for now, we need only 2 signatures as we specified during token declaration.

cellframe-node-cli token_emit sign -emission 0xF7D26F9667EFC236EECD1001394445E9A300858CFB3CCA853ED846B6E56A9E27 -chain zerochain -net riemann -certs riemann.bridge.pvt.1

Datum was successfully signed.

result: Datum 0x1FF3A2565F9F523CA59C485F49F675693119F5D420FB90433890D1CAF723B58F with 256bit emission is placed in datum pool

We also must process this datum. In case when emission datums autoprocessing mempool_auto_types=[emission] is enabled in the network config, we don’t have to process it manually, so the datum automatically gets in chain.

But if an autoprocessing is disabled, use this command.

cellframe-node-cli mempool_proc -net riemann -datum 0x1FF3A2565F9F523CA59C485F49F675693119F5D420FB90433890D1CAF723B58F

Now, we have to create a transaction which will transfer funds from the emission to the specified wallet.

cellframe-node-cli tx_create -net riemann -chain main -from_emission 0x1FF3A2565F9F523CA59C485F49F675693119F5D420FB90433890D1CAF723B58F -cert riemann.root.pvt.0

Transaction is created.

   emission: Ok
        hash: 0xE17EE5A89343DAA2E2D9A3CD85CE85B398C17361908D553B66A70045762A0B3D

Let’s check the wallet balance using Node Command - WALLET INFO.

cellframe-node-cli wallet info -addr o9z3wUTSTicckJuoznovQRfNbRKv2viuHsUtmmJFockaWLtuCx2BTU7SBMfJRCFwpLo7UnkMkdpX5VsTFBHoRFQzfQpow2S6Ddc713aE -net riemann

So we have 10000 CRINGE datoshi on our wallet or just 0.00000000000001 coin.

addr: o9z3wUTSTicckJuoznovQRfNbRKv2viuHsUtmmJFockaWLtuCx2BTU7SBMfJRCFwpLo7UnkMkdpX5VsTFBHoRFQzfQpow2S6Ddc713aE
            network: riemann
            signs: sig_dil
            tokens:
 
                    balance:
                    coins: 0.00000000000001
                    datoshi: 10000
                    token:
                        ticker: CRINGE
                        description: Updated: total supply increased on 5000 and the flags were set as STATIC