> ## Documentation Index
> Fetch the complete documentation index at: https://ckbfans.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# UDT Tokens

> Mint and transfer User Defined Tokens (UDT) on CKB using the @ckb-ccc/udt package. Supports both SSRI-compliant UDTs and legacy xUDT/sUDT tokens.

UDT (User Defined Token) is CKB's standard for fungible tokens — similar to ERC-20 on Ethereum. The `@ckb-ccc/udt` package provides a high-level `Udt` class that handles token transfers, minting, change calculation, and metadata retrieval.

The `Udt` class supports two token standards:

* **SSRI-compliant UDTs** — the modern standard with richer on-chain metadata.
* **Legacy xUDT / sUDT** — the original CKB token standard, also supported for compatibility.

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install @ckb-ccc/udt
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn add @ckb-ccc/udt
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add @ckb-ccc/udt
    ```
  </Tab>
</Tabs>

## Import

```typescript theme={null}
import { ccc } from "@ckb-ccc/udt";
```

The `@ckb-ccc/udt` package re-exports the full `ccc` namespace and adds `ccc.udt.Udt`.

## Create a Udt instance

Instantiate `Udt` with the code cell's `OutPoint` and the token's type `Script`.

```typescript theme={null}
const udt = new ccc.udt.Udt(
  // OutPoint of the UDT's code cell (the on-chain script code)
  {
    txHash: "0x4e2e832e0b1e7b5994681b621b00c1e65f577ee4b440ef95fa07db9bb3d50269",
    index: 0,
  },
  // Type script that identifies this specific token
  {
    codeHash: "0xcc9dc33ef234e14bc788c43a4848556a5fb16401a04662fc55db9bb201987037",
    hashType: "type",
    args: "0x71fd1985b2971a9903e4d8ed0d59e6710166985217ca0681437883837b86162f",
  },
);
```

<Info>
  `txHash` / `index` point to the cell that contains the UDT script code on-chain — find these values in the token's documentation or on a CKB explorer. The type script `args` field uniquely identifies the specific token.
</Info>

### Using a known script (xUDT)

For the standard xUDT script, use `ccc.Script.fromKnownScript()` to resolve the type script automatically:

```typescript theme={null}
import { ccc } from "@ckb-ccc/udt";

const type = await ccc.Script.fromKnownScript(
  signer.client,
  ccc.KnownScript.XUdt,
  "0xf8f94a13dfe1b87c10312fb9678ab5276eefbe1e0b2c62b4841b1f393494eff2",
);

const code = (
  await signer.client.getCellDeps(
    (await signer.client.getKnownScript(ccc.KnownScript.XUdt)).cellDeps,
  )
)[0].outPoint;

const udt = new ccc.udt.Udt(code, type);
```

## Transfer tokens

Call `udt.transfer()` to produce a transaction that sends tokens to one or more recipients.

```typescript theme={null}
const { script: toLock } = await ccc.Address.fromString(toAddress, signer.client);

// Build the transfer outputs
const { res: tx } = await udt.transfer(signer, [
  { to: toLock, amount: ccc.fixedPointFrom(1) },
]);
```

`transfer()` returns an `ExecutorResponse` wrapping the transaction. Destructure `.res` to get the `Transaction` object.

## Complete the transaction

After calling `transfer()` the transaction only has the desired outputs. You must add:

1. **UDT change inputs** — cells holding enough of the token to cover the transfer.
2. **CKB inputs** — cells covering the byte cost of all outputs.
3. **Fee** — the network transaction fee.

```typescript theme={null}
// 1. Fill in UDT inputs and add a change output for leftover tokens
tx = await udt.completeBy(tx, signer);

// 2. Fill in CKB inputs to cover output capacity
await tx.completeInputsByCapacity(signer);

// 3. Calculate and pay the transaction fee
await tx.completeFeeBy(signer);

// 4. Sign and broadcast
const txHash = await signer.sendTransaction(tx);
console.log("Transaction hash:", txHash);
```

### Full transfer example

```typescript transfer-udt.ts theme={null}
import { ccc } from "@ckb-ccc/udt";

