@relai-fi/x402 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +116 -0
  2. package/dist/bridge.cjs +109 -0
  3. package/dist/bridge.cjs.map +1 -0
  4. package/dist/bridge.d.cts +78 -0
  5. package/dist/bridge.d.ts +78 -0
  6. package/dist/bridge.js +80 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/client.cjs +131 -1
  9. package/dist/client.cjs.map +1 -1
  10. package/dist/client.d.cts +12 -0
  11. package/dist/client.d.ts +12 -0
  12. package/dist/client.js +131 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/index.cjs +247 -73
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +247 -73
  17. package/dist/index.js.map +1 -1
  18. package/dist/mpp/bridge-client.cjs +23922 -0
  19. package/dist/mpp/bridge-client.cjs.map +1 -0
  20. package/dist/mpp/bridge-client.d.cts +58 -0
  21. package/dist/mpp/bridge-client.d.ts +58 -0
  22. package/dist/mpp/bridge-client.js +23892 -0
  23. package/dist/mpp/bridge-client.js.map +1 -0
  24. package/dist/mpp/bridge-method.cjs +13202 -0
  25. package/dist/mpp/bridge-method.cjs.map +1 -0
  26. package/dist/mpp/bridge-method.d.cts +69 -0
  27. package/dist/mpp/bridge-method.d.ts +69 -0
  28. package/dist/mpp/bridge-method.js +13181 -0
  29. package/dist/mpp/bridge-method.js.map +1 -0
  30. package/dist/mpp/bridge-server.cjs +13887 -0
  31. package/dist/mpp/bridge-server.cjs.map +1 -0
  32. package/dist/mpp/bridge-server.d.cts +62 -0
  33. package/dist/mpp/bridge-server.d.ts +62 -0
  34. package/dist/mpp/bridge-server.js +13866 -0
  35. package/dist/mpp/bridge-server.js.map +1 -0
  36. package/dist/mpp/evm-server.cjs +49 -33
  37. package/dist/mpp/evm-server.cjs.map +1 -1
  38. package/dist/mpp/evm-server.js +49 -33
  39. package/dist/mpp/evm-server.js.map +1 -1
  40. package/dist/mpp/verify-erc20.cjs +71 -0
  41. package/dist/mpp/verify-erc20.cjs.map +1 -0
  42. package/dist/mpp/verify-erc20.d.cts +27 -0
  43. package/dist/mpp/verify-erc20.d.ts +27 -0
  44. package/dist/mpp/verify-erc20.js +46 -0
  45. package/dist/mpp/verify-erc20.js.map +1 -0
  46. package/dist/mpp/verify-spl.cjs +96 -0
  47. package/dist/mpp/verify-spl.cjs.map +1 -0
  48. package/dist/mpp/verify-spl.d.cts +30 -0
  49. package/dist/mpp/verify-spl.d.ts +30 -0
  50. package/dist/mpp/verify-spl.js +71 -0
  51. package/dist/mpp/verify-spl.js.map +1 -0
  52. package/dist/mpp/with-bridge.cjs +23956 -0
  53. package/dist/mpp/with-bridge.cjs.map +1 -0
  54. package/dist/mpp/with-bridge.d.cts +53 -0
  55. package/dist/mpp/with-bridge.d.ts +53 -0
  56. package/dist/mpp/with-bridge.js +23926 -0
  57. package/dist/mpp/with-bridge.js.map +1 -0
  58. package/dist/react/index.cjs +131 -1
  59. package/dist/react/index.cjs.map +1 -1
  60. package/dist/react/index.js +131 -1
  61. package/dist/react/index.js.map +1 -1
  62. package/dist/server.cjs +6 -39
  63. package/dist/server.cjs.map +1 -1
  64. package/dist/server.js +6 -39
  65. package/dist/server.js.map +1 -1
  66. package/package.json +31 -1
package/README.md CHANGED
@@ -1483,6 +1483,122 @@ Both interfaces are exported from `@relai-fi/x402`.
1483
1483
 
1484
1484
  ---
1485
1485
 
