@ckb-ccc/udt provides the Udt and UdtPausable classes for interacting with CKB User Defined Tokens. It supports both SSRI-compliant UDTs and legacy sUDT/xUDT tokens.
User Defined Tokens (UDT) are CKB’s fungible token standard. A UDT is identified by a type script; the token balance of a cell is stored as a little-endian 128-bit integer in the cell’s data field.
Installation
import { Udt, UdtPausable } from "@ckb-ccc/udt";
// or via shell
import { udt } from "@ckb-ccc/shell";
Udt class
Udt extends ssri.Trait and represents a single deployed UDT contract.
Constructor
new Udt(
code: ccc.OutPointLike,
script: ccc.ScriptLike,
config?: { executor?: ssri.Executor | null } | null,
)
| Parameter | Description |
|---|
code | The OutPoint of the cell that holds the UDT contract code. |
script | The type script that identifies this token (code hash + hash type + args). |
config.executor | Optional SSRI executor for on-chain script execution. Omit or pass null for legacy xUDT mode. |
These methods query on-chain token metadata when an SSRI executor is provided. They return undefined for legacy xUDT tokens.
name
async name(context?: ssri.ContextScript): Promise<ssri.ExecutorResponse<string | undefined>>
symbol
async symbol(context?: ssri.ContextScript): Promise<ssri.ExecutorResponse<string | undefined>>
decimals
async decimals(context?: ssri.ContextScript): Promise<ssri.ExecutorResponse<ccc.Num | undefined>>
icon
async icon(context?: ssri.ContextScript): Promise<ssri.ExecutorResponse<string | undefined>>
Transfer
Send tokens to one or more recipients. Returns a transaction that you must complete and sign.
async transfer(
signer: ccc.Signer,
transfers: { to: ccc.ScriptLike; amount: ccc.NumLike }[],
tx?: ccc.TransactionLike | null,
): Promise<ssri.ExecutorResponse<ccc.Transaction>>
Mint
Mint new tokens to one or more recipients. The calling signer must be the token issuer.
async mint(
signer: ccc.Signer,
mints: { to: ccc.ScriptLike; amount: ccc.NumLike }[],
tx?: ccc.TransactionLike | null,
): Promise<ssri.ExecutorResponse<ccc.Transaction>>
completeBy
Complete the UDT inputs and add a change output back to the signer’s recommended lock.
async completeBy(
tx: ccc.TransactionLike,
from: ccc.Signer,
): Promise<ccc.Transaction>
completeChangeToLock
Like completeBy, but send the UDT change to an explicit lock script instead of the signer’s address.
async completeChangeToLock(
txLike: ccc.TransactionLike,
signer: ccc.Signer,
change: ccc.ScriptLike,
): Promise<ccc.Transaction>
UdtPausable class
UdtPausable extends Udt and adds the ability to pause and unpause token transfers for specific lock scripts. It requires an SSRI executor and only works with SSRI-compliant UDT contracts that support the UDTPausable interface.
pause
Pause token transfers for the given lock scripts.
async pause(
signer: ccc.Signer,
locks: ccc.ScriptLike[],
tx?: ccc.TransactionLike | null,
extraLockHashes?: ccc.HexLike[] | null,
): Promise<ssri.ExecutorResponse<ccc.Transaction>>
unpause
Re-enable token transfers for the given lock scripts.
async unpause(
signer: ccc.Signer,
locks: ccc.ScriptLike[],
tx?: ccc.TransactionLike | null,
extraLockHashes?: ccc.HexLike[] | null,
): Promise<ssri.ExecutorResponse<ccc.Transaction>>
isPaused
Check whether specific lock scripts are currently paused.
async isPaused(
locks: ccc.ScriptLike[],
extraLockHashes?: ccc.HexLike[] | null,
): Promise<ssri.ExecutorResponse<boolean[]>>
enumeratePaused
List all lock hashes that are currently paused, with optional pagination.
async enumeratePaused(
offset?: ccc.Num,
limit?: ccc.Num,
): Promise<ssri.ExecutorResponse<ccc.Hex[]>>
Example: transfer UDT tokens
Instantiate the token
import { Udt } from "@ckb-ccc/udt";
import { ccc } from "@ckb-ccc/shell";
const client = new ccc.ClientPublicTestnet();
const signer = new ccc.SignerCkbPrivateKey(client, "0x<private-key>");
const token = new Udt(
// OutPoint of the UDT contract code cell
{
txHash: "0x4e2e832e0b1e7b5994681b621b00c1e65f577ee4b440ef95fa07db9bb3d50269",
index: 0,
},
// Type script that identifies this specific token
{
codeHash: "0xcc9dc33ef234e14bc788c43a4848556a5fb16401a04662fc55db9bb201987037",
hashType: "type",
args: "0x71fd1985b2971a9903e4d8ed0d59e6710166985217ca0681437883837b86162f",
},
);
Build the transfer transaction
const { script: toScript } = await ccc.Address.fromString(
"ckt1qzda0cr08m85hc8jlnfp3gp88t7re8he4qkd3he97k2tfe4ckb...",
client,
);
const { res: tx } = await token.transfer(signer, [
{ to: toScript, amount: 100n },
]);
Complete inputs, fees, and send
const completedTx = await token.completeBy(tx, signer);
await completedTx.completeInputsByCapacity(signer);
await completedTx.completeFeeBy(signer);
const txHash = await signer.sendTransaction(completedTx);
console.log("Transfer sent:", txHash);
transfer() and mint() return an ssri.ExecutorResponse. Access the transaction via .res on the response object before completing it.