@relai-fi/x402 0.6.4 → 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 +209 -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 +209 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/index.cjs +327 -74
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +327 -74
  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 +209 -1
  59. package/dist/react/index.cjs.map +1 -1
  60. package/dist/react/index.js +209 -1
  61. package/dist/react/index.js.map +1 -1
  62. package/dist/server.cjs +8 -40
  63. package/dist/server.cjs.map +1 -1
  64. package/dist/server.js +8 -40
  65. package/dist/server.js.map +1 -1
  66. package/package.json +32 -1
package/dist/index.cjs CHANGED
@@ -7539,7 +7539,8 @@ var Relai = class {
7539
7539
  }
7540
7540
  const isRelAI = this.facilitatorUrl.includes("facilitator.x402.fi") || this.facilitatorUrl.includes("relai");
7541
7541
  if (isRelAI) {
7542
- const relaiFeePayer = "0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085";
7542
+ const isSolanaNetwork2 = caip2.startsWith("solana:");
7543
+ const relaiFeePayer = isSolanaNetwork2 ? "4x4ZhcqiT1FnirM8Ne97iVupkN4NcQgc2YYbE2jDZbZn" : "0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085";
7543
7544
  this.feePayerCache.set(caip2, relaiFeePayer);
7544
7545
  return relaiFeePayer;
7545
7546
  }