1486
+ ## Cross-Chain Bridge Flows
1487
+
1488
+ The SDK supports transparent cross-chain payments via the RelAI bridge. There are two approaches depending on who manages the bridge logic — the **client** or the **server**.
1489
+
1490
+ ### x402 Auto-Bridge (client-side)
1491
+
1492
+ The server is a standard x402 server. The client has a wallet on a different chain and uses `bridge: { enabled: true }` to auto-bridge.
1493
+
1494
+ ```mermaid
1495
+ sequenceDiagram
1496
+ participant Client as Client (Solana)
1497
+ participant Server as Server (Base)
1498
+ participant Bridge as RelAI Bridge API
1499
+ participant Facilitator as Facilitator
1500
+
1501
+ Client->>Server: GET /api/data
1502
+ Server-->>Client: 402 (accepts: Base USDC)
1503
+
1504
+ Note over Client: No Base wallet → auto-bridge
1505
+
1506
+ Client->>Bridge: GET /bridge/info
1507
+ Bridge-->>Client: source chains, payTo, fees
1508
+
1509
+ Client->>Client: Build Solana SPL transfer to bridge payTo
1510
+ Client->>Bridge: POST /bridge/settle (sourcePayment, targetAccept)
1511
+ Bridge->>Bridge: Co-sign & broadcast Solana tx
1512
+ Bridge->>Bridge: Sign EIP-3009 authorization (Base)
1513
+ Bridge-->>Client: xPayment (base64)
1514
+
1515
+ Client->>Server: GET /api/data + X-PAYMENT header
1516
+ Server->>Facilitator: POST /settle (paymentPayload)
1517
+ Facilitator->>Facilitator: Execute transferWithAuthorization on Base
1518
+ Facilitator-->>Server: tx hash
1519
+ Server-->>Client: 200 OK + data
1520
+ ```
1521
+
1522
+ ### MPP Client-Side Bridge (evmChargeWithBridge)
1523
+
1524
+ The server exposes a standard `evm/charge` MPP method. The client detects it's on a different chain and bridges transparently — the server never knows.
1525
+
1526
+ ```mermaid
1527
+ sequenceDiagram
1528
+ participant Client as Client (Tempo)
1529
+ participant Server as Server (SKALE)
1530
+ participant Bridge as RelAI Bridge API
1531
+ participant Facilitator as Facilitator
1532
+
1533
+ Client->>Server: GET /api/data
1534
+ Server-->>Client: 402 + WWW-Authenticate: Payment (evm/charge, chainId=SKALE)
1535
+
1536
+ Note over Client: Source chain ≠ target chain → bridge
1537
+
1538
+ Client->>Bridge: GET /bridge/info
1539
+ Bridge-->>Client: source chains, payTo, fees
1540
+
1541
+ Client->>Client: ERC-20 transfer on Tempo → bridge payTo
1542
+ Client->>Client: Sign settle message (ECDSA)
1543
+ Client->>Bridge: POST /bridge/settle (sourceTxHash, signature, targetAccept)
1544
+ Bridge->>Bridge: Verify source tx on-chain + signature
1545
+ Bridge->>Bridge: Sign EIP-3009 authorization (SKALE)
1546
+ Bridge-->>Client: xPayment + targetTxId=pending
1547
+
1548
+ Client->>Facilitator: POST /settle (paymentPayload, paymentRequirements)
1549
+ Facilitator->>Facilitator: Execute transferWithAuthorization on SKALE
1550
+ Facilitator-->>Client: tx hash (SKALE)
1551
+
1552
+ Client->>Server: GET /api/data + Authorization: Payment (hash=SKALE tx)
1553
+ Server->>Server: Verify ERC-20 Transfer on SKALE (standard evm/charge verify)
1554
+ Server-->>Client: 200 OK + data
1555
+ ```
1556
+
1557
+ ### MPP Server-Side Bridge (bridge/charge method)
1558
+
1559
+ The server explicitly exposes a `bridge/charge` method alongside its direct method. The client picks bridge/charge when it can't pay directly. Use this when the target chain is not EVM (e.g. Solana).
1560
+
1561
+ ```mermaid
1562
+ sequenceDiagram
1563
+ participant Client as Client (Tempo)
1564
+ participant Server as Server (SKALE)
1565
+ participant Bridge as RelAI Bridge API
1566
+ participant Facilitator as Facilitator
1567
+
1568
+ Client->>Server: GET /api/data
1569
+ Server->>Bridge: GET /bridge/info (auto-discover)
1570
+ Server-->>Client: 402 + WWW-Authenticate: Payment<br/>(evm/charge + bridge/charge)
1571
+
1572
+ Note over Client: Can't match evm/charge → picks bridge/charge
1573
+
1574
+ Client->>Client: ERC-20 transfer on Tempo → bridge payTo
1575
+ Client->>Client: Sign settle message (ECDSA)
1576
+ Client->>Bridge: POST /bridge/settle (sourceTxHash, signature, targetAccept)
1577
+ Bridge->>Bridge: Verify source tx on-chain + signature
1578
+ Bridge->>Bridge: Sign EIP-3009 authorization (SKALE)
1579
+ Bridge-->>Client: xPayment + targetTxId=pending
1580
+
1581
+ Client->>Facilitator: POST /settle (paymentPayload, paymentRequirements)
1582
+ Facilitator->>Facilitator: Execute transferWithAuthorization on SKALE
1583
+ Facilitator-->>Client: tx hash (SKALE)
1584
+
1585
+ Client->>Server: Authorization: Payment (targetTxHash=SKALE tx)
1586
+ Server->>Server: bridge/charge verify() — check ERC-20 Transfer on SKALE
1587
+ Server-->>Client: 200 OK + data
1588
+ ```
1589
+
1590
+ ### When to use which approach
1591
+
1592
+ | Approach | Server config | Client config | Best for |
1593
+ |----------|--------------|---------------|----------|
1594
+ | **x402 auto-bridge** | Standard x402 (no change) | `bridge: { enabled: true }` | Solana client → EVM server |
1595
+ | **MPP client-side** | Standard `evm/charge` (no change) | `evmChargeWithBridge()` | EVM client → different EVM server |
1596
+ | **MPP server-side** | `evm/charge` + `bridge/charge` | `bridgeCharge()` client | Any client → Solana server, or when server wants to control bridge options |
1597
+
1598
+ See [`examples/bridge/`](./examples/bridge/) for runnable examples of each approach.
1599
+
1600
+ ---
1601
+
1486
1602
  ## Development
1487
1603
 
