{"version":3,"sources":["../../src/signer.ts"],"sourcesContent":["import type {\n  TransactionSigner,\n  MessagePartialSigner,\n  RpcDevnet,\n  SolanaRpcApiDevnet,\n  RpcTestnet,\n  SolanaRpcApiTestnet,\n  RpcMainnet,\n  SolanaRpcApiMainnet,\n  Address,\n} from \"@solana/kit\";\nimport { getBase64EncodedWireTransaction } from \"@solana/kit\";\nimport { createRpcClient, decodeTransactionFromPayload } from \"./utils\";\n\n/**\n * Client-side signer for creating and signing Solana transactions\n * This is a wrapper around TransactionSigner from @solana/kit\n */\nexport type ClientSvmSigner = TransactionSigner;\n\n/**\n * Configuration for client operations\n */\nexport type ClientSvmConfig = {\n  /**\n   * Optional custom RPC URL for the client to use\n   */\n  rpcUrl?: string;\n};\n\n/**\n * Signing capabilities needed by the facilitator\n * Must support both transaction and message signing\n * KeyPairSigner from @solana/kit satisfies this interface\n */\nexport type FacilitatorSigningCapabilities = TransactionSigner & MessagePartialSigner;\n\n/**\n * RPC client type from @solana/kit\n * Can be devnet, testnet, or mainnet RPC client\n */\nexport type FacilitatorRpcClient =\n  | RpcDevnet<SolanaRpcApiDevnet>\n  | RpcTestnet<SolanaRpcApiTestnet>\n  | RpcMainnet<SolanaRpcApiMainnet>;\n\n/**\n * RPC capabilities needed by the facilitator for verification and settlement\n * This is a legacy interface for custom RPC implementations\n */\nexport type FacilitatorRpcCapabilities = {\n  /**\n   * Get the SOL balance of an account\n   *\n   * @param address - Base58 encoded address\n   * @returns Balance in lamports\n   */\n  getBalance(address: string): Promise<bigint>;\n\n  /**\n   * Get the token account balance\n   *\n   * @param address - Base58 encoded token account address\n   * @returns Token balance in smallest units\n   */\n  getTokenAccountBalance(address: string): Promise<bigint>;\n\n  /**\n   * Get the latest blockhash information\n   *\n   * @returns Blockhash and last valid block height\n   */\n  getLatestBlockhash(): Promise<{ blockhash: string; lastValidBlockHeight: bigint }>;\n\n  /**\n   * Simulate a transaction to check if it would succeed\n   *\n   * @param transaction - Base64 encoded transaction\n   * @param config - Simulation configuration\n   * @returns Simulation result\n   */\n  simulateTransaction(transaction: string, config: unknown): Promise<unknown>;\n\n  /**\n   * Send a transaction to the network\n   *\n   * @param transaction - Base64 encoded signed transaction\n   * @returns Transaction signature\n   */\n  sendTransaction(transaction: string): Promise<string>;\n\n  /**\n   * Wait for transaction confirmation\n   *\n   * @param signature - Transaction signature\n   * @returns Confirmation result\n   */\n  confirmTransaction(signature: string): Promise<unknown>;\n\n  /**\n   * Fetch token mint information\n   *\n   * @param address - Base58 encoded mint address\n   * @returns Mint information including decimals\n   */\n  fetchMint(address: string): Promise<unknown>;\n};\n\n/**\n * Minimal facilitator signer interface for SVM operations.\n * Supports multiple signers for load balancing and high availability.\n * All implementation details (RPC clients, key management, signature handling) are hidden.\n */\nexport type FacilitatorSvmSigner = {\n  /**\n   * Get all addresses this facilitator can use as fee payers\n   * Enables dynamic address selection for load balancing and key rotation\n   *\n   * @returns Array of addresses available for signing\n   */\n  getAddresses(): readonly Address[];\n\n  /**\n   * Sign a partially-signed transaction with the signer matching feePayer\n   * Transaction is decoded, signed, and re-encoded internally\n   *\n   * @param transaction - Base64 encoded partially-signed transaction\n   * @param feePayer - Fee payer address (determines which signer to use)\n   * @param network - CAIP-2 network identifier\n   * @returns Base64 encoded fully-signed transaction\n   * @throws Error if no signer exists for feePayer or signing fails\n   */\n  signTransaction(transaction: string, feePayer: Address, network: string): Promise<string>;\n\n  /**\n   * Simulate a signed transaction to verify it would succeed\n   * Implementation manages RPC client selection and simulation details\n   *\n   * @param transaction - Base64 encoded signed transaction\n   * @param network - CAIP-2 network identifier\n   * @throws Error if simulation fails\n   */\n  simulateTransaction(transaction: string, network: string): Promise<void>;\n\n  /**\n   * Send a signed transaction to the network\n   * Implementation manages RPC client selection and sending details\n   *\n   * @param transaction - Base64 encoded signed transaction\n   * @param network - CAIP-2 network identifier\n   * @returns Transaction signature\n   * @throws Error if send fails\n   */\n  sendTransaction(transaction: string, network: string): Promise<string>;\n\n  /**\n   * Wait for transaction confirmation\n   * Allows signer to implement custom retry logic, timeouts, and confirmation strategies\n   *\n   * @param signature - Transaction signature to confirm\n   * @param network - CAIP-2 network identifier\n   * @returns Promise that resolves when transaction is confirmed\n   * @throws Error if confirmation fails or times out\n   */\n  confirmTransaction(signature: string, network: string): Promise<void>;\n};\n\n/**\n * Convert a signer to ClientSvmSigner (identity function for type safety)\n *\n * @param signer - The signer to convert\n * @returns The signer as ClientSvmSigner\n */\nexport function toClientSvmSigner(signer: ClientSvmSigner): ClientSvmSigner {\n  return signer;\n}\n\n/**\n * Create RPC capabilities from a Solana Kit RPC client\n *\n * @param rpc - The RPC client from @solana/kit\n * @returns RPC capabilities for the facilitator\n */\nexport function createRpcCapabilitiesFromRpc(\n  rpc: FacilitatorRpcClient,\n): FacilitatorRpcCapabilities {\n  return {\n    getBalance: async address => {\n      const result = await rpc.getBalance(address as never).send();\n      return result.value;\n    },\n    getTokenAccountBalance: async address => {\n      const accountInfo = await rpc\n        .getAccountInfo(address as never, {\n          encoding: \"jsonParsed\",\n        })\n        .send();\n\n      if (!accountInfo.value) {\n        throw new Error(`Token account not found: ${address}`);\n      }\n\n      const parsed = accountInfo.value.data as {\n        parsed: { info: { tokenAmount: { amount: string } } };\n      };\n      return BigInt(parsed.parsed.info.tokenAmount.amount);\n    },\n    getLatestBlockhash: async () => {\n      const result = await rpc.getLatestBlockhash().send();\n      return {\n        blockhash: result.value.blockhash,\n        lastValidBlockHeight: result.value.lastValidBlockHeight,\n      };\n    },\n    simulateTransaction: async (transaction, config) => {\n      return await rpc.simulateTransaction(transaction as never, config as never).send();\n    },\n    sendTransaction: async transaction => {\n      return await rpc\n        .sendTransaction(transaction as never, {\n          encoding: \"base64\",\n        })\n        .send();\n    },\n    confirmTransaction: async signature => {\n      let confirmed = false;\n      let attempts = 0;\n      const maxAttempts = 30;\n\n      while (!confirmed && attempts < maxAttempts) {\n        const status = await rpc.getSignatureStatuses([signature as never]).send();\n\n        if (\n          status.value[0]?.confirmationStatus === \"confirmed\" ||\n          status.value[0]?.confirmationStatus === \"finalized\"\n        ) {\n          confirmed = true;\n          return status.value[0];\n        }\n\n        await new Promise(resolve => setTimeout(resolve, 1000));\n        attempts++;\n      }\n\n      throw new Error(\"Transaction confirmation timeout\");\n    },\n    fetchMint: async address => {\n      const { fetchMint } = await import(\"@solana-program/token-2022\");\n      return await fetchMint(rpc, address as never);\n    },\n  };\n}\n\n/**\n * RPC configuration for the facilitator\n * Can be a single RPC (all networks), a network mapping, or config options\n */\nexport type FacilitatorRpcConfig =\n  | FacilitatorRpcClient // Single RPC for all networks\n  | Record<string, FacilitatorRpcClient> // Per-network RPC mapping\n  | { defaultRpcUrl?: string }; // Custom default RPC URL\n\n/**\n * Create a FacilitatorSvmSigner from a TransactionSigner and optional RPC config\n *\n * @param signer - The TransactionSigner (e.g., from createKeyPairSignerFromBytes)\n * @param rpcConfig - Optional RPC configuration (single RPC, per-network map, or config)\n * @returns A complete FacilitatorSvmSigner\n *\n * @example\n * ```ts\n * import { createKeyPairSignerFromBytes, createSolanaRpc, devnet } from \"@solana/kit\";\n *\n * // Option 1: No RPC - use defaults (SIMPLEST)\n * const keypair = await createKeyPairSignerFromBytes(privateKeyBytes);\n * const facilitator = toFacilitatorSvmSigner(keypair);\n *\n * // Option 2: Single RPC for all networks\n * const rpc = createSolanaRpc(devnet(\"https://api.devnet.solana.com\"));\n * const facilitator = toFacilitatorSvmSigner(keypair, rpc);\n *\n * // Option 3: Per-network RPC (FLEXIBLE)\n * const facilitator = toFacilitatorSvmSigner(keypair, {\n *   [SOLANA_MAINNET_CAIP2]: myQuickNodeRpc,\n *   // Devnet/testnet use defaults\n * });\n *\n * // Option 4: Custom default RPC URL\n * const facilitator = toFacilitatorSvmSigner(keypair, {\n *   defaultRpcUrl: \"https://my-rpc.com\"\n * });\n * ```\n */\nexport function toFacilitatorSvmSigner(\n  signer: TransactionSigner & MessagePartialSigner,\n  rpcConfig?: FacilitatorRpcConfig,\n): FacilitatorSvmSigner {\n  let rpcMap: Record<string, FacilitatorRpcClient> = {};\n  let defaultRpcUrl: string | undefined;\n\n  if (rpcConfig) {\n    // Check if it's a config object with defaultRpcUrl\n    if (\"defaultRpcUrl\" in rpcConfig && typeof rpcConfig.defaultRpcUrl === \"string\") {\n      defaultRpcUrl = rpcConfig.defaultRpcUrl;\n    }\n    // Check if it's a single RPC client\n    else if (\"getBalance\" in rpcConfig || \"getSlot\" in rpcConfig) {\n      rpcMap[\"*\"] = rpcConfig as FacilitatorRpcClient;\n    }\n    // Otherwise, it's a network mapping\n    else {\n      rpcMap = rpcConfig as Record<string, FacilitatorRpcClient>;\n    }\n  }\n\n  const getRpcForNetwork = (network: string): FacilitatorRpcClient => {\n    // 1. Check for exact network match\n    if (rpcMap[network]) {\n      return rpcMap[network];\n    }\n\n    // 2. Check for wildcard RPC\n    if (rpcMap[\"*\"]) {\n      return rpcMap[\"*\"];\n    }\n\n    // 3. Create default RPC for this network\n    return createRpcClient(network as `${string}:${string}`, defaultRpcUrl);\n  };\n\n  return {\n    getAddresses: () => {\n      return [signer.address];\n    },\n\n    signTransaction: async (transaction: string, feePayer: Address, _: string) => {\n      if (feePayer !== signer.address) {\n        throw new Error(`No signer for feePayer ${feePayer}. Available: ${signer.address}`);\n      }\n\n      // Decode transaction from base64\n      const tx = decodeTransactionFromPayload({ transaction });\n\n      // Sign the transaction\n      const signableMessage = {\n        content: tx.messageBytes,\n        signatures: tx.signatures,\n      };\n\n      const [facilitatorSignatureDictionary] = await signer.signMessages([\n        signableMessage as never,\n      ]);\n\n      // Merge signatures and encode\n      const fullySignedTx = {\n        ...tx,\n        signatures: {\n          ...tx.signatures,\n          ...facilitatorSignatureDictionary,\n        },\n      };\n\n      return getBase64EncodedWireTransaction(fullySignedTx);\n    },\n\n    simulateTransaction: async (transaction: string, network: string) => {\n      const rpc = getRpcForNetwork(network);\n      const result = await rpc\n        .simulateTransaction(transaction as never, {\n          sigVerify: true,\n          replaceRecentBlockhash: false,\n          commitment: \"confirmed\",\n          encoding: \"base64\",\n        })\n        .send();\n\n      if (result.value.err) {\n        // Use replacer to handle BigInt values from Solana RPC responses\n        const errorStr = JSON.stringify(result.value.err, (_, v) =>\n          typeof v === \"bigint\" ? v.toString() : v,\n        );\n        throw new Error(`Simulation failed: ${errorStr}`);\n      }\n    },\n\n    sendTransaction: async (transaction: string, network: string) => {\n      const rpc = getRpcForNetwork(network);\n      return await rpc\n        .sendTransaction(transaction as never, {\n          encoding: \"base64\",\n        })\n        .send();\n    },\n\n    confirmTransaction: async (signature: string, network: string) => {\n      const rpc = getRpcForNetwork(network);\n      const rpcCapabilities = createRpcCapabilitiesFromRpc(rpc);\n      await rpcCapabilities.confirmTransaction(signature);\n    },\n  };\n}\n"],"mappings":"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","names":[]}