Address
Parse and construct CKB addresses, which encode a lock script and network prefix.
A CKB address is a bech32m-encoded string that packages two things:
- A lock script — the on-chain predicate that controls who can spend cells at this address (
codeHash,hashType, andargs). - A network prefix —
ckbfor mainnet,cktfor testnet.
Because the lock script is embedded in the address, you can always recover the exact script needed to send funds — no additional metadata required.
The Address class
ccc.Address pairs a Script with a network prefix:
export class Address {
constructor(
public script: Script,
public prefix: string, // "ckb" | "ckt"
) {}
}Wherever an API accepts an AddressLike, you can pass an existing Address or a plain object — Address.from() normalizes both:
const address = ccc.Address.from({
script: { codeHash: "0x...", hashType: "type", args: "0x..." },
prefix: "ckb",
});Usage
Parse an address string
Address.fromString parses an encoded CKB address into a structured object. If
the string is not an encoded address, the client's optional addressResolver
can resolve another representation, such as a name:
import { ccc } from "@ckb-ccc/ccc";
const clientOwner = ccc.ClientPublicMainnet.open();
const client = clientOwner.value;
// Call `await clientOwner.dispose()` when it is no longer needed.
const address = await ccc.Address.fromString(
"ckb1qzda0cr08m85hc8jlnfp3sdrlalyatuqvqdveld...",
client,
);
console.log(address.script.codeHash); // lock script code hash
console.log(address.script.hashType); // "type" | "data"
console.log(address.script.args); // lock script args
console.log(address.prefix); // "ckb"For encoded addresses, it validates that the prefix matches the client's
network and throws on a mismatch (e.g. passing a ckt address to a mainnet
client).
Passing a prefix-keyed record of clients is deprecated. When you need to handle both networks in the same code path, try the clients explicitly and handle the failed attempt at the call site:
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();
}Resolve names and other representations
Configure an AddressResolver when an application accepts representations
other than encoded CKB addresses:
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();The resolver is consulted only after normal address parsing fails.
shouldResolve selects the representations it owns. If it returns false,
Address.fromString preserves the original parsing error; if resolve returns
undefined, it throws Address ... not found. A resolved script is paired with
the supplied client's network prefix.
Construct an address from a script
Build an Address directly from a lock script and a client:
const address = ccc.Address.fromScript(lockScript, client);
console.log(address.toString()); // "ckb1q..."Or derive one from a built-in KnownScript:
const address = await ccc.Address.fromKnownScript(
client,
ccc.KnownScript.Secp256k1Blake160,
"0xPublicKeyHash...",
);Get an address from a signer
In a dApp, the easiest way to get an address is directly from the connected signer:
// Primary address as a string
const addressStr = await signer.getRecommendedAddress();
// Primary address as an Address object (includes the lock script)
const addressObj = await signer.getRecommendedAddressObj();
const { script: lock } = addressObj;
// All addresses controlled by this signer
const allAddresses = await signer.getAddresses(); // string[]Prefer getRecommendedAddressObj when you need the lock script — for example, to set it as a transaction output recipient — rather than calling Address.fromString on the string result.
Convert an address to a string
Call toString() on any Address instance to produce the bech32m string:
const str = address.toString(); // "ckb1q..." | "ckt1q..."toString() always emits the modern Full format. CCC can parse legacy formats for backwards compatibility, but never emits them.
Address formats
CCC recognizes four on-the-wire formats, distinguished by their leading format byte. Only Full is produced by toString(); the rest exist for compatibility with pre-2021 addresses.
| Format | Encoding | Payload layout | Notes |
|---|---|---|---|
Full | bech32m | [0x00, codeHash(32), hashType(1), args] | Current standard. Emitted by toString(). |
FullData | bech32 | [0x02, codeHash(32), args] — hashType is "data" | Legacy 2019 format. |
FullType | bech32 | [0x04, codeHash(32), args] — hashType is "type" | Legacy 2019 format. |
Short | bech32 | [0x01, scriptIndex(1), args(20)] | Legacy short form for well-known scripts. |
The Short format encodes a script by index rather than a full code hash. Supported scripts:
scriptIndex | Script |
|---|---|
0 | KnownScript.Secp256k1Blake160 |
1 | KnownScript.Secp256k1Multisig |
2 | KnownScript.AnyoneCanPay |
Decoding a short address requires a Client to look up the current codeHash and cellDeps for the indexed script.
Network prefix
| Prefix | Network |
|---|---|
ckb | Mainnet |
ckt | Testnet |
Read the prefix at runtime from the client:
const clientOwner = ccc.ClientPublicTestnet.open();
const client = clientOwner.value;
// Call `await clientOwner.dispose()` when it is no longer needed.
console.log(client.addressPrefix); // "ckt"Never mix mainnet and testnet addresses. Passing a ckt address to a mainnet client will throw. Always pair Address.fromString with the correct client.
Advanced: low-level parsing
For most apps, Address.fromString and Address.fromScript are all you need. When you need to inspect or transform an address without a Client round-trip, CCC exposes the underlying steps via the cccA advanced namespace:
import { cccA } from "@ckb-ccc/ccc/advanced";addressPayloadFromString
Decodes the bech32/bech32m envelope and returns the raw format byte and payload — without resolving any KnownScript:
const { prefix, format, payload } = cccA.addressPayloadFromString(
"ckb1qzda0cr08m85hc8jlnfp3sdrlalyatuqvqdveld...",
);
// prefix: "ckb"
// format: cccA.AddressFormat.Full
// payload: number[] (codeHash + hashType + args bytes)Tries bech32m first, falls back to legacy bech32. Throws Unknown address format if neither matches.
addressFromPayload
Turns a decoded payload back into an AddressLike, resolving short-format script-index lookups against the client:
const addressLike = await cccA.addressFromPayload(prefix, format, payload, client);
const address = ccc.Address.from(addressLike);For an encoded address, Address.fromString combines
addressPayloadFromString + prefix validation + addressFromPayload. If the
first step fails, it may instead delegate to the client's addressResolver.
Last updated on