diff --git a/packages/core/src/signer/btc/index.ts b/packages/core/src/signer/btc/index.ts index d0aa15884..694ff7250 100644 --- a/packages/core/src/signer/btc/index.ts +++ b/packages/core/src/signer/btc/index.ts @@ -1,3 +1,4 @@ +export * from "./psbt.js"; export * from "./signerBtc.js"; export * from "./signerBtcPublicKeyReadonly.js"; export * from "./verify.js"; diff --git a/packages/core/src/signer/btc/psbt.ts b/packages/core/src/signer/btc/psbt.ts new file mode 100644 index 000000000..77ac646f7 --- /dev/null +++ b/packages/core/src/signer/btc/psbt.ts @@ -0,0 +1,56 @@ +/** + * Options for signing a PSBT (Partially Signed Bitcoin Transaction) + */ +export type SignPsbtOptions = { + /** + * Whether to finalize the PSBT after signing. + * Default is true. + */ + autoFinalized?: boolean; + /** + * Array of inputs to sign + */ + toSignInputs?: ToSignInput[]; +}; + +/** + * Specification for an input to sign in a PSBT. + * Must specify at least one of: address or pubkey. + */ +export type ToSignInput = { + /** + * Which input to sign (index in the PSBT inputs array) + */ + index: number; + /** + * (Optional) Sighash types to use for signing. + */ + sighashTypes?: number[]; + /** + * (Optional) When signing and unlocking Taproot addresses, the tweakSigner is used by default + * for signature generation. Setting this to true allows for signing with the original private key. + * Default value is false. + */ + disableTweakSigner?: boolean; +} & ( + | { + /** + * The address whose corresponding private key to use for signing. + */ + address: string; + /** + * The public key whose corresponding private key to use for signing. + */ + publicKey?: string; + } + | { + /** + * The address whose corresponding private key to use for signing. + */ + address?: string; + /** + * The public key whose corresponding private key to use for signing. + */ + publicKey: string; + } +); diff --git a/packages/core/src/signer/btc/signerBtc.ts b/packages/core/src/signer/btc/signerBtc.ts index 64112a74a..b450feefb 100644 --- a/packages/core/src/signer/btc/signerBtc.ts +++ b/packages/core/src/signer/btc/signerBtc.ts @@ -2,9 +2,10 @@ import { Address } from "../../address/index.js"; import { bytesConcat, bytesFrom } from "../../bytes/index.js"; import { Transaction, TransactionLike, WitnessArgs } from "../../ckb/index.js"; import { KnownScript } from "../../client/index.js"; -import { HexLike, hexFrom } from "../../hex/index.js"; +import { Hex, HexLike, hexFrom } from "../../hex/index.js"; import { numToBytes } from "../../num/index.js"; import { Signer, SignerSignType, SignerType } from "../signer/index.js"; +import { SignPsbtOptions } from "./psbt.js"; import { btcEcdsaPublicKeyHash } from "./verify.js"; /** @@ -22,6 +23,21 @@ export abstract class SignerBtc extends Signer { return SignerSignType.BtcEcdsa; } + /** + * Sign and broadcast a PSBT. + * + * @param psbtHex - The hex string of PSBT to sign and broadcast. + * @param options - Options for signing the PSBT. + * @returns A promise that resolves to the transaction ID as a Hex string. + */ + async signAndBroadcastPsbt( + psbtHex: HexLike, + options?: SignPsbtOptions, + ): Promise { + const signedPsbt = await this.signPsbt(psbtHex, options); + return this.broadcastPsbt(signedPsbt, options); + } + /** * Gets the Bitcoin account associated with the signer. * @@ -123,4 +139,27 @@ export abstract class SignerBtc extends Signer { tx.setWitnessArgsAt(info.position, witness); return tx; } + + /** + * Signs a Partially Signed Bitcoin Transaction (PSBT). + * + * @param psbtHex - The hex string of PSBT to sign. + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT as a Hex string. + */ + abstract signPsbt(psbtHex: HexLike, options?: SignPsbtOptions): Promise; + + /** + * Broadcasts a PSBT to the Bitcoin network. + * + * @param psbtHex - The hex string of the PSBT to broadcast. + * @param options - Options for broadcasting the PSBT. + * @returns A promise that resolves to the transaction ID as a Hex string. + */ + async broadcastPsbt( + _psbtHex: HexLike, + _options?: SignPsbtOptions, + ): Promise { + throw new Error("Not implemented"); + } } diff --git a/packages/core/src/signer/btc/signerBtcPublicKeyReadonly.ts b/packages/core/src/signer/btc/signerBtcPublicKeyReadonly.ts index 50096db7e..1b2a403d6 100644 --- a/packages/core/src/signer/btc/signerBtcPublicKeyReadonly.ts +++ b/packages/core/src/signer/btc/signerBtcPublicKeyReadonly.ts @@ -1,5 +1,6 @@ import { Client } from "../../client/index.js"; import { Hex, HexLike, hexFrom } from "../../hex/index.js"; +import { SignPsbtOptions } from "./psbt.js"; import { SignerBtc } from "./signerBtc.js"; /** @@ -70,4 +71,15 @@ export class SignerBtcPublicKeyReadonly extends SignerBtc { async getBtcPublicKey(): Promise { return this.publicKey; } + + async signPsbt(_psbtHex: HexLike, _options?: SignPsbtOptions): Promise { + throw new Error("Read-only signer does not support signPsbt"); + } + + async broadcastPsbt( + _psbtHex: HexLike, + _options?: SignPsbtOptions, + ): Promise { + throw new Error("Read-only signer does not support broadcastPsbt"); + } } diff --git a/packages/joy-id/src/btc/index.ts b/packages/joy-id/src/btc/index.ts index 4a1e92fad..b4efe236b 100644 --- a/packages/joy-id/src/btc/index.ts +++ b/packages/joy-id/src/btc/index.ts @@ -198,4 +198,78 @@ export class BitcoinSigner extends ccc.SignerBtc { ); return signature; } + + /** + * Signs a PSBT using JoyID wallet. + * + * @param psbtHex - The hex string of PSBT to sign. + * @returns A promise that resolves to the signed PSBT as a Hex string. + */ + async signPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + const { address } = await this.assertConnection(); + + const config = this.getConfig(); + const { tx: signedPsbtHex } = await createPopup( + buildJoyIDURL( + { + ...config, + tx: ccc.hexFrom(psbtHex).slice(2), + options, + signerAddress: address, + autoFinalized: options?.autoFinalized ?? true, + }, + "popup", + "/sign-psbt", + ), + { ...config, type: DappRequestType.SignPsbt }, + ); + + return ccc.hexFrom(signedPsbtHex); + } + + /** + * Broadcasts a PSBT to the Bitcoin network. + * + * @remarks + * JoyID does not support broadcasting a signed PSBT directly. + * It only supports "Sign and Broadcast" as a single atomic operation via `signAndBroadcastPsbt`. + */ + async broadcastPsbt( + _psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + throw new Error( + "JoyID does not support broadcasting signed PSBTs directly. Use signAndBroadcastPsbt instead.", + ); + } + + async signAndBroadcastPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + const { address } = await this.assertConnection(); + + const config = this.getConfig(); + // ccc.hexFrom adds 0x prefix, but BTC expects non-0x + const { tx: txid } = await createPopup( + buildJoyIDURL( + { + ...config, + tx: ccc.hexFrom(psbtHex).slice(2), + options, + signerAddress: address, + autoFinalized: true, // sendPsbt always finalizes + isSend: true, + }, + "popup", + "/sign-psbt", + ), + { ...config, type: DappRequestType.SignPsbt }, // Use SignPsbt type for both operations + ); + + return ccc.hexFrom(txid); + } } diff --git a/packages/okx/src/advancedBarrel.ts b/packages/okx/src/advancedBarrel.ts index 4704b662f..bdd6b3e04 100644 --- a/packages/okx/src/advancedBarrel.ts +++ b/packages/okx/src/advancedBarrel.ts @@ -2,8 +2,21 @@ import { Nip07A } from "@ckb-ccc/nip07/advanced"; import { UniSatA } from "@ckb-ccc/uni-sat/advanced"; export interface BitcoinProvider - extends Pick, - Partial> { + extends Pick< + UniSatA.Provider, + "on" | "removeListener" | "signMessage" | "signPsbt" | "pushPsbt" + >, + Partial< + Omit< + UniSatA.Provider, + | "on" + | "removeListener" + | "signMessage" + | "signPsbt" + | "pushPsbt" + | "pushTx" + > + > { connect?(): Promise<{ address: string; publicKey: string; diff --git a/packages/okx/src/btc/index.ts b/packages/okx/src/btc/index.ts index c35b9a480..3497e79fd 100644 --- a/packages/okx/src/btc/index.ts +++ b/packages/okx/src/btc/index.ts @@ -176,4 +176,34 @@ export class BitcoinSigner extends ccc.SignerBtc { return this.provider.signMessage(challenge, "ecdsa"); } + + /** + * Signs a PSBT using OKX wallet. + * + * @param psbtHex - The hex string of PSBT to sign. + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT as a Hex string + */ + async signPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + return ccc.hexFrom( + await this.provider.signPsbt(ccc.hexFrom(psbtHex).slice(2), options), + ); + } + + /** + * Broadcasts a signed PSBT to the Bitcoin network. + * + * @param psbtHex - The hex string (without 0x prefix) of signed PSBT to broadcast. + * @returns A promise that resolves to the transaction ID (without 0x prefix) + */ + async broadcastPsbt( + psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + const txid = await this.provider.pushPsbt(ccc.hexFrom(psbtHex).slice(2)); + return ccc.hexFrom(txid); + } } diff --git a/packages/uni-sat/src/advancedBarrel.ts b/packages/uni-sat/src/advancedBarrel.ts index e6ae56b58..8ff7e0c04 100644 --- a/packages/uni-sat/src/advancedBarrel.ts +++ b/packages/uni-sat/src/advancedBarrel.ts @@ -1,7 +1,26 @@ +import { ccc } from "@ckb-ccc/core"; + /** * Interface representing a provider for interacting with accounts and signing messages. */ export interface Provider { + /** + * Signs a PSBT using UniSat wallet. + * + * @param psbtHex - The hex string of PSBT to sign + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT hex string + */ + signPsbt(psbtHex: string, options?: ccc.SignPsbtOptions): Promise; + + /** + * Broadcasts a signed PSBT to the Bitcoin network. + * + * @param psbtHex - The hex string of the signed PSBT to broadcast. + * @returns A promise that resolves to the transaction ID. + */ + pushPsbt(psbtHex: string): Promise; + /** * Requests user accounts. * @returns A promise that resolves to an array of account addresses. diff --git a/packages/uni-sat/src/signer.ts b/packages/uni-sat/src/signer.ts index 653bba8ee..333683f58 100644 --- a/packages/uni-sat/src/signer.ts +++ b/packages/uni-sat/src/signer.ts @@ -150,4 +150,34 @@ export class Signer extends ccc.SignerBtc { return this.provider.signMessage(challenge, "ecdsa"); } + + /** + * Signs a PSBT using UniSat wallet. + * + * @param psbtHex - The hex string of PSBT to sign. + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT as a Hex string + */ + async signPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + return ccc.hexFrom( + await this.provider.signPsbt(ccc.hexFrom(psbtHex).slice(2), options), + ); + } + + /** + * Broadcasts a signed PSBT to the Bitcoin network. + * + * @param psbtHex - The hex string (without 0x prefix) of signed PSBT to broadcast. + * @returns A promise that resolves to the transaction ID (without 0x prefix) + */ + async broadcastPsbt( + psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + const txid = await this.provider.pushPsbt(ccc.hexFrom(psbtHex).slice(2)); + return ccc.hexFrom(txid); + } } diff --git a/packages/utxo-global/src/btc/index.ts b/packages/utxo-global/src/btc/index.ts index 57e73594d..7aee417cd 100644 --- a/packages/utxo-global/src/btc/index.ts +++ b/packages/utxo-global/src/btc/index.ts @@ -127,4 +127,32 @@ export class SignerBtc extends ccc.SignerBtc { this.accountCache ?? (await this.getBtcAccount()), ); } + + /** + * Signs a PSBT using UTXO Global wallet. + * + * @param psbtHex - The hex string of PSBT to sign. + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT as a Hex string + */ + async signPsbt( + _psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + throw new Error("UTXO Global PSBT signing not implemented yet"); + } + + /** + * Broadcasts a signed PSBT to the Bitcoin network. + * + * @param psbtHex - The hex string of signed PSBT to broadcast. + * @returns A promise that resolves to the transaction ID as a Hex string + * @todo Implement PSBT broadcasting with UTXO Global + */ + async broadcastPsbt( + _psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + throw new Error("UTXO Global PSBT broadcasting not implemented yet"); + } } diff --git a/packages/xverse/package.json b/packages/xverse/package.json index 6dcfcc970..21f9ae545 100644 --- a/packages/xverse/package.json +++ b/packages/xverse/package.json @@ -57,6 +57,7 @@ }, "dependencies": { "@ckb-ccc/core": "workspace:*", + "bitcoinjs-lib": "^7.0.0", "valibot": "^1.1.0" }, "packageManager": "pnpm@10.8.1" diff --git a/packages/xverse/src/signer.ts b/packages/xverse/src/signer.ts index af243a11a..441fdc0cc 100644 --- a/packages/xverse/src/signer.ts +++ b/packages/xverse/src/signer.ts @@ -1,4 +1,5 @@ import { ccc } from "@ckb-ccc/core"; +import { Psbt } from "bitcoinjs-lib"; import * as v from "valibot"; import { Address, @@ -169,4 +170,159 @@ export class Signer extends ccc.SignerBtc { ) ).signature; } + + /** + * Build default toSignInputs for all unsigned inputs + */ + private buildDefaultToSignInputs( + psbtHex: ccc.Hex, + address: string, + ): ccc.ToSignInput[] { + const toSignInputs: ccc.ToSignInput[] = []; + + try { + // Collect all unsigned inputs + const psbt = Psbt.fromHex(psbtHex.slice(2)); + psbt.data.inputs.forEach((input, index) => { + const isSigned = + input.finalScriptSig || + input.finalScriptWitness || + input.tapKeySig || + (input.partialSig && input.partialSig.length > 0) || + (input.tapScriptSig && input.tapScriptSig.length > 0); + + if (!isSigned) { + toSignInputs.push({ index, address }); + } + }); + + // If no unsigned inputs found, the PSBT is already fully signed + // Let the wallet handle this case (likely a no-op or error) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new Error( + `Failed to parse PSBT hex. Please provide toSignInputs explicitly in options. Original error: ${errorMessage}`, + ); + } + + return toSignInputs; + } + + private async prepareSignPsbtParams( + psbtHex: ccc.Hex, + options?: ccc.SignPsbtOptions, + ): Promise<{ + psbtBase64: string; + signInputs: Record; + }> { + let toSignInputs = options?.toSignInputs; + if (!toSignInputs || !toSignInputs.length) { + const address = await this.getBtcAccount(); + toSignInputs = this.buildDefaultToSignInputs(psbtHex, address); + } + + const psbtBase64 = ccc.bytesTo(psbtHex, "base64"); + + const signInputs = toSignInputs.reduce( + (acc, input) => { + if (!input.address) { + throw new Error( + "Xverse only supports signing with address. Please provide 'address' in toSignInputs.", + ); + } + if (acc[input.address]) { + acc[input.address].push(input.index); + } else { + acc[input.address] = [input.index]; + } + return acc; + }, + {} as Record, + ); + + return { psbtBase64, signInputs }; + } + + /** + * Signs a PSBT using Xverse wallet. + * + * @param psbtHex - The hex string of PSBT to sign. + * @param options - Options for signing the PSBT + * @returns A promise that resolves to the signed PSBT as a Hex string + * + * @remarks + * Xverse accepts: + * - psbt: A string representing the PSBT to sign, encoded in base64 + * - signInputs: A Record where: + * - keys are the addresses to use for signing + * - values are the indexes of the inputs to sign with each address + * + * Xverse returns: + * - psbt: The base64 encoded signed PSBT + * + * @see https://docs.xverse.app/sats-connect/bitcoin-methods/signpsbt + */ + async signPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + const { psbtBase64, signInputs } = await this.prepareSignPsbtParams( + ccc.hexFrom(psbtHex), + options, + ); + + const signedPsbtBase64 = ( + await checkResponse( + this.provider.request("signPsbt", { + psbt: psbtBase64, + signInputs, + broadcast: false, + }), + ) + ).psbt; + + return ccc.hexFrom(ccc.bytesFrom(signedPsbtBase64, "base64")); + } + + /** + * Broadcasts a PSBT to the Bitcoin network. + * + * @remarks + * Xverse does not support broadcasting a signed PSBT directly. + * It only supports "Sign and Broadcast" as a single atomic operation via `signAndBroadcastPsbt`. + */ + async broadcastPsbt( + _psbtHex: ccc.HexLike, + _options?: ccc.SignPsbtOptions, + ): Promise { + throw new Error( + "Xverse does not support broadcasting signed PSBTs directly. Use signAndBroadcastPsbt instead.", + ); + } + + async signAndBroadcastPsbt( + psbtHex: ccc.HexLike, + options?: ccc.SignPsbtOptions, + ): Promise { + // ccc.hexFrom adds 0x prefix, but BTC expects non-0x + const { psbtBase64, signInputs } = await this.prepareSignPsbtParams( + ccc.hexFrom(psbtHex), + options, + ); + + const result = await checkResponse( + this.provider.request("signPsbt", { + psbt: psbtBase64, + signInputs, + broadcast: true, + }), + ); + + if (!result.txid) { + throw new Error("Failed to broadcast PSBT"); + } + + return ccc.hexFrom(result.txid); + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7fa2ac90a..704be8ca0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1238,6 +1238,9 @@ importers: '@ckb-ccc/core': specifier: workspace:* version: link:../core + bitcoinjs-lib: + specifier: ^7.0.0 + version: 7.0.0(typescript@5.9.2) valibot: specifier: ^1.1.0 version: 1.1.0(typescript@5.9.2) @@ -4993,6 +4996,14 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bip174@3.0.0: + resolution: {integrity: sha512-N3vz3rqikLEu0d6yQL8GTrSkpYb35NQKWMR7Hlza0lOj6ZOlvQ3Xr7N9Y+JPebaCVoEUHdBeBSuLxcHr71r+Lw==} + engines: {node: '>=18.0.0'} + + bitcoinjs-lib@7.0.0: + resolution: {integrity: sha512-2W6dGXFd1KG3Bs90Bzb5+ViCeSKNIYkCUWZ4cvUzUgwnneiNNZ6Sk8twGNcjlesmxC0JyLc/958QycfpvXLg7A==} + engines: {node: '>=18.0.0'} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -10107,6 +10118,14 @@ packages: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} + uint8array-tools@0.0.8: + resolution: {integrity: sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==} + engines: {node: '>=14.0.0'} + + uint8array-tools@0.0.9: + resolution: {integrity: sha512-9vqDWmoSXOoi+K14zNaf6LBV51Q8MayF0/IiQs3GlygIKUYtog603e6virExkjjFosfJUBI4LhbQK1iq8IG11A==} + engines: {node: '>=14.0.0'} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -10240,6 +10259,14 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + valibot@0.38.0: + resolution: {integrity: sha512-RCJa0fetnzp+h+KN9BdgYOgtsMAG9bfoJ9JSjIhFHobKWVWyzM3jjaeNTdpFK9tQtf3q1sguXeERJ/LcmdFE7w==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + valibot@1.1.0: resolution: {integrity: sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==} peerDependencies: @@ -10258,6 +10285,9 @@ packages: value-equal@1.0.1: resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} + varuint-bitcoin@2.0.0: + resolution: {integrity: sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog==} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -15588,6 +15618,23 @@ snapshots: binary-extensions@2.3.0: {} + bip174@3.0.0: + dependencies: + uint8array-tools: 0.0.9 + varuint-bitcoin: 2.0.0 + + bitcoinjs-lib@7.0.0(typescript@5.9.2): + dependencies: + '@noble/hashes': 1.8.0 + bech32: 2.0.0 + bip174: 3.0.0 + bs58check: 4.0.0(patch_hash=0848a2e3956f24abf1dd8620cba2a3f468393e489185d9536ad109f7e5712d26) + uint8array-tools: 0.0.9 + valibot: 0.38.0(typescript@5.9.2) + varuint-bitcoin: 2.0.0 + transitivePeerDependencies: + - typescript + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -21869,6 +21916,10 @@ snapshots: uint8array-extras@1.5.0: {} + uint8array-tools@0.0.8: {} + + uint8array-tools@0.0.9: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -22031,6 +22082,10 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + valibot@0.38.0(typescript@5.9.2): + optionalDependencies: + typescript: 5.9.2 + valibot@1.1.0(typescript@5.9.2): optionalDependencies: typescript: 5.9.2 @@ -22044,6 +22099,10 @@ snapshots: value-equal@1.0.1: {} + varuint-bitcoin@2.0.0: + dependencies: + uint8array-tools: 0.0.8 + vary@1.1.2: {} vfile-location@5.0.3: