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

Prerequisites

  • A connected signer from useSigner() or a manual signer.connect() call.
  • The @ckb-ccc/connector-react (React) or @ckb-ccc/ccc (Node.js/scripts) package installed.

Transfer a fixed amount

1

Get the signer

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 connected
if (!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.
const receiver = "ckt1qzda0cr08m85hc8jlnfp3sdrp5mec2azpfhsaz6ghptrs4m9k0mj...";
const { script: lock } = await ccc.Address.fromString(receiver, signer.client);
3

Build the transaction

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.
const tx = ccc.Transaction.from({
  outputs: [
    {
      capacity: ccc.fixedPointFrom(100), // 100 CKB
      lock,
    },
  ],
});
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.
const txHash = await signer.sendTransaction(tx);
console.log("Transaction hash:", txHash);

Full example

transfer.ts
import { ccc } from "@ckb-ccc/ccc";

async function transferCkb(signer: ccc.Signer, toAddress: string) {
  // Resolve the destination lock script
  const { script: lock } = await ccc.Address.fromString(toAddress, signer.client);

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

  // Auto-select inputs and compute fee
  await tx.completeInputsByCapacity(signer);
  await tx.completeFeeBy(signer);

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

Transfer all CKB

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.

In a React component

send-button.tsx
"use client";

import { useState } from "react";
import { ccc } from "@ckb-ccc/connector-react";

export function SendButton() {
  const signer = ccc.useSigner();
  const [txHash, setTxHash] = useState<string | null>(null);

  async function send() {
    if (!signer) return;

    const toAddress = await signer.getRecommendedAddress(); // send to self for demo
    const { script: lock } = await ccc.Address.fromString(toAddress, signer.client);

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

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

    const hash = await signer.sendTransaction(tx);
    setTxHash(hash);
  }

  return (
    <div>
      <button onClick={send} disabled={!signer}>
        Send 100 CKB to self
      </button>
      {txHash && <p>Sent: {txHash}</p>}
    </div>
  );
}

Next steps

UDT Tokens

Transfer CKB-native fungible tokens with the UDT package.

Sign Message

Sign an arbitrary message with the connected wallet.