Address
解析和构造 CKB 地址——地址中编码了 Lock 脚本和网络前缀。
CKB 地址是一个 bech32m 编码的字符串,其中打包了两部分信息:
- Lock 脚本——决定谁可以花费该地址下 Cell 的链上判定逻辑(
codeHash、hashType和args)。 - 网络前缀——主网用
ckb,测试网用ckt。
由于 Lock 脚本已内嵌于地址中,向某个地址发送资产时,可以直接从地址还原出所需的脚本,无需额外元数据。
Address 类
ccc.Address 将 Script 与网络前缀绑定在一起:
export class Address {
constructor(
public script: Script,
public prefix: string, // "ckb" | "ckt"
) {}
}凡接受 AddressLike 的 API,都可以传入已有的 Address 实例或普通对象——Address.from() 会做归一化处理:
const address = ccc.Address.from({
script: { codeHash: "0x...", hashType: "type", args: "0x..." },
prefix: "ckb",
});使用方法
解析地址字符串
Address.fromString 将编码后的 CKB 地址解析为结构化对象。如果字符串不是编码地址,client 上可选的 addressResolver 可以解析名称等其他表示形式:
import { ccc } from "@ckb-ccc/ccc";
const clientOwner = ccc.ClientPublicMainnet.open();
const client = clientOwner.value;
// 不再使用时调用 `await clientOwner.dispose()`。
const address = await ccc.Address.fromString(
"ckb1qzda0cr08m85hc8jlnfp3sdrlalyatuqvqdveld...",
client,
);
console.log(address.script.codeHash); // Lock 脚本 code hash
console.log(address.script.hashType); // "type" | "data"
console.log(address.script.args); // Lock 脚本 args
console.log(address.prefix); // "ckb"对于编码地址,该方法会校验地址前缀与 client 所在网络是否匹配,不匹配时抛出异常(例如将 ckt 地址传给主网 client)。
传入以 prefix 为 key 的 client 对象字典已经 deprecated。如果同一段代码需要同时处理两个网络,应由调用方依次尝试各个 client 并处理失败:
const clientsOwner = ccc.OwnerAggregated.from([
ccc.ClientPublicMainnet.open(),
ccc.ClientPublicTestnet.open(),
] as const);
const [mainnetClient, testnetClient] = clientsOwner.value;
let address: ccc.Address;
try {
try {
address = await ccc.Address.fromString(someAddress, mainnetClient);
} catch {
address = await ccc.Address.fromString(someAddress, testnetClient);
}
} finally {
await clientsOwner.dispose();
}解析名称及其他表示形式
如果应用接受编码后的 CKB 地址以外的表示形式,可以配置 AddressResolver:
const names: Record<string, ccc.ScriptLike> = {
"alice.example": aliceLock,
};
const addressResolver: ccc.AddressResolver = {
shouldResolve: (value) => value.endsWith(".example"),
resolve: async (value) => names[value],
};
const clientOwner = ccc.ClientPublicMainnet.open({ addressResolver });
const address = await ccc.Address.fromString(
"alice.example",
clientOwner.value,
);
await clientOwner.dispose();只有常规地址解析失败后才会调用 resolver。shouldResolve 用于判断该 resolver 是否负责给定的表示形式;如果返回 false,Address.fromString 会保留原始解析错误;如果 resolve 返回 undefined,则抛出 Address ... not found。解析得到的脚本会与传入 client 的网络前缀组合成 Address。
从脚本构造地址
直接由 Lock 脚本和 client 构造 Address:
const address = ccc.Address.fromScript(lockScript, client);
console.log(address.toString()); // "ckb1q..."也可以从内置的 KnownScript 派生:
const address = await ccc.Address.fromKnownScript(
client,
ccc.KnownScript.Secp256k1Blake160,
"0xPublicKeyHash...",
);从 Signer 获取地址
在 dApp 中,最便捷的方式是直接从已连接的 Signer 获取地址:
// 推荐地址的字符串形式
const addressStr = await signer.getRecommendedAddress();
// 推荐地址的 Address 对象(包含 Lock 脚本)
const addressObj = await signer.getRecommendedAddressObj();
const { script: lock } = addressObj;
// 该 Signer 控制的所有地址
const allAddresses = await signer.getAddresses(); // string[]需要 Lock 脚本时(例如将其设为交易输出的接收方),优先使用 getRecommendedAddressObj,而不是先调用 getRecommendedAddress 再对结果执行 Address.fromString。
将地址转换为字符串
对任意 Address 实例调用 toString(),得到 bech32m 编码的地址字符串:
const str = address.toString(); // "ckb1q..." | "ckt1q..."toString() 始终生成当前推荐的长(Full)地址格式。CCC 为了向后兼容可以解析旧版(legacy)格式,但不会生成它们。
地址格式
CCC 支持四种地址格式,由首字节(format byte)区分。toString() 只输出 Full 格式,其余格式仅用于兼容 2021 年前的旧版地址。
| 格式 | 编码 | Payload 结构 | 备注 |
|---|---|---|---|
Full | bech32m | [0x00, codeHash(32), hashType(1), args] | 当前标准格式,由 toString() 输出。 |
FullData | bech32 | [0x02, codeHash(32), args],hashType 为 "data" | 2019 年旧版格式。 |
FullType | bech32 | [0x04, codeHash(32), args],hashType 为 "type" | 2019 年旧版格式。 |
Short | bech32 | [0x01, scriptIndex(1), args(20)] | 通过脚本索引(scriptIndex)而非完整 code hash 编码脚本。 |
Short 格式通过脚本索引而非完整 codeHash 来标识脚本,支持以下脚本:
scriptIndex | 脚本 |
|---|---|
0 | KnownScript.Secp256k1Blake160 |
1 | KnownScript.Secp256k1Multisig |
2 | KnownScript.AnyoneCanPay |
解析 Short 格式地址需要 Client,用于查找索引对应脚本当前的 codeHash 和 cellDeps。
网络前缀
| 前缀 | 网络 |
|---|---|
ckb | 主网 |
ckt | 测试网 |
运行时可从 client 读取前缀:
const clientOwner = ccc.ClientPublicTestnet.open();
const client = clientOwner.value;
// 不再使用时调用 `await clientOwner.dispose()`。
console.log(client.addressPrefix); // "ckt"不要混用主网和测试网地址。将 ckt 地址传给主网 client 会抛出异常。使用 Address.fromString 时,务必配合正确网络的 client。
进阶:底层解析
绝大多数应用场景,Address.fromString 和 Address.fromScript 已经足够。如果需要在不发起 Client 请求的情况下检查或转换地址,可以使用 cccA 进阶命名空间中暴露的底层步骤:
import { cccA } from "@ckb-ccc/ccc/advanced";addressPayloadFromString
解码 bech32/bech32m 封装,返回原始的 format byte 和 payload——不解析任何 KnownScript:
const { prefix, format, payload } = cccA.addressPayloadFromString(
"ckb1qzda0cr08m85hc8jlnfp3sdrlalyatuqvqdveld...",
);
// prefix: "ckb"
// format: cccA.AddressFormat.Full
// payload: number[](codeHash + hashType + args 字节)优先尝试 bech32m 解码,失败后回退到旧版 bech32。两者均不匹配时,抛出 Unknown address format。
addressFromPayload
将解码后的 payload 还原为 AddressLike,并通过 client 解析 Short 格式的脚本索引:
const addressLike = await cccA.addressFromPayload(prefix, format, payload, client);
const address = ccc.Address.from(addressLike);对于编码地址,Address.fromString 会依次执行 addressPayloadFromString、前缀校验和 addressFromPayload。如果第一步失败,则可能改由 client.addressResolver 解析。
Last updated on