@piprail/sdk 2.8.0 → 2.10.0
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 +124 -0
- package/README.md +7 -4
- package/dist/{algorand-WJL6VRXW.js → algorand-AA3WXKW4.js} +1 -1
- package/dist/{algorand-EKD34T4Q.cjs → algorand-FCEECDG6.cjs} +32 -32
- package/dist/{aptos-CVHLVMAF.cjs → aptos-GHJPO6JJ.cjs} +31 -31
- package/dist/{aptos-WMPFEASY.js → aptos-LY67Q6QF.js} +1 -1
- package/dist/chunk-3SBTJKAG.js +92 -0
- package/dist/chunk-CQQI5IJX.cjs +92 -0
- package/dist/{chunk-VPENSVOI.cjs → chunk-MWBT7MCE.cjs} +7 -3
- package/dist/chunk-O4UQOZ4Z.cjs +14 -0
- package/dist/{chunk-W3HOMXOF.js → chunk-SC2ZYDHD.js} +6 -2
- package/dist/chunk-SK3CB7UA.js +14 -0
- package/dist/index.cjs +2247 -425
- package/dist/index.d.cts +3094 -2583
- package/dist/index.d.ts +3094 -2583
- package/dist/index.js +1937 -115
- package/dist/ledger-OQos-tmj.d.cts +925 -0
- package/dist/ledger-OQos-tmj.d.ts +925 -0
- package/dist/{near-NYFMX2KA.cjs → near-6KAQVVG2.cjs} +26 -26
- package/dist/{near-QV4IOZNX.js → near-FZBUICCS.js} +1 -1
- package/dist/node.cjs +38 -0
- package/dist/node.d.cts +16 -0
- package/dist/node.d.ts +16 -0
- package/dist/node.js +38 -0
- package/dist/receipt-NNEID77X.js +14 -0
- package/dist/receipt-QCDPMWNF.cjs +14 -0
- package/dist/{solana-Z34OWXMO.js → solana-ELUWO6N5.js} +1 -1
- package/dist/{solana-L342QCSV.cjs → solana-MYF4HBO4.cjs} +32 -32
- package/dist/{stellar-PWC4LETJ.js → stellar-ASP2THL2.js} +2 -2
- package/dist/{stellar-YEXRVR3W.cjs → stellar-CRWBSX4E.cjs} +21 -21
- package/dist/{sui-VEOGHBYE.js → sui-FZIKZNVI.js} +14 -4
- package/dist/{sui-EFRSCDWN.cjs → sui-Y4RLKKE2.cjs} +29 -19
- package/dist/{ton-N7KSPSAR.js → ton-7GKCTC5H.js} +1 -1
- package/dist/{ton-LZKJWYHV.cjs → ton-AOR3EURW.cjs} +16 -16
- package/dist/{tron-WKDUQVZR.js → tron-BMCWN5SS.js} +8 -2
- package/dist/{tron-DQU4WTGQ.cjs → tron-OMXB6EW2.cjs} +31 -25
- package/dist/{xrpl-6B4IYRWI.js → xrpl-DD7TJL5L.js} +1 -1
- package/dist/{xrpl-K3U3SBF4.cjs → xrpl-Y6SQNYLC.cjs} +20 -20
- package/package.json +11 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;
|
|
2
2
|
|
|
3
|
+
var _chunkO4UQOZ4Zcjs = require('./chunk-O4UQOZ4Z.cjs');
|
|
3
4
|
|
|
4
5
|
|
|
6
|
+
var _chunkCQQI5IJXcjs = require('./chunk-CQQI5IJX.cjs');
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
|
|
@@ -24,7 +26,12 @@
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
var _chunkMWBT7MCEcjs = require('./chunk-MWBT7MCE.cjs');
|
|
28
35
|
|
|
29
36
|
// src/drivers/registry.ts
|
|
30
37
|
var byFamily = /* @__PURE__ */ new Map();
|
|
@@ -53,13 +60,13 @@ function resolveNetwork(opts) {
|
|
|
53
60
|
const family = familyForChain(opts.chain);
|
|
54
61
|
const driver = byFamily.get(family);
|
|
55
62
|
if (!driver) {
|
|
56
|
-
throw new (0,
|
|
63
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
57
64
|
`No driver registered for the "${family}" family \u2014 it may not be mounted yet (use the async resolveNetwork()).`
|
|
58
65
|
);
|
|
59
66
|
}
|
|
60
67
|
const net = driver.resolve(opts);
|
|
61
68
|
if (!net) {
|
|
62
|
-
throw new (0,
|
|
69
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
63
70
|
`The ${family} driver didn't recognise this chain input.`
|
|
64
71
|
);
|
|
65
72
|
}
|
|
@@ -320,12 +327,12 @@ function resolveChain(input, rpcUrlOverride) {
|
|
|
320
327
|
|
|
321
328
|
var _accounts = require('viem/accounts');
|
|
322
329
|
function createWalletAdapter(config, resolved) {
|
|
323
|
-
|
|
330
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, config, "EVM");
|
|
324
331
|
if ("key" in config) {
|
|
325
332
|
const key = config.key;
|
|
326
333
|
if (typeof key !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(key)) {
|
|
327
334
|
const hint = typeof key === "string" && !key.startsWith("0x") ? " (got a non-0x string \u2014 a base58/seed key belongs to another family; pass { key } as the EVM chain's 0x\u2026 hex secret)." : " (expected 0x followed by 64 hex characters).";
|
|
328
|
-
throw new (0,
|
|
335
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
329
336
|
`chain is EVM; the wallet { key } is not a valid 0x\u2026 32-byte hex private key${hint}`
|
|
330
337
|
);
|
|
331
338
|
}
|
|
@@ -333,7 +340,7 @@ function createWalletAdapter(config, resolved) {
|
|
|
333
340
|
try {
|
|
334
341
|
account = _accounts.privateKeyToAccount.call(void 0, key);
|
|
335
342
|
} catch (err) {
|
|
336
|
-
throw new (0,
|
|
343
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
337
344
|
`chain is EVM; the wallet { key } is not a valid 0x\u2026 32-byte hex private key: ${err instanceof Error ? err.message : String(err)}.`
|
|
338
345
|
);
|
|
339
346
|
}
|
|
@@ -343,12 +350,12 @@ function createWalletAdapter(config, resolved) {
|
|
|
343
350
|
}
|
|
344
351
|
const wc = config.walletClient;
|
|
345
352
|
if (!wc.account) {
|
|
346
|
-
throw new (0,
|
|
353
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
347
354
|
"chain is EVM; the provided walletClient has no attached account. Use `createWalletClient({ account, chain, transport })`, or pass { key }."
|
|
348
355
|
);
|
|
349
356
|
}
|
|
350
357
|
if (wc.chain && wc.chain.id !== resolved.chainId) {
|
|
351
|
-
throw new (0,
|
|
358
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
|
|
352
359
|
`PipRailClient: walletClient is on chain ${wc.chain.id} but the SDK was configured with chain ${resolved.chainId}. They must match.`
|
|
353
360
|
);
|
|
354
361
|
}
|
|
@@ -678,7 +685,7 @@ async function payExactEvm(input) {
|
|
|
678
685
|
code = void 0;
|
|
679
686
|
}
|
|
680
687
|
if (code && code !== "0x") {
|
|
681
|
-
throw new (0,
|
|
688
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
682
689
|
`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.`
|
|
683
690
|
);
|
|
684
691
|
}
|
|
@@ -688,13 +695,13 @@ async function payExactEvm(input) {
|
|
|
688
695
|
domain = await readExactDomain(publicClient, accept.asset);
|
|
689
696
|
}
|
|
690
697
|
if (!domain) {
|
|
691
|
-
throw new (0,
|
|
698
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
692
699
|
`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.`
|
|
693
700
|
);
|
|
694
701
|
}
|
|
695
702
|
const g = globalThis.crypto;
|
|
696
703
|
if (!_optionalChain([g, 'optionalAccess', _5 => _5.getRandomValues])) {
|
|
697
|
-
throw new (0,
|
|
704
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
698
705
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the exact rail needs a CSPRNG nonce."
|
|
699
706
|
);
|
|
700
707
|
}
|
|
@@ -951,7 +958,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
951
958
|
args: writeArgs
|
|
952
959
|
});
|
|
953
960
|
} catch (err) {
|
|
954
|
-
throw new (0,
|
|
961
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
955
962
|
`exact settle: the merchant relayer failed to broadcast transferWithAuthorization (${shorten(err instanceof Error ? err.message : String(err))}). The payer's authorization is still valid and unused \u2014 fund/fix the relayer and the payer can retry.`,
|
|
956
963
|
{ cause: err }
|
|
957
964
|
);
|
|
@@ -963,7 +970,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
963
970
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
964
971
|
}
|
|
965
972
|
} catch (err) {
|
|
966
|
-
throw new (0,
|
|
973
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
967
974
|
`exact settle: broadcast ${txHash} but couldn't confirm it (${shorten(err instanceof Error ? err.message : String(err))}).`,
|
|
968
975
|
{ cause: err }
|
|
969
976
|
);
|
|
@@ -1094,7 +1101,7 @@ function shorten2(msg) {
|
|
|
1094
1101
|
function randomPermit2Nonce() {
|
|
1095
1102
|
const g = globalThis.crypto;
|
|
1096
1103
|
if (!_optionalChain([g, 'optionalAccess', _8 => _8.getRandomValues])) {
|
|
1097
|
-
throw new (0,
|
|
1104
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1098
1105
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the permit2 rail needs a CSPRNG nonce."
|
|
1099
1106
|
);
|
|
1100
1107
|
}
|
|
@@ -1113,7 +1120,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1113
1120
|
args: [account.address, PERMIT2_ADDRESS]
|
|
1114
1121
|
});
|
|
1115
1122
|
} catch (err) {
|
|
1116
|
-
throw new (0,
|
|
1123
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1117
1124
|
`permit2: couldn't read the Permit2 allowance for ${token} (${shorten2(err instanceof Error ? err.message : String(err))}).`
|
|
1118
1125
|
);
|
|
1119
1126
|
}
|
|
@@ -1130,7 +1137,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1130
1137
|
await publicClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1131
1138
|
return hash;
|
|
1132
1139
|
} catch (err) {
|
|
1133
|
-
throw _nullishCoalesce(
|
|
1140
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1134
1141
|
`permit2: the one-time Permit2 approval for ${token} failed to broadcast (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1135
1142
|
{ cause: err }
|
|
1136
1143
|
)));
|
|
@@ -1145,7 +1152,7 @@ async function payPermit2Evm(input) {
|
|
|
1145
1152
|
code = void 0;
|
|
1146
1153
|
}
|
|
1147
1154
|
if (code && code !== "0x") {
|
|
1148
|
-
throw new (0,
|
|
1155
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1149
1156
|
`permit2 buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
|
|
1150
1157
|
);
|
|
1151
1158
|
}
|
|
@@ -1320,7 +1327,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1320
1327
|
args: settleArgs
|
|
1321
1328
|
});
|
|
1322
1329
|
} catch (err) {
|
|
1323
|
-
throw new (0,
|
|
1330
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1324
1331
|
`permit2 settle: the merchant relayer failed to broadcast the proxy settle (${shorten2(err instanceof Error ? err.message : String(err))}). The payer's signature is still valid and its nonce unused \u2014 fund/fix the relayer and the payer can retry.`,
|
|
1325
1332
|
{ cause: err }
|
|
1326
1333
|
);
|
|
@@ -1332,7 +1339,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1332
1339
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
1333
1340
|
}
|
|
1334
1341
|
} catch (err) {
|
|
1335
|
-
throw new (0,
|
|
1342
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1336
1343
|
`permit2 settle: broadcast ${txHash} but couldn't confirm it (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1337
1344
|
{ cause: err }
|
|
1338
1345
|
);
|
|
@@ -1353,144 +1360,540 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1353
1360
|
};
|
|
1354
1361
|
}
|
|
1355
1362
|
|
|
1356
|
-
// src/
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
var
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1363
|
+
// src/drivers/evm/upto.ts
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
var X402_UPTO_PERMIT2_PROXY = "0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002";
|
|
1369
|
+
var UPTO_PROXY_CHAIN_IDS = /* @__PURE__ */ new Set([
|
|
1370
|
+
1,
|
|
1371
|
+
// Ethereum
|
|
1372
|
+
8453,
|
|
1373
|
+
// Base
|
|
1374
|
+
42161,
|
|
1375
|
+
// Arbitrum
|
|
1376
|
+
10,
|
|
1377
|
+
// Optimism
|
|
1378
|
+
137,
|
|
1379
|
+
// Polygon
|
|
1380
|
+
56
|
|
1381
|
+
// BNB
|
|
1382
|
+
]);
|
|
1383
|
+
function isUptoProxyChain(chainId) {
|
|
1384
|
+
return UPTO_PROXY_CHAIN_IDS.has(chainId);
|
|
1385
|
+
}
|
|
1386
|
+
var PERMIT2_UPTO_WITNESS_TYPES = {
|
|
1387
|
+
PermitWitnessTransferFrom: PERMIT2_WITNESS_TYPES.PermitWitnessTransferFrom,
|
|
1388
|
+
TokenPermissions: PERMIT2_WITNESS_TYPES.TokenPermissions,
|
|
1389
|
+
Witness: [
|
|
1390
|
+
{ name: "to", type: "address" },
|
|
1391
|
+
{ name: "facilitator", type: "address" },
|
|
1392
|
+
{ name: "validAfter", type: "uint256" }
|
|
1393
|
+
]
|
|
1394
|
+
};
|
|
1395
|
+
var x402UptoProxyAbi = [
|
|
1396
|
+
{
|
|
1397
|
+
type: "function",
|
|
1398
|
+
name: "settle",
|
|
1399
|
+
stateMutability: "nonpayable",
|
|
1400
|
+
outputs: [],
|
|
1401
|
+
inputs: [
|
|
1402
|
+
{
|
|
1403
|
+
name: "permit",
|
|
1404
|
+
type: "tuple",
|
|
1405
|
+
components: [
|
|
1406
|
+
{
|
|
1407
|
+
name: "permitted",
|
|
1408
|
+
type: "tuple",
|
|
1409
|
+
components: [
|
|
1410
|
+
{ name: "token", type: "address" },
|
|
1411
|
+
{ name: "amount", type: "uint256" }
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
{ name: "nonce", type: "uint256" },
|
|
1415
|
+
{ name: "deadline", type: "uint256" }
|
|
1416
|
+
]
|
|
1417
|
+
},
|
|
1418
|
+
{ name: "amount", type: "uint256" },
|
|
1419
|
+
{ name: "owner", type: "address" },
|
|
1420
|
+
{
|
|
1421
|
+
name: "witness",
|
|
1422
|
+
type: "tuple",
|
|
1423
|
+
components: [
|
|
1424
|
+
{ name: "to", type: "address" },
|
|
1425
|
+
{ name: "facilitator", type: "address" },
|
|
1426
|
+
{ name: "validAfter", type: "uint256" }
|
|
1427
|
+
]
|
|
1428
|
+
},
|
|
1429
|
+
{ name: "signature", type: "bytes" }
|
|
1430
|
+
]
|
|
1369
1431
|
}
|
|
1370
|
-
|
|
1432
|
+
];
|
|
1433
|
+
function shorten3(msg) {
|
|
1434
|
+
const oneLine = msg.replace(/\s+/g, " ").trim();
|
|
1435
|
+
return oneLine.length > 200 ? `${oneLine.slice(0, 200)}\u2026` : oneLine;
|
|
1371
1436
|
}
|
|
1372
|
-
function
|
|
1373
|
-
|
|
1374
|
-
if (
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
return new TextDecoder().decode(bytes);
|
|
1437
|
+
function randomPermit2Nonce2() {
|
|
1438
|
+
const g = globalThis.crypto;
|
|
1439
|
+
if (!_optionalChain([g, 'optionalAccess', _9 => _9.getRandomValues])) {
|
|
1440
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1441
|
+
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the upto rail needs a CSPRNG nonce."
|
|
1442
|
+
);
|
|
1379
1443
|
}
|
|
1380
|
-
|
|
1444
|
+
const raw = new Uint8Array(32);
|
|
1445
|
+
g.getRandomValues(raw);
|
|
1446
|
+
return BigInt(`0x${[...raw].map((b) => b.toString(16).padStart(2, "0")).join("")}`);
|
|
1381
1447
|
}
|
|
1382
|
-
function
|
|
1448
|
+
async function payUptoEvm(input) {
|
|
1449
|
+
const { publicClient, walletClient, account, chainId, chain, accept } = input;
|
|
1450
|
+
let code;
|
|
1383
1451
|
try {
|
|
1384
|
-
|
|
1452
|
+
code = await publicClient.getCode({ address: account.address });
|
|
1385
1453
|
} catch (e16) {
|
|
1386
|
-
|
|
1454
|
+
code = void 0;
|
|
1387
1455
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
function networkForChain(chainId) {
|
|
1393
|
-
return `eip155:${chainId}`;
|
|
1394
|
-
}
|
|
1395
|
-
function chainIdFromNetwork(network) {
|
|
1396
|
-
const match = /^eip155:(\d+)$/.exec(network);
|
|
1397
|
-
if (!match || !match[1]) return null;
|
|
1398
|
-
const n = Number(match[1]);
|
|
1399
|
-
return Number.isSafeInteger(n) && n > 0 ? n : null;
|
|
1400
|
-
}
|
|
1401
|
-
function buildChallengeHeader(challenge) {
|
|
1402
|
-
return toBase64Json(challenge);
|
|
1403
|
-
}
|
|
1404
|
-
function buildReceiptHeader(receipt) {
|
|
1405
|
-
return toBase64Json(receipt);
|
|
1406
|
-
}
|
|
1407
|
-
function buildSignatureHeader(signature) {
|
|
1408
|
-
return toBase64Json(signature);
|
|
1409
|
-
}
|
|
1410
|
-
function buildExactSignatureHeader(input) {
|
|
1411
|
-
return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
|
|
1412
|
-
}
|
|
1413
|
-
async function parseChallenge(response) {
|
|
1414
|
-
const headerValue = response.headers.get(HEADER_REQUIRED);
|
|
1415
|
-
if (headerValue) {
|
|
1416
|
-
const parsed = fromBase64Json(headerValue);
|
|
1417
|
-
if (isValidChallenge(parsed)) return parsed;
|
|
1456
|
+
if (code && code !== "0x") {
|
|
1457
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1458
|
+
`upto buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
|
|
1459
|
+
);
|
|
1418
1460
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1461
|
+
const facilitatorAddress = _optionalChain([accept, 'access', _10 => _10.extra, 'optionalAccess', _11 => _11.facilitatorAddress]);
|
|
1462
|
+
if (typeof facilitatorAddress !== "string" || facilitatorAddress.length === 0) {
|
|
1463
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1464
|
+
`upto: the rail carries no extra.facilitatorAddress to bind into witness.facilitator \u2014 refusing to sign.`
|
|
1465
|
+
);
|
|
1423
1466
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
const
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1467
|
+
const token = _viem.getAddress.call(void 0, accept.asset);
|
|
1468
|
+
const payTo = _viem.getAddress.call(void 0, accept.payTo);
|
|
1469
|
+
const facilitator = _viem.getAddress.call(void 0, facilitatorAddress);
|
|
1470
|
+
const value = BigInt(accept.amount);
|
|
1471
|
+
const approvalTx = await ensurePermit2Allowance({
|
|
1472
|
+
publicClient,
|
|
1473
|
+
walletClient,
|
|
1474
|
+
account,
|
|
1475
|
+
chain,
|
|
1476
|
+
token,
|
|
1477
|
+
amount: value
|
|
1478
|
+
});
|
|
1479
|
+
const nonce = randomPermit2Nonce2();
|
|
1480
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + accept.maxTimeoutSeconds);
|
|
1481
|
+
const validAfter = 0n;
|
|
1482
|
+
const spender = _viem.getAddress.call(void 0, X402_UPTO_PERMIT2_PROXY);
|
|
1483
|
+
const from = account.address;
|
|
1484
|
+
const signature = await walletClient.signTypedData({
|
|
1485
|
+
account,
|
|
1486
|
+
domain: { name: "Permit2", chainId, verifyingContract: PERMIT2_ADDRESS },
|
|
1487
|
+
types: PERMIT2_UPTO_WITNESS_TYPES,
|
|
1488
|
+
primaryType: "PermitWitnessTransferFrom",
|
|
1489
|
+
message: {
|
|
1490
|
+
permitted: { token, amount: value },
|
|
1491
|
+
spender,
|
|
1492
|
+
nonce,
|
|
1493
|
+
deadline,
|
|
1494
|
+
witness: { to: payTo, facilitator, validAfter }
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
const permit2Authorization = {
|
|
1498
|
+
permitted: { token, amount: value.toString() },
|
|
1499
|
+
from,
|
|
1500
|
+
spender,
|
|
1501
|
+
nonce: nonce.toString(),
|
|
1502
|
+
deadline: deadline.toString(),
|
|
1503
|
+
witness: { to: payTo, facilitator, validAfter: validAfter.toString() }
|
|
1504
|
+
};
|
|
1437
1505
|
return {
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
...
|
|
1442
|
-
...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {}
|
|
1506
|
+
payload: { signature, permit2Authorization },
|
|
1507
|
+
payerFrom: from,
|
|
1508
|
+
nonce: nonce.toString(),
|
|
1509
|
+
...approvalTx ? { approvalTx } : {}
|
|
1443
1510
|
};
|
|
1444
1511
|
}
|
|
1445
|
-
function
|
|
1446
|
-
const
|
|
1447
|
-
if (
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
if (!payload || typeof payload.txHash !== "string" || typeof payload.nonce !== "string") {
|
|
1454
|
-
return null;
|
|
1512
|
+
function resolveUptoRailEvm(input) {
|
|
1513
|
+
const { asset, relayerAddress, proxySupported, domain } = input;
|
|
1514
|
+
if (asset === "native") return null;
|
|
1515
|
+
if (!proxySupported()) return null;
|
|
1516
|
+
const extra = { facilitatorAddress: _viem.getAddress.call(void 0, relayerAddress) };
|
|
1517
|
+
if (domain) {
|
|
1518
|
+
extra.name = domain.name;
|
|
1519
|
+
extra.version = domain.version;
|
|
1455
1520
|
}
|
|
1456
|
-
return
|
|
1521
|
+
return { method: "permit2-upto", extra };
|
|
1457
1522
|
}
|
|
1458
|
-
function
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
const
|
|
1462
|
-
const
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1523
|
+
async function verifyAndSettleUptoEvm(input) {
|
|
1524
|
+
const { publicClient, walletClient, account, chain, payload, accept, settleAmount } = input;
|
|
1525
|
+
const token = _viem.getAddress.call(void 0, accept.asset);
|
|
1526
|
+
const payTo = _viem.getAddress.call(void 0, accept.payTo);
|
|
1527
|
+
const maxAmount = BigInt(accept.amount);
|
|
1528
|
+
const proxy = _viem.getAddress.call(void 0, X402_UPTO_PERMIT2_PROXY);
|
|
1529
|
+
const relayerAddress = _viem.getAddress.call(void 0, account.address);
|
|
1530
|
+
let from;
|
|
1531
|
+
let spender;
|
|
1532
|
+
let permittedToken;
|
|
1533
|
+
let witnessTo;
|
|
1534
|
+
let witnessFacilitator;
|
|
1535
|
+
let permittedAmount;
|
|
1536
|
+
let nonce;
|
|
1537
|
+
let deadline;
|
|
1538
|
+
let validAfter;
|
|
1539
|
+
const signature = payload.signature;
|
|
1540
|
+
try {
|
|
1541
|
+
const pa = payload.permit2Authorization;
|
|
1542
|
+
from = _viem.getAddress.call(void 0, pa.from);
|
|
1543
|
+
spender = _viem.getAddress.call(void 0, pa.spender);
|
|
1544
|
+
permittedToken = _viem.getAddress.call(void 0, pa.permitted.token);
|
|
1545
|
+
witnessTo = _viem.getAddress.call(void 0, pa.witness.to);
|
|
1546
|
+
witnessFacilitator = _viem.getAddress.call(void 0, pa.witness.facilitator);
|
|
1547
|
+
permittedAmount = BigInt(pa.permitted.amount);
|
|
1548
|
+
nonce = BigInt(pa.nonce);
|
|
1549
|
+
deadline = BigInt(pa.deadline);
|
|
1550
|
+
validAfter = BigInt(pa.witness.validAfter);
|
|
1551
|
+
if (!/^0x[0-9a-fA-F]+$/.test(signature)) throw new Error("signature must be hex");
|
|
1552
|
+
} catch (err) {
|
|
1553
|
+
return {
|
|
1554
|
+
ok: false,
|
|
1555
|
+
error: "signature_invalid",
|
|
1556
|
+
detail: `Malformed upto authorization: ${err instanceof Error ? err.message : String(err)}.`
|
|
1557
|
+
};
|
|
1474
1558
|
}
|
|
1475
|
-
if (
|
|
1476
|
-
return {
|
|
1559
|
+
if (witnessTo !== payTo) {
|
|
1560
|
+
return { ok: false, error: "wrong_recipient", detail: `Authorization pays witness.to ${witnessTo}, not ${payTo}.` };
|
|
1477
1561
|
}
|
|
1478
|
-
if (
|
|
1479
|
-
return {
|
|
1562
|
+
if (permittedToken !== token) {
|
|
1563
|
+
return { ok: false, error: "signature_invalid", detail: `Authorization permits token ${permittedToken}, not the rail's ${token}.` };
|
|
1480
1564
|
}
|
|
1481
|
-
if (
|
|
1482
|
-
|
|
1483
|
-
const index = payload.paymentIndex;
|
|
1484
|
-
if (group.length === 0 || group.length > 16 || !group.every((t) => typeof t === "string") || typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= group.length) {
|
|
1485
|
-
return null;
|
|
1486
|
-
}
|
|
1487
|
-
return {
|
|
1488
|
-
...base2,
|
|
1489
|
-
method: "algorand",
|
|
1490
|
-
payload: { paymentIndex: index, paymentGroup: group }
|
|
1491
|
-
};
|
|
1565
|
+
if (spender !== proxy) {
|
|
1566
|
+
return { ok: false, error: "signature_invalid", detail: `Authorization spender ${spender} is not the x402UptoPermit2Proxy ${proxy}; it can't be settled here.` };
|
|
1492
1567
|
}
|
|
1493
|
-
|
|
1568
|
+
if (witnessFacilitator !== relayerAddress) {
|
|
1569
|
+
return { ok: false, error: "signature_invalid", detail: `Authorization binds facilitator ${witnessFacilitator}, not this relayer ${relayerAddress}; only the bound facilitator can settle.` };
|
|
1570
|
+
}
|
|
1571
|
+
if (permittedAmount < maxAmount) {
|
|
1572
|
+
return { ok: false, error: "amount_too_low", detail: `Permitted (signed MAX) ${permittedAmount} is below the rail max ${maxAmount}.` };
|
|
1573
|
+
}
|
|
1574
|
+
if (permittedAmount > maxAmount) {
|
|
1575
|
+
return { ok: false, error: "upto_settle_exceeds_max", detail: `Permitted (signed MAX) ${permittedAmount} exceeds the advertised rail max ${maxAmount}; sign exactly the ceiling.` };
|
|
1576
|
+
}
|
|
1577
|
+
if (settleAmount < 0n) {
|
|
1578
|
+
return { ok: false, error: "upto_settle_exceeds_max", detail: `Settle amount ${settleAmount} is negative.` };
|
|
1579
|
+
}
|
|
1580
|
+
if (settleAmount > permittedAmount) {
|
|
1581
|
+
return { ok: false, error: "upto_settle_exceeds_max", detail: `Settle amount ${settleAmount} exceeds the signed MAX ${permittedAmount}.` };
|
|
1582
|
+
}
|
|
1583
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
1584
|
+
if (deadline <= now) {
|
|
1585
|
+
return { ok: false, error: "payment_expired", detail: `Permit2 deadline ${deadline} <= now ${now}.` };
|
|
1586
|
+
}
|
|
1587
|
+
let fromCode;
|
|
1588
|
+
try {
|
|
1589
|
+
fromCode = await publicClient.getCode({ address: from });
|
|
1590
|
+
} catch (e17) {
|
|
1591
|
+
return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
|
|
1592
|
+
}
|
|
1593
|
+
if (!(fromCode && fromCode !== "0x")) {
|
|
1594
|
+
let recovered;
|
|
1595
|
+
try {
|
|
1596
|
+
recovered = await _viem.recoverTypedDataAddress.call(void 0, {
|
|
1597
|
+
domain: { name: "Permit2", chainId: chain.id, verifyingContract: PERMIT2_ADDRESS },
|
|
1598
|
+
types: PERMIT2_UPTO_WITNESS_TYPES,
|
|
1599
|
+
primaryType: "PermitWitnessTransferFrom",
|
|
1600
|
+
message: {
|
|
1601
|
+
permitted: { token: permittedToken, amount: permittedAmount },
|
|
1602
|
+
spender,
|
|
1603
|
+
nonce,
|
|
1604
|
+
deadline,
|
|
1605
|
+
witness: { to: witnessTo, facilitator: witnessFacilitator, validAfter }
|
|
1606
|
+
},
|
|
1607
|
+
signature
|
|
1608
|
+
});
|
|
1609
|
+
} catch (err) {
|
|
1610
|
+
return { ok: false, error: "signature_invalid", detail: `Not a valid EIP-712 signature: ${shorten3(err instanceof Error ? err.message : String(err))}.` };
|
|
1611
|
+
}
|
|
1612
|
+
if (recovered !== from) {
|
|
1613
|
+
return { ok: false, error: "signature_invalid", detail: `Signature recovered to ${recovered}, not the authorizer ${from}.` };
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (settleAmount === 0n) {
|
|
1617
|
+
return {
|
|
1618
|
+
ok: true,
|
|
1619
|
+
receipt: {
|
|
1620
|
+
scheme: "upto",
|
|
1621
|
+
success: true,
|
|
1622
|
+
network: accept.network,
|
|
1623
|
+
transaction: "",
|
|
1624
|
+
asset: accept.asset,
|
|
1625
|
+
amount: "0",
|
|
1626
|
+
payer: from,
|
|
1627
|
+
payTo: accept.payTo,
|
|
1628
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
try {
|
|
1633
|
+
const word = nonce >> 8n;
|
|
1634
|
+
const bit = nonce & 0xffn;
|
|
1635
|
+
const bitmap = await publicClient.readContract({
|
|
1636
|
+
address: PERMIT2_ADDRESS,
|
|
1637
|
+
abi: permit2NonceBitmapAbi,
|
|
1638
|
+
functionName: "nonceBitmap",
|
|
1639
|
+
args: [from, word]
|
|
1640
|
+
});
|
|
1641
|
+
if ((bitmap >> bit & 1n) === 1n) {
|
|
1642
|
+
return { ok: false, error: "tx_already_used", detail: `Permit2 nonce ${nonce} already used or invalidated for ${from}.` };
|
|
1643
|
+
}
|
|
1644
|
+
} catch (e18) {
|
|
1645
|
+
return { ok: false, error: "tx_not_found", detail: "Could not read the Permit2 nonce bitmap (transient RPC) \u2014 retry." };
|
|
1646
|
+
}
|
|
1647
|
+
const settleArgs = [
|
|
1648
|
+
{ permitted: { token: permittedToken, amount: permittedAmount }, nonce, deadline },
|
|
1649
|
+
settleAmount,
|
|
1650
|
+
from,
|
|
1651
|
+
{ to: witnessTo, facilitator: witnessFacilitator, validAfter },
|
|
1652
|
+
signature
|
|
1653
|
+
];
|
|
1654
|
+
try {
|
|
1655
|
+
await publicClient.simulateContract({
|
|
1656
|
+
account,
|
|
1657
|
+
address: proxy,
|
|
1658
|
+
abi: x402UptoProxyAbi,
|
|
1659
|
+
functionName: "settle",
|
|
1660
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1661
|
+
args: settleArgs
|
|
1662
|
+
});
|
|
1663
|
+
} catch (err) {
|
|
1664
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1665
|
+
if (/nonce|invalidated|used/i.test(msg)) return { ok: false, error: "tx_already_used", detail: "Permit2 nonce is used or invalidated." };
|
|
1666
|
+
if (/expired|deadline|too early|not yet/i.test(msg)) return { ok: false, error: "payment_expired", detail: shorten3(msg) };
|
|
1667
|
+
if (/exceeds.*permitted|AmountExceeds/i.test(msg)) return { ok: false, error: "upto_settle_exceeds_max", detail: shorten3(msg) };
|
|
1668
|
+
if (/facilitator|Unauthorized/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten3(msg) };
|
|
1669
|
+
if (/signature/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten3(msg) };
|
|
1670
|
+
return { ok: false, error: "tx_reverted", detail: `upto settle would revert: ${shorten3(msg)}` };
|
|
1671
|
+
}
|
|
1672
|
+
let txHash;
|
|
1673
|
+
try {
|
|
1674
|
+
txHash = await walletClient.writeContract({
|
|
1675
|
+
account,
|
|
1676
|
+
chain,
|
|
1677
|
+
address: proxy,
|
|
1678
|
+
abi: x402UptoProxyAbi,
|
|
1679
|
+
functionName: "settle",
|
|
1680
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1681
|
+
args: settleArgs
|
|
1682
|
+
});
|
|
1683
|
+
} catch (err) {
|
|
1684
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1685
|
+
`upto settle: the merchant relayer failed to broadcast the proxy settle (${shorten3(err instanceof Error ? err.message : String(err))}). The payer's signature is still valid and its nonce unused \u2014 fund/fix the relayer and the payer can retry.`,
|
|
1686
|
+
{ cause: err }
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
try {
|
|
1690
|
+
const confirmations = _nullishCoalesce(accept.extra.minConfirmations, () => ( 1));
|
|
1691
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, confirmations });
|
|
1692
|
+
if (receipt.status !== "success") {
|
|
1693
|
+
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
1694
|
+
}
|
|
1695
|
+
} catch (err) {
|
|
1696
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1697
|
+
`upto settle: broadcast ${txHash} but couldn't confirm it (${shorten3(err instanceof Error ? err.message : String(err))}).`,
|
|
1698
|
+
{ cause: err }
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
return {
|
|
1702
|
+
ok: true,
|
|
1703
|
+
receipt: {
|
|
1704
|
+
scheme: "upto",
|
|
1705
|
+
success: true,
|
|
1706
|
+
network: accept.network,
|
|
1707
|
+
transaction: txHash,
|
|
1708
|
+
asset: accept.asset,
|
|
1709
|
+
// The ACTUAL settled amount (≤ max) — what the buyer reads back to record metered spend.
|
|
1710
|
+
amount: settleAmount.toString(),
|
|
1711
|
+
payer: from,
|
|
1712
|
+
payTo: accept.payTo,
|
|
1713
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
// src/x402.ts
|
|
1719
|
+
var HEADER_REQUIRED = "payment-required";
|
|
1720
|
+
var HEADER_SIGNATURE = "payment-signature";
|
|
1721
|
+
var HEADER_RESPONSE = "payment-response";
|
|
1722
|
+
var HEADER_SIGNATURE_V1 = "x-payment";
|
|
1723
|
+
var HEADER_RESPONSE_V1 = "x-payment-response";
|
|
1724
|
+
function encodeBase64(str) {
|
|
1725
|
+
if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
|
|
1726
|
+
if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
|
|
1727
|
+
const bytes = new TextEncoder().encode(str);
|
|
1728
|
+
let binary = "";
|
|
1729
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
1730
|
+
return btoa(binary);
|
|
1731
|
+
}
|
|
1732
|
+
throw new Error("No base64 encoder available in this runtime.");
|
|
1733
|
+
}
|
|
1734
|
+
function decodeBase64(b64) {
|
|
1735
|
+
if (typeof Buffer !== "undefined") return Buffer.from(b64, "base64").toString("utf8");
|
|
1736
|
+
if (typeof atob === "function" && typeof TextDecoder !== "undefined") {
|
|
1737
|
+
const binary = atob(b64);
|
|
1738
|
+
const bytes = new Uint8Array(binary.length);
|
|
1739
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
1740
|
+
return new TextDecoder().decode(bytes);
|
|
1741
|
+
}
|
|
1742
|
+
throw new Error("No base64 decoder available in this runtime.");
|
|
1743
|
+
}
|
|
1744
|
+
function fromBase64Json(b64) {
|
|
1745
|
+
try {
|
|
1746
|
+
return JSON.parse(decodeBase64(b64));
|
|
1747
|
+
} catch (e19) {
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
function decodeBase64Json(value) {
|
|
1752
|
+
return fromBase64Json(value);
|
|
1753
|
+
}
|
|
1754
|
+
function toBase64Json(value) {
|
|
1755
|
+
return encodeBase64(JSON.stringify(value));
|
|
1756
|
+
}
|
|
1757
|
+
function networkForChain(chainId) {
|
|
1758
|
+
return `eip155:${chainId}`;
|
|
1759
|
+
}
|
|
1760
|
+
function chainIdFromNetwork(network) {
|
|
1761
|
+
const match = /^eip155:(\d+)$/.exec(network);
|
|
1762
|
+
if (!match || !match[1]) return null;
|
|
1763
|
+
const n = Number(match[1]);
|
|
1764
|
+
return Number.isSafeInteger(n) && n > 0 ? n : null;
|
|
1765
|
+
}
|
|
1766
|
+
function buildChallengeHeader(challenge) {
|
|
1767
|
+
return toBase64Json(challenge);
|
|
1768
|
+
}
|
|
1769
|
+
function buildReceiptHeader(receipt, extensions) {
|
|
1770
|
+
return toBase64Json(extensions ? { ...receipt, extensions } : receipt);
|
|
1771
|
+
}
|
|
1772
|
+
var EXT_OFFER_RECEIPT = "offer-receipt";
|
|
1773
|
+
function buildReceiptExtension(bundle) {
|
|
1774
|
+
const info = { settlement: bundle.receipt, resource: bundle.resource };
|
|
1775
|
+
if (typeof bundle.decimals === "number") info.decimals = bundle.decimals;
|
|
1776
|
+
if (bundle.attestation) info.receipt = bundle.attestation;
|
|
1777
|
+
return { [EXT_OFFER_RECEIPT]: { info } };
|
|
1778
|
+
}
|
|
1779
|
+
function buildSignatureHeader(signature) {
|
|
1780
|
+
return toBase64Json(signature);
|
|
1781
|
+
}
|
|
1782
|
+
function buildExactSignatureHeader(input) {
|
|
1783
|
+
return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
|
|
1784
|
+
}
|
|
1785
|
+
function buildUptoSignatureHeader(input) {
|
|
1786
|
+
return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
|
|
1787
|
+
}
|
|
1788
|
+
async function parseChallenge(response) {
|
|
1789
|
+
const headerValue = response.headers.get(HEADER_REQUIRED);
|
|
1790
|
+
if (headerValue) {
|
|
1791
|
+
const parsed = fromBase64Json(headerValue);
|
|
1792
|
+
if (isValidChallenge(parsed)) return parsed;
|
|
1793
|
+
}
|
|
1794
|
+
try {
|
|
1795
|
+
const body = await response.clone().json();
|
|
1796
|
+
if (isValidChallenge(body)) return body;
|
|
1797
|
+
} catch (e20) {
|
|
1798
|
+
}
|
|
1799
|
+
return null;
|
|
1800
|
+
}
|
|
1801
|
+
function parseReceipt(response) {
|
|
1802
|
+
const headerValue = _nullishCoalesce(response.headers.get(HEADER_RESPONSE), () => ( response.headers.get(HEADER_RESPONSE_V1)));
|
|
1803
|
+
if (!headerValue) return null;
|
|
1804
|
+
const parsed = fromBase64Json(headerValue);
|
|
1805
|
+
return isValidReceipt(parsed) ? parsed : null;
|
|
1806
|
+
}
|
|
1807
|
+
function parseReceiptExtension(response) {
|
|
1808
|
+
const headerValue = _nullishCoalesce(response.headers.get(HEADER_RESPONSE), () => ( response.headers.get(HEADER_RESPONSE_V1)));
|
|
1809
|
+
if (!headerValue) return null;
|
|
1810
|
+
const parsed = fromBase64Json(headerValue);
|
|
1811
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1812
|
+
const ext = parsed.extensions;
|
|
1813
|
+
const block = _optionalChain([ext, 'optionalAccess', _12 => _12[EXT_OFFER_RECEIPT]]);
|
|
1814
|
+
const info = _optionalChain([block, 'optionalAccess', _13 => _13.info]);
|
|
1815
|
+
if (!info) return null;
|
|
1816
|
+
const settlement = isValidReceipt(info.settlement) ? info.settlement : isValidReceipt(info.receipt) ? info.receipt : void 0;
|
|
1817
|
+
if (!settlement) return null;
|
|
1818
|
+
const attestation = info.receipt && info.receipt !== settlement ? info.receipt : void 0;
|
|
1819
|
+
const res = info.resource;
|
|
1820
|
+
return {
|
|
1821
|
+
piprail: "1",
|
|
1822
|
+
receipt: settlement,
|
|
1823
|
+
resource: { url: res && typeof res.url === "string" ? res.url : "" },
|
|
1824
|
+
...typeof info.decimals === "number" ? { decimals: info.decimals } : {},
|
|
1825
|
+
...attestation ? { attestation } : {}
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
function parseSettleResponse(response) {
|
|
1829
|
+
const headerValue = _nullishCoalesce(response.headers.get(HEADER_RESPONSE), () => ( response.headers.get(HEADER_RESPONSE_V1)));
|
|
1830
|
+
if (!headerValue) return null;
|
|
1831
|
+
const parsed = fromBase64Json(headerValue);
|
|
1832
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.success !== "boolean") return null;
|
|
1833
|
+
return {
|
|
1834
|
+
success: parsed.success,
|
|
1835
|
+
...typeof parsed.transaction === "string" ? { transaction: parsed.transaction } : {},
|
|
1836
|
+
...typeof parsed.network === "string" ? { network: parsed.network } : {},
|
|
1837
|
+
...typeof parsed.payer === "string" ? { payer: parsed.payer } : {},
|
|
1838
|
+
...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {},
|
|
1839
|
+
// The upto SettleResponse's REQUIRED `amount` (actual settled atomic units) — string
|
|
1840
|
+
// passthrough so the upto buyer records ACTUAL, not MAX. Absent on onchain-proof/exact.
|
|
1841
|
+
...typeof parsed.amount === "string" ? { amount: parsed.amount } : {}
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
function parseSignatureObject(parsed) {
|
|
1845
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1846
|
+
const v = parsed;
|
|
1847
|
+
const accepted = v.accepted;
|
|
1848
|
+
const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _14 => _14.scheme]), () => ( v.scheme));
|
|
1849
|
+
if (scheme !== "onchain-proof") return null;
|
|
1850
|
+
const payload = v.payload;
|
|
1851
|
+
if (!payload || typeof payload.txHash !== "string" || typeof payload.nonce !== "string") {
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
return parsed;
|
|
1855
|
+
}
|
|
1856
|
+
function parseSignatureHeader(value) {
|
|
1857
|
+
return parseSignatureObject(fromBase64Json(value));
|
|
1858
|
+
}
|
|
1859
|
+
function parseExactPaymentHeader(value) {
|
|
1860
|
+
return parseExactObject(fromBase64Json(value));
|
|
1861
|
+
}
|
|
1862
|
+
function parseExactObject(parsed) {
|
|
1863
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1864
|
+
const v = parsed;
|
|
1865
|
+
const accepted = _nullishCoalesce(v.accepted, () => ( null));
|
|
1866
|
+
const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _15 => _15.scheme]), () => ( v.scheme));
|
|
1867
|
+
if (scheme !== "exact") return null;
|
|
1868
|
+
const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _16 => _16.network]), () => ( v.network));
|
|
1869
|
+
if (typeof network !== "string") return null;
|
|
1870
|
+
const payload = v.payload;
|
|
1871
|
+
if (!payload || typeof payload !== "object") return null;
|
|
1872
|
+
const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
|
|
1873
|
+
const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
|
|
1874
|
+
const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
|
|
1875
|
+
if (typeof payload.signedDelegateAction === "string") {
|
|
1876
|
+
return { ...base2, method: "near", payload: { signedDelegateAction: payload.signedDelegateAction } };
|
|
1877
|
+
}
|
|
1878
|
+
if (typeof payload.transaction === "string" && typeof payload.senderAuth === "string") {
|
|
1879
|
+
return { ...base2, method: "aptos", payload: { transaction: payload.transaction, senderAuth: payload.senderAuth } };
|
|
1880
|
+
}
|
|
1881
|
+
if (typeof payload.transaction === "string") {
|
|
1882
|
+
return { ...base2, method: "svm", payload: { transaction: payload.transaction } };
|
|
1883
|
+
}
|
|
1884
|
+
if (Array.isArray(payload.paymentGroup)) {
|
|
1885
|
+
const group = payload.paymentGroup;
|
|
1886
|
+
const index = payload.paymentIndex;
|
|
1887
|
+
if (group.length === 0 || group.length > 16 || !group.every((t) => typeof t === "string") || typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= group.length) {
|
|
1888
|
+
return null;
|
|
1889
|
+
}
|
|
1890
|
+
return {
|
|
1891
|
+
...base2,
|
|
1892
|
+
method: "algorand",
|
|
1893
|
+
payload: { paymentIndex: index, paymentGroup: group }
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
const signature = payload.signature;
|
|
1494
1897
|
if (typeof signature !== "string") return null;
|
|
1495
1898
|
const authorization = payload.authorization;
|
|
1496
1899
|
if (authorization && typeof authorization === "object") {
|
|
@@ -1521,6 +1924,42 @@ function parseExactPaymentHeader(value) {
|
|
|
1521
1924
|
}
|
|
1522
1925
|
return null;
|
|
1523
1926
|
}
|
|
1927
|
+
function parseUptoPaymentHeader(value) {
|
|
1928
|
+
return parseUptoObject(fromBase64Json(value));
|
|
1929
|
+
}
|
|
1930
|
+
function parseUptoObject(parsed) {
|
|
1931
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1932
|
+
const v = parsed;
|
|
1933
|
+
const accepted = _nullishCoalesce(v.accepted, () => ( null));
|
|
1934
|
+
const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _17 => _17.scheme]), () => ( v.scheme));
|
|
1935
|
+
if (scheme !== "upto") return null;
|
|
1936
|
+
const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _18 => _18.network]), () => ( v.network));
|
|
1937
|
+
if (typeof network !== "string") return null;
|
|
1938
|
+
const payload = v.payload;
|
|
1939
|
+
if (!payload || typeof payload !== "object") return null;
|
|
1940
|
+
const signature = payload.signature;
|
|
1941
|
+
if (typeof signature !== "string") return null;
|
|
1942
|
+
const p2 = payload.permit2Authorization;
|
|
1943
|
+
if (!p2 || typeof p2 !== "object") return null;
|
|
1944
|
+
const permitted = p2.permitted;
|
|
1945
|
+
const witness = p2.witness;
|
|
1946
|
+
if (!permitted || typeof permitted !== "object" || !witness || typeof witness !== "object") return null;
|
|
1947
|
+
if (typeof permitted.token !== "string" || typeof permitted.amount !== "string") return null;
|
|
1948
|
+
if (typeof witness.to !== "string" || typeof witness.facilitator !== "string" || typeof witness.validAfter !== "string") {
|
|
1949
|
+
return null;
|
|
1950
|
+
}
|
|
1951
|
+
for (const k of ["from", "spender", "nonce", "deadline"]) {
|
|
1952
|
+
if (typeof p2[k] !== "string") return null;
|
|
1953
|
+
}
|
|
1954
|
+
const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
|
|
1955
|
+
const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
|
|
1956
|
+
const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
|
|
1957
|
+
return {
|
|
1958
|
+
...base2,
|
|
1959
|
+
method: "permit2-upto",
|
|
1960
|
+
payload: { signature, permit2Authorization: p2 }
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1524
1963
|
function isValidChallenge(value) {
|
|
1525
1964
|
if (!value || typeof value !== "object") return false;
|
|
1526
1965
|
const v = value;
|
|
@@ -1533,7 +1972,7 @@ function isValidChallenge(value) {
|
|
|
1533
1972
|
function isValidReceipt(value) {
|
|
1534
1973
|
if (!value || typeof value !== "object") return false;
|
|
1535
1974
|
const v = value;
|
|
1536
|
-
if (v.scheme !== "onchain-proof" && v.scheme !== "exact") return false;
|
|
1975
|
+
if (v.scheme !== "onchain-proof" && v.scheme !== "exact" && v.scheme !== "upto") return false;
|
|
1537
1976
|
if (typeof v.transaction !== "string" && typeof v.txHash !== "string") return false;
|
|
1538
1977
|
if (typeof v.payer !== "string") return false;
|
|
1539
1978
|
return true;
|
|
@@ -1556,7 +1995,7 @@ var evmDriver = {
|
|
|
1556
1995
|
let resolved;
|
|
1557
1996
|
try {
|
|
1558
1997
|
resolved = resolveChain(opts.chain, opts.rpcUrl);
|
|
1559
|
-
} catch (
|
|
1998
|
+
} catch (e21) {
|
|
1560
1999
|
return null;
|
|
1561
2000
|
}
|
|
1562
2001
|
return makeEvmNetwork(resolved);
|
|
@@ -1584,15 +2023,15 @@ function makeEvmNetwork(resolved) {
|
|
|
1584
2023
|
const info = resolved.tokens[token.toUpperCase()];
|
|
1585
2024
|
if (!info) {
|
|
1586
2025
|
const known = Object.keys(resolved.tokens).join(", ") || "(none built in)";
|
|
1587
|
-
throw new (0,
|
|
2026
|
+
throw new (0, _chunkMWBT7MCEcjs.UnknownTokenError)(
|
|
1588
2027
|
`token "${token}" isn't built in for ${resolved.chain.name} (known: ${known}). Pass { address, decimals } instead, or use 'native'.`
|
|
1589
2028
|
);
|
|
1590
2029
|
}
|
|
1591
2030
|
return { asset: info.address, decimals: info.decimals, symbol: info.symbol };
|
|
1592
2031
|
}
|
|
1593
|
-
|
|
2032
|
+
_chunkMWBT7MCEcjs.rejectForeignToken.call(void 0, token, "evm", network);
|
|
1594
2033
|
if (!("address" in token)) {
|
|
1595
|
-
throw new (0,
|
|
2034
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1596
2035
|
`chain ${network} is EVM; a custom token must be { address, decimals }.`
|
|
1597
2036
|
);
|
|
1598
2037
|
}
|
|
@@ -1612,7 +2051,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1612
2051
|
let normalized;
|
|
1613
2052
|
try {
|
|
1614
2053
|
normalized = _viem.getAddress.call(void 0, asset);
|
|
1615
|
-
} catch (
|
|
2054
|
+
} catch (e22) {
|
|
1616
2055
|
return null;
|
|
1617
2056
|
}
|
|
1618
2057
|
for (const info of Object.values(resolved.tokens)) {
|
|
@@ -1624,20 +2063,20 @@ function makeEvmNetwork(resolved) {
|
|
|
1624
2063
|
},
|
|
1625
2064
|
assertValidPayTo(payTo) {
|
|
1626
2065
|
if (!_viem.isAddress.call(void 0, payTo)) {
|
|
1627
|
-
throw new (0,
|
|
2066
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1628
2067
|
`chain ${network} is EVM, but payTo "${payTo}" is not a valid 0x address.`
|
|
1629
2068
|
);
|
|
1630
2069
|
}
|
|
1631
2070
|
},
|
|
1632
2071
|
bindWallet(wallet) {
|
|
1633
2072
|
if (typeof wallet !== "object" || wallet === null) {
|
|
1634
|
-
throw new (0,
|
|
2073
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1635
2074
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1636
2075
|
);
|
|
1637
2076
|
}
|
|
1638
|
-
|
|
2077
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, wallet, "EVM");
|
|
1639
2078
|
if (!("key" in wallet) && !("walletClient" in wallet)) {
|
|
1640
|
-
throw new (0,
|
|
2079
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1641
2080
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1642
2081
|
);
|
|
1643
2082
|
}
|
|
@@ -1654,12 +2093,12 @@ function makeEvmNetwork(resolved) {
|
|
|
1654
2093
|
});
|
|
1655
2094
|
} catch (err) {
|
|
1656
2095
|
if (isViemInsufficientFunds(err)) {
|
|
1657
|
-
throw new (0,
|
|
2096
|
+
throw new (0, _chunkMWBT7MCEcjs.InsufficientFundsError)(
|
|
1658
2097
|
err instanceof Error ? err.message : "Insufficient funds for payment.",
|
|
1659
2098
|
{ cause: err }
|
|
1660
2099
|
);
|
|
1661
2100
|
}
|
|
1662
|
-
throw _nullishCoalesce(
|
|
2101
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
|
|
1663
2102
|
}
|
|
1664
2103
|
},
|
|
1665
2104
|
async confirm(ref, minConfirmations) {
|
|
@@ -1670,7 +2109,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1670
2109
|
});
|
|
1671
2110
|
return { height: receipt.blockNumber.toString() };
|
|
1672
2111
|
} catch (err) {
|
|
1673
|
-
throw new (0,
|
|
2112
|
+
throw new (0, _chunkMWBT7MCEcjs.ConfirmationTimeoutError)(
|
|
1674
2113
|
`EVM tx ${ref} did not reach ${minConfirmations} confirmation(s) in time.`,
|
|
1675
2114
|
{ cause: err }
|
|
1676
2115
|
);
|
|
@@ -1680,7 +2119,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1680
2119
|
const { decimals, symbol } = resolved.chain.nativeCurrency;
|
|
1681
2120
|
if (accept.scheme === "exact") {
|
|
1682
2121
|
const permit2 = accept.extra.assetTransferMethod === "permit2";
|
|
1683
|
-
return
|
|
2122
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1684
2123
|
symbol,
|
|
1685
2124
|
decimals,
|
|
1686
2125
|
fee: 0n,
|
|
@@ -1691,16 +2130,16 @@ function makeEvmNetwork(resolved) {
|
|
|
1691
2130
|
const gasLimit = accept.asset === "native" ? 21000n : 65000n;
|
|
1692
2131
|
try {
|
|
1693
2132
|
const gasPrice = await publicClient.getGasPrice();
|
|
1694
|
-
return
|
|
2133
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1695
2134
|
symbol,
|
|
1696
2135
|
decimals,
|
|
1697
2136
|
fee: gasPrice * gasLimit,
|
|
1698
2137
|
basis: "estimated",
|
|
1699
2138
|
detail: `~${gasLimit} gas @ ${gasPrice} wei/gas`
|
|
1700
2139
|
});
|
|
1701
|
-
} catch (
|
|
2140
|
+
} catch (e23) {
|
|
1702
2141
|
const gasPrice = 5000000000n;
|
|
1703
|
-
return
|
|
2142
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1704
2143
|
symbol,
|
|
1705
2144
|
decimals,
|
|
1706
2145
|
fee: gasPrice * gasLimit,
|
|
@@ -1721,7 +2160,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1721
2160
|
functionName: "balanceOf",
|
|
1722
2161
|
args: [owner]
|
|
1723
2162
|
});
|
|
1724
|
-
} catch (
|
|
2163
|
+
} catch (e24) {
|
|
1725
2164
|
token = null;
|
|
1726
2165
|
}
|
|
1727
2166
|
return { token, native };
|
|
@@ -1741,6 +2180,12 @@ function makeEvmNetwork(resolved) {
|
|
|
1741
2180
|
signMessage: (message) => a.walletClient.signMessage({ account: a.account, message })
|
|
1742
2181
|
};
|
|
1743
2182
|
},
|
|
2183
|
+
// Tier-2 service-delivery attestation (EVM-only) — sign the official offer-receipt
|
|
2184
|
+
// EIP-712 RECEIPT_TYPES with the bound (payTo) wallet. Chain-independent (domain
|
|
2185
|
+
// chainId is hardcoded 1); viem lives in ./receipt.ts (a lazy chunk).
|
|
2186
|
+
signReceipt(wallet, input) {
|
|
2187
|
+
return _chunkCQQI5IJXcjs.signReceiptEvm.call(void 0, wallet, input);
|
|
2188
|
+
},
|
|
1744
2189
|
async verify(ref, accept) {
|
|
1745
2190
|
return verifyEvm({
|
|
1746
2191
|
publicClient,
|
|
@@ -1824,6 +2269,43 @@ function makeEvmNetwork(resolved) {
|
|
|
1824
2269
|
payload,
|
|
1825
2270
|
accept
|
|
1826
2271
|
});
|
|
2272
|
+
},
|
|
2273
|
+
// Standard x402 `upto` (metered) rail — EVM-Permit2 ONLY. The metered sibling of the
|
|
2274
|
+
// exact trio. resolveUptoRail advertises (Permit2-only, proxy-gated, relayer = facilitator);
|
|
2275
|
+
// payUpto signs the MAX with witness.facilitator bound; settleUptoSelf self-settles the actual.
|
|
2276
|
+
async resolveUptoRail({ asset, relayer }) {
|
|
2277
|
+
const relayerAddress = relayer._native.account.address;
|
|
2278
|
+
const domain = asset === "native" ? null : await readExactDomain(publicClient, asset).catch(() => null);
|
|
2279
|
+
return resolveUptoRailEvm({
|
|
2280
|
+
asset,
|
|
2281
|
+
relayerAddress,
|
|
2282
|
+
proxySupported: () => isUptoProxyChain(resolved.chainId),
|
|
2283
|
+
domain
|
|
2284
|
+
});
|
|
2285
|
+
},
|
|
2286
|
+
async payUpto(wallet, accept) {
|
|
2287
|
+
const a = wallet._native;
|
|
2288
|
+
const { payload, payerFrom, nonce } = await payUptoEvm({
|
|
2289
|
+
publicClient,
|
|
2290
|
+
walletClient: a.walletClient,
|
|
2291
|
+
account: a.account,
|
|
2292
|
+
chainId: resolved.chainId,
|
|
2293
|
+
chain: resolved.chain,
|
|
2294
|
+
accept
|
|
2295
|
+
});
|
|
2296
|
+
return { payload, accepted: accept, payerFrom, nonce };
|
|
2297
|
+
},
|
|
2298
|
+
async settleUptoSelf({ relayer, payload, accept, settleAmount }) {
|
|
2299
|
+
const a = relayer._native;
|
|
2300
|
+
return verifyAndSettleUptoEvm({
|
|
2301
|
+
publicClient,
|
|
2302
|
+
walletClient: a.walletClient,
|
|
2303
|
+
account: a.account,
|
|
2304
|
+
chain: resolved.chain,
|
|
2305
|
+
payload,
|
|
2306
|
+
accept,
|
|
2307
|
+
settleAmount
|
|
2308
|
+
});
|
|
1827
2309
|
}
|
|
1828
2310
|
};
|
|
1829
2311
|
}
|
|
@@ -1840,9 +2322,9 @@ var loaders = {
|
|
|
1840
2322
|
solana: async () => {
|
|
1841
2323
|
let mod;
|
|
1842
2324
|
try {
|
|
1843
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-
|
|
2325
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-MYF4HBO4.cjs")));
|
|
1844
2326
|
} catch (cause) {
|
|
1845
|
-
throw new (0,
|
|
2327
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1846
2328
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
1847
2329
|
{ cause }
|
|
1848
2330
|
);
|
|
@@ -1852,9 +2334,9 @@ var loaders = {
|
|
|
1852
2334
|
ton: async () => {
|
|
1853
2335
|
let mod;
|
|
1854
2336
|
try {
|
|
1855
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-
|
|
2337
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-AOR3EURW.cjs")));
|
|
1856
2338
|
} catch (cause) {
|
|
1857
|
-
throw new (0,
|
|
2339
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1858
2340
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
1859
2341
|
{ cause }
|
|
1860
2342
|
);
|
|
@@ -1864,9 +2346,9 @@ var loaders = {
|
|
|
1864
2346
|
stellar: async () => {
|
|
1865
2347
|
let mod;
|
|
1866
2348
|
try {
|
|
1867
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-
|
|
2349
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-CRWBSX4E.cjs")));
|
|
1868
2350
|
} catch (cause) {
|
|
1869
|
-
throw new (0,
|
|
2351
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1870
2352
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
1871
2353
|
{ cause }
|
|
1872
2354
|
);
|
|
@@ -1876,9 +2358,9 @@ var loaders = {
|
|
|
1876
2358
|
xrpl: async () => {
|
|
1877
2359
|
let mod;
|
|
1878
2360
|
try {
|
|
1879
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-
|
|
2361
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-Y6SQNYLC.cjs")));
|
|
1880
2362
|
} catch (cause) {
|
|
1881
|
-
throw new (0,
|
|
2363
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1882
2364
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
1883
2365
|
{ cause }
|
|
1884
2366
|
);
|
|
@@ -1888,9 +2370,9 @@ var loaders = {
|
|
|
1888
2370
|
tron: async () => {
|
|
1889
2371
|
let mod;
|
|
1890
2372
|
try {
|
|
1891
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-
|
|
2373
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-OMXB6EW2.cjs")));
|
|
1892
2374
|
} catch (cause) {
|
|
1893
|
-
throw new (0,
|
|
2375
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1894
2376
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
1895
2377
|
{ cause }
|
|
1896
2378
|
);
|
|
@@ -1900,9 +2382,9 @@ var loaders = {
|
|
|
1900
2382
|
sui: async () => {
|
|
1901
2383
|
let mod;
|
|
1902
2384
|
try {
|
|
1903
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-
|
|
2385
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-Y4RLKKE2.cjs")));
|
|
1904
2386
|
} catch (cause) {
|
|
1905
|
-
throw new (0,
|
|
2387
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1906
2388
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
1907
2389
|
{ cause }
|
|
1908
2390
|
);
|
|
@@ -1912,9 +2394,9 @@ var loaders = {
|
|
|
1912
2394
|
near: async () => {
|
|
1913
2395
|
let mod;
|
|
1914
2396
|
try {
|
|
1915
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-
|
|
2397
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-6KAQVVG2.cjs")));
|
|
1916
2398
|
} catch (cause) {
|
|
1917
|
-
throw new (0,
|
|
2399
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1918
2400
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
1919
2401
|
{ cause }
|
|
1920
2402
|
);
|
|
@@ -1924,9 +2406,9 @@ var loaders = {
|
|
|
1924
2406
|
aptos: async () => {
|
|
1925
2407
|
let mod;
|
|
1926
2408
|
try {
|
|
1927
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-
|
|
2409
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-GHJPO6JJ.cjs")));
|
|
1928
2410
|
} catch (cause) {
|
|
1929
|
-
throw new (0,
|
|
2411
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1930
2412
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
1931
2413
|
{ cause }
|
|
1932
2414
|
);
|
|
@@ -1936,9 +2418,9 @@ var loaders = {
|
|
|
1936
2418
|
algorand: async () => {
|
|
1937
2419
|
let mod;
|
|
1938
2420
|
try {
|
|
1939
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-
|
|
2421
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-FCEECDG6.cjs")));
|
|
1940
2422
|
} catch (cause) {
|
|
1941
|
-
throw new (0,
|
|
2423
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1942
2424
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
1943
2425
|
{ cause }
|
|
1944
2426
|
);
|
|
@@ -2105,7 +2587,7 @@ function fieldTokens(r) {
|
|
|
2105
2587
|
try {
|
|
2106
2588
|
const u = new URL(r.resource);
|
|
2107
2589
|
pathText = `${u.hostname} ${u.pathname}`;
|
|
2108
|
-
} catch (
|
|
2590
|
+
} catch (e25) {
|
|
2109
2591
|
}
|
|
2110
2592
|
return [
|
|
2111
2593
|
[tokenize(r.name), FIELD_WEIGHTS.name],
|
|
@@ -2160,7 +2642,7 @@ function sortResources(items, sort, order) {
|
|
|
2160
2642
|
async function safeSearch(run) {
|
|
2161
2643
|
try {
|
|
2162
2644
|
return await run();
|
|
2163
|
-
} catch (
|
|
2645
|
+
} catch (e26) {
|
|
2164
2646
|
return [];
|
|
2165
2647
|
}
|
|
2166
2648
|
}
|
|
@@ -2299,7 +2781,7 @@ async function register402Index(input) {
|
|
|
2299
2781
|
if (res.ok) {
|
|
2300
2782
|
const body = await res.json().catch(() => ({}));
|
|
2301
2783
|
const msg = typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
|
|
2302
|
-
const live = _optionalChain([body, 'access',
|
|
2784
|
+
const live = _optionalChain([body, 'access', _19 => _19.service, 'optionalAccess', _20 => _20.status]) === "active";
|
|
2303
2785
|
return {
|
|
2304
2786
|
source: "402index",
|
|
2305
2787
|
ok: true,
|
|
@@ -2326,7 +2808,7 @@ async function readIndexError(res) {
|
|
|
2326
2808
|
(p) => typeof p === "string" && p.length > 0
|
|
2327
2809
|
);
|
|
2328
2810
|
return parts.length ? [...new Set(parts)].join(" \u2014 ") : void 0;
|
|
2329
|
-
} catch (
|
|
2811
|
+
} catch (e27) {
|
|
2330
2812
|
return void 0;
|
|
2331
2813
|
}
|
|
2332
2814
|
}
|
|
@@ -2436,11 +2918,11 @@ async function readSiwxInfo(res) {
|
|
|
2436
2918
|
try {
|
|
2437
2919
|
const body = await res.json();
|
|
2438
2920
|
const ext = body.extensions;
|
|
2439
|
-
const siwx = _optionalChain([ext, 'optionalAccess',
|
|
2440
|
-
const info = _nullishCoalesce(_optionalChain([siwx, 'optionalAccess',
|
|
2441
|
-
if (info && info.chainId == null && Array.isArray(_optionalChain([siwx, 'optionalAccess',
|
|
2921
|
+
const siwx = _optionalChain([ext, 'optionalAccess', _21 => _21["sign-in-with-x"]]);
|
|
2922
|
+
const info = _nullishCoalesce(_optionalChain([siwx, 'optionalAccess', _22 => _22.info]), () => ( siwx));
|
|
2923
|
+
if (info && info.chainId == null && Array.isArray(_optionalChain([siwx, 'optionalAccess', _23 => _23.supportedChains]))) {
|
|
2442
2924
|
const evm = siwx.supportedChains.find(
|
|
2443
|
-
(c) => typeof _optionalChain([c, 'optionalAccess',
|
|
2925
|
+
(c) => typeof _optionalChain([c, 'optionalAccess', _24 => _24.chainId]) === "string" && c.chainId.startsWith("eip155:")
|
|
2444
2926
|
);
|
|
2445
2927
|
if (evm && typeof evm.chainId === "string") info.chainId = evm.chainId;
|
|
2446
2928
|
}
|
|
@@ -2448,7 +2930,7 @@ async function readSiwxInfo(res) {
|
|
|
2448
2930
|
return info;
|
|
2449
2931
|
}
|
|
2450
2932
|
return null;
|
|
2451
|
-
} catch (
|
|
2933
|
+
} catch (e28) {
|
|
2452
2934
|
return null;
|
|
2453
2935
|
}
|
|
2454
2936
|
}
|
|
@@ -2545,7 +3027,7 @@ function hostOf(url) {
|
|
|
2545
3027
|
try {
|
|
2546
3028
|
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(url) ? url : `https://${url}`;
|
|
2547
3029
|
return new URL(withScheme).hostname || url;
|
|
2548
|
-
} catch (
|
|
3030
|
+
} catch (e29) {
|
|
2549
3031
|
return url;
|
|
2550
3032
|
}
|
|
2551
3033
|
}
|
|
@@ -2582,6 +3064,53 @@ function encodeBase642(str) {
|
|
|
2582
3064
|
}
|
|
2583
3065
|
|
|
2584
3066
|
// src/policy.ts
|
|
3067
|
+
var DENOM_PRECISION = _chunkMWBT7MCEcjs.MAX_DECIMALS;
|
|
3068
|
+
var BUILTIN_DENOMS = {
|
|
3069
|
+
USDC: "USD",
|
|
3070
|
+
USDT: "USD",
|
|
3071
|
+
USD1: "USD",
|
|
3072
|
+
FDUSD: "USD",
|
|
3073
|
+
RLUSD: "USD",
|
|
3074
|
+
EURC: "EUR"
|
|
3075
|
+
};
|
|
3076
|
+
function denomOf(symbol, asset, policy) {
|
|
3077
|
+
if (asset === "native") return void 0;
|
|
3078
|
+
const norm = (v) => typeof v === "string" && v.trim() !== "" ? v.trim().toUpperCase() : void 0;
|
|
3079
|
+
const override = _optionalChain([policy, 'optionalAccess', _25 => _25.denomFor]);
|
|
3080
|
+
if (override && typeof override === "object") {
|
|
3081
|
+
if (asset && asset in override) {
|
|
3082
|
+
const d = norm(override[asset]);
|
|
3083
|
+
if (d) return d;
|
|
3084
|
+
}
|
|
3085
|
+
if (symbol) {
|
|
3086
|
+
const hit = Object.entries(override).find(([k]) => k.trim().toUpperCase() === symbol.trim().toUpperCase());
|
|
3087
|
+
if (hit) {
|
|
3088
|
+
const d = norm(hit[1]);
|
|
3089
|
+
if (d) return d;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
if (symbol) {
|
|
3094
|
+
const builtin = BUILTIN_DENOMS[symbol.trim().toUpperCase()];
|
|
3095
|
+
if (builtin) return builtin;
|
|
3096
|
+
}
|
|
3097
|
+
return void 0;
|
|
3098
|
+
}
|
|
3099
|
+
function scaleToDenom(amountBase, decimals) {
|
|
3100
|
+
if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > DENOM_PRECISION) return null;
|
|
3101
|
+
return amountBase * 10n ** BigInt(DENOM_PRECISION - decimals);
|
|
3102
|
+
}
|
|
3103
|
+
function capForDenom(caps, denom) {
|
|
3104
|
+
if (!caps || typeof caps !== "object") return void 0;
|
|
3105
|
+
const target = denom.trim().toUpperCase();
|
|
3106
|
+
let best;
|
|
3107
|
+
for (const [k, v] of Object.entries(caps)) {
|
|
3108
|
+
if (typeof k !== "string" || typeof v !== "string") continue;
|
|
3109
|
+
if (k.trim().toUpperCase() !== target) continue;
|
|
3110
|
+
if (best === void 0 || Number(v) < Number(best)) best = v;
|
|
3111
|
+
}
|
|
3112
|
+
return best;
|
|
3113
|
+
}
|
|
2585
3114
|
var ALLOW = { allowed: true };
|
|
2586
3115
|
var deny = (code, reason) => ({
|
|
2587
3116
|
allowed: false,
|
|
@@ -2648,7 +3177,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2648
3177
|
}
|
|
2649
3178
|
}
|
|
2650
3179
|
if (policy.maxAmount !== void 0) {
|
|
2651
|
-
const cap =
|
|
3180
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
|
|
2652
3181
|
if (intent.amountBase > cap) {
|
|
2653
3182
|
return deny(
|
|
2654
3183
|
"MAX_AMOUNT",
|
|
@@ -2657,7 +3186,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2657
3186
|
}
|
|
2658
3187
|
}
|
|
2659
3188
|
if (policy.maxTotal !== void 0) {
|
|
2660
|
-
const cap =
|
|
3189
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
|
|
2661
3190
|
if (spentForAssetBase + intent.amountBase > cap) {
|
|
2662
3191
|
return deny(
|
|
2663
3192
|
"MAX_TOTAL",
|
|
@@ -2665,8 +3194,36 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2665
3194
|
);
|
|
2666
3195
|
}
|
|
2667
3196
|
}
|
|
3197
|
+
if (ctx && policy.maxTotalPerDenom) {
|
|
3198
|
+
const denom = denomOf(intent.symbol, intent.asset, policy);
|
|
3199
|
+
const capStr = denom ? capForDenom(policy.maxTotalPerDenom, denom) : void 0;
|
|
3200
|
+
if (denom && capStr !== void 0) {
|
|
3201
|
+
const thisScaled = scaleToDenom(intent.amountBase, intent.decimals);
|
|
3202
|
+
if (thisScaled === null) {
|
|
3203
|
+
return deny(
|
|
3204
|
+
"MAX_TOTAL_DENOM",
|
|
3205
|
+
`token for ${denom} has more than ${DENOM_PRECISION} decimals \u2014 refusing to count it toward policy.maxTotalPerDenom.${denom} on trust (it can't be summed safely).`
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
const capScaled = _chunkMWBT7MCEcjs.floorUnits.call(void 0, capStr, DENOM_PRECISION);
|
|
3209
|
+
if ((_nullishCoalesce(ctx.spentInDenomScaled, () => ( 0n))) + thisScaled > capScaled) {
|
|
3210
|
+
return deny(
|
|
3211
|
+
"MAX_TOTAL_DENOM",
|
|
3212
|
+
`this payment would push total ${denom} spend past policy.maxTotalPerDenom.${denom} (${capStr}) \u2014 summed across every ${denom} token and chain.`
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
if (ctx && policy.maxPayments !== void 0) {
|
|
3218
|
+
if ((_nullishCoalesce(ctx.paymentCount, () => ( 0))) + 1 > policy.maxPayments) {
|
|
3219
|
+
return deny(
|
|
3220
|
+
"MAX_PAYMENTS",
|
|
3221
|
+
`this payment would exceed policy.maxPayments (${policy.maxPayments}); already settled ${_nullishCoalesce(ctx.paymentCount, () => ( 0))}.`
|
|
3222
|
+
);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
2668
3225
|
if (ctx && policy.windowTotal !== void 0 && policy.windowSeconds !== void 0) {
|
|
2669
|
-
const cap =
|
|
3226
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.windowTotal, intent.decimals);
|
|
2670
3227
|
if (ctx.spentInWindowBase + intent.amountBase > cap) {
|
|
2671
3228
|
return deny(
|
|
2672
3229
|
"WINDOW_TOTAL",
|
|
@@ -2674,44 +3231,150 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2674
3231
|
);
|
|
2675
3232
|
}
|
|
2676
3233
|
}
|
|
3234
|
+
if (ctx && policy.maxPaymentsPerWindow !== void 0 && policy.windowSeconds !== void 0) {
|
|
3235
|
+
if ((_nullishCoalesce(ctx.paymentCountInWindow, () => ( 0))) + 1 > policy.maxPaymentsPerWindow) {
|
|
3236
|
+
return deny(
|
|
3237
|
+
"WINDOW_COUNT",
|
|
3238
|
+
`this payment would exceed policy.maxPaymentsPerWindow (${policy.maxPaymentsPerWindow}) within the last ${policy.windowSeconds}s.`
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
2677
3242
|
return ALLOW;
|
|
2678
3243
|
}
|
|
2679
3244
|
|
|
2680
3245
|
// src/ledger.ts
|
|
2681
3246
|
var keyFor = (network, asset) => `${network}|${asset}`;
|
|
2682
|
-
var SpendLedger = (_class = class {
|
|
3247
|
+
var SpendLedger = (_class = class {
|
|
2683
3248
|
__init() {this.records = []}
|
|
2684
3249
|
__init2() {this.buckets = /* @__PURE__ */ new Map()}
|
|
3250
|
+
/** Per-denomination running total, scaled to {@link DENOM_PRECISION}. Keyed by the
|
|
3251
|
+
* UPPERCASE denomination so lookups are case-insensitive. */
|
|
3252
|
+
__init3() {this.denomTotals = /* @__PURE__ */ new Map()}
|
|
3253
|
+
/** Threshold keys already warned (`warnAtFraction` fires once per crossing per cap).
|
|
3254
|
+
* Lives on the LEDGER — not the client — so clients SHARING one (a cross-chain
|
|
3255
|
+
* MultiChainPayer) dedupe together: a denomination/count threshold fires once across the
|
|
3256
|
+
* whole shared budget, not once per chain. */
|
|
3257
|
+
__init4() {this.warned = /* @__PURE__ */ new Set()}
|
|
3258
|
+
|
|
2685
3259
|
/**
|
|
2686
3260
|
* Session clock origin (epoch-ms) — process/session start = ledger
|
|
2687
3261
|
* construction. In-memory; a new process is a new session. The client reads it
|
|
2688
3262
|
* to compute the `ttlSeconds` deadline and the rolling-window slice.
|
|
2689
3263
|
*/
|
|
2690
|
-
|
|
2691
|
-
/**
|
|
2692
|
-
*
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
3264
|
+
__init5() {this.sessionStart = Date.now()}
|
|
3265
|
+
/**
|
|
3266
|
+
* @param store Optional durable {@link SpendStore}. When supplied, the ledger
|
|
3267
|
+
* HYDRATES from `store.load()` here (so prior spend resumes after a restart) and
|
|
3268
|
+
* `append()`s every settled payment. A throwing/absent store fails SAFE to an
|
|
3269
|
+
* empty in-memory ledger — it never blocks construction (ERRORS.md: never throw).
|
|
3270
|
+
*/
|
|
3271
|
+
constructor(store) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);
|
|
3272
|
+
this.store = store;
|
|
3273
|
+
if (store) {
|
|
3274
|
+
let seed = [];
|
|
3275
|
+
try {
|
|
3276
|
+
seed = _nullishCoalesce(store.load(), () => ( []));
|
|
3277
|
+
} catch (e30) {
|
|
3278
|
+
seed = [];
|
|
3279
|
+
}
|
|
3280
|
+
for (const r of seed) this.ingest(r, _nullishCoalesce(_optionalChain([r, 'optionalAccess', _26 => _26.decimals]), () => ( 0)), _optionalChain([r, 'optionalAccess', _27 => _27.denom]));
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* A record is safe to tally iff its `amountBase` is a non-negative integer STRING and its
|
|
3285
|
+
* `decimals` is an integer in `[0, MAX_DECIMALS]`. The live `record()` path always passes
|
|
3286
|
+
* (the client validated the quote), but a hydrated record comes from an UNTRUSTED store
|
|
3287
|
+
* (a tampered/corrupt/future-version JSONL line), so we gate it here — a bad record is
|
|
3288
|
+
* dropped rather than allowed to throw `BigInt(...)`/`formatUnits(...)` later and brick a
|
|
3289
|
+
* read or the constructor.
|
|
3290
|
+
*/
|
|
3291
|
+
isTallyable(r, decimals) {
|
|
3292
|
+
return !!r && typeof r.amountBase === "string" && /^\d+$/.test(r.amountBase) && Number.isInteger(decimals) && decimals >= 0 && decimals <= _chunkMWBT7MCEcjs.MAX_DECIMALS;
|
|
3293
|
+
}
|
|
3294
|
+
/** Apply a record to the in-memory tallies (records + per-asset bucket + denom total).
|
|
3295
|
+
* Shared by {@link record} and constructor hydration; does NOT persist. Returns the
|
|
3296
|
+
* stored record, or `null` when the record is corrupt and was skipped. */
|
|
3297
|
+
ingest(r, decimals, denom) {
|
|
3298
|
+
if (!this.isTallyable(r, decimals)) return null;
|
|
3299
|
+
const denomStr = typeof denom === "string" && denom.trim() !== "" ? denom : void 0;
|
|
3300
|
+
const rec = { ...r, decimals, ...denomStr ? { denom: denomStr } : {} };
|
|
3301
|
+
this.records.push(rec);
|
|
3302
|
+
const key = keyFor(rec.network, rec.asset);
|
|
2696
3303
|
const bucket = this.buckets.get(key);
|
|
2697
3304
|
if (bucket) {
|
|
2698
|
-
bucket.total += BigInt(
|
|
3305
|
+
bucket.total += BigInt(rec.amountBase);
|
|
2699
3306
|
bucket.count += 1;
|
|
2700
|
-
if (!bucket.symbol &&
|
|
3307
|
+
if (!bucket.symbol && rec.symbol) bucket.symbol = rec.symbol;
|
|
2701
3308
|
} else {
|
|
2702
3309
|
this.buckets.set(key, {
|
|
2703
|
-
network:
|
|
2704
|
-
asset:
|
|
2705
|
-
symbol:
|
|
3310
|
+
network: rec.network,
|
|
3311
|
+
asset: rec.asset,
|
|
3312
|
+
symbol: rec.symbol,
|
|
2706
3313
|
decimals,
|
|
2707
|
-
total: BigInt(
|
|
3314
|
+
total: BigInt(rec.amountBase),
|
|
2708
3315
|
count: 1
|
|
2709
3316
|
});
|
|
2710
3317
|
}
|
|
3318
|
+
if (denomStr) {
|
|
3319
|
+
const scaled = scaleToDenom(BigInt(rec.amountBase), decimals);
|
|
3320
|
+
if (scaled !== null) {
|
|
3321
|
+
const k = denomStr.toUpperCase();
|
|
3322
|
+
this.denomTotals.set(k, (_nullishCoalesce(this.denomTotals.get(k), () => ( 0n))) + scaled);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
return rec;
|
|
3326
|
+
}
|
|
3327
|
+
/** Record a settled payment. `decimals` is the TRUE token decimals (for the
|
|
3328
|
+
* per-asset running total + the formatted summary). `denom` is the unit-of-account
|
|
3329
|
+
* the payment counts toward in the grand total (or omit for none). Persists to the
|
|
3330
|
+
* {@link SpendStore} when one is configured (a failed append never throws). */
|
|
3331
|
+
record(r, decimals, denom) {
|
|
3332
|
+
const rec = this.ingest(r, decimals, denom);
|
|
3333
|
+
if (rec && this.store) {
|
|
3334
|
+
try {
|
|
3335
|
+
this.store.append(rec);
|
|
3336
|
+
} catch (e31) {
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
2711
3339
|
}
|
|
2712
3340
|
/** Running total (base units) already spent on this (network, asset). */
|
|
2713
3341
|
totalFor(network, asset) {
|
|
2714
|
-
return _nullishCoalesce(_optionalChain([this, 'access',
|
|
3342
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _28 => _28.buckets, 'access', _29 => _29.get, 'call', _30 => _30(keyFor(network, asset)), 'optionalAccess', _31 => _31.total]), () => ( 0n));
|
|
3343
|
+
}
|
|
3344
|
+
/**
|
|
3345
|
+
* Running grand total for a DENOMINATION, scaled to {@link DENOM_PRECISION} (so
|
|
3346
|
+
* tokens of different decimals add up exactly). Summed across every token of that
|
|
3347
|
+
* denomination and every chain this ledger has seen. Powers `maxTotalPerDenom`.
|
|
3348
|
+
* `0n` for a denomination never spent on. Case-insensitive.
|
|
3349
|
+
*/
|
|
3350
|
+
totalForDenom(denom) {
|
|
3351
|
+
return _nullishCoalesce(this.denomTotals.get(denom.toUpperCase()), () => ( 0n));
|
|
3352
|
+
}
|
|
3353
|
+
/** Total number of settled payments (across every chain + token). Powers `maxPayments`. */
|
|
3354
|
+
count() {
|
|
3355
|
+
return this.records.length;
|
|
3356
|
+
}
|
|
3357
|
+
/** Mark a `warnAtFraction` threshold key as fired; returns `true` the FIRST time (so the
|
|
3358
|
+
* caller emits the `budget-threshold` event once) and `false` thereafter. Shared across
|
|
3359
|
+
* every client on this ledger, so a cross-chain threshold fires once for the whole budget. */
|
|
3360
|
+
markWarned(key) {
|
|
3361
|
+
if (this.warned.has(key)) return false;
|
|
3362
|
+
this.warned.add(key);
|
|
3363
|
+
return true;
|
|
3364
|
+
}
|
|
3365
|
+
/**
|
|
3366
|
+
* Number of settled payments whose `at` (ISO) is at or after `sinceMs` (epoch-ms),
|
|
3367
|
+
* across every chain + token. Backs the rolling payment-count cap
|
|
3368
|
+
* (`maxPaymentsPerWindow`, `sinceMs = now - windowSeconds*1000`). Linear scan —
|
|
3369
|
+
* negligible at agent-session cardinality and only when a window count cap is set.
|
|
3370
|
+
*/
|
|
3371
|
+
countSince(sinceMs) {
|
|
3372
|
+
let n = 0;
|
|
3373
|
+
for (const r of this.records) {
|
|
3374
|
+
const t = Date.parse(r.at);
|
|
3375
|
+
if (Number.isNaN(t) || t >= sinceMs) n += 1;
|
|
3376
|
+
}
|
|
3377
|
+
return n;
|
|
2715
3378
|
}
|
|
2716
3379
|
/**
|
|
2717
3380
|
* Sum of base-unit amounts for (network, asset) whose record `at` (ISO
|
|
@@ -2723,9 +3386,9 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2723
3386
|
totalSince(network, asset, sinceMs) {
|
|
2724
3387
|
let sum = 0n;
|
|
2725
3388
|
for (const r of this.records) {
|
|
2726
|
-
if (r.network
|
|
2727
|
-
|
|
2728
|
-
|
|
3389
|
+
if (r.network !== network || r.asset !== asset) continue;
|
|
3390
|
+
const t = Date.parse(r.at);
|
|
3391
|
+
if (Number.isNaN(t) || t >= sinceMs) sum += BigInt(r.amountBase);
|
|
2729
3392
|
}
|
|
2730
3393
|
return sum;
|
|
2731
3394
|
}
|
|
@@ -2745,6 +3408,14 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2745
3408
|
totalBase: b.total
|
|
2746
3409
|
}));
|
|
2747
3410
|
}
|
|
3411
|
+
/**
|
|
3412
|
+
* The per-denomination grand totals, as read-only tuples — `denom` and the running
|
|
3413
|
+
* `totalScaled` (at {@link DENOM_PRECISION}). Lets the client compose the grand-total
|
|
3414
|
+
* budget view; the cap math lives in the client. A never-spent denomination is absent.
|
|
3415
|
+
*/
|
|
3416
|
+
denomBuckets() {
|
|
3417
|
+
return [...this.denomTotals.entries()].map(([denom, totalScaled]) => ({ denom, totalScaled }));
|
|
3418
|
+
}
|
|
2748
3419
|
/** An immutable snapshot of all spend so far. */
|
|
2749
3420
|
summary() {
|
|
2750
3421
|
return {
|
|
@@ -2755,9 +3426,15 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2755
3426
|
symbol: b.symbol,
|
|
2756
3427
|
decimals: b.decimals,
|
|
2757
3428
|
totalBase: b.total.toString(),
|
|
2758
|
-
totalFormatted:
|
|
3429
|
+
totalFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, b.total, b.decimals),
|
|
2759
3430
|
count: b.count
|
|
2760
3431
|
})),
|
|
3432
|
+
byDenom: [...this.denomTotals.entries()].map(([denom, scaled]) => ({
|
|
3433
|
+
denom,
|
|
3434
|
+
totalScaled: scaled.toString(),
|
|
3435
|
+
totalFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, scaled, DENOM_PRECISION),
|
|
3436
|
+
count: this.records.filter((r) => _optionalChain([r, 'access', _32 => _32.denom, 'optionalAccess', _33 => _33.toUpperCase, 'call', _34 => _34()]) === denom).length
|
|
3437
|
+
})),
|
|
2761
3438
|
records: [...this.records]
|
|
2762
3439
|
};
|
|
2763
3440
|
}
|
|
@@ -2771,24 +3448,41 @@ var RECIPIENT_FIX = {
|
|
|
2771
3448
|
NOT_OPTED_IN: "the recipient must opt into this asset once (a 0-amount self-transfer)",
|
|
2772
3449
|
INACTIVE: "the recipient account doesn't exist yet \u2014 fund it with the chain's base reserve to activate it"
|
|
2773
3450
|
};
|
|
3451
|
+
var RECEIPT_VERIFY_WINDOW_SECONDS = 100 * 365 * 24 * 60 * 60;
|
|
2774
3452
|
var PipRailClient = (_class2 = class {
|
|
2775
3453
|
|
|
2776
3454
|
|
|
2777
3455
|
|
|
2778
3456
|
|
|
2779
|
-
// Per-asset tally of everything this client has paid
|
|
2780
|
-
//
|
|
2781
|
-
|
|
3457
|
+
// Per-asset (+ per-denomination + count) tally of everything this client has paid —
|
|
3458
|
+
// powers spent()/budget() and the maxTotal/maxTotalPerDenom/maxPayments caps. Either
|
|
3459
|
+
// its own (optionally store-backed) ledger, or a SHARED one injected so the grand
|
|
3460
|
+
// total + count caps span several chains (MultiChainPayer.fromWallets).
|
|
3461
|
+
|
|
3462
|
+
// `warnAtFraction` threshold dedup lives on the LEDGER (`ledger.markWarned`), so clients
|
|
3463
|
+
// sharing one ledger (a cross-chain MultiChainPayer) fire each threshold once for the whole
|
|
3464
|
+
// shared budget — not once per chain.
|
|
2782
3465
|
// Resolved lazily on first request — this is what lets Solana (and future
|
|
2783
3466
|
// families) auto-mount with no setup call.
|
|
2784
3467
|
|
|
2785
|
-
|
|
3468
|
+
// The verifiable receipt from the most recent settled fetch (null if the server emitted
|
|
3469
|
+
// none). Captured pure (no chain read) and surfaced via lastReceipt(); the resource URL is
|
|
3470
|
+
// stamped from the URL this client actually fetched (authoritative over the gate's default).
|
|
3471
|
+
__init6() {this.lastReceiptValue = null}
|
|
3472
|
+
constructor(opts) {;_class2.prototype.__init6.call(this);
|
|
2786
3473
|
this.opts = opts;
|
|
2787
3474
|
this.maxRetries = Math.max(1, _nullishCoalesce(opts.maxPaymentRetries, () => ( 3)));
|
|
2788
3475
|
this.retryTimeoutMs = _nullishCoalesce(opts.retryTimeoutMs, () => ( 3e4));
|
|
2789
3476
|
this.onEvent = _nullishCoalesce(opts.onEvent, () => ( (() => void 0)));
|
|
3477
|
+
if (opts.ledger && opts.spendStore) {
|
|
3478
|
+
throw new TypeError(
|
|
3479
|
+
"Pass either `ledger` (a shared SpendLedger) or `spendStore`, not both \u2014 a shared ledger already owns its store."
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
this.ledger = _nullishCoalesce(opts.ledger, () => ( new SpendLedger(opts.spendStore)));
|
|
2790
3483
|
this.assertPolicyAmountCaps(opts.policy);
|
|
2791
3484
|
this.assertPolicyTimeOptions(opts.policy);
|
|
3485
|
+
this.assertPolicySpendControls(opts.policy);
|
|
2792
3486
|
}
|
|
2793
3487
|
/**
|
|
2794
3488
|
* Fail LOUDLY at construction on a malformed amount cap — a security boundary
|
|
@@ -2809,6 +3503,65 @@ var PipRailClient = (_class2 = class {
|
|
|
2809
3503
|
);
|
|
2810
3504
|
}
|
|
2811
3505
|
}
|
|
3506
|
+
if (policy.maxTotalPerDenom !== void 0) {
|
|
3507
|
+
const m = policy.maxTotalPerDenom;
|
|
3508
|
+
const proto = m && typeof m === "object" ? Object.getPrototypeOf(m) : false;
|
|
3509
|
+
if (!m || typeof m !== "object" || Array.isArray(m) || proto !== Object.prototype && proto !== null) {
|
|
3510
|
+
throw new TypeError(
|
|
3511
|
+
`policy.maxTotalPerDenom must be a plain { DENOM: amount } object (e.g. { USD: '20.00' }); got ${JSON.stringify(policy.maxTotalPerDenom)}.`
|
|
3512
|
+
);
|
|
3513
|
+
}
|
|
3514
|
+
for (const [denom, v] of Object.entries(policy.maxTotalPerDenom)) {
|
|
3515
|
+
if (denom.trim() === "") {
|
|
3516
|
+
throw new TypeError("policy.maxTotalPerDenom has a blank denomination key.");
|
|
3517
|
+
}
|
|
3518
|
+
if (typeof v !== "string" || !/^\d+(\.\d+)?$/.test(v)) {
|
|
3519
|
+
throw new TypeError(
|
|
3520
|
+
`policy.maxTotalPerDenom.${denom} must be a non-negative decimal string (e.g. '20.00'); got ${JSON.stringify(v)}.`
|
|
3521
|
+
);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
if (policy.denomFor !== void 0) {
|
|
3526
|
+
const m = policy.denomFor;
|
|
3527
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) {
|
|
3528
|
+
throw new TypeError(`policy.denomFor must be a { token: DENOM } object; got ${JSON.stringify(m)}.`);
|
|
3529
|
+
}
|
|
3530
|
+
for (const [k, v] of Object.entries(policy.denomFor)) {
|
|
3531
|
+
if (typeof v !== "string" || v.trim() === "") {
|
|
3532
|
+
throw new TypeError(`policy.denomFor.${k} must be a non-empty denomination string; got ${JSON.stringify(v)}.`);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
/**
|
|
3538
|
+
* Fail LOUDLY at construction on a misconfigured count / threshold control (a
|
|
3539
|
+
* programmer error → `TypeError`): payment counts must be positive safe integers,
|
|
3540
|
+
* `maxPaymentsPerWindow` needs `windowSeconds` (it shares the window), and
|
|
3541
|
+
* `warnAtFraction` must be in (0, 1].
|
|
3542
|
+
*/
|
|
3543
|
+
assertPolicySpendControls(policy) {
|
|
3544
|
+
if (!policy) return;
|
|
3545
|
+
for (const field of ["maxPayments", "maxPaymentsPerWindow"]) {
|
|
3546
|
+
const v = policy[field];
|
|
3547
|
+
if (v === void 0) continue;
|
|
3548
|
+
if (!Number.isSafeInteger(v) || v <= 0) {
|
|
3549
|
+
throw new TypeError(`policy.${field} must be a positive integer; got ${JSON.stringify(v)}.`);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
if (policy.maxPaymentsPerWindow !== void 0 && policy.windowSeconds === void 0) {
|
|
3553
|
+
throw new TypeError(
|
|
3554
|
+
"policy.maxPaymentsPerWindow needs policy.windowSeconds (the rolling window it counts within) \u2014 set both, or neither."
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
if (policy.warnAtFraction !== void 0) {
|
|
3558
|
+
const f = policy.warnAtFraction;
|
|
3559
|
+
if (typeof f !== "number" || !(f > 0 && f <= 1)) {
|
|
3560
|
+
throw new TypeError(
|
|
3561
|
+
`policy.warnAtFraction must be a number in (0, 1] (e.g. 0.8); got ${JSON.stringify(f)}.`
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
2812
3565
|
}
|
|
2813
3566
|
/**
|
|
2814
3567
|
* Fail LOUDLY at construction on a misconfigured time policy — a security
|
|
@@ -2823,11 +3576,17 @@ var PipRailClient = (_class2 = class {
|
|
|
2823
3576
|
if (!policy) return;
|
|
2824
3577
|
const hasWindowTotal = policy.windowTotal !== void 0;
|
|
2825
3578
|
const hasWindowSeconds = policy.windowSeconds !== void 0;
|
|
2826
|
-
|
|
3579
|
+
const hasWindowCount = policy.maxPaymentsPerWindow !== void 0;
|
|
3580
|
+
if (hasWindowTotal && !hasWindowSeconds) {
|
|
2827
3581
|
throw new TypeError(
|
|
2828
3582
|
"policy.windowTotal and policy.windowSeconds must be set together \u2014 a rolling-window cap can't be half-armed (set both, or neither)."
|
|
2829
3583
|
);
|
|
2830
3584
|
}
|
|
3585
|
+
if (hasWindowSeconds && !hasWindowTotal && !hasWindowCount) {
|
|
3586
|
+
throw new TypeError(
|
|
3587
|
+
"policy.windowSeconds must be set together with policy.windowTotal and/or policy.maxPaymentsPerWindow \u2014 a window width alone bounds nothing."
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
2831
3590
|
if (hasWindowSeconds && !(Number.isSafeInteger(policy.windowSeconds) && policy.windowSeconds > 0)) {
|
|
2832
3591
|
throw new TypeError("policy.windowSeconds must be a positive integer number of seconds.");
|
|
2833
3592
|
}
|
|
@@ -2839,13 +3598,144 @@ var PipRailClient = (_class2 = class {
|
|
|
2839
3598
|
);
|
|
2840
3599
|
}
|
|
2841
3600
|
}
|
|
3601
|
+
if (policy.expiresAt !== void 0) {
|
|
3602
|
+
const at = policy.expiresAt;
|
|
3603
|
+
if (!Number.isSafeInteger(at) || Math.abs(at) > 864e13) {
|
|
3604
|
+
throw new TypeError(
|
|
3605
|
+
`policy.expiresAt must be an absolute epoch-MILLISECONDS integer (like Date.now()); got ${JSON.stringify(at)}.`
|
|
3606
|
+
);
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
2842
3609
|
}
|
|
2843
3610
|
/** Emit an observability event, never letting a throwing handler break the
|
|
2844
3611
|
* payment flow (mirrors the server gate's `onPaid` isolation). */
|
|
2845
3612
|
safeEmit(event) {
|
|
2846
3613
|
try {
|
|
2847
3614
|
this.onEvent(event);
|
|
2848
|
-
} catch (
|
|
3615
|
+
} catch (e32) {
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
/**
|
|
3619
|
+
* Capture the verifiable receipt from a settled response (pure — no chain read), stamping
|
|
3620
|
+
* the resource URL this client actually fetched (authoritative over the gate's default ''). A
|
|
3621
|
+
* settled fetch with no receipt extension sets it to `null` so {@link lastReceipt} reflects the
|
|
3622
|
+
* latest fetch. Never throws — a malformed header just yields `null`.
|
|
3623
|
+
*/
|
|
3624
|
+
captureReceipt(response, url) {
|
|
3625
|
+
try {
|
|
3626
|
+
const parsed = parseReceiptExtension(response);
|
|
3627
|
+
this.lastReceiptValue = parsed ? { ...parsed, resource: { url } } : null;
|
|
3628
|
+
} catch (e33) {
|
|
3629
|
+
this.lastReceiptValue = null;
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* The verifiable {@link PipRailReceipt} from the most recent settled `fetch` — the
|
|
3634
|
+
* self-contained record the buyer KEEPS and anyone re-verifies against the chain
|
|
3635
|
+
* (see {@link PipRailClient.verifyReceipt}). `null` when the last settled fetch carried
|
|
3636
|
+
* no receipt (the gate's `receipts` option was off) or no payment has settled yet. Pure.
|
|
3637
|
+
*/
|
|
3638
|
+
lastReceipt() {
|
|
3639
|
+
return this.lastReceiptValue;
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Re-verify ANY {@link PipRailReceipt} against the chain — the anyone-can-run primitive.
|
|
3643
|
+
* Re-reads `receipt.transaction` via the receipt's own network driver and re-derives
|
|
3644
|
+
* `payTo`/`asset`/`payer` from the tx, **never trusting the receipt's claims**: a forged
|
|
3645
|
+
* `payTo`/`asset`/over-stated `amount` makes the driver's `verify()` fail (`ok:false`); a
|
|
3646
|
+
* forged `payer` surfaces as `matchesClaims:false`. Static + WALLET-FREE — a third party
|
|
3647
|
+
* verifies with only a chain + RPC, no PipRail account. **Never throws** (an RPC error or a
|
|
3648
|
+
* malformed receipt → `{ ok:false, error }`). Viem-free here — the chain read happens inside
|
|
3649
|
+
* the lazily-mounted family driver (the protocol layer pulls no chain libs). Durable for
|
|
3650
|
+
* digest-bound families; recency-bounded for the account-watch families (see
|
|
3651
|
+
* {@link ReceiptVerification}).
|
|
3652
|
+
*/
|
|
3653
|
+
static async verifyReceipt(receipt, opts) {
|
|
3654
|
+
const r = _optionalChain([receipt, 'optionalAccess', _35 => _35.receipt]);
|
|
3655
|
+
if (!r || typeof r !== "object") {
|
|
3656
|
+
return { ok: false, onChain: { payTo: "", asset: "", amount: "", payer: "" }, matchesClaims: false, ageSeconds: 0, error: "tx_not_found" };
|
|
3657
|
+
}
|
|
3658
|
+
const claimed = { payTo: _nullishCoalesce(r.payTo, () => ( "")), asset: _nullishCoalesce(r.asset, () => ( "")), amount: _nullishCoalesce(r.amount, () => ( "")), payer: _nullishCoalesce(r.payer, () => ( "")) };
|
|
3659
|
+
const ageSeconds = receiptAgeSeconds(r.verifiedAt);
|
|
3660
|
+
try {
|
|
3661
|
+
const chain = chainSelectorForNetwork(r.network, _optionalChain([opts, 'optionalAccess', _36 => _36.rpcUrl]));
|
|
3662
|
+
const net = await resolveNetwork2({
|
|
3663
|
+
chain,
|
|
3664
|
+
..._optionalChain([opts, 'optionalAccess', _37 => _37.rpcUrl]) ? { rpcUrl: opts.rpcUrl } : {}
|
|
3665
|
+
});
|
|
3666
|
+
const accept = {
|
|
3667
|
+
scheme: "onchain-proof",
|
|
3668
|
+
network: r.network,
|
|
3669
|
+
amount: r.amount,
|
|
3670
|
+
asset: r.asset,
|
|
3671
|
+
payTo: r.payTo,
|
|
3672
|
+
maxTimeoutSeconds: RECEIPT_VERIFY_WINDOW_SECONDS,
|
|
3673
|
+
extra: {
|
|
3674
|
+
nonce: _nullishCoalesce(r.nonce, () => ( "")),
|
|
3675
|
+
// the challenge/memo nonce — REQUIRED for Template-A re-verify
|
|
3676
|
+
decimals: _nullishCoalesce(receipt.decimals, () => ( 6)),
|
|
3677
|
+
// B10c — Stellar/XRPL/TON re-scale by it; 6 = USDC fallback
|
|
3678
|
+
minConfirmations: 0,
|
|
3679
|
+
amountFormatted: ""
|
|
3680
|
+
}
|
|
3681
|
+
};
|
|
3682
|
+
const ref = familyForChain(chain) === "near" ? `${r.payer}:${r.transaction}` : r.transaction;
|
|
3683
|
+
const result = await net.verify(ref, accept);
|
|
3684
|
+
if (!result.ok) {
|
|
3685
|
+
return { ok: false, onChain: claimed, matchesClaims: false, ageSeconds, error: result.error };
|
|
3686
|
+
}
|
|
3687
|
+
const oc = result.receipt;
|
|
3688
|
+
const onChain = { payTo: oc.payTo, asset: oc.asset, amount: oc.amount, payer: oc.payer };
|
|
3689
|
+
const matchesClaims = sameAddress(oc.payer, r.payer);
|
|
3690
|
+
return { ok: true, onChain, matchesClaims, ageSeconds };
|
|
3691
|
+
} catch (e34) {
|
|
3692
|
+
return { ok: false, onChain: claimed, matchesClaims: false, ageSeconds, error: "tx_not_found" };
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
/**
|
|
3696
|
+
* Verify the OPTIONAL Tier-2 service-delivery attestation on a {@link PipRailReceipt}
|
|
3697
|
+
* — the merchant's signed proof that the resource was actually SERVED (the one thing
|
|
3698
|
+
* the chain can't attest). For an EVM EIP-712 attestation this re-recovers the signer
|
|
3699
|
+
* from the signature over the official `offer-receipt` typed data and checks
|
|
3700
|
+
* `recover === receipt.payTo` (spec §4.5.1 / §5.5) — the classic EIP-712 footgun made
|
|
3701
|
+
* safe (`recoverTypedDataAddress` returns a WRONG address rather than throwing on a bad
|
|
3702
|
+
* signature, so the equality check is the real verification). A tampered signature →
|
|
3703
|
+
* `{ ok:false }`, never a throw.
|
|
3704
|
+
*
|
|
3705
|
+
* Static + wallet-free. **Never throws** (a malformed/absent attestation, an unsupported
|
|
3706
|
+
* format, or a recovery fault → `{ ok:false, reason }`). Viem-free HERE — the recover runs
|
|
3707
|
+
* inside the lazily-imported EVM receipt driver (a lazy chunk), so the protocol layer pulls
|
|
3708
|
+
* no chain libs. The JWS format defers to R3 (`{ ok:false, reason:'jws-not-loaded' }`).
|
|
3709
|
+
*/
|
|
3710
|
+
static async verifyAttestation(receipt) {
|
|
3711
|
+
const att = _optionalChain([receipt, 'optionalAccess', _38 => _38.attestation]);
|
|
3712
|
+
if (!att || typeof att !== "object" || typeof att.signature !== "string") {
|
|
3713
|
+
return { ok: false, reason: "no-attestation" };
|
|
3714
|
+
}
|
|
3715
|
+
if (att.format === "jws") {
|
|
3716
|
+
return { ok: false, reason: "jws-not-loaded" };
|
|
3717
|
+
}
|
|
3718
|
+
const r = receipt.receipt;
|
|
3719
|
+
if (!r || typeof r !== "object") return { ok: false, reason: "no-receipt" };
|
|
3720
|
+
const payload = _nullishCoalesce(att.payload, () => ( {}));
|
|
3721
|
+
const network = typeof payload.network === "string" ? payload.network : r.network;
|
|
3722
|
+
const resourceUrl = typeof payload.resourceUrl === "string" ? payload.resourceUrl : _nullishCoalesce(_optionalChain([receipt, 'access', _39 => _39.resource, 'optionalAccess', _40 => _40.url]), () => ( ""));
|
|
3723
|
+
const payer = typeof payload.payer === "string" ? payload.payer : r.payer;
|
|
3724
|
+
const issuedAt = typeof payload.issuedAt === "number" ? payload.issuedAt : receiptIssuedAtSeconds(r.verifiedAt);
|
|
3725
|
+
const transaction = typeof payload.transaction === "string" ? payload.transaction : _nullishCoalesce(r.transaction, () => ( ""));
|
|
3726
|
+
try {
|
|
3727
|
+
const { verifyReceiptAttestationEvm } = await Promise.resolve().then(() => _interopRequireWildcard(require("./receipt-QCDPMWNF.cjs")));
|
|
3728
|
+
return await verifyReceiptAttestationEvm({
|
|
3729
|
+
payTo: r.payTo,
|
|
3730
|
+
network,
|
|
3731
|
+
resourceUrl,
|
|
3732
|
+
payer,
|
|
3733
|
+
issuedAt,
|
|
3734
|
+
transaction,
|
|
3735
|
+
signature: att.signature
|
|
3736
|
+
});
|
|
3737
|
+
} catch (e35) {
|
|
3738
|
+
return { ok: false, reason: "verify-failed" };
|
|
2849
3739
|
}
|
|
2850
3740
|
}
|
|
2851
3741
|
/** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
|
|
@@ -2875,7 +3765,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2875
3765
|
* as-is) or a plain object (serialised as JSON).
|
|
2876
3766
|
*/
|
|
2877
3767
|
post(url, body, init) {
|
|
2878
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
3768
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _41 => _41.headers]));
|
|
2879
3769
|
let payload;
|
|
2880
3770
|
if (body === void 0 || body === null) {
|
|
2881
3771
|
payload = void 0;
|
|
@@ -2906,7 +3796,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2906
3796
|
* "0.05 USDC on Base, within budget → pay it." No funds move.
|
|
2907
3797
|
*/
|
|
2908
3798
|
async quote(url, init) {
|
|
2909
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3799
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _42 => _42.method]), () => ( "GET")) });
|
|
2910
3800
|
if (res.status !== 402) return null;
|
|
2911
3801
|
const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
2912
3802
|
return quote;
|
|
@@ -2925,22 +3815,37 @@ var PipRailClient = (_class2 = class {
|
|
|
2925
3815
|
* on Tron, where a USD₮ transfer can cost real TRX.
|
|
2926
3816
|
*/
|
|
2927
3817
|
async estimateCost(url, init) {
|
|
2928
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3818
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _43 => _43.method]), () => ( "GET")) });
|
|
2929
3819
|
if (res.status !== 402) return null;
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3820
|
+
try {
|
|
3821
|
+
const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
3822
|
+
const cost = await net.estimateCost(accept);
|
|
3823
|
+
return { quote, cost };
|
|
3824
|
+
} catch (err) {
|
|
3825
|
+
if (err instanceof _chunkMWBT7MCEcjs.InvalidEnvelopeError) return null;
|
|
3826
|
+
throw err;
|
|
3827
|
+
}
|
|
2933
3828
|
}
|
|
2934
3829
|
/** Aggregated snapshot of every payment this client has settled — total
|
|
2935
|
-
* count, cumulative spend per token,
|
|
3830
|
+
* count, cumulative spend per token, cumulative spend per denomination (the
|
|
3831
|
+
* cross-token grand total), and the individual records. */
|
|
2936
3832
|
spent() {
|
|
2937
3833
|
return this.ledger.summary();
|
|
2938
3834
|
}
|
|
3835
|
+
/** The CONFIGURED spend policy, read back unchanged — so an agent can self-check
|
|
3836
|
+
* its WHOLE leash (caps, allowlists, time, denom + count limits) without hitting a
|
|
3837
|
+
* decline. `undefined` when no policy is set. Pure; never throws. */
|
|
3838
|
+
policy() {
|
|
3839
|
+
return this.opts.policy;
|
|
3840
|
+
}
|
|
2939
3841
|
/**
|
|
2940
3842
|
* Read-only budget + time leash for a Mode-A (headless) agent — the policy IS
|
|
2941
3843
|
* the consent, and this is how the agent SEES what's left of it before paying.
|
|
2942
|
-
* Composes the
|
|
2943
|
-
*
|
|
3844
|
+
* Composes the ledger with the configured policy: the per-asset money leash
|
|
3845
|
+
* (`byAsset`), the cross-token GRAND TOTAL per denomination (`byDenom`, present
|
|
3846
|
+
* from the start), and the payment-COUNT leash (`counts`). Never throws, moves no
|
|
3847
|
+
* funds. The money/count figures persist if a `spendStore` is set; the time
|
|
3848
|
+
* envelope is process-scoped (see {@link SessionBudget}).
|
|
2944
3849
|
*/
|
|
2945
3850
|
budget() {
|
|
2946
3851
|
const view = this.sessionView();
|
|
@@ -2948,12 +3853,57 @@ var PipRailClient = (_class2 = class {
|
|
|
2948
3853
|
return {
|
|
2949
3854
|
session: {
|
|
2950
3855
|
start,
|
|
2951
|
-
expiresAt: _optionalChain([view, 'optionalAccess',
|
|
2952
|
-
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess',
|
|
3856
|
+
expiresAt: _optionalChain([view, 'optionalAccess', _44 => _44.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
|
|
3857
|
+
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _45 => _45.secondsRemaining]), () => ( null))
|
|
2953
3858
|
},
|
|
2954
|
-
byAsset: this.remaining()
|
|
3859
|
+
byAsset: this.remaining(),
|
|
3860
|
+
byDenom: this.denomRemaining(),
|
|
3861
|
+
counts: this.countStatus()
|
|
2955
3862
|
};
|
|
2956
3863
|
}
|
|
3864
|
+
/**
|
|
3865
|
+
* The cross-token GRAND-TOTAL leash — one row per denomination capped by
|
|
3866
|
+
* `policy.maxTotalPerDenom`. Unlike `remaining()`, rows exist from the START
|
|
3867
|
+
* (the cap is a single declared number, so headroom is previewable before any
|
|
3868
|
+
* spend). `[]` when no `maxTotalPerDenom` is set. Pure; never throws; never a
|
|
3869
|
+
* price-converted figure (tokens grouped as one unit, each 1:1).
|
|
3870
|
+
*/
|
|
3871
|
+
denomRemaining() {
|
|
3872
|
+
const caps = _optionalChain([this, 'access', _46 => _46.opts, 'access', _47 => _47.policy, 'optionalAccess', _48 => _48.maxTotalPerDenom]);
|
|
3873
|
+
if (!caps) return [];
|
|
3874
|
+
return Object.entries(caps).map(([rawDenom, capStr]) => {
|
|
3875
|
+
const denom = rawDenom.toUpperCase();
|
|
3876
|
+
const spentScaled = this.ledger.totalForDenom(denom);
|
|
3877
|
+
const capScaled = _chunkMWBT7MCEcjs.floorUnits.call(void 0, capStr, DENOM_PRECISION);
|
|
3878
|
+
const remainingScaled = capScaled > spentScaled ? capScaled - spentScaled : 0n;
|
|
3879
|
+
const fraction = capScaled === 0n ? spentScaled > 0n ? 1 : 0 : Number(spentScaled * 1000000n / capScaled) / 1e6;
|
|
3880
|
+
return {
|
|
3881
|
+
denom,
|
|
3882
|
+
spentFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, spentScaled, DENOM_PRECISION),
|
|
3883
|
+
capFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, capScaled, DENOM_PRECISION),
|
|
3884
|
+
remainingFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, remainingScaled, DENOM_PRECISION),
|
|
3885
|
+
fraction
|
|
3886
|
+
};
|
|
3887
|
+
});
|
|
3888
|
+
}
|
|
3889
|
+
/** The payment-COUNT leash — settled count so far + the configured lifetime/window
|
|
3890
|
+
* caps and what's left. Pure; never throws. */
|
|
3891
|
+
countStatus() {
|
|
3892
|
+
const policy = this.opts.policy;
|
|
3893
|
+
const settled = this.ledger.count();
|
|
3894
|
+
const out = { settled };
|
|
3895
|
+
if (_optionalChain([policy, 'optionalAccess', _49 => _49.maxPayments]) !== void 0) {
|
|
3896
|
+
out.lifetimeCap = policy.maxPayments;
|
|
3897
|
+
out.lifetimeRemaining = Math.max(0, policy.maxPayments - settled);
|
|
3898
|
+
}
|
|
3899
|
+
if (_optionalChain([policy, 'optionalAccess', _50 => _50.maxPaymentsPerWindow]) !== void 0 && policy.windowSeconds !== void 0) {
|
|
3900
|
+
const windowSettled = this.ledger.countSince(Date.now() - policy.windowSeconds * 1e3);
|
|
3901
|
+
out.windowCap = policy.maxPaymentsPerWindow;
|
|
3902
|
+
out.windowSettled = windowSettled;
|
|
3903
|
+
out.windowRemaining = Math.max(0, policy.maxPaymentsPerWindow - windowSettled);
|
|
3904
|
+
}
|
|
3905
|
+
return out;
|
|
3906
|
+
}
|
|
2957
3907
|
/**
|
|
2958
3908
|
* Per-(network, asset) remaining budget — ONE row per pair the ledger already
|
|
2959
3909
|
* holds (decimals are known only after the first spend), so a fresh client with
|
|
@@ -2962,7 +3912,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2962
3912
|
* never throws, never sums across tokens (no price oracle). PROCESS-SCOPED.
|
|
2963
3913
|
*/
|
|
2964
3914
|
remaining() {
|
|
2965
|
-
const maxTotal = _optionalChain([this, 'access',
|
|
3915
|
+
const maxTotal = _optionalChain([this, 'access', _51 => _51.opts, 'access', _52 => _52.policy, 'optionalAccess', _53 => _53.maxTotal]);
|
|
2966
3916
|
return this.ledger.assetBuckets().map((b) => {
|
|
2967
3917
|
const base2 = {
|
|
2968
3918
|
network: b.network,
|
|
@@ -2972,13 +3922,13 @@ var PipRailClient = (_class2 = class {
|
|
|
2972
3922
|
spentBase: b.totalBase.toString()
|
|
2973
3923
|
};
|
|
2974
3924
|
if (maxTotal === void 0) return base2;
|
|
2975
|
-
const capBase =
|
|
3925
|
+
const capBase = _chunkMWBT7MCEcjs.floorUnits.call(void 0, maxTotal, b.decimals);
|
|
2976
3926
|
const remainingBase = capBase > b.totalBase ? capBase - b.totalBase : 0n;
|
|
2977
3927
|
return {
|
|
2978
3928
|
...base2,
|
|
2979
3929
|
capBase: capBase.toString(),
|
|
2980
3930
|
remainingBase: remainingBase.toString(),
|
|
2981
|
-
remainingFormatted:
|
|
3931
|
+
remainingFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, remainingBase, b.decimals)
|
|
2982
3932
|
};
|
|
2983
3933
|
});
|
|
2984
3934
|
}
|
|
@@ -3014,15 +3964,15 @@ var PipRailClient = (_class2 = class {
|
|
|
3014
3964
|
* the plan yourself. No funds move.
|
|
3015
3965
|
*/
|
|
3016
3966
|
async planPayment(url, init) {
|
|
3017
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3967
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _54 => _54.method]), () => ( "GET")) });
|
|
3018
3968
|
if (res.status !== 402) return null;
|
|
3019
3969
|
const challenge = await parseChallenge(res);
|
|
3020
3970
|
if (!challenge) {
|
|
3021
|
-
throw new (0,
|
|
3971
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
|
|
3022
3972
|
}
|
|
3023
3973
|
const { net, wallet } = await this.ensure();
|
|
3024
3974
|
if (!wallet) {
|
|
3025
|
-
throw new (0,
|
|
3975
|
+
throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
|
|
3026
3976
|
"planPayment needs a wallet (it checks YOUR balance, gas, and recipient-readiness). This client is read-only \u2014 construct it with a `wallet` to plan or pay."
|
|
3027
3977
|
);
|
|
3028
3978
|
}
|
|
@@ -3195,40 +4145,48 @@ var PipRailClient = (_class2 = class {
|
|
|
3195
4145
|
* streams throw `NonReplayableBodyError`.
|
|
3196
4146
|
*/
|
|
3197
4147
|
async fetch(url, init) {
|
|
3198
|
-
const body = _optionalChain([init, 'optionalAccess',
|
|
4148
|
+
const body = _optionalChain([init, 'optionalAccess', _55 => _55.body]);
|
|
3199
4149
|
if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
|
|
3200
|
-
throw new (0,
|
|
4150
|
+
throw new (0, _chunkMWBT7MCEcjs.NonReplayableBodyError)(
|
|
3201
4151
|
"fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
|
|
3202
4152
|
);
|
|
3203
4153
|
}
|
|
3204
4154
|
const firstResponse = await fetch(url, init);
|
|
3205
4155
|
if (firstResponse.status !== 402) return firstResponse;
|
|
3206
|
-
const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess',
|
|
4156
|
+
const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _56 => _56.schemes]));
|
|
3207
4157
|
const resolved = await this.resolveChallenge(url, firstResponse, schemes);
|
|
3208
4158
|
const { net, wallet, challenge } = resolved;
|
|
3209
4159
|
if (!wallet) {
|
|
3210
|
-
throw new (0,
|
|
4160
|
+
throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
|
|
3211
4161
|
"Paying a 402 needs a wallet to sign + settle. This client is read-only \u2014 construct it with a `wallet` to pay (quote/discover/register still work without one)."
|
|
3212
4162
|
);
|
|
3213
4163
|
}
|
|
3214
4164
|
let accept = resolved.accept;
|
|
3215
4165
|
let quote = resolved.quote;
|
|
3216
|
-
const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
4166
|
+
const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _57 => _57.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
|
|
3217
4167
|
if (autoRoute) {
|
|
3218
4168
|
const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
|
|
3219
4169
|
if (!plan.best) {
|
|
3220
4170
|
const reason = _nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment."));
|
|
3221
4171
|
this.safeEmit({ kind: "payment-failed", reason });
|
|
3222
|
-
throw new (0,
|
|
4172
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(reason);
|
|
3223
4173
|
}
|
|
3224
4174
|
accept = plan.best.accept;
|
|
3225
4175
|
quote = plan.best.quote;
|
|
3226
4176
|
}
|
|
3227
4177
|
this.safeEmit({ kind: "payment-required", challenge, accept });
|
|
3228
4178
|
await this.authorize(quote);
|
|
4179
|
+
if (accept.scheme === "upto") {
|
|
4180
|
+
return this.payUptoRail(net, wallet, accept, url, init, quote);
|
|
4181
|
+
}
|
|
3229
4182
|
if (accept.scheme === "exact") {
|
|
3230
4183
|
return this.payExactRail(net, wallet, accept, url, init, quote);
|
|
3231
4184
|
}
|
|
4185
|
+
if (accept.scheme !== "onchain-proof") {
|
|
4186
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
4187
|
+
`internal: unrouted accept scheme '${accept.scheme}' reached the onchain-proof pay path.`
|
|
4188
|
+
);
|
|
4189
|
+
}
|
|
3232
4190
|
const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
|
|
3233
4191
|
const response = await this.retryWithProof(url, init, accept, ref, confirmed);
|
|
3234
4192
|
this.recordSpend(quote, ref);
|
|
@@ -3243,7 +4201,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3243
4201
|
async resolveChallenge(url, response, schemes) {
|
|
3244
4202
|
const challenge = await parseChallenge(response);
|
|
3245
4203
|
if (!challenge) {
|
|
3246
|
-
throw new (0,
|
|
4204
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3247
4205
|
"402 response did not include a parseable x402 challenge."
|
|
3248
4206
|
);
|
|
3249
4207
|
}
|
|
@@ -3254,7 +4212,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3254
4212
|
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network)
|
|
3255
4213
|
);
|
|
3256
4214
|
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
3257
|
-
throw new (0,
|
|
4215
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
3258
4216
|
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM, Solana, Algorand + NEAR today), and no 'onchain-proof' rail was offered.`
|
|
3259
4217
|
);
|
|
3260
4218
|
}
|
|
@@ -3263,13 +4221,13 @@ var PipRailClient = (_class2 = class {
|
|
|
3263
4221
|
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network) && net.describeAsset(a.asset) != null
|
|
3264
4222
|
);
|
|
3265
4223
|
if (payable) {
|
|
3266
|
-
throw new (0,
|
|
4224
|
+
throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
|
|
3267
4225
|
`This 402 is payable only via the standard 'exact' rail on ${net.network}, which is OFF by default. Enable it: new PipRailClient({ \u2026, schemes: ['onchain-proof', 'exact'] }) or per call fetch(url, { schemes: ['exact'] }) (MCP: PIPRAIL_SCHEMES=onchain-proof,exact).`
|
|
3268
4226
|
);
|
|
3269
4227
|
}
|
|
3270
4228
|
}
|
|
3271
4229
|
const networks = [...new Set(challenge.accepts.map((a) => a.network))].join(", ");
|
|
3272
|
-
throw new (0,
|
|
4230
|
+
throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
|
|
3273
4231
|
`No accepts[] entry payable by this client on ${net.network} (schemes: ${schemes.join(", ")}; challenge offered: ${networks || "none"}).`
|
|
3274
4232
|
);
|
|
3275
4233
|
}
|
|
@@ -3315,6 +4273,13 @@ var PipRailClient = (_class2 = class {
|
|
|
3315
4273
|
)
|
|
3316
4274
|
);
|
|
3317
4275
|
}
|
|
4276
|
+
if (schemes.includes("upto")) {
|
|
4277
|
+
out.push(
|
|
4278
|
+
...challenge.accepts.filter(
|
|
4279
|
+
(a) => a.scheme === "upto" && this.supportsNetwork(net, a.network) && typeof net.payUpto === "function" && net.describeAsset(a.asset) != null && typeof _optionalChain([a, 'access', _58 => _58.extra, 'optionalAccess', _59 => _59.facilitatorAddress]) === "string" && a.extra.facilitatorAddress.length > 0 && Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
|
|
4280
|
+
)
|
|
4281
|
+
);
|
|
4282
|
+
}
|
|
3318
4283
|
return out;
|
|
3319
4284
|
}
|
|
3320
4285
|
/** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
|
|
@@ -3336,11 +4301,11 @@ var PipRailClient = (_class2 = class {
|
|
|
3336
4301
|
...session ? { session } : {}
|
|
3337
4302
|
};
|
|
3338
4303
|
}
|
|
3339
|
-
const analysed = await Promise.all(
|
|
4304
|
+
const analysed = (await Promise.all(
|
|
3340
4305
|
candidates.map(
|
|
3341
|
-
(accept) => this.analyzeRail(net, wallet, accept, url, challenge.resource.description)
|
|
4306
|
+
(accept) => this.analyzeRail(net, wallet, accept, url, challenge.resource.description).catch(() => null)
|
|
3342
4307
|
)
|
|
3343
|
-
);
|
|
4308
|
+
)).filter((o) => o !== null);
|
|
3344
4309
|
const options = rankOptions(analysed);
|
|
3345
4310
|
const best = _nullishCoalesce(options.find((o) => o.state === "payable"), () => ( null));
|
|
3346
4311
|
const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
|
|
@@ -3364,14 +4329,14 @@ var PipRailClient = (_class2 = class {
|
|
|
3364
4329
|
const rr = await net.recipientReady(accept.payTo, accept.asset).catch(() => ({ ready: "unknown" }));
|
|
3365
4330
|
const amount = BigInt(accept.amount);
|
|
3366
4331
|
const fee = safeBig(cost.fee);
|
|
3367
|
-
const isExact = accept.scheme === "exact";
|
|
4332
|
+
const isExact = accept.scheme === "exact" || accept.scheme === "upto";
|
|
3368
4333
|
const isNative = accept.asset === "native";
|
|
3369
4334
|
const blockers = [];
|
|
3370
4335
|
const warnings = [];
|
|
3371
4336
|
const shortfall = {};
|
|
3372
4337
|
if (!quote.withinPolicy) {
|
|
3373
4338
|
blockers.push(
|
|
3374
|
-
quote.policyCode === "SESSION_EXPIRED" || quote.policyCode === "WINDOW_TOTAL" ? "OUTSIDE_WINDOW" : "OUTSIDE_POLICY"
|
|
4339
|
+
quote.policyCode === "SESSION_EXPIRED" || quote.policyCode === "WINDOW_TOTAL" || quote.policyCode === "WINDOW_COUNT" ? "OUTSIDE_WINDOW" : "OUTSIDE_POLICY"
|
|
3375
4340
|
);
|
|
3376
4341
|
}
|
|
3377
4342
|
if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
|
|
@@ -3382,21 +4347,21 @@ var PipRailClient = (_class2 = class {
|
|
|
3382
4347
|
if (isExact) {
|
|
3383
4348
|
if (tokenKnown && bal.token < amount) {
|
|
3384
4349
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3385
|
-
shortfall.token =
|
|
4350
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
|
|
3386
4351
|
}
|
|
3387
4352
|
} else if (isNative) {
|
|
3388
4353
|
if (nativeKnown && bal.native < amount + fee) {
|
|
3389
4354
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3390
|
-
shortfall.token =
|
|
4355
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
|
|
3391
4356
|
}
|
|
3392
4357
|
} else {
|
|
3393
4358
|
if (tokenKnown && bal.token < amount) {
|
|
3394
4359
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3395
|
-
shortfall.token =
|
|
4360
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
|
|
3396
4361
|
}
|
|
3397
4362
|
if (nativeKnown && bal.native < fee) {
|
|
3398
4363
|
blockers.push("INSUFFICIENT_GAS");
|
|
3399
|
-
shortfall.native =
|
|
4364
|
+
shortfall.native = _chunkMWBT7MCEcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
|
|
3400
4365
|
} else if (nativeKnown && fee > 0n && bal.native < fee * 3n / 2n) {
|
|
3401
4366
|
warnings.push("THIN_GAS_MARGIN");
|
|
3402
4367
|
}
|
|
@@ -3421,8 +4386,8 @@ var PipRailClient = (_class2 = class {
|
|
|
3421
4386
|
blockers,
|
|
3422
4387
|
warnings,
|
|
3423
4388
|
balance: {
|
|
3424
|
-
token: bal.token != null ?
|
|
3425
|
-
native: bal.native != null ?
|
|
4389
|
+
token: bal.token != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
|
|
4390
|
+
native: bal.native != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
|
|
3426
4391
|
},
|
|
3427
4392
|
need: { token: quote.amountFormatted, native: cost.feeFormatted },
|
|
3428
4393
|
...shortfall.token || shortfall.native ? { shortfall } : {},
|
|
@@ -3433,25 +4398,30 @@ var PipRailClient = (_class2 = class {
|
|
|
3433
4398
|
* driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
|
|
3434
4399
|
buildQuote(net, accept, url, description) {
|
|
3435
4400
|
if (typeof accept.amount !== "string" || !/^\d+$/.test(accept.amount)) {
|
|
3436
|
-
throw new (0,
|
|
4401
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3437
4402
|
`challenge amount "${String(accept.amount)}" is not a base-unit integer string.`
|
|
3438
4403
|
);
|
|
3439
4404
|
}
|
|
3440
4405
|
if (typeof accept.asset !== "string" || accept.asset.length === 0) {
|
|
3441
|
-
throw new (0,
|
|
4406
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3442
4407
|
`challenge on ${accept.network} states no (string) asset \u2014 refusing to price it.`
|
|
3443
4408
|
);
|
|
3444
4409
|
}
|
|
3445
4410
|
const amountBase = BigInt(accept.amount);
|
|
3446
4411
|
const described = net.describeAsset(accept.asset);
|
|
3447
|
-
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess',
|
|
4412
|
+
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _60 => _60.decimals]), () => ( _optionalChain([accept, 'access', _61 => _61.extra, 'optionalAccess', _62 => _62.decimals])));
|
|
3448
4413
|
if (typeof decimals !== "number" || !Number.isInteger(decimals) || decimals < 0) {
|
|
3449
|
-
throw new (0,
|
|
4414
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3450
4415
|
`challenge for ${accept.asset} on ${accept.network} states no valid decimals and the SDK doesn't recognise the token \u2014 refusing to price it.`
|
|
3451
4416
|
);
|
|
3452
4417
|
}
|
|
3453
|
-
|
|
3454
|
-
|
|
4418
|
+
if (decimals > _chunkMWBT7MCEcjs.MAX_DECIMALS) {
|
|
4419
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
4420
|
+
`challenge for ${accept.asset} on ${accept.network} states ${decimals} decimals (> ${_chunkMWBT7MCEcjs.MAX_DECIMALS}) \u2014 refusing to price it (no real token is that deep).`
|
|
4421
|
+
);
|
|
4422
|
+
}
|
|
4423
|
+
const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _63 => _63.symbol]), () => ( _optionalChain([accept, 'access', _64 => _64.extra, 'optionalAccess', _65 => _65.symbol])));
|
|
4424
|
+
const amountFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amountBase, decimals);
|
|
3455
4425
|
const intent = {
|
|
3456
4426
|
host: hostOf2(url),
|
|
3457
4427
|
chain: this.opts.chain,
|
|
@@ -3464,9 +4434,14 @@ var PipRailClient = (_class2 = class {
|
|
|
3464
4434
|
};
|
|
3465
4435
|
const policy = this.opts.policy;
|
|
3466
4436
|
const hasWindow = !!policy && policy.windowTotal != null && policy.windowSeconds != null;
|
|
4437
|
+
const hasWindowCount = !!policy && policy.maxPaymentsPerWindow != null && policy.windowSeconds != null;
|
|
4438
|
+
const hasDenomCap = !!policy && policy.maxTotalPerDenom != null && Object.keys(policy.maxTotalPerDenom).length > 0;
|
|
4439
|
+
const hasCountCap = !!policy && policy.maxPayments != null;
|
|
3467
4440
|
const hasTimePolicy = !!policy && (policy.ttlSeconds != null || policy.expiresAt != null || hasWindow);
|
|
4441
|
+
const needsCtx = hasTimePolicy || hasWindowCount || hasDenomCap || hasCountCap;
|
|
4442
|
+
const denom = hasDenomCap ? denomOf(intent.symbol, intent.asset, policy) : void 0;
|
|
3468
4443
|
const now = Date.now();
|
|
3469
|
-
const ctx =
|
|
4444
|
+
const ctx = needsCtx ? {
|
|
3470
4445
|
now,
|
|
3471
4446
|
sessionStart: this.ledger.sessionStart,
|
|
3472
4447
|
// Window slice ONLY when BOTH fields are set — never a `?? 0` width.
|
|
@@ -3474,7 +4449,12 @@ var PipRailClient = (_class2 = class {
|
|
|
3474
4449
|
accept.network,
|
|
3475
4450
|
accept.asset,
|
|
3476
4451
|
now - policy.windowSeconds * 1e3
|
|
3477
|
-
) : 0n
|
|
4452
|
+
) : 0n,
|
|
4453
|
+
// Running grand total on this intent's denomination, across the (shared) ledger.
|
|
4454
|
+
spentInDenomScaled: denom ? this.ledger.totalForDenom(denom) : 0n,
|
|
4455
|
+
// Settled-payment counts (lifetime + window) for the count caps.
|
|
4456
|
+
paymentCount: hasCountCap ? this.ledger.count() : 0,
|
|
4457
|
+
paymentCountInWindow: hasWindowCount ? this.ledger.countSince(now - policy.windowSeconds * 1e3) : 0
|
|
3478
4458
|
} : void 0;
|
|
3479
4459
|
const decision = evaluatePolicy(
|
|
3480
4460
|
intent,
|
|
@@ -3482,7 +4462,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3482
4462
|
this.ledger.totalFor(accept.network, accept.asset),
|
|
3483
4463
|
ctx
|
|
3484
4464
|
);
|
|
3485
|
-
const serverSymbol = _optionalChain([accept, 'access',
|
|
4465
|
+
const serverSymbol = _optionalChain([accept, 'access', _66 => _66.extra, 'optionalAccess', _67 => _67.symbol]);
|
|
3486
4466
|
const symbolMismatch = intent.recognized && !!serverSymbol && !!symbol && serverSymbol.toUpperCase() !== symbol.toUpperCase();
|
|
3487
4467
|
return {
|
|
3488
4468
|
url,
|
|
@@ -3510,9 +4490,11 @@ var PipRailClient = (_class2 = class {
|
|
|
3510
4490
|
async authorize(quote) {
|
|
3511
4491
|
if (!quote.withinPolicy) {
|
|
3512
4492
|
const reason = `Payment refused by policy: ${_nullishCoalesce(quote.policyReason, () => ( "not allowed"))}`;
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
4493
|
+
this.refuse(reason, {
|
|
4494
|
+
reasonCode: reasonCodeForPolicy(quote.policyCode),
|
|
4495
|
+
policyCode: quote.policyCode,
|
|
4496
|
+
quote
|
|
4497
|
+
});
|
|
3516
4498
|
}
|
|
3517
4499
|
const hook = this.opts.onBeforePay;
|
|
3518
4500
|
if (!hook) return;
|
|
@@ -3520,43 +4502,161 @@ var PipRailClient = (_class2 = class {
|
|
|
3520
4502
|
try {
|
|
3521
4503
|
approved = await hook(quote);
|
|
3522
4504
|
} catch (err) {
|
|
3523
|
-
|
|
3524
|
-
this.safeEmit({ kind: "payment-failed", reason, code: "APPROVAL" });
|
|
3525
|
-
throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { cause: err, reasonCode: "APPROVAL" });
|
|
4505
|
+
this.refuse("onBeforePay threw \u2014 refusing to pay.", { reasonCode: "APPROVAL", quote, cause: err });
|
|
3526
4506
|
}
|
|
3527
4507
|
if (!approved) {
|
|
3528
4508
|
const reason = `onBeforePay declined ${quote.amountFormatted} ${_nullishCoalesce(quote.symbol, () => ( ""))}`.trimEnd() + ` on ${quote.network}.`;
|
|
3529
|
-
this.
|
|
3530
|
-
throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { reasonCode: "APPROVAL" });
|
|
4509
|
+
this.refuse(reason, { reasonCode: "APPROVAL", quote });
|
|
3531
4510
|
}
|
|
3532
4511
|
}
|
|
3533
|
-
/**
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
},
|
|
3547
|
-
quote.
|
|
3548
|
-
|
|
4512
|
+
/**
|
|
4513
|
+
* Refuse a payment BEFORE any send: emit BOTH the legacy `payment-failed` (so existing
|
|
4514
|
+
* `onEvent` consumers are unaffected) AND the richer, dedicated `payment-declined`
|
|
4515
|
+
* (typed reasonCode + fine PolicyDenyCode + the quote + a budget snapshot), then throw
|
|
4516
|
+
* the typed {@link PaymentDeclinedError}. Returns `never` so callers' control flow is
|
|
4517
|
+
* exhaustive.
|
|
4518
|
+
*/
|
|
4519
|
+
refuse(reason, opts) {
|
|
4520
|
+
this.safeEmit({ kind: "payment-failed", reason, ...opts.reasonCode ? { code: opts.reasonCode } : {} });
|
|
4521
|
+
this.safeEmit({
|
|
4522
|
+
kind: "payment-declined",
|
|
4523
|
+
reason,
|
|
4524
|
+
...opts.reasonCode ? { reasonCode: opts.reasonCode } : {},
|
|
4525
|
+
...opts.policyCode ? { code: opts.policyCode } : {},
|
|
4526
|
+
...opts.quote ? { quote: opts.quote } : {},
|
|
4527
|
+
budget: this.budget()
|
|
4528
|
+
});
|
|
4529
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(reason, {
|
|
4530
|
+
...opts.reasonCode ? { reasonCode: opts.reasonCode } : {},
|
|
4531
|
+
...opts.cause !== void 0 ? { cause: opts.cause } : {}
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
/** Record a settled payment in the ledger (TRUE decimals for the running total + the
|
|
4535
|
+
* denomination it counts toward in the grand total). Then fire the `onSpend` callback
|
|
4536
|
+
* with the record + the post-payment budget, and emit any `warnAtFraction` thresholds
|
|
4537
|
+
* this payment just crossed. All observability is isolated — a throwing hook never
|
|
4538
|
+
* affects the (already-settled) payment.
|
|
4539
|
+
*
|
|
4540
|
+
* `settledAmountBase` is the SINGLE upto ledger-reconciliation seam: the quote (and thus
|
|
4541
|
+
* the policy/budget) gates on the MAX, and for the metered `upto` rail the budgeted amount
|
|
4542
|
+
* RECORDED is ALSO the authorized MAX — the only buyer-provable bound. The merchant's
|
|
4543
|
+
* claimed actual is UNTRUSTED (a malicious merchant can settle the MAX on-chain yet report
|
|
4544
|
+
* a tiny `SettleOutcome.amount`); recording it would let an under-report silently loosen a
|
|
4545
|
+
* cumulative cap (`maxTotal`/`maxTotalPerDenom`/`windowTotal`) past the buyer's real on-chain
|
|
4546
|
+
* spend (POL-1). So the cap-bearing `amountBase` is the MAX; the clamped actual is surfaced
|
|
4547
|
+
* separately on `settledBase`/`settledFormatted` for transparency (it equals the receipt's
|
|
4548
|
+
* amount). When absent (onchain-proof/exact) this is byte-identical to before. */
|
|
4549
|
+
recordSpend(quote, ref, settledAmountBase) {
|
|
4550
|
+
const denom = denomOf(quote.symbol, quote.asset, this.opts.policy);
|
|
4551
|
+
const amountBase = quote.amount;
|
|
4552
|
+
const amountFormatted = quote.amountFormatted;
|
|
4553
|
+
let settledBase;
|
|
4554
|
+
let settledFormatted;
|
|
4555
|
+
if (settledAmountBase !== void 0 && /^\d+$/.test(settledAmountBase)) {
|
|
4556
|
+
try {
|
|
4557
|
+
const claimed = BigInt(settledAmountBase);
|
|
4558
|
+
const max = BigInt(quote.amount);
|
|
4559
|
+
const shown = claimed < max ? claimed : max;
|
|
4560
|
+
settledBase = shown.toString();
|
|
4561
|
+
settledFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, shown, quote.decimals);
|
|
4562
|
+
} catch (e36) {
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
const record = {
|
|
4566
|
+
url: quote.url,
|
|
4567
|
+
host: hostOf2(quote.url),
|
|
4568
|
+
network: quote.network,
|
|
4569
|
+
asset: quote.asset,
|
|
4570
|
+
amountBase,
|
|
4571
|
+
amountFormatted,
|
|
4572
|
+
...settledBase !== void 0 ? { settledBase, settledFormatted } : {},
|
|
4573
|
+
...quote.symbol ? { symbol: quote.symbol } : {},
|
|
4574
|
+
decimals: quote.decimals,
|
|
4575
|
+
...denom ? { denom } : {},
|
|
4576
|
+
ref,
|
|
4577
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4578
|
+
};
|
|
4579
|
+
this.ledger.record(record, quote.decimals, denom);
|
|
4580
|
+
const budget = this.budget();
|
|
4581
|
+
if (this.opts.onSpend) {
|
|
4582
|
+
try {
|
|
4583
|
+
this.opts.onSpend(record, budget);
|
|
4584
|
+
} catch (e37) {
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
this.emitThresholds(budget);
|
|
4588
|
+
}
|
|
4589
|
+
/**
|
|
4590
|
+
* Emit a `budget-threshold` event for each cap whose used-fraction just reached
|
|
4591
|
+
* `policy.warnAtFraction` — the early warning before a hard decline. Fires ONCE per
|
|
4592
|
+
* crossing per cap (deduped on the shared ledger via `markWarned`, so a cross-chain
|
|
4593
|
+
* threshold fires once for the whole budget, not once per chain). No-op when no
|
|
4594
|
+
* `warnAtFraction` is set. Reads the just-computed {@link SessionBudget}; isolated (safeEmit).
|
|
4595
|
+
*/
|
|
4596
|
+
emitThresholds(budget) {
|
|
4597
|
+
const frac = _optionalChain([this, 'access', _68 => _68.opts, 'access', _69 => _69.policy, 'optionalAccess', _70 => _70.warnAtFraction]);
|
|
4598
|
+
if (frac === void 0) return;
|
|
4599
|
+
const fire = (scope, label, spentFormatted, capFormatted, fraction) => {
|
|
4600
|
+
if (fraction < frac) return;
|
|
4601
|
+
if (!this.ledger.markWarned(`${scope}:${label}`)) return;
|
|
4602
|
+
this.safeEmit({ kind: "budget-threshold", scope, label, spentFormatted, capFormatted, fraction });
|
|
4603
|
+
};
|
|
4604
|
+
for (const r of budget.byAsset) {
|
|
4605
|
+
if (r.capBase === void 0) continue;
|
|
4606
|
+
const cap = BigInt(r.capBase);
|
|
4607
|
+
const spent = BigInt(r.spentBase);
|
|
4608
|
+
const fraction = cap === 0n ? spent > 0n ? 1 : 0 : Number(spent * 1000000n / cap) / 1e6;
|
|
4609
|
+
fire(
|
|
4610
|
+
"asset",
|
|
4611
|
+
`${_nullishCoalesce(r.symbol, () => ( r.asset))} on ${r.network}`,
|
|
4612
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, spent, r.decimals),
|
|
4613
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, cap, r.decimals),
|
|
4614
|
+
fraction
|
|
4615
|
+
);
|
|
4616
|
+
}
|
|
4617
|
+
for (const d of budget.byDenom) {
|
|
4618
|
+
fire("denom", d.denom, d.spentFormatted, d.capFormatted, d.fraction);
|
|
4619
|
+
}
|
|
4620
|
+
const policy = this.opts.policy;
|
|
4621
|
+
if (_optionalChain([policy, 'optionalAccess', _71 => _71.windowTotal]) !== void 0 && policy.windowSeconds !== void 0) {
|
|
4622
|
+
const since = Date.now() - policy.windowSeconds * 1e3;
|
|
4623
|
+
for (const r of budget.byAsset) {
|
|
4624
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.windowTotal, r.decimals);
|
|
4625
|
+
const spent = this.ledger.totalSince(r.network, r.asset, since);
|
|
4626
|
+
const fraction = cap === 0n ? spent > 0n ? 1 : 0 : Number(spent * 1000000n / cap) / 1e6;
|
|
4627
|
+
fire(
|
|
4628
|
+
"window",
|
|
4629
|
+
`${_nullishCoalesce(r.symbol, () => ( r.asset))} on ${r.network}`,
|
|
4630
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, spent, r.decimals),
|
|
4631
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, cap, r.decimals),
|
|
4632
|
+
fraction
|
|
4633
|
+
);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
const c = budget.counts;
|
|
4637
|
+
if (c.lifetimeCap !== void 0 && c.lifetimeCap > 0) {
|
|
4638
|
+
fire("count", "maxPayments", String(c.settled), String(c.lifetimeCap), c.settled / c.lifetimeCap);
|
|
4639
|
+
}
|
|
4640
|
+
if (c.windowCap !== void 0 && c.windowCap > 0 && c.windowSettled !== void 0) {
|
|
4641
|
+
fire(
|
|
4642
|
+
"window-count",
|
|
4643
|
+
"maxPaymentsPerWindow",
|
|
4644
|
+
String(c.windowSettled),
|
|
4645
|
+
String(c.windowCap),
|
|
4646
|
+
c.windowSettled / c.windowCap
|
|
4647
|
+
);
|
|
4648
|
+
}
|
|
3549
4649
|
}
|
|
3550
4650
|
async payAndConfirm(net, wallet, accept) {
|
|
3551
4651
|
if (!this.supportsNetwork(net, accept.network)) {
|
|
3552
|
-
throw new (0,
|
|
4652
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
|
|
3553
4653
|
`Challenge expects ${accept.network} but client is on ${net.network}.`
|
|
3554
4654
|
);
|
|
3555
4655
|
}
|
|
3556
4656
|
const ref = await net.send(wallet, accept);
|
|
3557
4657
|
this.safeEmit({ kind: "payment-broadcast", ref });
|
|
3558
4658
|
try {
|
|
3559
|
-
const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access',
|
|
4659
|
+
const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access', _72 => _72.extra, 'optionalAccess', _73 => _73.minConfirmations]), () => ( 1)));
|
|
3560
4660
|
this.safeEmit({
|
|
3561
4661
|
kind: "payment-confirmed",
|
|
3562
4662
|
ref,
|
|
@@ -3576,9 +4676,9 @@ var PipRailClient = (_class2 = class {
|
|
|
3576
4676
|
const signature = {
|
|
3577
4677
|
x402Version: 2,
|
|
3578
4678
|
accepted: accept,
|
|
3579
|
-
payload: { nonce: _optionalChain([accept, 'access',
|
|
4679
|
+
payload: { nonce: _optionalChain([accept, 'access', _74 => _74.extra, 'optionalAccess', _75 => _75.nonce]), txHash: ref }
|
|
3580
4680
|
};
|
|
3581
|
-
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess',
|
|
4681
|
+
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _76 => _76.headers]));
|
|
3582
4682
|
headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
|
|
3583
4683
|
let lastResponse = null;
|
|
3584
4684
|
let lastReason = null;
|
|
@@ -3593,7 +4693,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3593
4693
|
() => timeoutController.abort(),
|
|
3594
4694
|
this.retryTimeoutMs
|
|
3595
4695
|
);
|
|
3596
|
-
const signal = _optionalChain([originalInit, 'optionalAccess',
|
|
4696
|
+
const signal = _optionalChain([originalInit, 'optionalAccess', _77 => _77.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
|
|
3597
4697
|
try {
|
|
3598
4698
|
lastResponse = await fetch(url, {
|
|
3599
4699
|
..._nullishCoalesce(originalInit, () => ( {})),
|
|
@@ -3602,7 +4702,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3602
4702
|
});
|
|
3603
4703
|
} catch (err) {
|
|
3604
4704
|
if (timeoutController.signal.aborted) {
|
|
3605
|
-
throw new (0,
|
|
4705
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
|
|
3606
4706
|
`Server did not respond within ${this.retryTimeoutMs}ms after broadcasting payment ${ref}. Re-verify or re-submit ref=${ref} \u2014 do NOT re-pay.`,
|
|
3607
4707
|
{ cause: err, ref }
|
|
3608
4708
|
);
|
|
@@ -3613,6 +4713,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3613
4713
|
}
|
|
3614
4714
|
if (lastResponse.status !== 402) {
|
|
3615
4715
|
const receipt = parseReceipt(lastResponse);
|
|
4716
|
+
this.captureReceipt(lastResponse, url);
|
|
3616
4717
|
this.safeEmit({ kind: "payment-settled", receipt });
|
|
3617
4718
|
return lastResponse;
|
|
3618
4719
|
}
|
|
@@ -3625,42 +4726,126 @@ var PipRailClient = (_class2 = class {
|
|
|
3625
4726
|
reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`,
|
|
3626
4727
|
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
3627
4728
|
});
|
|
3628
|
-
throw new (0,
|
|
4729
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
3629
4730
|
`Server still returned 402 after ${attempts} attempt(s) with on-chain proof ref=${ref}${unconfirmedNote}. Last server rejection: ${why}. Re-verify or re-submit ref=${ref} before retrying \u2014 never re-pay (it would double-spend).`,
|
|
3630
4731
|
{ ref }
|
|
3631
4732
|
);
|
|
3632
4733
|
}
|
|
3633
4734
|
/**
|
|
3634
|
-
* Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative
|
|
3635
|
-
* path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE
|
|
3636
|
-
* (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS
|
|
3637
|
-
* it synchronously, so a blind re-POST of a still-in-flight authorization could
|
|
3638
|
-
* double-BROADCAST it. Hence, unlike the onchain-proof loop:
|
|
3639
|
-
*
|
|
3640
|
-
* • sign exactly once — reuse the SAME header on every retry, never re-sign;
|
|
3641
|
-
* • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded
|
|
3642
|
-
* well under `maxTimeoutSeconds` so the loop can't outlive the authorization;
|
|
3643
|
-
* • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the
|
|
3644
|
-
* nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay);
|
|
3645
|
-
* • a 5xx → return as-is (server settle failure; the authorization stays valid +
|
|
3646
|
-
* its nonce unused) — no settled event, no spend;
|
|
3647
|
-
* • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend;
|
|
3648
|
-
* • the spend is recorded EXACTLY ONCE, on an affirmative settlement only.
|
|
4735
|
+
* Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative
|
|
4736
|
+
* path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE
|
|
4737
|
+
* (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS
|
|
4738
|
+
* it synchronously, so a blind re-POST of a still-in-flight authorization could
|
|
4739
|
+
* double-BROADCAST it. Hence, unlike the onchain-proof loop:
|
|
4740
|
+
*
|
|
4741
|
+
* • sign exactly once — reuse the SAME header on every retry, never re-sign;
|
|
4742
|
+
* • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded
|
|
4743
|
+
* well under `maxTimeoutSeconds` so the loop can't outlive the authorization;
|
|
4744
|
+
* • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the
|
|
4745
|
+
* nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay);
|
|
4746
|
+
* • a 5xx → return as-is (server settle failure; the authorization stays valid +
|
|
4747
|
+
* its nonce unused) — no settled event, no spend;
|
|
4748
|
+
* • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend;
|
|
4749
|
+
* • the spend is recorded EXACTLY ONCE, on an affirmative settlement only.
|
|
4750
|
+
*/
|
|
4751
|
+
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
4752
|
+
if (!net.payExact) {
|
|
4753
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
4754
|
+
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana, Algorand + NEAR today).`
|
|
4755
|
+
);
|
|
4756
|
+
}
|
|
4757
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _78 => _78.signal]));
|
|
4758
|
+
const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
|
|
4759
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _79 => _79.headers]));
|
|
4760
|
+
headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
|
|
4761
|
+
const rejectDefinitive = (why2) => {
|
|
4762
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})`, code: why2 });
|
|
4763
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
4764
|
+
`exact: the facilitator rejected the payment (${why2}). Fix the cause, then re-present the SAME signed authorization (nonce=${nonce}) \u2014 do NOT re-sign a fresh nonce. ref=${nonce}.`,
|
|
4765
|
+
{ ref: nonce }
|
|
4766
|
+
);
|
|
4767
|
+
};
|
|
4768
|
+
const deadline = Date.now() + Math.max(1, Math.floor(accept.maxTimeoutSeconds / 2)) * 1e3;
|
|
4769
|
+
const maxAttempts = Math.min(this.maxRetries, 3);
|
|
4770
|
+
let lastReason = null;
|
|
4771
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
4772
|
+
if (attempt > 0) {
|
|
4773
|
+
if (Date.now() >= deadline) break;
|
|
4774
|
+
await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
|
|
4775
|
+
}
|
|
4776
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _80 => _80.signal]));
|
|
4777
|
+
const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
|
|
4778
|
+
if (budget <= 0) break;
|
|
4779
|
+
const timeoutController = new AbortController();
|
|
4780
|
+
const timeoutId = setTimeout(() => timeoutController.abort(), budget);
|
|
4781
|
+
const signal = _optionalChain([init, 'optionalAccess', _81 => _81.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
|
|
4782
|
+
let response;
|
|
4783
|
+
try {
|
|
4784
|
+
response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
|
|
4785
|
+
} catch (err) {
|
|
4786
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
|
|
4787
|
+
`exact: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The facilitator may have already settled it \u2014 verify on-chain with authorizationState(${payerFrom}, ${nonce}) before re-presenting; do NOT re-pay.`,
|
|
4788
|
+
{ cause: err, ref: nonce }
|
|
4789
|
+
);
|
|
4790
|
+
} finally {
|
|
4791
|
+
clearTimeout(timeoutId);
|
|
4792
|
+
}
|
|
4793
|
+
const settle = parseSettleResponse(response);
|
|
4794
|
+
if (response.status === 402) {
|
|
4795
|
+
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the facilitator reported success:false")));
|
|
4796
|
+
lastReason = await _asyncNullishCoalesce(await readInvalidReason(response), async () => ( lastReason));
|
|
4797
|
+
continue;
|
|
4798
|
+
}
|
|
4799
|
+
if (response.ok && !(settle && settle.success === false)) {
|
|
4800
|
+
const receipt = parseReceipt(response);
|
|
4801
|
+
this.captureReceipt(response, url);
|
|
4802
|
+
this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
|
|
4803
|
+
const ref = _optionalChain([settle, 'optionalAccess', _82 => _82.transaction]) || _optionalChain([receipt, 'optionalAccess', _83 => _83.transaction]) || `eip3009-nonce:${nonce}`;
|
|
4804
|
+
this.recordSpend(quote, ref);
|
|
4805
|
+
return response;
|
|
4806
|
+
}
|
|
4807
|
+
if (response.status >= 500) {
|
|
4808
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
4809
|
+
return response;
|
|
4810
|
+
}
|
|
4811
|
+
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the facilitator reported success:false")));
|
|
4812
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
4813
|
+
return response;
|
|
4814
|
+
}
|
|
4815
|
+
const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
|
|
4816
|
+
this.safeEmit({
|
|
4817
|
+
kind: "payment-failed",
|
|
4818
|
+
reason: `exact: 402 after submitting authorization nonce=${nonce} (${why})`,
|
|
4819
|
+
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
4820
|
+
});
|
|
4821
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
4822
|
+
`exact: server still returned 402 after submitting the signed authorization (nonce=${nonce}). Last rejection: ${why}. Re-present the SAME authorization \u2014 do NOT re-sign a fresh nonce; verify authorizationState(${payerFrom}, ${nonce}) first. ref=${nonce}.`,
|
|
4823
|
+
{ ref: nonce }
|
|
4824
|
+
);
|
|
4825
|
+
}
|
|
4826
|
+
/**
|
|
4827
|
+
* The standard `upto` (metered) buyer path — a near-clone of {@link payExactRail} with TWO
|
|
4828
|
+
* deltas: (1) it signs a Permit2-upto authorization for the MAX via `payUpto` + frames it
|
|
4829
|
+
* with `buildUptoSignatureHeader`; (2) it records the ACTUAL settled amount (read off the
|
|
4830
|
+
* SettleResponse's required `amount` field, via `recordSpend(quote, ref, settle.amount)`) in
|
|
4831
|
+
* the ledger — the budget gated on the MAX, the ledger records the ACTUAL. A server that omits
|
|
4832
|
+
* `settle.amount` FAILS SAFE to the MAX (over-counts, never under-counts). The buyer SIGNS, the
|
|
4833
|
+
* merchant self-settles — the buyer never broadcasts.
|
|
3649
4834
|
*/
|
|
3650
|
-
async
|
|
3651
|
-
if (!net.
|
|
3652
|
-
throw new (0,
|
|
3653
|
-
`the ${net.family} family can't pay a standard '
|
|
4835
|
+
async payUptoRail(net, wallet, accept, url, init, quote) {
|
|
4836
|
+
if (!net.payUpto) {
|
|
4837
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
4838
|
+
`the ${net.family} family can't pay a standard 'upto' rail (EVM-Permit2 only today).`
|
|
3654
4839
|
);
|
|
3655
4840
|
}
|
|
3656
|
-
throwIfAborted(_optionalChain([init, 'optionalAccess',
|
|
3657
|
-
const { payload, accepted, payerFrom, nonce } = await net.
|
|
3658
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
3659
|
-
headers.set(HEADER_SIGNATURE,
|
|
4841
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _84 => _84.signal]));
|
|
4842
|
+
const { payload, accepted, payerFrom, nonce } = await net.payUpto(wallet, accept);
|
|
4843
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _85 => _85.headers]));
|
|
4844
|
+
headers.set(HEADER_SIGNATURE, buildUptoSignatureHeader({ accepted, payload }));
|
|
3660
4845
|
const rejectDefinitive = (why2) => {
|
|
3661
|
-
this.safeEmit({ kind: "payment-failed", reason: `
|
|
3662
|
-
throw new (0,
|
|
3663
|
-
`
|
|
4846
|
+
this.safeEmit({ kind: "payment-failed", reason: `upto: facilitator rejected nonce=${nonce} (${why2})`, code: why2 });
|
|
4847
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
4848
|
+
`upto: the server rejected the payment (${why2}). Fix the cause, then re-present the SAME signed authorization (nonce=${nonce}) \u2014 do NOT re-sign a fresh nonce. ref=${nonce}.`,
|
|
3664
4849
|
{ ref: nonce }
|
|
3665
4850
|
);
|
|
3666
4851
|
};
|
|
@@ -3672,18 +4857,18 @@ var PipRailClient = (_class2 = class {
|
|
|
3672
4857
|
if (Date.now() >= deadline) break;
|
|
3673
4858
|
await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
|
|
3674
4859
|
}
|
|
3675
|
-
throwIfAborted(_optionalChain([init, 'optionalAccess',
|
|
4860
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _86 => _86.signal]));
|
|
3676
4861
|
const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
|
|
3677
4862
|
if (budget <= 0) break;
|
|
3678
4863
|
const timeoutController = new AbortController();
|
|
3679
4864
|
const timeoutId = setTimeout(() => timeoutController.abort(), budget);
|
|
3680
|
-
const signal = _optionalChain([init, 'optionalAccess',
|
|
4865
|
+
const signal = _optionalChain([init, 'optionalAccess', _87 => _87.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
|
|
3681
4866
|
let response;
|
|
3682
4867
|
try {
|
|
3683
4868
|
response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
|
|
3684
4869
|
} catch (err) {
|
|
3685
|
-
throw new (0,
|
|
3686
|
-
`
|
|
4870
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
|
|
4871
|
+
`upto: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The merchant may have already settled it \u2014 verify on-chain before re-presenting; do NOT re-pay.`,
|
|
3687
4872
|
{ cause: err, ref: nonce }
|
|
3688
4873
|
);
|
|
3689
4874
|
} finally {
|
|
@@ -3691,46 +4876,48 @@ var PipRailClient = (_class2 = class {
|
|
|
3691
4876
|
}
|
|
3692
4877
|
const settle = parseSettleResponse(response);
|
|
3693
4878
|
if (response.status === 402) {
|
|
3694
|
-
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the
|
|
4879
|
+
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the server reported success:false")));
|
|
3695
4880
|
lastReason = await _asyncNullishCoalesce(await readInvalidReason(response), async () => ( lastReason));
|
|
3696
4881
|
continue;
|
|
3697
4882
|
}
|
|
3698
4883
|
if (response.ok && !(settle && settle.success === false)) {
|
|
3699
4884
|
const receipt = parseReceipt(response);
|
|
4885
|
+
this.captureReceipt(response, url);
|
|
3700
4886
|
this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
|
|
3701
|
-
const ref = _optionalChain([settle, 'optionalAccess',
|
|
3702
|
-
|
|
4887
|
+
const ref = _optionalChain([settle, 'optionalAccess', _88 => _88.transaction]) || _optionalChain([receipt, 'optionalAccess', _89 => _89.transaction]) || `upto-nonce:${nonce}`;
|
|
4888
|
+
const settledAmount = _nullishCoalesce(_optionalChain([settle, 'optionalAccess', _90 => _90.amount]), () => ( _optionalChain([receipt, 'optionalAccess', _91 => _91.amount])));
|
|
4889
|
+
this.recordSpend(quote, ref, settledAmount);
|
|
3703
4890
|
return response;
|
|
3704
4891
|
}
|
|
3705
4892
|
if (response.status >= 500) {
|
|
3706
|
-
this.safeEmit({ kind: "payment-failed", reason: `
|
|
4893
|
+
this.safeEmit({ kind: "payment-failed", reason: `upto: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
3707
4894
|
return response;
|
|
3708
4895
|
}
|
|
3709
|
-
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the
|
|
3710
|
-
this.safeEmit({ kind: "payment-failed", reason: `
|
|
4896
|
+
if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the server reported success:false")));
|
|
4897
|
+
this.safeEmit({ kind: "payment-failed", reason: `upto: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
3711
4898
|
return response;
|
|
3712
4899
|
}
|
|
3713
4900
|
const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
|
|
3714
4901
|
this.safeEmit({
|
|
3715
4902
|
kind: "payment-failed",
|
|
3716
|
-
reason: `
|
|
4903
|
+
reason: `upto: 402 after submitting authorization nonce=${nonce} (${why})`,
|
|
3717
4904
|
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
3718
4905
|
});
|
|
3719
|
-
throw new (0,
|
|
3720
|
-
`
|
|
4906
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
4907
|
+
`upto: server still returned 402 after submitting the signed authorization (nonce=${nonce}). Last rejection: ${why}. Re-present the SAME authorization \u2014 do NOT re-sign a fresh nonce. ref=${nonce}.`,
|
|
3721
4908
|
{ ref: nonce }
|
|
3722
4909
|
);
|
|
3723
4910
|
}
|
|
3724
4911
|
}, _class2);
|
|
3725
4912
|
function throwIfAborted(signal) {
|
|
3726
|
-
if (_optionalChain([signal, 'optionalAccess',
|
|
4913
|
+
if (_optionalChain([signal, 'optionalAccess', _92 => _92.aborted])) {
|
|
3727
4914
|
throw _nullishCoalesce(signal.reason, () => ( new DOMException("This operation was aborted.", "AbortError")));
|
|
3728
4915
|
}
|
|
3729
4916
|
}
|
|
3730
4917
|
function safeBig(s) {
|
|
3731
4918
|
try {
|
|
3732
4919
|
return BigInt(s);
|
|
3733
|
-
} catch (
|
|
4920
|
+
} catch (e38) {
|
|
3734
4921
|
return 0n;
|
|
3735
4922
|
}
|
|
3736
4923
|
}
|
|
@@ -3793,10 +4980,10 @@ function buildFundingHint(options, chainLabel) {
|
|
|
3793
4980
|
return `Couldn't fully read your wallet on ${chainLabel} (RPC throttled) \u2014 retry; you may already be able to pay ${target.quote.amountFormatted} ${sym}.`;
|
|
3794
4981
|
}
|
|
3795
4982
|
const parts = [];
|
|
3796
|
-
if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access',
|
|
4983
|
+
if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _93 => _93.shortfall, 'optionalAccess', _94 => _94.token])) {
|
|
3797
4984
|
parts.push(`top up ${target.shortfall.token} ${sym}`);
|
|
3798
4985
|
}
|
|
3799
|
-
if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access',
|
|
4986
|
+
if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _95 => _95.shortfall, 'optionalAccess', _96 => _96.native])) {
|
|
3800
4987
|
parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
|
|
3801
4988
|
}
|
|
3802
4989
|
return parts.length ? `Can't settle on ${chainLabel}: ${parts.join(" and ")} (to pay ${target.quote.amountFormatted} ${sym}).` : `Can't settle on ${chainLabel} for ${target.quote.amountFormatted} ${sym}.`;
|
|
@@ -3810,7 +4997,7 @@ async function planAcross(clients, url, init) {
|
|
|
3810
4997
|
const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
|
|
3811
4998
|
return {
|
|
3812
4999
|
url,
|
|
3813
|
-
network: _nullishCoalesce(_optionalChain([best, 'optionalAccess',
|
|
5000
|
+
network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _97 => _97.accept, 'access', _98 => _98.network]), () => ( live[0].network)),
|
|
3814
5001
|
status,
|
|
3815
5002
|
payable: best !== null,
|
|
3816
5003
|
best,
|
|
@@ -3829,7 +5016,7 @@ async function fetchAcross(clients, url, init) {
|
|
|
3829
5016
|
const best = rankAcross(live.map((p) => p.plan)).find((o) => o.state === "payable");
|
|
3830
5017
|
if (!best) {
|
|
3831
5018
|
const hint = mergeDeclineHint(live.map((p) => p.plan));
|
|
3832
|
-
throw new (0,
|
|
5019
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(hint || "No funded chain can settle this payment right now.");
|
|
3833
5020
|
}
|
|
3834
5021
|
const owner = live.find((p) => p.plan.options.includes(best)).client;
|
|
3835
5022
|
return owner.fetch(url, { ..._nullishCoalesce(init, () => ( {})), autoRoute: true });
|
|
@@ -3843,8 +5030,11 @@ function reasonCodeForPolicy(code) {
|
|
|
3843
5030
|
case "SESSION_EXPIRED":
|
|
3844
5031
|
return "SESSION_EXPIRED";
|
|
3845
5032
|
case "WINDOW_TOTAL":
|
|
5033
|
+
case "WINDOW_COUNT":
|
|
3846
5034
|
return "OUTSIDE_WINDOW";
|
|
3847
5035
|
case "MAX_TOTAL":
|
|
5036
|
+
case "MAX_TOTAL_DENOM":
|
|
5037
|
+
case "MAX_PAYMENTS":
|
|
3848
5038
|
return "BUDGET";
|
|
3849
5039
|
case void 0:
|
|
3850
5040
|
return void 0;
|
|
@@ -3855,7 +5045,7 @@ function reasonCodeForPolicy(code) {
|
|
|
3855
5045
|
function hostOf2(url) {
|
|
3856
5046
|
try {
|
|
3857
5047
|
return new URL(url).hostname;
|
|
3858
|
-
} catch (
|
|
5048
|
+
} catch (e39) {
|
|
3859
5049
|
return url;
|
|
3860
5050
|
}
|
|
3861
5051
|
}
|
|
@@ -3872,8 +5062,8 @@ function isReplayableBodyInit(value) {
|
|
|
3872
5062
|
async function readInvalidReason(response) {
|
|
3873
5063
|
try {
|
|
3874
5064
|
const body = await response.clone().json();
|
|
3875
|
-
const ext = _optionalChain([body, 'optionalAccess',
|
|
3876
|
-
const piprail = _optionalChain([ext, 'optionalAccess',
|
|
5065
|
+
const ext = _optionalChain([body, 'optionalAccess', _99 => _99.extensions]);
|
|
5066
|
+
const piprail = _optionalChain([ext, 'optionalAccess', _100 => _100.piprail]);
|
|
3877
5067
|
if (piprail && typeof piprail.code === "string") {
|
|
3878
5068
|
return {
|
|
3879
5069
|
error: piprail.code,
|
|
@@ -3892,16 +5082,52 @@ async function readInvalidReason(response) {
|
|
|
3892
5082
|
detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
|
|
3893
5083
|
};
|
|
3894
5084
|
}
|
|
3895
|
-
} catch (
|
|
5085
|
+
} catch (e40) {
|
|
3896
5086
|
}
|
|
3897
5087
|
const settle = parseSettleResponse(response);
|
|
3898
|
-
if (_optionalChain([settle, 'optionalAccess',
|
|
5088
|
+
if (_optionalChain([settle, 'optionalAccess', _101 => _101.errorReason])) return { error: settle.errorReason, detail: "" };
|
|
3899
5089
|
return null;
|
|
3900
5090
|
}
|
|
5091
|
+
var EVM_PRESET_FOR_CHAINID = {
|
|
5092
|
+
1: "ethereum",
|
|
5093
|
+
8453: "base",
|
|
5094
|
+
137: "polygon",
|
|
5095
|
+
42161: "arbitrum",
|
|
5096
|
+
10: "optimism",
|
|
5097
|
+
43114: "avalanche",
|
|
5098
|
+
56: "bnb"
|
|
5099
|
+
};
|
|
5100
|
+
function chainSelectorForNetwork(network, rpcUrl) {
|
|
5101
|
+
const chainId = chainIdFromNetwork(network);
|
|
5102
|
+
if (chainId !== null) {
|
|
5103
|
+
const preset = EVM_PRESET_FOR_CHAINID[chainId];
|
|
5104
|
+
if (preset) return preset;
|
|
5105
|
+
return { id: chainId, rpcUrl: _nullishCoalesce(rpcUrl, () => ( "")) };
|
|
5106
|
+
}
|
|
5107
|
+
const namespace = _nullishCoalesce(network.split(":")[0], () => ( ""));
|
|
5108
|
+
return namespace === "tvm" ? "ton" : namespace;
|
|
5109
|
+
}
|
|
5110
|
+
function receiptAgeSeconds(verifiedAt) {
|
|
5111
|
+
const t = Date.parse(verifiedAt);
|
|
5112
|
+
if (Number.isNaN(t)) return 0;
|
|
5113
|
+
return Math.max(0, Math.round((Date.now() - t) / 1e3));
|
|
5114
|
+
}
|
|
5115
|
+
function receiptIssuedAtSeconds(verifiedAt) {
|
|
5116
|
+
const t = Date.parse(verifiedAt);
|
|
5117
|
+
if (Number.isNaN(t)) return 0;
|
|
5118
|
+
return Math.floor(t / 1e3);
|
|
5119
|
+
}
|
|
5120
|
+
function sameAddress(a, b) {
|
|
5121
|
+
return typeof a === "string" && typeof b === "string" && a.toLowerCase() === b.toLowerCase();
|
|
5122
|
+
}
|
|
3901
5123
|
|
|
3902
5124
|
// src/payer.ts
|
|
3903
5125
|
var MultiChainPayer = class _MultiChainPayer {
|
|
3904
5126
|
|
|
5127
|
+
/** True when every client shares ONE ledger (the `fromWallets` path) — so spend +
|
|
5128
|
+
* budget are read from that single source, never concatenated (which would
|
|
5129
|
+
* double-count a shared ledger). */
|
|
5130
|
+
|
|
3905
5131
|
/**
|
|
3906
5132
|
* Wrap an explicit, ordered set of single-chain clients — use this when a client
|
|
3907
5133
|
* needs full control (e.g. a custom EVM chain configured by a viem `Chain`). The
|
|
@@ -3909,12 +5135,18 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
3909
5135
|
* at MOST one client per chain — two clients on the SAME network would double-count in
|
|
3910
5136
|
* `spent()`/`budget()` and waste a plan round-trip (`fromWallets` can't produce this).
|
|
3911
5137
|
* For the common case, prefer {@link MultiChainPayer.fromWallets}.
|
|
5138
|
+
*
|
|
5139
|
+
* For a cross-chain grand total / count cap via this path, build the clients with a
|
|
5140
|
+
* SHARED `ledger` (`new SpendLedger(store?)` passed to each client's `ledger` option)
|
|
5141
|
+
* and set `opts.shared = true` so reads come from that one ledger. `fromWallets` does
|
|
5142
|
+
* this for you.
|
|
3912
5143
|
*/
|
|
3913
|
-
constructor(clients) {
|
|
5144
|
+
constructor(clients, opts = {}) {
|
|
3914
5145
|
if (clients.length === 0) {
|
|
3915
5146
|
throw new TypeError("MultiChainPayer needs at least one PipRailClient.");
|
|
3916
5147
|
}
|
|
3917
5148
|
this._clients = [...clients];
|
|
5149
|
+
this._shared = _nullishCoalesce(opts.shared, () => ( false));
|
|
3918
5150
|
}
|
|
3919
5151
|
/**
|
|
3920
5152
|
* Build one client per funded chain from a `{ chain → wallet }` map — the
|
|
@@ -3929,30 +5161,36 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
3929
5161
|
* solana: { key: process.env.SOLANA_SECRET! },
|
|
3930
5162
|
* xrpl: { key: process.env.XRPL_SEED! },
|
|
3931
5163
|
* },
|
|
3932
|
-
*
|
|
5164
|
+
* // ONE budget across all three chains: $20 total + at most 100 payments.
|
|
5165
|
+
* policy: { maxAmount: '1.00', maxTotalPerDenom: { USD: '20.00' }, maxPayments: 100 },
|
|
3933
5166
|
* })
|
|
3934
5167
|
* const res = await payer.get('https://api.example.com/paid') // pays on the first funded chain that can settle
|
|
3935
5168
|
* ```
|
|
5169
|
+
*
|
|
5170
|
+
* Every client shares ONE {@link SpendLedger} (optionally backed by `spendStore`), so
|
|
5171
|
+
* the cross-token grand total + the count caps span all chains as a single budget.
|
|
3936
5172
|
*/
|
|
3937
5173
|
static fromWallets(opts) {
|
|
3938
5174
|
const entries = Object.entries(opts.wallets);
|
|
3939
5175
|
if (entries.length === 0) {
|
|
3940
5176
|
throw new TypeError("MultiChainPayer.fromWallets needs at least one wallet.");
|
|
3941
5177
|
}
|
|
5178
|
+
const ledger = new SpendLedger(opts.spendStore);
|
|
3942
5179
|
const clients = entries.map(
|
|
3943
5180
|
([chain, wallet]) => new PipRailClient({
|
|
3944
5181
|
chain,
|
|
3945
5182
|
wallet,
|
|
5183
|
+
ledger,
|
|
3946
5184
|
...opts.policy ? { policy: opts.policy } : {},
|
|
3947
5185
|
...opts.schemes ? { schemes: opts.schemes } : {},
|
|
3948
|
-
..._optionalChain([opts, 'access',
|
|
5186
|
+
..._optionalChain([opts, 'access', _102 => _102.rpcUrls, 'optionalAccess', _103 => _103[chain]]) ? { rpcUrl: opts.rpcUrls[chain] } : {},
|
|
3949
5187
|
...opts.onBeforePay ? { onBeforePay: opts.onBeforePay } : {},
|
|
3950
5188
|
...opts.onEvent ? { onEvent: opts.onEvent } : {},
|
|
3951
5189
|
...opts.maxPaymentRetries != null ? { maxPaymentRetries: opts.maxPaymentRetries } : {},
|
|
3952
5190
|
...opts.retryTimeoutMs != null ? { retryTimeoutMs: opts.retryTimeoutMs } : {}
|
|
3953
5191
|
})
|
|
3954
5192
|
);
|
|
3955
|
-
return new _MultiChainPayer(clients);
|
|
5193
|
+
return new _MultiChainPayer(clients, { shared: true });
|
|
3956
5194
|
}
|
|
3957
5195
|
/** The underlying single-chain clients, in preference order. Reach for one of
|
|
3958
5196
|
* these for chain-specific reads (`estimateCost`, `discoverySigner`, per-chain
|
|
@@ -4003,7 +5241,7 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
4003
5241
|
* {@link PipRailClient.post}.
|
|
4004
5242
|
*/
|
|
4005
5243
|
post(url, body, init) {
|
|
4006
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
5244
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _104 => _104.headers]));
|
|
4007
5245
|
let payload;
|
|
4008
5246
|
if (body === void 0 || body === null) {
|
|
4009
5247
|
payload = void 0;
|
|
@@ -4046,28 +5284,50 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
4046
5284
|
register(url, opts = {}) {
|
|
4047
5285
|
return this._clients[0].register(url, opts);
|
|
4048
5286
|
}
|
|
4049
|
-
/**
|
|
4050
|
-
*
|
|
5287
|
+
/**
|
|
5288
|
+
* Aggregate spend across every chain. With a SHARED ledger (`fromWallets`), this reads
|
|
5289
|
+
* that single ledger ONCE (its records already span every chain) — so the count and the
|
|
5290
|
+
* cross-token `byDenom` grand total are correct and never double-counted. With
|
|
5291
|
+
* independent clients (the explicit constructor), it concatenates: counts summed,
|
|
5292
|
+
* per-(network,asset) `byAsset` + `byDenom` + records concatenated.
|
|
5293
|
+
*/
|
|
4051
5294
|
spent() {
|
|
5295
|
+
if (this._shared) return this._clients[0].spent();
|
|
4052
5296
|
const summaries = this._clients.map((c) => c.spent());
|
|
4053
5297
|
return {
|
|
4054
5298
|
count: summaries.reduce((n, s) => n + s.count, 0),
|
|
4055
5299
|
byAsset: summaries.flatMap((s) => s.byAsset),
|
|
5300
|
+
byDenom: summaries.flatMap((s) => s.byDenom),
|
|
4056
5301
|
records: summaries.flatMap((s) => s.records)
|
|
4057
5302
|
};
|
|
4058
5303
|
}
|
|
4059
|
-
/**
|
|
4060
|
-
*
|
|
4061
|
-
*
|
|
4062
|
-
*
|
|
5304
|
+
/**
|
|
5305
|
+
* A merged budget view. With a SHARED ledger (`fromWallets`) it's the single source of
|
|
5306
|
+
* truth — `byAsset`/`byDenom`/`counts` already span every chain, so the grand-total and
|
|
5307
|
+
* count leashes read as ONE budget. With independent clients it merges: per-(network,
|
|
5308
|
+
* asset) rows + per-denom rows concatenated, counts summed, and the MOST-RESTRICTIVE
|
|
5309
|
+
* session time envelope (the soonest deadline wins). Per-chain detail is on each
|
|
5310
|
+
* `clients[i].budget()`.
|
|
5311
|
+
*/
|
|
4063
5312
|
budget() {
|
|
5313
|
+
if (this._shared) return this._clients[0].budget();
|
|
4064
5314
|
const budgets = this._clients.map((c) => c.budget());
|
|
4065
5315
|
const session = budgets.map((b) => b.session).reduce((soonest, s) => {
|
|
4066
5316
|
if (soonest.secondsRemaining == null) return s;
|
|
4067
5317
|
if (s.secondsRemaining == null) return soonest;
|
|
4068
5318
|
return s.secondsRemaining < soonest.secondsRemaining ? s : soonest;
|
|
4069
5319
|
});
|
|
4070
|
-
return {
|
|
5320
|
+
return {
|
|
5321
|
+
session,
|
|
5322
|
+
byAsset: budgets.flatMap((b) => b.byAsset),
|
|
5323
|
+
byDenom: budgets.flatMap((b) => b.byDenom),
|
|
5324
|
+
counts: { settled: budgets.reduce((n, b) => n + b.counts.settled, 0) }
|
|
5325
|
+
};
|
|
5326
|
+
}
|
|
5327
|
+
/** The CONFIGURED spend policy (the shared policy `fromWallets` applies to every chain;
|
|
5328
|
+
* the first client's for the explicit constructor). `undefined` when none is set. */
|
|
5329
|
+
policy() {
|
|
5330
|
+
return this._clients[0].policy();
|
|
4071
5331
|
}
|
|
4072
5332
|
};
|
|
4073
5333
|
function isBodyInit(value) {
|
|
@@ -4092,7 +5352,13 @@ var BRAND = {
|
|
|
4092
5352
|
};
|
|
4093
5353
|
var WHAT = 'This is an x402 "402 Payment Required" endpoint. Pay one of the offered rails to access it.';
|
|
4094
5354
|
function howFor(scheme) {
|
|
4095
|
-
|
|
5355
|
+
if (scheme === "exact") {
|
|
5356
|
+
return "Standard x402 exact rail \u2014 sign an EIP-3009 / Permit2 / SVM authorization; any stock x402 client (e.g. @x402/fetch) can pay this.";
|
|
5357
|
+
}
|
|
5358
|
+
if (scheme === "upto") {
|
|
5359
|
+
return "Standard x402 upto (metered) rail \u2014 sign a Permit2 authorization for the MAX amount; the server settles the ACTUAL (\u2264 max) after serving. BUDGET AGAINST THE MAX \u2014 the server MAY fully charge it. EVM-Permit2 only; enable with schemes:['upto'].";
|
|
5360
|
+
}
|
|
5361
|
+
return "Pay this amount on-chain to payTo, then resubmit with a payment-signature header carrying the proof ref + nonce. Easiest with @piprail/sdk (see sdk.install).";
|
|
4096
5362
|
}
|
|
4097
5363
|
function railOf(a) {
|
|
4098
5364
|
const extra = _nullishCoalesce(a.extra, () => ( {}));
|
|
@@ -4119,19 +5385,20 @@ function buildSelfDescription(input) {
|
|
|
4119
5385
|
mcp: { run: BRAND.mcpRun, tool: "piprail_pay_request" },
|
|
4120
5386
|
docs: { home: BRAND.home, agents: BRAND.docs, pay: BRAND.payDocs },
|
|
4121
5387
|
discovery: { openapi: "/openapi.json", wellKnown: "/.well-known/x402" },
|
|
5388
|
+
...input.verifiableReceipts ? { verifiableReceipts: true } : {},
|
|
4122
5389
|
...input.instruction ? { instruction: input.instruction } : {}
|
|
4123
5390
|
};
|
|
4124
5391
|
}
|
|
4125
5392
|
function buildEndpointInfo(input) {
|
|
4126
5393
|
const d = input.descriptor;
|
|
4127
|
-
const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
4128
|
-
const hasInput = _optionalChain([d, 'optionalAccess',
|
|
5394
|
+
const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess', _105 => _105.summary]), () => ( input.description));
|
|
5395
|
+
const hasInput = _optionalChain([d, 'optionalAccess', _106 => _106.queryParams]) && Object.keys(d.queryParams).length > 0;
|
|
4129
5396
|
const endpoint = {
|
|
4130
5397
|
...summary ? { summary } : {},
|
|
4131
|
-
..._optionalChain([d, 'optionalAccess',
|
|
5398
|
+
..._optionalChain([d, 'optionalAccess', _107 => _107.method]) ? { method: d.method.toUpperCase() } : {},
|
|
4132
5399
|
...input.mimeType ? { mimeType: input.mimeType } : {},
|
|
4133
5400
|
...hasInput ? { input: d.queryParams } : {},
|
|
4134
|
-
..._optionalChain([d, 'optionalAccess',
|
|
5401
|
+
..._optionalChain([d, 'optionalAccess', _108 => _108.output]) ? { output: d.output } : {}
|
|
4135
5402
|
};
|
|
4136
5403
|
return Object.keys(endpoint).length > 0 ? endpoint : void 0;
|
|
4137
5404
|
}
|
|
@@ -4149,7 +5416,7 @@ function summarizePlan(plan) {
|
|
|
4149
5416
|
return `NOT payable: ${_nullishCoalesce(plan.fundingHint, () => ( `no settleable rail on ${plan.network}`))}`;
|
|
4150
5417
|
}
|
|
4151
5418
|
function explainDecline(err) {
|
|
4152
|
-
if (!(err instanceof
|
|
5419
|
+
if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) {
|
|
4153
5420
|
return `Payment failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
4154
5421
|
}
|
|
4155
5422
|
switch (err.code) {
|
|
@@ -4172,9 +5439,12 @@ function explainDecline(err) {
|
|
|
4172
5439
|
}
|
|
4173
5440
|
function formatSpendReport(summary) {
|
|
4174
5441
|
if (summary.count === 0) return "No payments yet.";
|
|
4175
|
-
|
|
5442
|
+
const perAsset = summary.byAsset.map(
|
|
4176
5443
|
(a) => `${a.totalFormatted} ${_nullishCoalesce(a.symbol, () => ( a.asset))} on ${a.network} (${a.count} payment${a.count === 1 ? "" : "s"})`
|
|
4177
5444
|
).join("; ");
|
|
5445
|
+
if (summary.byDenom.length === 0) return perAsset;
|
|
5446
|
+
const grand = summary.byDenom.map((d) => `${d.totalFormatted} ${d.denom} total`).join("; ");
|
|
5447
|
+
return `${perAsset} \u2014 grand total: ${grand}`;
|
|
4178
5448
|
}
|
|
4179
5449
|
function describeChallenge(challenge) {
|
|
4180
5450
|
const first = challenge.accepts[0];
|
|
@@ -4215,17 +5485,21 @@ and pay with the tools below.
|
|
|
4215
5485
|
Always plan before you pay so you never commit to a payment you cannot finish.
|
|
4216
5486
|
|
|
4217
5487
|
## Gasless \u2014 the exact rail (zero gas for you)
|
|
4218
|
-
A 402 may offer up to
|
|
5488
|
+
A 402 may offer up to three rails; you don't choose per payment \u2014 the client does, automatically:
|
|
4219
5489
|
- onchain-proof (PipRail's default): you broadcast the payment yourself and pay the network gas
|
|
4220
5490
|
(the native coin \u2014 ETH/SOL/\u2026). Works on every chain.
|
|
4221
5491
|
- exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose
|
|
4222
5492
|
(e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It
|
|
4223
5493
|
works on EVM, Solana + Algorand, and the on-chain method (EIP-3009 / Permit2 / SVM / Algorand
|
|
4224
5494
|
fee-pooled group) is picked automatically.
|
|
5495
|
+
- upto (the metered/variable x402 rail, opt-in, EVM): the amount you see is a MAXIMUM \u2014 you sign
|
|
5496
|
+
a ceiling, the server meters real usage and settles the ACTUAL (<= the max). BUDGET AGAINST THE MAX:
|
|
5497
|
+
the plan/policy treat the ceiling as the spend (a server may charge up to it), so a payable plan
|
|
5498
|
+
means the MAX fits your budget; the settled actual is recorded for reconciliation.
|
|
4225
5499
|
When the exact scheme is enabled AND balance-aware routing is on, paying picks the cheapest
|
|
4226
5500
|
settleable rail \u2014 i.e. the gasless exact one. Nothing changes in your loop: quote \u2192 plan \u2192 pay is
|
|
4227
|
-
identical. The exact
|
|
4228
|
-
you can't enable
|
|
5501
|
+
identical. The exact/upto schemes are OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact,upto);
|
|
5502
|
+
you can't enable them yourself, but you can report when a 402 needs one (see UNSUPPORTED_SCHEME below).
|
|
4229
5503
|
|
|
4230
5504
|
## Reading a refusal \u2014 never crash, never double-spend
|
|
4231
5505
|
A failed pay returns a STRUCTURED object, never a thrown error you must catch:
|
|
@@ -4253,9 +5527,11 @@ Branch on \`code\` (always reliable). Key cases:
|
|
|
4253
5527
|
once (enable the exact scheme); report it, don't retry the same call blindly.
|
|
4254
5528
|
|
|
4255
5529
|
## Knowing your leash \u2014 call piprail_budget
|
|
4256
|
-
piprail_budget tells you how much budget and time you have left
|
|
4257
|
-
|
|
4258
|
-
|
|
5530
|
+
piprail_budget tells you how much budget and time you have left: per (network,
|
|
5531
|
+
asset) remaining, the cross-token GRAND TOTAL per denomination (e.g. how much
|
|
5532
|
+
USD you can still spend across every stablecoin and chain), the payment-count
|
|
5533
|
+
leash, the session time envelope, your spend so far, and the configured policy
|
|
5534
|
+
read back. Read-only; moves no funds. Use it in Mode A to self-check before paying.
|
|
4259
5535
|
|
|
4260
5536
|
## Two modes
|
|
4261
5537
|
- Mode A (headless, default): you run FREE inside a pre-set budget + time
|
|
@@ -4266,10 +5542,15 @@ Mode A to self-check before paying.
|
|
|
4266
5542
|
do NOT retry it as if it were a transient error.
|
|
4267
5543
|
|
|
4268
5544
|
## Hard facts
|
|
4269
|
-
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
5545
|
+
- Per-payment + per-(network, asset) caps always apply. A cross-token GRAND TOTAL per
|
|
5546
|
+
denomination (maxTotalPerDenom, e.g. "$20 across every USD stablecoin + chain") is
|
|
5547
|
+
OPTIONAL \u2014 it sums tokens declared as one unit, each 1:1; it is NOT a price oracle and
|
|
5548
|
+
never prices a volatile native coin. Payment-COUNT caps (maxPayments / per-window) also
|
|
5549
|
+
span every chain + token.
|
|
5550
|
+
- The time envelope lives IN-MEMORY for THIS process (resets on restart). The money + count
|
|
5551
|
+
totals also reset on restart UNLESS a durable spend store is configured \u2014 then they resume.
|
|
5552
|
+
- A refusal arrives as declined:true with a reasonCode; 'BUDGET' covers the lifetime, denom,
|
|
5553
|
+
and count caps; 'OUTSIDE_WINDOW' covers both the rolling money and rolling count windows.
|
|
4273
5554
|
`;
|
|
4274
5555
|
function agentGuide() {
|
|
4275
5556
|
return PIPRAIL_AGENT_GUIDE;
|
|
@@ -4282,19 +5563,19 @@ async function readBody(res) {
|
|
|
4282
5563
|
if (!text) return null;
|
|
4283
5564
|
try {
|
|
4284
5565
|
return JSON.parse(text);
|
|
4285
|
-
} catch (
|
|
5566
|
+
} catch (e41) {
|
|
4286
5567
|
return text;
|
|
4287
5568
|
}
|
|
4288
5569
|
}
|
|
4289
5570
|
function toToolError(err) {
|
|
4290
|
-
if (!(err instanceof
|
|
5571
|
+
if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) throw err;
|
|
4291
5572
|
const out = {
|
|
4292
5573
|
ok: false,
|
|
4293
5574
|
code: err.code,
|
|
4294
5575
|
reason: err.message,
|
|
4295
5576
|
explain: explainDecline(err)
|
|
4296
5577
|
};
|
|
4297
|
-
if (err instanceof
|
|
5578
|
+
if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
|
|
4298
5579
|
out.declined = true;
|
|
4299
5580
|
if (err.reasonCode) out.reasonCode = err.reasonCode;
|
|
4300
5581
|
}
|
|
@@ -4501,21 +5782,23 @@ function paymentTools(client) {
|
|
|
4501
5782
|
}
|
|
4502
5783
|
res = await client.fetch(url, { method, headers, body });
|
|
4503
5784
|
}
|
|
5785
|
+
const verifiable = parseReceiptExtension(res);
|
|
4504
5786
|
return {
|
|
4505
5787
|
status: res.status,
|
|
4506
5788
|
ok: res.ok,
|
|
4507
5789
|
body: await readBody(res),
|
|
4508
|
-
receipt: parseReceipt(res)
|
|
5790
|
+
receipt: parseReceipt(res),
|
|
5791
|
+
...verifiable ? { verifiableReceipt: { ...verifiable, resource: { url } } } : {}
|
|
4509
5792
|
};
|
|
4510
5793
|
} catch (err) {
|
|
4511
|
-
if (err instanceof
|
|
5794
|
+
if (err instanceof _chunkMWBT7MCEcjs.PipRailError) {
|
|
4512
5795
|
const out = {
|
|
4513
5796
|
ok: false,
|
|
4514
5797
|
code: err.code,
|
|
4515
5798
|
reason: err.message,
|
|
4516
5799
|
explain: explainDecline(err)
|
|
4517
5800
|
};
|
|
4518
|
-
if (err instanceof
|
|
5801
|
+
if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
|
|
4519
5802
|
out.declined = true;
|
|
4520
5803
|
if (err.reasonCode) out.reasonCode = err.reasonCode;
|
|
4521
5804
|
}
|
|
@@ -4584,11 +5867,11 @@ function paymentTools(client) {
|
|
|
4584
5867
|
},
|
|
4585
5868
|
{
|
|
4586
5869
|
name: "piprail_budget",
|
|
4587
|
-
description: "Read how much of your spend budget and time leash is left \u2014 per (network, asset) remaining, the session time envelope, and your spend so far. Use it in Mode A (headless) to self-check BEFORE paying, so you never discover the leash by hitting a decline. Read-only; moves no funds. NOTE:
|
|
5870
|
+
description: "Read how much of your spend budget and time leash is left \u2014 per (network, asset) remaining, the cross-token GRAND TOTAL per denomination (e.g. how much USD you can still spend across every stablecoin and chain), the payment-count leash, the session time envelope, and your spend so far. Use it in Mode A (headless) to self-check BEFORE paying, so you never discover the leash by hitting a decline. Read-only; moves no funds. NOTE: the time envelope is in-memory for THIS process; the money/count totals persist only if a spend store is configured.",
|
|
4588
5871
|
annotations: {
|
|
4589
5872
|
title: "Check remaining budget",
|
|
4590
5873
|
readOnlyHint: true,
|
|
4591
|
-
// reads the
|
|
5874
|
+
// reads the ledger + policy; never pays
|
|
4592
5875
|
idempotentHint: true
|
|
4593
5876
|
// a pure read
|
|
4594
5877
|
},
|
|
@@ -4601,7 +5884,13 @@ function paymentTools(client) {
|
|
|
4601
5884
|
return {
|
|
4602
5885
|
spent,
|
|
4603
5886
|
remaining: budget.byAsset,
|
|
5887
|
+
grandTotal: budget.byDenom,
|
|
5888
|
+
// cross-token spend cap per denomination (USD/EUR/…)
|
|
5889
|
+
counts: budget.counts,
|
|
5890
|
+
// payment-count leash (settled + lifetime/window caps)
|
|
4604
5891
|
session: budget.session,
|
|
5892
|
+
policy: _nullishCoalesce(client.policy(), () => ( null)),
|
|
5893
|
+
// the configured leash, read back
|
|
4605
5894
|
report: formatSpendReport(spent)
|
|
4606
5895
|
};
|
|
4607
5896
|
} catch (err) {
|
|
@@ -4619,6 +5908,38 @@ function paymentTools(client) {
|
|
|
4619
5908
|
},
|
|
4620
5909
|
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
4621
5910
|
invoke: async () => ({ guide: PIPRAIL_AGENT_GUIDE })
|
|
5911
|
+
},
|
|
5912
|
+
{
|
|
5913
|
+
name: "piprail_verify_receipt",
|
|
5914
|
+
description: "Re-verify a PipRail VERIFIABLE RECEIPT against the chain \u2014 confirm a payment REALLY settled (the funds provably moved to payTo for AT LEAST the stated amount) WITHOUT trusting whoever handed you the receipt. Read-only and WALLET-FREE: pass the PipRailReceipt JSON (from a prior piprail_pay_request `verifiableReceipt`, or any third party). Returns { ok, onChain:{payTo,asset,amount,payer}, matchesClaims, ageSeconds, error? }: `ok` = the chain confirms the settlement; `onChain.payer` is RE-DERIVED from the tx and `matchesClaims:false` means the receipt forged the payer; `amount` is a verified lower bound. Pass `rpcUrl` for a chain outside the common presets.",
|
|
5915
|
+
annotations: {
|
|
5916
|
+
title: "Verify a payment receipt",
|
|
5917
|
+
readOnlyHint: true,
|
|
5918
|
+
// re-reads the chain; moves nothing, needs no wallet
|
|
5919
|
+
idempotentHint: true,
|
|
5920
|
+
openWorldHint: true
|
|
5921
|
+
// reads an on-chain tx via RPC
|
|
5922
|
+
},
|
|
5923
|
+
parameters: {
|
|
5924
|
+
type: "object",
|
|
5925
|
+
properties: {
|
|
5926
|
+
receipt: {
|
|
5927
|
+
type: "object",
|
|
5928
|
+
description: "The PipRailReceipt JSON ({ piprail, receipt, resource, decimals? }) to re-verify."
|
|
5929
|
+
},
|
|
5930
|
+
rpcUrl: {
|
|
5931
|
+
type: "string",
|
|
5932
|
+
description: "Optional RPC URL for the receipt's chain (required for chains outside the common presets)."
|
|
5933
|
+
}
|
|
5934
|
+
},
|
|
5935
|
+
required: ["receipt"],
|
|
5936
|
+
additionalProperties: false
|
|
5937
|
+
},
|
|
5938
|
+
invoke: async (args) => {
|
|
5939
|
+
const receipt = args.receipt;
|
|
5940
|
+
const opts = args.rpcUrl ? { rpcUrl: String(args.rpcUrl) } : void 0;
|
|
5941
|
+
return await PipRailClient.verifyReceipt(receipt, opts);
|
|
5942
|
+
}
|
|
4622
5943
|
}
|
|
4623
5944
|
];
|
|
4624
5945
|
}
|
|
@@ -4675,7 +5996,7 @@ function buildBazaarExtension(descriptor = {}) {
|
|
|
4675
5996
|
function pathOf(url) {
|
|
4676
5997
|
try {
|
|
4677
5998
|
return new URL(url).pathname || "/";
|
|
4678
|
-
} catch (
|
|
5999
|
+
} catch (e42) {
|
|
4679
6000
|
return url.startsWith("/") ? url : `/${url}`;
|
|
4680
6001
|
}
|
|
4681
6002
|
}
|
|
@@ -4784,28 +6105,28 @@ async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
|
|
|
4784
6105
|
const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
|
|
4785
6106
|
if (!res.ok) return void 0;
|
|
4786
6107
|
const body = await res.json();
|
|
4787
|
-
const kinds = Array.isArray(_optionalChain([body, 'optionalAccess',
|
|
6108
|
+
const kinds = Array.isArray(_optionalChain([body, 'optionalAccess', _109 => _109.kinds])) ? body.kinds : [];
|
|
4788
6109
|
const want = normalizeNetwork(network);
|
|
4789
|
-
const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess',
|
|
4790
|
-
const fp = _optionalChain([kind, 'optionalAccess',
|
|
6110
|
+
const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _110 => _110.scheme]) === "exact" && normalizeNetwork(String(_nullishCoalesce(_optionalChain([k, 'optionalAccess', _111 => _111.network]), () => ( "")))) === want);
|
|
6111
|
+
const fp = _optionalChain([kind, 'optionalAccess', _112 => _112.extra, 'optionalAccess', _113 => _113.feePayer]);
|
|
4791
6112
|
return typeof fp === "string" ? fp : void 0;
|
|
4792
|
-
} catch (
|
|
6113
|
+
} catch (e43) {
|
|
4793
6114
|
return void 0;
|
|
4794
6115
|
} finally {
|
|
4795
6116
|
clearTimeout(timer);
|
|
4796
6117
|
}
|
|
4797
6118
|
}
|
|
4798
6119
|
function parseFacilitatorSupported(body) {
|
|
4799
|
-
const kinds = _optionalChain([body, 'optionalAccess',
|
|
6120
|
+
const kinds = _optionalChain([body, 'optionalAccess', _114 => _114.kinds]);
|
|
4800
6121
|
if (!Array.isArray(kinds)) return [];
|
|
4801
6122
|
const out = [];
|
|
4802
6123
|
for (const k of kinds) {
|
|
4803
6124
|
if (!k || typeof k !== "object") continue;
|
|
4804
6125
|
const o = k;
|
|
4805
6126
|
if (typeof o.scheme !== "string" || typeof o.network !== "string") continue;
|
|
4806
|
-
const fp = _optionalChain([o, 'access',
|
|
6127
|
+
const fp = _optionalChain([o, 'access', _115 => _115.extra, 'optionalAccess', _116 => _116.feePayer]);
|
|
4807
6128
|
const ver = o.x402Version;
|
|
4808
|
-
const method = _optionalChain([o, 'access',
|
|
6129
|
+
const method = _optionalChain([o, 'access', _117 => _117.extra, 'optionalAccess', _118 => _118.assetTransferMethod]);
|
|
4809
6130
|
out.push({
|
|
4810
6131
|
scheme: o.scheme,
|
|
4811
6132
|
network: o.network,
|
|
@@ -4824,7 +6145,7 @@ async function facilitatorCoverage(url, timeoutMs = 8e3) {
|
|
|
4824
6145
|
const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
|
|
4825
6146
|
if (!res.ok) return [];
|
|
4826
6147
|
return parseFacilitatorSupported(await res.json());
|
|
4827
|
-
} catch (
|
|
6148
|
+
} catch (e44) {
|
|
4828
6149
|
return [];
|
|
4829
6150
|
} finally {
|
|
4830
6151
|
clearTimeout(timer);
|
|
@@ -4853,7 +6174,7 @@ async function post(url, body, headers) {
|
|
|
4853
6174
|
let json = null;
|
|
4854
6175
|
try {
|
|
4855
6176
|
json = await res.json();
|
|
4856
|
-
} catch (
|
|
6177
|
+
} catch (e45) {
|
|
4857
6178
|
}
|
|
4858
6179
|
return { status: res.status, json };
|
|
4859
6180
|
}
|
|
@@ -4869,13 +6190,13 @@ async function settleViaFacilitator(input) {
|
|
|
4869
6190
|
try {
|
|
4870
6191
|
verify = await post(`${base2}/verify`, body, auth);
|
|
4871
6192
|
} catch (err) {
|
|
4872
|
-
throw new (0,
|
|
6193
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4873
6194
|
`exact settle (facilitator ${base2}): /verify request failed (${err instanceof Error ? err.message : String(err)}).`,
|
|
4874
6195
|
{ cause: err }
|
|
4875
6196
|
);
|
|
4876
6197
|
}
|
|
4877
6198
|
if (verify.status !== 200) {
|
|
4878
|
-
throw new (0,
|
|
6199
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4879
6200
|
`exact settle (facilitator ${base2}): /verify returned HTTP ${verify.status} (transport/auth error).`
|
|
4880
6201
|
);
|
|
4881
6202
|
}
|
|
@@ -4891,13 +6212,13 @@ async function settleViaFacilitator(input) {
|
|
|
4891
6212
|
try {
|
|
4892
6213
|
settle = await post(`${base2}/settle`, body, auth);
|
|
4893
6214
|
} catch (err) {
|
|
4894
|
-
throw new (0,
|
|
6215
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4895
6216
|
`exact settle (facilitator ${base2}): /settle request failed (${err instanceof Error ? err.message : String(err)}).`,
|
|
4896
6217
|
{ cause: err }
|
|
4897
6218
|
);
|
|
4898
6219
|
}
|
|
4899
6220
|
if (settle.status !== 200) {
|
|
4900
|
-
throw new (0,
|
|
6221
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4901
6222
|
`exact settle (facilitator ${base2}): /settle returned HTTP ${settle.status} (transport/auth error).`
|
|
4902
6223
|
);
|
|
4903
6224
|
}
|
|
@@ -5268,17 +6589,40 @@ function normaliseExactOption(exact) {
|
|
|
5268
6589
|
return exact;
|
|
5269
6590
|
}
|
|
5270
6591
|
var TRANSIENT_VERIFY_CODES = /* @__PURE__ */ new Set(["tx_not_found", "insufficient_confirmations"]);
|
|
6592
|
+
function nowUnixSeconds(verifiedAt) {
|
|
6593
|
+
if (verifiedAt) {
|
|
6594
|
+
const t = Date.parse(verifiedAt);
|
|
6595
|
+
if (!Number.isNaN(t)) return Math.floor(t / 1e3);
|
|
6596
|
+
}
|
|
6597
|
+
return Math.floor(Date.now() / 1e3);
|
|
6598
|
+
}
|
|
5271
6599
|
function createPaymentGate(options) {
|
|
5272
6600
|
const minConfirmations = _nullishCoalesce(options.minConfirmations, () => ( 1));
|
|
5273
6601
|
const maxTimeoutSeconds = _nullishCoalesce(options.maxTimeoutSeconds, () => ( 600));
|
|
5274
6602
|
const genNonce = _nullishCoalesce(options.generateNonce, () => ( (() => globalThis.crypto.randomUUID())));
|
|
5275
6603
|
const exactOption = normaliseExactOption(options.exact);
|
|
6604
|
+
const receiptsOn = options.receipts !== void 0 && options.receipts !== false;
|
|
6605
|
+
const receiptOpt = typeof options.receipts === "object" ? options.receipts : {};
|
|
6606
|
+
const receiptIncludeTxHash = receiptOpt.includeTxHash !== false;
|
|
6607
|
+
const receiptResourceUrl = _nullishCoalesce(receiptOpt.resource, () => ( ""));
|
|
6608
|
+
const attestWallet = receiptOpt.attest && "wallet" in receiptOpt.attest ? receiptOpt.attest.wallet : void 0;
|
|
6609
|
+
const attestJws = receiptOpt.attest && "jws" in receiptOpt.attest;
|
|
6610
|
+
let attestWarned = false;
|
|
6611
|
+
function warnAttestDegrade(reason) {
|
|
6612
|
+
if (attestWarned) return;
|
|
6613
|
+
attestWarned = true;
|
|
6614
|
+
try {
|
|
6615
|
+
console.warn(`[piprail] receipts.attest ${reason}; emitting an unsigned Tier-1 receipt instead.`);
|
|
6616
|
+
} catch (e46) {
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
5276
6619
|
let resolved;
|
|
5277
6620
|
function ready() {
|
|
5278
6621
|
if (resolved) return resolved;
|
|
5279
6622
|
const p = (async () => {
|
|
5280
6623
|
const accepts = normaliseAccepts(options);
|
|
5281
6624
|
const exactSkips = [];
|
|
6625
|
+
const uptoSkips = [];
|
|
5282
6626
|
const specs = await Promise.all(
|
|
5283
6627
|
accepts.map(async (a) => {
|
|
5284
6628
|
const net = await resolveNetwork2({ chain: a.chain, rpcUrl: _nullishCoalesce(a.rpcUrl, () => ( options.rpcUrl)) });
|
|
@@ -5290,20 +6634,30 @@ function createPaymentGate(options) {
|
|
|
5290
6634
|
}
|
|
5291
6635
|
net.assertValidPayTo(payTo);
|
|
5292
6636
|
const { asset, decimals, symbol } = net.resolveToken(a.token);
|
|
5293
|
-
const amountBase =
|
|
6637
|
+
const amountBase = _chunkMWBT7MCEcjs.parseUnits.call(void 0, a.amount, decimals);
|
|
5294
6638
|
const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
|
|
5295
6639
|
if (exactOption) {
|
|
5296
6640
|
const outcome = await resolveExactRail(net, asset);
|
|
5297
6641
|
if (outcome.rail) spec.exact = outcome.rail;
|
|
5298
6642
|
else if (outcome.skipReason) exactSkips.push(outcome.skipReason);
|
|
5299
6643
|
}
|
|
6644
|
+
if (options.upto) {
|
|
6645
|
+
const upto = await resolveUptoRail(net, asset);
|
|
6646
|
+
if (upto) spec.upto = upto;
|
|
6647
|
+
else uptoSkips.push(`${net.network}/${asset}`);
|
|
6648
|
+
}
|
|
5300
6649
|
return spec;
|
|
5301
6650
|
})
|
|
5302
6651
|
);
|
|
6652
|
+
if (options.upto && !specs.some((s) => s.upto)) {
|
|
6653
|
+
throw new Error(
|
|
6654
|
+
`requirePayment: \`upto\` (the metered rail) was requested but none of the offered rails support it (tried: ${uptoSkips.join(", ") || "none"}). The \`upto\` scheme is EVM-Permit2 ONLY \u2014 an ERC-20 on a chain with the x402UptoPermit2Proxy deployed (Ethereum/Base/Arbitrum/Optimism/Polygon/BNB), NOT a native coin, NOT a non-EVM family. (Native coins + non-Permit2 chains can never carry \`upto\`.)`
|
|
6655
|
+
);
|
|
6656
|
+
}
|
|
5303
6657
|
if (exactOption && !specs.some((s) => s.exact)) {
|
|
5304
6658
|
const why = 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.";
|
|
5305
6659
|
if (exactOption.settle === "keyless") {
|
|
5306
|
-
if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess',
|
|
6660
|
+
if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess', _119 => _119.env, 'optionalAccess', _120 => _120.PIPRAIL_NO_HINTS])) {
|
|
5307
6661
|
console.warn(
|
|
5308
6662
|
`[piprail] exact: true \u2014 no offered chain has a gasless \`exact\` rail available, so this gate serves ONCHAIN-PROOF ONLY (buyers PAY GAS \u2014 the fallback when no facilitator can sponsor). ${why} To be gasless: pin \`exact: { settle: { facilitator } }\` or self-settle \`exact: { settle: 'self', relayer }\`. (Suppress with PIPRAIL_NO_HINTS=1.)`
|
|
5309
6663
|
);
|
|
@@ -5331,7 +6685,7 @@ function createPaymentGate(options) {
|
|
|
5331
6685
|
skipReason: `${net.network}: \`exact: true\` found no known keyless facilitator for this network. Pass \`exact: { settle: { facilitator } }\`, \`exact: { settle: 'self', relayer }\`, or see the coverage map (KNOWN_FACILITATORS / docs.piprail.com).`
|
|
5332
6686
|
};
|
|
5333
6687
|
}
|
|
5334
|
-
if (typeof process === "undefined" || _optionalChain([process, 'optionalAccess',
|
|
6688
|
+
if (typeof process === "undefined" || _optionalChain([process, 'optionalAccess', _121 => _121.env, 'optionalAccess', _122 => _122.NODE_ENV]) !== "production" && !_optionalChain([process, 'optionalAccess', _123 => _123.env, 'optionalAccess', _124 => _124.PIPRAIL_NO_HINTS])) {
|
|
5335
6689
|
console.warn(
|
|
5336
6690
|
`[piprail] exact: keyless rail on ${net.network} auto-settles via ${picked.url} (zero-config; pin \`exact.settle.facilitator\` in production).`
|
|
5337
6691
|
);
|
|
@@ -5379,7 +6733,27 @@ function createPaymentGate(options) {
|
|
|
5379
6733
|
};
|
|
5380
6734
|
return { rail: { method: info.method, ...info.extra ? { extra: info.extra } : {}, mode } };
|
|
5381
6735
|
}
|
|
5382
|
-
|
|
6736
|
+
async function resolveUptoRail(net, asset) {
|
|
6737
|
+
const cfg = options.upto;
|
|
6738
|
+
if (!net.resolveUptoRail) return null;
|
|
6739
|
+
if (cfg.relayer === void 0) {
|
|
6740
|
+
throw new Error(
|
|
6741
|
+
"requirePayment: `upto` needs a `relayer` wallet (the gas-paying key that self-settles the metered actual AND is the bound witness.facilitator), e.g. upto: { relayer: { key }, settleAmount }."
|
|
6742
|
+
);
|
|
6743
|
+
}
|
|
6744
|
+
const relayer = net.bindWallet(cfg.relayer);
|
|
6745
|
+
const info = await net.resolveUptoRail({ asset, relayer });
|
|
6746
|
+
if (!info) return null;
|
|
6747
|
+
return { method: info.method, ...info.extra ? { extra: info.extra } : {}, relayer };
|
|
6748
|
+
}
|
|
6749
|
+
const hasIsUsed = typeof options.isUsed === "function";
|
|
6750
|
+
const hasMarkUsed = typeof options.markUsed === "function";
|
|
6751
|
+
if (hasIsUsed !== hasMarkUsed) {
|
|
6752
|
+
throw new Error(
|
|
6753
|
+
"requirePayment/createPaymentGate: `isUsed` and `markUsed` must be provided TOGETHER \u2014 a custom replay store needs both a read and a write. Supplying only " + (hasIsUsed ? "`isUsed`" : "`markUsed`") + " silently disables replay protection (double-spend). Provide both, or neither (the built-in in-memory store)."
|
|
6754
|
+
);
|
|
6755
|
+
}
|
|
6756
|
+
const hasCustomStore = hasIsUsed && hasMarkUsed;
|
|
5383
6757
|
const localUsed = /* @__PURE__ */ new Map();
|
|
5384
6758
|
const replayWindowMs = maxTimeoutSeconds * 1e3;
|
|
5385
6759
|
function pruneUsed(now) {
|
|
@@ -5442,10 +6816,33 @@ function createPaymentGate(options) {
|
|
|
5442
6816
|
}
|
|
5443
6817
|
};
|
|
5444
6818
|
}
|
|
6819
|
+
function buildUptoAccept(s) {
|
|
6820
|
+
const rail = s.upto;
|
|
6821
|
+
return {
|
|
6822
|
+
scheme: "upto",
|
|
6823
|
+
network: s.net.network,
|
|
6824
|
+
amount: s.amountBase.toString(),
|
|
6825
|
+
// the authorized MAX (base units)
|
|
6826
|
+
asset: s.asset,
|
|
6827
|
+
payTo: s.payTo,
|
|
6828
|
+
maxTimeoutSeconds,
|
|
6829
|
+
extra: {
|
|
6830
|
+
assetTransferMethod: "permit2-upto",
|
|
6831
|
+
// facilitatorAddress comes from rail.extra; the spread below carries it (+ name/version).
|
|
6832
|
+
facilitatorAddress: _nullishCoalesce(_optionalChain([rail, 'access', _125 => _125.extra, 'optionalAccess', _126 => _126.facilitatorAddress]), () => ( "")),
|
|
6833
|
+
minConfirmations,
|
|
6834
|
+
decimals: s.decimals,
|
|
6835
|
+
amountFormatted: s.amountFormatted,
|
|
6836
|
+
...s.symbol ? { symbol: s.symbol } : {},
|
|
6837
|
+
...rail.extra
|
|
6838
|
+
}
|
|
6839
|
+
};
|
|
6840
|
+
}
|
|
5445
6841
|
function buildAccepts(specs, nonce) {
|
|
5446
6842
|
const out = [];
|
|
5447
6843
|
for (const s of specs) {
|
|
5448
6844
|
if (s.exact) out.push(buildExactAccept(s));
|
|
6845
|
+
if (s.upto) out.push(buildUptoAccept(s));
|
|
5449
6846
|
out.push(buildAccept(s, nonce));
|
|
5450
6847
|
}
|
|
5451
6848
|
return out;
|
|
@@ -5463,9 +6860,10 @@ function createPaymentGate(options) {
|
|
|
5463
6860
|
const selfDescribe = options.selfDescribe === false ? void 0 : buildSelfDescription({
|
|
5464
6861
|
accepts,
|
|
5465
6862
|
instruction: describeChallenge({ x402Version: 2, resource: { url: resourceUrl }, accepts }),
|
|
5466
|
-
...endpointInfo ? { endpoint: endpointInfo } : {}
|
|
6863
|
+
...endpointInfo ? { endpoint: endpointInfo } : {},
|
|
6864
|
+
...receiptsOn ? { verifiableReceipts: true } : {}
|
|
5467
6865
|
});
|
|
5468
|
-
const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
6866
|
+
const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _127 => _127.extensions]), () => ( {}));
|
|
5469
6867
|
const rejectionPiprail = _nullishCoalesce(rejectionExt.piprail, () => ( {}));
|
|
5470
6868
|
const bodyPiprail = { ..._nullishCoalesce(selfDescribe, () => ( {})), ...rejectionPiprail };
|
|
5471
6869
|
const bodyExtensions = {
|
|
@@ -5485,7 +6883,7 @@ function createPaymentGate(options) {
|
|
|
5485
6883
|
...options.mimeType ? { mimeType: options.mimeType } : {}
|
|
5486
6884
|
},
|
|
5487
6885
|
accepts,
|
|
5488
|
-
..._optionalChain([opts, 'optionalAccess',
|
|
6886
|
+
..._optionalChain([opts, 'optionalAccess', _128 => _128.error]) ? { error: opts.error } : {},
|
|
5489
6887
|
...Object.keys(bodyExtensions).length > 0 ? { extensions: bodyExtensions } : {}
|
|
5490
6888
|
};
|
|
5491
6889
|
const headerChallenge = {
|
|
@@ -5516,8 +6914,8 @@ function createPaymentGate(options) {
|
|
|
5516
6914
|
function enrichReceipt(spec, receipt) {
|
|
5517
6915
|
let amountFormatted = receipt.amount;
|
|
5518
6916
|
try {
|
|
5519
|
-
amountFormatted =
|
|
5520
|
-
} catch (
|
|
6917
|
+
amountFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
|
|
6918
|
+
} catch (e47) {
|
|
5521
6919
|
}
|
|
5522
6920
|
return {
|
|
5523
6921
|
...receipt,
|
|
@@ -5531,7 +6929,7 @@ function createPaymentGate(options) {
|
|
|
5531
6929
|
if (!options.onPaidError) return;
|
|
5532
6930
|
try {
|
|
5533
6931
|
options.onPaidError(error, receipt);
|
|
5534
|
-
} catch (
|
|
6932
|
+
} catch (e48) {
|
|
5535
6933
|
}
|
|
5536
6934
|
}
|
|
5537
6935
|
function fireOnPaid(receipt) {
|
|
@@ -5552,11 +6950,60 @@ function createPaymentGate(options) {
|
|
|
5552
6950
|
if (options.awaitOnPaid) await fireOnPaid(paid);
|
|
5553
6951
|
else void fireOnPaid(paid);
|
|
5554
6952
|
}
|
|
6953
|
+
async function buildPaidResult(spec, receipt, nonce) {
|
|
6954
|
+
if (!receiptsOn) {
|
|
6955
|
+
return { kind: "paid", receipt, receiptHeader: buildReceiptHeader(receipt) };
|
|
6956
|
+
}
|
|
6957
|
+
try {
|
|
6958
|
+
const stamped = {
|
|
6959
|
+
...receipt,
|
|
6960
|
+
...nonce ? { nonce } : {},
|
|
6961
|
+
// §5.3: a suppressed tx is the empty string on the wire, never a missing key.
|
|
6962
|
+
...receiptIncludeTxHash ? {} : { transaction: "" }
|
|
6963
|
+
};
|
|
6964
|
+
const attestation = await maybeSignAttestation(spec, stamped);
|
|
6965
|
+
const extensions = buildReceiptExtension({
|
|
6966
|
+
receipt: stamped,
|
|
6967
|
+
resource: { url: receiptResourceUrl },
|
|
6968
|
+
decimals: spec.decimals,
|
|
6969
|
+
...attestation ? { attestation } : {}
|
|
6970
|
+
});
|
|
6971
|
+
return { kind: "paid", receipt: stamped, receiptHeader: buildReceiptHeader(stamped, extensions) };
|
|
6972
|
+
} catch (e49) {
|
|
6973
|
+
return { kind: "paid", receipt, receiptHeader: buildReceiptHeader(receipt) };
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
async function maybeSignAttestation(spec, stamped) {
|
|
6977
|
+
if (attestJws) {
|
|
6978
|
+
warnAttestDegrade("JWS attestation is not yet implemented (R3)");
|
|
6979
|
+
return void 0;
|
|
6980
|
+
}
|
|
6981
|
+
if (attestWallet === void 0) return void 0;
|
|
6982
|
+
if (typeof spec.net.signReceipt !== "function") {
|
|
6983
|
+
warnAttestDegrade(`is EVM-only; the ${spec.net.family} rail can't sign an EIP-712 attestation`);
|
|
6984
|
+
return void 0;
|
|
6985
|
+
}
|
|
6986
|
+
try {
|
|
6987
|
+
const wallet = spec.net.bindWallet(attestWallet);
|
|
6988
|
+
return await spec.net.signReceipt(wallet, {
|
|
6989
|
+
payTo: spec.payTo,
|
|
6990
|
+
network: spec.net.network,
|
|
6991
|
+
resourceUrl: receiptResourceUrl,
|
|
6992
|
+
payer: stamped.payer,
|
|
6993
|
+
issuedAt: nowUnixSeconds(stamped.verifiedAt),
|
|
6994
|
+
// §5.3: the signed message carries the empty string for a suppressed tx, never omitted.
|
|
6995
|
+
transaction: receiptIncludeTxHash ? stamped.transaction : ""
|
|
6996
|
+
});
|
|
6997
|
+
} catch (e50) {
|
|
6998
|
+
warnAttestDegrade("signing failed");
|
|
6999
|
+
return void 0;
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
5555
7002
|
function reportOnFailedError(error, failure) {
|
|
5556
7003
|
if (!options.onFailedError) return;
|
|
5557
7004
|
try {
|
|
5558
7005
|
options.onFailedError(error, failure);
|
|
5559
|
-
} catch (
|
|
7006
|
+
} catch (e51) {
|
|
5560
7007
|
}
|
|
5561
7008
|
}
|
|
5562
7009
|
function fireOnFailed(failure) {
|
|
@@ -5578,6 +7025,14 @@ function createPaymentGate(options) {
|
|
|
5578
7025
|
if (options.awaitOnFailed) await fireOnFailed(failure);
|
|
5579
7026
|
else void fireOnFailed(failure);
|
|
5580
7027
|
}
|
|
7028
|
+
function railExtra(full) {
|
|
7029
|
+
const out = {};
|
|
7030
|
+
for (const k of ["assetTransferMethod", "facilitatorAddress", "name", "version"]) {
|
|
7031
|
+
const v = full[k];
|
|
7032
|
+
if (v !== void 0 && v !== "") out[k] = v;
|
|
7033
|
+
}
|
|
7034
|
+
return Object.keys(out).length > 0 ? { extra: out } : {};
|
|
7035
|
+
}
|
|
5581
7036
|
async function describe(resourceUrl = "") {
|
|
5582
7037
|
const specs = await ready();
|
|
5583
7038
|
const accepts = [];
|
|
@@ -5592,7 +7047,8 @@ function createPaymentGate(options) {
|
|
|
5592
7047
|
maxTimeoutSeconds,
|
|
5593
7048
|
...s.symbol ? { symbol: s.symbol } : {}
|
|
5594
7049
|
};
|
|
5595
|
-
if (s.exact) accepts.push({ scheme: "exact", ...base2 });
|
|
7050
|
+
if (s.exact) accepts.push({ scheme: "exact", ...base2, ...railExtra(buildExactAccept(s).extra) });
|
|
7051
|
+
if (s.upto) accepts.push({ scheme: "upto", ...base2, ...railExtra(buildUptoAccept(s).extra) });
|
|
5596
7052
|
accepts.push({ scheme: "onchain-proof", ...base2 });
|
|
5597
7053
|
}
|
|
5598
7054
|
return {
|
|
@@ -5628,7 +7084,7 @@ function createPaymentGate(options) {
|
|
|
5628
7084
|
}
|
|
5629
7085
|
await settleTx(ref, true);
|
|
5630
7086
|
await deliverOnPaid(spec, result.receipt);
|
|
5631
|
-
return
|
|
7087
|
+
return await buildPaidResult(spec, result.receipt, sig.payload.nonce);
|
|
5632
7088
|
}
|
|
5633
7089
|
async function verifyExact(exact) {
|
|
5634
7090
|
const specs = await ready();
|
|
@@ -5656,28 +7112,28 @@ function createPaymentGate(options) {
|
|
|
5656
7112
|
nonce = [exact.payload.transaction, exact.payload.senderAuth].map((t) => {
|
|
5657
7113
|
try {
|
|
5658
7114
|
return Buffer.from(t, "base64").toString("base64");
|
|
5659
|
-
} catch (
|
|
7115
|
+
} catch (e52) {
|
|
5660
7116
|
return t;
|
|
5661
7117
|
}
|
|
5662
7118
|
}).join("|");
|
|
5663
7119
|
} else if ("transaction" in exact.payload) {
|
|
5664
7120
|
try {
|
|
5665
7121
|
nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
|
|
5666
|
-
} catch (
|
|
7122
|
+
} catch (e53) {
|
|
5667
7123
|
nonce = exact.payload.transaction;
|
|
5668
7124
|
}
|
|
5669
7125
|
} else if ("paymentGroup" in exact.payload) {
|
|
5670
7126
|
nonce = exact.payload.paymentGroup.map((t) => {
|
|
5671
7127
|
try {
|
|
5672
7128
|
return Buffer.from(t, "base64").toString("base64");
|
|
5673
|
-
} catch (
|
|
7129
|
+
} catch (e54) {
|
|
5674
7130
|
return t;
|
|
5675
7131
|
}
|
|
5676
7132
|
}).join("|");
|
|
5677
7133
|
} else if ("signedDelegateAction" in exact.payload) {
|
|
5678
7134
|
try {
|
|
5679
7135
|
nonce = Buffer.from(exact.payload.signedDelegateAction, "base64").toString("base64");
|
|
5680
|
-
} catch (
|
|
7136
|
+
} catch (e55) {
|
|
5681
7137
|
nonce = exact.payload.signedDelegateAction;
|
|
5682
7138
|
}
|
|
5683
7139
|
} else if ("permit2Authorization" in exact.payload) {
|
|
@@ -5700,7 +7156,7 @@ function createPaymentGate(options) {
|
|
|
5700
7156
|
const ftMethod = accept.extra.assetTransferMethod;
|
|
5701
7157
|
const needsFeePayer = ftMethod === "svm" || ftMethod === "algorand" || ftMethod === "aptos" || ftMethod === "near";
|
|
5702
7158
|
if (needsFeePayer && !accept.extra.feePayer) {
|
|
5703
|
-
throw new (0,
|
|
7159
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
5704
7160
|
`exact settle: the ${ftMethod} facilitator rail is missing extra.feePayer (the gas sponsor) \u2014 cannot settle.`
|
|
5705
7161
|
);
|
|
5706
7162
|
}
|
|
@@ -5738,34 +7194,129 @@ function createPaymentGate(options) {
|
|
|
5738
7194
|
}
|
|
5739
7195
|
await settleTx(nonce, true);
|
|
5740
7196
|
await deliverOnPaid(spec, result.receipt);
|
|
5741
|
-
return
|
|
7197
|
+
return await buildPaidResult(spec, result.receipt);
|
|
7198
|
+
}
|
|
7199
|
+
function resolveSettleAmount(raw, maxAmount, decimals) {
|
|
7200
|
+
if (typeof raw === "bigint") return raw < 0n ? null : raw;
|
|
7201
|
+
const s = raw.trim();
|
|
7202
|
+
if (s.length === 0) return null;
|
|
7203
|
+
try {
|
|
7204
|
+
if (s.endsWith("%")) {
|
|
7205
|
+
const pct = s.slice(0, -1).trim();
|
|
7206
|
+
if (!/^\d+(\.\d+)?$/.test(pct)) return null;
|
|
7207
|
+
const pctScaled = _chunkMWBT7MCEcjs.floorUnits.call(void 0, pct, 4);
|
|
7208
|
+
return maxAmount * pctScaled / (100n * 10n ** 4n);
|
|
7209
|
+
}
|
|
7210
|
+
if (s.startsWith("$")) {
|
|
7211
|
+
const amt = s.slice(1).trim();
|
|
7212
|
+
return _chunkMWBT7MCEcjs.floorUnits.call(void 0, amt, decimals);
|
|
7213
|
+
}
|
|
7214
|
+
if (/^\d+$/.test(s)) return BigInt(s);
|
|
7215
|
+
return _chunkMWBT7MCEcjs.floorUnits.call(void 0, s, decimals);
|
|
7216
|
+
} catch (e56) {
|
|
7217
|
+
return null;
|
|
7218
|
+
}
|
|
7219
|
+
}
|
|
7220
|
+
async function verifyUpto(upto) {
|
|
7221
|
+
const specs = await ready();
|
|
7222
|
+
const uptoSpecs = specs.filter((s) => s.upto);
|
|
7223
|
+
if (uptoSpecs.length === 0) {
|
|
7224
|
+
return rejection("transfer_not_found", "This resource offers no standard `upto` (metered) rail.");
|
|
7225
|
+
}
|
|
7226
|
+
const isCaip = upto.network.includes(":");
|
|
7227
|
+
let candidates = isCaip ? uptoSpecs.filter((s) => s.net.network === upto.network) : uptoSpecs;
|
|
7228
|
+
if (upto.asset) {
|
|
7229
|
+
candidates = candidates.filter((s) => s.asset.toLowerCase() === upto.asset.toLowerCase());
|
|
7230
|
+
}
|
|
7231
|
+
let spec = candidates[0];
|
|
7232
|
+
if (!isCaip && !upto.asset && uptoSpecs.length > 1) spec = void 0;
|
|
7233
|
+
if (!spec && !isCaip && !upto.asset && uptoSpecs.length === 1) spec = uptoSpecs[0];
|
|
7234
|
+
if (!spec || !spec.upto) {
|
|
7235
|
+
return rejection(
|
|
7236
|
+
"transfer_not_found",
|
|
7237
|
+
`No \`upto\` rail offered for ${upto.network}${upto.asset ? `/${upto.asset}` : ""} (offered: ${uptoSpecs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
|
|
7238
|
+
);
|
|
7239
|
+
}
|
|
7240
|
+
const nonce = upto.payload.permit2Authorization.nonce;
|
|
7241
|
+
if (await claimTx(nonce)) {
|
|
7242
|
+
return rejection("tx_already_used", `Authorization nonce ${nonce} was already redeemed.`);
|
|
7243
|
+
}
|
|
7244
|
+
const accept = buildUptoAccept(spec);
|
|
7245
|
+
const relayer = spec.upto.relayer;
|
|
7246
|
+
let result;
|
|
7247
|
+
try {
|
|
7248
|
+
const rawAmount = await options.upto.settleAmount({
|
|
7249
|
+
maxAmount: spec.amountBase,
|
|
7250
|
+
asset: spec.asset,
|
|
7251
|
+
network: spec.net.network,
|
|
7252
|
+
decimals: spec.decimals,
|
|
7253
|
+
...upto.raw ? { request: upto.raw } : {}
|
|
7254
|
+
});
|
|
7255
|
+
const settleAmount = resolveSettleAmount(rawAmount, spec.amountBase, spec.decimals);
|
|
7256
|
+
if (settleAmount === null) {
|
|
7257
|
+
await settleTx(nonce, false);
|
|
7258
|
+
return rejection(
|
|
7259
|
+
"upto_settle_exceeds_max",
|
|
7260
|
+
`The settleAmount callback returned an unparseable amount (${String(rawAmount)}). Return a bigint, a raw/"NN%"/"$X" string, or 0.`
|
|
7261
|
+
);
|
|
7262
|
+
}
|
|
7263
|
+
result = await spec.net.settleUptoSelf({ relayer, payload: upto.payload, accept, settleAmount });
|
|
7264
|
+
} catch (err) {
|
|
7265
|
+
await settleTx(nonce, false);
|
|
7266
|
+
if (err instanceof _chunkMWBT7MCEcjs.SettlementError) throw err;
|
|
7267
|
+
return rejection(
|
|
7268
|
+
"tx_reverted",
|
|
7269
|
+
`upto: metering/settle failed (${err instanceof Error ? err.message : String(err)}).`
|
|
7270
|
+
);
|
|
7271
|
+
}
|
|
7272
|
+
if (!result.ok) {
|
|
7273
|
+
await settleTx(nonce, false);
|
|
7274
|
+
return rejection(result.error, result.detail);
|
|
7275
|
+
}
|
|
7276
|
+
await settleTx(nonce, true);
|
|
7277
|
+
await deliverOnPaid(spec, result.receipt);
|
|
7278
|
+
return await buildPaidResult(spec, result.receipt);
|
|
5742
7279
|
}
|
|
5743
7280
|
async function verify(paymentSignature) {
|
|
5744
|
-
const
|
|
7281
|
+
const raw = normaliseHeader(paymentSignature);
|
|
7282
|
+
const result = await resolveVerdictObject(raw === void 0 ? void 0 : decodeBase64Json(raw));
|
|
5745
7283
|
if (result.kind === "invalid") await deliverOnFailed(result);
|
|
5746
7284
|
return result;
|
|
5747
7285
|
}
|
|
5748
|
-
async function
|
|
5749
|
-
const
|
|
5750
|
-
if (
|
|
5751
|
-
|
|
7286
|
+
async function verifyObject(payload) {
|
|
7287
|
+
const result = await resolveVerdictObject(payload);
|
|
7288
|
+
if (result.kind === "invalid") await deliverOnFailed(result);
|
|
7289
|
+
return result;
|
|
7290
|
+
}
|
|
7291
|
+
async function resolveVerdictObject(obj) {
|
|
7292
|
+
if (obj === void 0 || obj === null) return asChallenge();
|
|
7293
|
+
const sig = parseSignatureObject(obj);
|
|
5752
7294
|
if (sig && sig.accepted && typeof sig.accepted.network === "string" && typeof sig.accepted.asset === "string") {
|
|
5753
7295
|
return verifyOnchainProof(sig);
|
|
5754
7296
|
}
|
|
5755
|
-
const
|
|
7297
|
+
const upto = parseUptoObject(obj);
|
|
7298
|
+
if (upto) return verifyUpto(upto);
|
|
7299
|
+
const exact = parseExactObject(obj);
|
|
5756
7300
|
if (exact) return verifyExact(exact);
|
|
5757
7301
|
return asChallenge();
|
|
5758
7302
|
}
|
|
5759
|
-
return { challenge, verify, describe, landingPage };
|
|
7303
|
+
return { challenge, verify, verifyObject, describe, landingPage };
|
|
5760
7304
|
}
|
|
5761
7305
|
function requirePayment(options) {
|
|
7306
|
+
if (options.upto) {
|
|
7307
|
+
throw new (_class3 = class extends _chunkMWBT7MCEcjs.PipRailError {constructor(...args2) { super(...args2); _class3.prototype.__init7.call(this); }
|
|
7308
|
+
__init7() {this.code = "UNSUPPORTED_SCHEME"}
|
|
7309
|
+
}, _class3)(
|
|
7310
|
+
"requirePayment: the 'upto' (metered) rail is unsupported through the Express middleware \u2014 it settles before the route handler serves, so metered usage is unknown at settle time. Call gate.verify() directly (createPaymentGate) and meter inside settleAmount; see docs/accepting-payments/upto-rail-seller.md."
|
|
7311
|
+
);
|
|
7312
|
+
}
|
|
5762
7313
|
const gate = createPaymentGate(options);
|
|
5763
7314
|
return async (req, res, next) => {
|
|
5764
7315
|
let result;
|
|
5765
7316
|
try {
|
|
5766
7317
|
result = await gate.verify(_nullishCoalesce(req.headers[HEADER_SIGNATURE], () => ( req.headers[HEADER_SIGNATURE_V1])));
|
|
5767
7318
|
} catch (err) {
|
|
5768
|
-
if (err instanceof
|
|
7319
|
+
if (err instanceof _chunkMWBT7MCEcjs.SettlementError) {
|
|
5769
7320
|
res.status(502);
|
|
5770
7321
|
res.json({
|
|
5771
7322
|
x402Version: 2,
|
|
@@ -5813,7 +7364,7 @@ function isRetryableStatus(status) {
|
|
|
5813
7364
|
}
|
|
5814
7365
|
var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
5815
7366
|
async function signBody(secret, body) {
|
|
5816
|
-
const subtle = _optionalChain([globalThis, 'access',
|
|
7367
|
+
const subtle = _optionalChain([globalThis, 'access', _129 => _129.crypto, 'optionalAccess', _130 => _130.subtle]);
|
|
5817
7368
|
if (!subtle) return null;
|
|
5818
7369
|
try {
|
|
5819
7370
|
const enc = new TextEncoder();
|
|
@@ -5823,7 +7374,7 @@ async function signBody(secret, body) {
|
|
|
5823
7374
|
const sig = await subtle.sign("HMAC", key, enc.encode(body));
|
|
5824
7375
|
const hex = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5825
7376
|
return `sha256=${hex}`;
|
|
5826
|
-
} catch (
|
|
7377
|
+
} catch (e57) {
|
|
5827
7378
|
return null;
|
|
5828
7379
|
}
|
|
5829
7380
|
}
|
|
@@ -5883,8 +7434,8 @@ async function deliverReceipt(receipt, options) {
|
|
|
5883
7434
|
const retryable = status === void 0 ? true : isRetryableStatus(status);
|
|
5884
7435
|
const willRetry = !ok && retryable && attempt < maxAttempts;
|
|
5885
7436
|
try {
|
|
5886
|
-
_optionalChain([onAttempt, 'optionalCall',
|
|
5887
|
-
} catch (
|
|
7437
|
+
_optionalChain([onAttempt, 'optionalCall', _131 => _131({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
|
|
7438
|
+
} catch (e58) {
|
|
5888
7439
|
}
|
|
5889
7440
|
if (ok) return { delivered: true, attempts: attempt, status };
|
|
5890
7441
|
if (!willRetry) {
|
|
@@ -5905,6 +7456,277 @@ async function deliverReceipt(receipt, options) {
|
|
|
5905
7456
|
};
|
|
5906
7457
|
}
|
|
5907
7458
|
|
|
7459
|
+
// src/transports/a2a.ts
|
|
7460
|
+
var A2A_X402_EXTENSION_URI_V01 = "https://github.com/google-a2a/a2a-x402/v0.1";
|
|
7461
|
+
var A2A_X402_EXTENSION_URI_V02 = "https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2";
|
|
7462
|
+
var A2A_STATUS_KEY = "x402.payment.status";
|
|
7463
|
+
var A2A_REQUIRED_KEY = "x402.payment.required";
|
|
7464
|
+
var A2A_PAYLOAD_KEY = "x402.payment.payload";
|
|
7465
|
+
var A2A_RECEIPTS_KEY = "x402.payment.receipts";
|
|
7466
|
+
var A2A_ERROR_KEY = "x402.payment.error";
|
|
7467
|
+
var A2A_EXTENSIONS_HEADER = "X-A2A-Extensions";
|
|
7468
|
+
var MAX_TASK_RECEIPTS = 64;
|
|
7469
|
+
var VERIFY_CODE_TO_A2A_ERROR = {
|
|
7470
|
+
payment_expired: "EXPIRED_PAYMENT",
|
|
7471
|
+
tx_already_used: "DUPLICATE_NONCE",
|
|
7472
|
+
amount_too_low: "INVALID_AMOUNT",
|
|
7473
|
+
upto_settle_exceeds_max: "INVALID_AMOUNT",
|
|
7474
|
+
signature_invalid: "INVALID_SIGNATURE",
|
|
7475
|
+
transfer_not_found: "INVALID_AMOUNT",
|
|
7476
|
+
wrong_recipient: "INVALID_AMOUNT",
|
|
7477
|
+
tx_reverted: "SETTLEMENT_FAILED",
|
|
7478
|
+
tx_not_found: "EXPIRED_PAYMENT",
|
|
7479
|
+
insufficient_confirmations: "EXPIRED_PAYMENT",
|
|
7480
|
+
no_meta: "INVALID_SIGNATURE",
|
|
7481
|
+
// The settlement-side throw code the handler emits for a SettlementError (the merchant's
|
|
7482
|
+
// relayer/facilitator never moved funds) — maps to the spec's SETTLEMENT_FAILED.
|
|
7483
|
+
settlement_failed: "SETTLEMENT_FAILED"
|
|
7484
|
+
};
|
|
7485
|
+
function toA2AErrorCode(code) {
|
|
7486
|
+
return _nullishCoalesce(VERIFY_CODE_TO_A2A_ERROR[code], () => ( code));
|
|
7487
|
+
}
|
|
7488
|
+
function toA2APaymentRequired(taskId, challenge, parts) {
|
|
7489
|
+
const metadata = {
|
|
7490
|
+
[A2A_STATUS_KEY]: "payment-required",
|
|
7491
|
+
[A2A_REQUIRED_KEY]: challenge
|
|
7492
|
+
};
|
|
7493
|
+
return {
|
|
7494
|
+
kind: "task",
|
|
7495
|
+
id: taskId,
|
|
7496
|
+
status: {
|
|
7497
|
+
state: "input-required",
|
|
7498
|
+
message: { kind: "message", role: "agent", taskId, ...parts ? { parts } : {}, metadata }
|
|
7499
|
+
}
|
|
7500
|
+
};
|
|
7501
|
+
}
|
|
7502
|
+
function toA2APaymentReceipts(receipts) {
|
|
7503
|
+
return { [A2A_RECEIPTS_KEY]: receipts };
|
|
7504
|
+
}
|
|
7505
|
+
function toA2APaymentFailed(code, detail, receipts = [], network) {
|
|
7506
|
+
const entry = {
|
|
7507
|
+
success: false,
|
|
7508
|
+
transaction: "",
|
|
7509
|
+
...network ? { network } : {},
|
|
7510
|
+
errorReason: `${code}: ${detail}`
|
|
7511
|
+
};
|
|
7512
|
+
return {
|
|
7513
|
+
[A2A_STATUS_KEY]: "payment-failed",
|
|
7514
|
+
[A2A_ERROR_KEY]: toA2AErrorCode(code),
|
|
7515
|
+
[A2A_RECEIPTS_KEY]: [...receipts, entry]
|
|
7516
|
+
};
|
|
7517
|
+
}
|
|
7518
|
+
function fromA2APaymentRequired(task) {
|
|
7519
|
+
const meta = _optionalChain([task, 'access', _132 => _132.status, 'optionalAccess', _133 => _133.message, 'optionalAccess', _134 => _134.metadata]);
|
|
7520
|
+
const required = _optionalChain([meta, 'optionalAccess', _135 => _135[A2A_REQUIRED_KEY]]);
|
|
7521
|
+
if (!required || typeof required !== "object") return null;
|
|
7522
|
+
return required;
|
|
7523
|
+
}
|
|
7524
|
+
function fromA2APaymentPayload(message) {
|
|
7525
|
+
const raw = _optionalChain([message, 'access', _136 => _136.metadata, 'optionalAccess', _137 => _137[A2A_PAYLOAD_KEY]]);
|
|
7526
|
+
if (raw === void 0 || raw === null) return null;
|
|
7527
|
+
return { raw, taskId: _nullishCoalesce(message.taskId, () => ( "")) };
|
|
7528
|
+
}
|
|
7529
|
+
function defaultTaskStore() {
|
|
7530
|
+
const map = /* @__PURE__ */ new Map();
|
|
7531
|
+
function prune(now) {
|
|
7532
|
+
for (const [key, { expiry }] of map) {
|
|
7533
|
+
if (expiry > now) break;
|
|
7534
|
+
map.delete(key);
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
return {
|
|
7538
|
+
get(taskId) {
|
|
7539
|
+
const now = Date.now();
|
|
7540
|
+
prune(now);
|
|
7541
|
+
const hit = map.get(taskId);
|
|
7542
|
+
return hit && hit.expiry > now ? hit.record : void 0;
|
|
7543
|
+
},
|
|
7544
|
+
set(taskId, value, ttlMs) {
|
|
7545
|
+
const now = Date.now();
|
|
7546
|
+
prune(now);
|
|
7547
|
+
map.delete(taskId);
|
|
7548
|
+
map.set(taskId, { record: value, expiry: now + ttlMs });
|
|
7549
|
+
}
|
|
7550
|
+
};
|
|
7551
|
+
}
|
|
7552
|
+
function createA2APaymentHandler(options) {
|
|
7553
|
+
const gate = _nullishCoalesce(options.gate, () => ( createPaymentGate(options)));
|
|
7554
|
+
const maxTimeoutSeconds = _nullishCoalesce(options.maxTimeoutSeconds, () => ( 600));
|
|
7555
|
+
const ttlMs = _nullishCoalesce(options.taskTtlMs, () => ( maxTimeoutSeconds * 1e3));
|
|
7556
|
+
const store = _nullishCoalesce(options.taskStore, () => ( defaultTaskStore()));
|
|
7557
|
+
function appendReceipt(taskId, entry) {
|
|
7558
|
+
const prior = _nullishCoalesce(_optionalChain([store, 'access', _138 => _138.get, 'call', _139 => _139(taskId), 'optionalAccess', _140 => _140.receipts]), () => ( []));
|
|
7559
|
+
const isDup = "success" in entry && entry.success === true && prior.some((r) => "transaction" in r && r.transaction === entry.transaction);
|
|
7560
|
+
const next = isDup ? prior : [...prior, entry];
|
|
7561
|
+
const receipts = next.length > MAX_TASK_RECEIPTS ? next.slice(-MAX_TASK_RECEIPTS) : next;
|
|
7562
|
+
store.set(taskId, { receipts }, ttlMs);
|
|
7563
|
+
return receipts;
|
|
7564
|
+
}
|
|
7565
|
+
function completedTask(taskId, receipts, artifacts, status = "payment-completed") {
|
|
7566
|
+
const metadata = { [A2A_STATUS_KEY]: status, ...toA2APaymentReceipts(receipts) };
|
|
7567
|
+
return {
|
|
7568
|
+
kind: "task",
|
|
7569
|
+
id: taskId,
|
|
7570
|
+
status: { state: "completed", message: { kind: "message", role: "agent", taskId, metadata } },
|
|
7571
|
+
...artifacts.length > 0 ? { artifacts } : {}
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
function reChallengeTask(taskId, result, attempted) {
|
|
7575
|
+
if (result.kind !== "invalid") {
|
|
7576
|
+
const metadata2 = {
|
|
7577
|
+
[A2A_STATUS_KEY]: "payment-required",
|
|
7578
|
+
[A2A_REQUIRED_KEY]: result.challenge
|
|
7579
|
+
};
|
|
7580
|
+
return {
|
|
7581
|
+
kind: "task",
|
|
7582
|
+
id: taskId,
|
|
7583
|
+
status: { state: "input-required", message: { kind: "message", role: "agent", taskId, metadata: metadata2 } }
|
|
7584
|
+
};
|
|
7585
|
+
}
|
|
7586
|
+
const network = _nullishCoalesce(attempted, () => ( singleNetworkOf(result.challenge)));
|
|
7587
|
+
const receipts = appendReceiptFailed(taskId, result.error, result.detail, network);
|
|
7588
|
+
const metadata = {
|
|
7589
|
+
[A2A_STATUS_KEY]: "payment-rejected",
|
|
7590
|
+
[A2A_ERROR_KEY]: toA2AErrorCode(result.error),
|
|
7591
|
+
[A2A_REQUIRED_KEY]: result.challenge,
|
|
7592
|
+
...toA2APaymentReceipts(receipts)
|
|
7593
|
+
};
|
|
7594
|
+
return {
|
|
7595
|
+
kind: "task",
|
|
7596
|
+
id: taskId,
|
|
7597
|
+
status: { state: "input-required", message: { kind: "message", role: "agent", taskId, metadata } }
|
|
7598
|
+
};
|
|
7599
|
+
}
|
|
7600
|
+
function failedTask(taskId, code, detail, network) {
|
|
7601
|
+
const receipts = appendReceiptFailed(taskId, code, detail, network);
|
|
7602
|
+
const metadata = {
|
|
7603
|
+
[A2A_STATUS_KEY]: "payment-failed",
|
|
7604
|
+
[A2A_ERROR_KEY]: toA2AErrorCode(code),
|
|
7605
|
+
...toA2APaymentReceipts(receipts)
|
|
7606
|
+
};
|
|
7607
|
+
return {
|
|
7608
|
+
kind: "task",
|
|
7609
|
+
id: taskId,
|
|
7610
|
+
status: { state: "failed", message: { kind: "message", role: "agent", taskId, metadata } }
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7613
|
+
function appendReceiptFailed(taskId, code, detail, network) {
|
|
7614
|
+
return appendReceipt(taskId, {
|
|
7615
|
+
success: false,
|
|
7616
|
+
transaction: "",
|
|
7617
|
+
...network ? { network } : {},
|
|
7618
|
+
errorReason: `${code}: ${detail}`
|
|
7619
|
+
});
|
|
7620
|
+
}
|
|
7621
|
+
async function handleMessage(message, taskId) {
|
|
7622
|
+
const id = _nullishCoalesce(_nullishCoalesce(taskId, () => ( message.taskId)), () => ( newTaskId()));
|
|
7623
|
+
const inbound = fromA2APaymentPayload(message);
|
|
7624
|
+
if (!inbound) {
|
|
7625
|
+
const { challenge } = await gate.challenge(resourceUrlFromMessage(message));
|
|
7626
|
+
store.set(id, { receipts: [] }, ttlMs);
|
|
7627
|
+
return toA2APaymentRequired(id, challenge);
|
|
7628
|
+
}
|
|
7629
|
+
const attempted = networkFromPayload(inbound.raw);
|
|
7630
|
+
let result;
|
|
7631
|
+
try {
|
|
7632
|
+
result = await gate.verifyObject(inbound.raw);
|
|
7633
|
+
} catch (err) {
|
|
7634
|
+
if (err instanceof _chunkMWBT7MCEcjs.SettlementError) {
|
|
7635
|
+
return failedTask(id, "settlement_failed", err.message, attempted);
|
|
7636
|
+
}
|
|
7637
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7638
|
+
return failedTask(id, "tx_reverted", detail, attempted);
|
|
7639
|
+
}
|
|
7640
|
+
switch (result.kind) {
|
|
7641
|
+
case "challenge":
|
|
7642
|
+
return reChallengeTask(id, result, attempted);
|
|
7643
|
+
case "invalid":
|
|
7644
|
+
return reChallengeTask(id, result, attempted);
|
|
7645
|
+
case "paid": {
|
|
7646
|
+
const receipts = appendReceipt(id, result.receipt);
|
|
7647
|
+
let artifacts = [];
|
|
7648
|
+
if (options.fulfill) {
|
|
7649
|
+
try {
|
|
7650
|
+
artifacts = await _asyncNullishCoalesce(await options.fulfill({ taskId: id, receipt: result.receipt, message }), async () => ( []));
|
|
7651
|
+
} catch (err) {
|
|
7652
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7653
|
+
const annotation = {
|
|
7654
|
+
name: "fulfillment-error",
|
|
7655
|
+
metadata: { "x402.fulfillment.error": detail, "x402.fulfillment.settled": true },
|
|
7656
|
+
parts: [{ kind: "text", text: `Payment settled, but serving the result failed: ${detail}` }]
|
|
7657
|
+
};
|
|
7658
|
+
return completedTask(id, receipts, [annotation]);
|
|
7659
|
+
}
|
|
7660
|
+
}
|
|
7661
|
+
return completedTask(id, receipts, artifacts);
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
function agentCardExtension(opts) {
|
|
7666
|
+
const uri = _optionalChain([opts, 'optionalAccess', _141 => _141.version]) === "v0.2" ? A2A_X402_EXTENSION_URI_V02 : A2A_X402_EXTENSION_URI_V01;
|
|
7667
|
+
return {
|
|
7668
|
+
uri,
|
|
7669
|
+
description: "Supports payments using the x402 protocol for on-chain settlement.",
|
|
7670
|
+
..._optionalChain([opts, 'optionalAccess', _142 => _142.required]) ? { required: true } : {}
|
|
7671
|
+
};
|
|
7672
|
+
}
|
|
7673
|
+
return { handleMessage, agentCardExtension, gate };
|
|
7674
|
+
}
|
|
7675
|
+
function singleNetworkOf(challenge) {
|
|
7676
|
+
const nets = new Set((_nullishCoalesce(challenge.accepts, () => ( []))).map((a) => a.network).filter(Boolean));
|
|
7677
|
+
return nets.size === 1 ? [...nets][0] : void 0;
|
|
7678
|
+
}
|
|
7679
|
+
function networkFromPayload(raw) {
|
|
7680
|
+
const v = raw;
|
|
7681
|
+
if (v && typeof _optionalChain([v, 'access', _143 => _143.accepted, 'optionalAccess', _144 => _144.network]) === "string") return v.accepted.network;
|
|
7682
|
+
if (v && typeof v.network === "string") return v.network;
|
|
7683
|
+
return void 0;
|
|
7684
|
+
}
|
|
7685
|
+
function newTaskId() {
|
|
7686
|
+
return `task-${globalThis.crypto.randomUUID()}`;
|
|
7687
|
+
}
|
|
7688
|
+
function resourceUrlFromMessage(message) {
|
|
7689
|
+
for (const part of _nullishCoalesce(message.parts, () => ( []))) {
|
|
7690
|
+
const data = part.data;
|
|
7691
|
+
if (data && typeof data.url === "string") return data.url;
|
|
7692
|
+
}
|
|
7693
|
+
return "";
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
|
|
7697
|
+
|
|
7698
|
+
|
|
7699
|
+
|
|
7700
|
+
|
|
7701
|
+
|
|
7702
|
+
|
|
7703
|
+
|
|
7704
|
+
|
|
7705
|
+
|
|
7706
|
+
|
|
7707
|
+
|
|
7708
|
+
|
|
7709
|
+
|
|
7710
|
+
|
|
7711
|
+
|
|
7712
|
+
|
|
7713
|
+
|
|
7714
|
+
|
|
7715
|
+
|
|
7716
|
+
|
|
7717
|
+
|
|
7718
|
+
|
|
7719
|
+
|
|
7720
|
+
|
|
7721
|
+
|
|
7722
|
+
|
|
7723
|
+
|
|
7724
|
+
|
|
7725
|
+
|
|
7726
|
+
|
|
7727
|
+
|
|
7728
|
+
|
|
7729
|
+
|
|
5908
7730
|
|
|
5909
7731
|
|
|
5910
7732
|
|
|
@@ -6003,4 +7825,4 @@ async function deliverReceipt(receipt, options) {
|
|
|
6003
7825
|
|
|
6004
7826
|
|
|
6005
7827
|
|
|
6006
|
-
exports.BRAND = BRAND; exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError =
|
|
7828
|
+
exports.A2A_ERROR_KEY = A2A_ERROR_KEY; exports.A2A_EXTENSIONS_HEADER = A2A_EXTENSIONS_HEADER; exports.A2A_PAYLOAD_KEY = A2A_PAYLOAD_KEY; exports.A2A_RECEIPTS_KEY = A2A_RECEIPTS_KEY; exports.A2A_REQUIRED_KEY = A2A_REQUIRED_KEY; exports.A2A_STATUS_KEY = A2A_STATUS_KEY; exports.A2A_X402_EXTENSION_URI_V01 = A2A_X402_EXTENSION_URI_V01; exports.A2A_X402_EXTENSION_URI_V02 = A2A_X402_EXTENSION_URI_V02; exports.BRAND = BRAND; exports.BUILTIN_DENOMS = BUILTIN_DENOMS; exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkMWBT7MCEcjs.ConfirmationTimeoutError; exports.DENOM_PRECISION = DENOM_PRECISION; exports.DIRECTORY_INFO = DIRECTORY_INFO; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.EXT_OFFER_RECEIPT = EXT_OFFER_RECEIPT; exports.GENERATOR = GENERATOR; exports.HEADER_REQUIRED = HEADER_REQUIRED; exports.HEADER_RESPONSE = HEADER_RESPONSE; exports.HEADER_RESPONSE_V1 = HEADER_RESPONSE_V1; exports.HEADER_SIGNATURE = HEADER_SIGNATURE; exports.HEADER_SIGNATURE_V1 = HEADER_SIGNATURE_V1; exports.InsufficientFundsError = _chunkMWBT7MCEcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkMWBT7MCEcjs.InvalidEnvelopeError; exports.KNOWN_FACILITATORS = KNOWN_FACILITATORS; exports.MaxRetriesExceededError = _chunkMWBT7MCEcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkMWBT7MCEcjs.MissingDriverError; exports.MultiChainPayer = MultiChainPayer; exports.NoCompatibleAcceptError = _chunkMWBT7MCEcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkMWBT7MCEcjs.NonReplayableBodyError; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.PERMIT2_PROXY_CHAIN_IDS = PERMIT2_PROXY_CHAIN_IDS; exports.PERMIT2_UPTO_WITNESS_TYPES = PERMIT2_UPTO_WITNESS_TYPES; exports.PERMIT2_WITNESS_TYPES = PERMIT2_WITNESS_TYPES; exports.PIPRAIL_AGENT_GUIDE = PIPRAIL_AGENT_GUIDE; exports.POWERED_BY = POWERED_BY; exports.PaymentDeclinedError = _chunkMWBT7MCEcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkMWBT7MCEcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkMWBT7MCEcjs.PipRailError; exports.REGISTER_ATTRIBUTION = REGISTER_ATTRIBUTION; exports.RecipientNotReadyError = _chunkMWBT7MCEcjs.RecipientNotReadyError; exports.SettlementError = _chunkMWBT7MCEcjs.SettlementError; exports.SpendLedger = SpendLedger; exports.UPTO_PROXY_CHAIN_IDS = UPTO_PROXY_CHAIN_IDS; exports.UnknownTokenError = _chunkMWBT7MCEcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkMWBT7MCEcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkMWBT7MCEcjs.UnsupportedSchemeError; exports.VERIFY_CODE_TO_A2A_ERROR = VERIFY_CODE_TO_A2A_ERROR; exports.WalletRequiredError = _chunkMWBT7MCEcjs.WalletRequiredError; exports.WrongChainError = _chunkMWBT7MCEcjs.WrongChainError; exports.WrongFamilyError = _chunkMWBT7MCEcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_PERMIT2_PROXY; exports.X402_UPTO_PERMIT2_PROXY = X402_UPTO_PERMIT2_PROXY; exports.agentGuide = agentGuide; exports.appendAttribution = appendAttribution; exports.appendKeywords = appendKeywords; exports.buildBazaarExtension = buildBazaarExtension; exports.buildChallengeHeader = buildChallengeHeader; exports.buildEndpointInfo = buildEndpointInfo; exports.buildExactAuthorization = buildExactAuthorization; exports.buildExactSignatureHeader = buildExactSignatureHeader; exports.buildOpenApi = buildOpenApi; exports.buildReceiptExtension = buildReceiptExtension; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSelfDescription = buildSelfDescription; exports.buildSignatureHeader = buildSignatureHeader; exports.buildUptoSignatureHeader = buildUptoSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.classifyChallenge = classifyChallenge; exports.createA2APaymentHandler = createA2APaymentHandler; exports.createPaymentGate = createPaymentGate; exports.decodeBase64Json = decodeBase64Json; exports.decorateOutcome = decorateOutcome; exports.deliverReceipt = deliverReceipt; exports.denomOf = denomOf; exports.describeChallenge = describeChallenge; exports.discoveryHeaders = discoveryHeaders; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.explainDecline = explainDecline; exports.facilitatorCoverage = facilitatorCoverage; exports.fetchAcross = fetchAcross; exports.firstKeylessFacilitator = firstKeylessFacilitator; exports.formatSpendReport = formatSpendReport; exports.fromA2APaymentPayload = fromA2APaymentPayload; exports.fromA2APaymentRequired = fromA2APaymentRequired; exports.getDirectoryInfo = getDirectoryInfo; exports.isPermit2ProxyChain = isPermit2ProxyChain; exports.isUptoProxyChain = isUptoProxyChain; exports.knownFacilitatorsFor = knownFacilitatorsFor; exports.memorySpendStore = _chunkO4UQOZ4Zcjs.memorySpendStore; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactObject = parseExactObject; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseFacilitatorSupported = parseFacilitatorSupported; exports.parseReceipt = parseReceipt; exports.parseReceiptExtension = parseReceiptExtension; exports.parseSettleResponse = parseSettleResponse; exports.parseSignatureHeader = parseSignatureHeader; exports.parseSignatureObject = parseSignatureObject; exports.parseUptoObject = parseUptoObject; exports.parseUptoPaymentHeader = parseUptoPaymentHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.rankResources = rankResources; exports.readExactDomain = readExactDomain; exports.register402Index = register402Index; exports.registerDriver = registerDriver; exports.registerX402Scan = registerX402Scan; exports.renderLandingPage = renderLandingPage; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.scoreResource = scoreResource; exports.searchOpenIndexes = searchOpenIndexes; exports.settleViaFacilitator = settleViaFacilitator; exports.summarizePlan = summarizePlan; exports.toA2AErrorCode = toA2AErrorCode; exports.toA2APaymentFailed = toA2APaymentFailed; exports.toA2APaymentReceipts = toA2APaymentReceipts; exports.toA2APaymentRequired = toA2APaymentRequired; exports.toInsufficientFundsError = _chunkMWBT7MCEcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
|