1488
1604
  ```bash
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/bridge.ts
21
+ var bridge_exports = {};
22
+ __export(bridge_exports, {
23
+ DEFAULT_EVM_RPC: () => DEFAULT_EVM_RPC,
24
+ computeSourceAmount: () => computeSourceAmount,
25
+ getBridgeInfo: () => getBridgeInfo,
26
+ selectSourceChain: () => selectSourceChain,
27
+ settleBridge: () => settleBridge
28
+ });
29
+ module.exports = __toCommonJS(bridge_exports);
30
+ var RELAI_API_BASE = "https://api.relai.fi";
31
+ var _cacheMap = /* @__PURE__ */ new Map();
32
+ var CACHE_TTL = 5 * 60 * 1e3;
33
+ async function getBridgeInfo(baseUrl = RELAI_API_BASE) {
34
+ const key = baseUrl.replace(/\/$/, "");
35
+ const now = Date.now();
36
+ const cached = _cacheMap.get(key);
37
+ if (cached && now - cached.time < CACHE_TTL) return cached.info;
38
+ const url = `${key}/bridge/info`;
39
+ const res = await fetch(url);
40
+ if (!res.ok) {
41
+ if (cached) return cached.info;
42
+ throw new Error(`[relai:bridge] Failed to fetch ${url}: ${res.status}`);
43
+ }
44
+ const data = await res.json();
45
+ const info = {
46
+ settleEndpoint: data.settleEndpoint,
47
+ supportedSourceChains: data.supportedSourceChains || [],
48
+ supportedSourceAssets: data.supportedSourceAssets || [],
49
+ payTo: data.payTo || {},
50
+ feePayerSvm: data.feePayerSvm ?? null,
51
+ feeBps: data.feeBps ?? 100,
52
+ paymentFacilitator: data.paymentFacilitator || "https://facilitator.x402.fi"
53
+ };
54
+ _cacheMap.set(key, { info, time: now });
55
+ return info;
56
+ }
57
+ async function settleBridge(settleEndpoint, body) {
58
+ const res = await fetch(settleEndpoint, {
59
+ method: "POST",
60
+ headers: { "Content-Type": "application/json" },
61
+ body: JSON.stringify(body)
62
+ });
63
+ if (!res.ok) {
64
+ const err = await res.json().catch(() => ({}));
65
+ throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? " \u2014 " + err.details : ""}`);
66
+ }
67
+ return res.json();
68
+ }
69
+ function selectSourceChain(supportedChains, hasEvmWallet, hasSolanaWallet, preferredSourceChainId) {
70
+ if (preferredSourceChainId && hasEvmWallet) {
71
+ const preferred = `eip155:${preferredSourceChainId}`;
72
+ if (supportedChains.includes(preferred)) {
73
+ return { type: "evm", chain: preferred };
74
+ }
75
+ }
76
+ if (hasSolanaWallet) {
77
+ const sol = supportedChains.find((c) => c.startsWith("solana:"));
78
+ if (sol) return { type: "solana", chain: sol };
79
+ }
80
+ if (hasEvmWallet) {
81
+ for (const chain of supportedChains) {
82
+ if (chain.startsWith("eip155:")) {
83
+ return { type: "evm", chain };
84
+ }
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ function computeSourceAmount(targetAmount, feeBps) {
90
+ const fee = targetAmount * BigInt(feeBps) / 10000n;
91
+ return targetAmount + fee;
92
+ }
93
+ var DEFAULT_EVM_RPC = {
94
+ "eip155:8453": "https://mainnet.base.org",
95
+ "eip155:137": "https://polygon-rpc.com",
96
+ "eip155:1": "https://eth.llamarpc.com",
97
+ "eip155:42161": "https://arb1.arbitrum.io/rpc",
98
+ "eip155:43114": "https://api.avax.network/ext/bc/C/rpc",
99
+ "eip155:1187947933": "https://skale-base.skalenodes.com/v1/base"
100
+ };
101
+ // Annotate the CommonJS export names for ESM import in node:
102
+ 0 && (module.exports = {
103
+ DEFAULT_EVM_RPC,
104
+ computeSourceAmount,
105
+ getBridgeInfo,
106
+ selectSourceChain,
107
+ settleBridge
108
+ });
109
+ //# sourceMappingURL=bridge.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge.ts"],"sourcesContent":["/**\n * Core bridge utilities — shared between x402 and MPP clients.\n *\n * Auto-discovers bridge info from the RelAI API and provides helpers\n * for executing cross-chain payments transparently.\n *\n * Usage (MPP):\n * ```ts\n * import { evmChargeWithBridge } from '@relai-fi/x402/mpp/with-bridge'\n * ```\n *\n * Usage (x402):\n * ```ts\n * import { createX402Client } from '@relai-fi/x402'\n * const client = createX402Client({ ..., bridge: { enabled: true } })\n * ```\n */\n\nconst RELAI_API_BASE = 'https://api.relai.fi'\n\nexport interface BridgeInfo {\n settleEndpoint: string\n supportedSourceChains: string[]\n supportedSourceAssets: string[]\n payTo: Record<string, string>\n feePayerSvm: string | null\n feeBps: number\n paymentFacilitator: string\n}\n\nexport interface BridgeSettleRequest {\n /** x402 mode: base64-encoded payment header */\n sourcePayment?: string\n /** MPP mode: source tx hash */\n sourceTxHash?: string\n /** MPP mode: wallet address that sent the source tx */\n senderAddress?: string\n /** MPP mode: signature of (sourceTxHash + JSON(targetAccept)) */\n senderSignature?: string\n /** CAIP-2 source chain identifier */\n sourceChain: string\n targetAccept: {\n scheme: 'exact'\n network: string\n asset: string\n payTo: string\n amount: string\n }\n /** x402 payment requirements (full 402 body) — needed by settle for x402 flow */\n requirements?: any\n resource?: string\n paymentFacilitator?: string | null\n}\n\nexport interface BridgeSettleResponse {\n success?: boolean\n targetTxId?: string\n sourceTxId?: string\n /** x402 payment header (returned by x402-flavored settle) */\n xPayment?: string\n}\n\n// ── Cache (keyed by base URL) ──────────────────────────────────────────\n\nconst _cacheMap = new Map<string, { info: BridgeInfo; time: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * Fetch bridge info from the RelAI API (cached per base URL for 5 minutes).\n */\nexport async function getBridgeInfo(baseUrl = RELAI_API_BASE): Promise<BridgeInfo> {\n const key = baseUrl.replace(/\\/$/, '')\n const now = Date.now()\n const cached = _cacheMap.get(key)\n if (cached && now - cached.time < CACHE_TTL) return cached.info\n\n const url = `${key}/bridge/info`\n const res = await fetch(url)\n if (!res.ok) {\n if (cached) return cached.info // stale cache better than failure\n throw new Error(`[relai:bridge] Failed to fetch ${url}: ${res.status}`)\n }\n\n const data = (await res.json()) as any\n const info: BridgeInfo = {\n settleEndpoint: data.settleEndpoint,\n supportedSourceChains: data.supportedSourceChains || [],\n supportedSourceAssets: data.supportedSourceAssets || [],\n payTo: data.payTo || {},\n feePayerSvm: data.feePayerSvm ?? null,\n feeBps: data.feeBps ?? 100,\n paymentFacilitator: data.paymentFacilitator || 'https://facilitator.x402.fi',\n }\n _cacheMap.set(key, { info, time: now })\n return info\n}\n\n/**\n * Call the bridge settle endpoint.\n */\nexport async function settleBridge(\n settleEndpoint: string,\n body: BridgeSettleRequest,\n): Promise<BridgeSettleResponse> {\n const res = await fetch(settleEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const err = (await res.json().catch(() => ({}))) as any\n throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? ' — ' + err.details : ''}`)\n }\n\n return res.json() as Promise<BridgeSettleResponse>\n}\n\n/**\n * Select a source chain from supported chains that matches the available wallet.\n */\nexport function selectSourceChain(\n supportedChains: string[],\n hasEvmWallet: boolean,\n hasSolanaWallet: boolean,\n preferredSourceChainId?: number,\n): { type: 'evm' | 'solana'; chain: string } | null {\n // If a specific source chain is preferred, try it first\n if (preferredSourceChainId && hasEvmWallet) {\n const preferred = `eip155:${preferredSourceChainId}`\n if (supportedChains.includes(preferred)) {\n return { type: 'evm', chain: preferred }\n }\n }\n\n // Solana first (lower fees)\n if (hasSolanaWallet) {\n const sol = supportedChains.find((c) => c.startsWith('solana:'))\n if (sol) return { type: 'solana', chain: sol }\n }\n\n // Then any EVM chain\n if (hasEvmWallet) {\n for (const chain of supportedChains) {\n if (chain.startsWith('eip155:')) {\n return { type: 'evm', chain }\n }\n }\n }\n\n return null\n}\n\n/**\n * Compute source amount including bridge fee.\n */\nexport function computeSourceAmount(targetAmount: bigint, feeBps: number): bigint {\n const fee = (targetAmount * BigInt(feeBps)) / 10000n\n return targetAmount + fee\n}\n\n// Known EVM RPC URLs (fallback)\nexport const DEFAULT_EVM_RPC: Record<string, string> = {\n 'eip155:8453': 'https://mainnet.base.org',\n 'eip155:137': 'https://polygon-rpc.com',\n 'eip155:1': 'https://eth.llamarpc.com',\n 'eip155:42161': 'https://arb1.arbitrum.io/rpc',\n 'eip155:43114': 'https://api.avax.network/ext/bc/C/rpc',\n 'eip155:1187947933': 'https://skale-base.skalenodes.com/v1/base',\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,IAAM,iBAAiB;AA8CvB,IAAM,YAAY,oBAAI,IAAgD;AACtE,IAAM,YAAY,IAAI,KAAK;AAK3B,eAAsB,cAAc,UAAU,gBAAqC;AACjF,QAAM,MAAM,QAAQ,QAAQ,OAAO,EAAE;AACrC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,GAAG;AAChC,MAAI,UAAU,MAAM,OAAO,OAAO,UAAW,QAAO,OAAO;AAE3D,QAAM,MAAM,GAAG,GAAG;AAClB,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,OAAQ,QAAO,OAAO;AAC1B,UAAM,IAAI,MAAM,kCAAkC,GAAG,KAAK,IAAI,MAAM,EAAE;AAAA,EACxE;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAM,OAAmB;AAAA,IACvB,gBAAgB,KAAK;AAAA,IACrB,uBAAuB,KAAK,yBAAyB,CAAC;AAAA,IACtD,uBAAuB,KAAK,yBAAyB,CAAC;AAAA,IACtD,OAAO,KAAK,SAAS,CAAC;AAAA,IACtB,aAAa,KAAK,eAAe;AAAA,IACjC,QAAQ,KAAK,UAAU;AAAA,IACvB,oBAAoB,KAAK,sBAAsB;AAAA,EACjD;AACA,YAAU,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;AACtC,SAAO;AACT;AAKA,eAAsB,aACpB,gBACA,MAC+B;AAC/B,QAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,IAAI,MAAM,iCAAiC,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,UAAU,aAAQ,IAAI,UAAU,EAAE,EAAE;AAAA,EACrH;AAEA,SAAO,IAAI,KAAK;AAClB;AAKO,SAAS,kBACd,iBACA,cACA,iBACA,wBACkD;AAElD,MAAI,0BAA0B,cAAc;AAC1C,UAAM,YAAY,UAAU,sBAAsB;AAClD,QAAI,gBAAgB,SAAS,SAAS,GAAG;AACvC,aAAO,EAAE,MAAM,OAAO,OAAO,UAAU;AAAA,IACzC;AAAA,EACF;AAGA,MAAI,iBAAiB;AACnB,UAAM,MAAM,gBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAC/D,QAAI,IAAK,QAAO,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,EAC/C;AAGA,MAAI,cAAc;AAChB,eAAW,SAAS,iBAAiB;AACnC,UAAI,MAAM,WAAW,SAAS,GAAG;AAC/B,eAAO,EAAE,MAAM,OAAO,MAAM;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,oBAAoB,cAAsB,QAAwB;AAChF,QAAM,MAAO,eAAe,OAAO,MAAM,IAAK;AAC9C,SAAO,eAAe;AACxB;AAGO,IAAM,kBAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,qBAAqB;AACvB;","names":[]}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Core bridge utilities — shared between x402 and MPP clients.
3
+ *
4
+ * Auto-discovers bridge info from the RelAI API and provides helpers
5
+ * for executing cross-chain payments transparently.
6
+ *
7
+ * Usage (MPP):
8
+ * ```ts
9
+ * import { evmChargeWithBridge } from '@relai-fi/x402/mpp/with-bridge'
10
+ * ```
11
+ *
12
+ * Usage (x402):
13
+ * ```ts
14
+ * import { createX402Client } from '@relai-fi/x402'
15
+ * const client = createX402Client({ ..., bridge: { enabled: true } })
16
+ * ```
17
+ */
18
+ interface BridgeInfo {
19
+ settleEndpoint: string;
20
+ supportedSourceChains: string[];
21
+ supportedSourceAssets: string[];
22
+ payTo: Record<string, string>;
23
+ feePayerSvm: string | null;
24
+ feeBps: number;
25
+ paymentFacilitator: string;
26
+ }
27
+ interface BridgeSettleRequest {
28
+ /** x402 mode: base64-encoded payment header */
29
+ sourcePayment?: string;
30
+ /** MPP mode: source tx hash */
31
+ sourceTxHash?: string;
32
+ /** MPP mode: wallet address that sent the source tx */
33
+ senderAddress?: string;
34
+ /** MPP mode: signature of (sourceTxHash + JSON(targetAccept)) */
35
+ senderSignature?: string;
36
+ /** CAIP-2 source chain identifier */
37
+ sourceChain: string;
38
+ targetAccept: {
39
+ scheme: 'exact';
40
+ network: string;
41
+ asset: string;
42
+ payTo: string;
43
+ amount: string;
44
+ };
45
+ /** x402 payment requirements (full 402 body) — needed by settle for x402 flow */
46
+ requirements?: any;
47
+ resource?: string;
48
+ paymentFacilitator?: string | null;
49
+ }
50
+ interface BridgeSettleResponse {
51
+ success?: boolean;
52
+ targetTxId?: string;
53
+ sourceTxId?: string;
54
+ /** x402 payment header (returned by x402-flavored settle) */
55
+ xPayment?: string;
56
+ }
57
+ /**
58
+ * Fetch bridge info from the RelAI API (cached per base URL for 5 minutes).
59
+ */
60
+ declare function getBridgeInfo(baseUrl?: string): Promise<BridgeInfo>;
61
+ /**
62
+ * Call the bridge settle endpoint.
63
+ */
64
+ declare function settleBridge(settleEndpoint: string, body: BridgeSettleRequest): Promise<BridgeSettleResponse>;
65
+ /**
66
+ * Select a source chain from supported chains that matches the available wallet.
67
+ */
68
+ declare function selectSourceChain(supportedChains: string[], hasEvmWallet: boolean, hasSolanaWallet: boolean, preferredSourceChainId?: number): {
69
+ type: 'evm' | 'solana';
70
+ chain: string;
71
+ } | null;
72
+ /**
73
+ * Compute source amount including bridge fee.
74
+ */
75
+ declare function computeSourceAmount(targetAmount: bigint, feeBps: number): bigint;
76
+ declare const DEFAULT_EVM_RPC: Record<string, string>;
77
+
78
+ export { type BridgeInfo, type BridgeSettleRequest, type BridgeSettleResponse, DEFAULT_EVM_RPC, computeSourceAmount, getBridgeInfo, selectSourceChain, settleBridge };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Core bridge utilities — shared between x402 and MPP clients.
3
+ *
4
+ * Auto-discovers bridge info from the RelAI API and provides helpers
5
+ * for executing cross-chain payments transparently.
6
+ *
7
+ * Usage (MPP):
8
+ * ```ts
9
+ * import { evmChargeWithBridge } from '@relai-fi/x402/mpp/with-bridge'
10
+ * ```
11
+ *
12
+ * Usage (x402):
13
+ * ```ts
14
+ * import { createX402Client } from '@relai-fi/x402'
15
+ * const client = createX402Client({ ..., bridge: { enabled: true } })
16
+ * ```
17
+ */
18
+ interface BridgeInfo {
19
+ settleEndpoint: string;
20
+ supportedSourceChains: string[];
21
+ supportedSourceAssets: string[];
22
+ payTo: Record<string, string>;
23
+ feePayerSvm: string | null;
24
+ feeBps: number;
25
+ paymentFacilitator: string;
26
+ }
27
+ interface BridgeSettleRequest {
28
+ /** x402 mode: base64-encoded payment header */
29
+ sourcePayment?: string;
30
+ /** MPP mode: source tx hash */
31
+ sourceTxHash?: string;
32
+ /** MPP mode: wallet address that sent the source tx */
33
+ senderAddress?: string;
34
+ /** MPP mode: signature of (sourceTxHash + JSON(targetAccept)) */
35
+ senderSignature?: string;
36
+ /** CAIP-2 source chain identifier */
37
+ sourceChain: string;
38
+ targetAccept: {
39
+ scheme: 'exact';
40
+ network: string;
41
+ asset: string;
42
+ payTo: string;
43
+ amount: string;
44
+ };
45
+ /** x402 payment requirements (full 402 body) — needed by settle for x402 flow */
46
+ requirements?: any;
47
+ resource?: string;
48
+ paymentFacilitator?: string | null;
49
+ }
50
+ interface BridgeSettleResponse {
51
+ success?: boolean;
52
+ targetTxId?: string;
53
+ sourceTxId?: string;
54
+ /** x402 payment header (returned by x402-flavored settle) */
55
+ xPayment?: string;
56
+ }
57
+ /**
58
+ * Fetch bridge info from the RelAI API (cached per base URL for 5 minutes).
59
+ */
60
+ declare function getBridgeInfo(baseUrl?: string): Promise<BridgeInfo>;
61
+ /**
62
+ * Call the bridge settle endpoint.
63
+ */
64
+ declare function settleBridge(settleEndpoint: string, body: BridgeSettleRequest): Promise<BridgeSettleResponse>;
65
+ /**
66
+ * Select a source chain from supported chains that matches the available wallet.
67
+ */
68
+ declare function selectSourceChain(supportedChains: string[], hasEvmWallet: boolean, hasSolanaWallet: boolean, preferredSourceChainId?: number): {
69
+ type: 'evm' | 'solana';
70
+ chain: string;
71
+ } | null;
72
+ /**
73
+ * Compute source amount including bridge fee.
74
+ */
75
+ declare function computeSourceAmount(targetAmount: bigint, feeBps: number): bigint;
76
+ declare const DEFAULT_EVM_RPC: Record<string, string>;
77
+
78
+ export { type BridgeInfo, type BridgeSettleRequest, type BridgeSettleResponse, DEFAULT_EVM_RPC, computeSourceAmount, getBridgeInfo, selectSourceChain, settleBridge };
package/dist/bridge.js ADDED
@@ -0,0 +1,80 @@
1
+ // src/bridge.ts
2
+ var RELAI_API_BASE = "https://api.relai.fi";
3
+ var _cacheMap = /* @__PURE__ */ new Map();
4
+ var CACHE_TTL = 5 * 60 * 1e3;
5
+ async function getBridgeInfo(baseUrl = RELAI_API_BASE) {
6
+ const key = baseUrl.replace(/\/$/, "");
7
+ const now = Date.now();
8
+ const cached = _cacheMap.get(key);
9
+ if (cached && now - cached.time < CACHE_TTL) return cached.info;
10
+ const url = `${key}/bridge/info`;
11
+ const res = await fetch(url);
12
+ if (!res.ok) {
13
+ if (cached) return cached.info;
14
+ throw new Error(`[relai:bridge] Failed to fetch ${url}: ${res.status}`);
15
+ }
16
+ const data = await res.json();
17
+ const info = {
18
+ settleEndpoint: data.settleEndpoint,
19
+ supportedSourceChains: data.supportedSourceChains || [],
20
+ supportedSourceAssets: data.supportedSourceAssets || [],
21
+ payTo: data.payTo || {},
22
+ feePayerSvm: data.feePayerSvm ?? null,
23
+ feeBps: data.feeBps ?? 100,
24
+ paymentFacilitator: data.paymentFacilitator || "https://facilitator.x402.fi"
25
+ };
26
+ _cacheMap.set(key, { info, time: now });
27
+ return info;
28
+ }
29
+ async function settleBridge(settleEndpoint, body) {
30
+ const res = await fetch(settleEndpoint, {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify(body)
34
+ });
35
+ if (!res.ok) {
36
+ const err = await res.json().catch(() => ({}));
37
+ throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? " \u2014 " + err.details : ""}`);
38
+ }
39
+ return res.json();
40
+ }
41
+ function selectSourceChain(supportedChains, hasEvmWallet, hasSolanaWallet, preferredSourceChainId) {
42
+ if (preferredSourceChainId && hasEvmWallet) {
43
+ const preferred = `eip155:${preferredSourceChainId}`;
44
+ if (supportedChains.includes(preferred)) {
45
+ return { type: "evm", chain: preferred };
46
+ }
47
+ }
48
+ if (hasSolanaWallet) {
49
+ const sol = supportedChains.find((c) => c.startsWith("solana:"));
50
+ if (sol) return { type: "solana", chain: sol };
51
+ }
52
+ if (hasEvmWallet) {
53
+ for (const chain of supportedChains) {
54
+ if (chain.startsWith("eip155:")) {
55
+ return { type: "evm", chain };
56
+ }
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ function computeSourceAmount(targetAmount, feeBps) {
62
+ const fee = targetAmount * BigInt(feeBps) / 10000n;
63
+ return targetAmount + fee;
64
+ }
65
+ var DEFAULT_EVM_RPC = {
66
+ "eip155:8453": "https://mainnet.base.org",
67
+ "eip155:137": "https://polygon-rpc.com",
68
+ "eip155:1": "https://eth.llamarpc.com",
69
+ "eip155:42161": "https://arb1.arbitrum.io/rpc",
70
+ "eip155:43114": "https://api.avax.network/ext/bc/C/rpc",
71
+ "eip155:1187947933": "https://skale-base.skalenodes.com/v1/base"
72
+ };
73
+ export {
74
+ DEFAULT_EVM_RPC,
75
+ computeSourceAmount,
76
+ getBridgeInfo,
77
+ selectSourceChain,
78
+ settleBridge
79
+ };
80
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge.ts"],"sourcesContent":["/**\n * Core bridge utilities — shared between x402 and MPP clients.\n *\n * Auto-discovers bridge info from the RelAI API and provides helpers\n * for executing cross-chain payments transparently.\n *\n * Usage (MPP):\n * ```ts\n * import { evmChargeWithBridge } from '@relai-fi/x402/mpp/with-bridge'\n * ```\n *\n * Usage (x402):\n * ```ts\n * import { createX402Client } from '@relai-fi/x402'\n * const client = createX402Client({ ..., bridge: { enabled: true } })\n * ```\n */\n\nconst RELAI_API_BASE = 'https://api.relai.fi'\n\nexport interface BridgeInfo {\n settleEndpoint: string\n supportedSourceChains: string[]\n supportedSourceAssets: string[]\n payTo: Record<string, string>\n feePayerSvm: string | null\n feeBps: number\n paymentFacilitator: string\n}\n\nexport interface BridgeSettleRequest {\n /** x402 mode: base64-encoded payment header */\n sourcePayment?: string\n /** MPP mode: source tx hash */\n sourceTxHash?: string\n /** MPP mode: wallet address that sent the source tx */\n senderAddress?: string\n /** MPP mode: signature of (sourceTxHash + JSON(targetAccept)) */\n senderSignature?: string\n /** CAIP-2 source chain identifier */\n sourceChain: string\n targetAccept: {\n scheme: 'exact'\n network: string\n asset: string\n payTo: string\n amount: string\n }\n /** x402 payment requirements (full 402 body) — needed by settle for x402 flow */\n requirements?: any\n resource?: string\n paymentFacilitator?: string | null\n}\n\nexport interface BridgeSettleResponse {\n success?: boolean\n targetTxId?: string\n sourceTxId?: string\n /** x402 payment header (returned by x402-flavored settle) */\n xPayment?: string\n}\n\n// ── Cache (keyed by base URL) ──────────────────────────────────────────\n\nconst _cacheMap = new Map<string, { info: BridgeInfo; time: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * Fetch bridge info from the RelAI API (cached per base URL for 5 minutes).\n */\nexport async function getBridgeInfo(baseUrl = RELAI_API_BASE): Promise<BridgeInfo> {\n const key = baseUrl.replace(/\\/$/, '')\n const now = Date.now()\n const cached = _cacheMap.get(key)\n if (cached && now - cached.time < CACHE_TTL) return cached.info\n\n const url = `${key}/bridge/info`\n const res = await fetch(url)\n if (!res.ok) {\n if (cached) return cached.info // stale cache better than failure\n throw new Error(`[relai:bridge] Failed to fetch ${url}: ${res.status}`)\n }\n\n const data = (await res.json()) as any\n const info: BridgeInfo = {\n settleEndpoint: data.settleEndpoint,\n supportedSourceChains: data.supportedSourceChains || [],\n supportedSourceAssets: data.supportedSourceAssets || [],\n payTo: data.payTo || {},\n feePayerSvm: data.feePayerSvm ?? null,\n feeBps: data.feeBps ?? 100,\n paymentFacilitator: data.paymentFacilitator || 'https://facilitator.x402.fi',\n }\n _cacheMap.set(key, { info, time: now })\n return info\n}\n\n/**\n * Call the bridge settle endpoint.\n */\nexport async function settleBridge(\n settleEndpoint: string,\n body: BridgeSettleRequest,\n): Promise<BridgeSettleResponse> {\n const res = await fetch(settleEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const err = (await res.json().catch(() => ({}))) as any\n throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? ' — ' + err.details : ''}`)\n }\n\n return res.json() as Promise<BridgeSettleResponse>\n}\n\n/**\n * Select a source chain from supported chains that matches the available wallet.\n */\nexport function selectSourceChain(\n supportedChains: string[],\n hasEvmWallet: boolean,\n hasSolanaWallet: boolean,\n preferredSourceChainId?: number,\n): { type: 'evm' | 'solana'; chain: string } | null {\n // If a specific source chain is preferred, try it first\n if (preferredSourceChainId && hasEvmWallet) {\n const preferred = `eip155:${preferredSourceChainId}`\n if (supportedChains.includes(preferred)) {\n return { type: 'evm', chain: preferred }\n }\n }\n\n // Solana first (lower fees)\n if (hasSolanaWallet) {\n const sol = supportedChains.find((c) => c.startsWith('solana:'))\n if (sol) return { type: 'solana', chain: sol }\n }\n\n // Then any EVM chain\n if (hasEvmWallet) {\n for (const chain of supportedChains) {\n if (chain.startsWith('eip155:')) {\n return { type: 'evm', chain }\n }\n }\n }\n\n return null\n}\n\n/**\n * Compute source amount including bridge fee.\n */\nexport function computeSourceAmount(targetAmount: bigint, feeBps: number): bigint {\n const fee = (targetAmount * BigInt(feeBps)) / 10000n\n return targetAmount + fee\n}\n\n// Known EVM RPC URLs (fallback)\nexport const DEFAULT_EVM_RPC: Record<string, string> = {\n 'eip155:8453': 'https://mainnet.base.org',\n 'eip155:137': 'https://polygon-rpc.com',\n 'eip155:1': 'https://eth.llamarpc.com',\n 'eip155:42161': 'https://arb1.arbitrum.io/rpc',\n 'eip155:43114': 'https://api.avax.network/ext/bc/C/rpc',\n 'eip155:1187947933': 'https://skale-base.skalenodes.com/v1/base',\n}\n"],"mappings":";AAkBA,IAAM,iBAAiB;AA8CvB,IAAM,YAAY,oBAAI,IAAgD;AACtE,IAAM,YAAY,IAAI,KAAK;AAK3B,eAAsB,cAAc,UAAU,gBAAqC;AACjF,QAAM,MAAM,QAAQ,QAAQ,OAAO,EAAE;AACrC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,GAAG;AAChC,MAAI,UAAU,MAAM,OAAO,OAAO,UAAW,QAAO,OAAO;AAE3D,QAAM,MAAM,GAAG,GAAG;AAClB,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,OAAQ,QAAO,OAAO;AAC1B,UAAM,IAAI,MAAM,kCAAkC,GAAG,KAAK,IAAI,MAAM,EAAE;AAAA,EACxE;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAM,OAAmB;AAAA,IACvB,gBAAgB,KAAK;AAAA,IACrB,uBAAuB,KAAK,yBAAyB,CAAC;AAAA,IACtD,uBAAuB,KAAK,yBAAyB,CAAC;AAAA,IACtD,OAAO,KAAK,SAAS,CAAC;AAAA,IACtB,aAAa,KAAK,eAAe;AAAA,IACjC,QAAQ,KAAK,UAAU;AAAA,IACvB,oBAAoB,KAAK,sBAAsB;AAAA,EACjD;AACA,YAAU,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;AACtC,SAAO;AACT;AAKA,eAAsB,aACpB,gBACA,MAC+B;AAC/B,QAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,IAAI,MAAM,iCAAiC,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,UAAU,aAAQ,IAAI,UAAU,EAAE,EAAE;AAAA,EACrH;AAEA,SAAO,IAAI,KAAK;AAClB;AAKO,SAAS,kBACd,iBACA,cACA,iBACA,wBACkD;AAElD,MAAI,0BAA0B,cAAc;AAC1C,UAAM,YAAY,UAAU,sBAAsB;AAClD,QAAI,gBAAgB,SAAS,SAAS,GAAG;AACvC,aAAO,EAAE,MAAM,OAAO,OAAO,UAAU;AAAA,IACzC;AAAA,EACF;AAGA,MAAI,iBAAiB;AACnB,UAAM,MAAM,gBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAC/D,QAAI,IAAK,QAAO,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,EAC/C;AAGA,MAAI,cAAc;AAChB,eAAW,SAAS,iBAAiB;AACnC,UAAI,MAAM,WAAW,SAAS,GAAG;AAC/B,eAAO,EAAE,MAAM,OAAO,MAAM;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,oBAAoB,cAAsB,QAAwB;AAChF,QAAM,MAAO,eAAe,OAAO,MAAM,IAAK;AAC9C,SAAO,eAAe;AACxB;AAGO,IAAM,kBAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,qBAAqB;AACvB;","names":[]}
package/dist/client.cjs CHANGED
@@ -240,6 +240,71 @@ function normalizeNetwork(network) {
240
240
  return null;
241
241
  }
242
242
 
243
+ // src/bridge.ts
244
+ var RELAI_API_BASE = "https://api.relai.fi";
245
+ var _cacheMap = /* @__PURE__ */ new Map();
246
+ var CACHE_TTL = 5 * 60 * 1e3;
247
+ async function getBridgeInfo(baseUrl = RELAI_API_BASE) {
248
+ const key = baseUrl.replace(/\/$/, "");
249
+ const now = Date.now();
250
+ const cached = _cacheMap.get(key);
251
+ if (cached && now - cached.time < CACHE_TTL) return cached.info;
252
+ const url = `${key}/bridge/info`;
253
+ const res = await fetch(url);
254
+ if (!res.ok) {
255
+ if (cached) return cached.info;
256
+ throw new Error(`[relai:bridge] Failed to fetch ${url}: ${res.status}`);
257
+ }
258
+ const data = await res.json();
259
+ const info = {
260
+ settleEndpoint: data.settleEndpoint,
261
+ supportedSourceChains: data.supportedSourceChains || [],
262
+ supportedSourceAssets: data.supportedSourceAssets || [],
263
+ payTo: data.payTo || {},
264
+ feePayerSvm: data.feePayerSvm ?? null,
265
+ feeBps: data.feeBps ?? 100,
266
+ paymentFacilitator: data.paymentFacilitator || "https://facilitator.x402.fi"
267
+ };
268
+ _cacheMap.set(key, { info, time: now });
269
+ return info;
270
+ }
271
+ async function settleBridge(settleEndpoint, body) {
272
+ const res = await fetch(settleEndpoint, {
273
+ method: "POST",
274
+ headers: { "Content-Type": "application/json" },
275
+ body: JSON.stringify(body)
276
+ });
277
+ if (!res.ok) {
278
+ const err = await res.json().catch(() => ({}));
279
+ throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? " \u2014 " + err.details : ""}`);
280
+ }
281
+ return res.json();
282
+ }
283
+ function selectSourceChain(supportedChains, hasEvmWallet, hasSolanaWallet, preferredSourceChainId) {
284
+ if (preferredSourceChainId && hasEvmWallet) {
285
+ const preferred = `eip155:${preferredSourceChainId}`;
286
+ if (supportedChains.includes(preferred)) {
287
+ return { type: "evm", chain: preferred };
288
+ }
289
+ }
290
+ if (hasSolanaWallet) {
291
+ const sol = supportedChains.find((c) => c.startsWith("solana:"));
292
+ if (sol) return { type: "solana", chain: sol };
293
+ }
294
+ if (hasEvmWallet) {
295
+ for (const chain of supportedChains) {
296
+ if (chain.startsWith("eip155:")) {
297
+ return { type: "evm", chain };
298
+ }
299
+ }
300
+ }
301
+ return null;
302
+ }
303
+ function computeSourceAmount(targetAmount, feeBps) {
304
+ const fee = targetAmount * BigInt(feeBps) / 10000n;
305
+ return targetAmount + fee;
306
+ }
307
+
243
308
  // src/client.ts
244
309
  var PERMIT_NETWORKS = /* @__PURE__ */ new Set([]);
245
310
  var DEFAULT_EVM_RPC_URLS = {
@@ -266,7 +331,8 @@ function createX402Client(config) {
266
331
  integritas,
267
332
  verbose = false,
268
333
  defaultHeaders = {},
269
- mpp
334
+ mpp,
335
+ bridge: bridgeConfig
270
336
  } = config;
271
337
  const relayWsEnabled = relayWs?.enabled === true;
272
338
  const relayWsPreflightTimeoutMs = relayWs?.preflightTimeoutMs ?? 5e3;
@@ -1381,6 +1447,70 @@ function createX402Client(config) {
1381
1447
  headers: { ...requestHeaders, "X-PAYMENT": paymentHeader }
1382
1448
  });
1383
1449
  }
1450
+ if (bridgeConfig?.enabled) {
1451
+ log("No direct wallet match \u2014 attempting auto-bridge via RelAI API");
1452
+ try {
1453
+ const info = await getBridgeInfo(bridgeConfig.baseUrl);
1454
+ const targetAccept = accepts[0];
1455
+ const hasEvm = !!effectiveWallets.evm;
1456
+ const hasSol = !!hasSolanaWallet;
1457
+ const source = selectSourceChain(info.supportedSourceChains, hasEvm, hasSol);
1458
+ if (source) {
1459
+ const bridgePayTo = info.payTo[source.chain];
1460
+ if (bridgePayTo) {
1461
+ const targetAmount = targetAccept.amount || targetAccept.maxAmountRequired;
1462
+ const sourceAmount = computeSourceAmount(BigInt(targetAmount), info.feeBps).toString();
1463
+ const sourceChainIdx = info.supportedSourceChains.indexOf(source.chain);
1464
+ const sourceAsset = sourceChainIdx >= 0 && info.supportedSourceAssets[sourceChainIdx] || (source.type === "evm" ? info.supportedSourceAssets.find((a) => a.startsWith("0x")) || targetAccept.asset : info.supportedSourceAssets.find((a) => !a.startsWith("0x")) || "");
1465
+ let sourcePaymentHeader;
1466
+ const sourceAccept = {
1467
+ scheme: "exact",
1468
+ network: source.chain,
1469
+ asset: sourceAsset,
1470
+ payTo: bridgePayTo,
1471
+ amount: sourceAmount,
1472
+ extra: {
1473
+ ...targetAccept.extra || {},
1474
+ ...source.type === "solana" && info.feePayerSvm ? { feePayer: info.feePayerSvm } : {}
1475
+ }
1476
+ };
1477
+ if (source.type === "solana" && hasSolanaWallet) {
1478
+ sourcePaymentHeader = await buildSolanaPayment(sourceAccept, requirements, url);
1479
+ } else if (source.type === "evm") {
1480
+ const evmNetwork = normalizeNetwork(source.chain);
1481
+ const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);
1482
+ sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url) : await buildEvmPayment(sourceAccept, requirements, url);
1483
+ } else {
1484
+ throw new Error(`[relai-x402] No wallet for source chain type: ${source.type}`);
1485
+ }
1486
+ const settleData = await settleBridge(info.settleEndpoint, {
1487
+ sourcePayment: sourcePaymentHeader,
1488
+ sourceChain: source.chain,
1489
+ targetAccept: {
1490
+ scheme: "exact",
1491
+ network: targetAccept.network,
1492
+ asset: targetAccept.asset,
1493
+ payTo: targetAccept.payTo,
1494
+ amount: targetAmount
1495
+ },
1496
+ requirements,
1497
+ resource: url,
1498
+ paymentFacilitator: info.paymentFacilitator
1499
+ });
1500
+ if (settleData.xPayment) {
1501
+ log(`Auto-bridge settled: target=${settleData.targetTxId}`);
1502
+ return fetch(input, {
1503
+ ...requestInitWithHeaders,
1504
+ headers: { ...requestHeaders, "X-PAYMENT": settleData.xPayment }
1505
+ });
1506
+ }
1507
+ throw new Error("[relai-x402] Bridge settle did not return xPayment");
1508
+ }
1509
+ }
1510
+ } catch (bridgeErr) {
1511
+ log(`Auto-bridge failed: ${bridgeErr instanceof Error ? bridgeErr.message : bridgeErr}`);
1512
+ }
1513
+ }
1384
1514
  throw new Error(buildNoWalletError(accepts, false));
1385
1515
  }
1386
1516
  const { accept, chain } = selected;