{"version":3,"file":"fiat-service.mjs","names":[],"sources":["../../../src/services/fiat-service.ts"],"sourcesContent":["/**\n * Fiat Rails Service — off-ramps, on-ramps, and payment routing.\n *\n * Provides a unified interface for moving money between crypto and fiat.\n * Supports multiple providers (Bridge.xyz, MoonPay, Coinbase) with a\n * preference-based fallback system.\n *\n * Architecture:\n * - Provider-agnostic interface: all providers implement FiatProvider\n * - Quote aggregation: fetch quotes from multiple providers, pick best\n * - Idempotent transfers: each transfer gets a unique ID for tracking\n * - Persistent state: transfer history saved to disk for accounting\n *\n * Env vars:\n *   BRIDGE_API_KEY         — Bridge.xyz API key\n *   MOONPAY_API_KEY        — MoonPay API key (optional)\n *   COINBASE_API_KEY       — Coinbase Pay API key (optional)\n *   FIAT_CURRENCY          — Default fiat currency (default: USD)\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport type FiatDirection = 'off_ramp' | 'on_ramp';\nexport type FiatCurrency = 'USD' | 'EUR' | 'GBP' | 'CAD' | 'AUD' | 'CHF' | 'JPY';\nexport type FiatTransferStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';\n\nexport interface FiatQuote {\n  provider: string;\n  direction: FiatDirection;\n  cryptoAmount: number;\n  cryptoToken: string;\n  cryptoChainId: number;\n  fiatAmount: number;\n  fiatCurrency: FiatCurrency;\n  /** Fee in fiat currency. */\n  fee: number;\n  /** Exchange rate: 1 crypto token = X fiat. */\n  exchangeRate: number;\n  /** How long the quote is valid (ms). */\n  expiresIn: number;\n  /** Provider-specific quote ID. */\n  quoteId: string;\n  /** Estimated settlement time. */\n  estimatedSettlement: string;\n}\n\nexport interface FiatTransfer {\n  id: string;\n  userId: string;\n  direction: FiatDirection;\n  provider: string;\n  cryptoAmount: number;\n  cryptoToken: string;\n  cryptoChainId: number;\n  fiatAmount: number;\n  fiatCurrency: FiatCurrency;\n  fee: number;\n  status: FiatTransferStatus;\n  /** Provider-specific transfer/order ID. */\n  externalId?: string;\n  /** Crypto tx hash (for off-ramps: the send-to-provider tx). */\n  txHash?: string;\n  /** Bank account identifier (masked). */\n  bankAccount?: string;\n  createdAt: number;\n  updatedAt: number;\n  /** Error message if failed. */\n  error?: string;\n}\n\nexport interface BankAccount {\n  id: string;\n  label: string;\n  /** Masked account number (e.g. \"****1234\"). */\n  maskedNumber: string;\n  bankName: string;\n  currency: FiatCurrency;\n  /** Provider-specific account ID. */\n  externalId: string;\n  provider: string;\n}\n\nexport interface FiatProvider {\n  name: string;\n  isConfigured(): boolean;\n  getQuote(params: {\n    direction: FiatDirection;\n    cryptoToken: string;\n    cryptoChainId: number;\n    amount: number;\n    amountType: 'crypto' | 'fiat';\n    fiatCurrency: FiatCurrency;\n  }): Promise<FiatQuote>;\n  executeTransfer(quote: FiatQuote, opts: {\n    walletAddress: string;\n    bankAccountId?: string;\n  }): Promise<{ transferId: string; depositAddress?: string; instructions?: string }>;\n  getTransferStatus(transferId: string): Promise<FiatTransferStatus>;\n  listBankAccounts?(): Promise<BankAccount[]>;\n}\n\n// ─── Bridge.xyz Provider ────────────────────────────────────────────────\n\nclass BridgeProvider implements FiatProvider {\n  name = 'bridge';\n\n  isConfigured(): boolean {\n    return !!process.env.BRIDGE_API_KEY;\n  }\n\n  private async apiFetch(path: string, opts?: RequestInit): Promise<any> {\n    const apiKey = process.env.BRIDGE_API_KEY;\n    if (!apiKey) throw new Error('BRIDGE_API_KEY not set');\n\n    const res = await fetch(`https://api.bridge.xyz/v0${path}`, {\n      ...opts,\n      headers: {\n        'Content-Type': 'application/json',\n        'Api-Key': apiKey,\n        ...(opts?.headers as Record<string, string> ?? {}),\n      },\n      signal: AbortSignal.timeout(30_000),\n    });\n\n    if (!res.ok) {\n      const body = await res.text().catch(() => '');\n      throw new Error(`Bridge API ${res.status}: ${body.slice(0, 200)}`);\n    }\n    return res.json();\n  }\n\n  async getQuote(params: {\n    direction: FiatDirection;\n    cryptoToken: string;\n    cryptoChainId: number;\n    amount: number;\n    amountType: 'crypto' | 'fiat';\n    fiatCurrency: FiatCurrency;\n  }): Promise<FiatQuote> {\n    // Bridge uses /transfers/quote endpoint\n    const chainMap: Record<number, string> = {\n      1: 'ethereum', 8453: 'base', 42161: 'arbitrum',\n      10: 'optimism', 137: 'polygon', 43114: 'avalanche',\n    };\n    const chain = chainMap[params.cryptoChainId] ?? 'base';\n\n    const body: Record<string, unknown> = {\n      source_currency: params.direction === 'off_ramp' ? params.cryptoToken.toLowerCase() : params.fiatCurrency.toLowerCase(),\n      destination_currency: params.direction === 'off_ramp' ? params.fiatCurrency.toLowerCase() : params.cryptoToken.toLowerCase(),\n      chain,\n    };\n\n    if (params.amountType === 'crypto') {\n      body.amount = String(params.amount);\n    } else {\n      body.destination_amount = String(params.amount);\n    }\n\n    const data = await this.apiFetch('/transfers/quote', {\n      method: 'POST',\n      body: JSON.stringify(body),\n    });\n\n    const cryptoAmt = parseFloat(data.source_amount ?? data.amount ?? params.amount);\n    const fiatAmt = parseFloat(data.destination_amount ?? data.fiat_amount ?? params.amount);\n    const fee = parseFloat(data.fee ?? '0');\n\n    return {\n      provider: 'bridge',\n      direction: params.direction,\n      cryptoAmount: params.direction === 'off_ramp' ? cryptoAmt : fiatAmt,\n      cryptoToken: params.cryptoToken,\n      cryptoChainId: params.cryptoChainId,\n      fiatAmount: params.direction === 'off_ramp' ? fiatAmt : cryptoAmt,\n      fiatCurrency: params.fiatCurrency,\n      fee,\n      exchangeRate: fiatAmt / (cryptoAmt || 1),\n      expiresIn: 60_000, // 1 minute\n      quoteId: data.id ?? `bridge_${Date.now()}`,\n      estimatedSettlement: data.estimated_settlement ?? '1-3 business days',\n    };\n  }\n\n  async executeTransfer(quote: FiatQuote, opts: {\n    walletAddress: string;\n    bankAccountId?: string;\n  }): Promise<{ transferId: string; depositAddress?: string; instructions?: string }> {\n    const data = await this.apiFetch('/transfers', {\n      method: 'POST',\n      body: JSON.stringify({\n        quote_id: quote.quoteId,\n        source_address: opts.walletAddress,\n        external_account_id: opts.bankAccountId,\n      }),\n    });\n\n    return {\n      transferId: data.id ?? `bridge_tx_${Date.now()}`,\n      depositAddress: data.deposit_address,\n      instructions: data.instructions ?? `Transfer ${quote.cryptoAmount} ${quote.cryptoToken} to the deposit address.`,\n    };\n  }\n\n  async getTransferStatus(transferId: string): Promise<FiatTransferStatus> {\n    const data = await this.apiFetch(`/transfers/${transferId}`);\n    const statusMap: Record<string, FiatTransferStatus> = {\n      pending: 'pending', processing: 'processing', completed: 'completed',\n      failed: 'failed', cancelled: 'cancelled',\n    };\n    return statusMap[data.status] ?? 'pending';\n  }\n\n  async listBankAccounts(): Promise<BankAccount[]> {\n    try {\n      const data = await this.apiFetch('/external_accounts');\n      return (data.data ?? []).map((a: any) => ({\n        id: a.id,\n        label: a.account_name ?? a.label ?? 'Bank Account',\n        maskedNumber: a.last_4 ? `****${a.last_4}` : '****',\n        bankName: a.bank_name ?? 'Unknown',\n        currency: (a.currency ?? 'USD').toUpperCase() as FiatCurrency,\n        externalId: a.id,\n        provider: 'bridge',\n      }));\n    } catch {\n      return [];\n    }\n  }\n}\n\n// ─── MoonPay Provider (Stub) ────────────────────────────────────────────\n// MoonPay requires a widget-based flow for KYC. The service provides\n// quote information; actual execution redirects users to the MoonPay widget.\n\nclass MoonPayProvider implements FiatProvider {\n  name = 'moonpay';\n\n  isConfigured(): boolean {\n    return !!process.env.MOONPAY_API_KEY;\n  }\n\n  async getQuote(params: {\n    direction: FiatDirection;\n    cryptoToken: string;\n    cryptoChainId: number;\n    amount: number;\n    amountType: 'crypto' | 'fiat';\n    fiatCurrency: FiatCurrency;\n  }): Promise<FiatQuote> {\n    const apiKey = process.env.MOONPAY_API_KEY;\n    if (!apiKey) throw new Error('MOONPAY_API_KEY not set');\n\n    const endpoint = params.direction === 'on_ramp'\n      ? 'https://api.moonpay.com/v3/currencies/quote'\n      : 'https://api.moonpay.com/v3/sell_quotes';\n\n    const queryParams = new URLSearchParams({\n      apiKey,\n      baseCurrencyCode: params.fiatCurrency.toLowerCase(),\n      currencyCode: params.cryptoToken.toLowerCase(),\n      baseCurrencyAmount: String(params.amountType === 'fiat' ? params.amount : ''),\n      quoteCurrencyAmount: String(params.amountType === 'crypto' ? params.amount : ''),\n    });\n\n    const res = await fetch(`${endpoint}?${queryParams}`, {\n      signal: AbortSignal.timeout(15_000),\n    });\n    if (!res.ok) throw new Error(`MoonPay ${res.status}`);\n    const data: any = await res.json();\n\n    return {\n      provider: 'moonpay',\n      direction: params.direction,\n      cryptoAmount: parseFloat(data.quoteCurrencyAmount ?? params.amount),\n      cryptoToken: params.cryptoToken,\n      cryptoChainId: params.cryptoChainId,\n      fiatAmount: parseFloat(data.baseCurrencyAmount ?? params.amount),\n      fiatCurrency: params.fiatCurrency,\n      fee: parseFloat(data.feeAmount ?? data.totalFee ?? '0'),\n      exchangeRate: parseFloat(data.quoteCurrencyPrice ?? '0'),\n      expiresIn: 30_000,\n      quoteId: `moonpay_${Date.now()}`,\n      estimatedSettlement: 'Instant to 1 business day',\n    };\n  }\n\n  async executeTransfer(_quote: FiatQuote, _opts: {\n    walletAddress: string;\n  }): Promise<{ transferId: string; instructions?: string }> {\n    // MoonPay requires widget redirect — return instructions\n    const apiKey = process.env.MOONPAY_API_KEY;\n    const widgetUrl = `https://buy.moonpay.com?apiKey=${apiKey}&currencyCode=${_quote.cryptoToken.toLowerCase()}&walletAddress=${_opts.walletAddress}`;\n    return {\n      transferId: `moonpay_${Date.now()}`,\n      instructions: `Complete the purchase at: ${widgetUrl}`,\n    };\n  }\n\n  async getTransferStatus(_transferId: string): Promise<FiatTransferStatus> {\n    return 'pending'; // MoonPay status requires webhook\n  }\n}\n\n// ─── Fiat Service ───────────────────────────────────────────────────────\n\nexport class FiatService {\n  private providers: FiatProvider[] = [];\n  private transfers = new Map<string, FiatTransfer>();\n  private stateDir: string;\n\n  constructor(opts?: { stateDir?: string }) {\n    this.stateDir = opts?.stateDir ?? join(\n      process.env.HOME ?? '', '.openclawnch', 'fiat'\n    );\n\n    // Register providers in preference order\n    this.providers.push(new BridgeProvider());\n    this.providers.push(new MoonPayProvider());\n\n    this.loadState();\n  }\n\n  // ── Provider Discovery ──────────────────────────────────────────────\n\n  /** Get configured providers. */\n  getConfiguredProviders(): string[] {\n    return this.providers.filter(p => p.isConfigured()).map(p => p.name);\n  }\n\n  /** Check if any fiat provider is configured. */\n  isAvailable(): boolean {\n    return this.providers.some(p => p.isConfigured());\n  }\n\n  // ── Quotes ──────────────────────────────────────────────────────────\n\n  /** Get quotes from all configured providers. */\n  async getQuotes(params: {\n    direction: FiatDirection;\n    cryptoToken: string;\n    cryptoChainId?: number;\n    amount: number;\n    amountType?: 'crypto' | 'fiat';\n    fiatCurrency?: FiatCurrency;\n  }): Promise<FiatQuote[]> {\n    const configured = this.providers.filter(p => p.isConfigured());\n    if (configured.length === 0) throw new Error('No fiat providers configured. Set BRIDGE_API_KEY or MOONPAY_API_KEY.');\n\n    const results = await Promise.allSettled(\n      configured.map(p => p.getQuote({\n        direction: params.direction,\n        cryptoToken: params.cryptoToken,\n        cryptoChainId: params.cryptoChainId ?? 8453,\n        amount: params.amount,\n        amountType: params.amountType ?? 'crypto',\n        fiatCurrency: params.fiatCurrency ?? 'USD',\n      }))\n    );\n\n    const quotes: FiatQuote[] = [];\n    for (const r of results) {\n      if (r.status === 'fulfilled') quotes.push(r.value);\n    }\n\n    // Sort by best rate (lowest fee for off-ramp, highest crypto for on-ramp)\n    quotes.sort((a, b) => {\n      if (params.direction === 'off_ramp') {\n        return (b.fiatAmount - b.fee) - (a.fiatAmount - a.fee); // highest payout first\n      }\n      return (b.cryptoAmount) - (a.cryptoAmount); // most crypto first\n    });\n\n    return quotes;\n  }\n\n  // ── Transfer Execution ──────────────────────────────────────────────\n\n  /** Execute a fiat transfer using a quote. */\n  async executeTransfer(quote: FiatQuote, opts: {\n    userId: string;\n    walletAddress: string;\n    bankAccountId?: string;\n  }): Promise<FiatTransfer> {\n    const provider = this.providers.find(p => p.name === quote.provider);\n    if (!provider) throw new Error(`Provider \"${quote.provider}\" not found`);\n\n    const result = await provider.executeTransfer(quote, {\n      walletAddress: opts.walletAddress,\n      bankAccountId: opts.bankAccountId,\n    });\n\n    const transfer: FiatTransfer = {\n      id: `fiat_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n      userId: opts.userId,\n      direction: quote.direction,\n      provider: quote.provider,\n      cryptoAmount: quote.cryptoAmount,\n      cryptoToken: quote.cryptoToken,\n      cryptoChainId: quote.cryptoChainId,\n      fiatAmount: quote.fiatAmount,\n      fiatCurrency: quote.fiatCurrency,\n      fee: quote.fee,\n      status: 'pending',\n      externalId: result.transferId,\n      createdAt: Date.now(),\n      updatedAt: Date.now(),\n    };\n\n    this.transfers.set(transfer.id, transfer);\n    this.saveState();\n\n    return transfer;\n  }\n\n  // ── Status Tracking ─────────────────────────────────────────────────\n\n  /** Check transfer status from provider. */\n  async refreshTransferStatus(transferId: string): Promise<FiatTransfer | null> {\n    const transfer = this.transfers.get(transferId);\n    if (!transfer || !transfer.externalId) return transfer ?? null;\n\n    const provider = this.providers.find(p => p.name === transfer.provider);\n    if (!provider) return transfer;\n\n    try {\n      const status = await provider.getTransferStatus(transfer.externalId);\n      transfer.status = status;\n      transfer.updatedAt = Date.now();\n      this.saveState();\n    } catch { /* keep existing status */ }\n\n    return transfer;\n  }\n\n  /** Get transfer by ID. */\n  getTransfer(transferId: string): FiatTransfer | null {\n    return this.transfers.get(transferId) ?? null;\n  }\n\n  /** List all transfers for a user. */\n  listTransfers(userId?: string): FiatTransfer[] {\n    const all = Array.from(this.transfers.values());\n    if (!userId) return all;\n    return all.filter(t => t.userId === userId);\n  }\n\n  // ── Bank Accounts ───────────────────────────────────────────────────\n\n  /** List linked bank accounts from configured providers. */\n  async listBankAccounts(): Promise<BankAccount[]> {\n    const accounts: BankAccount[] = [];\n    for (const provider of this.providers) {\n      if (provider.isConfigured() && provider.listBankAccounts) {\n        try {\n          const providerAccounts = await provider.listBankAccounts();\n          accounts.push(...providerAccounts);\n        } catch { /* skip provider */ }\n      }\n    }\n    return accounts;\n  }\n\n  // ── Persistence ─────────────────────────────────────────────────────\n\n  private loadState(): void {\n    try {\n      const filePath = join(this.stateDir, 'transfers.json');\n      if (existsSync(filePath)) {\n        const data = JSON.parse(readFileSync(filePath, 'utf8'));\n        for (const t of data) {\n          this.transfers.set(t.id, t);\n        }\n      }\n    } catch { /* fresh start */ }\n  }\n\n  private saveState(): void {\n    try {\n      if (!existsSync(this.stateDir)) mkdirSync(this.stateDir, { recursive: true });\n      const filePath = join(this.stateDir, 'transfers.json');\n      writeFileSync(filePath, JSON.stringify(Array.from(this.transfers.values()), null, 2), 'utf8');\n    } catch { /* best effort */ }\n  }\n\n  /** Clear all state (for testing). */\n  clear(): void {\n    this.transfers.clear();\n  }\n}\n\n// ─── Singleton ──────────────────────────────────────────────────────────\n\nlet instance: FiatService | null = null;\n\nexport function getFiatService(opts?: { stateDir?: string }): FiatService {\n  if (!instance) {\n    instance = new FiatService(opts);\n  }\n  return instance;\n}\n\nexport function resetFiatService(): void {\n  instance?.clear();\n  instance = null;\n}\n"],"mappings":"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"}