Compose and broadcast a CKB transfer transaction using the CCC transaction builder. Covers single transfers, transfer-all, and fee estimation.
CCC provides a declarative transaction builder. You describe the outputs you want, then call helper methods to automatically select inputs and calculate fees. The pattern is the same regardless of which wallet type the user has connected.
Retrieve the active signer. In a React app use the useSigner() hook; in a script use a signer instance directly.
import { ccc } from "@ckb-ccc/connector-react";const signer = ccc.useSigner(); // returns undefined when not connectedif (!signer) return;
2
Resolve the destination address
Convert a bech32 CKB address string to a lock script using ccc.Address.fromString(). Pass the signer’s client so the method can look up the correct address format for the current network.
Create a transaction that declares the outputs you want. Specify the amount in Shannon (1 CKB = 10⁸ Shannon). Use ccc.fixedPointFrom() to convert from a human-readable CKB value.
Capacity is the amount of on-chain storage (and CKB) locked in the cell. For a plain CKB transfer the minimum capacity is 61 CKB (the byte size of the cell’s structure).
4
Fill inputs automatically
Call tx.completeInputsByCapacity(signer) to have CCC scan the signer’s cells and add enough inputs to cover the requested output capacity.
await tx.completeInputsByCapacity(signer);
5
Calculate and pay the fee
Call tx.completeFeeBy(signer) to calculate the network fee, add a change output back to the signer if needed, and finalize the transaction.
await tx.completeFeeBy(signer);
6
Sign and broadcast
Send the transaction. signer.sendTransaction() signs the transaction and submits it to the network, returning the transaction hash.
Use completeInputsAll() to sweep all cells into a single output, then completeFeeChangeToOutput() to deduct the fee from that output instead of adding a separate change cell.
transfer-all.ts
import { ccc } from "@ckb-ccc/ccc";async function transferAllCkb(signer: ccc.Signer, toAddress: string) { const { script: lock } = await ccc.Address.fromString(toAddress, signer.client); // Output with no capacity set — will be filled by completeFeeChangeToOutput const tx = ccc.Transaction.from({ outputs: [{ lock }], }); // Collect every cell owned by the signer await tx.completeInputsAll(signer); // Deduct the fee from output[0] and assign the remaining balance to it await tx.completeFeeChangeToOutput(signer, 0); const txHash = await signer.sendTransaction(tx); console.log("Transaction hash:", txHash); return txHash;}
completeInputsAll() consumes every live cell the signer controls, including any cells holding UDT tokens or Spore NFTs. Use it only when you intend to sweep the entire wallet.