async function transferUdt(
  signer: ccc.Signer,
  udt: ccc.udt.Udt,
  toAddress: string,
  amount: bigint,
) {
  const { script: toLock } = await ccc.Address.fromString(toAddress, signer.client);

  // Describe desired outputs
  let { res: tx } = await udt.transfer(signer, [
    { to: toLock, amount },
  ]);

  // Balance UDT inputs and outputs
  tx = await udt.completeBy(tx, signer);

  // Cover cell capacity and fee
  await tx.completeInputsByCapacity(signer);
  await tx.completeFeeBy(signer);

  return signer.sendTransaction(tx);
}
```

## Mint tokens

`udt.mint()` works identically to `transfer()` but creates new tokens rather than moving existing ones. Use it only if the signer has minting authority over the token.

```typescript theme={null}
const { script: toLock } = await ccc.Address.fromString(toAddress, signer.client);

let { res: tx } = await udt.mint(signer, [
  { to: toLock, amount: ccc.fixedPointFrom(1000) },
]);

await tx.completeInputsByCapacity(signer);
await tx.completeFeeBy(signer);

const txHash = await signer.sendTransaction(tx);
```

<Warning>
  Minting is only permitted by accounts authorized by the token's type script logic. Calling `mint()` with an unauthorized signer will produce a transaction that is rejected by the CKB network.
</Warning>

## Query token metadata

For SSRI-compliant UDTs, you can read metadata directly from the chain. Each method returns an `ExecutorResponse` — access the value via `.res`.

```typescript theme={null}
const { res: name } = await udt.name();
const { res: symbol } = await udt.symbol();
const { res: decimals } = await udt.decimals();
const { res: icon } = await udt.icon();

console.log(`${name} (${symbol}), decimals: ${decimals}`);
```

<Note>
  Metadata methods return `undefined` when the UDT was created without an SSRI executor or the token does not implement the optional metadata methods. Always check for `undefined` before using the value.
</Note>

## API reference

### `new ccc.udt.Udt(code, script, config?)`

| Parameter         | Type                    | Description                                          |
| ----------------- | ----------------------- | ---------------------------------------------------- |
| `code`            | `ccc.OutPointLike`      | OutPoint of the cell containing the UDT script code. |
| `script`          | `ccc.ScriptLike`        | Type script that uniquely identifies this token.     |
| `config.executor` | `ssri.Executor \| null` | Optional SSRI executor for on-chain method calls.    |

### `udt.transfer(signer, transfers, tx?)`

| Parameter   | Type                                    | Description                              |
| ----------- | --------------------------------------- | ---------------------------------------- |
| `signer`    | `ccc.Signer`                            | The account sending the tokens.          |
| `transfers` | `{ to: ScriptLike, amount: NumLike }[]` | Array of recipients and amounts.         |
| `tx`        | `ccc.TransactionLike \| null`           | Optional existing transaction to extend. |

Returns `Promise<ExecutorResponse<ccc.Transaction>>`.

### `udt.mint(signer, mints, tx?)`

Same signature as `transfer()`. Returns a transaction that creates new tokens at the specified outputs.

### `udt.completeBy(tx, signer)`

Scans the signer's cells for UDT inputs, balances them against the outputs, and adds a change output for any leftover tokens. Returns the updated `ccc.Transaction`.

### `udt.name() / symbol() / decimals() / icon()`

Query SSRI on-chain metadata. Return `Promise<ExecutorResponse<string | ccc.Num | undefined>>`.

## Next steps

<CardGroup cols={2}>
  <Card title="Spore NFTs" icon="image" href="/guides/spore-nfts">
    Create and manage on-chain NFTs with the Spore Protocol.
  </Card>

  <Card title="Send CKB" icon="paper-plane" href="/guides/send-ckb">
    Review the transaction building pattern used for plain CKB transfers.
  </Card>
</CardGroup>
