Related posts:
RFC: Compact UDT Lock
Random idea: compact sUDT / xUDT cell
RFC: Multi-purpose NFT Draft Spec
Compact Extension for m-NFT Protocol: The Key to Mass Adoption
Background
m-NFT protocol has been used by the community for several months, and thousands of users enjoy its products. Although it’s very user friendly and easy to use, the minting cost problem is increasingly apparent alone with the CKB price fluctuation. One has to create at least two cells to to start sending NFTs, and they need another more cell for every distribution. It will be an obvious barrier for the huge number of user adoption in the near future.
The compact extension for m-NFT proposed a primary solution for this problem, which uses an SMT to store the NFT instances. But it still require an issuer cell
and an nft class cell
to mint. Here we post an upgraded standard to eliminate the cost problem once and for all. There will be no state storage cost for token creating, minting, and transferring by adopting this standard.
Basic Ideas
Similar with m-NFT compact extension, here we introduce a CoTA /kəʊta/
cell to manage the token states SMT, including the holding, withdrawal, and claimed ones. And we use a global registry cell to enforce the uniqueness of users’ CoTA cell. And we also introduce the two-step-transfer method to avoid the cell collision problem during token transferring between different users.
Global CoTA registry cell
The main purpose of CoTA registry cell is to make sure every lockscript maintains only one cell.
# global_cota_registry_cell data structure
data:
version: byte
registry_smt_root: bytes32
type:
code_hash: registry_type
args: type_id
lock:
always_success lock
CoTA Cell
cota_cell
is used to store user’s token states, including those the user holds, sent, and claimed. To keep the cell unique and untransferable, its typescript.args
must match its lockscript.
# cota cell data structure
data:
version: byte # must be 0
smt_root: byte32
type:
code_hash: cota_type
args: lockscript_hash[0..20] # must match self.lockscript
lock:
user-defined
Note: the CoTA cell is indestructible to make sure the token cannot be claimed twice.
Ensure the token uniqueness
Every UTXO in Nervos chain is unique globally. So we could use it (outpoint hash) as the identity for created token without a shared state manager.
K-V structure for CoTA SMT
The cota.smt_root
is the most important data field in the design. Data stored in the SMT is managed by key-value pair. There are several kinds of data stored and compressed in this hash root. We use a two-bytes variable smt_root.key.smt_type
to identify different key types.
NFT related SMT k-v pair definition
# **cota-NFT-define** data structure
key:
smt_type: uint16 # type bytes for cota-NFT-define big endian
cota_id: Byte[20] # first 20bytes of [tx's 1st input] outpoint hash
reserved: Byte[10]
value:
total: uint32 # 0 for unlimited
issued: uint32
configure: Byte[1]
reserved: Byte[23]
# cota-NFT-hold data structure
key:
smt_type: uint16 # type byte for cota-NFT-hold
cota_id: Byte[20]
index_id: uint32
reserved: Byte[6]
value:
configure: Byte[1]
state: Byte[1]
characteristic: Byte[20] # user-defined data
reserved: Byte[10]
# cota-NFT-withdraw data structure
key:
smt_type: uint16 # type byte for cota-NFT-withdraw
cota_id: Byte[20]
index_id: uint32
reserved: Byte[6]
value:
blake2b_hash of {
configure: Byte[1]
state: Byte[1]
characteristic: Byte[20] # user-defined data
to: lock_hash[0..20]
out_point: OutPoint[12..36] # Outpoint of previous input cell with SMT
}
# cota-NFT-claim data structure
key:
blake2b_hash of {
smt_type: uint16 # type byte for cota-NFT-claim
cota_id: Byte[20]
index_id: uint32
out_point: OutPoint[12..36] # Outpoint field recorded in the proof
}
value:
0x00...00 for nonclaimed
0xFF...FF for claimed
FT related SMT k-v pair definition
# **cota-FT-define** data structure
key:
smt_type: uint16 # type bytes for cota-FT-define big endian
cota_id: Byte[20] # first 20bytes of [tx's 1st input] outpoint hash
reserved: Byte[10]
value:
blake2b_hash of {
total: uint128 # 0 for unlimited
issued: uint128
decimal: uint8
}
# cota-FT-hold data structure
key:
smt_type: uint16 # type byte for cota-FT-hold
cota_id: Byte[20]
reserved: Byte[10]
value:
amount: uint128
reserved: Byte[16]
# cota-FT-withdraw data structure
key:
smt_type: uint16 # type byte for cota-FT-withdraw
cota_id: Byte[20]
reserved: Byte[10]
value:
blake2b_hash of {
amount: uint128
to: lock_hash[0..20]
out_point: OutPoint[12..36] # Outpoint of previous input cell with SMT
}
# cota-FT-claimed data structure
key:
blake2b_hash of {
smt_type: uint16 # type byte for cota-NFT-claimed
cota_id: Byte[20]
out_point: OutPoint[12..36] # Outpoint field recorded in the proof
}
value:
0x00...00 for nonclaimed
0xFF...FF for claimed
smt_type table
name | 1st byte | 2nd byte | note |
---|---|---|---|
cota-NFT-define | 0x81 (NFT) | 0x00 | NFT definition |
cota-NFT-held | 0x81 (NFT) | 0x01 | NFT held record |
cota-NFT-withdrawal | 0x81 (NFT) | 0x02 | NFT withdrawal record |
cota-NFT-claim | 0x81 (NFT) | 0x03 | NFT claim record |
cota-FT-define | 0x82 (FT) | 0x00 | FT definition |
cota-FT-held | 0x82 (FT) | 0x01 | FT held record |
cota-FT-withdrawal | 0x82 (FT) | 0x02 | FT withdrawal record |
cota-FT-claim | 0x82 (FT) | 0x03 | FT claim record |
Metadata definition
The k-v data in smt_root
has stored the necessary dynamic data for token usage, but we still need more static data to facilitate the application level access. These static information includes the token name, issuer data, token URI, and so on. Here we use the CKB Transaction Metadata Standard Proposal to store these static metadata.
Here demos the usage of metadata in CoTA related transactions.
# User information update
input:
cota_cell
output:
cota_cell
witness:
[0]: signature
[1]: CTMeta - user information
### cota-NFT/FT creation
input:
cota_cell:
nft_smt_root:
smt_root_old
output:
cota_cell:
nft_smt_root:
smt_root_new:
key:
cotaid: <outpoint_hash of input.cota_cell>
value:
...
witness:
[0]: signature
[1]: CTMeta - token information
Transaction types
tx type | tx.inputs | tx.outputs | affected data |
---|---|---|---|
CoTA cell register | capacity cell , registry cell |
cota cell , registry cell |
registry.smt |
personal data update | cota cell |
cota cell |
tx.witness.metadata |
create new NFT | cota cell |
cota cell |
cota.smt.nft_define , witness.metadata |
distribute NFT | cota cell |
cota cell |
cota.smt.nft_define , cota.smt.nft_withdraw |
NFT claim | cota cell |
cota cell |
cota.smt.nft_claim , cota.smt.nft_hold |
NFT withdraw | cota cell |
cota cell |
cota.smt.nft_hold , cota.smt.nft_withdraw |