Guides

Compose Transactions

Build and send CKB transactions with automatic input selection and fee calculation.

Edit on GitHub

Build and send CKB transactions without manually selecting inputs or computing fees. You declare the desired outputs, and CCC automatically finds matching inputs, calculates the fee, and creates change — all in a few method calls.

What you'll get

After this guide you'll be able to:

  • Transfer CKB to any address with automatic input selection and fee calculation
  • Sweep an entire wallet into a single output
  • Understand the declare → fill → fee → send pattern that every CCC transaction follows

All examples assume you already have a connected signer. If you don't, see Connect Wallets first.

Core concepts

On CKB, every output must declare its capacity in shannon (1 CKB = 10⁸ shannon). Use ccc.fixedPointFrom(amount) to convert a human-readable CKB amount to the internal representation:

ccc.fixedPointFrom(100)    // 100 CKB → 10_000_000_000n shannon
ccc.fixedPointFrom("0.01") // 0.01 CKB → 1_000_000n shannon

Transfer CKB

Use this when: you want to send a specific amount of CKB to another address — the most common transaction type.

The four steps below embody the declare → fill → fee → send pattern you'll reuse for every CKB transaction:

Create a transaction skeleton with just the outputs you want. CCC will figure out which inputs are needed.

import { ccc } from "@ckb-ccc/ccc";

const receiver = await signer.getRecommendedAddress();
const { script: lock } = await ccc.Address.fromString(receiver, signer.client);

const tx = ccc.Transaction.from({
  outputs: [{ capacity: ccc.fixedPointFrom(100), lock }],
});

completeInputsByCapacity queries live cells owned by the signer and adds them as inputs until the total capacity covers all outputs.

await tx.completeInputsByCapacity(signer);

completeFeeBy adds a change output back to the signer and adjusts its capacity to cover the network fee. The fee rate is calculated automatically based on current network conditions.

await tx.completeFeeBy(signer);
const txHash = await signer.sendTransaction(tx);
console.log("Transaction hash:", txHash); // "0x..." — 32-byte hex hash

Transfer all CKB

Use this when: you want to empty a wallet completely — migrate to a new address, consolidate cells, etc. Instead of specifying a fixed capacity, you collect all inputs first and deduct the fee from the output:

import { ccc } from "@ckb-ccc/ccc";

const receiver = await signer.getRecommendedAddress();
const { script: lock } = await ccc.Address.fromString(receiver, signer.client);

// No capacity set — we'll fill it after collecting all inputs
const tx = ccc.Transaction.from({
  outputs: [{ lock }],
});

// Collect every cell owned by the signer as inputs
await tx.completeInputsAll(signer);

// Deduct fee from output 0 and send all remaining CKB there
await tx.completeFeeChangeToOutput(signer, 0);

const txHash = await signer.sendTransaction(tx);

completeFeeChangeToOutput(signer, 0) adjusts the capacity of the output at index 0 downward to pay the fee. Make sure that output exists before calling this method.

Fee calculation

CCC auto-fetches the current median fee rate from the node — you never need to set it manually. Both completeFeeBy and completeFeeChangeToOutput handle this for you.

If you need fine-grained control (e.g. priority transactions), pass an explicit fee rate in shannon per kilobyte:

await tx.completeFeeBy(signer, 2000); // fixed rate of 2000 shannon/kB

API reference

MethodDescription
ccc.Transaction.from(skeleton)Create a transaction from a partial description
ccc.fixedPointFrom(amount)Convert CKB amount to shannon (bigint)
tx.completeInputsByCapacity(signer)Add inputs until capacity is sufficient
tx.completeInputsAll(signer)Collect all cells owned by the signer
tx.completeFeeBy(signer, feeRate?)Add change output and pay fee
tx.completeFeeChangeToOutput(signer, index, feeRate?)Deduct fee from a specific output
signer.sendTransaction(tx)Sign and broadcast; returns tx hash

Troubleshooting

completeInputsByCapacity throws "not enough capacity" The signer's cells don't have enough total CKB to cover the outputs plus minimum change cell (61 CKB). Fund the address or reduce the output amount.

Transaction is rejected with "InsufficientCellCapacity" Every cell must hold at least enough CKB to pay for its own on-chain storage (the minimum is 61 CKB for a basic cell). Make sure each output's capacity meets this minimum.

I want to add custom cell deps or witnesses ccc.Transaction.from() accepts a full TransactionLike — you can pass cellDeps, headerDeps, witnesses, and inputs alongside outputs. CCC's auto-fill methods append to, not replace, what you provide.

Next steps

  • Sign messages — prove address ownership without sending a transaction.
  • UDT tokens — issue and transfer fungible tokens on CKB.
  • Spore Protocol — create on-chain digital objects (DOBs).

Try these examples interactively in the CCC playground at live.ckbccc.com.

On this page