Skip to main content
@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

npm install @ckb-ccc/udt
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,
)
ParameterDescription
codeThe OutPoint of the cell that holds the UDT contract code.
scriptThe type script that identifies this token (code hash + hash type + args).
config.executorOptional SSRI executor for on-chain script execution. Omit or pass null for legacy xUDT mode.

Metadata methods

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

1

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",
  },
);
2

Build the transfer transaction

const { script: toScript } = await ccc.Address.fromString(
  "ckt1qzda0cr08m85hc8jlnfp3gp88t7re8he4qkd3he97k2tfe4ckb...",
  client,
);

const { res: tx } = await token.transfer(signer, [
  { to: toScript, amount: 100n },
]);
3

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.