{"version":3,"file":"barrel.mjs","names":[],"sources":["../src/signer.ts","../src/signersFactory.ts","../src/barrel.ts"],"sourcesContent":["import { ccc } from \"@ckb-ccc/core\";\nimport { Psbt } from \"bitcoinjs-lib\";\nimport * as v from \"valibot\";\nimport {\n  Address,\n  AddressPurpose,\n  BtcProvider,\n  MessageSigningProtocols,\n  Requests,\n  Return,\n  RpcErrorCode,\n  RpcResponse,\n  rpcErrorResponseMessageSchema,\n  rpcSuccessResponseMessageSchema,\n} from \"./advancedBarrel.js\";\n\nasync function checkResponse<T extends keyof Requests>(\n  response: Promise<RpcResponse<T>>,\n): Promise<Return<T>> {\n  const res = await response;\n  if (v.is(rpcErrorResponseMessageSchema, res)) {\n    // eslint-disable-next-line @typescript-eslint/only-throw-error\n    throw res.error;\n  }\n\n  if (v.is(rpcSuccessResponseMessageSchema, res)) {\n    return res.result;\n  }\n\n  // eslint-disable-next-line @typescript-eslint/only-throw-error\n  throw {\n    code: RpcErrorCode.INTERNAL_ERROR,\n    message: \"Received unknown response from provider.\",\n    data: res,\n  };\n}\n\n/**\n * Class representing a Bitcoin signer that extends SignerBtc\n * @public\n */\nexport class Signer extends ccc.SignerBtc {\n  private addressCache: Promise<Address | undefined> | undefined;\n\n  /**\n   * Creates an instance of Signer.\n   * @param client - The client instance.\n   * @param provider - The provider instance.\n   */\n  constructor(\n    client: ccc.Client,\n    public readonly provider: BtcProvider,\n    private readonly preferredNetworks: ccc.NetworkPreference[] = [\n      {\n        addressPrefix: \"ckb\",\n        signerType: ccc.SignerType.BTC,\n        network: \"btc\",\n      },\n      {\n        addressPrefix: \"ckt\",\n        signerType: ccc.SignerType.BTC,\n        network: \"btcTestnet\",\n      },\n    ],\n  ) {\n    super(client);\n  }\n\n  async assertAddress(): Promise<Address> {\n    this.addressCache =\n      this.addressCache ??\n      (async () => {\n        if (!(await this.isConnected())) {\n          return;\n        }\n\n        return (\n          await checkResponse(\n            this.provider.request(\"getAddresses\", {\n              purposes: [AddressPurpose.Payment],\n            }),\n          )\n        ).addresses[0];\n      })();\n    const address = await this.addressCache;\n\n    if (address) {\n      return address;\n    }\n    throw Error(\"Not connected\");\n  }\n\n  /**\n   * Gets the Bitcoin account address.\n   * @returns A promise that resolves to the Bitcoin account address.\n   */\n  async getBtcAccount(): Promise<string> {\n    return (await this.assertAddress()).address;\n  }\n\n  /**\n   * Gets the Bitcoin public key.\n   * @returns A promise that resolves to the Bitcoin public key.\n   */\n  async getBtcPublicKey(): Promise<ccc.Hex> {\n    return ccc.hexFrom((await this.assertAddress()).publicKey);\n  }\n\n  /**\n   * Connects to the provider by requesting accounts.\n   * @returns A promise that resolves when the connection is established.\n   */\n  async connect(): Promise<void> {\n    if (await this.isConnected()) {\n      return;\n    }\n\n    await checkResponse(\n      this.provider.request(\"wallet_requestPermissions\", undefined),\n    );\n  }\n\n  async disconnect(): Promise<void> {\n    this.addressCache = undefined;\n  }\n\n  onReplaced(listener: () => void): () => void {\n    const stop: (() => void)[] = [];\n    const replacer = () => {\n      listener();\n      stop[0]?.();\n    };\n    stop.push(\n      this.provider.addListener(\"accountChange\", replacer),\n      this.provider.addListener(\"networkChange\", replacer),\n    );\n\n    return stop[0];\n  }\n\n  /**\n   * Checks if the signer is connected.\n   * @returns A promise that resolves to true if connected, false otherwise.\n   */\n  async isConnected(): Promise<boolean> {\n    try {\n      await checkResponse(this.provider.request(\"getBalance\", undefined));\n      return true;\n    } catch (_error) {\n      return false;\n    }\n  }\n\n  /**\n   * Signs a raw message with the Bitcoin account.\n   * @param message - The message to sign.\n   * @returns A promise that resolves to the signed message.\n   */\n  async signMessageRaw(message: string | ccc.BytesLike): Promise<string> {\n    const challenge =\n      typeof message === \"string\" ? message : ccc.hexFrom(message).slice(2);\n\n    return (\n      await checkResponse(\n        this.provider.request(\"signMessage\", {\n          message: challenge,\n          address: (await this.assertAddress()).address,\n          protocol: MessageSigningProtocols.ECDSA,\n        }),\n      )\n    ).signature;\n  }\n\n  /**\n   * Build default inputsToSign for all unsigned inputs\n   */\n  private buildDefaultinputsToSign(\n    psbtHex: ccc.Hex,\n    address: string,\n  ): ccc.InputToSignLike[] {\n    const inputsToSign: ccc.InputToSignLike[] = [];\n\n    try {\n      // Collect all unsigned inputs\n      const psbt = Psbt.fromHex(psbtHex.slice(2));\n      psbt.data.inputs.forEach((input, index) => {\n        const isSigned =\n          input.finalScriptSig ||\n          input.finalScriptWitness ||\n          input.tapKeySig ||\n          (input.partialSig && input.partialSig.length > 0) ||\n          (input.tapScriptSig && input.tapScriptSig.length > 0);\n\n        if (!isSigned) {\n          inputsToSign.push({ index, address });\n        }\n      });\n\n      // If no unsigned inputs found, the PSBT is already fully signed\n      // Let the wallet handle this case (likely a no-op or error)\n    } catch (error) {\n      const errorMessage =\n        error instanceof Error ? error.message : String(error);\n      throw new Error(\n        `Failed to parse PSBT hex. Please provide inputsToSign explicitly in options. Original error: ${errorMessage}`,\n        { cause: error },\n      );\n    }\n\n    return inputsToSign;\n  }\n\n  private async prepareSignPsbtParams(\n    psbtHex: ccc.Hex,\n    options?: ccc.SignPsbtOptionsLike,\n  ): Promise<{\n    psbtBase64: string;\n    signInputs: Record<string, number[]>;\n  }> {\n    let inputsToSign = options?.inputsToSign;\n\n    if (!inputsToSign || !inputsToSign.length) {\n      const address = await this.getBtcAccount();\n      inputsToSign = this.buildDefaultinputsToSign(psbtHex, address);\n    }\n\n    const psbtBase64 = ccc.bytesTo(psbtHex, \"base64\");\n\n    const signInputs = inputsToSign.reduce(\n      (acc, input) => {\n        if (!input.address) {\n          throw new Error(\n            \"Xverse only supports signing with address. Please provide 'address' in inputsToSign.\",\n          );\n        }\n        if (acc[input.address]) {\n          acc[input.address].push(input.index);\n        } else {\n          acc[input.address] = [input.index];\n        }\n        return acc;\n      },\n      {} as Record<string, number[]>,\n    );\n\n    return { psbtBase64, signInputs };\n  }\n\n  /**\n   * Signs a PSBT using Xverse wallet.\n   *\n   * @param psbtHex - The hex string of PSBT to sign.\n   * @param options - Options for signing the PSBT\n   * @returns A promise that resolves to the signed PSBT as a Hex string\n   *\n   * @remarks\n   * Xverse accepts:\n   * - psbt: A string representing the PSBT to sign, encoded in base64\n   * - signInputs: A Record<string, number[]> where:\n   *   - keys are the addresses to use for signing\n   *   - values are the indexes of the inputs to sign with each address\n   *\n   * Xverse returns:\n   * - psbt: The base64 encoded signed PSBT\n   *\n   * @see https://docs.xverse.app/sats-connect/bitcoin-methods/signpsbt\n   */\n  async signPsbt(\n    psbtHex: ccc.HexLike,\n    options?: ccc.SignPsbtOptionsLike,\n  ): Promise<ccc.Hex> {\n    const { psbtBase64, signInputs } = await this.prepareSignPsbtParams(\n      ccc.hexFrom(psbtHex),\n      options,\n    );\n\n    const signedPsbtBase64 = (\n      await checkResponse(\n        this.provider.request(\"signPsbt\", {\n          psbt: psbtBase64,\n          signInputs,\n          broadcast: false,\n        }),\n      )\n    ).psbt;\n\n    return ccc.hexFrom(ccc.bytesFrom(signedPsbtBase64, \"base64\"));\n  }\n\n  /**\n   * Broadcasts a PSBT to the Bitcoin network.\n   *\n   * @remarks\n   * Xverse does not support broadcasting a signed PSBT directly.\n   * It only supports \"Sign and Broadcast\" as a single atomic operation via `signAndBroadcastPsbt`.\n   */\n  async broadcastPsbt(\n    _psbtHex: ccc.HexLike,\n    _options?: ccc.SignPsbtOptionsLike,\n  ): Promise<ccc.Hex> {\n    throw new Error(\n      \"Xverse does not support broadcasting signed PSBTs directly. Use signAndBroadcastPsbt instead.\",\n    );\n  }\n\n  async signAndBroadcastPsbt(\n    psbtHex: ccc.HexLike,\n    options?: ccc.SignPsbtOptionsLike,\n  ): Promise<ccc.Hex> {\n    // ccc.hexFrom adds 0x prefix, but BTC expects non-0x\n    const { psbtBase64, signInputs } = await this.prepareSignPsbtParams(\n      ccc.hexFrom(psbtHex),\n      options,\n    );\n\n    const result = await checkResponse(\n      this.provider.request(\"signPsbt\", {\n        psbt: psbtBase64,\n        signInputs,\n        broadcast: true,\n      }),\n    );\n\n    if (!result.txid) {\n      throw new Error(\"Failed to broadcast PSBT\");\n    }\n\n    return ccc.hexFrom(result.txid);\n  }\n}\n","import { ccc } from \"@ckb-ccc/core\";\nimport { BtcProvider, Provider } from \"./advancedBarrel.js\";\nimport { Signer } from \"./signer.js\";\n\nfunction getProviderById(providerId: string) {\n  return (\n    providerId\n      ?.split(\".\")\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return\n      .reduce((acc: any, part) => acc?.[part], window) as BtcProvider\n  );\n}\n\n/**\n * Retrieves the Xverse signer if available.\n * @public\n *\n * @param client - The client instance.\n * @returns All Xverse Signer instances\n */\nexport function getXverseSigners(\n  client: ccc.Client,\n  preferredNetworks?: ccc.NetworkPreference[],\n): { wallet: ccc.Wallet; signerInfo: ccc.SignerInfo }[] {\n  const windowRef = window as {\n    BitcoinProvider?: BtcProvider;\n    XverseProviders?: {\n      BitcoinProvider?: BtcProvider;\n    };\n    btc_providers?: Provider[];\n  };\n\n  const signers = (() => {\n    if (windowRef.btc_providers) {\n      return windowRef.btc_providers.map((provider) => ({\n        wallet: {\n          name: provider.name,\n          icon: provider.icon,\n        },\n        signerInfo: {\n          name: \"BTC\",\n          signer: new Signer(\n            client,\n            getProviderById(provider.id),\n            preferredNetworks,\n          ),\n        },\n      }));\n    }\n\n    return [];\n  })();\n\n  return signers;\n}\n","export * from \"./signer.js\";\nexport * from \"./signersFactory.js\";\n"],"mappings":"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"}