import type { SendPaymentParams, SwapParams, AgentPayParams, QuoteParams, PaymentResult, BatchSendParams, BatchSendResult, ApprovalStatusParams, ApprovalStatusResult, } from "./types"; export class PaymentSDK { constructor(private fetch: (path: string, init?: RequestInit) => Promise) {} // ── Send ────────────────────────────────────────────────────────────────── /** Send ETH or ERC-20 tokens to a single recipient */ async send(params: SendPaymentParams): Promise { const r = await this.fetch("/v1/agents/send", { method: "POST", body: JSON.stringify(params) }); const data = await r.json() as any; if (!r.ok) throw new Error(data.error ?? "send failed"); return data as PaymentResult; } /** * Send USDC to multiple recipients in one call. * Executes each transfer sequentially server-side. * Returns a per-recipient breakdown of successes and failures. * * @example * const result = await agent.payments.batchSend({ * recipients: [ * { to: "0xAlice", amount: "125", label: "1st place" }, * { to: "0xBob", amount: "75", label: "2nd place" }, * ], * chain: "base", * }); */ async batchSend(params: BatchSendParams): Promise { const r = await this.fetch("/v1/agents/batch-send", { method: "POST", body: JSON.stringify(params) }); const data = await r.json() as any; if (!r.ok) { // Server may not support batch endpoint yet — fall back to sequential sends if (r.status === 404) return this._batchSendFallback(params); throw new Error(data.error ?? "batch-send failed"); } return data as BatchSendResult; } private async _batchSendFallback(params: BatchSendParams): Promise { const sent: BatchSendResult["sent"] = []; const failed: BatchSendResult["failed"] = []; let total = 0; for (const recipient of params.recipients) { try { const res = await this.send({ to: recipient.to, amount: recipient.amount, chain: params.chain, token: params.token ?? "USDC", }); if (res.status === "confirmed" || res.status === "completed" || res.txHash) { sent.push(recipient); total += parseFloat(recipient.amount) || 0; } else { failed.push({ recipient, error: res.message ?? "unknown" }); } } catch (e: any) { failed.push({ recipient, error: e.message ?? "unknown" }); } } return { sent, failed, total: total.toFixed(6), chain: params.chain ?? "base", token: params.token ?? "USDC" }; } // ── Swap ───────────────────────────────────────────────────────────────── /** Execute a token swap via FluidSOR */ async swap(params: SwapParams): Promise { const { otpCode, ...body } = params; const headers: Record = {}; if (otpCode) headers["X-OTP-Code"] = otpCode; const r = await this.fetch("/v1/agents/swap", { method: "POST", body: JSON.stringify(body), headers }); const data = await r.json() as any; if (data.error === "OTP_REQUIRED") throw new Error("OTP_REQUIRED: " + data.message); if (data.error === "OTP_INVALID") throw new Error("OTP_INVALID: " + data.message); if (!r.ok) throw new Error(data.error ?? "swap failed"); return data as PaymentResult; } /** Get a price quote across 25+ DEX venues before executing */ async quoteSwap(params: QuoteParams): Promise { const { fromToken, toToken, amount, chain = "base" } = params; const r = await this.fetch( `/api/sor/wallet-quote?tokenIn=${fromToken}&tokenOut=${toToken}&amountIn=${amount}&network=${chain}` ); const data = await r.json() as any; if (!r.ok) throw new Error(data.error ?? "quote failed"); return data; } /** Estimate gas cost in USD before sending */ async estimateGas(params: Pick): Promise { const r = await this.fetch("/v1/agents/estimate-gas", { method: "POST", body: JSON.stringify(params) }); const data = await r.json() as any; if (!r.ok) throw new Error(data.error ?? "estimate failed"); return data; } // ── Agent pay (email-based) ──────────────────────────────────────────────── /** * Pay another Fluid Wallet user by email. * Fluid resolves their wallet address server-side. */ async agentPay(params: AgentPayParams): Promise { const r = await this.fetch("/v1/agents/agent-pay", { method: "POST", body: JSON.stringify(params) }); const data = await r.json() as any; if (!r.ok) throw new Error(data.error ?? "agent-pay failed"); return data as PaymentResult; } // ── High-value approval polling ──────────────────────────────────────────── /** * Poll the status of a high-value transaction that required wallet-owner approval. * When fluid_send returns status "pending_approval", use the approvalToken here. */ async approvalStatus(params: ApprovalStatusParams): Promise { const r = await this.fetch(`/v1/agents/approval?token=${encodeURIComponent(params.approvalToken)}`); const data = await r.json() as any; if (!r.ok) throw new Error(data.error ?? "approval status fetch failed"); return data as ApprovalStatusResult; } }