Standard-udt-contracts: A Standardized UDT Contract Suite for CKB

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 script
  • sudt-meta: metadata type script for sUDT
  • xudt: xUDT token type script
  • xudt-meta: metadata type script for xUDT
  • access-list: AccessList shard type script used by xUDT

It also includes:

  • lib/types: shared Molecule schemas, generated types, and strict parsers
  • lib/script-utils: shared no-std script utilities for authority checks, token delta calculation, cell scanning, and error handling
  • tests: ckb-testtool integration 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_supply without 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_supply must 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:

  • InputLock
  • InputType
  • OutputType
  • DynamicLinking
  • Spawn

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.

10 Likes

大家好,我想介绍一个 CKB 上的 UDT 合约项目:standard-udt-contracts: xcshuan/ckb-standard-udt-contracts

原始设计: Enhanced UDT Standard - English / CKB Development & Technical Discussion - Nervos Talk

先做一个说明:这个项目的合约开发过程中大量使用了 AI 辅助,包括代码实现、测试补充和文档整理。但合约核心逻辑、状态机路径、权限校验和关键安全边界我都已经人工逐项审阅过。AI 在这里主要承担的是实现和整理工作,最终的协议设计取舍和合约逻辑判断仍然由人工完成。

这个项目实现了一组围绕 sUDT、xUDT、元数据、供应量追踪、权限控制和访问控制构建的标准化合约。目标是提供一套更完整、更可组合、边界更清晰的 UDT 合约基础设施。

项目包含五个主要 type script:

  • sudt:sUDT token type script

  • sudt-meta:sUDT metadata type script

  • xudt:xUDT token type script

  • xudt-meta:xUDT metadata type script

  • access-list:xUDT 使用的 AccessList shard type script

同时项目还包含:

  • lib/types:共享 Molecule schema、生成类型和严格解析器

  • lib/script-utils:共享 no-std 脚本工具,包括权限验证、token delta 计算、cell 扫描等

  • tests:基于 ckb-testtool 的集成测试和测试插件

核心功能

这个项目支持:

  • 普通 UDT 转账

  • 授权 mint

  • 授权 protocol burn

  • 用户自行销毁 token cell

  • 可选的 total supply tracking

  • metadata cell 创建、更新和销毁

  • metadata authority 和 mint authority

  • xUDT pause mode

  • xUDT whitelist / blacklist access mode

  • AccessList 分片管理

  • 动态扩展插件,包括 dynamic linking 和 spawn 两种执行模式

Supply Tracking 设计

供应量追踪是可选的,并且在 token 创建后不可变。

如果启用 CONFIG_SUPPLY_TRACKED,metadata 中的 current_supply 必须和交易中的 token cell delta 对齐:

  • mint 增加 supply,需要 mint_authority

  • protocol burn 减少 supply,需要 mint_authority

  • user destruction 不减少 tracked supply

  • metadata 不能单独修改 current_supply

这里区分了 protocol burn 和 user destruction:

  • 如果交易消费了 metadata input cell,负 delta 被视为 protocol burn

  • 如果没有消费 metadata input cell,负 delta 被视为用户自行销毁 token cell

这样可以允许用户销毁自己的 token cell 回收 CKB,同时不要求 mint authority,也不影响协议层记录的 total supply。

metadata cell 本身也可以销毁以回收 CKB,但条件更严格:

  • 必须是 supply-tracked mode

  • current_supply 必须为 0

  • 必须由 mint_authority 授权

对于 xUDT,如果 access mode 仍然开启,销毁 metadata 时还要求:

  • input 中包含 full-domain AccessList

  • output 中不能再留下绑定到该 metadata 的 AccessList cell

AccessList 和访问控制

xUDT 支持三种 access state:

  • disabled

  • blacklist

  • whitelist

AccessList 使用 shard 结构覆盖 lock hash domain。每个 shard 有自己的 range 和 entries。entries 在类型层保证:

  • 在 shard range 内

  • 有序

  • 不重复

xUDT 在转账时根据 access mode 验证 input lock:

  • whitelist:必须提供 membership proof

  • blacklist:必须提供 non-membership proof

Access mode 的全局切换由 xudt-meta 管理:

  • disabled → active 需要 full-domain AccessList outputs

  • active → disabled 需要 full-domain AccessList inputs

  • whitelist ↔ blacklist 需要 full-domain inputs 和 outputs

普通 AccessList 更新不需要消费全域,只需要 input shards 和 output shards 覆盖同一段连续区间。在这段区间内,可以同时修改 entries、split shard、merge shard。

权限模型

metadata 中可以配置多种 authority:

  • InputLock

  • InputType

  • OutputType

  • DynamicLinking

  • Spawn

其中 mint_authority 是最高权限,控制:

  • mint

  • protocol burn

  • supply-changing metadata update

  • mint authority 修改

  • extension list 修改

  • metadata/access authority 的 fallback 授权

项目中把权限验证逻辑抽到了共享的 AuthorityVerifier。同一条验证路径会复用 verifier,并缓存重复 authority check,避免重复扫描 cell 或重复执行动态权限代码。

