Hi everyone, I would like to introduce standard-udt-contracts ( xcshuan/ckb-standard-udt-contracts ), a CKB contract project for standardized UDTs.
original design: Enhanced UDT Standard - English / CKB Development & Technical Discussion - Nervos Talk
A transparency note first: AI was heavily used during the development of this contract project, including code implementation, test coverage, and documentation work. However, I have manually reviewed the core contract logic, state-machine paths, authority checks, and key security boundaries. AI was used mainly as an implementation and documentation assistant; the protocol design decisions and final contract logic review were done by a human.
The project implements a coordinated set of contracts around sUDT, xUDT, metadata, supply tracking, authority control, and access control. The goal is to provide a more complete and composable UDT contract foundation with clear script boundaries.
The project contains five main type scripts:
sudt: sUDT token type scriptsudt-meta: metadata type script for sUDTxudt: xUDT token type scriptxudt-meta: metadata type script for xUDTaccess-list: AccessList shard type script used by xUDT
It also includes:
lib/types: shared Molecule schemas, generated types, and strict parserslib/script-utils: shared no-std script utilities for authority checks, token delta calculation, cell scanning, and error handlingtests:ckb-testtoolintegration tests and test plugins
Core Features
The project supports:
- normal UDT transfers
- authorized mint
- authorized protocol burn
- user token-cell destruction
- optional total supply tracking
- metadata cell creation, update, and destruction
- metadata authority and mint authority
- xUDT pause mode
- xUDT whitelist / blacklist access mode
- AccessList shard management
- dynamic extension plugins through dynamic linking and spawn
Supply Tracking
Supply tracking is optional and fixed at token creation time.
When CONFIG_SUPPLY_TRACKED is enabled, the metadata current_supply must match the token cell delta in the transaction:
- mint increases supply and requires
mint_authority - protocol burn decreases supply and requires
mint_authority - user destruction does not reduce tracked supply
- metadata cannot directly change
current_supplywithout matching token movement
The design distinguishes protocol burn from user destruction:
- if the transaction consumes the metadata input cell, a negative token delta is treated as protocol burn
- if no metadata input cell is consumed, a negative token delta is treated as user token-cell destruction
This allows users to destroy their own token cells to reclaim CKB without requiring mint authority, while keeping protocol-level tracked supply unchanged.
Metadata cells can also be destroyed to reclaim CKB, but only under strict conditions:
- supply tracking must be enabled
current_supplymust be zero- the transaction must be authorized by
mint_authority
For xUDT, if access mode is still enabled, metadata destruction also requires:
- full-domain AccessList inputs
- no AccessList outputs still bound to the destroyed metadata cell
AccessList and Access Control
xUDT supports three access states:
- disabled
- blacklist
- whitelist
AccessList cells use shards to cover the lock-hash domain. Each shard has a range and sorted entries. The type layer guarantees that entries are:
- inside the shard range
- sorted
- unique
During xUDT transfers, access mode determines how input locks are checked:
- whitelist mode requires membership proof
- blacklist mode requires non-membership proof
Global access-mode transitions are governed by xudt-meta:
- disabled → active requires full-domain AccessList outputs
- active → disabled requires full-domain AccessList inputs
- whitelist ↔ blacklist requires both full-domain inputs and outputs
Normal AccessList updates do not require full-domain consumption. They only require input shards and output shards to cover the same continuous range. Within that range, entries may be changed while shards are split or merged in the same transaction.
Authority Model
Metadata can define several authority types:
InputLockInputTypeOutputTypeDynamicLinkingSpawn
mint_authority is the strongest authority. It controls:
- mint
- protocol burn
- supply-changing metadata updates
- mint authority changes
- extension list changes
- fallback authorization for metadata and access authority updates
Authority validation is implemented through a shared AuthorityVerifier. A validation path reuses one verifier and caches repeated authority checks, avoiding repeated cell scans or repeated execution of dynamic authority code.
Even a no-op update must be authorized if it consumes a metadata or AccessList state cell. State-cell unlock permissions are governed by the type script authority model, not by the lock script alone.
Technical Boundaries
The project emphasizes clear type-script ownership:
- token scripts own token movement
- metadata scripts own metadata state
- the AccessList script owns AccessList shard lifecycle
- shared libraries provide parsing and mechanics, not business-rule ownership
For example:
- token scripts do not validate metadata lifecycle
- metadata scripts do not validate token ownership
- xUDT consumes AccessList proofs but does not own the AccessList state machine
- AccessList does not care about token transfer semantics
This separation makes the system easier to test, compose, and audit.
An Indexing Caveat
There is also an important integration issue to keep in mind. The native CKB node RPC itself is not a good fit for querying live cells by script. In practice, applications usually use the CKB Indexer RPC get_cells method to find cells that use a given script. The indexer can now be integrated into the CKB node, and since CKB v0.106.0 the recommended setup is to enable the built-in node indexer.
The main get_cells query shape uses the full Script structure:
{
"script": {
"code_hash": "...",
"hash_type": "type",
"args": "..."
},
"script_type": "lock"
}
Or:
{
"script": {
"code_hash": "...",
"hash_type": "type",
"args": "..."
},
"script_type": "type"
}
In other words, the usual get_cells query is based on the full Script tuple, code_hash + hash_type + args, and a selected script type, either lock or type. It is not generally a direct script_hash query.
This has an impact on xUDT transaction construction. The xUDT type args contain the metadata type script hash. A wallet or application can read that meta type hash from the xUDT args, but the current indexer RPC cannot directly use that script hash to locate the corresponding metadata cell. Integrations therefore need an additional strategy:
- wallets, SDKs, or applications can cache the token → metadata cell mapping;
- an extra service API can query metadata cells by meta type hash;
- or the indexer could add a query interface for locating cells by type script hash.
This does not affect on-chain validation, but it does affect wallet, SDK, and application-side transaction construction. It is an important piece for future standardization and tooling support.
Testing
The project includes comprehensive ckb-testtool integration tests covering:
- sUDT / xUDT mint, burn, transfer, and user destruction
- metadata creation, update, and destruction
- supply tracking
- access-mode transitions
- AccessList shard lifecycle
- whitelist / blacklist proofs
- authority fallback
- dynamic linking / spawn extensions
- debug/release differences
The current debug integration suite contains 160+ test cases.
Summary
standard-udt-contracts is not just a minimal UDT implementation. It is a complete UDT contract framework with:
- optional supply tracking
- governed metadata
- CKB-reclaimable state-cell cleanup
- xUDT access control
- extension support
- a flexible authority model
- clear script responsibility boundaries
I hope this project can serve as a useful foundation for more advanced token, asset, and application protocols on CKB. Feedback, review, and discussion are very welcome.