@piprail/sdk 2.7.0 → 2.9.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 +96 -0
- package/README.md +5 -3
- package/dist/{algorand-WB6PBJU4.js → algorand-AA3WXKW4.js} +1 -1
- package/dist/{algorand-TMA62DN2.cjs → algorand-FCEECDG6.cjs} +32 -32
- package/dist/{aptos-JQMZNTMD.cjs → aptos-GHJPO6JJ.cjs} +31 -31
- package/dist/{aptos-QAAXIUY3.js → aptos-LY67Q6QF.js} +1 -1
- package/dist/{chunk-JG6KRAW6.cjs → chunk-MWBT7MCE.cjs} +13 -1
- package/dist/chunk-O4UQOZ4Z.cjs +14 -0
- package/dist/{chunk-7XK22JSQ.js → chunk-SC2ZYDHD.js} +12 -0
- package/dist/chunk-SK3CB7UA.js +14 -0
- package/dist/index.cjs +754 -238
- package/dist/index.d.cts +2386 -2577
- package/dist/index.d.ts +2386 -2577
- package/dist/index.js +595 -79
- package/dist/ledger-BtzrfO-3.d.cts +658 -0
- package/dist/ledger-BtzrfO-3.d.ts +658 -0
- package/dist/{near-H5AQ253I.cjs → near-6KAQVVG2.cjs} +26 -26
- package/dist/{near-OTPQD6BI.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/{solana-3FMCWSEE.js → solana-ELUWO6N5.js} +33 -2
- package/dist/{solana-M3VOHCMO.cjs → solana-MYF4HBO4.cjs} +64 -33
- package/dist/{stellar-U5NCRIOJ.js → stellar-BEMT7UYF.js} +1 -1
- package/dist/{stellar-FW6C6FBE.cjs → stellar-SUKASK4N.cjs} +20 -20
- package/dist/{sui-Y53M4GUM.js → sui-F5JQ2N6I.js} +1 -1
- package/dist/{sui-47C2KEZI.cjs → sui-VE5LT7BL.cjs} +16 -16
- package/dist/{ton-5ZPT5PSP.js → ton-7GKCTC5H.js} +11 -7
- package/dist/{ton-MMPKWT6N.cjs → ton-AOR3EURW.cjs} +25 -21
- package/dist/{tron-JOT4STIG.cjs → tron-EMFXDFHW.cjs} +24 -24
- package/dist/{tron-WYS4X2I5.js → tron-ZZZS3FNN.js} +1 -1
- package/dist/{xrpl-2MZEOIFY.js → xrpl-DD7TJL5L.js} +1 -1
- package/dist/{xrpl-PECT4IMX.cjs → xrpl-Y6SQNYLC.cjs} +20 -20
- package/package.json +11 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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;
|
|
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;
|
|
2
2
|
|
|
3
|
+
var _chunkO4UQOZ4Zcjs = require('./chunk-O4UQOZ4Z.cjs');
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
|
|
@@ -24,7 +25,10 @@
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
var _chunkMWBT7MCEcjs = require('./chunk-MWBT7MCE.cjs');
|
|
28
32
|
|
|
29
33
|
// src/drivers/registry.ts
|
|
30
34
|
var byFamily = /* @__PURE__ */ new Map();
|
|
@@ -53,13 +57,13 @@ function resolveNetwork(opts) {
|
|
|
53
57
|
const family = familyForChain(opts.chain);
|
|
54
58
|
const driver = byFamily.get(family);
|
|
55
59
|
if (!driver) {
|
|
56
|
-
throw new (0,
|
|
60
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
57
61
|
`No driver registered for the "${family}" family \u2014 it may not be mounted yet (use the async resolveNetwork()).`
|
|
58
62
|
);
|
|
59
63
|
}
|
|
60
64
|
const net = driver.resolve(opts);
|
|
61
65
|
if (!net) {
|
|
62
|
-
throw new (0,
|
|
66
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
63
67
|
`The ${family} driver didn't recognise this chain input.`
|
|
64
68
|
);
|
|
65
69
|
}
|
|
@@ -320,12 +324,12 @@ function resolveChain(input, rpcUrlOverride) {
|
|
|
320
324
|
|
|
321
325
|
var _accounts = require('viem/accounts');
|
|
322
326
|
function createWalletAdapter(config, resolved) {
|
|
323
|
-
|
|
327
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, config, "EVM");
|
|
324
328
|
if ("key" in config) {
|
|
325
329
|
const key = config.key;
|
|
326
330
|
if (typeof key !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(key)) {
|
|
327
331
|
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,
|
|
332
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
329
333
|
`chain is EVM; the wallet { key } is not a valid 0x\u2026 32-byte hex private key${hint}`
|
|
330
334
|
);
|
|
331
335
|
}
|
|
@@ -333,7 +337,7 @@ function createWalletAdapter(config, resolved) {
|
|
|
333
337
|
try {
|
|
334
338
|
account = _accounts.privateKeyToAccount.call(void 0, key);
|
|
335
339
|
} catch (err) {
|
|
336
|
-
throw new (0,
|
|
340
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
337
341
|
`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
342
|
);
|
|
339
343
|
}
|
|
@@ -343,12 +347,12 @@ function createWalletAdapter(config, resolved) {
|
|
|
343
347
|
}
|
|
344
348
|
const wc = config.walletClient;
|
|
345
349
|
if (!wc.account) {
|
|
346
|
-
throw new (0,
|
|
350
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
347
351
|
"chain is EVM; the provided walletClient has no attached account. Use `createWalletClient({ account, chain, transport })`, or pass { key }."
|
|
348
352
|
);
|
|
349
353
|
}
|
|
350
354
|
if (wc.chain && wc.chain.id !== resolved.chainId) {
|
|
351
|
-
throw new (0,
|
|
355
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
|
|
352
356
|
`PipRailClient: walletClient is on chain ${wc.chain.id} but the SDK was configured with chain ${resolved.chainId}. They must match.`
|
|
353
357
|
);
|
|
354
358
|
}
|
|
@@ -678,7 +682,7 @@ async function payExactEvm(input) {
|
|
|
678
682
|
code = void 0;
|
|
679
683
|
}
|
|
680
684
|
if (code && code !== "0x") {
|
|
681
|
-
throw new (0,
|
|
685
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
682
686
|
`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
687
|
);
|
|
684
688
|
}
|
|
@@ -688,13 +692,13 @@ async function payExactEvm(input) {
|
|
|
688
692
|
domain = await readExactDomain(publicClient, accept.asset);
|
|
689
693
|
}
|
|
690
694
|
if (!domain) {
|
|
691
|
-
throw new (0,
|
|
695
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
692
696
|
`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
697
|
);
|
|
694
698
|
}
|
|
695
699
|
const g = globalThis.crypto;
|
|
696
700
|
if (!_optionalChain([g, 'optionalAccess', _5 => _5.getRandomValues])) {
|
|
697
|
-
throw new (0,
|
|
701
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
698
702
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the exact rail needs a CSPRNG nonce."
|
|
699
703
|
);
|
|
700
704
|
}
|
|
@@ -951,7 +955,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
951
955
|
args: writeArgs
|
|
952
956
|
});
|
|
953
957
|
} catch (err) {
|
|
954
|
-
throw new (0,
|
|
958
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
955
959
|
`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
960
|
{ cause: err }
|
|
957
961
|
);
|
|
@@ -963,7 +967,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
963
967
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
964
968
|
}
|
|
965
969
|
} catch (err) {
|
|
966
|
-
throw new (0,
|
|
970
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
967
971
|
`exact settle: broadcast ${txHash} but couldn't confirm it (${shorten(err instanceof Error ? err.message : String(err))}).`,
|
|
968
972
|
{ cause: err }
|
|
969
973
|
);
|
|
@@ -1094,7 +1098,7 @@ function shorten2(msg) {
|
|
|
1094
1098
|
function randomPermit2Nonce() {
|
|
1095
1099
|
const g = globalThis.crypto;
|
|
1096
1100
|
if (!_optionalChain([g, 'optionalAccess', _8 => _8.getRandomValues])) {
|
|
1097
|
-
throw new (0,
|
|
1101
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1098
1102
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the permit2 rail needs a CSPRNG nonce."
|
|
1099
1103
|
);
|
|
1100
1104
|
}
|
|
@@ -1113,7 +1117,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1113
1117
|
args: [account.address, PERMIT2_ADDRESS]
|
|
1114
1118
|
});
|
|
1115
1119
|
} catch (err) {
|
|
1116
|
-
throw new (0,
|
|
1120
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1117
1121
|
`permit2: couldn't read the Permit2 allowance for ${token} (${shorten2(err instanceof Error ? err.message : String(err))}).`
|
|
1118
1122
|
);
|
|
1119
1123
|
}
|
|
@@ -1130,7 +1134,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1130
1134
|
await publicClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1131
1135
|
return hash;
|
|
1132
1136
|
} catch (err) {
|
|
1133
|
-
throw _nullishCoalesce(
|
|
1137
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1134
1138
|
`permit2: the one-time Permit2 approval for ${token} failed to broadcast (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1135
1139
|
{ cause: err }
|
|
1136
1140
|
)));
|
|
@@ -1145,7 +1149,7 @@ async function payPermit2Evm(input) {
|
|
|
1145
1149
|
code = void 0;
|
|
1146
1150
|
}
|
|
1147
1151
|
if (code && code !== "0x") {
|
|
1148
|
-
throw new (0,
|
|
1152
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1149
1153
|
`permit2 buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
|
|
1150
1154
|
);
|
|
1151
1155
|
}
|
|
@@ -1320,7 +1324,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1320
1324
|
args: settleArgs
|
|
1321
1325
|
});
|
|
1322
1326
|
} catch (err) {
|
|
1323
|
-
throw new (0,
|
|
1327
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1324
1328
|
`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
1329
|
{ cause: err }
|
|
1326
1330
|
);
|
|
@@ -1332,7 +1336,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1332
1336
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
1333
1337
|
}
|
|
1334
1338
|
} catch (err) {
|
|
1335
|
-
throw new (0,
|
|
1339
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1336
1340
|
`permit2 settle: broadcast ${txHash} but couldn't confirm it (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1337
1341
|
{ cause: err }
|
|
1338
1342
|
);
|
|
@@ -1584,15 +1588,15 @@ function makeEvmNetwork(resolved) {
|
|
|
1584
1588
|
const info = resolved.tokens[token.toUpperCase()];
|
|
1585
1589
|
if (!info) {
|
|
1586
1590
|
const known = Object.keys(resolved.tokens).join(", ") || "(none built in)";
|
|
1587
|
-
throw new (0,
|
|
1591
|
+
throw new (0, _chunkMWBT7MCEcjs.UnknownTokenError)(
|
|
1588
1592
|
`token "${token}" isn't built in for ${resolved.chain.name} (known: ${known}). Pass { address, decimals } instead, or use 'native'.`
|
|
1589
1593
|
);
|
|
1590
1594
|
}
|
|
1591
1595
|
return { asset: info.address, decimals: info.decimals, symbol: info.symbol };
|
|
1592
1596
|
}
|
|
1593
|
-
|
|
1597
|
+
_chunkMWBT7MCEcjs.rejectForeignToken.call(void 0, token, "evm", network);
|
|
1594
1598
|
if (!("address" in token)) {
|
|
1595
|
-
throw new (0,
|
|
1599
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1596
1600
|
`chain ${network} is EVM; a custom token must be { address, decimals }.`
|
|
1597
1601
|
);
|
|
1598
1602
|
}
|
|
@@ -1624,20 +1628,20 @@ function makeEvmNetwork(resolved) {
|
|
|
1624
1628
|
},
|
|
1625
1629
|
assertValidPayTo(payTo) {
|
|
1626
1630
|
if (!_viem.isAddress.call(void 0, payTo)) {
|
|
1627
|
-
throw new (0,
|
|
1631
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1628
1632
|
`chain ${network} is EVM, but payTo "${payTo}" is not a valid 0x address.`
|
|
1629
1633
|
);
|
|
1630
1634
|
}
|
|
1631
1635
|
},
|
|
1632
1636
|
bindWallet(wallet) {
|
|
1633
1637
|
if (typeof wallet !== "object" || wallet === null) {
|
|
1634
|
-
throw new (0,
|
|
1638
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1635
1639
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1636
1640
|
);
|
|
1637
1641
|
}
|
|
1638
|
-
|
|
1642
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, wallet, "EVM");
|
|
1639
1643
|
if (!("key" in wallet) && !("walletClient" in wallet)) {
|
|
1640
|
-
throw new (0,
|
|
1644
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1641
1645
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1642
1646
|
);
|
|
1643
1647
|
}
|
|
@@ -1654,12 +1658,12 @@ function makeEvmNetwork(resolved) {
|
|
|
1654
1658
|
});
|
|
1655
1659
|
} catch (err) {
|
|
1656
1660
|
if (isViemInsufficientFunds(err)) {
|
|
1657
|
-
throw new (0,
|
|
1661
|
+
throw new (0, _chunkMWBT7MCEcjs.InsufficientFundsError)(
|
|
1658
1662
|
err instanceof Error ? err.message : "Insufficient funds for payment.",
|
|
1659
1663
|
{ cause: err }
|
|
1660
1664
|
);
|
|
1661
1665
|
}
|
|
1662
|
-
throw _nullishCoalesce(
|
|
1666
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
|
|
1663
1667
|
}
|
|
1664
1668
|
},
|
|
1665
1669
|
async confirm(ref, minConfirmations) {
|
|
@@ -1670,7 +1674,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1670
1674
|
});
|
|
1671
1675
|
return { height: receipt.blockNumber.toString() };
|
|
1672
1676
|
} catch (err) {
|
|
1673
|
-
throw new (0,
|
|
1677
|
+
throw new (0, _chunkMWBT7MCEcjs.ConfirmationTimeoutError)(
|
|
1674
1678
|
`EVM tx ${ref} did not reach ${minConfirmations} confirmation(s) in time.`,
|
|
1675
1679
|
{ cause: err }
|
|
1676
1680
|
);
|
|
@@ -1680,7 +1684,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1680
1684
|
const { decimals, symbol } = resolved.chain.nativeCurrency;
|
|
1681
1685
|
if (accept.scheme === "exact") {
|
|
1682
1686
|
const permit2 = accept.extra.assetTransferMethod === "permit2";
|
|
1683
|
-
return
|
|
1687
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1684
1688
|
symbol,
|
|
1685
1689
|
decimals,
|
|
1686
1690
|
fee: 0n,
|
|
@@ -1691,7 +1695,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1691
1695
|
const gasLimit = accept.asset === "native" ? 21000n : 65000n;
|
|
1692
1696
|
try {
|
|
1693
1697
|
const gasPrice = await publicClient.getGasPrice();
|
|
1694
|
-
return
|
|
1698
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1695
1699
|
symbol,
|
|
1696
1700
|
decimals,
|
|
1697
1701
|
fee: gasPrice * gasLimit,
|
|
@@ -1700,7 +1704,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1700
1704
|
});
|
|
1701
1705
|
} catch (e20) {
|
|
1702
1706
|
const gasPrice = 5000000000n;
|
|
1703
|
-
return
|
|
1707
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1704
1708
|
symbol,
|
|
1705
1709
|
decimals,
|
|
1706
1710
|
fee: gasPrice * gasLimit,
|
|
@@ -1840,9 +1844,9 @@ var loaders = {
|
|
|
1840
1844
|
solana: async () => {
|
|
1841
1845
|
let mod;
|
|
1842
1846
|
try {
|
|
1843
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-
|
|
1847
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-MYF4HBO4.cjs")));
|
|
1844
1848
|
} catch (cause) {
|
|
1845
|
-
throw new (0,
|
|
1849
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1846
1850
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
1847
1851
|
{ cause }
|
|
1848
1852
|
);
|
|
@@ -1852,9 +1856,9 @@ var loaders = {
|
|
|
1852
1856
|
ton: async () => {
|
|
1853
1857
|
let mod;
|
|
1854
1858
|
try {
|
|
1855
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-
|
|
1859
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-AOR3EURW.cjs")));
|
|
1856
1860
|
} catch (cause) {
|
|
1857
|
-
throw new (0,
|
|
1861
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1858
1862
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
1859
1863
|
{ cause }
|
|
1860
1864
|
);
|
|
@@ -1864,9 +1868,9 @@ var loaders = {
|
|
|
1864
1868
|
stellar: async () => {
|
|
1865
1869
|
let mod;
|
|
1866
1870
|
try {
|
|
1867
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-
|
|
1871
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-SUKASK4N.cjs")));
|
|
1868
1872
|
} catch (cause) {
|
|
1869
|
-
throw new (0,
|
|
1873
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1870
1874
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
1871
1875
|
{ cause }
|
|
1872
1876
|
);
|
|
@@ -1876,9 +1880,9 @@ var loaders = {
|
|
|
1876
1880
|
xrpl: async () => {
|
|
1877
1881
|
let mod;
|
|
1878
1882
|
try {
|
|
1879
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-
|
|
1883
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-Y6SQNYLC.cjs")));
|
|
1880
1884
|
} catch (cause) {
|
|
1881
|
-
throw new (0,
|
|
1885
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1882
1886
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
1883
1887
|
{ cause }
|
|
1884
1888
|
);
|
|
@@ -1888,9 +1892,9 @@ var loaders = {
|
|
|
1888
1892
|
tron: async () => {
|
|
1889
1893
|
let mod;
|
|
1890
1894
|
try {
|
|
1891
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-
|
|
1895
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-EMFXDFHW.cjs")));
|
|
1892
1896
|
} catch (cause) {
|
|
1893
|
-
throw new (0,
|
|
1897
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1894
1898
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
1895
1899
|
{ cause }
|
|
1896
1900
|
);
|
|
@@ -1900,9 +1904,9 @@ var loaders = {
|
|
|
1900
1904
|
sui: async () => {
|
|
1901
1905
|
let mod;
|
|
1902
1906
|
try {
|
|
1903
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-
|
|
1907
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-VE5LT7BL.cjs")));
|
|
1904
1908
|
} catch (cause) {
|
|
1905
|
-
throw new (0,
|
|
1909
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1906
1910
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
1907
1911
|
{ cause }
|
|
1908
1912
|
);
|
|
@@ -1912,9 +1916,9 @@ var loaders = {
|
|
|
1912
1916
|
near: async () => {
|
|
1913
1917
|
let mod;
|
|
1914
1918
|
try {
|
|
1915
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-
|
|
1919
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-6KAQVVG2.cjs")));
|
|
1916
1920
|
} catch (cause) {
|
|
1917
|
-
throw new (0,
|
|
1921
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1918
1922
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
1919
1923
|
{ cause }
|
|
1920
1924
|
);
|
|
@@ -1924,9 +1928,9 @@ var loaders = {
|
|
|
1924
1928
|
aptos: async () => {
|
|
1925
1929
|
let mod;
|
|
1926
1930
|
try {
|
|
1927
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-
|
|
1931
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-GHJPO6JJ.cjs")));
|
|
1928
1932
|
} catch (cause) {
|
|
1929
|
-
throw new (0,
|
|
1933
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1930
1934
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
1931
1935
|
{ cause }
|
|
1932
1936
|
);
|
|
@@ -1936,9 +1940,9 @@ var loaders = {
|
|
|
1936
1940
|
algorand: async () => {
|
|
1937
1941
|
let mod;
|
|
1938
1942
|
try {
|
|
1939
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-
|
|
1943
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-FCEECDG6.cjs")));
|
|
1940
1944
|
} catch (cause) {
|
|
1941
|
-
throw new (0,
|
|
1945
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1942
1946
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
1943
1947
|
{ cause }
|
|
1944
1948
|
);
|
|
@@ -2027,7 +2031,7 @@ var SLUG_TO_CAIP2 = {
|
|
|
2027
2031
|
bsc: "eip155:56",
|
|
2028
2032
|
// non-EVM families — values mirror each driver's bound caip2 exactly
|
|
2029
2033
|
solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
2030
|
-
ton: "
|
|
2034
|
+
ton: "tvm:-239",
|
|
2031
2035
|
tron: "tron:mainnet",
|
|
2032
2036
|
near: "near:mainnet",
|
|
2033
2037
|
sui: "sui:mainnet",
|
|
@@ -2036,7 +2040,12 @@ var SLUG_TO_CAIP2 = {
|
|
|
2036
2040
|
stellar: "stellar:pubnet",
|
|
2037
2041
|
xrpl: "xrpl:0"
|
|
2038
2042
|
};
|
|
2043
|
+
var LEGACY_CAIP2_ALIAS = {
|
|
2044
|
+
"ton:-239": "tvm:-239"
|
|
2045
|
+
};
|
|
2039
2046
|
function normalizeNetwork(network) {
|
|
2047
|
+
const legacy = LEGACY_CAIP2_ALIAS[network];
|
|
2048
|
+
if (legacy) return legacy;
|
|
2040
2049
|
if (network.includes(":")) return network;
|
|
2041
2050
|
return _nullishCoalesce(SLUG_TO_CAIP2[network.toLowerCase()], () => ( network));
|
|
2042
2051
|
}
|
|
@@ -2577,6 +2586,53 @@ function encodeBase642(str) {
|
|
|
2577
2586
|
}
|
|
2578
2587
|
|
|
2579
2588
|
// src/policy.ts
|
|
2589
|
+
var DENOM_PRECISION = _chunkMWBT7MCEcjs.MAX_DECIMALS;
|
|
2590
|
+
var BUILTIN_DENOMS = {
|
|
2591
|
+
USDC: "USD",
|
|
2592
|
+
USDT: "USD",
|
|
2593
|
+
USD1: "USD",
|
|
2594
|
+
FDUSD: "USD",
|
|
2595
|
+
RLUSD: "USD",
|
|
2596
|
+
EURC: "EUR"
|
|
2597
|
+
};
|
|
2598
|
+
function denomOf(symbol, asset, policy) {
|
|
2599
|
+
if (asset === "native") return void 0;
|
|
2600
|
+
const norm = (v) => typeof v === "string" && v.trim() !== "" ? v.trim().toUpperCase() : void 0;
|
|
2601
|
+
const override = _optionalChain([policy, 'optionalAccess', _18 => _18.denomFor]);
|
|
2602
|
+
if (override && typeof override === "object") {
|
|
2603
|
+
if (asset && asset in override) {
|
|
2604
|
+
const d = norm(override[asset]);
|
|
2605
|
+
if (d) return d;
|
|
2606
|
+
}
|
|
2607
|
+
if (symbol) {
|
|
2608
|
+
const hit = Object.entries(override).find(([k]) => k.trim().toUpperCase() === symbol.trim().toUpperCase());
|
|
2609
|
+
if (hit) {
|
|
2610
|
+
const d = norm(hit[1]);
|
|
2611
|
+
if (d) return d;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
if (symbol) {
|
|
2616
|
+
const builtin = BUILTIN_DENOMS[symbol.trim().toUpperCase()];
|
|
2617
|
+
if (builtin) return builtin;
|
|
2618
|
+
}
|
|
2619
|
+
return void 0;
|
|
2620
|
+
}
|
|
2621
|
+
function scaleToDenom(amountBase, decimals) {
|
|
2622
|
+
if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > DENOM_PRECISION) return null;
|
|
2623
|
+
return amountBase * 10n ** BigInt(DENOM_PRECISION - decimals);
|
|
2624
|
+
}
|
|
2625
|
+
function capForDenom(caps, denom) {
|
|
2626
|
+
if (!caps || typeof caps !== "object") return void 0;
|
|
2627
|
+
const target = denom.trim().toUpperCase();
|
|
2628
|
+
let best;
|
|
2629
|
+
for (const [k, v] of Object.entries(caps)) {
|
|
2630
|
+
if (typeof k !== "string" || typeof v !== "string") continue;
|
|
2631
|
+
if (k.trim().toUpperCase() !== target) continue;
|
|
2632
|
+
if (best === void 0 || Number(v) < Number(best)) best = v;
|
|
2633
|
+
}
|
|
2634
|
+
return best;
|
|
2635
|
+
}
|
|
2580
2636
|
var ALLOW = { allowed: true };
|
|
2581
2637
|
var deny = (code, reason) => ({
|
|
2582
2638
|
allowed: false,
|
|
@@ -2643,7 +2699,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2643
2699
|
}
|
|
2644
2700
|
}
|
|
2645
2701
|
if (policy.maxAmount !== void 0) {
|
|
2646
|
-
const cap =
|
|
2702
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
|
|
2647
2703
|
if (intent.amountBase > cap) {
|
|
2648
2704
|
return deny(
|
|
2649
2705
|
"MAX_AMOUNT",
|
|
@@ -2652,7 +2708,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2652
2708
|
}
|
|
2653
2709
|
}
|
|
2654
2710
|
if (policy.maxTotal !== void 0) {
|
|
2655
|
-
const cap =
|
|
2711
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
|
|
2656
2712
|
if (spentForAssetBase + intent.amountBase > cap) {
|
|
2657
2713
|
return deny(
|
|
2658
2714
|
"MAX_TOTAL",
|
|
@@ -2660,8 +2716,36 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2660
2716
|
);
|
|
2661
2717
|
}
|
|
2662
2718
|
}
|
|
2719
|
+
if (ctx && policy.maxTotalPerDenom) {
|
|
2720
|
+
const denom = denomOf(intent.symbol, intent.asset, policy);
|
|
2721
|
+
const capStr = denom ? capForDenom(policy.maxTotalPerDenom, denom) : void 0;
|
|
2722
|
+
if (denom && capStr !== void 0) {
|
|
2723
|
+
const thisScaled = scaleToDenom(intent.amountBase, intent.decimals);
|
|
2724
|
+
if (thisScaled === null) {
|
|
2725
|
+
return deny(
|
|
2726
|
+
"MAX_TOTAL_DENOM",
|
|
2727
|
+
`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).`
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2730
|
+
const capScaled = _chunkMWBT7MCEcjs.floorUnits.call(void 0, capStr, DENOM_PRECISION);
|
|
2731
|
+
if ((_nullishCoalesce(ctx.spentInDenomScaled, () => ( 0n))) + thisScaled > capScaled) {
|
|
2732
|
+
return deny(
|
|
2733
|
+
"MAX_TOTAL_DENOM",
|
|
2734
|
+
`this payment would push total ${denom} spend past policy.maxTotalPerDenom.${denom} (${capStr}) \u2014 summed across every ${denom} token and chain.`
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
if (ctx && policy.maxPayments !== void 0) {
|
|
2740
|
+
if ((_nullishCoalesce(ctx.paymentCount, () => ( 0))) + 1 > policy.maxPayments) {
|
|
2741
|
+
return deny(
|
|
2742
|
+
"MAX_PAYMENTS",
|
|
2743
|
+
`this payment would exceed policy.maxPayments (${policy.maxPayments}); already settled ${_nullishCoalesce(ctx.paymentCount, () => ( 0))}.`
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2663
2747
|
if (ctx && policy.windowTotal !== void 0 && policy.windowSeconds !== void 0) {
|
|
2664
|
-
const cap =
|
|
2748
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.windowTotal, intent.decimals);
|
|
2665
2749
|
if (ctx.spentInWindowBase + intent.amountBase > cap) {
|
|
2666
2750
|
return deny(
|
|
2667
2751
|
"WINDOW_TOTAL",
|
|
@@ -2669,44 +2753,150 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
|
|
|
2669
2753
|
);
|
|
2670
2754
|
}
|
|
2671
2755
|
}
|
|
2756
|
+
if (ctx && policy.maxPaymentsPerWindow !== void 0 && policy.windowSeconds !== void 0) {
|
|
2757
|
+
if ((_nullishCoalesce(ctx.paymentCountInWindow, () => ( 0))) + 1 > policy.maxPaymentsPerWindow) {
|
|
2758
|
+
return deny(
|
|
2759
|
+
"WINDOW_COUNT",
|
|
2760
|
+
`this payment would exceed policy.maxPaymentsPerWindow (${policy.maxPaymentsPerWindow}) within the last ${policy.windowSeconds}s.`
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2672
2764
|
return ALLOW;
|
|
2673
2765
|
}
|
|
2674
2766
|
|
|
2675
2767
|
// src/ledger.ts
|
|
2676
2768
|
var keyFor = (network, asset) => `${network}|${asset}`;
|
|
2677
|
-
var SpendLedger = (_class = class {
|
|
2769
|
+
var SpendLedger = (_class = class {
|
|
2678
2770
|
__init() {this.records = []}
|
|
2679
2771
|
__init2() {this.buckets = /* @__PURE__ */ new Map()}
|
|
2772
|
+
/** Per-denomination running total, scaled to {@link DENOM_PRECISION}. Keyed by the
|
|
2773
|
+
* UPPERCASE denomination so lookups are case-insensitive. */
|
|
2774
|
+
__init3() {this.denomTotals = /* @__PURE__ */ new Map()}
|
|
2775
|
+
/** Threshold keys already warned (`warnAtFraction` fires once per crossing per cap).
|
|
2776
|
+
* Lives on the LEDGER — not the client — so clients SHARING one (a cross-chain
|
|
2777
|
+
* MultiChainPayer) dedupe together: a denomination/count threshold fires once across the
|
|
2778
|
+
* whole shared budget, not once per chain. */
|
|
2779
|
+
__init4() {this.warned = /* @__PURE__ */ new Set()}
|
|
2780
|
+
|
|
2680
2781
|
/**
|
|
2681
2782
|
* Session clock origin (epoch-ms) — process/session start = ledger
|
|
2682
2783
|
* construction. In-memory; a new process is a new session. The client reads it
|
|
2683
2784
|
* to compute the `ttlSeconds` deadline and the rolling-window slice.
|
|
2684
2785
|
*/
|
|
2685
|
-
|
|
2686
|
-
/**
|
|
2687
|
-
*
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2786
|
+
__init5() {this.sessionStart = Date.now()}
|
|
2787
|
+
/**
|
|
2788
|
+
* @param store Optional durable {@link SpendStore}. When supplied, the ledger
|
|
2789
|
+
* HYDRATES from `store.load()` here (so prior spend resumes after a restart) and
|
|
2790
|
+
* `append()`s every settled payment. A throwing/absent store fails SAFE to an
|
|
2791
|
+
* empty in-memory ledger — it never blocks construction (ERRORS.md: never throw).
|
|
2792
|
+
*/
|
|
2793
|
+
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);
|
|
2794
|
+
this.store = store;
|
|
2795
|
+
if (store) {
|
|
2796
|
+
let seed = [];
|
|
2797
|
+
try {
|
|
2798
|
+
seed = _nullishCoalesce(store.load(), () => ( []));
|
|
2799
|
+
} catch (e27) {
|
|
2800
|
+
seed = [];
|
|
2801
|
+
}
|
|
2802
|
+
for (const r of seed) this.ingest(r, _nullishCoalesce(_optionalChain([r, 'optionalAccess', _19 => _19.decimals]), () => ( 0)), _optionalChain([r, 'optionalAccess', _20 => _20.denom]));
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* A record is safe to tally iff its `amountBase` is a non-negative integer STRING and its
|
|
2807
|
+
* `decimals` is an integer in `[0, MAX_DECIMALS]`. The live `record()` path always passes
|
|
2808
|
+
* (the client validated the quote), but a hydrated record comes from an UNTRUSTED store
|
|
2809
|
+
* (a tampered/corrupt/future-version JSONL line), so we gate it here — a bad record is
|
|
2810
|
+
* dropped rather than allowed to throw `BigInt(...)`/`formatUnits(...)` later and brick a
|
|
2811
|
+
* read or the constructor.
|
|
2812
|
+
*/
|
|
2813
|
+
isTallyable(r, decimals) {
|
|
2814
|
+
return !!r && typeof r.amountBase === "string" && /^\d+$/.test(r.amountBase) && Number.isInteger(decimals) && decimals >= 0 && decimals <= _chunkMWBT7MCEcjs.MAX_DECIMALS;
|
|
2815
|
+
}
|
|
2816
|
+
/** Apply a record to the in-memory tallies (records + per-asset bucket + denom total).
|
|
2817
|
+
* Shared by {@link record} and constructor hydration; does NOT persist. Returns the
|
|
2818
|
+
* stored record, or `null` when the record is corrupt and was skipped. */
|
|
2819
|
+
ingest(r, decimals, denom) {
|
|
2820
|
+
if (!this.isTallyable(r, decimals)) return null;
|
|
2821
|
+
const denomStr = typeof denom === "string" && denom.trim() !== "" ? denom : void 0;
|
|
2822
|
+
const rec = { ...r, decimals, ...denomStr ? { denom: denomStr } : {} };
|
|
2823
|
+
this.records.push(rec);
|
|
2824
|
+
const key = keyFor(rec.network, rec.asset);
|
|
2691
2825
|
const bucket = this.buckets.get(key);
|
|
2692
2826
|
if (bucket) {
|
|
2693
|
-
bucket.total += BigInt(
|
|
2827
|
+
bucket.total += BigInt(rec.amountBase);
|
|
2694
2828
|
bucket.count += 1;
|
|
2695
|
-
if (!bucket.symbol &&
|
|
2829
|
+
if (!bucket.symbol && rec.symbol) bucket.symbol = rec.symbol;
|
|
2696
2830
|
} else {
|
|
2697
2831
|
this.buckets.set(key, {
|
|
2698
|
-
network:
|
|
2699
|
-
asset:
|
|
2700
|
-
symbol:
|
|
2832
|
+
network: rec.network,
|
|
2833
|
+
asset: rec.asset,
|
|
2834
|
+
symbol: rec.symbol,
|
|
2701
2835
|
decimals,
|
|
2702
|
-
total: BigInt(
|
|
2836
|
+
total: BigInt(rec.amountBase),
|
|
2703
2837
|
count: 1
|
|
2704
2838
|
});
|
|
2705
2839
|
}
|
|
2840
|
+
if (denomStr) {
|
|
2841
|
+
const scaled = scaleToDenom(BigInt(rec.amountBase), decimals);
|
|
2842
|
+
if (scaled !== null) {
|
|
2843
|
+
const k = denomStr.toUpperCase();
|
|
2844
|
+
this.denomTotals.set(k, (_nullishCoalesce(this.denomTotals.get(k), () => ( 0n))) + scaled);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
return rec;
|
|
2848
|
+
}
|
|
2849
|
+
/** Record a settled payment. `decimals` is the TRUE token decimals (for the
|
|
2850
|
+
* per-asset running total + the formatted summary). `denom` is the unit-of-account
|
|
2851
|
+
* the payment counts toward in the grand total (or omit for none). Persists to the
|
|
2852
|
+
* {@link SpendStore} when one is configured (a failed append never throws). */
|
|
2853
|
+
record(r, decimals, denom) {
|
|
2854
|
+
const rec = this.ingest(r, decimals, denom);
|
|
2855
|
+
if (rec && this.store) {
|
|
2856
|
+
try {
|
|
2857
|
+
this.store.append(rec);
|
|
2858
|
+
} catch (e28) {
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2706
2861
|
}
|
|
2707
2862
|
/** Running total (base units) already spent on this (network, asset). */
|
|
2708
2863
|
totalFor(network, asset) {
|
|
2709
|
-
return _nullishCoalesce(_optionalChain([this, 'access',
|
|
2864
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _21 => _21.buckets, 'access', _22 => _22.get, 'call', _23 => _23(keyFor(network, asset)), 'optionalAccess', _24 => _24.total]), () => ( 0n));
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Running grand total for a DENOMINATION, scaled to {@link DENOM_PRECISION} (so
|
|
2868
|
+
* tokens of different decimals add up exactly). Summed across every token of that
|
|
2869
|
+
* denomination and every chain this ledger has seen. Powers `maxTotalPerDenom`.
|
|
2870
|
+
* `0n` for a denomination never spent on. Case-insensitive.
|
|
2871
|
+
*/
|
|
2872
|
+
totalForDenom(denom) {
|
|
2873
|
+
return _nullishCoalesce(this.denomTotals.get(denom.toUpperCase()), () => ( 0n));
|
|
2874
|
+
}
|
|
2875
|
+
/** Total number of settled payments (across every chain + token). Powers `maxPayments`. */
|
|
2876
|
+
count() {
|
|
2877
|
+
return this.records.length;
|
|
2878
|
+
}
|
|
2879
|
+
/** Mark a `warnAtFraction` threshold key as fired; returns `true` the FIRST time (so the
|
|
2880
|
+
* caller emits the `budget-threshold` event once) and `false` thereafter. Shared across
|
|
2881
|
+
* every client on this ledger, so a cross-chain threshold fires once for the whole budget. */
|
|
2882
|
+
markWarned(key) {
|
|
2883
|
+
if (this.warned.has(key)) return false;
|
|
2884
|
+
this.warned.add(key);
|
|
2885
|
+
return true;
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Number of settled payments whose `at` (ISO) is at or after `sinceMs` (epoch-ms),
|
|
2889
|
+
* across every chain + token. Backs the rolling payment-count cap
|
|
2890
|
+
* (`maxPaymentsPerWindow`, `sinceMs = now - windowSeconds*1000`). Linear scan —
|
|
2891
|
+
* negligible at agent-session cardinality and only when a window count cap is set.
|
|
2892
|
+
*/
|
|
2893
|
+
countSince(sinceMs) {
|
|
2894
|
+
let n = 0;
|
|
2895
|
+
for (const r of this.records) {
|
|
2896
|
+
const t = Date.parse(r.at);
|
|
2897
|
+
if (Number.isNaN(t) || t >= sinceMs) n += 1;
|
|
2898
|
+
}
|
|
2899
|
+
return n;
|
|
2710
2900
|
}
|
|
2711
2901
|
/**
|
|
2712
2902
|
* Sum of base-unit amounts for (network, asset) whose record `at` (ISO
|
|
@@ -2718,9 +2908,9 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2718
2908
|
totalSince(network, asset, sinceMs) {
|
|
2719
2909
|
let sum = 0n;
|
|
2720
2910
|
for (const r of this.records) {
|
|
2721
|
-
if (r.network
|
|
2722
|
-
|
|
2723
|
-
|
|
2911
|
+
if (r.network !== network || r.asset !== asset) continue;
|
|
2912
|
+
const t = Date.parse(r.at);
|
|
2913
|
+
if (Number.isNaN(t) || t >= sinceMs) sum += BigInt(r.amountBase);
|
|
2724
2914
|
}
|
|
2725
2915
|
return sum;
|
|
2726
2916
|
}
|
|
@@ -2740,6 +2930,14 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2740
2930
|
totalBase: b.total
|
|
2741
2931
|
}));
|
|
2742
2932
|
}
|
|
2933
|
+
/**
|
|
2934
|
+
* The per-denomination grand totals, as read-only tuples — `denom` and the running
|
|
2935
|
+
* `totalScaled` (at {@link DENOM_PRECISION}). Lets the client compose the grand-total
|
|
2936
|
+
* budget view; the cap math lives in the client. A never-spent denomination is absent.
|
|
2937
|
+
*/
|
|
2938
|
+
denomBuckets() {
|
|
2939
|
+
return [...this.denomTotals.entries()].map(([denom, totalScaled]) => ({ denom, totalScaled }));
|
|
2940
|
+
}
|
|
2743
2941
|
/** An immutable snapshot of all spend so far. */
|
|
2744
2942
|
summary() {
|
|
2745
2943
|
return {
|
|
@@ -2750,9 +2948,15 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
2750
2948
|
symbol: b.symbol,
|
|
2751
2949
|
decimals: b.decimals,
|
|
2752
2950
|
totalBase: b.total.toString(),
|
|
2753
|
-
totalFormatted:
|
|
2951
|
+
totalFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, b.total, b.decimals),
|
|
2754
2952
|
count: b.count
|
|
2755
2953
|
})),
|
|
2954
|
+
byDenom: [...this.denomTotals.entries()].map(([denom, scaled]) => ({
|
|
2955
|
+
denom,
|
|
2956
|
+
totalScaled: scaled.toString(),
|
|
2957
|
+
totalFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, scaled, DENOM_PRECISION),
|
|
2958
|
+
count: this.records.filter((r) => _optionalChain([r, 'access', _25 => _25.denom, 'optionalAccess', _26 => _26.toUpperCase, 'call', _27 => _27()]) === denom).length
|
|
2959
|
+
})),
|
|
2756
2960
|
records: [...this.records]
|
|
2757
2961
|
};
|
|
2758
2962
|
}
|
|
@@ -2766,24 +2970,36 @@ var RECIPIENT_FIX = {
|
|
|
2766
2970
|
NOT_OPTED_IN: "the recipient must opt into this asset once (a 0-amount self-transfer)",
|
|
2767
2971
|
INACTIVE: "the recipient account doesn't exist yet \u2014 fund it with the chain's base reserve to activate it"
|
|
2768
2972
|
};
|
|
2769
|
-
var PipRailClient =
|
|
2973
|
+
var PipRailClient = class {
|
|
2974
|
+
|
|
2770
2975
|
|
|
2771
2976
|
|
|
2772
2977
|
|
|
2978
|
+
// Per-asset (+ per-denomination + count) tally of everything this client has paid —
|
|
2979
|
+
// powers spent()/budget() and the maxTotal/maxTotalPerDenom/maxPayments caps. Either
|
|
2980
|
+
// its own (optionally store-backed) ledger, or a SHARED one injected so the grand
|
|
2981
|
+
// total + count caps span several chains (MultiChainPayer.fromWallets).
|
|
2773
2982
|
|
|
2774
|
-
//
|
|
2775
|
-
//
|
|
2776
|
-
|
|
2983
|
+
// `warnAtFraction` threshold dedup lives on the LEDGER (`ledger.markWarned`), so clients
|
|
2984
|
+
// sharing one ledger (a cross-chain MultiChainPayer) fire each threshold once for the whole
|
|
2985
|
+
// shared budget — not once per chain.
|
|
2777
2986
|
// Resolved lazily on first request — this is what lets Solana (and future
|
|
2778
2987
|
// families) auto-mount with no setup call.
|
|
2779
2988
|
|
|
2780
|
-
constructor(opts) {
|
|
2989
|
+
constructor(opts) {
|
|
2781
2990
|
this.opts = opts;
|
|
2782
2991
|
this.maxRetries = Math.max(1, _nullishCoalesce(opts.maxPaymentRetries, () => ( 3)));
|
|
2783
2992
|
this.retryTimeoutMs = _nullishCoalesce(opts.retryTimeoutMs, () => ( 3e4));
|
|
2784
2993
|
this.onEvent = _nullishCoalesce(opts.onEvent, () => ( (() => void 0)));
|
|
2994
|
+
if (opts.ledger && opts.spendStore) {
|
|
2995
|
+
throw new TypeError(
|
|
2996
|
+
"Pass either `ledger` (a shared SpendLedger) or `spendStore`, not both \u2014 a shared ledger already owns its store."
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2999
|
+
this.ledger = _nullishCoalesce(opts.ledger, () => ( new SpendLedger(opts.spendStore)));
|
|
2785
3000
|
this.assertPolicyAmountCaps(opts.policy);
|
|
2786
3001
|
this.assertPolicyTimeOptions(opts.policy);
|
|
3002
|
+
this.assertPolicySpendControls(opts.policy);
|
|
2787
3003
|
}
|
|
2788
3004
|
/**
|
|
2789
3005
|
* Fail LOUDLY at construction on a malformed amount cap — a security boundary
|
|
@@ -2804,6 +3020,65 @@ var PipRailClient = (_class2 = class {
|
|
|
2804
3020
|
);
|
|
2805
3021
|
}
|
|
2806
3022
|
}
|
|
3023
|
+
if (policy.maxTotalPerDenom !== void 0) {
|
|
3024
|
+
const m = policy.maxTotalPerDenom;
|
|
3025
|
+
const proto = m && typeof m === "object" ? Object.getPrototypeOf(m) : false;
|
|
3026
|
+
if (!m || typeof m !== "object" || Array.isArray(m) || proto !== Object.prototype && proto !== null) {
|
|
3027
|
+
throw new TypeError(
|
|
3028
|
+
`policy.maxTotalPerDenom must be a plain { DENOM: amount } object (e.g. { USD: '20.00' }); got ${JSON.stringify(policy.maxTotalPerDenom)}.`
|
|
3029
|
+
);
|
|
3030
|
+
}
|
|
3031
|
+
for (const [denom, v] of Object.entries(policy.maxTotalPerDenom)) {
|
|
3032
|
+
if (denom.trim() === "") {
|
|
3033
|
+
throw new TypeError("policy.maxTotalPerDenom has a blank denomination key.");
|
|
3034
|
+
}
|
|
3035
|
+
if (typeof v !== "string" || !/^\d+(\.\d+)?$/.test(v)) {
|
|
3036
|
+
throw new TypeError(
|
|
3037
|
+
`policy.maxTotalPerDenom.${denom} must be a non-negative decimal string (e.g. '20.00'); got ${JSON.stringify(v)}.`
|
|
3038
|
+
);
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
if (policy.denomFor !== void 0) {
|
|
3043
|
+
const m = policy.denomFor;
|
|
3044
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) {
|
|
3045
|
+
throw new TypeError(`policy.denomFor must be a { token: DENOM } object; got ${JSON.stringify(m)}.`);
|
|
3046
|
+
}
|
|
3047
|
+
for (const [k, v] of Object.entries(policy.denomFor)) {
|
|
3048
|
+
if (typeof v !== "string" || v.trim() === "") {
|
|
3049
|
+
throw new TypeError(`policy.denomFor.${k} must be a non-empty denomination string; got ${JSON.stringify(v)}.`);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* Fail LOUDLY at construction on a misconfigured count / threshold control (a
|
|
3056
|
+
* programmer error → `TypeError`): payment counts must be positive safe integers,
|
|
3057
|
+
* `maxPaymentsPerWindow` needs `windowSeconds` (it shares the window), and
|
|
3058
|
+
* `warnAtFraction` must be in (0, 1].
|
|
3059
|
+
*/
|
|
3060
|
+
assertPolicySpendControls(policy) {
|
|
3061
|
+
if (!policy) return;
|
|
3062
|
+
for (const field of ["maxPayments", "maxPaymentsPerWindow"]) {
|
|
3063
|
+
const v = policy[field];
|
|
3064
|
+
if (v === void 0) continue;
|
|
3065
|
+
if (!Number.isSafeInteger(v) || v <= 0) {
|
|
3066
|
+
throw new TypeError(`policy.${field} must be a positive integer; got ${JSON.stringify(v)}.`);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
if (policy.maxPaymentsPerWindow !== void 0 && policy.windowSeconds === void 0) {
|
|
3070
|
+
throw new TypeError(
|
|
3071
|
+
"policy.maxPaymentsPerWindow needs policy.windowSeconds (the rolling window it counts within) \u2014 set both, or neither."
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
if (policy.warnAtFraction !== void 0) {
|
|
3075
|
+
const f = policy.warnAtFraction;
|
|
3076
|
+
if (typeof f !== "number" || !(f > 0 && f <= 1)) {
|
|
3077
|
+
throw new TypeError(
|
|
3078
|
+
`policy.warnAtFraction must be a number in (0, 1] (e.g. 0.8); got ${JSON.stringify(f)}.`
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
2807
3082
|
}
|
|
2808
3083
|
/**
|
|
2809
3084
|
* Fail LOUDLY at construction on a misconfigured time policy — a security
|
|
@@ -2818,11 +3093,17 @@ var PipRailClient = (_class2 = class {
|
|
|
2818
3093
|
if (!policy) return;
|
|
2819
3094
|
const hasWindowTotal = policy.windowTotal !== void 0;
|
|
2820
3095
|
const hasWindowSeconds = policy.windowSeconds !== void 0;
|
|
2821
|
-
|
|
3096
|
+
const hasWindowCount = policy.maxPaymentsPerWindow !== void 0;
|
|
3097
|
+
if (hasWindowTotal && !hasWindowSeconds) {
|
|
2822
3098
|
throw new TypeError(
|
|
2823
3099
|
"policy.windowTotal and policy.windowSeconds must be set together \u2014 a rolling-window cap can't be half-armed (set both, or neither)."
|
|
2824
3100
|
);
|
|
2825
3101
|
}
|
|
3102
|
+
if (hasWindowSeconds && !hasWindowTotal && !hasWindowCount) {
|
|
3103
|
+
throw new TypeError(
|
|
3104
|
+
"policy.windowSeconds must be set together with policy.windowTotal and/or policy.maxPaymentsPerWindow \u2014 a window width alone bounds nothing."
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
2826
3107
|
if (hasWindowSeconds && !(Number.isSafeInteger(policy.windowSeconds) && policy.windowSeconds > 0)) {
|
|
2827
3108
|
throw new TypeError("policy.windowSeconds must be a positive integer number of seconds.");
|
|
2828
3109
|
}
|
|
@@ -2834,13 +3115,21 @@ var PipRailClient = (_class2 = class {
|
|
|
2834
3115
|
);
|
|
2835
3116
|
}
|
|
2836
3117
|
}
|
|
3118
|
+
if (policy.expiresAt !== void 0) {
|
|
3119
|
+
const at = policy.expiresAt;
|
|
3120
|
+
if (!Number.isSafeInteger(at) || Math.abs(at) > 864e13) {
|
|
3121
|
+
throw new TypeError(
|
|
3122
|
+
`policy.expiresAt must be an absolute epoch-MILLISECONDS integer (like Date.now()); got ${JSON.stringify(at)}.`
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
2837
3126
|
}
|
|
2838
3127
|
/** Emit an observability event, never letting a throwing handler break the
|
|
2839
3128
|
* payment flow (mirrors the server gate's `onPaid` isolation). */
|
|
2840
3129
|
safeEmit(event) {
|
|
2841
3130
|
try {
|
|
2842
3131
|
this.onEvent(event);
|
|
2843
|
-
} catch (
|
|
3132
|
+
} catch (e29) {
|
|
2844
3133
|
}
|
|
2845
3134
|
}
|
|
2846
3135
|
/** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
|
|
@@ -2870,7 +3159,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2870
3159
|
* as-is) or a plain object (serialised as JSON).
|
|
2871
3160
|
*/
|
|
2872
3161
|
post(url, body, init) {
|
|
2873
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
3162
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _28 => _28.headers]));
|
|
2874
3163
|
let payload;
|
|
2875
3164
|
if (body === void 0 || body === null) {
|
|
2876
3165
|
payload = void 0;
|
|
@@ -2901,7 +3190,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2901
3190
|
* "0.05 USDC on Base, within budget → pay it." No funds move.
|
|
2902
3191
|
*/
|
|
2903
3192
|
async quote(url, init) {
|
|
2904
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3193
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _29 => _29.method]), () => ( "GET")) });
|
|
2905
3194
|
if (res.status !== 402) return null;
|
|
2906
3195
|
const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
2907
3196
|
return quote;
|
|
@@ -2920,22 +3209,32 @@ var PipRailClient = (_class2 = class {
|
|
|
2920
3209
|
* on Tron, where a USD₮ transfer can cost real TRX.
|
|
2921
3210
|
*/
|
|
2922
3211
|
async estimateCost(url, init) {
|
|
2923
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3212
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _30 => _30.method]), () => ( "GET")) });
|
|
2924
3213
|
if (res.status !== 402) return null;
|
|
2925
3214
|
const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
2926
3215
|
const cost = await net.estimateCost(accept);
|
|
2927
3216
|
return { quote, cost };
|
|
2928
3217
|
}
|
|
2929
3218
|
/** Aggregated snapshot of every payment this client has settled — total
|
|
2930
|
-
* count, cumulative spend per token,
|
|
3219
|
+
* count, cumulative spend per token, cumulative spend per denomination (the
|
|
3220
|
+
* cross-token grand total), and the individual records. */
|
|
2931
3221
|
spent() {
|
|
2932
3222
|
return this.ledger.summary();
|
|
2933
3223
|
}
|
|
3224
|
+
/** The CONFIGURED spend policy, read back unchanged — so an agent can self-check
|
|
3225
|
+
* its WHOLE leash (caps, allowlists, time, denom + count limits) without hitting a
|
|
3226
|
+
* decline. `undefined` when no policy is set. Pure; never throws. */
|
|
3227
|
+
policy() {
|
|
3228
|
+
return this.opts.policy;
|
|
3229
|
+
}
|
|
2934
3230
|
/**
|
|
2935
3231
|
* Read-only budget + time leash for a Mode-A (headless) agent — the policy IS
|
|
2936
3232
|
* the consent, and this is how the agent SEES what's left of it before paying.
|
|
2937
|
-
* Composes the
|
|
2938
|
-
*
|
|
3233
|
+
* Composes the ledger with the configured policy: the per-asset money leash
|
|
3234
|
+
* (`byAsset`), the cross-token GRAND TOTAL per denomination (`byDenom`, present
|
|
3235
|
+
* from the start), and the payment-COUNT leash (`counts`). Never throws, moves no
|
|
3236
|
+
* funds. The money/count figures persist if a `spendStore` is set; the time
|
|
3237
|
+
* envelope is process-scoped (see {@link SessionBudget}).
|
|
2939
3238
|
*/
|
|
2940
3239
|
budget() {
|
|
2941
3240
|
const view = this.sessionView();
|
|
@@ -2943,12 +3242,57 @@ var PipRailClient = (_class2 = class {
|
|
|
2943
3242
|
return {
|
|
2944
3243
|
session: {
|
|
2945
3244
|
start,
|
|
2946
|
-
expiresAt: _optionalChain([view, 'optionalAccess',
|
|
2947
|
-
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess',
|
|
3245
|
+
expiresAt: _optionalChain([view, 'optionalAccess', _31 => _31.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
|
|
3246
|
+
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _32 => _32.secondsRemaining]), () => ( null))
|
|
2948
3247
|
},
|
|
2949
|
-
byAsset: this.remaining()
|
|
3248
|
+
byAsset: this.remaining(),
|
|
3249
|
+
byDenom: this.denomRemaining(),
|
|
3250
|
+
counts: this.countStatus()
|
|
2950
3251
|
};
|
|
2951
3252
|
}
|
|
3253
|
+
/**
|
|
3254
|
+
* The cross-token GRAND-TOTAL leash — one row per denomination capped by
|
|
3255
|
+
* `policy.maxTotalPerDenom`. Unlike `remaining()`, rows exist from the START
|
|
3256
|
+
* (the cap is a single declared number, so headroom is previewable before any
|
|
3257
|
+
* spend). `[]` when no `maxTotalPerDenom` is set. Pure; never throws; never a
|
|
3258
|
+
* price-converted figure (tokens grouped as one unit, each 1:1).
|
|
3259
|
+
*/
|
|
3260
|
+
denomRemaining() {
|
|
3261
|
+
const caps = _optionalChain([this, 'access', _33 => _33.opts, 'access', _34 => _34.policy, 'optionalAccess', _35 => _35.maxTotalPerDenom]);
|
|
3262
|
+
if (!caps) return [];
|
|
3263
|
+
return Object.entries(caps).map(([rawDenom, capStr]) => {
|
|
3264
|
+
const denom = rawDenom.toUpperCase();
|
|
3265
|
+
const spentScaled = this.ledger.totalForDenom(denom);
|
|
3266
|
+
const capScaled = _chunkMWBT7MCEcjs.floorUnits.call(void 0, capStr, DENOM_PRECISION);
|
|
3267
|
+
const remainingScaled = capScaled > spentScaled ? capScaled - spentScaled : 0n;
|
|
3268
|
+
const fraction = capScaled === 0n ? spentScaled > 0n ? 1 : 0 : Number(spentScaled * 1000000n / capScaled) / 1e6;
|
|
3269
|
+
return {
|
|
3270
|
+
denom,
|
|
3271
|
+
spentFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, spentScaled, DENOM_PRECISION),
|
|
3272
|
+
capFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, capScaled, DENOM_PRECISION),
|
|
3273
|
+
remainingFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, remainingScaled, DENOM_PRECISION),
|
|
3274
|
+
fraction
|
|
3275
|
+
};
|
|
3276
|
+
});
|
|
3277
|
+
}
|
|
3278
|
+
/** The payment-COUNT leash — settled count so far + the configured lifetime/window
|
|
3279
|
+
* caps and what's left. Pure; never throws. */
|
|
3280
|
+
countStatus() {
|
|
3281
|
+
const policy = this.opts.policy;
|
|
3282
|
+
const settled = this.ledger.count();
|
|
3283
|
+
const out = { settled };
|
|
3284
|
+
if (_optionalChain([policy, 'optionalAccess', _36 => _36.maxPayments]) !== void 0) {
|
|
3285
|
+
out.lifetimeCap = policy.maxPayments;
|
|
3286
|
+
out.lifetimeRemaining = Math.max(0, policy.maxPayments - settled);
|
|
3287
|
+
}
|
|
3288
|
+
if (_optionalChain([policy, 'optionalAccess', _37 => _37.maxPaymentsPerWindow]) !== void 0 && policy.windowSeconds !== void 0) {
|
|
3289
|
+
const windowSettled = this.ledger.countSince(Date.now() - policy.windowSeconds * 1e3);
|
|
3290
|
+
out.windowCap = policy.maxPaymentsPerWindow;
|
|
3291
|
+
out.windowSettled = windowSettled;
|
|
3292
|
+
out.windowRemaining = Math.max(0, policy.maxPaymentsPerWindow - windowSettled);
|
|
3293
|
+
}
|
|
3294
|
+
return out;
|
|
3295
|
+
}
|
|
2952
3296
|
/**
|
|
2953
3297
|
* Per-(network, asset) remaining budget — ONE row per pair the ledger already
|
|
2954
3298
|
* holds (decimals are known only after the first spend), so a fresh client with
|
|
@@ -2957,7 +3301,7 @@ var PipRailClient = (_class2 = class {
|
|
|
2957
3301
|
* never throws, never sums across tokens (no price oracle). PROCESS-SCOPED.
|
|
2958
3302
|
*/
|
|
2959
3303
|
remaining() {
|
|
2960
|
-
const maxTotal = _optionalChain([this, 'access',
|
|
3304
|
+
const maxTotal = _optionalChain([this, 'access', _38 => _38.opts, 'access', _39 => _39.policy, 'optionalAccess', _40 => _40.maxTotal]);
|
|
2961
3305
|
return this.ledger.assetBuckets().map((b) => {
|
|
2962
3306
|
const base2 = {
|
|
2963
3307
|
network: b.network,
|
|
@@ -2967,13 +3311,13 @@ var PipRailClient = (_class2 = class {
|
|
|
2967
3311
|
spentBase: b.totalBase.toString()
|
|
2968
3312
|
};
|
|
2969
3313
|
if (maxTotal === void 0) return base2;
|
|
2970
|
-
const capBase =
|
|
3314
|
+
const capBase = _chunkMWBT7MCEcjs.floorUnits.call(void 0, maxTotal, b.decimals);
|
|
2971
3315
|
const remainingBase = capBase > b.totalBase ? capBase - b.totalBase : 0n;
|
|
2972
3316
|
return {
|
|
2973
3317
|
...base2,
|
|
2974
3318
|
capBase: capBase.toString(),
|
|
2975
3319
|
remainingBase: remainingBase.toString(),
|
|
2976
|
-
remainingFormatted:
|
|
3320
|
+
remainingFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, remainingBase, b.decimals)
|
|
2977
3321
|
};
|
|
2978
3322
|
});
|
|
2979
3323
|
}
|
|
@@ -3009,15 +3353,15 @@ var PipRailClient = (_class2 = class {
|
|
|
3009
3353
|
* the plan yourself. No funds move.
|
|
3010
3354
|
*/
|
|
3011
3355
|
async planPayment(url, init) {
|
|
3012
|
-
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3356
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _41 => _41.method]), () => ( "GET")) });
|
|
3013
3357
|
if (res.status !== 402) return null;
|
|
3014
3358
|
const challenge = await parseChallenge(res);
|
|
3015
3359
|
if (!challenge) {
|
|
3016
|
-
throw new (0,
|
|
3360
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
|
|
3017
3361
|
}
|
|
3018
3362
|
const { net, wallet } = await this.ensure();
|
|
3019
3363
|
if (!wallet) {
|
|
3020
|
-
throw new (0,
|
|
3364
|
+
throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
|
|
3021
3365
|
"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."
|
|
3022
3366
|
);
|
|
3023
3367
|
}
|
|
@@ -3190,31 +3534,31 @@ var PipRailClient = (_class2 = class {
|
|
|
3190
3534
|
* streams throw `NonReplayableBodyError`.
|
|
3191
3535
|
*/
|
|
3192
3536
|
async fetch(url, init) {
|
|
3193
|
-
const body = _optionalChain([init, 'optionalAccess',
|
|
3537
|
+
const body = _optionalChain([init, 'optionalAccess', _42 => _42.body]);
|
|
3194
3538
|
if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
|
|
3195
|
-
throw new (0,
|
|
3539
|
+
throw new (0, _chunkMWBT7MCEcjs.NonReplayableBodyError)(
|
|
3196
3540
|
"fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
|
|
3197
3541
|
);
|
|
3198
3542
|
}
|
|
3199
3543
|
const firstResponse = await fetch(url, init);
|
|
3200
3544
|
if (firstResponse.status !== 402) return firstResponse;
|
|
3201
|
-
const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess',
|
|
3545
|
+
const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _43 => _43.schemes]));
|
|
3202
3546
|
const resolved = await this.resolveChallenge(url, firstResponse, schemes);
|
|
3203
3547
|
const { net, wallet, challenge } = resolved;
|
|
3204
3548
|
if (!wallet) {
|
|
3205
|
-
throw new (0,
|
|
3549
|
+
throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
|
|
3206
3550
|
"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)."
|
|
3207
3551
|
);
|
|
3208
3552
|
}
|
|
3209
3553
|
let accept = resolved.accept;
|
|
3210
3554
|
let quote = resolved.quote;
|
|
3211
|
-
const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess',
|
|
3555
|
+
const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _44 => _44.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
|
|
3212
3556
|
if (autoRoute) {
|
|
3213
3557
|
const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
|
|
3214
3558
|
if (!plan.best) {
|
|
3215
3559
|
const reason = _nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment."));
|
|
3216
3560
|
this.safeEmit({ kind: "payment-failed", reason });
|
|
3217
|
-
throw new (0,
|
|
3561
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(reason);
|
|
3218
3562
|
}
|
|
3219
3563
|
accept = plan.best.accept;
|
|
3220
3564
|
quote = plan.best.quote;
|
|
@@ -3238,7 +3582,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3238
3582
|
async resolveChallenge(url, response, schemes) {
|
|
3239
3583
|
const challenge = await parseChallenge(response);
|
|
3240
3584
|
if (!challenge) {
|
|
3241
|
-
throw new (0,
|
|
3585
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3242
3586
|
"402 response did not include a parseable x402 challenge."
|
|
3243
3587
|
);
|
|
3244
3588
|
}
|
|
@@ -3249,7 +3593,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3249
3593
|
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network)
|
|
3250
3594
|
);
|
|
3251
3595
|
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
3252
|
-
throw new (0,
|
|
3596
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
3253
3597
|
`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.`
|
|
3254
3598
|
);
|
|
3255
3599
|
}
|
|
@@ -3258,13 +3602,13 @@ var PipRailClient = (_class2 = class {
|
|
|
3258
3602
|
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network) && net.describeAsset(a.asset) != null
|
|
3259
3603
|
);
|
|
3260
3604
|
if (payable) {
|
|
3261
|
-
throw new (0,
|
|
3605
|
+
throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
|
|
3262
3606
|
`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).`
|
|
3263
3607
|
);
|
|
3264
3608
|
}
|
|
3265
3609
|
}
|
|
3266
3610
|
const networks = [...new Set(challenge.accepts.map((a) => a.network))].join(", ");
|
|
3267
|
-
throw new (0,
|
|
3611
|
+
throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
|
|
3268
3612
|
`No accepts[] entry payable by this client on ${net.network} (schemes: ${schemes.join(", ")}; challenge offered: ${networks || "none"}).`
|
|
3269
3613
|
);
|
|
3270
3614
|
}
|
|
@@ -3366,7 +3710,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3366
3710
|
const shortfall = {};
|
|
3367
3711
|
if (!quote.withinPolicy) {
|
|
3368
3712
|
blockers.push(
|
|
3369
|
-
quote.policyCode === "SESSION_EXPIRED" || quote.policyCode === "WINDOW_TOTAL" ? "OUTSIDE_WINDOW" : "OUTSIDE_POLICY"
|
|
3713
|
+
quote.policyCode === "SESSION_EXPIRED" || quote.policyCode === "WINDOW_TOTAL" || quote.policyCode === "WINDOW_COUNT" ? "OUTSIDE_WINDOW" : "OUTSIDE_POLICY"
|
|
3370
3714
|
);
|
|
3371
3715
|
}
|
|
3372
3716
|
if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
|
|
@@ -3377,21 +3721,21 @@ var PipRailClient = (_class2 = class {
|
|
|
3377
3721
|
if (isExact) {
|
|
3378
3722
|
if (tokenKnown && bal.token < amount) {
|
|
3379
3723
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3380
|
-
shortfall.token =
|
|
3724
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
|
|
3381
3725
|
}
|
|
3382
3726
|
} else if (isNative) {
|
|
3383
3727
|
if (nativeKnown && bal.native < amount + fee) {
|
|
3384
3728
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3385
|
-
shortfall.token =
|
|
3729
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
|
|
3386
3730
|
}
|
|
3387
3731
|
} else {
|
|
3388
3732
|
if (tokenKnown && bal.token < amount) {
|
|
3389
3733
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
3390
|
-
shortfall.token =
|
|
3734
|
+
shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
|
|
3391
3735
|
}
|
|
3392
3736
|
if (nativeKnown && bal.native < fee) {
|
|
3393
3737
|
blockers.push("INSUFFICIENT_GAS");
|
|
3394
|
-
shortfall.native =
|
|
3738
|
+
shortfall.native = _chunkMWBT7MCEcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
|
|
3395
3739
|
} else if (nativeKnown && fee > 0n && bal.native < fee * 3n / 2n) {
|
|
3396
3740
|
warnings.push("THIN_GAS_MARGIN");
|
|
3397
3741
|
}
|
|
@@ -3416,8 +3760,8 @@ var PipRailClient = (_class2 = class {
|
|
|
3416
3760
|
blockers,
|
|
3417
3761
|
warnings,
|
|
3418
3762
|
balance: {
|
|
3419
|
-
token: bal.token != null ?
|
|
3420
|
-
native: bal.native != null ?
|
|
3763
|
+
token: bal.token != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
|
|
3764
|
+
native: bal.native != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
|
|
3421
3765
|
},
|
|
3422
3766
|
need: { token: quote.amountFormatted, native: cost.feeFormatted },
|
|
3423
3767
|
...shortfall.token || shortfall.native ? { shortfall } : {},
|
|
@@ -3428,25 +3772,30 @@ var PipRailClient = (_class2 = class {
|
|
|
3428
3772
|
* driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
|
|
3429
3773
|
buildQuote(net, accept, url, description) {
|
|
3430
3774
|
if (typeof accept.amount !== "string" || !/^\d+$/.test(accept.amount)) {
|
|
3431
|
-
throw new (0,
|
|
3775
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3432
3776
|
`challenge amount "${String(accept.amount)}" is not a base-unit integer string.`
|
|
3433
3777
|
);
|
|
3434
3778
|
}
|
|
3435
3779
|
if (typeof accept.asset !== "string" || accept.asset.length === 0) {
|
|
3436
|
-
throw new (0,
|
|
3780
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3437
3781
|
`challenge on ${accept.network} states no (string) asset \u2014 refusing to price it.`
|
|
3438
3782
|
);
|
|
3439
3783
|
}
|
|
3440
3784
|
const amountBase = BigInt(accept.amount);
|
|
3441
3785
|
const described = net.describeAsset(accept.asset);
|
|
3442
|
-
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess',
|
|
3786
|
+
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _45 => _45.decimals]), () => ( _optionalChain([accept, 'access', _46 => _46.extra, 'optionalAccess', _47 => _47.decimals])));
|
|
3443
3787
|
if (typeof decimals !== "number" || !Number.isInteger(decimals) || decimals < 0) {
|
|
3444
|
-
throw new (0,
|
|
3788
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3445
3789
|
`challenge for ${accept.asset} on ${accept.network} states no valid decimals and the SDK doesn't recognise the token \u2014 refusing to price it.`
|
|
3446
3790
|
);
|
|
3447
3791
|
}
|
|
3448
|
-
|
|
3449
|
-
|
|
3792
|
+
if (decimals > _chunkMWBT7MCEcjs.MAX_DECIMALS) {
|
|
3793
|
+
throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
|
|
3794
|
+
`challenge for ${accept.asset} on ${accept.network} states ${decimals} decimals (> ${_chunkMWBT7MCEcjs.MAX_DECIMALS}) \u2014 refusing to price it (no real token is that deep).`
|
|
3795
|
+
);
|
|
3796
|
+
}
|
|
3797
|
+
const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _48 => _48.symbol]), () => ( _optionalChain([accept, 'access', _49 => _49.extra, 'optionalAccess', _50 => _50.symbol])));
|
|
3798
|
+
const amountFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amountBase, decimals);
|
|
3450
3799
|
const intent = {
|
|
3451
3800
|
host: hostOf2(url),
|
|
3452
3801
|
chain: this.opts.chain,
|
|
@@ -3459,9 +3808,14 @@ var PipRailClient = (_class2 = class {
|
|
|
3459
3808
|
};
|
|
3460
3809
|
const policy = this.opts.policy;
|
|
3461
3810
|
const hasWindow = !!policy && policy.windowTotal != null && policy.windowSeconds != null;
|
|
3811
|
+
const hasWindowCount = !!policy && policy.maxPaymentsPerWindow != null && policy.windowSeconds != null;
|
|
3812
|
+
const hasDenomCap = !!policy && policy.maxTotalPerDenom != null && Object.keys(policy.maxTotalPerDenom).length > 0;
|
|
3813
|
+
const hasCountCap = !!policy && policy.maxPayments != null;
|
|
3462
3814
|
const hasTimePolicy = !!policy && (policy.ttlSeconds != null || policy.expiresAt != null || hasWindow);
|
|
3815
|
+
const needsCtx = hasTimePolicy || hasWindowCount || hasDenomCap || hasCountCap;
|
|
3816
|
+
const denom = hasDenomCap ? denomOf(intent.symbol, intent.asset, policy) : void 0;
|
|
3463
3817
|
const now = Date.now();
|
|
3464
|
-
const ctx =
|
|
3818
|
+
const ctx = needsCtx ? {
|
|
3465
3819
|
now,
|
|
3466
3820
|
sessionStart: this.ledger.sessionStart,
|
|
3467
3821
|
// Window slice ONLY when BOTH fields are set — never a `?? 0` width.
|
|
@@ -3469,7 +3823,12 @@ var PipRailClient = (_class2 = class {
|
|
|
3469
3823
|
accept.network,
|
|
3470
3824
|
accept.asset,
|
|
3471
3825
|
now - policy.windowSeconds * 1e3
|
|
3472
|
-
) : 0n
|
|
3826
|
+
) : 0n,
|
|
3827
|
+
// Running grand total on this intent's denomination, across the (shared) ledger.
|
|
3828
|
+
spentInDenomScaled: denom ? this.ledger.totalForDenom(denom) : 0n,
|
|
3829
|
+
// Settled-payment counts (lifetime + window) for the count caps.
|
|
3830
|
+
paymentCount: hasCountCap ? this.ledger.count() : 0,
|
|
3831
|
+
paymentCountInWindow: hasWindowCount ? this.ledger.countSince(now - policy.windowSeconds * 1e3) : 0
|
|
3473
3832
|
} : void 0;
|
|
3474
3833
|
const decision = evaluatePolicy(
|
|
3475
3834
|
intent,
|
|
@@ -3477,7 +3836,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3477
3836
|
this.ledger.totalFor(accept.network, accept.asset),
|
|
3478
3837
|
ctx
|
|
3479
3838
|
);
|
|
3480
|
-
const serverSymbol = _optionalChain([accept, 'access',
|
|
3839
|
+
const serverSymbol = _optionalChain([accept, 'access', _51 => _51.extra, 'optionalAccess', _52 => _52.symbol]);
|
|
3481
3840
|
const symbolMismatch = intent.recognized && !!serverSymbol && !!symbol && serverSymbol.toUpperCase() !== symbol.toUpperCase();
|
|
3482
3841
|
return {
|
|
3483
3842
|
url,
|
|
@@ -3505,9 +3864,11 @@ var PipRailClient = (_class2 = class {
|
|
|
3505
3864
|
async authorize(quote) {
|
|
3506
3865
|
if (!quote.withinPolicy) {
|
|
3507
3866
|
const reason = `Payment refused by policy: ${_nullishCoalesce(quote.policyReason, () => ( "not allowed"))}`;
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3867
|
+
this.refuse(reason, {
|
|
3868
|
+
reasonCode: reasonCodeForPolicy(quote.policyCode),
|
|
3869
|
+
policyCode: quote.policyCode,
|
|
3870
|
+
quote
|
|
3871
|
+
});
|
|
3511
3872
|
}
|
|
3512
3873
|
const hook = this.opts.onBeforePay;
|
|
3513
3874
|
if (!hook) return;
|
|
@@ -3515,43 +3876,136 @@ var PipRailClient = (_class2 = class {
|
|
|
3515
3876
|
try {
|
|
3516
3877
|
approved = await hook(quote);
|
|
3517
3878
|
} catch (err) {
|
|
3518
|
-
|
|
3519
|
-
this.safeEmit({ kind: "payment-failed", reason, code: "APPROVAL" });
|
|
3520
|
-
throw new (0, _chunkJG6KRAW6cjs.PaymentDeclinedError)(reason, { cause: err, reasonCode: "APPROVAL" });
|
|
3879
|
+
this.refuse("onBeforePay threw \u2014 refusing to pay.", { reasonCode: "APPROVAL", quote, cause: err });
|
|
3521
3880
|
}
|
|
3522
3881
|
if (!approved) {
|
|
3523
3882
|
const reason = `onBeforePay declined ${quote.amountFormatted} ${_nullishCoalesce(quote.symbol, () => ( ""))}`.trimEnd() + ` on ${quote.network}.`;
|
|
3524
|
-
this.
|
|
3525
|
-
throw new (0, _chunkJG6KRAW6cjs.PaymentDeclinedError)(reason, { reasonCode: "APPROVAL" });
|
|
3883
|
+
this.refuse(reason, { reasonCode: "APPROVAL", quote });
|
|
3526
3884
|
}
|
|
3527
3885
|
}
|
|
3528
|
-
/**
|
|
3886
|
+
/**
|
|
3887
|
+
* Refuse a payment BEFORE any send: emit BOTH the legacy `payment-failed` (so existing
|
|
3888
|
+
* `onEvent` consumers are unaffected) AND the richer, dedicated `payment-declined`
|
|
3889
|
+
* (typed reasonCode + fine PolicyDenyCode + the quote + a budget snapshot), then throw
|
|
3890
|
+
* the typed {@link PaymentDeclinedError}. Returns `never` so callers' control flow is
|
|
3891
|
+
* exhaustive.
|
|
3892
|
+
*/
|
|
3893
|
+
refuse(reason, opts) {
|
|
3894
|
+
this.safeEmit({ kind: "payment-failed", reason, ...opts.reasonCode ? { code: opts.reasonCode } : {} });
|
|
3895
|
+
this.safeEmit({
|
|
3896
|
+
kind: "payment-declined",
|
|
3897
|
+
reason,
|
|
3898
|
+
...opts.reasonCode ? { reasonCode: opts.reasonCode } : {},
|
|
3899
|
+
...opts.policyCode ? { code: opts.policyCode } : {},
|
|
3900
|
+
...opts.quote ? { quote: opts.quote } : {},
|
|
3901
|
+
budget: this.budget()
|
|
3902
|
+
});
|
|
3903
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(reason, {
|
|
3904
|
+
...opts.reasonCode ? { reasonCode: opts.reasonCode } : {},
|
|
3905
|
+
...opts.cause !== void 0 ? { cause: opts.cause } : {}
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
/** Record a settled payment in the ledger (TRUE decimals for the running total + the
|
|
3909
|
+
* denomination it counts toward in the grand total). Then fire the `onSpend` callback
|
|
3910
|
+
* with the record + the post-payment budget, and emit any `warnAtFraction` thresholds
|
|
3911
|
+
* this payment just crossed. All observability is isolated — a throwing hook never
|
|
3912
|
+
* affects the (already-settled) payment. */
|
|
3529
3913
|
recordSpend(quote, ref) {
|
|
3530
|
-
this.
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3914
|
+
const denom = denomOf(quote.symbol, quote.asset, this.opts.policy);
|
|
3915
|
+
const record = {
|
|
3916
|
+
url: quote.url,
|
|
3917
|
+
host: hostOf2(quote.url),
|
|
3918
|
+
network: quote.network,
|
|
3919
|
+
asset: quote.asset,
|
|
3920
|
+
amountBase: quote.amount,
|
|
3921
|
+
amountFormatted: quote.amountFormatted,
|
|
3922
|
+
...quote.symbol ? { symbol: quote.symbol } : {},
|
|
3923
|
+
decimals: quote.decimals,
|
|
3924
|
+
...denom ? { denom } : {},
|
|
3925
|
+
ref,
|
|
3926
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3927
|
+
};
|
|
3928
|
+
this.ledger.record(record, quote.decimals, denom);
|
|
3929
|
+
const budget = this.budget();
|
|
3930
|
+
if (this.opts.onSpend) {
|
|
3931
|
+
try {
|
|
3932
|
+
this.opts.onSpend(record, budget);
|
|
3933
|
+
} catch (e30) {
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
this.emitThresholds(budget);
|
|
3937
|
+
}
|
|
3938
|
+
/**
|
|
3939
|
+
* Emit a `budget-threshold` event for each cap whose used-fraction just reached
|
|
3940
|
+
* `policy.warnAtFraction` — the early warning before a hard decline. Fires ONCE per
|
|
3941
|
+
* crossing per cap (deduped on the shared ledger via `markWarned`, so a cross-chain
|
|
3942
|
+
* threshold fires once for the whole budget, not once per chain). No-op when no
|
|
3943
|
+
* `warnAtFraction` is set. Reads the just-computed {@link SessionBudget}; isolated (safeEmit).
|
|
3944
|
+
*/
|
|
3945
|
+
emitThresholds(budget) {
|
|
3946
|
+
const frac = _optionalChain([this, 'access', _53 => _53.opts, 'access', _54 => _54.policy, 'optionalAccess', _55 => _55.warnAtFraction]);
|
|
3947
|
+
if (frac === void 0) return;
|
|
3948
|
+
const fire = (scope, label, spentFormatted, capFormatted, fraction) => {
|
|
3949
|
+
if (fraction < frac) return;
|
|
3950
|
+
if (!this.ledger.markWarned(`${scope}:${label}`)) return;
|
|
3951
|
+
this.safeEmit({ kind: "budget-threshold", scope, label, spentFormatted, capFormatted, fraction });
|
|
3952
|
+
};
|
|
3953
|
+
for (const r of budget.byAsset) {
|
|
3954
|
+
if (r.capBase === void 0) continue;
|
|
3955
|
+
const cap = BigInt(r.capBase);
|
|
3956
|
+
const spent = BigInt(r.spentBase);
|
|
3957
|
+
const fraction = cap === 0n ? spent > 0n ? 1 : 0 : Number(spent * 1000000n / cap) / 1e6;
|
|
3958
|
+
fire(
|
|
3959
|
+
"asset",
|
|
3960
|
+
`${_nullishCoalesce(r.symbol, () => ( r.asset))} on ${r.network}`,
|
|
3961
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, spent, r.decimals),
|
|
3962
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, cap, r.decimals),
|
|
3963
|
+
fraction
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
for (const d of budget.byDenom) {
|
|
3967
|
+
fire("denom", d.denom, d.spentFormatted, d.capFormatted, d.fraction);
|
|
3968
|
+
}
|
|
3969
|
+
const policy = this.opts.policy;
|
|
3970
|
+
if (_optionalChain([policy, 'optionalAccess', _56 => _56.windowTotal]) !== void 0 && policy.windowSeconds !== void 0) {
|
|
3971
|
+
const since = Date.now() - policy.windowSeconds * 1e3;
|
|
3972
|
+
for (const r of budget.byAsset) {
|
|
3973
|
+
const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.windowTotal, r.decimals);
|
|
3974
|
+
const spent = this.ledger.totalSince(r.network, r.asset, since);
|
|
3975
|
+
const fraction = cap === 0n ? spent > 0n ? 1 : 0 : Number(spent * 1000000n / cap) / 1e6;
|
|
3976
|
+
fire(
|
|
3977
|
+
"window",
|
|
3978
|
+
`${_nullishCoalesce(r.symbol, () => ( r.asset))} on ${r.network}`,
|
|
3979
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, spent, r.decimals),
|
|
3980
|
+
_chunkMWBT7MCEcjs.formatUnits.call(void 0, cap, r.decimals),
|
|
3981
|
+
fraction
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
const c = budget.counts;
|
|
3986
|
+
if (c.lifetimeCap !== void 0 && c.lifetimeCap > 0) {
|
|
3987
|
+
fire("count", "maxPayments", String(c.settled), String(c.lifetimeCap), c.settled / c.lifetimeCap);
|
|
3988
|
+
}
|
|
3989
|
+
if (c.windowCap !== void 0 && c.windowCap > 0 && c.windowSettled !== void 0) {
|
|
3990
|
+
fire(
|
|
3991
|
+
"window-count",
|
|
3992
|
+
"maxPaymentsPerWindow",
|
|
3993
|
+
String(c.windowSettled),
|
|
3994
|
+
String(c.windowCap),
|
|
3995
|
+
c.windowSettled / c.windowCap
|
|
3996
|
+
);
|
|
3997
|
+
}
|
|
3544
3998
|
}
|
|
3545
3999
|
async payAndConfirm(net, wallet, accept) {
|
|
3546
4000
|
if (!this.supportsNetwork(net, accept.network)) {
|
|
3547
|
-
throw new (0,
|
|
4001
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
|
|
3548
4002
|
`Challenge expects ${accept.network} but client is on ${net.network}.`
|
|
3549
4003
|
);
|
|
3550
4004
|
}
|
|
3551
4005
|
const ref = await net.send(wallet, accept);
|
|
3552
4006
|
this.safeEmit({ kind: "payment-broadcast", ref });
|
|
3553
4007
|
try {
|
|
3554
|
-
const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access',
|
|
4008
|
+
const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access', _57 => _57.extra, 'optionalAccess', _58 => _58.minConfirmations]), () => ( 1)));
|
|
3555
4009
|
this.safeEmit({
|
|
3556
4010
|
kind: "payment-confirmed",
|
|
3557
4011
|
ref,
|
|
@@ -3571,9 +4025,9 @@ var PipRailClient = (_class2 = class {
|
|
|
3571
4025
|
const signature = {
|
|
3572
4026
|
x402Version: 2,
|
|
3573
4027
|
accepted: accept,
|
|
3574
|
-
payload: { nonce: _optionalChain([accept, 'access',
|
|
4028
|
+
payload: { nonce: _optionalChain([accept, 'access', _59 => _59.extra, 'optionalAccess', _60 => _60.nonce]), txHash: ref }
|
|
3575
4029
|
};
|
|
3576
|
-
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess',
|
|
4030
|
+
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _61 => _61.headers]));
|
|
3577
4031
|
headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
|
|
3578
4032
|
let lastResponse = null;
|
|
3579
4033
|
let lastReason = null;
|
|
@@ -3588,7 +4042,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3588
4042
|
() => timeoutController.abort(),
|
|
3589
4043
|
this.retryTimeoutMs
|
|
3590
4044
|
);
|
|
3591
|
-
const signal = _optionalChain([originalInit, 'optionalAccess',
|
|
4045
|
+
const signal = _optionalChain([originalInit, 'optionalAccess', _62 => _62.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
|
|
3592
4046
|
try {
|
|
3593
4047
|
lastResponse = await fetch(url, {
|
|
3594
4048
|
..._nullishCoalesce(originalInit, () => ( {})),
|
|
@@ -3597,7 +4051,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3597
4051
|
});
|
|
3598
4052
|
} catch (err) {
|
|
3599
4053
|
if (timeoutController.signal.aborted) {
|
|
3600
|
-
throw new (0,
|
|
4054
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
|
|
3601
4055
|
`Server did not respond within ${this.retryTimeoutMs}ms after broadcasting payment ${ref}. Re-verify or re-submit ref=${ref} \u2014 do NOT re-pay.`,
|
|
3602
4056
|
{ cause: err, ref }
|
|
3603
4057
|
);
|
|
@@ -3620,7 +4074,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3620
4074
|
reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`,
|
|
3621
4075
|
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
3622
4076
|
});
|
|
3623
|
-
throw new (0,
|
|
4077
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
3624
4078
|
`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).`,
|
|
3625
4079
|
{ ref }
|
|
3626
4080
|
);
|
|
@@ -3644,17 +4098,17 @@ var PipRailClient = (_class2 = class {
|
|
|
3644
4098
|
*/
|
|
3645
4099
|
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
3646
4100
|
if (!net.payExact) {
|
|
3647
|
-
throw new (0,
|
|
4101
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
3648
4102
|
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana, Algorand + NEAR today).`
|
|
3649
4103
|
);
|
|
3650
4104
|
}
|
|
3651
|
-
throwIfAborted(_optionalChain([init, 'optionalAccess',
|
|
4105
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _63 => _63.signal]));
|
|
3652
4106
|
const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
|
|
3653
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
4107
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _64 => _64.headers]));
|
|
3654
4108
|
headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
|
|
3655
4109
|
const rejectDefinitive = (why2) => {
|
|
3656
4110
|
this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})`, code: why2 });
|
|
3657
|
-
throw new (0,
|
|
4111
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
3658
4112
|
`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}.`,
|
|
3659
4113
|
{ ref: nonce }
|
|
3660
4114
|
);
|
|
@@ -3667,17 +4121,17 @@ var PipRailClient = (_class2 = class {
|
|
|
3667
4121
|
if (Date.now() >= deadline) break;
|
|
3668
4122
|
await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
|
|
3669
4123
|
}
|
|
3670
|
-
throwIfAborted(_optionalChain([init, 'optionalAccess',
|
|
4124
|
+
throwIfAborted(_optionalChain([init, 'optionalAccess', _65 => _65.signal]));
|
|
3671
4125
|
const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
|
|
3672
4126
|
if (budget <= 0) break;
|
|
3673
4127
|
const timeoutController = new AbortController();
|
|
3674
4128
|
const timeoutId = setTimeout(() => timeoutController.abort(), budget);
|
|
3675
|
-
const signal = _optionalChain([init, 'optionalAccess',
|
|
4129
|
+
const signal = _optionalChain([init, 'optionalAccess', _66 => _66.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
|
|
3676
4130
|
let response;
|
|
3677
4131
|
try {
|
|
3678
4132
|
response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
|
|
3679
4133
|
} catch (err) {
|
|
3680
|
-
throw new (0,
|
|
4134
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
|
|
3681
4135
|
`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.`,
|
|
3682
4136
|
{ cause: err, ref: nonce }
|
|
3683
4137
|
);
|
|
@@ -3693,7 +4147,7 @@ var PipRailClient = (_class2 = class {
|
|
|
3693
4147
|
if (response.ok && !(settle && settle.success === false)) {
|
|
3694
4148
|
const receipt = parseReceipt(response);
|
|
3695
4149
|
this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
|
|
3696
|
-
const ref = _optionalChain([settle, 'optionalAccess',
|
|
4150
|
+
const ref = _optionalChain([settle, 'optionalAccess', _67 => _67.transaction]) || _optionalChain([receipt, 'optionalAccess', _68 => _68.transaction]) || `eip3009-nonce:${nonce}`;
|
|
3697
4151
|
this.recordSpend(quote, ref);
|
|
3698
4152
|
return response;
|
|
3699
4153
|
}
|
|
@@ -3711,21 +4165,21 @@ var PipRailClient = (_class2 = class {
|
|
|
3711
4165
|
reason: `exact: 402 after submitting authorization nonce=${nonce} (${why})`,
|
|
3712
4166
|
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
3713
4167
|
});
|
|
3714
|
-
throw new (0,
|
|
4168
|
+
throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
|
|
3715
4169
|
`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}.`,
|
|
3716
4170
|
{ ref: nonce }
|
|
3717
4171
|
);
|
|
3718
4172
|
}
|
|
3719
|
-
}
|
|
4173
|
+
};
|
|
3720
4174
|
function throwIfAborted(signal) {
|
|
3721
|
-
if (_optionalChain([signal, 'optionalAccess',
|
|
4175
|
+
if (_optionalChain([signal, 'optionalAccess', _69 => _69.aborted])) {
|
|
3722
4176
|
throw _nullishCoalesce(signal.reason, () => ( new DOMException("This operation was aborted.", "AbortError")));
|
|
3723
4177
|
}
|
|
3724
4178
|
}
|
|
3725
4179
|
function safeBig(s) {
|
|
3726
4180
|
try {
|
|
3727
4181
|
return BigInt(s);
|
|
3728
|
-
} catch (
|
|
4182
|
+
} catch (e31) {
|
|
3729
4183
|
return 0n;
|
|
3730
4184
|
}
|
|
3731
4185
|
}
|
|
@@ -3788,10 +4242,10 @@ function buildFundingHint(options, chainLabel) {
|
|
|
3788
4242
|
return `Couldn't fully read your wallet on ${chainLabel} (RPC throttled) \u2014 retry; you may already be able to pay ${target.quote.amountFormatted} ${sym}.`;
|
|
3789
4243
|
}
|
|
3790
4244
|
const parts = [];
|
|
3791
|
-
if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access',
|
|
4245
|
+
if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _70 => _70.shortfall, 'optionalAccess', _71 => _71.token])) {
|
|
3792
4246
|
parts.push(`top up ${target.shortfall.token} ${sym}`);
|
|
3793
4247
|
}
|
|
3794
|
-
if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access',
|
|
4248
|
+
if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _72 => _72.shortfall, 'optionalAccess', _73 => _73.native])) {
|
|
3795
4249
|
parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
|
|
3796
4250
|
}
|
|
3797
4251
|
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}.`;
|
|
@@ -3805,7 +4259,7 @@ async function planAcross(clients, url, init) {
|
|
|
3805
4259
|
const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
|
|
3806
4260
|
return {
|
|
3807
4261
|
url,
|
|
3808
|
-
network: _nullishCoalesce(_optionalChain([best, 'optionalAccess',
|
|
4262
|
+
network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _74 => _74.accept, 'access', _75 => _75.network]), () => ( live[0].network)),
|
|
3809
4263
|
status,
|
|
3810
4264
|
payable: best !== null,
|
|
3811
4265
|
best,
|
|
@@ -3824,7 +4278,7 @@ async function fetchAcross(clients, url, init) {
|
|
|
3824
4278
|
const best = rankAcross(live.map((p) => p.plan)).find((o) => o.state === "payable");
|
|
3825
4279
|
if (!best) {
|
|
3826
4280
|
const hint = mergeDeclineHint(live.map((p) => p.plan));
|
|
3827
|
-
throw new (0,
|
|
4281
|
+
throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(hint || "No funded chain can settle this payment right now.");
|
|
3828
4282
|
}
|
|
3829
4283
|
const owner = live.find((p) => p.plan.options.includes(best)).client;
|
|
3830
4284
|
return owner.fetch(url, { ..._nullishCoalesce(init, () => ( {})), autoRoute: true });
|
|
@@ -3838,8 +4292,11 @@ function reasonCodeForPolicy(code) {
|
|
|
3838
4292
|
case "SESSION_EXPIRED":
|
|
3839
4293
|
return "SESSION_EXPIRED";
|
|
3840
4294
|
case "WINDOW_TOTAL":
|
|
4295
|
+
case "WINDOW_COUNT":
|
|
3841
4296
|
return "OUTSIDE_WINDOW";
|
|
3842
4297
|
case "MAX_TOTAL":
|
|
4298
|
+
case "MAX_TOTAL_DENOM":
|
|
4299
|
+
case "MAX_PAYMENTS":
|
|
3843
4300
|
return "BUDGET";
|
|
3844
4301
|
case void 0:
|
|
3845
4302
|
return void 0;
|
|
@@ -3850,7 +4307,7 @@ function reasonCodeForPolicy(code) {
|
|
|
3850
4307
|
function hostOf2(url) {
|
|
3851
4308
|
try {
|
|
3852
4309
|
return new URL(url).hostname;
|
|
3853
|
-
} catch (
|
|
4310
|
+
} catch (e32) {
|
|
3854
4311
|
return url;
|
|
3855
4312
|
}
|
|
3856
4313
|
}
|
|
@@ -3867,8 +4324,8 @@ function isReplayableBodyInit(value) {
|
|
|
3867
4324
|
async function readInvalidReason(response) {
|
|
3868
4325
|
try {
|
|
3869
4326
|
const body = await response.clone().json();
|
|
3870
|
-
const ext = _optionalChain([body, 'optionalAccess',
|
|
3871
|
-
const piprail = _optionalChain([ext, 'optionalAccess',
|
|
4327
|
+
const ext = _optionalChain([body, 'optionalAccess', _76 => _76.extensions]);
|
|
4328
|
+
const piprail = _optionalChain([ext, 'optionalAccess', _77 => _77.piprail]);
|
|
3872
4329
|
if (piprail && typeof piprail.code === "string") {
|
|
3873
4330
|
return {
|
|
3874
4331
|
error: piprail.code,
|
|
@@ -3887,16 +4344,20 @@ async function readInvalidReason(response) {
|
|
|
3887
4344
|
detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
|
|
3888
4345
|
};
|
|
3889
4346
|
}
|
|
3890
|
-
} catch (
|
|
4347
|
+
} catch (e33) {
|
|
3891
4348
|
}
|
|
3892
4349
|
const settle = parseSettleResponse(response);
|
|
3893
|
-
if (_optionalChain([settle, 'optionalAccess',
|
|
4350
|
+
if (_optionalChain([settle, 'optionalAccess', _78 => _78.errorReason])) return { error: settle.errorReason, detail: "" };
|
|
3894
4351
|
return null;
|
|
3895
4352
|
}
|
|
3896
4353
|
|
|
3897
4354
|
// src/payer.ts
|
|
3898
4355
|
var MultiChainPayer = class _MultiChainPayer {
|
|
3899
4356
|
|
|
4357
|
+
/** True when every client shares ONE ledger (the `fromWallets` path) — so spend +
|
|
4358
|
+
* budget are read from that single source, never concatenated (which would
|
|
4359
|
+
* double-count a shared ledger). */
|
|
4360
|
+
|
|
3900
4361
|
/**
|
|
3901
4362
|
* Wrap an explicit, ordered set of single-chain clients — use this when a client
|
|
3902
4363
|
* needs full control (e.g. a custom EVM chain configured by a viem `Chain`). The
|
|
@@ -3904,12 +4365,18 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
3904
4365
|
* at MOST one client per chain — two clients on the SAME network would double-count in
|
|
3905
4366
|
* `spent()`/`budget()` and waste a plan round-trip (`fromWallets` can't produce this).
|
|
3906
4367
|
* For the common case, prefer {@link MultiChainPayer.fromWallets}.
|
|
4368
|
+
*
|
|
4369
|
+
* For a cross-chain grand total / count cap via this path, build the clients with a
|
|
4370
|
+
* SHARED `ledger` (`new SpendLedger(store?)` passed to each client's `ledger` option)
|
|
4371
|
+
* and set `opts.shared = true` so reads come from that one ledger. `fromWallets` does
|
|
4372
|
+
* this for you.
|
|
3907
4373
|
*/
|
|
3908
|
-
constructor(clients) {
|
|
4374
|
+
constructor(clients, opts = {}) {
|
|
3909
4375
|
if (clients.length === 0) {
|
|
3910
4376
|
throw new TypeError("MultiChainPayer needs at least one PipRailClient.");
|
|
3911
4377
|
}
|
|
3912
4378
|
this._clients = [...clients];
|
|
4379
|
+
this._shared = _nullishCoalesce(opts.shared, () => ( false));
|
|
3913
4380
|
}
|
|
3914
4381
|
/**
|
|
3915
4382
|
* Build one client per funded chain from a `{ chain → wallet }` map — the
|
|
@@ -3924,30 +4391,36 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
3924
4391
|
* solana: { key: process.env.SOLANA_SECRET! },
|
|
3925
4392
|
* xrpl: { key: process.env.XRPL_SEED! },
|
|
3926
4393
|
* },
|
|
3927
|
-
*
|
|
4394
|
+
* // ONE budget across all three chains: $20 total + at most 100 payments.
|
|
4395
|
+
* policy: { maxAmount: '1.00', maxTotalPerDenom: { USD: '20.00' }, maxPayments: 100 },
|
|
3928
4396
|
* })
|
|
3929
4397
|
* const res = await payer.get('https://api.example.com/paid') // pays on the first funded chain that can settle
|
|
3930
4398
|
* ```
|
|
4399
|
+
*
|
|
4400
|
+
* Every client shares ONE {@link SpendLedger} (optionally backed by `spendStore`), so
|
|
4401
|
+
* the cross-token grand total + the count caps span all chains as a single budget.
|
|
3931
4402
|
*/
|
|
3932
4403
|
static fromWallets(opts) {
|
|
3933
4404
|
const entries = Object.entries(opts.wallets);
|
|
3934
4405
|
if (entries.length === 0) {
|
|
3935
4406
|
throw new TypeError("MultiChainPayer.fromWallets needs at least one wallet.");
|
|
3936
4407
|
}
|
|
4408
|
+
const ledger = new SpendLedger(opts.spendStore);
|
|
3937
4409
|
const clients = entries.map(
|
|
3938
4410
|
([chain, wallet]) => new PipRailClient({
|
|
3939
4411
|
chain,
|
|
3940
4412
|
wallet,
|
|
4413
|
+
ledger,
|
|
3941
4414
|
...opts.policy ? { policy: opts.policy } : {},
|
|
3942
4415
|
...opts.schemes ? { schemes: opts.schemes } : {},
|
|
3943
|
-
..._optionalChain([opts, 'access',
|
|
4416
|
+
..._optionalChain([opts, 'access', _79 => _79.rpcUrls, 'optionalAccess', _80 => _80[chain]]) ? { rpcUrl: opts.rpcUrls[chain] } : {},
|
|
3944
4417
|
...opts.onBeforePay ? { onBeforePay: opts.onBeforePay } : {},
|
|
3945
4418
|
...opts.onEvent ? { onEvent: opts.onEvent } : {},
|
|
3946
4419
|
...opts.maxPaymentRetries != null ? { maxPaymentRetries: opts.maxPaymentRetries } : {},
|
|
3947
4420
|
...opts.retryTimeoutMs != null ? { retryTimeoutMs: opts.retryTimeoutMs } : {}
|
|
3948
4421
|
})
|
|
3949
4422
|
);
|
|
3950
|
-
return new _MultiChainPayer(clients);
|
|
4423
|
+
return new _MultiChainPayer(clients, { shared: true });
|
|
3951
4424
|
}
|
|
3952
4425
|
/** The underlying single-chain clients, in preference order. Reach for one of
|
|
3953
4426
|
* these for chain-specific reads (`estimateCost`, `discoverySigner`, per-chain
|
|
@@ -3998,7 +4471,7 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
3998
4471
|
* {@link PipRailClient.post}.
|
|
3999
4472
|
*/
|
|
4000
4473
|
post(url, body, init) {
|
|
4001
|
-
const headers = new Headers(_optionalChain([init, 'optionalAccess',
|
|
4474
|
+
const headers = new Headers(_optionalChain([init, 'optionalAccess', _81 => _81.headers]));
|
|
4002
4475
|
let payload;
|
|
4003
4476
|
if (body === void 0 || body === null) {
|
|
4004
4477
|
payload = void 0;
|
|
@@ -4041,28 +4514,50 @@ var MultiChainPayer = class _MultiChainPayer {
|
|
|
4041
4514
|
register(url, opts = {}) {
|
|
4042
4515
|
return this._clients[0].register(url, opts);
|
|
4043
4516
|
}
|
|
4044
|
-
/**
|
|
4045
|
-
*
|
|
4517
|
+
/**
|
|
4518
|
+
* Aggregate spend across every chain. With a SHARED ledger (`fromWallets`), this reads
|
|
4519
|
+
* that single ledger ONCE (its records already span every chain) — so the count and the
|
|
4520
|
+
* cross-token `byDenom` grand total are correct and never double-counted. With
|
|
4521
|
+
* independent clients (the explicit constructor), it concatenates: counts summed,
|
|
4522
|
+
* per-(network,asset) `byAsset` + `byDenom` + records concatenated.
|
|
4523
|
+
*/
|
|
4046
4524
|
spent() {
|
|
4525
|
+
if (this._shared) return this._clients[0].spent();
|
|
4047
4526
|
const summaries = this._clients.map((c) => c.spent());
|
|
4048
4527
|
return {
|
|
4049
4528
|
count: summaries.reduce((n, s) => n + s.count, 0),
|
|
4050
4529
|
byAsset: summaries.flatMap((s) => s.byAsset),
|
|
4530
|
+
byDenom: summaries.flatMap((s) => s.byDenom),
|
|
4051
4531
|
records: summaries.flatMap((s) => s.records)
|
|
4052
4532
|
};
|
|
4053
4533
|
}
|
|
4054
|
-
/**
|
|
4055
|
-
*
|
|
4056
|
-
*
|
|
4057
|
-
*
|
|
4534
|
+
/**
|
|
4535
|
+
* A merged budget view. With a SHARED ledger (`fromWallets`) it's the single source of
|
|
4536
|
+
* truth — `byAsset`/`byDenom`/`counts` already span every chain, so the grand-total and
|
|
4537
|
+
* count leashes read as ONE budget. With independent clients it merges: per-(network,
|
|
4538
|
+
* asset) rows + per-denom rows concatenated, counts summed, and the MOST-RESTRICTIVE
|
|
4539
|
+
* session time envelope (the soonest deadline wins). Per-chain detail is on each
|
|
4540
|
+
* `clients[i].budget()`.
|
|
4541
|
+
*/
|
|
4058
4542
|
budget() {
|
|
4543
|
+
if (this._shared) return this._clients[0].budget();
|
|
4059
4544
|
const budgets = this._clients.map((c) => c.budget());
|
|
4060
4545
|
const session = budgets.map((b) => b.session).reduce((soonest, s) => {
|
|
4061
4546
|
if (soonest.secondsRemaining == null) return s;
|
|
4062
4547
|
if (s.secondsRemaining == null) return soonest;
|
|
4063
4548
|
return s.secondsRemaining < soonest.secondsRemaining ? s : soonest;
|
|
4064
4549
|
});
|
|
4065
|
-
return {
|
|
4550
|
+
return {
|
|
4551
|
+
session,
|
|
4552
|
+
byAsset: budgets.flatMap((b) => b.byAsset),
|
|
4553
|
+
byDenom: budgets.flatMap((b) => b.byDenom),
|
|
4554
|
+
counts: { settled: budgets.reduce((n, b) => n + b.counts.settled, 0) }
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
/** The CONFIGURED spend policy (the shared policy `fromWallets` applies to every chain;
|
|
4558
|
+
* the first client's for the explicit constructor). `undefined` when none is set. */
|
|
4559
|
+
policy() {
|
|
4560
|
+
return this._clients[0].policy();
|
|
4066
4561
|
}
|
|
4067
4562
|
};
|
|
4068
4563
|
function isBodyInit(value) {
|
|
@@ -4119,14 +4614,14 @@ function buildSelfDescription(input) {
|
|
|
4119
4614
|
}
|
|
4120
4615
|
function buildEndpointInfo(input) {
|
|
4121
4616
|
const d = input.descriptor;
|
|
4122
|
-
const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
4123
|
-
const hasInput = _optionalChain([d, 'optionalAccess',
|
|
4617
|
+
const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess', _82 => _82.summary]), () => ( input.description));
|
|
4618
|
+
const hasInput = _optionalChain([d, 'optionalAccess', _83 => _83.queryParams]) && Object.keys(d.queryParams).length > 0;
|
|
4124
4619
|
const endpoint = {
|
|
4125
4620
|
...summary ? { summary } : {},
|
|
4126
|
-
..._optionalChain([d, 'optionalAccess',
|
|
4621
|
+
..._optionalChain([d, 'optionalAccess', _84 => _84.method]) ? { method: d.method.toUpperCase() } : {},
|
|
4127
4622
|
...input.mimeType ? { mimeType: input.mimeType } : {},
|
|
4128
4623
|
...hasInput ? { input: d.queryParams } : {},
|
|
4129
|
-
..._optionalChain([d, 'optionalAccess',
|
|
4624
|
+
..._optionalChain([d, 'optionalAccess', _85 => _85.output]) ? { output: d.output } : {}
|
|
4130
4625
|
};
|
|
4131
4626
|
return Object.keys(endpoint).length > 0 ? endpoint : void 0;
|
|
4132
4627
|
}
|
|
@@ -4144,7 +4639,7 @@ function summarizePlan(plan) {
|
|
|
4144
4639
|
return `NOT payable: ${_nullishCoalesce(plan.fundingHint, () => ( `no settleable rail on ${plan.network}`))}`;
|
|
4145
4640
|
}
|
|
4146
4641
|
function explainDecline(err) {
|
|
4147
|
-
if (!(err instanceof
|
|
4642
|
+
if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) {
|
|
4148
4643
|
return `Payment failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
4149
4644
|
}
|
|
4150
4645
|
switch (err.code) {
|
|
@@ -4167,9 +4662,12 @@ function explainDecline(err) {
|
|
|
4167
4662
|
}
|
|
4168
4663
|
function formatSpendReport(summary) {
|
|
4169
4664
|
if (summary.count === 0) return "No payments yet.";
|
|
4170
|
-
|
|
4665
|
+
const perAsset = summary.byAsset.map(
|
|
4171
4666
|
(a) => `${a.totalFormatted} ${_nullishCoalesce(a.symbol, () => ( a.asset))} on ${a.network} (${a.count} payment${a.count === 1 ? "" : "s"})`
|
|
4172
4667
|
).join("; ");
|
|
4668
|
+
if (summary.byDenom.length === 0) return perAsset;
|
|
4669
|
+
const grand = summary.byDenom.map((d) => `${d.totalFormatted} ${d.denom} total`).join("; ");
|
|
4670
|
+
return `${perAsset} \u2014 grand total: ${grand}`;
|
|
4173
4671
|
}
|
|
4174
4672
|
function describeChallenge(challenge) {
|
|
4175
4673
|
const first = challenge.accepts[0];
|
|
@@ -4248,9 +4746,11 @@ Branch on \`code\` (always reliable). Key cases:
|
|
|
4248
4746
|
once (enable the exact scheme); report it, don't retry the same call blindly.
|
|
4249
4747
|
|
|
4250
4748
|
## Knowing your leash \u2014 call piprail_budget
|
|
4251
|
-
piprail_budget tells you how much budget and time you have left
|
|
4252
|
-
|
|
4253
|
-
|
|
4749
|
+
piprail_budget tells you how much budget and time you have left: per (network,
|
|
4750
|
+
asset) remaining, the cross-token GRAND TOTAL per denomination (e.g. how much
|
|
4751
|
+
USD you can still spend across every stablecoin and chain), the payment-count
|
|
4752
|
+
leash, the session time envelope, your spend so far, and the configured policy
|
|
4753
|
+
read back. Read-only; moves no funds. Use it in Mode A to self-check before paying.
|
|
4254
4754
|
|
|
4255
4755
|
## Two modes
|
|
4256
4756
|
- Mode A (headless, default): you run FREE inside a pre-set budget + time
|
|
@@ -4261,10 +4761,15 @@ Mode A to self-check before paying.
|
|
|
4261
4761
|
do NOT retry it as if it were a transient error.
|
|
4262
4762
|
|
|
4263
4763
|
## Hard facts
|
|
4264
|
-
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4764
|
+
- Per-payment + per-(network, asset) caps always apply. A cross-token GRAND TOTAL per
|
|
4765
|
+
denomination (maxTotalPerDenom, e.g. "$20 across every USD stablecoin + chain") is
|
|
4766
|
+
OPTIONAL \u2014 it sums tokens declared as one unit, each 1:1; it is NOT a price oracle and
|
|
4767
|
+
never prices a volatile native coin. Payment-COUNT caps (maxPayments / per-window) also
|
|
4768
|
+
span every chain + token.
|
|
4769
|
+
- The time envelope lives IN-MEMORY for THIS process (resets on restart). The money + count
|
|
4770
|
+
totals also reset on restart UNLESS a durable spend store is configured \u2014 then they resume.
|
|
4771
|
+
- A refusal arrives as declined:true with a reasonCode; 'BUDGET' covers the lifetime, denom,
|
|
4772
|
+
and count caps; 'OUTSIDE_WINDOW' covers both the rolling money and rolling count windows.
|
|
4268
4773
|
`;
|
|
4269
4774
|
function agentGuide() {
|
|
4270
4775
|
return PIPRAIL_AGENT_GUIDE;
|
|
@@ -4277,19 +4782,19 @@ async function readBody(res) {
|
|
|
4277
4782
|
if (!text) return null;
|
|
4278
4783
|
try {
|
|
4279
4784
|
return JSON.parse(text);
|
|
4280
|
-
} catch (
|
|
4785
|
+
} catch (e34) {
|
|
4281
4786
|
return text;
|
|
4282
4787
|
}
|
|
4283
4788
|
}
|
|
4284
4789
|
function toToolError(err) {
|
|
4285
|
-
if (!(err instanceof
|
|
4790
|
+
if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) throw err;
|
|
4286
4791
|
const out = {
|
|
4287
4792
|
ok: false,
|
|
4288
4793
|
code: err.code,
|
|
4289
4794
|
reason: err.message,
|
|
4290
4795
|
explain: explainDecline(err)
|
|
4291
4796
|
};
|
|
4292
|
-
if (err instanceof
|
|
4797
|
+
if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
|
|
4293
4798
|
out.declined = true;
|
|
4294
4799
|
if (err.reasonCode) out.reasonCode = err.reasonCode;
|
|
4295
4800
|
}
|
|
@@ -4503,14 +5008,14 @@ function paymentTools(client) {
|
|
|
4503
5008
|
receipt: parseReceipt(res)
|
|
4504
5009
|
};
|
|
4505
5010
|
} catch (err) {
|
|
4506
|
-
if (err instanceof
|
|
5011
|
+
if (err instanceof _chunkMWBT7MCEcjs.PipRailError) {
|
|
4507
5012
|
const out = {
|
|
4508
5013
|
ok: false,
|
|
4509
5014
|
code: err.code,
|
|
4510
5015
|
reason: err.message,
|
|
4511
5016
|
explain: explainDecline(err)
|
|
4512
5017
|
};
|
|
4513
|
-
if (err instanceof
|
|
5018
|
+
if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
|
|
4514
5019
|
out.declined = true;
|
|
4515
5020
|
if (err.reasonCode) out.reasonCode = err.reasonCode;
|
|
4516
5021
|
}
|
|
@@ -4579,11 +5084,11 @@ function paymentTools(client) {
|
|
|
4579
5084
|
},
|
|
4580
5085
|
{
|
|
4581
5086
|
name: "piprail_budget",
|
|
4582
|
-
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:
|
|
5087
|
+
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.",
|
|
4583
5088
|
annotations: {
|
|
4584
5089
|
title: "Check remaining budget",
|
|
4585
5090
|
readOnlyHint: true,
|
|
4586
|
-
// reads the
|
|
5091
|
+
// reads the ledger + policy; never pays
|
|
4587
5092
|
idempotentHint: true
|
|
4588
5093
|
// a pure read
|
|
4589
5094
|
},
|
|
@@ -4596,7 +5101,13 @@ function paymentTools(client) {
|
|
|
4596
5101
|
return {
|
|
4597
5102
|
spent,
|
|
4598
5103
|
remaining: budget.byAsset,
|
|
5104
|
+
grandTotal: budget.byDenom,
|
|
5105
|
+
// cross-token spend cap per denomination (USD/EUR/…)
|
|
5106
|
+
counts: budget.counts,
|
|
5107
|
+
// payment-count leash (settled + lifetime/window caps)
|
|
4599
5108
|
session: budget.session,
|
|
5109
|
+
policy: _nullishCoalesce(client.policy(), () => ( null)),
|
|
5110
|
+
// the configured leash, read back
|
|
4600
5111
|
report: formatSpendReport(spent)
|
|
4601
5112
|
};
|
|
4602
5113
|
} catch (err) {
|
|
@@ -4670,7 +5181,7 @@ function buildBazaarExtension(descriptor = {}) {
|
|
|
4670
5181
|
function pathOf(url) {
|
|
4671
5182
|
try {
|
|
4672
5183
|
return new URL(url).pathname || "/";
|
|
4673
|
-
} catch (
|
|
5184
|
+
} catch (e35) {
|
|
4674
5185
|
return url.startsWith("/") ? url : `/${url}`;
|
|
4675
5186
|
}
|
|
4676
5187
|
}
|
|
@@ -4779,28 +5290,28 @@ async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
|
|
|
4779
5290
|
const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
|
|
4780
5291
|
if (!res.ok) return void 0;
|
|
4781
5292
|
const body = await res.json();
|
|
4782
|
-
const kinds = Array.isArray(_optionalChain([body, 'optionalAccess',
|
|
5293
|
+
const kinds = Array.isArray(_optionalChain([body, 'optionalAccess', _86 => _86.kinds])) ? body.kinds : [];
|
|
4783
5294
|
const want = normalizeNetwork(network);
|
|
4784
|
-
const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess',
|
|
4785
|
-
const fp = _optionalChain([kind, 'optionalAccess',
|
|
5295
|
+
const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _87 => _87.scheme]) === "exact" && normalizeNetwork(String(_nullishCoalesce(_optionalChain([k, 'optionalAccess', _88 => _88.network]), () => ( "")))) === want);
|
|
5296
|
+
const fp = _optionalChain([kind, 'optionalAccess', _89 => _89.extra, 'optionalAccess', _90 => _90.feePayer]);
|
|
4786
5297
|
return typeof fp === "string" ? fp : void 0;
|
|
4787
|
-
} catch (
|
|
5298
|
+
} catch (e36) {
|
|
4788
5299
|
return void 0;
|
|
4789
5300
|
} finally {
|
|
4790
5301
|
clearTimeout(timer);
|
|
4791
5302
|
}
|
|
4792
5303
|
}
|
|
4793
5304
|
function parseFacilitatorSupported(body) {
|
|
4794
|
-
const kinds = _optionalChain([body, 'optionalAccess',
|
|
5305
|
+
const kinds = _optionalChain([body, 'optionalAccess', _91 => _91.kinds]);
|
|
4795
5306
|
if (!Array.isArray(kinds)) return [];
|
|
4796
5307
|
const out = [];
|
|
4797
5308
|
for (const k of kinds) {
|
|
4798
5309
|
if (!k || typeof k !== "object") continue;
|
|
4799
5310
|
const o = k;
|
|
4800
5311
|
if (typeof o.scheme !== "string" || typeof o.network !== "string") continue;
|
|
4801
|
-
const fp = _optionalChain([o, 'access',
|
|
5312
|
+
const fp = _optionalChain([o, 'access', _92 => _92.extra, 'optionalAccess', _93 => _93.feePayer]);
|
|
4802
5313
|
const ver = o.x402Version;
|
|
4803
|
-
const method = _optionalChain([o, 'access',
|
|
5314
|
+
const method = _optionalChain([o, 'access', _94 => _94.extra, 'optionalAccess', _95 => _95.assetTransferMethod]);
|
|
4804
5315
|
out.push({
|
|
4805
5316
|
scheme: o.scheme,
|
|
4806
5317
|
network: o.network,
|
|
@@ -4819,7 +5330,7 @@ async function facilitatorCoverage(url, timeoutMs = 8e3) {
|
|
|
4819
5330
|
const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
|
|
4820
5331
|
if (!res.ok) return [];
|
|
4821
5332
|
return parseFacilitatorSupported(await res.json());
|
|
4822
|
-
} catch (
|
|
5333
|
+
} catch (e37) {
|
|
4823
5334
|
return [];
|
|
4824
5335
|
} finally {
|
|
4825
5336
|
clearTimeout(timer);
|
|
@@ -4848,7 +5359,7 @@ async function post(url, body, headers) {
|
|
|
4848
5359
|
let json = null;
|
|
4849
5360
|
try {
|
|
4850
5361
|
json = await res.json();
|
|
4851
|
-
} catch (
|
|
5362
|
+
} catch (e38) {
|
|
4852
5363
|
}
|
|
4853
5364
|
return { status: res.status, json };
|
|
4854
5365
|
}
|
|
@@ -4864,13 +5375,13 @@ async function settleViaFacilitator(input) {
|
|
|
4864
5375
|
try {
|
|
4865
5376
|
verify = await post(`${base2}/verify`, body, auth);
|
|
4866
5377
|
} catch (err) {
|
|
4867
|
-
throw new (0,
|
|
5378
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4868
5379
|
`exact settle (facilitator ${base2}): /verify request failed (${err instanceof Error ? err.message : String(err)}).`,
|
|
4869
5380
|
{ cause: err }
|
|
4870
5381
|
);
|
|
4871
5382
|
}
|
|
4872
5383
|
if (verify.status !== 200) {
|
|
4873
|
-
throw new (0,
|
|
5384
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4874
5385
|
`exact settle (facilitator ${base2}): /verify returned HTTP ${verify.status} (transport/auth error).`
|
|
4875
5386
|
);
|
|
4876
5387
|
}
|
|
@@ -4886,13 +5397,13 @@ async function settleViaFacilitator(input) {
|
|
|
4886
5397
|
try {
|
|
4887
5398
|
settle = await post(`${base2}/settle`, body, auth);
|
|
4888
5399
|
} catch (err) {
|
|
4889
|
-
throw new (0,
|
|
5400
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4890
5401
|
`exact settle (facilitator ${base2}): /settle request failed (${err instanceof Error ? err.message : String(err)}).`,
|
|
4891
5402
|
{ cause: err }
|
|
4892
5403
|
);
|
|
4893
5404
|
}
|
|
4894
5405
|
if (settle.status !== 200) {
|
|
4895
|
-
throw new (0,
|
|
5406
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
4896
5407
|
`exact settle (facilitator ${base2}): /settle returned HTTP ${settle.status} (transport/auth error).`
|
|
4897
5408
|
);
|
|
4898
5409
|
}
|
|
@@ -5285,7 +5796,7 @@ function createPaymentGate(options) {
|
|
|
5285
5796
|
}
|
|
5286
5797
|
net.assertValidPayTo(payTo);
|
|
5287
5798
|
const { asset, decimals, symbol } = net.resolveToken(a.token);
|
|
5288
|
-
const amountBase =
|
|
5799
|
+
const amountBase = _chunkMWBT7MCEcjs.parseUnits.call(void 0, a.amount, decimals);
|
|
5289
5800
|
const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
|
|
5290
5801
|
if (exactOption) {
|
|
5291
5802
|
const outcome = await resolveExactRail(net, asset);
|
|
@@ -5298,7 +5809,7 @@ function createPaymentGate(options) {
|
|
|
5298
5809
|
if (exactOption && !specs.some((s) => s.exact)) {
|
|
5299
5810
|
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.";
|
|
5300
5811
|
if (exactOption.settle === "keyless") {
|
|
5301
|
-
if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess',
|
|
5812
|
+
if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess', _96 => _96.env, 'optionalAccess', _97 => _97.PIPRAIL_NO_HINTS])) {
|
|
5302
5813
|
console.warn(
|
|
5303
5814
|
`[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.)`
|
|
5304
5815
|
);
|
|
@@ -5326,7 +5837,7 @@ function createPaymentGate(options) {
|
|
|
5326
5837
|
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).`
|
|
5327
5838
|
};
|
|
5328
5839
|
}
|
|
5329
|
-
if (typeof process === "undefined" || _optionalChain([process, 'optionalAccess',
|
|
5840
|
+
if (typeof process === "undefined" || _optionalChain([process, 'optionalAccess', _98 => _98.env, 'optionalAccess', _99 => _99.NODE_ENV]) !== "production" && !_optionalChain([process, 'optionalAccess', _100 => _100.env, 'optionalAccess', _101 => _101.PIPRAIL_NO_HINTS])) {
|
|
5330
5841
|
console.warn(
|
|
5331
5842
|
`[piprail] exact: keyless rail on ${net.network} auto-settles via ${picked.url} (zero-config; pin \`exact.settle.facilitator\` in production).`
|
|
5332
5843
|
);
|
|
@@ -5460,7 +5971,7 @@ function createPaymentGate(options) {
|
|
|
5460
5971
|
instruction: describeChallenge({ x402Version: 2, resource: { url: resourceUrl }, accepts }),
|
|
5461
5972
|
...endpointInfo ? { endpoint: endpointInfo } : {}
|
|
5462
5973
|
});
|
|
5463
|
-
const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
5974
|
+
const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _102 => _102.extensions]), () => ( {}));
|
|
5464
5975
|
const rejectionPiprail = _nullishCoalesce(rejectionExt.piprail, () => ( {}));
|
|
5465
5976
|
const bodyPiprail = { ..._nullishCoalesce(selfDescribe, () => ( {})), ...rejectionPiprail };
|
|
5466
5977
|
const bodyExtensions = {
|
|
@@ -5480,7 +5991,7 @@ function createPaymentGate(options) {
|
|
|
5480
5991
|
...options.mimeType ? { mimeType: options.mimeType } : {}
|
|
5481
5992
|
},
|
|
5482
5993
|
accepts,
|
|
5483
|
-
..._optionalChain([opts, 'optionalAccess',
|
|
5994
|
+
..._optionalChain([opts, 'optionalAccess', _103 => _103.error]) ? { error: opts.error } : {},
|
|
5484
5995
|
...Object.keys(bodyExtensions).length > 0 ? { extensions: bodyExtensions } : {}
|
|
5485
5996
|
};
|
|
5486
5997
|
const headerChallenge = {
|
|
@@ -5511,8 +6022,8 @@ function createPaymentGate(options) {
|
|
|
5511
6022
|
function enrichReceipt(spec, receipt) {
|
|
5512
6023
|
let amountFormatted = receipt.amount;
|
|
5513
6024
|
try {
|
|
5514
|
-
amountFormatted =
|
|
5515
|
-
} catch (
|
|
6025
|
+
amountFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
|
|
6026
|
+
} catch (e39) {
|
|
5516
6027
|
}
|
|
5517
6028
|
return {
|
|
5518
6029
|
...receipt,
|
|
@@ -5526,7 +6037,7 @@ function createPaymentGate(options) {
|
|
|
5526
6037
|
if (!options.onPaidError) return;
|
|
5527
6038
|
try {
|
|
5528
6039
|
options.onPaidError(error, receipt);
|
|
5529
|
-
} catch (
|
|
6040
|
+
} catch (e40) {
|
|
5530
6041
|
}
|
|
5531
6042
|
}
|
|
5532
6043
|
function fireOnPaid(receipt) {
|
|
@@ -5551,7 +6062,7 @@ function createPaymentGate(options) {
|
|
|
5551
6062
|
if (!options.onFailedError) return;
|
|
5552
6063
|
try {
|
|
5553
6064
|
options.onFailedError(error, failure);
|
|
5554
|
-
} catch (
|
|
6065
|
+
} catch (e41) {
|
|
5555
6066
|
}
|
|
5556
6067
|
}
|
|
5557
6068
|
function fireOnFailed(failure) {
|
|
@@ -5651,28 +6162,28 @@ function createPaymentGate(options) {
|
|
|
5651
6162
|
nonce = [exact.payload.transaction, exact.payload.senderAuth].map((t) => {
|
|
5652
6163
|
try {
|
|
5653
6164
|
return Buffer.from(t, "base64").toString("base64");
|
|
5654
|
-
} catch (
|
|
6165
|
+
} catch (e42) {
|
|
5655
6166
|
return t;
|
|
5656
6167
|
}
|
|
5657
6168
|
}).join("|");
|
|
5658
6169
|
} else if ("transaction" in exact.payload) {
|
|
5659
6170
|
try {
|
|
5660
6171
|
nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
|
|
5661
|
-
} catch (
|
|
6172
|
+
} catch (e43) {
|
|
5662
6173
|
nonce = exact.payload.transaction;
|
|
5663
6174
|
}
|
|
5664
6175
|
} else if ("paymentGroup" in exact.payload) {
|
|
5665
6176
|
nonce = exact.payload.paymentGroup.map((t) => {
|
|
5666
6177
|
try {
|
|
5667
6178
|
return Buffer.from(t, "base64").toString("base64");
|
|
5668
|
-
} catch (
|
|
6179
|
+
} catch (e44) {
|
|
5669
6180
|
return t;
|
|
5670
6181
|
}
|
|
5671
6182
|
}).join("|");
|
|
5672
6183
|
} else if ("signedDelegateAction" in exact.payload) {
|
|
5673
6184
|
try {
|
|
5674
6185
|
nonce = Buffer.from(exact.payload.signedDelegateAction, "base64").toString("base64");
|
|
5675
|
-
} catch (
|
|
6186
|
+
} catch (e45) {
|
|
5676
6187
|
nonce = exact.payload.signedDelegateAction;
|
|
5677
6188
|
}
|
|
5678
6189
|
} else if ("permit2Authorization" in exact.payload) {
|
|
@@ -5695,7 +6206,7 @@ function createPaymentGate(options) {
|
|
|
5695
6206
|
const ftMethod = accept.extra.assetTransferMethod;
|
|
5696
6207
|
const needsFeePayer = ftMethod === "svm" || ftMethod === "algorand" || ftMethod === "aptos" || ftMethod === "near";
|
|
5697
6208
|
if (needsFeePayer && !accept.extra.feePayer) {
|
|
5698
|
-
throw new (0,
|
|
6209
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
5699
6210
|
`exact settle: the ${ftMethod} facilitator rail is missing extra.feePayer (the gas sponsor) \u2014 cannot settle.`
|
|
5700
6211
|
);
|
|
5701
6212
|
}
|
|
@@ -5760,7 +6271,7 @@ function requirePayment(options) {
|
|
|
5760
6271
|
try {
|
|
5761
6272
|
result = await gate.verify(_nullishCoalesce(req.headers[HEADER_SIGNATURE], () => ( req.headers[HEADER_SIGNATURE_V1])));
|
|
5762
6273
|
} catch (err) {
|
|
5763
|
-
if (err instanceof
|
|
6274
|
+
if (err instanceof _chunkMWBT7MCEcjs.SettlementError) {
|
|
5764
6275
|
res.status(502);
|
|
5765
6276
|
res.json({
|
|
5766
6277
|
x402Version: 2,
|
|
@@ -5808,7 +6319,7 @@ function isRetryableStatus(status) {
|
|
|
5808
6319
|
}
|
|
5809
6320
|
var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
5810
6321
|
async function signBody(secret, body) {
|
|
5811
|
-
const subtle = _optionalChain([globalThis, 'access',
|
|
6322
|
+
const subtle = _optionalChain([globalThis, 'access', _104 => _104.crypto, 'optionalAccess', _105 => _105.subtle]);
|
|
5812
6323
|
if (!subtle) return null;
|
|
5813
6324
|
try {
|
|
5814
6325
|
const enc = new TextEncoder();
|
|
@@ -5818,7 +6329,7 @@ async function signBody(secret, body) {
|
|
|
5818
6329
|
const sig = await subtle.sign("HMAC", key, enc.encode(body));
|
|
5819
6330
|
const hex = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5820
6331
|
return `sha256=${hex}`;
|
|
5821
|
-
} catch (
|
|
6332
|
+
} catch (e46) {
|
|
5822
6333
|
return null;
|
|
5823
6334
|
}
|
|
5824
6335
|
}
|
|
@@ -5878,8 +6389,8 @@ async function deliverReceipt(receipt, options) {
|
|
|
5878
6389
|
const retryable = status === void 0 ? true : isRetryableStatus(status);
|
|
5879
6390
|
const willRetry = !ok && retryable && attempt < maxAttempts;
|
|
5880
6391
|
try {
|
|
5881
|
-
_optionalChain([onAttempt, 'optionalCall',
|
|
5882
|
-
} catch (
|
|
6392
|
+
_optionalChain([onAttempt, 'optionalCall', _106 => _106({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
|
|
6393
|
+
} catch (e47) {
|
|
5883
6394
|
}
|
|
5884
6395
|
if (ok) return { delivered: true, attempts: attempt, status };
|
|
5885
6396
|
if (!willRetry) {
|
|
@@ -5998,4 +6509,9 @@ async function deliverReceipt(receipt, options) {
|
|
|
5998
6509
|
|
|
5999
6510
|
|
|
6000
6511
|
|
|
6001
|
-
|
|
6512
|
+
|
|
6513
|
+
|
|
6514
|
+
|
|
6515
|
+
|
|
6516
|
+
|
|
6517
|
+
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.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_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.UnknownTokenError = _chunkMWBT7MCEcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkMWBT7MCEcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkMWBT7MCEcjs.UnsupportedSchemeError; exports.WalletRequiredError = _chunkMWBT7MCEcjs.WalletRequiredError; exports.WrongChainError = _chunkMWBT7MCEcjs.WrongChainError; exports.WrongFamilyError = _chunkMWBT7MCEcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_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.buildReceiptHeader = buildReceiptHeader; exports.buildSelfDescription = buildSelfDescription; exports.buildSignatureHeader = buildSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.classifyChallenge = classifyChallenge; exports.createPaymentGate = createPaymentGate; 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.getDirectoryInfo = getDirectoryInfo; exports.isPermit2ProxyChain = isPermit2ProxyChain; exports.knownFacilitatorsFor = knownFacilitatorsFor; exports.memorySpendStore = _chunkO4UQOZ4Zcjs.memorySpendStore; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseFacilitatorSupported = parseFacilitatorSupported; exports.parseReceipt = parseReceipt; exports.parseSettleResponse = parseSettleResponse; exports.parseSignatureHeader = parseSignatureHeader; 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.toInsufficientFundsError = _chunkMWBT7MCEcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
|