即使是 no-op update,只要消费 metadata 或 AccessList state cell,也必须通过相应 authority 验证。状态 cell 的 unlock 权限不依赖 lock script,而由 type script 中的 authority 规则控制。

技术边界

项目强调 type script 的职责边界:

  • token script 负责 token movement

  • metadata script 负责 metadata state

  • access-list script 负责 AccessList shard lifecycle

  • shared libs 只提供解析和底层机制,不拥有业务规则

例如:

  • token script 不验证 metadata lifecycle

  • metadata script 不验证 token ownership

  • xUDT 只消费 AccessList proof,不负责 AccessList shard 状态机

  • AccessList script 不关心 token 转账语义

这种拆分让协议更容易测试、组合和审计。

一个需要注意的索引问题

这里还有一个实际工程问题需要说明:CKB 原生节点 RPC 本身不适合按 script 检索 live cell。通常要查找“使用某个 script 的 cell”,需要使用 CKB Indexer RPC 的 get_cells。现在 indexer 已经可以集成在 CKB node 里,CKB v0.106.0 之后也推荐直接启用 node 内置 indexer。

get_cells 的查询入口主要是完整的 Script 结构:


{

"script": {

"code_hash": "...",

"hash_type": "type",

"args": "..."

},

"script_type": "lock"

}

或者:


{

"script": {

"code_hash": "...",

"hash_type": "type",

"args": "..."

},

"script_type": "type"

}

也就是说,常规 get_cells 查询是按 code_hash + hash_type + args 组成的完整 Script 查询,并指定查 lock 还是 type,而不是直接传 script_hash

这对 xUDT 有一个影响:xUDT type args 中保存的是 metadata type script hash。拼装转账交易时,钱包或应用可以从 xUDT args 得到 meta type hash,但现有 indexer RPC 不能直接用这个 script hash 定位对应的 metadata cell。实际集成时需要额外处理:

  • 钱包、SDK 或应用自己缓存 token → metadata cell 的映射;

  • 提供额外的服务接口,通过 meta type hash 查询 metadata cell;

  • 或者推动 indexer 增加按 type script hash 定位 cell 的查询接口。

这个问题不影响链上合约验证逻辑,但会影响钱包、SDK 和应用侧的交易构建体验,是后续标准化和工具链支持里需要解决的一环。

测试

项目包含较完整的 ckb-testtool 集成测试,覆盖:

  • sUDT / xUDT mint、burn、transfer、user destruction

  • metadata 创建、更新、销毁

  • supply tracking

  • access mode transition

  • AccessList shard lifecycle

  • whitelist / blacklist proof

  • authority fallback

  • dynamic linking / spawn extension

  • debug/release 差异

目前 debug 集成测试覆盖 160+ 个用例。

总结

standard-udt-contracts 的目标不是只实现一个简单 UDT,而是提供一套完整的标准 UDT 合约框架:

  • 可选供应量追踪

  • 可治理 metadata

  • 可回收 CKB 的 state cell cleanup

  • xUDT access control

  • 可扩展插件系统

  • 明确的权限模型

  • 清晰的脚本职责边界

希望这个项目能为 CKB 上更复杂的 token、资产和应用协议提供基础组件,也欢迎大家 review 设计、提出建议或参与讨论。

6 Likes

CKBadger could serve as a powerful indexer here—it offers /cells APIs and supports fast index building. :slight_smile:

5 Likes

:+1: 这种一体化的、可供开发者直接使用的解决方案,是生态里比较缺少、又比较需要的。

5 Likes

:grinning_face: 你可以部署到测试网,再vibe一个操作demo,部署sUDT,mint sUDT,transfer sUDT;部署xUDT,mint xUDT,transfer xUDT,启用黑名单,黑名单的增删。

3 Likes

很开心能看到 Orange 大神又重新回来

4 Likes

我觉得这个项目确实挺好的,所以这几天断断续续让 AI 帮忙往上加了一个 CLI 的工具 (叫做udtx),这样对开发者来说可能更方便一些,代码在这里:

我使用 udtx 配合 offckb 起的本地链的环境,很容易就跑通了 issue token 的各项流程。同时让 AI 把几个合约都部署到了测试网上,感兴趣的可以试试看:

# 1) 初始化 testnet 工程
udtx init --name xudt-testnet-demo --network testnet

# 2) 准备 owner 私钥(建议使用环境变量,不写入配置文件)
export OWNER_PRIVKEY=0x...

# 3) 先做环境检查
udtx env check
udtx doctor

# 4) 先 dry-run,再正式 issue
udtx token issue \
  --token-type xudt \
  --name "My XUDT" \
  --symbol "MXD" \
  --decimals 8 \
  --supply 100000 \
  --owner owner \
  --dry-run

udtx token issue \
  --token-type xudt \
  --name "My XUDT" \
  --symbol "MXD" \
  --decimals 8 \
  --supply 100000 \
  --owner owner

# 5) 查询 owner 的 xUDT 信息
udtx token info --token-type xudt --owner owner

# 6) 查看/修改 access list
udtx access list
udtx access add --address ckt1...
udtx access remove --address ckt1...
udtx access list
6 Likes