# CCC (CKBers' Codebase)
CCC is a comprehensive JavaScript/TypeScript SDK for building applications on the CKB (Nervos Common Knowledge Base) blockchain. It provides intuitive APIs for transaction composition, wallet integration across multiple blockchain ecosystems (EVM, BTC, Nostr, CKB native), and cryptographic operations. The library offers a unified signing interface that enables interoperability with wallets from Ethereum, Bitcoin, and other chains while maintaining full compatibility with CKB's unique UTXO-based cell model.
The framework is designed as a monorepo containing multiple packages: `@ckb-ccc/core` for fundamental blockchain operations, `@ckb-ccc/ccc` for the complete SDK, `@ckb-ccc/connector` for wallet UI components, `@ckb-ccc/connector-react` for React integration, and specialized packages for different wallet providers (JoyId, UniSat, MetaMask via EIP-6963, etc.). CCC simplifies complex CKB operations like cell collection, fee calculation, and multi-signature support while providing helpers for User Defined Tokens (UDT) and Nervos DAO interactions.
## Client Initialization
The Client class provides connectivity to CKB nodes with built-in caching, cell searching, and transaction management. Pre-configured clients are available for mainnet and testnet.
```typescript
import { ccc } from "@ckb-ccc/ccc";
// Create testnet client (default WebSocket connection)
const testnetClient = new ccc.ClientPublicTestnet();
// Create mainnet client
const mainnetClient = new ccc.ClientPublicMainnet();
// Custom client with specific URL
const customClient = new ccc.ClientPublicTestnet({
url: "https://testnet.ckb.dev/",
fallbacks: ["https://testnet.ckbapp.dev/"]
});
// Get current tip block number
const tipNumber = await testnetClient.getTip();
console.log(`Current block: ${tipNumber}`);
// Get fee rate statistics
const feeRate = await testnetClient.getFeeRate();
console.log(`Current fee rate: ${feeRate} shannons/KB`);
```
## Address Parsing and Creation
The Address class handles CKB address encoding/decoding with support for both mainnet (ckb) and testnet (ckt) prefixes.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// Parse address string to get the underlying script
const address = await ccc.Address.fromString(
"ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq...",
client
);
console.log(`Code Hash: ${address.script.codeHash}`);
console.log(`Hash Type: ${address.script.hashType}`);
console.log(`Args: ${address.script.args}`);
// Create address from script
const script = ccc.Script.from({
codeHash: "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
hashType: "type",
args: "0x..."
});
const newAddress = ccc.Address.fromScript(script, client);
console.log(`Address: ${newAddress.toString()}`);
// Create address from known script (e.g., Secp256k1Blake160)
const knownAddress = await ccc.Address.fromKnownScript(
client,
ccc.KnownScript.Secp256k1Blake160,
"0xpubkeyhash..."
);
```
## Script Creation
Scripts define lock and type conditions for cells. CCC provides helpers for creating scripts from known templates.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// Create script manually
const lockScript = ccc.Script.from({
codeHash: "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
hashType: "type",
args: "0x..." // 20-byte pubkey hash
});
// Create script from known script template
const secp256k1Lock = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.Secp256k1Blake160,
"0xpubkeyhash..."
);
// Create xUDT type script
const xudtType = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.XUdt,
"0xowner_lock_hash..."
);
// Create Nervos DAO type script
const daoType = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.NervosDao,
"0x"
);
// Compare scripts
const isEqual = lockScript.eq(secp256k1Lock);
console.log(`Scripts equal: ${isEqual}`);
```
## Transaction Creation and Composition
The Transaction class provides intuitive APIs for building CKB transactions with automatic input collection and fee calculation.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// Create basic transfer transaction
const tx = ccc.Transaction.from({
outputs: [
{
capacity: ccc.fixedPointFrom(100), // 100 CKB
lock: recipientLockScript
}
]
});
// Add multiple outputs
tx.addOutput({
capacity: ccc.fixedPointFrom(200),
lock: anotherLockScript
});
// Add output with data
tx.addOutput(
{ lock: lockScript, type: typeScript },
"0x1234..." // output data
);
// Clone transaction for modifications
const txCopy = tx.clone();
// Get transaction hash (without witnesses)
const txHash = tx.hash();
// Get full hash (including witnesses)
const fullHash = tx.hashFull();
// Convert to bytes for serialization
const txBytes = tx.toBytes();
```
## Signer and Wallet Integration
Signers provide a unified interface for connecting wallets, signing messages, and sending transactions across different blockchain ecosystems.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// Create signer from private key (for development/testing)
const privateKeySigner = new ccc.SignerCkbPrivateKey(
client,
"0x..." // 32-byte private key
);
// Connect to signer
await privateKeySigner.connect();
console.log(`Connected: ${await privateKeySigner.isConnected()}`);
// Get addresses
const address = await privateKeySigner.getRecommendedAddress();
const allAddresses = await privateKeySigner.getAddresses();
console.log(`Primary address: ${address}`);
// Get balance
const balance = await privateKeySigner.getBalance();
console.log(`Balance: ${ccc.fixedPointToString(balance)} CKB`);
// Sign message
const signature = await privateKeySigner.signMessage("Hello CKB!");
console.log(`Signature: ${signature.signature}`);
console.log(`Sign type: ${signature.signType}`);
// Verify signature
const isValid = await ccc.Signer.verifyMessage("Hello CKB!", signature);
console.log(`Signature valid: ${isValid}`);
// Find cells owned by signer
for await (const cell of privateKeySigner.findCells({
scriptLenRange: [0, 1],
outputDataLenRange: [0, 1]
})) {
console.log(`Cell: ${cell.outPoint.txHash}:${cell.outPoint.index}`);
console.log(`Capacity: ${ccc.fixedPointToString(cell.cellOutput.capacity)} CKB`);
}
```
## Complete Transaction Flow
Full example of creating, completing, and sending a CKB transfer transaction.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
const signer = new ccc.SignerCkbPrivateKey(client, "0x...");
await signer.connect();
// Get receiver address
const receiverAddress = "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq...";
const { script: receiverLock } = await ccc.Address.fromString(receiverAddress, client);
// Create transaction with desired output
const tx = ccc.Transaction.from({
outputs: [
{ capacity: ccc.fixedPointFrom(100), lock: receiverLock }
]
});
// Complete inputs by capacity (automatically collects cells)
await tx.completeInputsByCapacity(signer);
// Complete fee with automatic change handling
await tx.completeFeeBy(signer);
// Sign and send transaction
const txHash = await signer.sendTransaction(tx);
console.log(`Transaction sent: ${txHash}`);
// Wait for confirmation
const txResponse = await client.waitTransaction(txHash, 1, 60000);
console.log(`Confirmed in block: ${txResponse?.blockNumber}`);
```
## Transfer All CKB
Example of transferring all CKB from one address to another.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
const signer = new ccc.SignerCkbPrivateKey(client, "0x...");
await signer.connect();
// Get receiver lock script
const { script: receiverLock } = await ccc.Address.fromString(receiverAddress, client);
// Create transaction with single output (no capacity specified yet)
const tx = ccc.Transaction.from({
outputs: [{ lock: receiverLock }]
});
// Collect ALL cells as inputs
await tx.completeInputsAll(signer);
// Add change to the first output (collects all CKB minus fee)
await tx.completeFeeChangeToOutput(signer, 0);
// Send transaction
const txHash = await signer.sendTransaction(tx);
console.log(`All CKB transferred: ${txHash}`);
```
## JoyId Wallet Integration
Example of connecting to JoyId wallet for signing.
```typescript
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// Create JoyId signer
const joyIdSigner = new ccc.JoyId.CkbSigner(
client,
"My dApp Name", // App name shown in JoyId
"https://example.com/icon.png" // App icon URL
);
// Connect (opens JoyId popup)
await joyIdSigner.connect();
// Get connected address
const address = await joyIdSigner.getRecommendedAddress();
console.log(`Connected JoyId address: ${address}`);
// Sign transaction
const tx = ccc.Transaction.from({
outputs: [{ capacity: ccc.fixedPointFrom(100), lock: recipientLock }]
});
await tx.completeInputsByCapacity(joyIdSigner);
await tx.completeFeeBy(joyIdSigner);
const signedTx = await joyIdSigner.signTransaction(tx);
const txHash = await client.sendTransaction(signedTx);
```
## User Defined Token (UDT) Operations
Example of transferring xUDT tokens using the UDT helper class.
```typescript
import { ccc } from "@ckb-ccc/ccc";
import { udt } from "@ckb-ccc/udt";
const client = new ccc.ClientPublicTestnet();
const signer = new ccc.SignerCkbPrivateKey(client, "0x...");
// Define UDT type script
const udtType = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.XUdt,
"0xf8f94a13dfe1b87c10312fb9678ab5276eefbe1e0b2c62b4841b1f393494eff2"
);
// Get xUDT cell dep code outpoint
const xudtScript = await client.getKnownScript(ccc.KnownScript.XUdt);
const cellDeps = await client.getCellDeps(xudtScript.cellDeps);
const codeOutPoint = cellDeps[0].outPoint;
// Create UDT instance
const udtToken = new udt.Udt(codeOutPoint, udtType);
// Get receiver
const { script: receiverLock } = await ccc.Address.fromString(receiverAddress, client);
// Create transfer transaction (1 UDT to receiver)
let { res: tx } = await udtToken.transfer(signer, [
{ to: receiverLock, amount: ccc.fixedPointFrom(1) }
]);
// Complete UDT inputs (collect UDT cells)
tx = await udtToken.completeBy(tx, signer);
// Complete CKB inputs for capacity
await tx.completeInputsByCapacity(signer);
// Pay fee
await tx.completeFeeBy(signer);
// Send transaction
const txHash = await signer.sendTransaction(tx);
console.log(`UDT transfer: ${txHash}`);
```
## React Provider and Hooks
React integration for wallet connection UI and state management.
```tsx
import { ccc } from "@ckb-ccc/connector-react";
// App wrapper with Provider
function App() {
return (
Connected: {wallet?.name}
> ) : ( )}