@piprail/sdk 1.20.1 → 1.21.1
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/CHANGELOG.md +71 -0
- package/dist/index.cjs +165 -65
- package/dist/index.d.cts +118 -37
- package/dist/index.d.ts +118 -37
- package/dist/index.js +156 -56
- package/dist/solana-IBVUZS54.js +724 -0
- package/dist/solana-WG7RGDSI.cjs +724 -0
- package/package.json +1 -1
- package/dist/solana-MPPE6K24.cjs +0 -364
- package/dist/solana-WDKWWF33.js +0 -364
package/dist/index.js
CHANGED
|
@@ -533,6 +533,37 @@ var EXACT_NETWORK_SLUGS = {
|
|
|
533
533
|
function chainIdForExactNetwork(slug) {
|
|
534
534
|
return EXACT_NETWORK_SLUGS[slug] ?? null;
|
|
535
535
|
}
|
|
536
|
+
async function resolveExactRailEvm(input) {
|
|
537
|
+
const { asset, method, readDomain, permit2Supported } = input;
|
|
538
|
+
if (asset === "native") return null;
|
|
539
|
+
const want = method === "eip3009" || method === "permit2" ? method : "auto";
|
|
540
|
+
let chosen;
|
|
541
|
+
let extra = {};
|
|
542
|
+
if (want === "permit2") {
|
|
543
|
+
chosen = "permit2";
|
|
544
|
+
} else {
|
|
545
|
+
const d = await readDomain(asset);
|
|
546
|
+
if (d) {
|
|
547
|
+
chosen = "eip3009";
|
|
548
|
+
extra = { name: d.name, version: d.version };
|
|
549
|
+
} else if (want === "eip3009") {
|
|
550
|
+
throw new Error(
|
|
551
|
+
`requirePayment: exact \`method: 'eip3009'\` requested for ${asset}, but it isn't an EIP-3009 token (no name()/version()/authorizationState). Use \`method: 'permit2'\` (any ERC-20, e.g. Binance-Peg USDC on BNB) or \`'auto'\`. (Or check your rpcUrl is reachable.)`
|
|
552
|
+
);
|
|
553
|
+
} else {
|
|
554
|
+
chosen = "permit2";
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (chosen === "permit2" && !permit2Supported()) {
|
|
558
|
+
if (method === "permit2") {
|
|
559
|
+
throw new Error(
|
|
560
|
+
`requirePayment: exact \`method: 'permit2'\` needs the x402 Permit2 proxy deployed on this chain, but it isn't there. Offer an EIP-3009 token (gasless, no proxy), or drop \`exact\` on this chain. (See PERMIT2_PROXY_CHAIN_IDS.)`
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
return { method: chosen, extra };
|
|
566
|
+
}
|
|
536
567
|
var EIP3009_TYPES = {
|
|
537
568
|
TransferWithAuthorization: [
|
|
538
569
|
{ name: "from", type: "address" },
|
|
@@ -635,10 +666,14 @@ async function payExactEvm(input) {
|
|
|
635
666
|
`exact buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account (no recoverable ECDSA signature). Pay via onchain-proof.`
|
|
636
667
|
);
|
|
637
668
|
}
|
|
638
|
-
|
|
669
|
+
let domain = await readExactDomain(publicClient, accept.asset);
|
|
670
|
+
if (!domain) {
|
|
671
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
672
|
+
domain = await readExactDomain(publicClient, accept.asset);
|
|
673
|
+
}
|
|
639
674
|
if (!domain) {
|
|
640
675
|
throw new UnsupportedSchemeError(
|
|
641
|
-
`exact: ${accept.asset} on ${accept.network} isn't an EIP-3009 token (USDT needs Permit2; native
|
|
676
|
+
`exact: couldn't derive the EIP-712 domain for ${accept.asset} on ${accept.network}. Either it isn't an EIP-3009 token (USDT needs Permit2; native/plain ERC-20 aren't exact-payable) \u2014 pay via onchain-proof \u2014 OR your RPC couldn't read it (transient): if the gate advertised eip3009, pass a reliable rpcUrl and retry.`
|
|
642
677
|
);
|
|
643
678
|
}
|
|
644
679
|
const g = globalThis.crypto;
|
|
@@ -1417,11 +1452,14 @@ function parseExactPaymentHeader(value) {
|
|
|
1417
1452
|
if (typeof network !== "string") return null;
|
|
1418
1453
|
const payload = v.payload;
|
|
1419
1454
|
if (!payload || typeof payload !== "object") return null;
|
|
1420
|
-
const signature = payload.signature;
|
|
1421
|
-
if (typeof signature !== "string") return null;
|
|
1422
1455
|
const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
|
|
1423
1456
|
const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
|
|
1424
1457
|
const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
|
|
1458
|
+
if (typeof payload.transaction === "string") {
|
|
1459
|
+
return { ...base2, method: "svm", payload: { transaction: payload.transaction } };
|
|
1460
|
+
}
|
|
1461
|
+
const signature = payload.signature;
|
|
1462
|
+
if (typeof signature !== "string") return null;
|
|
1425
1463
|
const authorization = payload.authorization;
|
|
1426
1464
|
if (authorization && typeof authorization === "object") {
|
|
1427
1465
|
for (const k of ["from", "to", "value", "validAfter", "validBefore", "nonce"]) {
|
|
@@ -1708,6 +1746,16 @@ function makeEvmNetwork(resolved) {
|
|
|
1708
1746
|
exactPermit2Supported() {
|
|
1709
1747
|
return isPermit2ProxyChain(resolved.chainId);
|
|
1710
1748
|
},
|
|
1749
|
+
// The gate's rail-advertisement SPI — EIP-3009 vs Permit2 selection (the pure helper),
|
|
1750
|
+
// injecting the on-chain domain read + the Permit2 proxy-presence check.
|
|
1751
|
+
async resolveExactRail({ asset, method }) {
|
|
1752
|
+
return resolveExactRailEvm({
|
|
1753
|
+
asset,
|
|
1754
|
+
method,
|
|
1755
|
+
readDomain: (a) => readExactDomain(publicClient, a),
|
|
1756
|
+
permit2Supported: () => isPermit2ProxyChain(resolved.chainId)
|
|
1757
|
+
});
|
|
1758
|
+
},
|
|
1711
1759
|
async settleExactSelf({ relayer, payload, accept }) {
|
|
1712
1760
|
const a = relayer._native;
|
|
1713
1761
|
if ("permit2Authorization" in payload) {
|
|
@@ -1720,6 +1768,9 @@ function makeEvmNetwork(resolved) {
|
|
|
1720
1768
|
accept
|
|
1721
1769
|
});
|
|
1722
1770
|
}
|
|
1771
|
+
if ("transaction" in payload) {
|
|
1772
|
+
return { ok: false, error: "signature_invalid", detail: "An SVM (Solana) payload was submitted to an EVM exact rail." };
|
|
1773
|
+
}
|
|
1723
1774
|
return verifyAndSettleExactEvm({
|
|
1724
1775
|
publicClient,
|
|
1725
1776
|
walletClient: a.walletClient,
|
|
@@ -1744,7 +1795,7 @@ var loaders = {
|
|
|
1744
1795
|
solana: async () => {
|
|
1745
1796
|
let mod;
|
|
1746
1797
|
try {
|
|
1747
|
-
mod = await import("./solana-
|
|
1798
|
+
mod = await import("./solana-IBVUZS54.js");
|
|
1748
1799
|
} catch (cause) {
|
|
1749
1800
|
throw new MissingDriverError(
|
|
1750
1801
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
@@ -2770,7 +2821,7 @@ var PipRailClient = class {
|
|
|
2770
2821
|
* before publishing, so retry with a brief backoff if a fresh listing is missing.
|
|
2771
2822
|
* - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
|
|
2772
2823
|
* `onchain-proof` rails by default, and standard `exact` rails too once you opt in
|
|
2773
|
-
* with `schemes: ['onchain-proof', 'exact']` (EVM
|
|
2824
|
+
* with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM).
|
|
2774
2825
|
*/
|
|
2775
2826
|
async discover(opts = {}) {
|
|
2776
2827
|
const found = await searchOpenIndexes({
|
|
@@ -2955,7 +3006,7 @@ var PipRailClient = class {
|
|
|
2955
3006
|
);
|
|
2956
3007
|
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
2957
3008
|
throw new UnsupportedSchemeError(
|
|
2958
|
-
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (EVM +
|
|
3009
|
+
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM + Solana today), and no 'onchain-proof' rail was offered.`
|
|
2959
3010
|
);
|
|
2960
3011
|
}
|
|
2961
3012
|
if (!schemes.includes("exact") && exactOnNet && typeof net.payExact === "function") {
|
|
@@ -3335,7 +3386,7 @@ var PipRailClient = class {
|
|
|
3335
3386
|
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
3336
3387
|
if (!net.payExact) {
|
|
3337
3388
|
throw new UnsupportedSchemeError(
|
|
3338
|
-
`the ${net.family} family can't pay a standard 'exact' rail (EVM +
|
|
3389
|
+
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM + Solana today).`
|
|
3339
3390
|
);
|
|
3340
3391
|
}
|
|
3341
3392
|
throwIfAborted(init?.signal);
|
|
@@ -3600,6 +3651,18 @@ straight from your wallet to the server; PipRail custodies nothing. Follow this.
|
|
|
3600
3651
|
and return the result.
|
|
3601
3652
|
Always plan before you pay so you never commit to a payment you cannot finish.
|
|
3602
3653
|
|
|
3654
|
+
## Gasless \u2014 the exact rail (zero gas for you)
|
|
3655
|
+
A 402 may offer up to two rails; you don't choose per payment \u2014 the client does, automatically:
|
|
3656
|
+
- onchain-proof (PipRail's default): you broadcast the payment yourself and pay the network gas
|
|
3657
|
+
(the native coin \u2014 ETH/SOL/\u2026). Works on every chain.
|
|
3658
|
+
- exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose
|
|
3659
|
+
(e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It
|
|
3660
|
+
works on EVM + Solana, and the on-chain method (EIP-3009 / Permit2 / SVM) is picked automatically.
|
|
3661
|
+
When the exact scheme is enabled AND balance-aware routing is on, paying picks the cheapest
|
|
3662
|
+
settleable rail \u2014 i.e. the gasless exact one. Nothing changes in your loop: quote \u2192 plan \u2192 pay is
|
|
3663
|
+
identical. The exact scheme is OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact);
|
|
3664
|
+
you can't enable it yourself, but you can report when a 402 needs it (see UNSUPPORTED_SCHEME below).
|
|
3665
|
+
|
|
3603
3666
|
## Reading a refusal \u2014 never crash, never double-spend
|
|
3604
3667
|
A failed pay returns a STRUCTURED object, never a thrown error you must catch:
|
|
3605
3668
|
{ ok:false, code, reason, explain, ref?, reasonCode?, declined? }
|
|
@@ -3617,9 +3680,13 @@ Branch on \`code\` (always reliable). Key cases:
|
|
|
3617
3680
|
- code:'INSUFFICIENT_FUNDS' \u2014 top up the wallet (token and/or native gas), retry.
|
|
3618
3681
|
- code:'PAYMENT_TIMEOUT' / 'MAX_RETRIES_EXCEEDED' / 'CONFIRMATION_TIMEOUT' \u2014 the
|
|
3619
3682
|
payment may ALREADY be on-chain. Recover using the proof on \`.ref\` (re-verify
|
|
3620
|
-
or re-submit it); never re-pay \u2014 a fresh payment would double-spend.
|
|
3683
|
+
or re-submit it); never re-pay \u2014 a fresh payment would double-spend. On a gasless
|
|
3684
|
+
exact rail \`.ref\` is the authorization NONCE, not a tx hash: re-present the SAME
|
|
3685
|
+
signed authorization, never sign a fresh one (that would risk a double-spend).
|
|
3621
3686
|
- code:'NO_COMPATIBLE_ACCEPT' / 'UNSUPPORTED_SCHEME' \u2014 the 402 isn't payable on
|
|
3622
3687
|
your chain/scheme; \`explain\` says whether it's the wrong chain or a scheme to enable.
|
|
3688
|
+
If it's a standard x402 server offering an exact rail, that's a config fix the operator makes
|
|
3689
|
+
once (enable the exact scheme); report it, don't retry the same call blindly.
|
|
3623
3690
|
|
|
3624
3691
|
## Knowing your leash \u2014 call piprail_budget
|
|
3625
3692
|
piprail_budget tells you how much budget and time you have left, per
|
|
@@ -4012,6 +4079,24 @@ function buildX402DnsTxt(input) {
|
|
|
4012
4079
|
}
|
|
4013
4080
|
|
|
4014
4081
|
// src/facilitator.ts
|
|
4082
|
+
async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
|
|
4083
|
+
const base2 = url.replace(/\/+$/, "");
|
|
4084
|
+
const ctrl = new AbortController();
|
|
4085
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
4086
|
+
try {
|
|
4087
|
+
const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
|
|
4088
|
+
if (!res.ok) return void 0;
|
|
4089
|
+
const body = await res.json();
|
|
4090
|
+
const kinds = Array.isArray(body?.kinds) ? body.kinds : [];
|
|
4091
|
+
const kind = kinds.find((k) => k?.scheme === "exact" && k?.network === network);
|
|
4092
|
+
const fp = kind?.extra?.feePayer;
|
|
4093
|
+
return typeof fp === "string" ? fp : void 0;
|
|
4094
|
+
} catch {
|
|
4095
|
+
return void 0;
|
|
4096
|
+
} finally {
|
|
4097
|
+
clearTimeout(timer);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4015
4100
|
function safeStringify(value) {
|
|
4016
4101
|
return JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v);
|
|
4017
4102
|
}
|
|
@@ -4134,6 +4219,7 @@ function createPaymentGate(options) {
|
|
|
4134
4219
|
if (resolved) return resolved;
|
|
4135
4220
|
const p = (async () => {
|
|
4136
4221
|
const accepts = normaliseAccepts(options);
|
|
4222
|
+
const exactSkips = [];
|
|
4137
4223
|
const specs = await Promise.all(
|
|
4138
4224
|
accepts.map(async (a) => {
|
|
4139
4225
|
const net = await resolveNetwork2({ chain: a.chain, rpcUrl: a.rpcUrl ?? options.rpcUrl });
|
|
@@ -4147,13 +4233,17 @@ function createPaymentGate(options) {
|
|
|
4147
4233
|
const { asset, decimals, symbol } = net.resolveToken(a.token);
|
|
4148
4234
|
const amountBase = parseUnits(a.amount, decimals);
|
|
4149
4235
|
const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
|
|
4150
|
-
if (options.exact)
|
|
4236
|
+
if (options.exact) {
|
|
4237
|
+
const outcome = await resolveExactRail(net, asset);
|
|
4238
|
+
if (outcome.rail) spec.exact = outcome.rail;
|
|
4239
|
+
else if (outcome.skipReason) exactSkips.push(outcome.skipReason);
|
|
4240
|
+
}
|
|
4151
4241
|
return spec;
|
|
4152
4242
|
})
|
|
4153
4243
|
);
|
|
4154
4244
|
if (options.exact && !specs.some((s) => s.exact)) {
|
|
4155
4245
|
throw new Error(
|
|
4156
|
-
"requirePayment: `exact` was requested but none of the offered rails support it. The standard `exact` rail is EVM ERC-20
|
|
4246
|
+
"requirePayment: `exact` was requested but none of the offered rails support it. " + (exactSkips.length > 0 ? exactSkips.join(" ") : "The standard `exact` rail is EVM ERC-20 (EIP-3009 \u2014 USDC / EURC \u2014 or Permit2, e.g. Binance-Peg USDC on BNB) or a Solana SPL token (SVM) \u2014 NOT native coins, NOT families without a standard `exact` scheme. Offer an EVM ERC-20 / Solana SPL token, or drop `exact`.")
|
|
4157
4247
|
);
|
|
4158
4248
|
}
|
|
4159
4249
|
return specs;
|
|
@@ -4166,53 +4256,48 @@ function createPaymentGate(options) {
|
|
|
4166
4256
|
}
|
|
4167
4257
|
async function resolveExactRail(net, asset) {
|
|
4168
4258
|
const cfg = options.exact;
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
if (want === "permit2") {
|
|
4176
|
-
method = "permit2";
|
|
4177
|
-
} else {
|
|
4178
|
-
const d = net.exactDomain ? await net.exactDomain(asset) : null;
|
|
4179
|
-
if (d) {
|
|
4180
|
-
method = "eip3009";
|
|
4181
|
-
domain = d;
|
|
4182
|
-
} else if (want === "eip3009") {
|
|
4259
|
+
const settle = cfg.settle;
|
|
4260
|
+
if (!net.resolveExactRail) return {};
|
|
4261
|
+
let relayer;
|
|
4262
|
+
let feePayer;
|
|
4263
|
+
if (settle === "self") {
|
|
4264
|
+
if (cfg.relayer === void 0) {
|
|
4183
4265
|
throw new Error(
|
|
4184
|
-
|
|
4266
|
+
"requirePayment: exact `settle: 'self'` needs a `relayer` wallet (the gas-paying key that broadcasts the settle), e.g. exact: { settle: 'self', relayer: { privateKey } }."
|
|
4185
4267
|
);
|
|
4186
|
-
} else {
|
|
4187
|
-
method = "permit2";
|
|
4188
4268
|
}
|
|
4269
|
+
relayer = net.bindWallet(cfg.relayer);
|
|
4270
|
+
} else {
|
|
4271
|
+
feePayer = settle.feePayer;
|
|
4189
4272
|
}
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4273
|
+
const method = cfg.method ?? "auto";
|
|
4274
|
+
let info = await net.resolveExactRail({ asset, method, relayer, feePayer });
|
|
4275
|
+
if (!info && settle !== "self" && !feePayer && asset !== "native") {
|
|
4276
|
+
const discovered = await fetchFacilitatorFeePayer(settle.facilitator, net.network);
|
|
4277
|
+
if (!discovered) {
|
|
4278
|
+
return {
|
|
4279
|
+
skipReason: `${net.network}: couldn't read a fee payer from the facilitator (${settle.facilitator}/supported) \u2014 it may be down, or may not sponsor this network. Set \`exact.settle.feePayer\` explicitly to remove the runtime dependency, switch to \`settle: 'self'\` with your own relayer, or retry.`
|
|
4280
|
+
};
|
|
4195
4281
|
}
|
|
4196
|
-
|
|
4282
|
+
info = await net.resolveExactRail({ asset, method, relayer, feePayer: discovered });
|
|
4197
4283
|
}
|
|
4198
|
-
if (
|
|
4199
|
-
|
|
4284
|
+
if (!info) return {};
|
|
4285
|
+
if (settle !== "self" && info.method === "permit2") {
|
|
4286
|
+
if (cfg.method === "permit2") {
|
|
4200
4287
|
throw new Error(
|
|
4201
|
-
"requirePayment: exact `
|
|
4288
|
+
"requirePayment: exact `method: 'permit2'` can't be settled by a third-party facilitator \u2014 facilitators settle the standard EIP-3009 (EVM) / SVM (Solana) schemes, not PipRail\u2019s Permit2 proxy. Use an EIP-3009 token (USDC / EURC) with the facilitator, or `settle: 'self'` (your own relayer) to settle Permit2 yourself."
|
|
4202
4289
|
);
|
|
4203
4290
|
}
|
|
4204
|
-
|
|
4205
|
-
|
|
4291
|
+
return {
|
|
4292
|
+
skipReason: `${net.network}: this token isn't EIP-3009 (auto-selected Permit2), which a third-party facilitator can't settle \u2014 it would serve onchain-proof only here. Use an EIP-3009 token (USDC / EURC) with the facilitator, or \`settle: 'self'\` to settle Permit2 yourself.`
|
|
4293
|
+
};
|
|
4206
4294
|
}
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
kind: "facilitator",
|
|
4212
|
-
url: cfg.settle.facilitator,
|
|
4213
|
-
...cfg.settle.authHeaders ? { authHeaders: cfg.settle.authHeaders } : {}
|
|
4214
|
-
}
|
|
4295
|
+
const mode = settle === "self" ? { kind: "self", relayer } : {
|
|
4296
|
+
kind: "facilitator",
|
|
4297
|
+
url: settle.facilitator,
|
|
4298
|
+
...settle.authHeaders ? { authHeaders: settle.authHeaders } : {}
|
|
4215
4299
|
};
|
|
4300
|
+
return { rail: { method: info.method, ...info.extra ? { extra: info.extra } : {}, mode } };
|
|
4216
4301
|
}
|
|
4217
4302
|
const hasCustomStore = Boolean(options.isUsed || options.markUsed);
|
|
4218
4303
|
const localUsed = /* @__PURE__ */ new Map();
|
|
@@ -4269,11 +4354,11 @@ function createPaymentGate(options) {
|
|
|
4269
4354
|
maxTimeoutSeconds,
|
|
4270
4355
|
extra: {
|
|
4271
4356
|
assetTransferMethod: rail.method,
|
|
4272
|
-
...rail.domain ? { name: rail.domain.name, version: rail.domain.version } : {},
|
|
4273
4357
|
minConfirmations,
|
|
4274
4358
|
decimals: s.decimals,
|
|
4275
4359
|
amountFormatted: s.amountFormatted,
|
|
4276
|
-
...s.symbol ? { symbol: s.symbol } : {}
|
|
4360
|
+
...s.symbol ? { symbol: s.symbol } : {},
|
|
4361
|
+
...rail.extra
|
|
4277
4362
|
}
|
|
4278
4363
|
};
|
|
4279
4364
|
}
|
|
@@ -4426,10 +4511,23 @@ function createPaymentGate(options) {
|
|
|
4426
4511
|
`No \`exact\` rail offered for ${exact.network}${exact.asset ? `/${exact.asset}` : ""} (offered: ${exactSpecs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
|
|
4427
4512
|
);
|
|
4428
4513
|
}
|
|
4429
|
-
|
|
4430
|
-
|
|
4514
|
+
let nonce;
|
|
4515
|
+
let evmAuth = null;
|
|
4516
|
+
if ("transaction" in exact.payload) {
|
|
4517
|
+
try {
|
|
4518
|
+
nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
|
|
4519
|
+
} catch {
|
|
4520
|
+
nonce = exact.payload.transaction;
|
|
4521
|
+
}
|
|
4522
|
+
} else if ("permit2Authorization" in exact.payload) {
|
|
4523
|
+
evmAuth = exact.payload.permit2Authorization;
|
|
4524
|
+
nonce = evmAuth.nonce;
|
|
4525
|
+
} else {
|
|
4526
|
+
evmAuth = exact.payload.authorization;
|
|
4527
|
+
nonce = evmAuth.nonce;
|
|
4528
|
+
}
|
|
4431
4529
|
if (await claimTx(nonce)) {
|
|
4432
|
-
return rejection("tx_already_used", `Authorization nonce ${nonce} was already redeemed.`);
|
|
4530
|
+
return rejection("tx_already_used", `Authorization ${evmAuth ? `nonce ${nonce}` : "transaction"} was already redeemed.`);
|
|
4433
4531
|
}
|
|
4434
4532
|
const accept = buildExactAccept(spec);
|
|
4435
4533
|
const mode = spec.exact.mode;
|
|
@@ -4454,12 +4552,14 @@ function createPaymentGate(options) {
|
|
|
4454
4552
|
amount: accept.amount,
|
|
4455
4553
|
payTo: accept.payTo,
|
|
4456
4554
|
maxTimeoutSeconds: accept.maxTimeoutSeconds,
|
|
4457
|
-
//
|
|
4458
|
-
//
|
|
4459
|
-
extra: { name: accept.extra.name ?? "", version: accept.extra.version ?? "" }
|
|
4555
|
+
// The scheme's chain-specific `extra`, from the gate's OWN trusted rail: SVM forwards the
|
|
4556
|
+
// facilitator's `feePayer` (the gas sponsor); EVM forwards the token's EIP-712 domain.
|
|
4557
|
+
extra: accept.extra.assetTransferMethod === "svm" ? { feePayer: accept.extra.feePayer ?? "" } : { name: accept.extra.name ?? "", version: accept.extra.version ?? "" }
|
|
4460
4558
|
},
|
|
4461
4559
|
receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
|
|
4462
|
-
|
|
4560
|
+
// The buyer address, for the receipt's `payer` fallback. EVM carries it in the
|
|
4561
|
+
// authorization; SVM doesn't (the facilitator returns the settled payer) → omit it.
|
|
4562
|
+
...evmAuth ? { payerHint: evmAuth.from } : {}
|
|
4463
4563
|
});
|
|
4464
4564
|
}
|
|
4465
4565
|
} catch (err) {
|