From 03ba2c5d60f067c21b37c659dac68648ea0f3161 Mon Sep 17 00:00:00 2001 From: ashuralyk Date: Thu, 3 Jul 2025 18:14:45 +0800 Subject: [PATCH 1/9] feat: add SignerCkbMultisig module and dependent features --- packages/core/src/ckb/script.ts | 105 ++++++++- packages/core/src/ckb/transaction.ts | 36 ++- packages/core/src/client/client.ts | 4 + .../core/src/client/clientPublicMainnet.ts | 16 ++ .../core/src/client/clientPublicTestnet.ts | 16 ++ packages/core/src/hex/index.ts | 18 +- packages/core/src/signer/ckb/index.ts | 1 + .../core/src/signer/ckb/signerCkbMultisig.ts | 223 ++++++++++++++++++ packages/examples/src/transferToMultisig.ts | 0 9 files changed, 414 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/signer/ckb/signerCkbMultisig.ts create mode 100644 packages/examples/src/transferToMultisig.ts diff --git a/packages/core/src/ckb/script.ts b/packages/core/src/ckb/script.ts index 1de3c7ac0..9dd6f8d90 100644 --- a/packages/core/src/ckb/script.ts +++ b/packages/core/src/ckb/script.ts @@ -1,7 +1,8 @@ +import { Since, SinceLike, hashCkb } from "../barrel.js"; import { Bytes, BytesLike, bytesFrom } from "../bytes/index.js"; import type { Client } from "../client/index.js"; import { KnownScript } from "../client/knownScript.js"; -import { Hex, HexLike, hexFrom } from "../hex/index.js"; +import { Hex, HexLike, hexConcat, hexFrom } from "../hex/index.js"; import { mol } from "../molecule/index.js"; import { HASH_TYPES, @@ -97,6 +98,57 @@ export function hashTypeFromBytes(bytes: BytesLike): HashType { return NUM_TO_HASH_TYPE[bytesFrom(bytes)[0]]; } +/** + * Generate the metadata and script args of a multisig script. + * @public + * + * @param pubkeys - The public keys engaged in the multisig script. + * @param threshold - The threshold of the signatures. + * @param mustMatch - The first nth must match of the public keys. + * @returns The serialized multisig information, known as metadata. + * + * @example + * ```typescript + * const { metadata, scriptArgs } = multisigMetadataFromPubkeys({ + * pubkeys: ["0x1234...", "0x5678...", "0x90ab...", "0xabcd..."], + * threshold: 2, + * mustMatch: 1, + * }); + * ``` + */ + +export function multisigMetadataFromPubkeys( + pubkeys: HexLike[], + threshold: number, + mustMatch: number, +): Hex { + if (threshold < 0 || threshold > 255) { + throw new Error("`threshold` must be positive and less than 256!"); + } + if (mustMatch < 0 || mustMatch > 255) { + throw new Error("`mustMatch` must be positive and less than 256!"); + } + if ( + pubkeys.length < mustMatch || + pubkeys.length < threshold || + pubkeys.length > 255 + ) { + throw new Error( + "length of `pubkeys` must be greater than or equal to `mustMatch` and `threshold` and less than 256!", + ); + } + const pubkeyBlake160Hashes = pubkeys.map((pubkey) => + hashCkb(hexFrom(pubkey)).slice(0, 20), + ); + return hexConcat( + "0x00", + hexFrom(("00" + mustMatch.toString(16)).slice(-2)), + hexFrom(("00" + threshold.toString(16)).slice(-2)), + hexFrom(("00" + pubkeyBlake160Hashes.length.toString(16)).slice(-2)), + ...pubkeyBlake160Hashes.map((h) => h.slice(2)), + ); +} + /** * @public */ @@ -225,10 +277,57 @@ export class Script extends mol.Entity.Base() { } /** - * Converts the Script instance to molecule data format. + * Creates a Script instance from known multisig script. + * + * @param client - A Client instance. + * @param metadata - The metadata of the multisig script. + * @param since - The since of the multisig script. + * @param multisigScript - A KnownScript enum. + * @returns A promise that resolves to the script instance. * - * @returns An object representing the script in molecule data format. + * @example + * ```typescript + * const metadata = multisigMetadataFromPubkeys( + * ["0x1234...", "0x5678...", "0x90ab..."], + * 2, + * 1, + * ); + * const script = await Script.fromKnownMultisigScript( + * client, + * metadata, + * { + * relative: "absolute", + * metric: "blockNumber", + * value: 1000, + * } + * ); + * ``` */ + + static async fromKnownMultisigScript( + client: Client, + metadata: HexLike, + since?: SinceLike, + multisigScript: + | KnownScript.Secp256k1Multisig + | KnownScript.Secp256k1MultisigV2 = KnownScript.Secp256k1MultisigV2, + ): Promise