核心概念
Client
Client 将 CCC 连接到 CKB 节点,提供链上数据的访问能力。
什么是 Client?
Client 是一个抽象类,通过 JSON-RPC 将 CCC 连接到 CKB 节点。它提供查询链状态、搜索 Cell 和交易、广播已签名交易等方法。
// 来自 packages/core/src/client/client.ts
export abstract class Client {
public cache: ClientCache;
constructor(config?: { cache?: ClientCache }) {
this.cache = config?.cache ?? new ClientCacheMemory();
}
abstract get url(): string;
abstract get addressPrefix(): string;
abstract getKnownScript(script: KnownScript): Promise<ScriptInfo>;
}内置 Client
CCC 提供两个开箱即用的 Client 实现:
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicMainnet();import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();React Provider 默认使用 ClientPublicTestnet。生产环境部署时,切换为
ClientPublicMainnet。
核心方法
链状态
// 获取当前最新区块高度
abstract getTip(): Promise<Num>;
// 获取当前最新区块头
abstract getTipHeader(verbosity?: number | null): Promise<ClientBlockHeader>;
// 按区块高度查询区块(优先读取缓存)
async getBlockByNumber(
blockNumber: NumLike,
verbosity?: number | null,
withCycles?: boolean | null,
): Promise<ClientBlock | undefined>;
// 按区块哈希查询区块(优先读取缓存)
async getBlockByHash(
blockHash: HexLike,
verbosity?: number | null,
withCycles?: boolean | null,
): Promise<ClientBlock | undefined>;交易
// 按哈希查询交易
async getTransaction(
txHashLike: HexLike,
): Promise<ClientTransactionResponse | undefined>;
// 广播已签名的交易,返回交易哈希
async sendTransaction(
transaction: TransactionLike,
validator?: OutputsValidator,
options?: { maxFeeRate?: NumLike },
): Promise<Hex>;
// 等待交易达到指定确认数
async waitTransaction(
txHash: HexLike,
confirmations: number, // 默认 0
timeout: number, // 默认 60000 毫秒
interval: number, // 默认 2000 毫秒
): Promise<ClientTransactionResponse | undefined>;Cell 查询
// 按 OutPoint 查询 Cell
async getCell(outPointLike: OutPointLike): Promise<Cell | undefined>;
// 查询活跃(未消费)的 Cell
async getCellLive(
outPointLike: OutPointLike,
withData?: boolean | null,
includeTxPool?: boolean | null,
): Promise<Cell | undefined>;
// 异步生成器,按搜索条件逐个返回匹配的 Cell
async *findCells(
keyLike: ClientCollectableSearchKeyLike,
order?: "asc" | "desc",
limit?: number,
): AsyncGenerator<Cell>;
// 快捷方法:按 Lock 脚本查询 Cell
findCellsByLock(
lock: ScriptLike,
type?: ScriptLike | null,
withData?: boolean,
order?: "asc" | "desc",
limit?: number,
): AsyncGenerator<Cell>;
// 快捷方法:按 Type 脚本查询 Cell
findCellsByType(
type: ScriptLike,
withData?: boolean,
order?: "asc" | "desc",
limit?: number,
): AsyncGenerator<Cell>;交易查询
// 异步生成器,按搜索条件逐个返回匹配的交易
async *findTransactions(
key: ClientIndexerSearchKeyTransactionLike,
order?: "asc" | "desc",
limit?: number,
): AsyncGenerator<...>;
// 快捷方法:按 Lock 脚本查询交易
findTransactionsByLock(
lock: ScriptLike,
type?: ScriptLike | null,
groupByTransaction?: boolean | null,
order?: "asc" | "desc",
limit?: number,
): AsyncGenerator<...>;余额与费率
// 查询所有指定 Lock 脚本持有的 Cell 总容量(单位:Shannon)
async getBalance(locks: ScriptLike[]): Promise<Num>;
// 获取当前网络费率(每 1000 字节的 Shannon 数)
async getFeeRate(
blockRange?: NumLike,
options?: { maxFeeRate?: NumLike },
): Promise<Num>;
// 获取费率统计数据(均值和中位数)
abstract getFeeRateStatistics(
blockRange?: NumLike,
): Promise<{ mean?: Num; median?: Num }>;已知脚本
// 将已知脚本解析为链上的 ScriptInfo
abstract getKnownScript(script: KnownScript): Promise<ScriptInfo>;KnownScript
KnownScript 枚举列出了 CCC 在主网和测试网上均可解析的预部署脚本:
// 来自 packages/core/src/client/knownScript.ts
export enum KnownScript {
NervosDao = "NervosDao",
Secp256k1Blake160 = "Secp256k1Blake160",
Secp256k1Multisig = "Secp256k1Multisig",
Secp256k1MultisigV2 = "Secp256k1MultisigV2",
AnyoneCanPay = "AnyoneCanPay",
TypeId = "TypeId",
XUdt = "XUdt",
JoyId = "JoyId",
COTA = "COTA",
PWLock = "PWLock",
OmniLock = "OmniLock",
NostrLock = "NostrLock",
UniqueType = "UniqueType",
// ckb-proxy-locks
AlwaysSuccess = "AlwaysSuccess",
InputTypeProxyLock = "InputTypeProxyLock",
OutputTypeProxyLock = "OutputTypeProxyLock",
LockProxyLock = "LockProxyLock",
SingleUseLock = "SingleUseLock",
TypeBurnLock = "TypeBurnLock",
EasyToDiscoverType = "EasyToDiscoverType",
TimeLock = "TimeLock",
}使用 getKnownScript() 可获取已知脚本的 codeHash、hashType 和 cellDeps。也可以用 Script.fromKnownScript() 作为快捷方式,直接构造 Script 实例:
import { ccc } from "@ckb-ccc/ccc";
const client = new ccc.ClientPublicTestnet();
// 直接构造 Script 实例
const xudtScript = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.XUdt,
"0xabcdef...", // args
);ClientCache
每个 Client 内置一个 ClientCache 实例,将已获取的 Cell、交易和区块头缓存在内存中。这样可以减少重复的 RPC 调用,并在交易发送后在本地标记已消费的 Cell。
// 访问任意 Client 的缓存
const cache: ccc.ClientCache = client.cache;默认缓存实现为 ClientCacheMemory,数据存储在进程内存中。可通过构造函数传入自定义缓存实现:
const client = new ccc.ClientPublicTestnet({ cache: myCustomCache });使用示例
查询链上数据
import { ccc } from "@ckb-ccc/ccc";
async function queryChain() {
const client = new ccc.ClientPublicTestnet();
// 获取当前最新区块高度
const tip = await client.getTip();
console.log("当前高度:", tip.toString());
// 获取费率
const feeRate = await client.getFeeRate();
console.log("费率(Shannon/KB):", feeRate.toString());
// 查询交易
const txResponse = await client.getTransaction("0xabc123...");
if (txResponse) {
console.log("交易状态:", txResponse.status);
}
// 流式获取某个脚本锁定的所有 Cell
const lockScript = await ccc.Script.fromKnownScript(
client,
ccc.KnownScript.Secp256k1Blake160,
"0x36c329ed630d6ce750712a477543672adab57f4c",
);
for await (const cell of client.findCellsByLock(lockScript)) {
console.log("Cell 容量:", cell.cellOutput.capacity.toString());
}
}等待交易确认
import { ccc } from "@ckb-ccc/ccc";
async function sendAndWait(signer: ccc.Signer, tx: ccc.Transaction) {
const txHash = await signer.sendTransaction(tx);
console.log("已发送:", txHash);
// 最多等待 60 秒,直到达到至少 1 个确认
const confirmed = await signer.client.waitTransaction(txHash, 1);
if (confirmed) {
console.log("已确认,区块高度:", confirmed.blockNumber?.toString());
}
}最后更新于