Skip to main content

View and Transfer a CKB Balance

⏰ Estimated Time: 2 - 5 min
🔧 What You Will Need:

Tutorial Overview​

CKB is based on a UTXO-like Cell Model. Every Cell has a capacity limit, which represents both the CKB balance and how much data can be stored in the Cell simultaneously.

Transferring balance in CKB involves consuming some input Cells from the sender's account and producing new output Cells which can be unlocked by the receiver's account. The amount transferred is equal to the total capacity of the consumed Cells.

In this tutorial, you will learn how to write a simple dApp to transfer CKB balance from one account to another.

Setup Devnet & Run Example​

Step 1: Download the Source Code​

To get started with the tutorial dApp, clone the repository and navigate to the appropriate directory:

git clone https://github.com/nervosnetwork/docs.nervos.org.git --depth 1
cd docs.nervos.org/examples/dApp/simple-transfer

You can also read the full code online or download it here.

Step 2: Start the Devnet​

To interact with the dApp, ensure that your Devnet is up and running. After installing @offckb/cli, open a terminal and start the Devnet with the following command:

offckb node

Keep the Devnet running throughout the tutorial. Open another terminal to list the pre-funded accounts:

offckb accounts

Private keys are hidden by default. If the tutorial asks you to copy a Devnet private key, append --show-private-keys:

offckb accounts --show-private-keys

Use plain offckb accounts for everyday account checks. These are public development keys for the local OffCKB Devnet; never use them for Mainnet or real assets. See OffCKB's account documentation.

Step 3: Run the Example​

Navigate to your project folder (docs.nervos.org/examples/dApp/simple-transfer), install the dependencies, and start running the example:

npm install
npm start

Now, the app is running at http://localhost:1234.


Behind the Scenes​

Open the lib.ts file in your project and check out the generateAccountFromPrivateKey function:

export const generateAccountFromPrivateKey = async (
privKey: string
): Promise<Account> => {
const signer = new ccc.SignerCkbPrivateKey(cccClient, privKey);
const lock = await signer.getAddressObjSecp256k1();
return {
lockScript: lock.script,
address: lock.toString(),
pubKey: signer.publicKey,
};
};

What this function does is generate the account's public key and address via a private key. Here, we need to construct and encode a Lock Script to obtain the corresponding address of this account. A Lock Script ensures that only the owner can consume their Live Cells.

Here, we use the CKB standard Lock Script template, combining the SECP256K1 signing algorithm with the BLAKE160 hashing algorithm, to build such a Lock Script. Note that different templates will yield different addresses when encoding the address, corresponding to different types of guard for the assets.

Once we have the Lock Script of an account, we can determine how much balance the account has. The calculation is straightforward: we query and find all the Cells that use the same Lock Script and sum all these Cells' capacities; the sum is the balance.

export async function capacityOf(address: string): Promise<bigint> {
const addr = await ccc.Address.fromString(address, cccClient);
let balance = await cccClient.getBalance([addr.script]);
return balance;
}
tip

In Nervos CKB, Shannon is the smallest currency unit, with 1 CKB = 10^8 Shannons. This unit system is similar to Bitcoin's Satoshis, where 1 Bitcoin = 10^8 Satoshis. In the CCC SDK, values are mostly handled in the Shannon unit.

Next, we can start to transfer balance. Check out the transfer function in lib.ts:

export async function transfer(
toAddress: string,
amountInCKB: string,
signerPrivateKey: string
): Promise<string>;

The transfer function accepts parameters such as toAddress, amountInShannon, and signerPrivateKey to sign the transfer transaction.

This transfer transaction collects and consumes as many capacities as needed using some Live Cells as the input Cells and produce some new output Cells. The Lock Script of all these new Cells is set to the new owner's Lock Script. In this way, the CKB balance is transferred from one account to another, marking the transition of Cells from old to new.

Thanks to the CCC SDK, we can use high-level helper function ccc.Transaction.from to perform the transfer transaction, which wraps the above logic.

export async function transfer(
toAddress: string,
amountInCKB: string,
signerPrivateKey: string
): Promise<string> {
const signer = new ccc.SignerCkbPrivateKey(cccClient, signerPrivateKey);
const { script: toLock } = await ccc.Address.fromString(toAddress, cccClient);

// Build the full transaction to estimate the fee
const tx = ccc.Transaction.from({
outputs: [{ lock: toLock }],
outputsData: [],
});

// CCC transactions are easy to be edited
tx.outputs.forEach((output, i) => {
if (output.capacity > ccc.fixedPointFrom(amountInCKB)) {
alert(`Insufficient capacity at output ${i} to store data`);
return;
}
output.capacity = ccc.fixedPointFrom(amountInCKB);
});

// ....
}

Next, we need to complete the inputs of the transaction.

//....

// Complete missing parts for transaction
await tx.completeInputsByCapacity(signer);
await tx.completeFeeBy(signer, 1000);

Now we can use signer to sign and send the CKB transaction

// ...
const txHash = await signer.sendTransaction(tx);
console.log(
`Go to explorer to check the sent transaction https://pudge.explorer.nervos.org/transaction/${txHash}`
);

return txHash;

You can open the console on the browser to see the full transaction to confirm the process.


Congratulations!​

After following this tutorial, you have mastered how balance transfers work on CKB. Here's a quick recap:

  • The capacity of a Cell indicates both the CKB balance and the amount of data that can be stored in the Cell simultaneously.
  • Transferring CKB balance involves transferring some Cells from the sender to the receiver.
  • We use ccc.Transaction.from from the CCC SDK to build the transfer transaction.

Next Step​

To try the example on Testnet, stop the app with Ctrl+C, then run:

npm run start:testnet

Use a separate Testnet-only account funded with test CKB. Devnet balances and assets do not exist on Testnet. To return to Devnet, stop the app and run npm start with NETWORK unset.

For more details, check out the full source code.

Additional Resources​