@relai-fi/x402 0.5.28 → 0.5.30

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.
package/dist/index.cjs CHANGED
@@ -1260,6 +1260,88 @@ function createX402Client(config) {
1260
1260
  }
1261
1261
  return null;
1262
1262
  }
1263
+ function getBridgeExtension(requirements) {
1264
+ const ext = requirements?.extensions?.bridge;
1265
+ if (!ext?.info?.endpoint || !Array.isArray(ext.info.supportedSourceChains)) return null;
1266
+ return ext.info;
1267
+ }
1268
+ function selectBridgeSource(bridge) {
1269
+ const sourceChains = bridge.supportedSourceChains || [];
1270
+ const sourceAssets = bridge.supportedSourceAssets || [];
1271
+ for (const caip2 of sourceChains) {
1272
+ if (isSolana(caip2) && hasSolanaWallet) {
1273
+ const asset = sourceAssets.find((a) => !a.startsWith("0x")) || "";
1274
+ return { chain: "solana", network: caip2, asset };
1275
+ }
1276
+ if (isEvm(caip2) && effectiveWallets.evm) {
1277
+ const asset = sourceAssets.find((a) => a.startsWith("0x")) || "";
1278
+ return { chain: "evm", network: caip2, asset };
1279
+ }
1280
+ }
1281
+ return null;
1282
+ }
1283
+ async function executeBridgePayment(bridge, accepts, requirements, url) {
1284
+ const source = selectBridgeSource(bridge);
1285
+ if (!source) {
1286
+ throw new Error("[relai-x402] bridge extension found but no wallet matches supported source chains");
1287
+ }
1288
+ const targetAccept = accepts[0];
1289
+ const amount = targetAccept.amount || targetAccept.maxAmountRequired;
1290
+ log(`Bridge: ${source.network} \u2192 ${targetAccept.network}, amount=${amount}`);
1291
+ if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {
1292
+ throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);
1293
+ }
1294
+ let sourcePaymentHeader;
1295
+ if (source.chain === "solana") {
1296
+ if (!bridge.payTo) {
1297
+ throw new Error("[relai-x402] bridge.info.payTo is required for Solana source payments");
1298
+ }
1299
+ const sourceAccept = {
1300
+ scheme: "exact",
1301
+ network: source.network,
1302
+ asset: source.asset,
1303
+ payTo: bridge.payTo,
1304
+ amount: targetAccept.amount || targetAccept.maxAmountRequired,
1305
+ extra: {
1306
+ ...bridge.feePayer ? { feePayer: bridge.feePayer } : {},
1307
+ decimals: 6
1308
+ }
1309
+ };
1310
+ sourcePaymentHeader = await buildSolanaPayment(sourceAccept, requirements, url);
1311
+ } else {
1312
+ const evmNetwork = normalizeNetwork(source.network);
1313
+ const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);
1314
+ const sourceAccept = {
1315
+ ...targetAccept,
1316
+ network: source.network,
1317
+ asset: source.asset || targetAccept.asset,
1318
+ ...bridge.payTo ? { payTo: bridge.payTo } : {}
1319
+ };
1320
+ sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url) : await buildEvmPayment(sourceAccept, requirements, url);
1321
+ }
1322
+ const bridgeRes = await fetch(bridge.endpoint, {
1323
+ method: "POST",
1324
+ headers: { "Content-Type": "application/json" },
1325
+ body: JSON.stringify({
1326
+ sourcePayment: sourcePaymentHeader,
1327
+ sourceChain: source.network,
1328
+ targetAccept,
1329
+ requirements,
1330
+ resource: url,
1331
+ paymentFacilitator: bridge.paymentFacilitator || null
1332
+ })
1333
+ });
1334
+ if (!bridgeRes.ok) {
1335
+ const err = await bridgeRes.json().catch(() => ({}));
1336
+ throw new Error(`[relai-x402] bridge settle failed: ${err.error || bridgeRes.status}`);
1337
+ }
1338
+ const bridgeData = await bridgeRes.json();
1339
+ if (!bridgeData.xPayment) {
1340
+ throw new Error("[relai-x402] bridge endpoint did not return xPayment header");
1341
+ }
1342
+ log(`Bridge settled: sourceTx=${bridgeData.sourceTxId}, targetTx=${bridgeData.targetTxId}`);
1343
+ return bridgeData.xPayment;
1344
+ }
1263
1345
  async function evmRpcCall(rpcUrl, to, data) {
1264
1346
  const res = await fetch(rpcUrl, {
1265
1347
  method: "POST",
@@ -1517,19 +1599,11 @@ function createX402Client(config) {
1517
1599
  [],
1518
1600
  programId
1519
1601
  );
1520
- const createDestAtaIx = (0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(
1521
- feePayerPubkey,
1522
- // payer (feePayer covers this)
1523
- destinationAta,
1524
- merchantPubkey,
1525
- mintPubkey,
1526
- programId
1527
- );
1528
1602
  const { blockhash } = await connection.getLatestBlockhash("confirmed");
1529
1603
  const message = new import_web3.TransactionMessage({
1530
1604
  payerKey: feePayerPubkey,
1531
1605
  recentBlockhash: blockhash,
1532
- instructions: [createDestAtaIx, transferIx]
1606
+ instructions: [transferIx]
1533
1607
  }).compileToV0Message();
1534
1608
  const transaction = new import_web3.VersionedTransaction(message);
1535
1609
  const signedTx = await solWallet.signTransaction(transaction);
@@ -1712,6 +1786,16 @@ function createX402Client(config) {
1712
1786
  if (!accepts.length) throw new Error("[relai-x402] No payment options in 402 response");
1713
1787
  const selected = selectAccept(accepts);
1714
1788
  if (!selected) {
1789
+ const bridge = getBridgeExtension(requirements);
1790
+ if (bridge && selectBridgeSource(bridge)) {
1791
+ log("No direct wallet match \u2014 attempting bridge extension flow");
1792
+ const paymentHeader = await executeBridgePayment(bridge, accepts, requirements, url);
1793
+ log("Retrying with X-PAYMENT header (bridge)");
1794
+ return fetch(input, {
1795
+ ...requestInitWithHeaders,
1796
+ headers: { ...requestHeaders, "X-PAYMENT": paymentHeader }
1797
+ });
1798
+ }
1715
1799
  throw new Error(buildNoWalletError(accepts, false));
1716
1800
  }
1717
1801
  const { accept, chain } = selected;