@@ -7623,7 +7624,6 @@ var Relai = class {
7623
7624
  const integritasMode = integritasFlow === "single" ? "single_signature_fee_included" : integritasFlow === "dual" ? "dual_signature_split" : void 0;
7624
7625
  const paymentHeader = req.headers["x-payment"] || req.headers["payment-signature"] || req.headers["x-payment-signature"];
7625
7626
  const authHeader = req.headers["authorization"] || "";
7626
- console.log(`[Relai] MPP check: paymentHeader=${!!paymentHeader}, hasMpp=${!!self.mpp}, authHeader=${authHeader?.slice(0, 30)}`);
7627
7627
  if (!paymentHeader && self.mpp && /^Payment\s+/i.test(authHeader)) {
7628
7628
  try {
7629
7629
  const mppAmount = resolvedPrice.toFixed(6);
@@ -7635,10 +7635,7 @@ var Relai = class {
7635
7635
  const mppRequest = new Request(mppUrl, { method: req.method, headers: mppHeaders });
7636
7636
  const chargeHandler = self.mpp.charge({ amount: mppAmount });
7637
7637
  const mppResult = await chargeHandler(mppRequest);
7638
- console.log(`[Relai] MPP charge result: status=${mppResult.status}, keys=${Object.keys(mppResult)}, hasChallenge=${!!mppResult.challenge}, hasWithReceipt=${!!mppResult.withReceipt}`);
7639
7638
  if (mppResult.status === 402 && mppResult.challenge instanceof Response) {
7640
- const retryAuth = mppResult.challenge.headers.get("www-authenticate");
7641
- console.log(`[Relai] MPP re-challenged (credential not accepted). New WWW-Auth: ${retryAuth?.slice(0, 60)}`);
7642
7639
  }
7643
7640
  if (mppResult.status !== 200 && !mppResult.withReceipt && mppResult.status !== 402) {
7644
7641
  if (self.plugins.length > 0) {
@@ -7681,7 +7678,10 @@ var Relai = class {
7681
7678
  const dummyResponse = new Response(null);
7682
7679
  const receiptResponse = mppResult.withReceipt(dummyResponse);
7683
7680
  const receiptHeader = receiptResponse.headers.get("payment-receipt");
7684
- if (receiptHeader) res.setHeader("Payment-Receipt", receiptHeader);
7681
+ if (receiptHeader) {
7682
+ res.setHeader?.("Payment-Receipt", receiptHeader);
7683
+ res.setHeader?.("Cache-Control", "private");
7684
+ }
7685
7685
  }
7686
7686
  options.onPaymentSettled?.(req, {
7687
7687
  success: true,
@@ -7898,12 +7898,13 @@ var Relai = class {
7898
7898
  if (mppResult?.challenge instanceof Response) {
7899
7899
  const wwwAuth = mppResult.challenge.headers.get("www-authenticate");
7900
7900
  if (wwwAuth) {
7901
- res.setHeader("WWW-Authenticate", wwwAuth);
7901
+ res.setHeader?.("WWW-Authenticate", wwwAuth);
7902
7902
  }
7903
7903
  }
7904
7904
  } catch {
7905
7905
  }
7906
7906
  }
7907
+ res.setHeader?.("Cache-Control", "no-store");
7907
7908
  return res.status(402).json(paymentRequiredResponse);
7908
7909
  }
7909
7910
  let paymentProof;
@@ -7920,39 +7921,6 @@ var Relai = class {
7920
7921
  });
7921
7922
  }
7922
7923
  }
7923
- if (paymentProof.bridged === true && paymentProof.targetTxId) {
7924
- console.log(`[Relai] Bridged payment accepted: source=${paymentProof.sourceTxId}, target=${paymentProof.targetTxId}`);
7925
- const paymentInfo2 = {
7926
- verified: true,
7927
- transactionId: paymentProof.targetTxId,
7928
- payer: paymentProof.sourceTxId || "bridge",
7929
- network,
7930
- amount: resolvedPrice
7931
- };
7932
- req.payment = paymentInfo2;
7933
- req.x402Payer = paymentProof.sourceTxId || "bridge";
7934
- req.x402Paid = true;
7935
- req.x402Transaction = paymentProof.targetTxId;
7936
- req.x402Network = network;
7937
- req.x402Bridged = true;
7938
- req.x402SourceChain = paymentProof.sourceChain;
7939
- const paymentResponse2 = {
7940
- x402Version: 2,
7941
- scheme: "exact",
7942
- network: caip2,
7943
- transaction: paymentProof.targetTxId,
7944
- payer: paymentProof.sourceTxId,
7945
- amount: amount2,
7946
- asset,
7947
- bridged: true
7948
- };
7949
- res.setHeader(
7950
- "PAYMENT-RESPONSE",
7951
- Buffer.from(JSON.stringify(paymentResponse2)).toString("base64")
7952
- );
7953
- options.onPaymentSettled?.(req, { success: true, transaction: paymentProof.targetTxId, payer: paymentProof.sourceTxId });
7954
- return next();
7955
- }
7956
7924
  let settlePayTo;
7957
7925
  if (stripeConfig) {
7958
7926
  settlePayTo = paymentProof.payload?.authorization?.to || paymentProof.accepted?.payTo || "";
@@ -8099,6 +8067,73 @@ var server_default = Relai;
8099
8067
  // src/client.ts
8100
8068
  var import_web3 = require("@solana/web3.js");
8101
8069
  var import_spl_token = require("@solana/spl-token");
8070
+
8071
+ // src/bridge.ts
8072
+ var RELAI_API_BASE = "https://api.relai.fi";
8073
+ var _cacheMap = /* @__PURE__ */ new Map();
8074
+ var CACHE_TTL = 5 * 60 * 1e3;
8075
+ async function getBridgeInfo(baseUrl = RELAI_API_BASE) {
8076
+ const key = baseUrl.replace(/\/$/, "");
8077
+ const now = Date.now();
8078
+ const cached2 = _cacheMap.get(key);
8079
+ if (cached2 && now - cached2.time < CACHE_TTL) return cached2.info;
8080
+ const url2 = `${key}/bridge/info`;
8081
+ const res = await fetch(url2);
8082
+ if (!res.ok) {
8083
+ if (cached2) return cached2.info;
8084
+ throw new Error(`[relai:bridge] Failed to fetch ${url2}: ${res.status}`);
8085
+ }
8086
+ const data = await res.json();
8087
+ const info = {
8088
+ settleEndpoint: data.settleEndpoint,
8089
+ supportedSourceChains: data.supportedSourceChains || [],
8090
+ supportedSourceAssets: data.supportedSourceAssets || [],
8091
+ payTo: data.payTo || {},
8092
+ feePayerSvm: data.feePayerSvm ?? null,
8093
+ feeBps: data.feeBps ?? 100,
8094
+ paymentFacilitator: data.paymentFacilitator || "https://facilitator.x402.fi"
8095
+ };
8096
+ _cacheMap.set(key, { info, time: now });
8097
+ return info;
8098
+ }
8099
+ async function settleBridge(settleEndpoint, body) {
8100
+ const res = await fetch(settleEndpoint, {
8101
+ method: "POST",
8102
+ headers: { "Content-Type": "application/json" },
8103
+ body: JSON.stringify(body)
8104
+ });
8105
+ if (!res.ok) {
8106
+ const err = await res.json().catch(() => ({}));
8107
+ throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? " \u2014 " + err.details : ""}`);
8108
+ }
8109
+ return res.json();
8110
+ }
8111
+ function selectSourceChain(supportedChains, hasEvmWallet, hasSolanaWallet, preferredSourceChainId) {
8112
+ if (preferredSourceChainId && hasEvmWallet) {
8113
+ const preferred = `eip155:${preferredSourceChainId}`;
8114
+ if (supportedChains.includes(preferred)) {
8115
+ return { type: "evm", chain: preferred };
8116
+ }
8117
+ }
8118
+ if (hasSolanaWallet) {
8119
+ const sol = supportedChains.find((c) => c.startsWith("solana:"));
8120
+ if (sol) return { type: "solana", chain: sol };
8121
+ }
8122
+ if (hasEvmWallet) {
8123
+ for (const chain of supportedChains) {
8124
+ if (chain.startsWith("eip155:")) {
8125
+ return { type: "evm", chain };
8126
+ }
8127
+ }
8128
+ }
8129
+ return null;
8130
+ }
8131
+ function computeSourceAmount(targetAmount, feeBps) {
8132
+ const fee = targetAmount * BigInt(feeBps) / 10000n;
8133
+ return targetAmount + fee;
8134
+ }
8135
+
8136
+ // src/client.ts
8102
8137
  var PERMIT_NETWORKS = /* @__PURE__ */ new Set([]);
8103
8138
  var DEFAULT_EVM_RPC_URLS = {
8104
8139
  "skale-base": "https://skale-base.skalenodes.com/v1/base",
@@ -8124,7 +8159,8 @@ function createX402Client(config2) {
8124
8159
  integritas,
8125
8160
  verbose = false,
8126
8161
  defaultHeaders = {},
8127
- mpp
8162
+ mpp,
8163
+ bridge: bridgeConfig
8128
8164
  } = config2;
8129
8165
  const relayWsEnabled = relayWs?.enabled === true;
8130
8166
  const relayWsPreflightTimeoutMs = relayWs?.preflightTimeoutMs ?? 5e3;
@@ -8543,6 +8579,38 @@ function createX402Client(config2) {
8543
8579
  }
8544
8580
  return null;
8545
8581
  }
8582
+ function extractMppChallengeFromWsError(error48) {
8583
+ if (isRecord(error48) && typeof error48.mppChallenge === "string") {
8584
+ return error48.mppChallenge;
8585
+ }
8586
+ if (isRecord(error48.data) && typeof error48.data.mppChallenge === "string") {
8587
+ return error48.data.mppChallenge;
8588
+ }
8589
+ const metadata = isRecord(error48) ? error48.responseHeaders : void 0;
8590
+ if (isRecord(metadata)) {
8591
+ for (const [key, value] of Object.entries(metadata)) {
8592
+ if (key.toLowerCase() === "www-authenticate" && typeof value === "string") {
8593
+ if (/^Payment\s+/i.test(value.trim())) {
8594
+ return value;
8595
+ }
8596
+ }
8597
+ }
8598
+ }
8599
+ return null;
8600
+ }
8601
+ function buildSyntheticMppResponse(mppChallenge, wsError) {
8602
+ const headers = new Headers();
8603
+ headers.set("WWW-Authenticate", mppChallenge);
8604
+ const responseHeaders = isRecord(wsError) ? wsError.responseHeaders : void 0;
8605
+ if (isRecord(responseHeaders)) {
8606
+ for (const [key, value] of Object.entries(responseHeaders)) {
8607
+ if (typeof value === "string" && key.toLowerCase() !== "www-authenticate") {
8608
+ headers.set(key, value);
8609
+ }
8610
+ }
8611
+ }
8612
+ return new Response(null, { status: 402, headers });
8613
+ }
8546
8614
  function buildWsResponse(wsResponse) {
8547
8615
  const statusFromMetadata = isRecord(wsResponse.metadata) && typeof wsResponse.metadata.status === "number" ? wsResponse.metadata.status : 200;
8548
8616
  const status = Number.isInteger(statusFromMetadata) && statusFromMetadata >= 100 && statusFromMetadata <= 599 ? statusFromMetadata : 200;
@@ -9037,6 +9105,41 @@ function createX402Client(config2) {
9037
9105
  if (Number(wsPreflightResponse.error.code) !== 402) {
9038
9106
  throw new Error(wsPreflightResponse.error.message || "[relai-x402] WebSocket relay request failed");
9039
9107
  }
9108
+ if (mpp) {
9109
+ const mppChallenge = extractMppChallengeFromWsError(wsPreflightResponse.error);
9110
+ if (mppChallenge) {
9111
+ log("MPP challenge detected in WS 402 response");
9112
+ try {
9113
+ const syntheticResponse = buildSyntheticMppResponse(mppChallenge, wsPreflightResponse.error);
9114
+ const credential = await mpp.createCredential(syntheticResponse);
9115
+ if (credential) {
9116
+ log("MPP credential created, retrying via WS with Authorization: Payment");
9117
+ wsPaymentPhaseStarted = true;
9118
+ const wsMppResponse = await relayCallOverWebSocket({
9119
+ relayUrl: url2,
9120
+ requestMethod,
9121
+ requestHeaders: {
9122
+ ...requestHeaders,
9123
+ "Authorization": credential.startsWith("Payment ") ? credential : `Payment ${credential}`
9124
+ },
9125
+ requestBody,
9126
+ timeoutMs: relayWsPaymentTimeoutMs
9127
+ });
9128
+ if (!wsMppResponse.error) {
9129
+ return buildWsResponse(wsMppResponse);
9130
+ }
9131
+ if (Number(wsMppResponse.error.code) !== 402) {
9132
+ throw new Error(wsMppResponse.error.message || "[relai-x402] WebSocket MPP retry failed");
9133
+ }
9134
+ log("MPP retry via WS still returned 402, falling through to x402 WS flow");
9135
+ wsPaymentPhaseStarted = false;
9136
+ }
9137
+ } catch (mppWsErr) {
9138
+ if (wsPaymentPhaseStarted) throw mppWsErr;
9139
+ log(`MPP over WS failed (${mppWsErr instanceof Error ? mppWsErr.message : mppWsErr}), falling through to x402 WS flow`);
9140
+ }
9141
+ }
9142
+ }
9040
9143
  const wsRequirements = extractPaymentRequirementsFromWsError(wsPreflightResponse.error);
9041
9144
  if (!wsRequirements) {
9042
9145
  throw new Error(
@@ -9054,6 +9157,17 @@ function createX402Client(config2) {
9054
9157
  }
9055
9158
  const wsSelected = selectAccept(wsAccepts);
9056
9159
  if (!wsSelected) {
9160
+ const wsBridge = getBridgeExtension(wsRequirements);
9161
+ if (wsBridge && selectBridgeSource(wsBridge)) {
9162
+ log("No direct wallet match in WS flow \u2014 attempting bridge extension");
9163
+ wsPaymentPhaseStarted = true;
9164
+ const bridgePaymentHeader = await executeBridgePayment(wsBridge, wsAccepts, wsRequirements, url2);
9165
+ log("Retrying with X-PAYMENT header (bridge via WS fallback to HTTP)");
9166
+ return fetch(input, {
9167
+ ...stripInternalInit(init) || {},
9168
+ headers: { ...requestHeaders, "X-PAYMENT": bridgePaymentHeader }
9169
+ });
9170
+ }
9057
9171
  throw new Error(buildNoWalletError(wsAccepts, true));
9058
9172
  }
9059
9173
  const { accept: accept2, chain: chain2 } = wsSelected;
@@ -9161,6 +9275,70 @@ function createX402Client(config2) {
9161
9275
  headers: { ...requestHeaders, "X-PAYMENT": paymentHeader }
9162
9276
  });
9163
9277
  }
9278
+ if (bridgeConfig?.enabled) {
9279
+ log("No direct wallet match \u2014 attempting auto-bridge via RelAI API");
9280
+ try {
9281
+ const info = await getBridgeInfo(bridgeConfig.baseUrl);
9282
+ const targetAccept = accepts[0];
9283
+ const hasEvm = !!effectiveWallets.evm;
9284
+ const hasSol = !!hasSolanaWallet;
9285
+ const source = selectSourceChain(info.supportedSourceChains, hasEvm, hasSol);
9286
+ if (source) {
9287
+ const bridgePayTo = info.payTo[source.chain];
9288
+ if (bridgePayTo) {
9289
+ const targetAmount = targetAccept.amount || targetAccept.maxAmountRequired;
9290
+ const sourceAmount = computeSourceAmount(BigInt(targetAmount), info.feeBps).toString();
9291
+ const sourceChainIdx = info.supportedSourceChains.indexOf(source.chain);
9292
+ 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")) || "");
9293
+ let sourcePaymentHeader;
9294
+ const sourceAccept = {
9295
+ scheme: "exact",
9296
+ network: source.chain,
9297
+ asset: sourceAsset,
9298
+ payTo: bridgePayTo,
9299
+ amount: sourceAmount,
9300
+ extra: {
9301
+ ...targetAccept.extra || {},
9302
+ ...source.type === "solana" && info.feePayerSvm ? { feePayer: info.feePayerSvm } : {}
9303
+ }
9304
+ };
9305
+ if (source.type === "solana" && hasSolanaWallet) {
9306
+ sourcePaymentHeader = await buildSolanaPayment(sourceAccept, requirements, url2);
9307
+ } else if (source.type === "evm") {
9308
+ const evmNetwork = normalizeNetwork(source.chain);
9309
+ const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);
9310
+ sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url2) : await buildEvmPayment(sourceAccept, requirements, url2);
9311
+ } else {
9312
+ throw new Error(`[relai-x402] No wallet for source chain type: ${source.type}`);
9313
+ }
9314
+ const settleData = await settleBridge(info.settleEndpoint, {
9315
+ sourcePayment: sourcePaymentHeader,
9316
+ sourceChain: source.chain,
9317
+ targetAccept: {
9318
+ scheme: "exact",
9319
+ network: targetAccept.network,
9320
+ asset: targetAccept.asset,
9321
+ payTo: targetAccept.payTo,
9322
+ amount: targetAmount
9323
+ },
9324
+ requirements,
9325
+ resource: url2,
9326
+ paymentFacilitator: info.paymentFacilitator
9327
+ });
9328
+ if (settleData.xPayment) {
9329
+ log(`Auto-bridge settled: target=${settleData.targetTxId}`);
9330
+ return fetch(input, {
9331
+ ...requestInitWithHeaders,
9332
+ headers: { ...requestHeaders, "X-PAYMENT": settleData.xPayment }
9333
+ });
9334
+ }
9335
+ throw new Error("[relai-x402] Bridge settle did not return xPayment");
9336
+ }
9337
+ }
9338
+ } catch (bridgeErr) {
9339
+ log(`Auto-bridge failed: ${bridgeErr instanceof Error ? bridgeErr.message : bridgeErr}`);
9340
+ }
9341
+ }
9164
9342
  throw new Error(buildNoWalletError(accepts, false));
9165
9343
  }
9166
9344
  const { accept, chain } = selected;
@@ -22856,8 +23034,50 @@ var charge = Method_exports.from({
22856
23034
  }
22857
23035
  });
22858
23036
 
22859
- // src/mpp/evm-server.ts
23037
+ // src/mpp/verify-erc20.ts
22860
23038
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
23039
+ async function verifyErc20Transfer(opts) {
23040
+ const { txHash, rpcUrl, tokenAddress, recipient, expectedAmount } = opts;
23041
+ let receipt = null;
23042
+ for (let attempt = 0; attempt < 5; attempt++) {
23043
+ const receiptRes = await fetch(rpcUrl, {
23044
+ method: "POST",
23045
+ headers: { "Content-Type": "application/json" },
23046
+ body: JSON.stringify({
23047
+ jsonrpc: "2.0",
23048
+ id: 1,
23049
+ method: "eth_getTransactionReceipt",
23050
+ params: [txHash]
23051
+ })
23052
+ });
23053
+ const receiptData = await receiptRes.json();
23054
+ if (receiptData.error) {
23055
+ throw new Error(`RPC error: ${receiptData.error.message}`);
23056
+ }
23057
+ receipt = receiptData.result;
23058
+ if (receipt) break;
23059
+ await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
23060
+ }
23061
+ if (!receipt) {
23062
+ throw new Error("Transaction not found or not yet confirmed");
23063
+ }
23064
+ if (receipt.status !== "0x1") {
23065
+ throw new Error("Transaction failed on-chain");
23066
+ }
23067
+ const recipientPadded = "0x" + recipient.slice(2).toLowerCase().padStart(64, "0");
23068
+ const tokenLower = tokenAddress.toLowerCase();
23069
+ const matchingLog = receipt.logs.find((log) => {
23070
+ if (log.address.toLowerCase() !== tokenLower) return false;
23071
+ if (log.topics[0] !== TRANSFER_EVENT_TOPIC) return false;
23072
+ if (log.topics[2]?.toLowerCase() !== recipientPadded) return false;
23073
+ return BigInt(log.data) >= expectedAmount;
23074
+ });
23075
+ if (!matchingLog) {
23076
+ throw new Error("No matching ERC-20 Transfer found for recipient and amount");
23077
+ }
23078
+ }
23079
+
23080
+ // src/mpp/evm-server.ts
22861
23081
  function evmCharge(config2) {
22862
23082
  const {
22863
23083
  recipient,
@@ -22899,39 +23119,13 @@ function evmCharge(config2) {
22899
23119
  if (!txHash || !txHash.startsWith("0x")) {
22900
23120
  throw new Error("Missing or invalid transaction hash in credential payload");
22901
23121
  }
22902
- const expectedAmount = BigInt(cred.challenge.request.amount);
22903
- const receiptRes = await fetch(rpcUrl, {
22904
- method: "POST",
22905
- headers: { "Content-Type": "application/json" },
22906
- body: JSON.stringify({
22907
- jsonrpc: "2.0",
22908
- id: 1,
22909
- method: "eth_getTransactionReceipt",
22910
- params: [txHash]
22911
- })
22912
- });
22913
- const receiptData = await receiptRes.json();
22914
- if (receiptData.error) {
22915
- throw new Error(`RPC error: ${receiptData.error.message}`);
22916
- }
22917
- const receipt = receiptData.result;
22918
- if (!receipt) {
22919
- throw new Error("Transaction not found or not yet confirmed");
22920
- }
22921
- if (receipt.status !== "0x1") {
22922
- throw new Error("Transaction failed on-chain");
22923
- }
22924
- const recipientPadded = "0x" + recipient.slice(2).toLowerCase().padStart(64, "0");
22925
- const tokenLower = tokenAddress.toLowerCase();
22926
- const matchingLog = receipt.logs.find((log) => {
22927
- if (log.address.toLowerCase() !== tokenLower) return false;
22928
- if (log.topics[0] !== TRANSFER_EVENT_TOPIC) return false;
22929
- if (log.topics[2]?.toLowerCase() !== recipientPadded) return false;
22930
- return BigInt(log.data) >= expectedAmount;
23122
+ await verifyErc20Transfer({
23123
+ txHash,
23124
+ rpcUrl,
23125
+ tokenAddress,
23126
+ recipient,
23127
+ expectedAmount: BigInt(cred.challenge.request.amount)
22931
23128
  });
22932
- if (!matchingLog) {
22933
- throw new Error("No matching ERC-20 Transfer found for recipient and amount");
22934
- }
22935
23129
  return Receipt_exports.from({
22936
23130
  method: "evm",
22937
23131
  reference: txHash,
@@ -26257,6 +26451,65 @@ async function waitForReceipt(rpcUrl, txHash, timeoutMs = 3e4) {
26257
26451
  }
26258
26452
  return false;
26259
26453
  }
26454
+
26455
+ // src/mpp/bridge-method.ts
26456
+ var charge2 = Method_exports.from({
26457
+ intent: "charge",
26458
+ name: "bridge",
26459
+ schema: {
26460
+ credential: {
26461
+ payload: zod_exports.object({
26462
+ /** "settled" = client called bridge settle, targetTxHash is proof */
26463
+ type: zod_exports.string(),
26464
+ /** Target chain tx hash (0x-prefixed) — server verifies on-chain */
26465
+ targetTxHash: zod_exports.optional(zod_exports.string()),
26466
+ /** Source chain tx hash/signature (for auditing) */
26467
+ sourceTxHash: zod_exports.optional(zod_exports.string()),
26468
+ /** CAIP-2 source chain used */
26469
+ sourceChain: zod_exports.optional(zod_exports.string())
26470
+ })
26471
+ },
26472
+ request: zod_exports.object({
26473
+ /** Amount in target token base units */
26474
+ amount: zod_exports.string(),
26475
+ /** Target ERC-20 token contract address */
26476
+ currency: zod_exports.string(),
26477
+ /** Target chain recipient address (merchant) */
26478
+ recipient: zod_exports.string(),
26479
+ /** Human-readable description */
26480
+ description: zod_exports.optional(zod_exports.string()),
26481
+ methodDetails: zod_exports.object({
26482
+ /** Target chain ID (where the merchant gets paid) */
26483
+ targetChainId: zod_exports.number(),
26484
+ /** Human-readable target network name (e.g. "skale-base") */
26485
+ targetNetwork: zod_exports.optional(zod_exports.string()),
26486
+ /** Target chain RPC URL (for server-side verification) */
26487
+ targetRpcUrl: zod_exports.optional(zod_exports.string()),
26488
+ /** Target token decimals */
26489
+ targetDecimals: zod_exports.optional(zod_exports.number()),
26490
+ /** Bridge settle endpoint URL */
26491
+ settleEndpoint: zod_exports.string(),
26492
+ /** Supported source chains (CAIP-2 format, e.g. ["eip155:8453", "solana:5eykt4..."]) */
26493
+ supportedSourceChains: zod_exports.array(zod_exports.string()),
26494
+ /** Supported source token addresses */
26495
+ supportedSourceAssets: zod_exports.optional(zod_exports.array(zod_exports.string())),
26496
+ /** Bridge receiver addresses per source chain: { [caip2]: address } */
26497
+ payToMap: zod_exports.record(zod_exports.string(), zod_exports.string()),
26498
+ /** Solana fee payer address (bridge facilitator sponsors gas) */
26499
+ feePayerSvm: zod_exports.optional(zod_exports.string()),
26500
+ /** Bridge fee in basis points */
26501
+ feeBps: zod_exports.optional(zod_exports.number()),
26502
+ /** Payment facilitator URL */
26503
+ paymentFacilitator: zod_exports.optional(zod_exports.string()),
26504
+ /** Unique reference ID (for replay protection) */
26505
+ reference: zod_exports.string()
26506
+ })
26507
+ })
26508
+ }
26509
+ });
26510
+
26511
+ // src/mpp/bridge-server.ts
26512
+ var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
26260
26513
  // Annotate the CommonJS export names for ESM import in node:
26261
26514
  0 && (module.exports = {
26262
26515
  BASE_MAINNET_NETWORK,