# 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 ( ); } // Component using CCC hooks function WalletComponent() { const { open, close, disconnect, client, wallet, signerInfo } = ccc.useCcc(); const signer = ccc.useSigner(); const handleConnect = () => { open(); // Opens wallet connection modal }; const handleDisconnect = () => { disconnect(); }; const sendCkb = async () => { if (!signer) return; const { script: toLock } = await ccc.Address.fromString(recipientAddress, client); const tx = ccc.Transaction.from({ outputs: [{ capacity: ccc.fixedPointFrom(100), lock: toLock }] }); await tx.completeInputsByCapacity(signer); await tx.completeFeeBy(signer); const txHash = await signer.sendTransaction(tx); console.log(`Sent: ${txHash}`); }; return (
{signerInfo ? ( <>

Connected: {wallet?.name}

) : ( )}
); } ``` ## Cell Search and Query Methods for finding cells on-chain with various filters. ```typescript import { ccc } from "@ckb-ccc/ccc"; const client = new ccc.ClientPublicTestnet(); // Find cells by lock script for await (const cell of client.findCellsByLock( lockScript, null, // type script filter (null = any) true, // include data "desc", // order 10 // limit per page )) { console.log(`Cell: ${cell.outPoint.txHash}:${cell.outPoint.index}`); console.log(`Capacity: ${cell.cellOutput.capacity}`); console.log(`Data: ${cell.outputData}`); } // Find cells by type script (e.g., find all xUDT cells) for await (const cell of client.findCellsByType(xudtTypeScript, true)) { const balance = ccc.udtBalanceFrom(cell.outputData); console.log(`UDT Balance: ${balance}`); } // Find singleton cell by type (returns first match) const singletonCell = await client.findSingletonCellByType(typeScript); // Get specific cell by outpoint const cell = await client.getCell({ txHash: "0x...", index: 0 }); // Get live cell (checks if still unspent) const liveCell = await client.getCellLive(outPoint, true, true); ``` ## Fixed Point Utilities CKB uses 8 decimal places (1 CKB = 100,000,000 shannons). CCC provides utilities for conversion. ```typescript import { ccc } from "@ckb-ccc/ccc"; // Convert human-readable to shannons const capacity = ccc.fixedPointFrom(100); // 100 CKB -> 10000000000n const fromString = ccc.fixedPointFrom("100.5"); // 100.5 CKB -> 10050000000n const fromDecimal = ccc.fixedPointFrom(100.5); // 100.5 CKB -> 10050000000n // Convert shannons to human-readable const ckbString = ccc.fixedPointToString(10050000000n); // "100.5" const withDecimals = ccc.fixedPointToString(123456789n, 8); // "1.23456789" // Constants console.log(ccc.Zero); // 0n console.log(ccc.One); // 100000000n (1 CKB) // Use in transactions const tx = ccc.Transaction.from({ outputs: [ { capacity: ccc.fixedPointFrom(61), lock: minimalLock } // Minimum cell capacity ] }); ``` ## Nervos DAO Operations Interact with the Nervos DAO for depositing and withdrawing CKB. ```typescript import { ccc } from "@ckb-ccc/ccc"; const client = new ccc.ClientPublicTestnet(); const signer = new ccc.SignerCkbPrivateKey(client, "0x..."); // Check if a cell is a DAO cell const cell = await client.getCell(outPoint); const isDao = await cell.isNervosDao(client); const isDeposited = await cell.isNervosDao(client, "deposited"); const isWithdrew = await cell.isNervosDao(client, "withdrew"); // Get DAO profit for a withdrawn cell const profit = await cell.getDaoProfit(client); console.log(`DAO Profit: ${ccc.fixedPointToString(profit)} CKB`); // Get DAO info (headers for profit calculation) const daoInfo = await cell.getNervosDaoInfo(client); if (daoInfo.depositHeader && daoInfo.withdrawHeader) { console.log(`Deposit block: ${daoInfo.depositHeader.number}`); console.log(`Withdraw block: ${daoInfo.withdrawHeader.number}`); } // Calculate claimable epoch const claimEpoch = ccc.calcDaoClaimEpoch(depositHeader, withdrawHeader); console.log(`Can claim at epoch: ${claimEpoch[0]}`); // Calculate expected profit const expectedProfit = ccc.calcDaoProfit( ccc.fixedPointFrom(1000), // profitable capacity depositHeader, withdrawHeader ); ``` ## Transaction Fee Management Different strategies for handling transaction fees and change. ```typescript import { ccc } from "@ckb-ccc/ccc"; const client = new ccc.ClientPublicTestnet(); const signer = new ccc.SignerCkbPrivateKey(client, "0x..."); const tx = ccc.Transaction.from({ outputs: [{ capacity: ccc.fixedPointFrom(100), lock: recipientLock }] }); await tx.completeInputsByCapacity(signer); // Option 1: Auto change to signer's address await tx.completeFeeBy(signer); // Option 2: Change to specific lock script await tx.completeFeeChangeToLock(signer, customChangeLock); // Option 3: Add change to existing output await tx.completeFeeChangeToOutput(signer, 0); // Add to first output // Option 4: Custom fee rate (shannons per 1000 bytes) await tx.completeFeeBy(signer, 2000n); // Option 5: Custom change handling await tx.completeFee( signer, (tx, excessCapacity) => { if (excessCapacity >= ccc.fixedPointFrom(61)) { tx.addOutput({ capacity: excessCapacity, lock: changeLock }); return 0n; } return ccc.fixedPointFrom(61); // Need more capacity for change cell }, 1000n // fee rate ); // Get estimated fee const estimatedFee = tx.estimateFee(1000n); // Get actual fee const actualFee = await tx.getFee(client); const feeRate = await tx.getFeeRate(client); ``` ## Summary CCC provides a complete toolkit for CKB blockchain development with its intuitive transaction composition API, multi-chain wallet integration, and comprehensive helper utilities. The library abstracts away CKB's complexity while maintaining full control over transaction construction. Key integration patterns include using the `Client` classes for node connectivity, `Signer` implementations for wallet interactions, and the `Transaction` class for building and completing transactions with automatic cell collection and fee calculation. For React applications, the `@ckb-ccc/connector-react` package offers a `Provider` component and hooks (`useCcc`, `useSigner`) for seamless wallet state management. The UDT module simplifies token operations, while built-in support for Nervos DAO enables staking functionality. Whether building a simple transfer dApp or complex DeFi protocol, CCC's modular architecture allows developers to use only the components they need while benefiting from consistent APIs across all supported wallet ecosystems.