@relai-fi/x402 0.6.5 → 0.6.7
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/README.md +251 -0
- package/dist/bridge.cjs +109 -0
- package/dist/bridge.cjs.map +1 -0
- package/dist/bridge.d.cts +78 -0
- package/dist/bridge.d.ts +78 -0
- package/dist/bridge.js +80 -0
- package/dist/bridge.js.map +1 -0
- package/dist/client.cjs +131 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +13 -1
- package/dist/client.d.ts +13 -1
- package/dist/client.js +131 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +586 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +291 -30
- package/dist/index.d.ts +291 -30
- package/dist/index.js +578 -105
- package/dist/index.js.map +1 -1
- package/dist/mpp/bridge-client.cjs +23922 -0
- package/dist/mpp/bridge-client.cjs.map +1 -0
- package/dist/mpp/bridge-client.d.cts +58 -0
- package/dist/mpp/bridge-client.d.ts +58 -0
- package/dist/mpp/bridge-client.js +23892 -0
- package/dist/mpp/bridge-client.js.map +1 -0
- package/dist/mpp/bridge-method.cjs +13202 -0
- package/dist/mpp/bridge-method.cjs.map +1 -0
- package/dist/mpp/bridge-method.d.cts +69 -0
- package/dist/mpp/bridge-method.d.ts +69 -0
- package/dist/mpp/bridge-method.js +13181 -0
- package/dist/mpp/bridge-method.js.map +1 -0
- package/dist/mpp/bridge-server.cjs +13887 -0
- package/dist/mpp/bridge-server.cjs.map +1 -0
- package/dist/mpp/bridge-server.d.cts +62 -0
- package/dist/mpp/bridge-server.d.ts +62 -0
- package/dist/mpp/bridge-server.js +13866 -0
- package/dist/mpp/bridge-server.js.map +1 -0
- package/dist/mpp/evm-server.cjs +49 -33
- package/dist/mpp/evm-server.cjs.map +1 -1
- package/dist/mpp/evm-server.js +49 -33
- package/dist/mpp/evm-server.js.map +1 -1
- package/dist/mpp/verify-erc20.cjs +71 -0
- package/dist/mpp/verify-erc20.cjs.map +1 -0
- package/dist/mpp/verify-erc20.d.cts +27 -0
- package/dist/mpp/verify-erc20.d.ts +27 -0
- package/dist/mpp/verify-erc20.js +46 -0
- package/dist/mpp/verify-erc20.js.map +1 -0
- package/dist/mpp/verify-spl.cjs +96 -0
- package/dist/mpp/verify-spl.cjs.map +1 -0
- package/dist/mpp/verify-spl.d.cts +30 -0
- package/dist/mpp/verify-spl.d.ts +30 -0
- package/dist/mpp/verify-spl.js +71 -0
- package/dist/mpp/verify-spl.js.map +1 -0
- package/dist/mpp/with-bridge.cjs +23956 -0
- package/dist/mpp/with-bridge.cjs.map +1 -0
- package/dist/mpp/with-bridge.d.cts +53 -0
- package/dist/mpp/with-bridge.d.ts +53 -0
- package/dist/mpp/with-bridge.js +23926 -0
- package/dist/mpp/with-bridge.js.map +1 -0
- package/dist/plugins.d.cts +2 -2
- package/dist/plugins.d.ts +2 -2
- package/dist/react/index.cjs +131 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +131 -1
- package/dist/react/index.js.map +1 -1
- package/dist/{server-DaySqG5H.d.ts → server-D9ZfrFFx.d.ts} +1 -1
- package/dist/{server-CBZ2RjEP.d.cts → server-DgMG2zhy.d.cts} +1 -1
- package/dist/server.cjs +6 -39
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +6 -39
- package/dist/server.js.map +1 -1
- package/dist/{types-Y9ni5XwY.d.cts → types-DjEveKgt.d.cts} +1 -1
- package/dist/{types-Y9ni5XwY.d.ts → types-DjEveKgt.d.ts} +1 -1
- package/package.json +31 -1
package/dist/index.js
CHANGED
|
@@ -7560,7 +7560,6 @@ var Relai = class {
|
|
|
7560
7560
|
const integritasMode = integritasFlow === "single" ? "single_signature_fee_included" : integritasFlow === "dual" ? "dual_signature_split" : void 0;
|
|
7561
7561
|
const paymentHeader = req.headers["x-payment"] || req.headers["payment-signature"] || req.headers["x-payment-signature"];
|
|
7562
7562
|
const authHeader = req.headers["authorization"] || "";
|
|
7563
|
-
console.log(`[Relai] MPP check: paymentHeader=${!!paymentHeader}, hasMpp=${!!self.mpp}, authHeader=${authHeader?.slice(0, 30)}`);
|
|
7564
7563
|
if (!paymentHeader && self.mpp && /^Payment\s+/i.test(authHeader)) {
|
|
7565
7564
|
try {
|
|
7566
7565
|
const mppAmount = resolvedPrice.toFixed(6);
|
|
@@ -7572,10 +7571,7 @@ var Relai = class {
|
|
|
7572
7571
|
const mppRequest = new Request(mppUrl, { method: req.method, headers: mppHeaders });
|
|
7573
7572
|
const chargeHandler = self.mpp.charge({ amount: mppAmount });
|
|
7574
7573
|
const mppResult = await chargeHandler(mppRequest);
|
|
7575
|
-
console.log(`[Relai] MPP charge result: status=${mppResult.status}, keys=${Object.keys(mppResult)}, hasChallenge=${!!mppResult.challenge}, hasWithReceipt=${!!mppResult.withReceipt}`);
|
|
7576
7574
|
if (mppResult.status === 402 && mppResult.challenge instanceof Response) {
|
|
7577
|
-
const retryAuth = mppResult.challenge.headers.get("www-authenticate");
|
|
7578
|
-
console.log(`[Relai] MPP re-challenged (credential not accepted). New WWW-Auth: ${retryAuth?.slice(0, 60)}`);
|
|
7579
7575
|
}
|
|
7580
7576
|
if (mppResult.status !== 200 && !mppResult.withReceipt && mppResult.status !== 402) {
|
|
7581
7577
|
if (self.plugins.length > 0) {
|
|
@@ -7618,7 +7614,10 @@ var Relai = class {
|
|
|
7618
7614
|
const dummyResponse = new Response(null);
|
|
7619
7615
|
const receiptResponse = mppResult.withReceipt(dummyResponse);
|
|
7620
7616
|
const receiptHeader = receiptResponse.headers.get("payment-receipt");
|
|
7621
|
-
if (receiptHeader)
|
|
7617
|
+
if (receiptHeader) {
|
|
7618
|
+
res.setHeader?.("Payment-Receipt", receiptHeader);
|
|
7619
|
+
res.setHeader?.("Cache-Control", "private");
|
|
7620
|
+
}
|
|
7622
7621
|
}
|
|
7623
7622
|
options.onPaymentSettled?.(req, {
|
|
7624
7623
|
success: true,
|
|
@@ -7835,12 +7834,13 @@ var Relai = class {
|
|
|
7835
7834
|
if (mppResult?.challenge instanceof Response) {
|
|
7836
7835
|
const wwwAuth = mppResult.challenge.headers.get("www-authenticate");
|
|
7837
7836
|
if (wwwAuth) {
|
|
7838
|
-
res.setHeader("WWW-Authenticate", wwwAuth);
|
|
7837
|
+
res.setHeader?.("WWW-Authenticate", wwwAuth);
|
|
7839
7838
|
}
|
|
7840
7839
|
}
|
|
7841
7840
|
} catch {
|
|
7842
7841
|
}
|
|
7843
7842
|
}
|
|
7843
|
+
res.setHeader?.("Cache-Control", "no-store");
|
|
7844
7844
|
return res.status(402).json(paymentRequiredResponse);
|
|
7845
7845
|
}
|
|
7846
7846
|
let paymentProof;
|
|
@@ -7857,39 +7857,6 @@ var Relai = class {
|
|
|
7857
7857
|
});
|
|
7858
7858
|
}
|
|
7859
7859
|
}
|
|
7860
|
-
if (paymentProof.bridged === true && paymentProof.targetTxId) {
|
|
7861
|
-
console.log(`[Relai] Bridged payment accepted: source=${paymentProof.sourceTxId}, target=${paymentProof.targetTxId}`);
|
|
7862
|
-
const paymentInfo2 = {
|
|
7863
|
-
verified: true,
|
|
7864
|
-
transactionId: paymentProof.targetTxId,
|
|
7865
|
-
payer: paymentProof.sourceTxId || "bridge",
|
|
7866
|
-
network,
|
|
7867
|
-
amount: resolvedPrice
|
|
7868
|
-
};
|
|
7869
|
-
req.payment = paymentInfo2;
|
|
7870
|
-
req.x402Payer = paymentProof.sourceTxId || "bridge";
|
|
7871
|
-
req.x402Paid = true;
|
|
7872
|
-
req.x402Transaction = paymentProof.targetTxId;
|
|
7873
|
-
req.x402Network = network;
|
|
7874
|
-
req.x402Bridged = true;
|
|
7875
|
-
req.x402SourceChain = paymentProof.sourceChain;
|
|
7876
|
-
const paymentResponse2 = {
|
|
7877
|
-
x402Version: 2,
|
|
7878
|
-
scheme: "exact",
|
|
7879
|
-
network: caip2,
|
|
7880
|
-
transaction: paymentProof.targetTxId,
|
|
7881
|
-
payer: paymentProof.sourceTxId,
|
|
7882
|
-
amount: amount2,
|
|
7883
|
-
asset,
|
|
7884
|
-
bridged: true
|
|
7885
|
-
};
|
|
7886
|
-
res.setHeader(
|
|
7887
|
-
"PAYMENT-RESPONSE",
|
|
7888
|
-
Buffer.from(JSON.stringify(paymentResponse2)).toString("base64")
|
|
7889
|
-
);
|
|
7890
|
-
options.onPaymentSettled?.(req, { success: true, transaction: paymentProof.targetTxId, payer: paymentProof.sourceTxId });
|
|
7891
|
-
return next();
|
|
7892
|
-
}
|
|
7893
7860
|
let settlePayTo;
|
|
7894
7861
|
if (stripeConfig) {
|
|
7895
7862
|
settlePayTo = paymentProof.payload?.authorization?.to || paymentProof.accepted?.payTo || "";
|
|
@@ -8047,6 +8014,73 @@ import {
|
|
|
8047
8014
|
TOKEN_PROGRAM_ID,
|
|
8048
8015
|
TOKEN_2022_PROGRAM_ID
|
|
8049
8016
|
} from "@solana/spl-token";
|
|
8017
|
+
|
|
8018
|
+
// src/bridge.ts
|
|
8019
|
+
var RELAI_API_BASE = "https://api.relai.fi";
|
|
8020
|
+
var _cacheMap = /* @__PURE__ */ new Map();
|
|
8021
|
+
var CACHE_TTL = 5 * 60 * 1e3;
|
|
8022
|
+
async function getBridgeInfo(baseUrl = RELAI_API_BASE) {
|
|
8023
|
+
const key = baseUrl.replace(/\/$/, "");
|
|
8024
|
+
const now = Date.now();
|
|
8025
|
+
const cached2 = _cacheMap.get(key);
|
|
8026
|
+
if (cached2 && now - cached2.time < CACHE_TTL) return cached2.info;
|
|
8027
|
+
const url2 = `${key}/bridge/info`;
|
|
8028
|
+
const res = await fetch(url2);
|
|
8029
|
+
if (!res.ok) {
|
|
8030
|
+
if (cached2) return cached2.info;
|
|
8031
|
+
throw new Error(`[relai:bridge] Failed to fetch ${url2}: ${res.status}`);
|
|
8032
|
+
}
|
|
8033
|
+
const data = await res.json();
|
|
8034
|
+
const info = {
|
|
8035
|
+
settleEndpoint: data.settleEndpoint,
|
|
8036
|
+
supportedSourceChains: data.supportedSourceChains || [],
|
|
8037
|
+
supportedSourceAssets: data.supportedSourceAssets || [],
|
|
8038
|
+
payTo: data.payTo || {},
|
|
8039
|
+
feePayerSvm: data.feePayerSvm ?? null,
|
|
8040
|
+
feeBps: data.feeBps ?? 100,
|
|
8041
|
+
paymentFacilitator: data.paymentFacilitator || "https://facilitator.x402.fi"
|
|
8042
|
+
};
|
|
8043
|
+
_cacheMap.set(key, { info, time: now });
|
|
8044
|
+
return info;
|
|
8045
|
+
}
|
|
8046
|
+
async function settleBridge(settleEndpoint, body) {
|
|
8047
|
+
const res = await fetch(settleEndpoint, {
|
|
8048
|
+
method: "POST",
|
|
8049
|
+
headers: { "Content-Type": "application/json" },
|
|
8050
|
+
body: JSON.stringify(body)
|
|
8051
|
+
});
|
|
8052
|
+
if (!res.ok) {
|
|
8053
|
+
const err = await res.json().catch(() => ({}));
|
|
8054
|
+
throw new Error(`[relai:bridge] settle failed: ${err.error || res.status}${err.details ? " \u2014 " + err.details : ""}`);
|
|
8055
|
+
}
|
|
8056
|
+
return res.json();
|
|
8057
|
+
}
|
|
8058
|
+
function selectSourceChain(supportedChains, hasEvmWallet, hasSolanaWallet, preferredSourceChainId) {
|
|
8059
|
+
if (preferredSourceChainId && hasEvmWallet) {
|
|
8060
|
+
const preferred = `eip155:${preferredSourceChainId}`;
|
|
8061
|
+
if (supportedChains.includes(preferred)) {
|
|
8062
|
+
return { type: "evm", chain: preferred };
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
if (hasSolanaWallet) {
|
|
8066
|
+
const sol = supportedChains.find((c) => c.startsWith("solana:"));
|
|
8067
|
+
if (sol) return { type: "solana", chain: sol };
|
|
8068
|
+
}
|
|
8069
|
+
if (hasEvmWallet) {
|
|
8070
|
+
for (const chain of supportedChains) {
|
|
8071
|
+
if (chain.startsWith("eip155:")) {
|
|
8072
|
+
return { type: "evm", chain };
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
return null;
|
|
8077
|
+
}
|
|
8078
|
+
function computeSourceAmount(targetAmount, feeBps) {
|
|
8079
|
+
const fee = targetAmount * BigInt(feeBps) / 10000n;
|
|
8080
|
+
return targetAmount + fee;
|
|
8081
|
+
}
|
|
8082
|
+
|
|
8083
|
+
// src/client.ts
|
|
8050
8084
|
var PERMIT_NETWORKS = /* @__PURE__ */ new Set([]);
|
|
8051
8085
|
var DEFAULT_EVM_RPC_URLS = {
|
|
8052
8086
|
"skale-base": "https://skale-base.skalenodes.com/v1/base",
|
|
@@ -8072,7 +8106,8 @@ function createX402Client(config2) {
|
|
|
8072
8106
|
integritas,
|
|
8073
8107
|
verbose = false,
|
|
8074
8108
|
defaultHeaders = {},
|
|
8075
|
-
mpp
|
|
8109
|
+
mpp,
|
|
8110
|
+
bridge: bridgeConfig
|
|
8076
8111
|
} = config2;
|
|
8077
8112
|
const relayWsEnabled = relayWs?.enabled === true;
|
|
8078
8113
|
const relayWsPreflightTimeoutMs = relayWs?.preflightTimeoutMs ?? 5e3;
|
|
@@ -9187,6 +9222,70 @@ function createX402Client(config2) {
|
|
|
9187
9222
|
headers: { ...requestHeaders, "X-PAYMENT": paymentHeader }
|
|
9188
9223
|
});
|
|
9189
9224
|
}
|
|
9225
|
+
if (bridgeConfig?.enabled) {
|
|
9226
|
+
log("No direct wallet match \u2014 attempting auto-bridge via RelAI API");
|
|
9227
|
+
try {
|
|
9228
|
+
const info = await getBridgeInfo(bridgeConfig.baseUrl);
|
|
9229
|
+
const targetAccept = accepts[0];
|
|
9230
|
+
const hasEvm = !!effectiveWallets.evm;
|
|
9231
|
+
const hasSol = !!hasSolanaWallet;
|
|
9232
|
+
const source = selectSourceChain(info.supportedSourceChains, hasEvm, hasSol);
|
|
9233
|
+
if (source) {
|
|
9234
|
+
const bridgePayTo = info.payTo[source.chain];
|
|
9235
|
+
if (bridgePayTo) {
|
|
9236
|
+
const targetAmount = targetAccept.amount || targetAccept.maxAmountRequired;
|
|
9237
|
+
const sourceAmount = computeSourceAmount(BigInt(targetAmount), info.feeBps).toString();
|
|
9238
|
+
const sourceChainIdx = info.supportedSourceChains.indexOf(source.chain);
|
|
9239
|
+
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")) || "");
|
|
9240
|
+
let sourcePaymentHeader;
|
|
9241
|
+
const sourceAccept = {
|
|
9242
|
+
scheme: "exact",
|
|
9243
|
+
network: source.chain,
|
|
9244
|
+
asset: sourceAsset,
|
|
9245
|
+
payTo: bridgePayTo,
|
|
9246
|
+
amount: sourceAmount,
|
|
9247
|
+
extra: {
|
|
9248
|
+
...targetAccept.extra || {},
|
|
9249
|
+
...source.type === "solana" && info.feePayerSvm ? { feePayer: info.feePayerSvm } : {}
|
|
9250
|
+
}
|
|
9251
|
+
};
|
|
9252
|
+
if (source.type === "solana" && hasSolanaWallet) {
|
|
9253
|
+
sourcePaymentHeader = await buildSolanaPayment(sourceAccept, requirements, url2);
|
|
9254
|
+
} else if (source.type === "evm") {
|
|
9255
|
+
const evmNetwork = normalizeNetwork(source.chain);
|
|
9256
|
+
const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);
|
|
9257
|
+
sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url2) : await buildEvmPayment(sourceAccept, requirements, url2);
|
|
9258
|
+
} else {
|
|
9259
|
+
throw new Error(`[relai-x402] No wallet for source chain type: ${source.type}`);
|
|
9260
|
+
}
|
|
9261
|
+
const settleData = await settleBridge(info.settleEndpoint, {
|
|
9262
|
+
sourcePayment: sourcePaymentHeader,
|
|
9263
|
+
sourceChain: source.chain,
|
|
9264
|
+
targetAccept: {
|
|
9265
|
+
scheme: "exact",
|
|
9266
|
+
network: targetAccept.network,
|
|
9267
|
+
asset: targetAccept.asset,
|
|
9268
|
+
payTo: targetAccept.payTo,
|
|
9269
|
+
amount: targetAmount
|
|
9270
|
+
},
|
|
9271
|
+
requirements,
|
|
9272
|
+
resource: url2,
|
|
9273
|
+
paymentFacilitator: info.paymentFacilitator
|
|
9274
|
+
});
|
|
9275
|
+
if (settleData.xPayment) {
|
|
9276
|
+
log(`Auto-bridge settled: target=${settleData.targetTxId}`);
|
|
9277
|
+
return fetch(input, {
|
|
9278
|
+
...requestInitWithHeaders,
|
|
9279
|
+
headers: { ...requestHeaders, "X-PAYMENT": settleData.xPayment }
|
|
9280
|
+
});
|
|
9281
|
+
}
|
|
9282
|
+
throw new Error("[relai-x402] Bridge settle did not return xPayment");
|
|
9283
|
+
}
|
|
9284
|
+
}
|
|
9285
|
+
} catch (bridgeErr) {
|
|
9286
|
+
log(`Auto-bridge failed: ${bridgeErr instanceof Error ? bridgeErr.message : bridgeErr}`);
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9190
9289
|
throw new Error(buildNoWalletError(accepts, false));
|
|
9191
9290
|
}
|
|
9192
9291
|
const { accept, chain } = selected;
|
|
@@ -9283,8 +9382,37 @@ function submitRelayFeedback(config2) {
|
|
|
9283
9382
|
}
|
|
9284
9383
|
|
|
9285
9384
|
// src/payment-codes.ts
|
|
9286
|
-
|
|
9287
|
-
var
|
|
9385
|
+
import { ethers as ethers2 } from "ethers";
|
|
9386
|
+
var NETWORK_CONFIGS = {
|
|
9387
|
+
"base": {
|
|
9388
|
+
chainId: 8453,
|
|
9389
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
9390
|
+
domainName: "USD Coin",
|
|
9391
|
+
rpc: "https://mainnet.base.org",
|
|
9392
|
+
settlementNetwork: "base"
|
|
9393
|
+
},
|
|
9394
|
+
"base-sepolia": {
|
|
9395
|
+
chainId: 84532,
|
|
9396
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
9397
|
+
domainName: "USDC",
|
|
9398
|
+
rpc: "https://sepolia.base.org",
|
|
9399
|
+
settlementNetwork: "base-sepolia"
|
|
9400
|
+
},
|
|
9401
|
+
"skale-base-sepolia": {
|
|
9402
|
+
chainId: 324705682,
|
|
9403
|
+
usdc: "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
|
|
9404
|
+
domainName: "Bridged USDC (SKALE Bridge)",
|
|
9405
|
+
rpc: "https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",
|
|
9406
|
+
settlementNetwork: "skale-base-sepolia"
|
|
9407
|
+
},
|
|
9408
|
+
"skale-base": {
|
|
9409
|
+
chainId: 1482601649,
|
|
9410
|
+
usdc: "0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",
|
|
9411
|
+
domainName: "Bridged USDC",
|
|
9412
|
+
rpc: "https://skale-base.skalenodes.com/v1/base",
|
|
9413
|
+
settlementNetwork: "skale-base"
|
|
9414
|
+
}
|
|
9415
|
+
};
|
|
9288
9416
|
var EIP3009_TYPES = {
|
|
9289
9417
|
TransferWithAuthorization: [
|
|
9290
9418
|
{ name: "from", type: "address" },
|
|
@@ -9295,6 +9423,14 @@ var EIP3009_TYPES = {
|
|
|
9295
9423
|
{ name: "nonce", type: "bytes32" }
|
|
9296
9424
|
]
|
|
9297
9425
|
};
|
|
9426
|
+
function createPrivateKeySigner(privateKey) {
|
|
9427
|
+
const wallet = new ethers2.Wallet(privateKey);
|
|
9428
|
+
return {
|
|
9429
|
+
getAddress: () => Promise.resolve(wallet.address),
|
|
9430
|
+
signTypedData: (domain2, types, value) => wallet.signTypedData(domain2, types, value)
|
|
9431
|
+
};
|
|
9432
|
+
}
|
|
9433
|
+
var DEFAULT_FACILITATOR = "https://relai.fi/facilitator";
|
|
9298
9434
|
function randomBytes32() {
|
|
9299
9435
|
const bytes = new Uint8Array(32);
|
|
9300
9436
|
if (typeof globalThis.crypto !== "undefined") {
|
|
@@ -9305,69 +9441,323 @@ function randomBytes32() {
|
|
|
9305
9441
|
}
|
|
9306
9442
|
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9307
9443
|
}
|
|
9308
|
-
async function
|
|
9309
|
-
const
|
|
9310
|
-
|
|
9311
|
-
const
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
const nonce = randomBytes32();
|
|
9444
|
+
async function fetchToAddress(facilitatorUrl, network) {
|
|
9445
|
+
const res = await fetch(`${facilitatorUrl}/payment-codes/relayer?network=${network}`);
|
|
9446
|
+
if (!res.ok) throw new Error("Failed to fetch relayer address from facilitator");
|
|
9447
|
+
const data = await res.json();
|
|
9448
|
+
if (!data.toAddress) throw new Error("Facilitator returned no toAddress");
|
|
9449
|
+
return data.toAddress;
|
|
9450
|
+
}
|
|
9451
|
+
async function signEip3009(signer, net, from4, toAddress, value, validAfter, validBefore, nonce, usdcOverride) {
|
|
9317
9452
|
const domain2 = {
|
|
9318
|
-
name:
|
|
9453
|
+
name: net.domainName,
|
|
9319
9454
|
version: "2",
|
|
9320
|
-
chainId:
|
|
9321
|
-
|
|
9322
|
-
verifyingContract: usdc
|
|
9455
|
+
chainId: net.chainId,
|
|
9456
|
+
verifyingContract: usdcOverride ?? net.usdc
|
|
9323
9457
|
};
|
|
9324
|
-
|
|
9458
|
+
return signer.signTypedData(domain2, EIP3009_TYPES, {
|
|
9325
9459
|
from: from4,
|
|
9326
|
-
to,
|
|
9327
|
-
value
|
|
9460
|
+
to: toAddress,
|
|
9461
|
+
value,
|
|
9328
9462
|
validAfter,
|
|
9329
9463
|
validBefore,
|
|
9330
9464
|
nonce
|
|
9331
|
-
};
|
|
9332
|
-
|
|
9465
|
+
});
|
|
9466
|
+
}
|
|
9467
|
+
async function generatePaymentCode(config2, params) {
|
|
9468
|
+
const {
|
|
9469
|
+
signer,
|
|
9470
|
+
value,
|
|
9471
|
+
ttl = 86400,
|
|
9472
|
+
description,
|
|
9473
|
+
payee,
|
|
9474
|
+
usdcContract,
|
|
9475
|
+
network = "base-sepolia"
|
|
9476
|
+
} = params;
|
|
9477
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
|
|
9478
|
+
const net = NETWORK_CONFIGS[network];
|
|
9479
|
+
if (!net) throw new Error(`Unsupported network: ${network}`);
|
|
9480
|
+
const from4 = await signer.getAddress();
|
|
9481
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9482
|
+
const validBefore = now + ttl;
|
|
9483
|
+
const nonce = randomBytes32();
|
|
9484
|
+
const usdc = usdcContract ?? net.usdc;
|
|
9485
|
+
const toAddress = await fetchToAddress(facilitatorUrl, network);
|
|
9486
|
+
const signature2 = await signEip3009(
|
|
9487
|
+
signer,
|
|
9488
|
+
net,
|
|
9489
|
+
from4,
|
|
9490
|
+
toAddress,
|
|
9491
|
+
BigInt(value).toString(),
|
|
9492
|
+
0,
|
|
9493
|
+
validBefore,
|
|
9494
|
+
nonce,
|
|
9495
|
+
usdc
|
|
9496
|
+
);
|
|
9333
9497
|
const res = await fetch(`${facilitatorUrl}/payment-codes`, {
|
|
9334
9498
|
method: "POST",
|
|
9335
9499
|
headers: { "Content-Type": "application/json" },
|
|
9336
9500
|
body: JSON.stringify({
|
|
9337
9501
|
from: from4,
|
|
9338
|
-
to,
|
|
9339
9502
|
value: BigInt(value).toString(),
|
|
9340
|
-
validAfter,
|
|
9503
|
+
validAfter: 0,
|
|
9341
9504
|
validBefore,
|
|
9342
9505
|
nonce,
|
|
9343
9506
|
signature: signature2,
|
|
9344
|
-
usdcContract: usdc
|
|
9507
|
+
usdcContract: usdc,
|
|
9508
|
+
settlementNetwork: net.settlementNetwork,
|
|
9509
|
+
...description ? { description } : {},
|
|
9510
|
+
...payee ? { payee } : {}
|
|
9345
9511
|
})
|
|
9346
9512
|
});
|
|
9347
9513
|
if (!res.ok) {
|
|
9348
9514
|
const err = await res.json().catch(() => ({}));
|
|
9349
|
-
throw new Error(`Failed to register payment code: ${err.error
|
|
9515
|
+
throw new Error(`Failed to register payment code: ${err.error ?? res.status}`);
|
|
9350
9516
|
}
|
|
9351
9517
|
return res.json();
|
|
9352
9518
|
}
|
|
9353
|
-
async function
|
|
9354
|
-
const
|
|
9355
|
-
|
|
9519
|
+
async function generatePaymentCodesBatch(config2, params) {
|
|
9520
|
+
const {
|
|
9521
|
+
signer,
|
|
9522
|
+
codes,
|
|
9523
|
+
payee,
|
|
9524
|
+
usdcContract,
|
|
9525
|
+
network = "base-sepolia",
|
|
9526
|
+
authToken
|
|
9527
|
+
} = params;
|
|
9528
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
|
|
9529
|
+
const net = NETWORK_CONFIGS[network];
|
|
9530
|
+
if (!net) throw new Error(`Unsupported network: ${network}`);
|
|
9531
|
+
if (codes.length > 20) throw new Error("Maximum 20 codes per batch");
|
|
9532
|
+
const from4 = await signer.getAddress();
|
|
9533
|
+
const usdc = usdcContract ?? net.usdc;
|
|
9534
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9535
|
+
const toAddress = await fetchToAddress(facilitatorUrl, network);
|
|
9536
|
+
const signedCodes = await Promise.all(
|
|
9537
|
+
codes.map(async (item) => {
|
|
9538
|
+
const validBefore = now + (item.ttl ?? 86400);
|
|
9539
|
+
const nonce = randomBytes32();
|
|
9540
|
+
const value = BigInt(item.value).toString();
|
|
9541
|
+
const signature2 = await signEip3009(
|
|
9542
|
+
signer,
|
|
9543
|
+
net,
|
|
9544
|
+
from4,
|
|
9545
|
+
toAddress,
|
|
9546
|
+
value,
|
|
9547
|
+
0,
|
|
9548
|
+
validBefore,
|
|
9549
|
+
nonce,
|
|
9550
|
+
usdc
|
|
9551
|
+
);
|
|
9552
|
+
return { value, validAfter: 0, validBefore, nonce, signature: signature2 };
|
|
9553
|
+
})
|
|
9554
|
+
);
|
|
9555
|
+
const res = await fetch(`${facilitatorUrl}/payment-codes/batch`, {
|
|
9356
9556
|
method: "POST",
|
|
9357
|
-
headers: {
|
|
9557
|
+
headers: {
|
|
9558
|
+
"Content-Type": "application/json",
|
|
9559
|
+
"Authorization": `Bearer ${authToken}`
|
|
9560
|
+
},
|
|
9561
|
+
body: JSON.stringify({
|
|
9562
|
+
from: from4,
|
|
9563
|
+
settlementNetwork: net.settlementNetwork,
|
|
9564
|
+
usdcContract: usdc,
|
|
9565
|
+
...payee ? { payee } : {},
|
|
9566
|
+
codes: signedCodes
|
|
9567
|
+
})
|
|
9358
9568
|
});
|
|
9359
9569
|
if (!res.ok) {
|
|
9360
9570
|
const err = await res.json().catch(() => ({}));
|
|
9361
|
-
throw new Error(`
|
|
9571
|
+
throw new Error(`Batch registration failed: ${err.error ?? res.status}`);
|
|
9572
|
+
}
|
|
9573
|
+
return res.json();
|
|
9574
|
+
}
|
|
9575
|
+
async function redeemPaymentCode(config2, code, payee) {
|
|
9576
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
|
|
9577
|
+
const res = await fetch(
|
|
9578
|
+
`${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}/redeem`,
|
|
9579
|
+
{
|
|
9580
|
+
method: "POST",
|
|
9581
|
+
headers: { "Content-Type": "application/json" },
|
|
9582
|
+
body: JSON.stringify(payee ? { payee } : {})
|
|
9583
|
+
}
|
|
9584
|
+
);
|
|
9585
|
+
if (!res.ok) {
|
|
9586
|
+
const err = await res.json().catch(() => ({}));
|
|
9587
|
+
throw new Error(err.error ?? `Redeem failed: ${res.status}`);
|
|
9362
9588
|
}
|
|
9363
9589
|
return res.json();
|
|
9364
9590
|
}
|
|
9365
9591
|
async function getPaymentCode(config2, code) {
|
|
9366
|
-
const facilitatorUrl = config2.facilitatorUrl
|
|
9367
|
-
const res = await fetch(
|
|
9592
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
|
|
9593
|
+
const res = await fetch(
|
|
9594
|
+
`${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}`
|
|
9595
|
+
);
|
|
9596
|
+
if (!res.ok) {
|
|
9597
|
+
const err = await res.json().catch(() => ({}));
|
|
9598
|
+
throw new Error(`Payment code not found: ${err.error ?? res.status}`);
|
|
9599
|
+
}
|
|
9600
|
+
return res.json();
|
|
9601
|
+
}
|
|
9602
|
+
async function cancelPaymentCode(config2, code) {
|
|
9603
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR;
|
|
9604
|
+
const res = await fetch(
|
|
9605
|
+
`${facilitatorUrl}/payment-codes/${code.trim().toUpperCase()}`,
|
|
9606
|
+
{ method: "DELETE" }
|
|
9607
|
+
);
|
|
9368
9608
|
if (!res.ok) {
|
|
9369
9609
|
const err = await res.json().catch(() => ({}));
|
|
9370
|
-
throw new Error(`
|
|
9610
|
+
throw new Error(`Cancel failed: ${err.error ?? res.status}`);
|
|
9611
|
+
}
|
|
9612
|
+
return res.json();
|
|
9613
|
+
}
|
|
9614
|
+
|
|
9615
|
+
// src/payment-requests.ts
|
|
9616
|
+
var DEFAULT_FACILITATOR2 = "https://relai.fi/facilitator";
|
|
9617
|
+
var EIP3009_TYPES2 = {
|
|
9618
|
+
TransferWithAuthorization: [
|
|
9619
|
+
{ name: "from", type: "address" },
|
|
9620
|
+
{ name: "to", type: "address" },
|
|
9621
|
+
{ name: "value", type: "uint256" },
|
|
9622
|
+
{ name: "validAfter", type: "uint256" },
|
|
9623
|
+
{ name: "validBefore", type: "uint256" },
|
|
9624
|
+
{ name: "nonce", type: "bytes32" }
|
|
9625
|
+
]
|
|
9626
|
+
};
|
|
9627
|
+
function randomBytes322() {
|
|
9628
|
+
const bytes = new Uint8Array(32);
|
|
9629
|
+
if (typeof globalThis.crypto !== "undefined") {
|
|
9630
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
9631
|
+
} else {
|
|
9632
|
+
const { randomBytes: randomBytes2 } = __require("crypto");
|
|
9633
|
+
randomBytes2(32).copy(Buffer.from(bytes.buffer));
|
|
9634
|
+
}
|
|
9635
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9636
|
+
}
|
|
9637
|
+
async function createPayRequest(config2, params) {
|
|
9638
|
+
const {
|
|
9639
|
+
to,
|
|
9640
|
+
amount: amount2,
|
|
9641
|
+
network = "base-sepolia",
|
|
9642
|
+
description,
|
|
9643
|
+
ttlSeconds
|
|
9644
|
+
} = params;
|
|
9645
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
|
|
9646
|
+
const res = await fetch(`${facilitatorUrl}/payment-requests`, {
|
|
9647
|
+
method: "POST",
|
|
9648
|
+
headers: { "Content-Type": "application/json" },
|
|
9649
|
+
body: JSON.stringify({
|
|
9650
|
+
to,
|
|
9651
|
+
amount: Number(amount2),
|
|
9652
|
+
network,
|
|
9653
|
+
...description ? { description } : {},
|
|
9654
|
+
...ttlSeconds ? { ttlSeconds } : {}
|
|
9655
|
+
})
|
|
9656
|
+
});
|
|
9657
|
+
if (!res.ok) {
|
|
9658
|
+
const err = await res.json().catch(() => ({}));
|
|
9659
|
+
throw new Error(`Failed to create payment request: ${err.error ?? res.status}`);
|
|
9660
|
+
}
|
|
9661
|
+
return res.json();
|
|
9662
|
+
}
|
|
9663
|
+
async function getPayRequest(config2, code) {
|
|
9664
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
|
|
9665
|
+
const res = await fetch(
|
|
9666
|
+
`${facilitatorUrl}/payment-requests/${code.trim().toUpperCase()}`
|
|
9667
|
+
);
|
|
9668
|
+
if (!res.ok) {
|
|
9669
|
+
const err = await res.json().catch(() => ({}));
|
|
9670
|
+
throw new Error(`Payment request not found: ${err.error ?? res.status}`);
|
|
9671
|
+
}
|
|
9672
|
+
return res.json();
|
|
9673
|
+
}
|
|
9674
|
+
async function payPayRequest(config2, code, signer) {
|
|
9675
|
+
const facilitatorUrl = config2.facilitatorUrl ?? DEFAULT_FACILITATOR2;
|
|
9676
|
+
const info = await getPayRequest(config2, code);
|
|
9677
|
+
if (!info.payable) {
|
|
9678
|
+
throw new Error(
|
|
9679
|
+
info.status === "paid" ? "Payment request already paid" : "Payment request expired or not payable"
|
|
9680
|
+
);
|
|
9681
|
+
}
|
|
9682
|
+
const netKey = info.network;
|
|
9683
|
+
const net = NETWORK_CONFIGS[netKey];
|
|
9684
|
+
if (!net) throw new Error(`Unknown network from payment request: ${info.network}`);
|
|
9685
|
+
const from4 = await signer.getAddress();
|
|
9686
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9687
|
+
const validBefore = Math.min(now + 300, info.validUntil);
|
|
9688
|
+
const nonce = randomBytes322();
|
|
9689
|
+
const domain2 = {
|
|
9690
|
+
name: net.domainName,
|
|
9691
|
+
version: "2",
|
|
9692
|
+
chainId: net.chainId,
|
|
9693
|
+
verifyingContract: info.usdcContract
|
|
9694
|
+
};
|
|
9695
|
+
const signature2 = await signer.signTypedData(domain2, EIP3009_TYPES2, {
|
|
9696
|
+
from: from4,
|
|
9697
|
+
to: info.toAddress,
|
|
9698
|
+
// settler/relayer — NOT the merchant directly
|
|
9699
|
+
value: String(info.amount),
|
|
9700
|
+
validAfter: 0,
|
|
9701
|
+
validBefore,
|
|
9702
|
+
nonce
|
|
9703
|
+
});
|
|
9704
|
+
const res = await fetch(
|
|
9705
|
+
`${facilitatorUrl}/payment-requests/${code.trim().toUpperCase()}/pay`,
|
|
9706
|
+
{
|
|
9707
|
+
method: "POST",
|
|
9708
|
+
headers: { "Content-Type": "application/json" },
|
|
9709
|
+
body: JSON.stringify({ from: from4, validAfter: 0, validBefore, nonce, signature: signature2 })
|
|
9710
|
+
}
|
|
9711
|
+
);
|
|
9712
|
+
if (!res.ok) {
|
|
9713
|
+
const err = await res.json().catch(() => ({}));
|
|
9714
|
+
throw new Error(err.error ?? `Payment failed: ${res.status}`);
|
|
9715
|
+
}
|
|
9716
|
+
return res.json();
|
|
9717
|
+
}
|
|
9718
|
+
async function payPayRequestWithCode(config2, requestCode, paymentCode, options = {}) {
|
|
9719
|
+
const { allowOverpayment = true, returnChange = "code" } = options;
|
|
9720
|
+
const info = await getPayRequest(config2, requestCode);
|
|
9721
|
+
if (!info.payable) {
|
|
9722
|
+
throw new Error(
|
|
9723
|
+
info.status === "paid" ? "Payment request already paid" : "Payment request expired or not payable"
|
|
9724
|
+
);
|
|
9725
|
+
}
|
|
9726
|
+
const codeStatus = await getPaymentCode(config2, paymentCode);
|
|
9727
|
+
if (!codeStatus.redeemable) {
|
|
9728
|
+
throw new Error(
|
|
9729
|
+
codeStatus.redeemed ? "Payment code already redeemed" : "Payment code expired or not redeemable"
|
|
9730
|
+
);
|
|
9731
|
+
}
|
|
9732
|
+
const codeValue = BigInt(codeStatus.value);
|
|
9733
|
+
const reqAmount = BigInt(info.amount);
|
|
9734
|
+
if (codeValue < reqAmount) {
|
|
9735
|
+
throw new Error(
|
|
9736
|
+
`Payment code value (${Number(codeValue) / 1e6} USDC) is less than the request amount (${Number(reqAmount) / 1e6} USDC)`
|
|
9737
|
+
);
|
|
9738
|
+
}
|
|
9739
|
+
if (!allowOverpayment && codeValue > reqAmount) {
|
|
9740
|
+
throw new Error(
|
|
9741
|
+
`Payment code value (${Number(codeValue) / 1e6} USDC) exceeds the request amount (${Number(reqAmount) / 1e6} USDC). Generate a code for the exact amount, or pass { allowOverpayment: true }.`
|
|
9742
|
+
);
|
|
9743
|
+
}
|
|
9744
|
+
const facilitatorUrl = config2.facilitatorUrl ?? "https://relai.fi/facilitator";
|
|
9745
|
+
const usePartial = codeValue > reqAmount;
|
|
9746
|
+
const res = await fetch(
|
|
9747
|
+
`${facilitatorUrl}/payment-codes/${paymentCode.trim().toUpperCase()}/redeem`,
|
|
9748
|
+
{
|
|
9749
|
+
method: "POST",
|
|
9750
|
+
headers: { "Content-Type": "application/json" },
|
|
9751
|
+
body: JSON.stringify({
|
|
9752
|
+
payee: info.to,
|
|
9753
|
+
...usePartial ? { invoiceAmount: info.amount.toString() } : {},
|
|
9754
|
+
...usePartial ? { returnChangeAsCode: returnChange === "code" } : {}
|
|
9755
|
+
})
|
|
9756
|
+
}
|
|
9757
|
+
);
|
|
9758
|
+
if (!res.ok) {
|
|
9759
|
+
const err = await res.json().catch(() => ({}));
|
|
9760
|
+
throw new Error(err.error ?? `Redeem failed: ${res.status}`);
|
|
9371
9761
|
}
|
|
9372
9762
|
return res.json();
|
|
9373
9763
|
}
|
|
@@ -22882,8 +23272,50 @@ var charge = Method_exports.from({
|
|
|
22882
23272
|
}
|
|
22883
23273
|
});
|
|
22884
23274
|
|
|
22885
|
-
// src/mpp/
|
|
23275
|
+
// src/mpp/verify-erc20.ts
|
|
22886
23276
|
var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
23277
|
+
async function verifyErc20Transfer(opts) {
|
|
23278
|
+
const { txHash, rpcUrl, tokenAddress, recipient, expectedAmount } = opts;
|
|
23279
|
+
let receipt = null;
|
|
23280
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
23281
|
+
const receiptRes = await fetch(rpcUrl, {
|
|
23282
|
+
method: "POST",
|
|
23283
|
+
headers: { "Content-Type": "application/json" },
|
|
23284
|
+
body: JSON.stringify({
|
|
23285
|
+
jsonrpc: "2.0",
|
|
23286
|
+
id: 1,
|
|
23287
|
+
method: "eth_getTransactionReceipt",
|
|
23288
|
+
params: [txHash]
|
|
23289
|
+
})
|
|
23290
|
+
});
|
|
23291
|
+
const receiptData = await receiptRes.json();
|
|
23292
|
+
if (receiptData.error) {
|
|
23293
|
+
throw new Error(`RPC error: ${receiptData.error.message}`);
|
|
23294
|
+
}
|
|
23295
|
+
receipt = receiptData.result;
|
|
23296
|
+
if (receipt) break;
|
|
23297
|
+
await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
|
|
23298
|
+
}
|
|
23299
|
+
if (!receipt) {
|
|
23300
|
+
throw new Error("Transaction not found or not yet confirmed");
|
|
23301
|
+
}
|
|
23302
|
+
if (receipt.status !== "0x1") {
|
|
23303
|
+
throw new Error("Transaction failed on-chain");
|
|
23304
|
+
}
|
|
23305
|
+
const recipientPadded = "0x" + recipient.slice(2).toLowerCase().padStart(64, "0");
|
|
23306
|
+
const tokenLower = tokenAddress.toLowerCase();
|
|
23307
|
+
const matchingLog = receipt.logs.find((log) => {
|
|
23308
|
+
if (log.address.toLowerCase() !== tokenLower) return false;
|
|
23309
|
+
if (log.topics[0] !== TRANSFER_EVENT_TOPIC) return false;
|
|
23310
|
+
if (log.topics[2]?.toLowerCase() !== recipientPadded) return false;
|
|
23311
|
+
return BigInt(log.data) >= expectedAmount;
|
|
23312
|
+
});
|
|
23313
|
+
if (!matchingLog) {
|
|
23314
|
+
throw new Error("No matching ERC-20 Transfer found for recipient and amount");
|
|
23315
|
+
}
|
|
23316
|
+
}
|
|
23317
|
+
|
|
23318
|
+
// src/mpp/evm-server.ts
|
|
22887
23319
|
function evmCharge(config2) {
|
|
22888
23320
|
const {
|
|
22889
23321
|
recipient,
|
|
@@ -22925,39 +23357,13 @@ function evmCharge(config2) {
|
|
|
22925
23357
|
if (!txHash || !txHash.startsWith("0x")) {
|
|
22926
23358
|
throw new Error("Missing or invalid transaction hash in credential payload");
|
|
22927
23359
|
}
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
id: 1,
|
|
22935
|
-
method: "eth_getTransactionReceipt",
|
|
22936
|
-
params: [txHash]
|
|
22937
|
-
})
|
|
22938
|
-
});
|
|
22939
|
-
const receiptData = await receiptRes.json();
|
|
22940
|
-
if (receiptData.error) {
|
|
22941
|
-
throw new Error(`RPC error: ${receiptData.error.message}`);
|
|
22942
|
-
}
|
|
22943
|
-
const receipt = receiptData.result;
|
|
22944
|
-
if (!receipt) {
|
|
22945
|
-
throw new Error("Transaction not found or not yet confirmed");
|
|
22946
|
-
}
|
|
22947
|
-
if (receipt.status !== "0x1") {
|
|
22948
|
-
throw new Error("Transaction failed on-chain");
|
|
22949
|
-
}
|
|
22950
|
-
const recipientPadded = "0x" + recipient.slice(2).toLowerCase().padStart(64, "0");
|
|
22951
|
-
const tokenLower = tokenAddress.toLowerCase();
|
|
22952
|
-
const matchingLog = receipt.logs.find((log) => {
|
|
22953
|
-
if (log.address.toLowerCase() !== tokenLower) return false;
|
|
22954
|
-
if (log.topics[0] !== TRANSFER_EVENT_TOPIC) return false;
|
|
22955
|
-
if (log.topics[2]?.toLowerCase() !== recipientPadded) return false;
|
|
22956
|
-
return BigInt(log.data) >= expectedAmount;
|
|
23360
|
+
await verifyErc20Transfer({
|
|
23361
|
+
txHash,
|
|
23362
|
+
rpcUrl,
|
|
23363
|
+
tokenAddress,
|
|
23364
|
+
recipient,
|
|
23365
|
+
expectedAmount: BigInt(cred.challenge.request.amount)
|
|
22957
23366
|
});
|
|
22958
|
-
if (!matchingLog) {
|
|
22959
|
-
throw new Error("No matching ERC-20 Transfer found for recipient and amount");
|
|
22960
|
-
}
|
|
22961
23367
|
return Receipt_exports.from({
|
|
22962
23368
|
method: "evm",
|
|
22963
23369
|
reference: txHash,
|
|
@@ -26283,12 +26689,72 @@ async function waitForReceipt(rpcUrl, txHash, timeoutMs = 3e4) {
|
|
|
26283
26689
|
}
|
|
26284
26690
|
return false;
|
|
26285
26691
|
}
|
|
26692
|
+
|
|
26693
|
+
// src/mpp/bridge-method.ts
|
|
26694
|
+
var charge2 = Method_exports.from({
|
|
26695
|
+
intent: "charge",
|
|
26696
|
+
name: "bridge",
|
|
26697
|
+
schema: {
|
|
26698
|
+
credential: {
|
|
26699
|
+
payload: zod_exports.object({
|
|
26700
|
+
/** "settled" = client called bridge settle, targetTxHash is proof */
|
|
26701
|
+
type: zod_exports.string(),
|
|
26702
|
+
/** Target chain tx hash (0x-prefixed) — server verifies on-chain */
|
|
26703
|
+
targetTxHash: zod_exports.optional(zod_exports.string()),
|
|
26704
|
+
/** Source chain tx hash/signature (for auditing) */
|
|
26705
|
+
sourceTxHash: zod_exports.optional(zod_exports.string()),
|
|
26706
|
+
/** CAIP-2 source chain used */
|
|
26707
|
+
sourceChain: zod_exports.optional(zod_exports.string())
|
|
26708
|
+
})
|
|
26709
|
+
},
|
|
26710
|
+
request: zod_exports.object({
|
|
26711
|
+
/** Amount in target token base units */
|
|
26712
|
+
amount: zod_exports.string(),
|
|
26713
|
+
/** Target ERC-20 token contract address */
|
|
26714
|
+
currency: zod_exports.string(),
|
|
26715
|
+
/** Target chain recipient address (merchant) */
|
|
26716
|
+
recipient: zod_exports.string(),
|
|
26717
|
+
/** Human-readable description */
|
|
26718
|
+
description: zod_exports.optional(zod_exports.string()),
|
|
26719
|
+
methodDetails: zod_exports.object({
|
|
26720
|
+
/** Target chain ID (where the merchant gets paid) */
|
|
26721
|
+
targetChainId: zod_exports.number(),
|
|
26722
|
+
/** Human-readable target network name (e.g. "skale-base") */
|
|
26723
|
+
targetNetwork: zod_exports.optional(zod_exports.string()),
|
|
26724
|
+
/** Target chain RPC URL (for server-side verification) */
|
|
26725
|
+
targetRpcUrl: zod_exports.optional(zod_exports.string()),
|
|
26726
|
+
/** Target token decimals */
|
|
26727
|
+
targetDecimals: zod_exports.optional(zod_exports.number()),
|
|
26728
|
+
/** Bridge settle endpoint URL */
|
|
26729
|
+
settleEndpoint: zod_exports.string(),
|
|
26730
|
+
/** Supported source chains (CAIP-2 format, e.g. ["eip155:8453", "solana:5eykt4..."]) */
|
|
26731
|
+
supportedSourceChains: zod_exports.array(zod_exports.string()),
|
|
26732
|
+
/** Supported source token addresses */
|
|
26733
|
+
supportedSourceAssets: zod_exports.optional(zod_exports.array(zod_exports.string())),
|
|
26734
|
+
/** Bridge receiver addresses per source chain: { [caip2]: address } */
|
|
26735
|
+
payToMap: zod_exports.record(zod_exports.string(), zod_exports.string()),
|
|
26736
|
+
/** Solana fee payer address (bridge facilitator sponsors gas) */
|
|
26737
|
+
feePayerSvm: zod_exports.optional(zod_exports.string()),
|
|
26738
|
+
/** Bridge fee in basis points */
|
|
26739
|
+
feeBps: zod_exports.optional(zod_exports.number()),
|
|
26740
|
+
/** Payment facilitator URL */
|
|
26741
|
+
paymentFacilitator: zod_exports.optional(zod_exports.string()),
|
|
26742
|
+
/** Unique reference ID (for replay protection) */
|
|
26743
|
+
reference: zod_exports.string()
|
|
26744
|
+
})
|
|
26745
|
+
})
|
|
26746
|
+
}
|
|
26747
|
+
});
|
|
26748
|
+
|
|
26749
|
+
// src/mpp/bridge-server.ts
|
|
26750
|
+
var BRIDGE_INFO_TTL_MS = 5 * 60 * 1e3;
|
|
26286
26751
|
export {
|
|
26287
26752
|
BASE_MAINNET_NETWORK,
|
|
26288
26753
|
CAIP2_TO_NETWORK,
|
|
26289
26754
|
CHAIN_IDS,
|
|
26290
26755
|
EXPLORER_TX_URL,
|
|
26291
26756
|
NETWORK_CAIP2,
|
|
26757
|
+
NETWORK_CONFIGS,
|
|
26292
26758
|
NETWORK_LABELS,
|
|
26293
26759
|
NETWORK_TOKENS,
|
|
26294
26760
|
NETWORK_V1_TO_V2,
|
|
@@ -26300,9 +26766,12 @@ export {
|
|
|
26300
26766
|
USDC_ADDRESSES,
|
|
26301
26767
|
USDC_BASE,
|
|
26302
26768
|
USDC_SOLANA,
|
|
26769
|
+
cancelPaymentCode,
|
|
26303
26770
|
convertPayloadToVersion,
|
|
26304
26771
|
convertV1ToV2,
|
|
26305
26772
|
convertV2ToV1,
|
|
26773
|
+
createPayRequest,
|
|
26774
|
+
createPrivateKeySigner,
|
|
26306
26775
|
createX402Client,
|
|
26307
26776
|
Relai as default,
|
|
26308
26777
|
detectPayloadVersion,
|
|
@@ -26312,6 +26781,8 @@ export {
|
|
|
26312
26781
|
formatUsd,
|
|
26313
26782
|
fromAtomicUnits,
|
|
26314
26783
|
generatePaymentCode,
|
|
26784
|
+
generatePaymentCodesBatch,
|
|
26785
|
+
getPayRequest,
|
|
26315
26786
|
getPaymentCode,
|
|
26316
26787
|
isEvm,
|
|
26317
26788
|
isEvmNetwork,
|
|
@@ -26321,6 +26792,8 @@ export {
|
|
|
26321
26792
|
networkV2ToV1,
|
|
26322
26793
|
normalizeNetwork,
|
|
26323
26794
|
normalizePaymentHeader,
|
|
26795
|
+
payPayRequest,
|
|
26796
|
+
payPayRequestWithCode,
|
|
26324
26797
|
redeemPaymentCode,
|
|
26325
26798
|
resolveToken,
|
|
26326
26799
|
stripePayTo,
|