{"version":3,"file":"LeatherWalletAdapter.impl-EzOvy-kh.mjs","sources":["../src/adapters/LeatherWalletAdapter.impl.ts"],"sourcesContent":["import type {\n  RequestFn,\n  RpcEndpointMap,\n  RpcErrorBody,\n  RpcErrorResponse,\n} from \"@leather.io/rpc\"\nimport { hex } from \"@scure/base\"\nimport * as btc from \"@scure/btc-signer\"\nimport { LOCAL_STORAGE_KEY_PREFIX } from \"../constants\"\nimport {\n  addressToScriptPubKey,\n  getTapInternalKeyOf_P2TR_publicKey,\n} from \"../utils/bitcoinAddressHelpers\"\nimport { getBitcoinNetwork } from \"../utils/bitcoinNetworkHelpers\"\nimport { BitcoinWalletAdapterError, UserRejectError } from \"../utils/error\"\nimport {\n  SignMessageAlgorithm,\n  SignMessageResult,\n  WalletAdapter,\n  WalletAdapter_onAddressesChanged_callback,\n  WalletAdapterAddress,\n  WalletAdapterAddressPurpose,\n  WalletAdapterAddressType,\n  WalletAdapterBitcoinNetwork,\n  WalletAdapterNotConnectedError,\n} from \"../WalletAdapters.types\"\nimport { adapterId, metadata } from \"./LeatherWalletAdapter\"\n\ntype PickSuccessResponse<T> = T extends { result: unknown } ? T : never\n\ntype GetAddressesResponseData = PickSuccessResponse<\n  RpcEndpointMap[\"getAddresses\"][\"response\"]\n>\n\n/**\n * https://github.com/leather-io/mono/blob/a664e64040fed1c25abef1f8864a1c7fae5444c1/packages/rpc/src/rpc/schemas.ts#L64-L78\n */\nenum RpcErrorCode {\n  // Spec defined server errors\n  PARSE_ERROR = -32700,\n  INVALID_REQUEST = -32600,\n  METHOD_NOT_FOUND = -32601,\n  INVALID_PARAMS = -32602,\n  INTERNAL_ERROR = -32603,\n  SERVER_ERROR = -32000,\n  // Client defined errors\n  USER_REJECTION = 4001,\n  METHOD_NOT_SUPPORTED = 4002,\n  // gRPC spec borrowed\n  PERMISSION_DENIED = 7,\n  UNAUTHENTICATED = 16,\n}\n\ntype Addresses = (WalletAdapterAddress & { publicKey: string })[]\n\nconst localStorageKeyPrefix = `${LOCAL_STORAGE_KEY_PREFIX}:${adapterId}`\nconst connectedAddress_localStorageKey = `${localStorageKeyPrefix}:connectedAddress`\n\n/**\n * Derivation path (BIP-84): m/84'/0'/ account_index '/0/0\n */\nexport class LeatherWalletAdapterImpl implements WalletAdapter {\n  constructor(private request: RequestFn) {}\n\n  private retrieveConnectedAddress_inMemory:\n    | GetAddressesResponseData\n    | undefined\n  private retrieveConnectedAddress(): GetAddressesResponseData | undefined {\n    if (this.retrieveConnectedAddress_inMemory != null) {\n      return this.retrieveConnectedAddress_inMemory\n    }\n\n    let resp: GetAddressesResponseData | undefined\n    const stored =\n      localStorage.getItem(connectedAddress_localStorageKey) || undefined\n    if (stored != null) {\n      try {\n        resp = JSON.parse(stored)\n        if (\n          resp == null ||\n          !(\"addresses\" in resp) ||\n          !Array.isArray(resp.addresses)\n        ) {\n          throw new Error(\"Invalid stored addresses\")\n        }\n        this.retrieveConnectedAddress_inMemory = resp\n      } catch {\n        localStorage.removeItem(connectedAddress_localStorageKey)\n      }\n    }\n\n    return resp\n  }\n\n  async connect(): Promise<void> {\n    if (this.retrieveConnectedAddress() == null) {\n      const resp = await handleRpcError(\n        /**\n         * https://leather.gitbook.io/developers/bitcoin/connect-users/get-addresses\n         */\n        this.request(\"getAddresses\"),\n      )\n      this.retrieveConnectedAddress_inMemory = resp\n      localStorage.setItem(\n        connectedAddress_localStorageKey,\n        JSON.stringify(resp),\n      )\n    }\n  }\n\n  async disconnect(): Promise<void> {\n    this.retrieveConnectedAddress_inMemory = undefined\n    localStorage.removeItem(connectedAddress_localStorageKey)\n    return Promise.resolve()\n  }\n\n  async getAddresses(): Promise<Addresses> {\n    const resp = this.retrieveConnectedAddress()\n\n    if (resp == null) {\n      throw new WalletAdapterNotConnectedError(metadata.name)\n    }\n\n    const addresses = resp.result.addresses.filter(\n      (a: any) => (a as any).symbol === \"BTC\" && a.type,\n    )\n\n    const isMainnet = addresses\n      .filter((a: any) => a.type === \"p2tr\")[0]\n      .address.startsWith(\"bc\")\n\n    return addresses.map((a: any) => ({\n      network: isMainnet\n        ? WalletAdapterBitcoinNetwork.MAINNET\n        : WalletAdapterBitcoinNetwork.TESTNET,\n      purposes:\n        a.type === \"p2tr\"\n          ? [\n              WalletAdapterAddressPurpose.Ordinals,\n              WalletAdapterAddressPurpose.BRC20,\n              WalletAdapterAddressPurpose.Runes,\n            ]\n          : [WalletAdapterAddressPurpose.Bitcoin],\n      addressType:\n        a.type === \"p2tr\"\n          ? WalletAdapterAddressType.P2TR\n          : WalletAdapterAddressType.P2WPKH,\n      address: a.address,\n      scriptPubKey: hex.encode(\n        addressToScriptPubKey(\n          getBitcoinNetwork(isMainnet ? \"mainnet\" : \"testnet\"),\n          a.address,\n        ),\n      ),\n      publicKey: a.publicKey!,\n      tapInternalKey:\n        a.type === \"p2tr\"\n          ? hex.encode(\n              getTapInternalKeyOf_P2TR_publicKey(\n                getBitcoinNetwork(isMainnet ? \"mainnet\" : \"testnet\"),\n                hex.decode(a.publicKey!),\n              ),\n            )\n          : undefined,\n    }))\n  }\n\n  async signMessage(\n    address: string,\n    message: string,\n  ): Promise<SignMessageResult> {\n    const accounts = await this.getAddresses()\n\n    const addressType = accounts.find(a => a.address === address)?.addressType\n\n    // prettier-ignore\n    const paymentType =\n      addressType === WalletAdapterAddressType.P2WPKH ? 'p2wpkh' as const :\n      addressType === WalletAdapterAddressType.P2TR ? 'p2tr' as const :\n      undefined\n    if (paymentType == null) {\n      throw new BitcoinWalletAdapterError(`Address ${address} is not supported`)\n    }\n\n    const resp: any = await handleRpcError(\n      /**\n       * https://leather.gitbook.io/developers/bitcoin-methods/signmessage\n       */\n      this.request(\"signMessage\", {\n        message,\n        paymentType,\n      }),\n    )\n\n    return {\n      algorithm:\n        resp.algorithm === \"ecdsa\"\n          ? SignMessageAlgorithm.ECDSA\n          : SignMessageAlgorithm.BIP322,\n      result: resp.signature,\n      address,\n      publicKey: resp.publicKey,\n    }\n  }\n\n  sendBitcoinFeeRateCapability = \"unavailable\" as const\n  async sendBitcoin(\n    fromAddress: string,\n    receiverAddress: string,\n    satoshiAmount: bigint,\n  ): Promise<{ txid: string }> {\n    const addresses = await this.getAddresses()\n    const foundAddressIndex = addresses.findIndex(\n      a => a.address === fromAddress,\n    )\n    if (foundAddressIndex < 0) {\n      throw new BitcoinWalletAdapterError(\n        `Address ${fromAddress} is not supported`,\n      )\n    }\n\n    const resp: any = await handleRpcError(\n      /**\n       * https://leather.gitbook.io/developers/bitcoin-methods/sendtransfer\n       */\n      this.request(\"sendTransfer\", {\n        recipients: [\n          {\n            address: receiverAddress,\n            amount: satoshiAmount.toString(),\n          },\n        ],\n        account: foundAddressIndex,\n        network:\n          addresses[foundAddressIndex].network ===\n          WalletAdapterBitcoinNetwork.MAINNET\n            ? \"mainnet\"\n            : \"testnet\",\n      }),\n    )\n\n    return { txid: resp.txid }\n  }\n\n  /**\n   * @internal\n   * @experimental\n   */\n  sendInscriptionFeeRateCapability = \"unavailable\" as const\n\n  async signAndFinalizePsbt(\n    psbtHex: string,\n    signIndices: [address: string, signIndex: number][],\n  ): Promise<{\n    signedPsbtHex: string\n  }> {\n    const resp: any = await handleRpcError(\n      /**\n       * https://leather.gitbook.io/developers/bitcoin-methods/signpsbt\n       */\n      this.request(\"signPsbt\", {\n        hex: psbtHex,\n        signAtIndex: signIndices.map(([, signIndex]) => signIndex),\n      }),\n    )\n\n    // signPsbt returns a signed but not finalized PSBT, need to finalize it\n    const tx = btc.Transaction.fromPSBT(hex.decode(resp.hex), {\n      allowUnknownInputs: true,\n      allowUnknownOutputs: true,\n      disableScriptCheck: true,\n      allowLegacyWitnessUtxo: true,\n    })\n    tx.finalize()\n\n    return { signedPsbtHex: hex.encode(tx.toPSBT()) }\n  }\n\n  onAddressesChanged(callback: WalletAdapter_onAddressesChanged_callback): {\n    unsubscribe: () => void\n  } {\n    // Leather doesn't provide event, fallback to polling\n    let stopped = false\n\n    const tick = async (): Promise<void> => {\n      if (stopped) return\n      try {\n        const addresses = await this.getAddresses()\n        callback({ addresses })\n      } catch (error) {\n        if (error instanceof WalletAdapterNotConnectedError) {\n          stopped = true\n          return\n        }\n        console.warn(\"[Leather] Failed to get addresses on change:\", error)\n      } finally {\n        if (!stopped) {\n          setTimeout(tick, 1000)\n        }\n      }\n    }\n\n    setTimeout(tick, 1000)\n\n    return {\n      unsubscribe: () => {\n        stopped = true\n      },\n    }\n  }\n}\n\nexport class LeatherWalletAdapterError extends BitcoinWalletAdapterError {\n  constructor(rpcError: RpcErrorResponse<RpcErrorBody>) {\n    super(\"Leather wallet error: \" + rpcError.error.message, {\n      cause: rpcError,\n    })\n  }\n}\n\nconst handleRpcError = async <T>(\n  promise: Promise<T>,\n): Promise<NonNullable<T>> => {\n  try {\n    return (await promise) as NonNullable<T>\n  } catch (e: any) {\n    if (e instanceof UserRejectError) {\n      throw e\n    }\n\n    if (e.error?.code === RpcErrorCode.USER_REJECTION) {\n      throw new UserRejectError()\n    }\n\n    throw new LeatherWalletAdapterError(e)\n  }\n}\n"],"names":["localStorageKeyPrefix","LOCAL_STORAGE_KEY_PREFIX","adapterId","connectedAddress_localStorageKey","LeatherWalletAdapterImpl","request","resp","stored","handleRpcError","WalletAdapterNotConnectedError","metadata","addresses","a","isMainnet","WalletAdapterBitcoinNetwork","WalletAdapterAddressPurpose","WalletAdapterAddressType","hex","addressToScriptPubKey","getBitcoinNetwork","getTapInternalKeyOf_P2TR_publicKey","address","message","addressType","paymentType","BitcoinWalletAdapterError","SignMessageAlgorithm","fromAddress","receiverAddress","satoshiAmount","foundAddressIndex","psbtHex","signIndices","signIndex","tx","btc.Transaction","callback","stopped","tick","error","LeatherWalletAdapterError","rpcError","promise","UserRejectError"],"mappings":"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"}