@piprail/sdk 2.8.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +5 -3
  3. package/dist/{algorand-WJL6VRXW.js → algorand-AA3WXKW4.js} +1 -1
  4. package/dist/{algorand-EKD34T4Q.cjs → algorand-FCEECDG6.cjs} +32 -32
  5. package/dist/{aptos-CVHLVMAF.cjs → aptos-GHJPO6JJ.cjs} +31 -31
  6. package/dist/{aptos-WMPFEASY.js → aptos-LY67Q6QF.js} +1 -1
  7. package/dist/{chunk-VPENSVOI.cjs → chunk-MWBT7MCE.cjs} +7 -3
  8. package/dist/chunk-O4UQOZ4Z.cjs +14 -0
  9. package/dist/{chunk-W3HOMXOF.js → chunk-SC2ZYDHD.js} +6 -2
  10. package/dist/chunk-SK3CB7UA.js +14 -0
  11. package/dist/index.cjs +748 -237
  12. package/dist/index.d.cts +2384 -2577
  13. package/dist/index.d.ts +2384 -2577
  14. package/dist/index.js +589 -78
  15. package/dist/ledger-BtzrfO-3.d.cts +658 -0
  16. package/dist/ledger-BtzrfO-3.d.ts +658 -0
  17. package/dist/{near-NYFMX2KA.cjs → near-6KAQVVG2.cjs} +26 -26
  18. package/dist/{near-QV4IOZNX.js → near-FZBUICCS.js} +1 -1
  19. package/dist/node.cjs +38 -0
  20. package/dist/node.d.cts +16 -0
  21. package/dist/node.d.ts +16 -0
  22. package/dist/node.js +38 -0
  23. package/dist/{solana-Z34OWXMO.js → solana-ELUWO6N5.js} +1 -1
  24. package/dist/{solana-L342QCSV.cjs → solana-MYF4HBO4.cjs} +32 -32
  25. package/dist/{stellar-PWC4LETJ.js → stellar-BEMT7UYF.js} +1 -1
  26. package/dist/{stellar-YEXRVR3W.cjs → stellar-SUKASK4N.cjs} +20 -20
  27. package/dist/{sui-VEOGHBYE.js → sui-F5JQ2N6I.js} +1 -1
  28. package/dist/{sui-EFRSCDWN.cjs → sui-VE5LT7BL.cjs} +16 -16
  29. package/dist/{ton-N7KSPSAR.js → ton-7GKCTC5H.js} +1 -1
  30. package/dist/{ton-LZKJWYHV.cjs → ton-AOR3EURW.cjs} +16 -16
  31. package/dist/{tron-DQU4WTGQ.cjs → tron-EMFXDFHW.cjs} +24 -24
  32. package/dist/{tron-WKDUQVZR.js → tron-ZZZS3FNN.js} +1 -1
  33. package/dist/{xrpl-6B4IYRWI.js → xrpl-DD7TJL5L.js} +1 -1
  34. package/dist/{xrpl-K3U3SBF4.cjs → xrpl-Y6SQNYLC.cjs} +20 -20
  35. 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; var _class2;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
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
- var _chunkVPENSVOIcjs = require('./chunk-VPENSVOI.cjs');
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, _chunkVPENSVOIcjs.UnsupportedNetworkError)(
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, _chunkVPENSVOIcjs.UnsupportedNetworkError)(
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
- _chunkVPENSVOIcjs.assertNoLegacyWalletKey.call(void 0, config, "EVM");
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, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.WrongChainError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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, _chunkVPENSVOIcjs.SettlementError)(
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, _chunkVPENSVOIcjs.SettlementError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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(_chunkVPENSVOIcjs.toInsufficientFundsError.call(void 0, err), () => ( new (0, _chunkVPENSVOIcjs.SettlementError)(
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, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
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, _chunkVPENSVOIcjs.SettlementError)(
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, _chunkVPENSVOIcjs.SettlementError)(
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, _chunkVPENSVOIcjs.UnknownTokenError)(
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
- _chunkVPENSVOIcjs.rejectForeignToken.call(void 0, token, "evm", network);
1597
+ _chunkMWBT7MCEcjs.rejectForeignToken.call(void 0, token, "evm", network);
1594
1598
  if (!("address" in token)) {
1595
- throw new (0, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.WrongFamilyError)(
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
- _chunkVPENSVOIcjs.assertNoLegacyWalletKey.call(void 0, wallet, "EVM");
1642
+ _chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, wallet, "EVM");
1639
1643
  if (!("key" in wallet) && !("walletClient" in wallet)) {
1640
- throw new (0, _chunkVPENSVOIcjs.WrongFamilyError)(
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, _chunkVPENSVOIcjs.InsufficientFundsError)(
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(_chunkVPENSVOIcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
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, _chunkVPENSVOIcjs.ConfirmationTimeoutError)(
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 _chunkVPENSVOIcjs.nativeCost.call(void 0, {
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 _chunkVPENSVOIcjs.nativeCost.call(void 0, {
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 _chunkVPENSVOIcjs.nativeCost.call(void 0, {
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-L342QCSV.cjs")));
1847
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-MYF4HBO4.cjs")));
1844
1848
  } catch (cause) {
1845
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-LZKJWYHV.cjs")));
1859
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-AOR3EURW.cjs")));
1856
1860
  } catch (cause) {
1857
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-YEXRVR3W.cjs")));
1871
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-SUKASK4N.cjs")));
1868
1872
  } catch (cause) {
1869
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-K3U3SBF4.cjs")));
1883
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-Y6SQNYLC.cjs")));
1880
1884
  } catch (cause) {
1881
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-DQU4WTGQ.cjs")));
1895
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-EMFXDFHW.cjs")));
1892
1896
  } catch (cause) {
1893
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-EFRSCDWN.cjs")));
1907
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-VE5LT7BL.cjs")));
1904
1908
  } catch (cause) {
1905
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-NYFMX2KA.cjs")));
1919
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-6KAQVVG2.cjs")));
1916
1920
  } catch (cause) {
1917
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-CVHLVMAF.cjs")));
1931
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-GHJPO6JJ.cjs")));
1928
1932
  } catch (cause) {
1929
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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-EKD34T4Q.cjs")));
1943
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-FCEECDG6.cjs")));
1940
1944
  } catch (cause) {
1941
- throw new (0, _chunkVPENSVOIcjs.MissingDriverError)(
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
  );
@@ -2582,6 +2586,53 @@ function encodeBase642(str) {
2582
2586
  }
2583
2587
 
2584
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
+ }
2585
2636
  var ALLOW = { allowed: true };
2586
2637
  var deny = (code, reason) => ({
2587
2638
  allowed: false,
@@ -2648,7 +2699,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
2648
2699
  }
2649
2700
  }
2650
2701
  if (policy.maxAmount !== void 0) {
2651
- const cap = _chunkVPENSVOIcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
2702
+ const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
2652
2703
  if (intent.amountBase > cap) {
2653
2704
  return deny(
2654
2705
  "MAX_AMOUNT",
@@ -2657,7 +2708,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
2657
2708
  }
2658
2709
  }
2659
2710
  if (policy.maxTotal !== void 0) {
2660
- const cap = _chunkVPENSVOIcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
2711
+ const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
2661
2712
  if (spentForAssetBase + intent.amountBase > cap) {
2662
2713
  return deny(
2663
2714
  "MAX_TOTAL",
@@ -2665,8 +2716,36 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
2665
2716
  );
2666
2717
  }
2667
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
+ }
2668
2747
  if (ctx && policy.windowTotal !== void 0 && policy.windowSeconds !== void 0) {
2669
- const cap = _chunkVPENSVOIcjs.floorUnits.call(void 0, policy.windowTotal, intent.decimals);
2748
+ const cap = _chunkMWBT7MCEcjs.floorUnits.call(void 0, policy.windowTotal, intent.decimals);
2670
2749
  if (ctx.spentInWindowBase + intent.amountBase > cap) {
2671
2750
  return deny(
2672
2751
  "WINDOW_TOTAL",
@@ -2674,44 +2753,150 @@ function evaluatePolicy(intent, policy, spentForAssetBase, ctx) {
2674
2753
  );
2675
2754
  }
2676
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
+ }
2677
2764
  return ALLOW;
2678
2765
  }
2679
2766
 
2680
2767
  // src/ledger.ts
2681
2768
  var keyFor = (network, asset) => `${network}|${asset}`;
2682
- var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this); }
2769
+ var SpendLedger = (_class = class {
2683
2770
  __init() {this.records = []}
2684
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
+
2685
2781
  /**
2686
2782
  * Session clock origin (epoch-ms) — process/session start = ledger
2687
2783
  * construction. In-memory; a new process is a new session. The client reads it
2688
2784
  * to compute the `ttlSeconds` deadline and the rolling-window slice.
2689
2785
  */
2690
- __init3() {this.sessionStart = Date.now()}
2691
- /** Record a settled payment. `decimals` is the TRUE token decimals (for the
2692
- * per-asset running total used by maxTotal + the formatted summary). */
2693
- record(r, decimals) {
2694
- this.records.push(r);
2695
- const key = keyFor(r.network, r.asset);
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);
2696
2825
  const bucket = this.buckets.get(key);
2697
2826
  if (bucket) {
2698
- bucket.total += BigInt(r.amountBase);
2827
+ bucket.total += BigInt(rec.amountBase);
2699
2828
  bucket.count += 1;
2700
- if (!bucket.symbol && r.symbol) bucket.symbol = r.symbol;
2829
+ if (!bucket.symbol && rec.symbol) bucket.symbol = rec.symbol;
2701
2830
  } else {
2702
2831
  this.buckets.set(key, {
2703
- network: r.network,
2704
- asset: r.asset,
2705
- symbol: r.symbol,
2832
+ network: rec.network,
2833
+ asset: rec.asset,
2834
+ symbol: rec.symbol,
2706
2835
  decimals,
2707
- total: BigInt(r.amountBase),
2836
+ total: BigInt(rec.amountBase),
2708
2837
  count: 1
2709
2838
  });
2710
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
+ }
2711
2861
  }
2712
2862
  /** Running total (base units) already spent on this (network, asset). */
2713
2863
  totalFor(network, asset) {
2714
- return _nullishCoalesce(_optionalChain([this, 'access', _18 => _18.buckets, 'access', _19 => _19.get, 'call', _20 => _20(keyFor(network, asset)), 'optionalAccess', _21 => _21.total]), () => ( 0n));
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;
2715
2900
  }
2716
2901
  /**
2717
2902
  * Sum of base-unit amounts for (network, asset) whose record `at` (ISO
@@ -2723,9 +2908,9 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
2723
2908
  totalSince(network, asset, sinceMs) {
2724
2909
  let sum = 0n;
2725
2910
  for (const r of this.records) {
2726
- if (r.network === network && r.asset === asset && Date.parse(r.at) >= sinceMs) {
2727
- sum += BigInt(r.amountBase);
2728
- }
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);
2729
2914
  }
2730
2915
  return sum;
2731
2916
  }
@@ -2745,6 +2930,14 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
2745
2930
  totalBase: b.total
2746
2931
  }));
2747
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
+ }
2748
2941
  /** An immutable snapshot of all spend so far. */
2749
2942
  summary() {
2750
2943
  return {
@@ -2755,9 +2948,15 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
2755
2948
  symbol: b.symbol,
2756
2949
  decimals: b.decimals,
2757
2950
  totalBase: b.total.toString(),
2758
- totalFormatted: _chunkVPENSVOIcjs.formatUnits.call(void 0, b.total, b.decimals),
2951
+ totalFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, b.total, b.decimals),
2759
2952
  count: b.count
2760
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
+ })),
2761
2960
  records: [...this.records]
2762
2961
  };
2763
2962
  }
@@ -2771,24 +2970,36 @@ var RECIPIENT_FIX = {
2771
2970
  NOT_OPTED_IN: "the recipient must opt into this asset once (a 0-amount self-transfer)",
2772
2971
  INACTIVE: "the recipient account doesn't exist yet \u2014 fund it with the chain's base reserve to activate it"
2773
2972
  };
2774
- var PipRailClient = (_class2 = class {
2973
+ var PipRailClient = class {
2775
2974
 
2776
2975
 
2777
2976
 
2778
2977
 
2779
- // Per-asset tally of everything this client has paid (powers spent() and the
2780
- // policy's maxTotal cap).
2781
- __init4() {this.ledger = new SpendLedger()}
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).
2982
+
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.
2782
2986
  // Resolved lazily on first request — this is what lets Solana (and future
2783
2987
  // families) auto-mount with no setup call.
2784
2988
 
2785
- constructor(opts) {;_class2.prototype.__init4.call(this);
2989
+ constructor(opts) {
2786
2990
  this.opts = opts;
2787
2991
  this.maxRetries = Math.max(1, _nullishCoalesce(opts.maxPaymentRetries, () => ( 3)));
2788
2992
  this.retryTimeoutMs = _nullishCoalesce(opts.retryTimeoutMs, () => ( 3e4));
2789
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)));
2790
3000
  this.assertPolicyAmountCaps(opts.policy);
2791
3001
  this.assertPolicyTimeOptions(opts.policy);
3002
+ this.assertPolicySpendControls(opts.policy);
2792
3003
  }
2793
3004
  /**
2794
3005
  * Fail LOUDLY at construction on a malformed amount cap — a security boundary
@@ -2809,6 +3020,65 @@ var PipRailClient = (_class2 = class {
2809
3020
  );
2810
3021
  }
2811
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
+ }
2812
3082
  }
2813
3083
  /**
2814
3084
  * Fail LOUDLY at construction on a misconfigured time policy — a security
@@ -2823,11 +3093,17 @@ var PipRailClient = (_class2 = class {
2823
3093
  if (!policy) return;
2824
3094
  const hasWindowTotal = policy.windowTotal !== void 0;
2825
3095
  const hasWindowSeconds = policy.windowSeconds !== void 0;
2826
- if (hasWindowTotal !== hasWindowSeconds) {
3096
+ const hasWindowCount = policy.maxPaymentsPerWindow !== void 0;
3097
+ if (hasWindowTotal && !hasWindowSeconds) {
2827
3098
  throw new TypeError(
2828
3099
  "policy.windowTotal and policy.windowSeconds must be set together \u2014 a rolling-window cap can't be half-armed (set both, or neither)."
2829
3100
  );
2830
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
+ }
2831
3107
  if (hasWindowSeconds && !(Number.isSafeInteger(policy.windowSeconds) && policy.windowSeconds > 0)) {
2832
3108
  throw new TypeError("policy.windowSeconds must be a positive integer number of seconds.");
2833
3109
  }
@@ -2839,13 +3115,21 @@ var PipRailClient = (_class2 = class {
2839
3115
  );
2840
3116
  }
2841
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
+ }
2842
3126
  }
2843
3127
  /** Emit an observability event, never letting a throwing handler break the
2844
3128
  * payment flow (mirrors the server gate's `onPaid` isolation). */
2845
3129
  safeEmit(event) {
2846
3130
  try {
2847
3131
  this.onEvent(event);
2848
- } catch (e27) {
3132
+ } catch (e29) {
2849
3133
  }
2850
3134
  }
2851
3135
  /** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
@@ -2875,7 +3159,7 @@ var PipRailClient = (_class2 = class {
2875
3159
  * as-is) or a plain object (serialised as JSON).
2876
3160
  */
2877
3161
  post(url, body, init) {
2878
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _22 => _22.headers]));
3162
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _28 => _28.headers]));
2879
3163
  let payload;
2880
3164
  if (body === void 0 || body === null) {
2881
3165
  payload = void 0;
@@ -2906,7 +3190,7 @@ var PipRailClient = (_class2 = class {
2906
3190
  * "0.05 USDC on Base, within budget → pay it." No funds move.
2907
3191
  */
2908
3192
  async quote(url, init) {
2909
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _23 => _23.method]), () => ( "GET")) });
3193
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _29 => _29.method]), () => ( "GET")) });
2910
3194
  if (res.status !== 402) return null;
2911
3195
  const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
2912
3196
  return quote;
@@ -2925,22 +3209,32 @@ var PipRailClient = (_class2 = class {
2925
3209
  * on Tron, where a USD₮ transfer can cost real TRX.
2926
3210
  */
2927
3211
  async estimateCost(url, init) {
2928
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _24 => _24.method]), () => ( "GET")) });
3212
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _30 => _30.method]), () => ( "GET")) });
2929
3213
  if (res.status !== 402) return null;
2930
3214
  const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
2931
3215
  const cost = await net.estimateCost(accept);
2932
3216
  return { quote, cost };
2933
3217
  }
2934
3218
  /** Aggregated snapshot of every payment this client has settled — total
2935
- * count, cumulative spend per token, and the individual records. */
3219
+ * count, cumulative spend per token, cumulative spend per denomination (the
3220
+ * cross-token grand total), and the individual records. */
2936
3221
  spent() {
2937
3222
  return this.ledger.summary();
2938
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
+ }
2939
3230
  /**
2940
3231
  * Read-only budget + time leash for a Mode-A (headless) agent — the policy IS
2941
3232
  * the consent, and this is how the agent SEES what's left of it before paying.
2942
- * Composes the in-memory ledger with the configured policy; never throws, moves
2943
- * no funds. PROCESS-SCOPED every figure resets on restart (see {@link SessionBudget}).
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}).
2944
3238
  */
2945
3239
  budget() {
2946
3240
  const view = this.sessionView();
@@ -2948,12 +3242,57 @@ var PipRailClient = (_class2 = class {
2948
3242
  return {
2949
3243
  session: {
2950
3244
  start,
2951
- expiresAt: _optionalChain([view, 'optionalAccess', _25 => _25.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
2952
- secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _26 => _26.secondsRemaining]), () => ( null))
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))
2953
3247
  },
2954
- byAsset: this.remaining()
3248
+ byAsset: this.remaining(),
3249
+ byDenom: this.denomRemaining(),
3250
+ counts: this.countStatus()
2955
3251
  };
2956
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
+ }
2957
3296
  /**
2958
3297
  * Per-(network, asset) remaining budget — ONE row per pair the ledger already
2959
3298
  * holds (decimals are known only after the first spend), so a fresh client with
@@ -2962,7 +3301,7 @@ var PipRailClient = (_class2 = class {
2962
3301
  * never throws, never sums across tokens (no price oracle). PROCESS-SCOPED.
2963
3302
  */
2964
3303
  remaining() {
2965
- const maxTotal = _optionalChain([this, 'access', _27 => _27.opts, 'access', _28 => _28.policy, 'optionalAccess', _29 => _29.maxTotal]);
3304
+ const maxTotal = _optionalChain([this, 'access', _38 => _38.opts, 'access', _39 => _39.policy, 'optionalAccess', _40 => _40.maxTotal]);
2966
3305
  return this.ledger.assetBuckets().map((b) => {
2967
3306
  const base2 = {
2968
3307
  network: b.network,
@@ -2972,13 +3311,13 @@ var PipRailClient = (_class2 = class {
2972
3311
  spentBase: b.totalBase.toString()
2973
3312
  };
2974
3313
  if (maxTotal === void 0) return base2;
2975
- const capBase = _chunkVPENSVOIcjs.floorUnits.call(void 0, maxTotal, b.decimals);
3314
+ const capBase = _chunkMWBT7MCEcjs.floorUnits.call(void 0, maxTotal, b.decimals);
2976
3315
  const remainingBase = capBase > b.totalBase ? capBase - b.totalBase : 0n;
2977
3316
  return {
2978
3317
  ...base2,
2979
3318
  capBase: capBase.toString(),
2980
3319
  remainingBase: remainingBase.toString(),
2981
- remainingFormatted: _chunkVPENSVOIcjs.formatUnits.call(void 0, remainingBase, b.decimals)
3320
+ remainingFormatted: _chunkMWBT7MCEcjs.formatUnits.call(void 0, remainingBase, b.decimals)
2982
3321
  };
2983
3322
  });
2984
3323
  }
@@ -3014,15 +3353,15 @@ var PipRailClient = (_class2 = class {
3014
3353
  * the plan yourself. No funds move.
3015
3354
  */
3016
3355
  async planPayment(url, init) {
3017
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _30 => _30.method]), () => ( "GET")) });
3356
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _41 => _41.method]), () => ( "GET")) });
3018
3357
  if (res.status !== 402) return null;
3019
3358
  const challenge = await parseChallenge(res);
3020
3359
  if (!challenge) {
3021
- throw new (0, _chunkVPENSVOIcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
3360
+ throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
3022
3361
  }
3023
3362
  const { net, wallet } = await this.ensure();
3024
3363
  if (!wallet) {
3025
- throw new (0, _chunkVPENSVOIcjs.WalletRequiredError)(
3364
+ throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
3026
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."
3027
3366
  );
3028
3367
  }
@@ -3195,31 +3534,31 @@ var PipRailClient = (_class2 = class {
3195
3534
  * streams throw `NonReplayableBodyError`.
3196
3535
  */
3197
3536
  async fetch(url, init) {
3198
- const body = _optionalChain([init, 'optionalAccess', _31 => _31.body]);
3537
+ const body = _optionalChain([init, 'optionalAccess', _42 => _42.body]);
3199
3538
  if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
3200
- throw new (0, _chunkVPENSVOIcjs.NonReplayableBodyError)(
3539
+ throw new (0, _chunkMWBT7MCEcjs.NonReplayableBodyError)(
3201
3540
  "fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
3202
3541
  );
3203
3542
  }
3204
3543
  const firstResponse = await fetch(url, init);
3205
3544
  if (firstResponse.status !== 402) return firstResponse;
3206
- const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _32 => _32.schemes]));
3545
+ const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _43 => _43.schemes]));
3207
3546
  const resolved = await this.resolveChallenge(url, firstResponse, schemes);
3208
3547
  const { net, wallet, challenge } = resolved;
3209
3548
  if (!wallet) {
3210
- throw new (0, _chunkVPENSVOIcjs.WalletRequiredError)(
3549
+ throw new (0, _chunkMWBT7MCEcjs.WalletRequiredError)(
3211
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)."
3212
3551
  );
3213
3552
  }
3214
3553
  let accept = resolved.accept;
3215
3554
  let quote = resolved.quote;
3216
- const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _33 => _33.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
3555
+ const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _44 => _44.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
3217
3556
  if (autoRoute) {
3218
3557
  const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
3219
3558
  if (!plan.best) {
3220
3559
  const reason = _nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment."));
3221
3560
  this.safeEmit({ kind: "payment-failed", reason });
3222
- throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason);
3561
+ throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(reason);
3223
3562
  }
3224
3563
  accept = plan.best.accept;
3225
3564
  quote = plan.best.quote;
@@ -3243,7 +3582,7 @@ var PipRailClient = (_class2 = class {
3243
3582
  async resolveChallenge(url, response, schemes) {
3244
3583
  const challenge = await parseChallenge(response);
3245
3584
  if (!challenge) {
3246
- throw new (0, _chunkVPENSVOIcjs.InvalidEnvelopeError)(
3585
+ throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
3247
3586
  "402 response did not include a parseable x402 challenge."
3248
3587
  );
3249
3588
  }
@@ -3254,7 +3593,7 @@ var PipRailClient = (_class2 = class {
3254
3593
  (a) => a.scheme === "exact" && this.supportsNetwork(net, a.network)
3255
3594
  );
3256
3595
  if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
3257
- throw new (0, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
3596
+ throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
3258
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.`
3259
3598
  );
3260
3599
  }
@@ -3263,13 +3602,13 @@ var PipRailClient = (_class2 = class {
3263
3602
  (a) => a.scheme === "exact" && this.supportsNetwork(net, a.network) && net.describeAsset(a.asset) != null
3264
3603
  );
3265
3604
  if (payable) {
3266
- throw new (0, _chunkVPENSVOIcjs.NoCompatibleAcceptError)(
3605
+ throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
3267
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).`
3268
3607
  );
3269
3608
  }
3270
3609
  }
3271
3610
  const networks = [...new Set(challenge.accepts.map((a) => a.network))].join(", ");
3272
- throw new (0, _chunkVPENSVOIcjs.NoCompatibleAcceptError)(
3611
+ throw new (0, _chunkMWBT7MCEcjs.NoCompatibleAcceptError)(
3273
3612
  `No accepts[] entry payable by this client on ${net.network} (schemes: ${schemes.join(", ")}; challenge offered: ${networks || "none"}).`
3274
3613
  );
3275
3614
  }
@@ -3371,7 +3710,7 @@ var PipRailClient = (_class2 = class {
3371
3710
  const shortfall = {};
3372
3711
  if (!quote.withinPolicy) {
3373
3712
  blockers.push(
3374
- 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"
3375
3714
  );
3376
3715
  }
3377
3716
  if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
@@ -3382,21 +3721,21 @@ var PipRailClient = (_class2 = class {
3382
3721
  if (isExact) {
3383
3722
  if (tokenKnown && bal.token < amount) {
3384
3723
  blockers.push("INSUFFICIENT_TOKEN");
3385
- shortfall.token = _chunkVPENSVOIcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
3724
+ shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
3386
3725
  }
3387
3726
  } else if (isNative) {
3388
3727
  if (nativeKnown && bal.native < amount + fee) {
3389
3728
  blockers.push("INSUFFICIENT_TOKEN");
3390
- shortfall.token = _chunkVPENSVOIcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
3729
+ shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
3391
3730
  }
3392
3731
  } else {
3393
3732
  if (tokenKnown && bal.token < amount) {
3394
3733
  blockers.push("INSUFFICIENT_TOKEN");
3395
- shortfall.token = _chunkVPENSVOIcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
3734
+ shortfall.token = _chunkMWBT7MCEcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
3396
3735
  }
3397
3736
  if (nativeKnown && bal.native < fee) {
3398
3737
  blockers.push("INSUFFICIENT_GAS");
3399
- shortfall.native = _chunkVPENSVOIcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
3738
+ shortfall.native = _chunkMWBT7MCEcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
3400
3739
  } else if (nativeKnown && fee > 0n && bal.native < fee * 3n / 2n) {
3401
3740
  warnings.push("THIN_GAS_MARGIN");
3402
3741
  }
@@ -3421,8 +3760,8 @@ var PipRailClient = (_class2 = class {
3421
3760
  blockers,
3422
3761
  warnings,
3423
3762
  balance: {
3424
- token: bal.token != null ? _chunkVPENSVOIcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
3425
- native: bal.native != null ? _chunkVPENSVOIcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : 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
3426
3765
  },
3427
3766
  need: { token: quote.amountFormatted, native: cost.feeFormatted },
3428
3767
  ...shortfall.token || shortfall.native ? { shortfall } : {},
@@ -3433,25 +3772,30 @@ var PipRailClient = (_class2 = class {
3433
3772
  * driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
3434
3773
  buildQuote(net, accept, url, description) {
3435
3774
  if (typeof accept.amount !== "string" || !/^\d+$/.test(accept.amount)) {
3436
- throw new (0, _chunkVPENSVOIcjs.InvalidEnvelopeError)(
3775
+ throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
3437
3776
  `challenge amount "${String(accept.amount)}" is not a base-unit integer string.`
3438
3777
  );
3439
3778
  }
3440
3779
  if (typeof accept.asset !== "string" || accept.asset.length === 0) {
3441
- throw new (0, _chunkVPENSVOIcjs.InvalidEnvelopeError)(
3780
+ throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
3442
3781
  `challenge on ${accept.network} states no (string) asset \u2014 refusing to price it.`
3443
3782
  );
3444
3783
  }
3445
3784
  const amountBase = BigInt(accept.amount);
3446
3785
  const described = net.describeAsset(accept.asset);
3447
- const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _34 => _34.decimals]), () => ( _optionalChain([accept, 'access', _35 => _35.extra, 'optionalAccess', _36 => _36.decimals])));
3786
+ const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _45 => _45.decimals]), () => ( _optionalChain([accept, 'access', _46 => _46.extra, 'optionalAccess', _47 => _47.decimals])));
3448
3787
  if (typeof decimals !== "number" || !Number.isInteger(decimals) || decimals < 0) {
3449
- throw new (0, _chunkVPENSVOIcjs.InvalidEnvelopeError)(
3788
+ throw new (0, _chunkMWBT7MCEcjs.InvalidEnvelopeError)(
3450
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.`
3451
3790
  );
3452
3791
  }
3453
- const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _37 => _37.symbol]), () => ( _optionalChain([accept, 'access', _38 => _38.extra, 'optionalAccess', _39 => _39.symbol])));
3454
- const amountFormatted = _chunkVPENSVOIcjs.formatUnits.call(void 0, amountBase, decimals);
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);
3455
3799
  const intent = {
3456
3800
  host: hostOf2(url),
3457
3801
  chain: this.opts.chain,
@@ -3464,9 +3808,14 @@ var PipRailClient = (_class2 = class {
3464
3808
  };
3465
3809
  const policy = this.opts.policy;
3466
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;
3467
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;
3468
3817
  const now = Date.now();
3469
- const ctx = hasTimePolicy ? {
3818
+ const ctx = needsCtx ? {
3470
3819
  now,
3471
3820
  sessionStart: this.ledger.sessionStart,
3472
3821
  // Window slice ONLY when BOTH fields are set — never a `?? 0` width.
@@ -3474,7 +3823,12 @@ var PipRailClient = (_class2 = class {
3474
3823
  accept.network,
3475
3824
  accept.asset,
3476
3825
  now - policy.windowSeconds * 1e3
3477
- ) : 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
3478
3832
  } : void 0;
3479
3833
  const decision = evaluatePolicy(
3480
3834
  intent,
@@ -3482,7 +3836,7 @@ var PipRailClient = (_class2 = class {
3482
3836
  this.ledger.totalFor(accept.network, accept.asset),
3483
3837
  ctx
3484
3838
  );
3485
- const serverSymbol = _optionalChain([accept, 'access', _40 => _40.extra, 'optionalAccess', _41 => _41.symbol]);
3839
+ const serverSymbol = _optionalChain([accept, 'access', _51 => _51.extra, 'optionalAccess', _52 => _52.symbol]);
3486
3840
  const symbolMismatch = intent.recognized && !!serverSymbol && !!symbol && serverSymbol.toUpperCase() !== symbol.toUpperCase();
3487
3841
  return {
3488
3842
  url,
@@ -3510,9 +3864,11 @@ var PipRailClient = (_class2 = class {
3510
3864
  async authorize(quote) {
3511
3865
  if (!quote.withinPolicy) {
3512
3866
  const reason = `Payment refused by policy: ${_nullishCoalesce(quote.policyReason, () => ( "not allowed"))}`;
3513
- const reasonCode = reasonCodeForPolicy(quote.policyCode);
3514
- this.safeEmit({ kind: "payment-failed", reason, code: reasonCode });
3515
- throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { reasonCode });
3867
+ this.refuse(reason, {
3868
+ reasonCode: reasonCodeForPolicy(quote.policyCode),
3869
+ policyCode: quote.policyCode,
3870
+ quote
3871
+ });
3516
3872
  }
3517
3873
  const hook = this.opts.onBeforePay;
3518
3874
  if (!hook) return;
@@ -3520,43 +3876,136 @@ var PipRailClient = (_class2 = class {
3520
3876
  try {
3521
3877
  approved = await hook(quote);
3522
3878
  } catch (err) {
3523
- const reason = "onBeforePay threw \u2014 refusing to pay.";
3524
- this.safeEmit({ kind: "payment-failed", reason, code: "APPROVAL" });
3525
- throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { cause: err, reasonCode: "APPROVAL" });
3879
+ this.refuse("onBeforePay threw \u2014 refusing to pay.", { reasonCode: "APPROVAL", quote, cause: err });
3526
3880
  }
3527
3881
  if (!approved) {
3528
3882
  const reason = `onBeforePay declined ${quote.amountFormatted} ${_nullishCoalesce(quote.symbol, () => ( ""))}`.trimEnd() + ` on ${quote.network}.`;
3529
- this.safeEmit({ kind: "payment-failed", reason, code: "APPROVAL" });
3530
- throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { reasonCode: "APPROVAL" });
3883
+ this.refuse(reason, { reasonCode: "APPROVAL", quote });
3531
3884
  }
3532
3885
  }
3533
- /** Record a settled payment in the ledger (true decimals for the running total). */
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. */
3534
3913
  recordSpend(quote, ref) {
3535
- this.ledger.record(
3536
- {
3537
- url: quote.url,
3538
- host: hostOf2(quote.url),
3539
- network: quote.network,
3540
- asset: quote.asset,
3541
- amountBase: quote.amount,
3542
- amountFormatted: quote.amountFormatted,
3543
- ...quote.symbol ? { symbol: quote.symbol } : {},
3544
- ref,
3545
- at: (/* @__PURE__ */ new Date()).toISOString()
3546
- },
3547
- quote.decimals
3548
- );
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
+ }
3549
3998
  }
3550
3999
  async payAndConfirm(net, wallet, accept) {
3551
4000
  if (!this.supportsNetwork(net, accept.network)) {
3552
- throw new (0, _chunkVPENSVOIcjs.WrongChainError)(
4001
+ throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
3553
4002
  `Challenge expects ${accept.network} but client is on ${net.network}.`
3554
4003
  );
3555
4004
  }
3556
4005
  const ref = await net.send(wallet, accept);
3557
4006
  this.safeEmit({ kind: "payment-broadcast", ref });
3558
4007
  try {
3559
- const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access', _42 => _42.extra, 'optionalAccess', _43 => _43.minConfirmations]), () => ( 1)));
4008
+ const { height } = await net.confirm(ref, _nullishCoalesce(_optionalChain([accept, 'access', _57 => _57.extra, 'optionalAccess', _58 => _58.minConfirmations]), () => ( 1)));
3560
4009
  this.safeEmit({
3561
4010
  kind: "payment-confirmed",
3562
4011
  ref,
@@ -3576,9 +4025,9 @@ var PipRailClient = (_class2 = class {
3576
4025
  const signature = {
3577
4026
  x402Version: 2,
3578
4027
  accepted: accept,
3579
- payload: { nonce: _optionalChain([accept, 'access', _44 => _44.extra, 'optionalAccess', _45 => _45.nonce]), txHash: ref }
4028
+ payload: { nonce: _optionalChain([accept, 'access', _59 => _59.extra, 'optionalAccess', _60 => _60.nonce]), txHash: ref }
3580
4029
  };
3581
- const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _46 => _46.headers]));
4030
+ const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _61 => _61.headers]));
3582
4031
  headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
3583
4032
  let lastResponse = null;
3584
4033
  let lastReason = null;
@@ -3593,7 +4042,7 @@ var PipRailClient = (_class2 = class {
3593
4042
  () => timeoutController.abort(),
3594
4043
  this.retryTimeoutMs
3595
4044
  );
3596
- const signal = _optionalChain([originalInit, 'optionalAccess', _47 => _47.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
4045
+ const signal = _optionalChain([originalInit, 'optionalAccess', _62 => _62.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
3597
4046
  try {
3598
4047
  lastResponse = await fetch(url, {
3599
4048
  ..._nullishCoalesce(originalInit, () => ( {})),
@@ -3602,7 +4051,7 @@ var PipRailClient = (_class2 = class {
3602
4051
  });
3603
4052
  } catch (err) {
3604
4053
  if (timeoutController.signal.aborted) {
3605
- throw new (0, _chunkVPENSVOIcjs.PaymentTimeoutError)(
4054
+ throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
3606
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.`,
3607
4056
  { cause: err, ref }
3608
4057
  );
@@ -3625,7 +4074,7 @@ var PipRailClient = (_class2 = class {
3625
4074
  reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`,
3626
4075
  ...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
3627
4076
  });
3628
- throw new (0, _chunkVPENSVOIcjs.MaxRetriesExceededError)(
4077
+ throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
3629
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).`,
3630
4079
  { ref }
3631
4080
  );
@@ -3649,17 +4098,17 @@ var PipRailClient = (_class2 = class {
3649
4098
  */
3650
4099
  async payExactRail(net, wallet, accept, url, init, quote) {
3651
4100
  if (!net.payExact) {
3652
- throw new (0, _chunkVPENSVOIcjs.UnsupportedSchemeError)(
4101
+ throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
3653
4102
  `the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana, Algorand + NEAR today).`
3654
4103
  );
3655
4104
  }
3656
- throwIfAborted(_optionalChain([init, 'optionalAccess', _48 => _48.signal]));
4105
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _63 => _63.signal]));
3657
4106
  const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
3658
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _49 => _49.headers]));
4107
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _64 => _64.headers]));
3659
4108
  headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
3660
4109
  const rejectDefinitive = (why2) => {
3661
4110
  this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})`, code: why2 });
3662
- throw new (0, _chunkVPENSVOIcjs.MaxRetriesExceededError)(
4111
+ throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
3663
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}.`,
3664
4113
  { ref: nonce }
3665
4114
  );
@@ -3672,17 +4121,17 @@ var PipRailClient = (_class2 = class {
3672
4121
  if (Date.now() >= deadline) break;
3673
4122
  await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
3674
4123
  }
3675
- throwIfAborted(_optionalChain([init, 'optionalAccess', _50 => _50.signal]));
4124
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _65 => _65.signal]));
3676
4125
  const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
3677
4126
  if (budget <= 0) break;
3678
4127
  const timeoutController = new AbortController();
3679
4128
  const timeoutId = setTimeout(() => timeoutController.abort(), budget);
3680
- const signal = _optionalChain([init, 'optionalAccess', _51 => _51.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
4129
+ const signal = _optionalChain([init, 'optionalAccess', _66 => _66.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
3681
4130
  let response;
3682
4131
  try {
3683
4132
  response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
3684
4133
  } catch (err) {
3685
- throw new (0, _chunkVPENSVOIcjs.PaymentTimeoutError)(
4134
+ throw new (0, _chunkMWBT7MCEcjs.PaymentTimeoutError)(
3686
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.`,
3687
4136
  { cause: err, ref: nonce }
3688
4137
  );
@@ -3698,7 +4147,7 @@ var PipRailClient = (_class2 = class {
3698
4147
  if (response.ok && !(settle && settle.success === false)) {
3699
4148
  const receipt = parseReceipt(response);
3700
4149
  this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
3701
- const ref = _optionalChain([settle, 'optionalAccess', _52 => _52.transaction]) || _optionalChain([receipt, 'optionalAccess', _53 => _53.transaction]) || `eip3009-nonce:${nonce}`;
4150
+ const ref = _optionalChain([settle, 'optionalAccess', _67 => _67.transaction]) || _optionalChain([receipt, 'optionalAccess', _68 => _68.transaction]) || `eip3009-nonce:${nonce}`;
3702
4151
  this.recordSpend(quote, ref);
3703
4152
  return response;
3704
4153
  }
@@ -3716,21 +4165,21 @@ var PipRailClient = (_class2 = class {
3716
4165
  reason: `exact: 402 after submitting authorization nonce=${nonce} (${why})`,
3717
4166
  ...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
3718
4167
  });
3719
- throw new (0, _chunkVPENSVOIcjs.MaxRetriesExceededError)(
4168
+ throw new (0, _chunkMWBT7MCEcjs.MaxRetriesExceededError)(
3720
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}.`,
3721
4170
  { ref: nonce }
3722
4171
  );
3723
4172
  }
3724
- }, _class2);
4173
+ };
3725
4174
  function throwIfAborted(signal) {
3726
- if (_optionalChain([signal, 'optionalAccess', _54 => _54.aborted])) {
4175
+ if (_optionalChain([signal, 'optionalAccess', _69 => _69.aborted])) {
3727
4176
  throw _nullishCoalesce(signal.reason, () => ( new DOMException("This operation was aborted.", "AbortError")));
3728
4177
  }
3729
4178
  }
3730
4179
  function safeBig(s) {
3731
4180
  try {
3732
4181
  return BigInt(s);
3733
- } catch (e28) {
4182
+ } catch (e31) {
3734
4183
  return 0n;
3735
4184
  }
3736
4185
  }
@@ -3793,10 +4242,10 @@ function buildFundingHint(options, chainLabel) {
3793
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}.`;
3794
4243
  }
3795
4244
  const parts = [];
3796
- if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _55 => _55.shortfall, 'optionalAccess', _56 => _56.token])) {
4245
+ if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _70 => _70.shortfall, 'optionalAccess', _71 => _71.token])) {
3797
4246
  parts.push(`top up ${target.shortfall.token} ${sym}`);
3798
4247
  }
3799
- if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _57 => _57.shortfall, 'optionalAccess', _58 => _58.native])) {
4248
+ if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _72 => _72.shortfall, 'optionalAccess', _73 => _73.native])) {
3800
4249
  parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
3801
4250
  }
3802
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}.`;
@@ -3810,7 +4259,7 @@ async function planAcross(clients, url, init) {
3810
4259
  const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
3811
4260
  return {
3812
4261
  url,
3813
- network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _59 => _59.accept, 'access', _60 => _60.network]), () => ( live[0].network)),
4262
+ network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _74 => _74.accept, 'access', _75 => _75.network]), () => ( live[0].network)),
3814
4263
  status,
3815
4264
  payable: best !== null,
3816
4265
  best,
@@ -3829,7 +4278,7 @@ async function fetchAcross(clients, url, init) {
3829
4278
  const best = rankAcross(live.map((p) => p.plan)).find((o) => o.state === "payable");
3830
4279
  if (!best) {
3831
4280
  const hint = mergeDeclineHint(live.map((p) => p.plan));
3832
- throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(hint || "No funded chain can settle this payment right now.");
4281
+ throw new (0, _chunkMWBT7MCEcjs.PaymentDeclinedError)(hint || "No funded chain can settle this payment right now.");
3833
4282
  }
3834
4283
  const owner = live.find((p) => p.plan.options.includes(best)).client;
3835
4284
  return owner.fetch(url, { ..._nullishCoalesce(init, () => ( {})), autoRoute: true });
@@ -3843,8 +4292,11 @@ function reasonCodeForPolicy(code) {
3843
4292
  case "SESSION_EXPIRED":
3844
4293
  return "SESSION_EXPIRED";
3845
4294
  case "WINDOW_TOTAL":
4295
+ case "WINDOW_COUNT":
3846
4296
  return "OUTSIDE_WINDOW";
3847
4297
  case "MAX_TOTAL":
4298
+ case "MAX_TOTAL_DENOM":
4299
+ case "MAX_PAYMENTS":
3848
4300
  return "BUDGET";
3849
4301
  case void 0:
3850
4302
  return void 0;
@@ -3855,7 +4307,7 @@ function reasonCodeForPolicy(code) {
3855
4307
  function hostOf2(url) {
3856
4308
  try {
3857
4309
  return new URL(url).hostname;
3858
- } catch (e29) {
4310
+ } catch (e32) {
3859
4311
  return url;
3860
4312
  }
3861
4313
  }
@@ -3872,8 +4324,8 @@ function isReplayableBodyInit(value) {
3872
4324
  async function readInvalidReason(response) {
3873
4325
  try {
3874
4326
  const body = await response.clone().json();
3875
- const ext = _optionalChain([body, 'optionalAccess', _61 => _61.extensions]);
3876
- const piprail = _optionalChain([ext, 'optionalAccess', _62 => _62.piprail]);
4327
+ const ext = _optionalChain([body, 'optionalAccess', _76 => _76.extensions]);
4328
+ const piprail = _optionalChain([ext, 'optionalAccess', _77 => _77.piprail]);
3877
4329
  if (piprail && typeof piprail.code === "string") {
3878
4330
  return {
3879
4331
  error: piprail.code,
@@ -3892,16 +4344,20 @@ async function readInvalidReason(response) {
3892
4344
  detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
3893
4345
  };
3894
4346
  }
3895
- } catch (e30) {
4347
+ } catch (e33) {
3896
4348
  }
3897
4349
  const settle = parseSettleResponse(response);
3898
- if (_optionalChain([settle, 'optionalAccess', _63 => _63.errorReason])) return { error: settle.errorReason, detail: "" };
4350
+ if (_optionalChain([settle, 'optionalAccess', _78 => _78.errorReason])) return { error: settle.errorReason, detail: "" };
3899
4351
  return null;
3900
4352
  }
3901
4353
 
3902
4354
  // src/payer.ts
3903
4355
  var MultiChainPayer = class _MultiChainPayer {
3904
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
+
3905
4361
  /**
3906
4362
  * Wrap an explicit, ordered set of single-chain clients — use this when a client
3907
4363
  * needs full control (e.g. a custom EVM chain configured by a viem `Chain`). The
@@ -3909,12 +4365,18 @@ var MultiChainPayer = class _MultiChainPayer {
3909
4365
  * at MOST one client per chain — two clients on the SAME network would double-count in
3910
4366
  * `spent()`/`budget()` and waste a plan round-trip (`fromWallets` can't produce this).
3911
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.
3912
4373
  */
3913
- constructor(clients) {
4374
+ constructor(clients, opts = {}) {
3914
4375
  if (clients.length === 0) {
3915
4376
  throw new TypeError("MultiChainPayer needs at least one PipRailClient.");
3916
4377
  }
3917
4378
  this._clients = [...clients];
4379
+ this._shared = _nullishCoalesce(opts.shared, () => ( false));
3918
4380
  }
3919
4381
  /**
3920
4382
  * Build one client per funded chain from a `{ chain → wallet }` map — the
@@ -3929,30 +4391,36 @@ var MultiChainPayer = class _MultiChainPayer {
3929
4391
  * solana: { key: process.env.SOLANA_SECRET! },
3930
4392
  * xrpl: { key: process.env.XRPL_SEED! },
3931
4393
  * },
3932
- * policy: { maxAmount: '1.00', maxTotal: '20.00', tokens: ['USDC', 'USDT'] },
4394
+ * // ONE budget across all three chains: $20 total + at most 100 payments.
4395
+ * policy: { maxAmount: '1.00', maxTotalPerDenom: { USD: '20.00' }, maxPayments: 100 },
3933
4396
  * })
3934
4397
  * const res = await payer.get('https://api.example.com/paid') // pays on the first funded chain that can settle
3935
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.
3936
4402
  */
3937
4403
  static fromWallets(opts) {
3938
4404
  const entries = Object.entries(opts.wallets);
3939
4405
  if (entries.length === 0) {
3940
4406
  throw new TypeError("MultiChainPayer.fromWallets needs at least one wallet.");
3941
4407
  }
4408
+ const ledger = new SpendLedger(opts.spendStore);
3942
4409
  const clients = entries.map(
3943
4410
  ([chain, wallet]) => new PipRailClient({
3944
4411
  chain,
3945
4412
  wallet,
4413
+ ledger,
3946
4414
  ...opts.policy ? { policy: opts.policy } : {},
3947
4415
  ...opts.schemes ? { schemes: opts.schemes } : {},
3948
- ..._optionalChain([opts, 'access', _64 => _64.rpcUrls, 'optionalAccess', _65 => _65[chain]]) ? { rpcUrl: opts.rpcUrls[chain] } : {},
4416
+ ..._optionalChain([opts, 'access', _79 => _79.rpcUrls, 'optionalAccess', _80 => _80[chain]]) ? { rpcUrl: opts.rpcUrls[chain] } : {},
3949
4417
  ...opts.onBeforePay ? { onBeforePay: opts.onBeforePay } : {},
3950
4418
  ...opts.onEvent ? { onEvent: opts.onEvent } : {},
3951
4419
  ...opts.maxPaymentRetries != null ? { maxPaymentRetries: opts.maxPaymentRetries } : {},
3952
4420
  ...opts.retryTimeoutMs != null ? { retryTimeoutMs: opts.retryTimeoutMs } : {}
3953
4421
  })
3954
4422
  );
3955
- return new _MultiChainPayer(clients);
4423
+ return new _MultiChainPayer(clients, { shared: true });
3956
4424
  }
3957
4425
  /** The underlying single-chain clients, in preference order. Reach for one of
3958
4426
  * these for chain-specific reads (`estimateCost`, `discoverySigner`, per-chain
@@ -4003,7 +4471,7 @@ var MultiChainPayer = class _MultiChainPayer {
4003
4471
  * {@link PipRailClient.post}.
4004
4472
  */
4005
4473
  post(url, body, init) {
4006
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _66 => _66.headers]));
4474
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _81 => _81.headers]));
4007
4475
  let payload;
4008
4476
  if (body === void 0 || body === null) {
4009
4477
  payload = void 0;
@@ -4046,28 +4514,50 @@ var MultiChainPayer = class _MultiChainPayer {
4046
4514
  register(url, opts = {}) {
4047
4515
  return this._clients[0].register(url, opts);
4048
4516
  }
4049
- /** Aggregate spend across every chain — counts summed; per-(network,asset) rows
4050
- * and records concatenated (no cross-chain collisions, never a cross-token sum). */
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
+ */
4051
4524
  spent() {
4525
+ if (this._shared) return this._clients[0].spent();
4052
4526
  const summaries = this._clients.map((c) => c.spent());
4053
4527
  return {
4054
4528
  count: summaries.reduce((n, s) => n + s.count, 0),
4055
4529
  byAsset: summaries.flatMap((s) => s.byAsset),
4530
+ byDenom: summaries.flatMap((s) => s.byDenom),
4056
4531
  records: summaries.flatMap((s) => s.records)
4057
4532
  };
4058
4533
  }
4059
- /** A merged budget view: every chain's per-(network,asset) remaining rows, plus the
4060
- * MOST-RESTRICTIVE session time envelope across chains (the soonest deadline wins).
4061
- * Mirrors {@link PipRailClient.budget}'s shape so the agent toolkit reads it
4062
- * unchanged; per-chain session detail is on each `clients[i].budget()`. */
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
+ */
4063
4542
  budget() {
4543
+ if (this._shared) return this._clients[0].budget();
4064
4544
  const budgets = this._clients.map((c) => c.budget());
4065
4545
  const session = budgets.map((b) => b.session).reduce((soonest, s) => {
4066
4546
  if (soonest.secondsRemaining == null) return s;
4067
4547
  if (s.secondsRemaining == null) return soonest;
4068
4548
  return s.secondsRemaining < soonest.secondsRemaining ? s : soonest;
4069
4549
  });
4070
- return { session, byAsset: budgets.flatMap((b) => b.byAsset) };
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();
4071
4561
  }
4072
4562
  };
4073
4563
  function isBodyInit(value) {
@@ -4124,14 +4614,14 @@ function buildSelfDescription(input) {
4124
4614
  }
4125
4615
  function buildEndpointInfo(input) {
4126
4616
  const d = input.descriptor;
4127
- const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess', _67 => _67.summary]), () => ( input.description));
4128
- const hasInput = _optionalChain([d, 'optionalAccess', _68 => _68.queryParams]) && Object.keys(d.queryParams).length > 0;
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;
4129
4619
  const endpoint = {
4130
4620
  ...summary ? { summary } : {},
4131
- ..._optionalChain([d, 'optionalAccess', _69 => _69.method]) ? { method: d.method.toUpperCase() } : {},
4621
+ ..._optionalChain([d, 'optionalAccess', _84 => _84.method]) ? { method: d.method.toUpperCase() } : {},
4132
4622
  ...input.mimeType ? { mimeType: input.mimeType } : {},
4133
4623
  ...hasInput ? { input: d.queryParams } : {},
4134
- ..._optionalChain([d, 'optionalAccess', _70 => _70.output]) ? { output: d.output } : {}
4624
+ ..._optionalChain([d, 'optionalAccess', _85 => _85.output]) ? { output: d.output } : {}
4135
4625
  };
4136
4626
  return Object.keys(endpoint).length > 0 ? endpoint : void 0;
4137
4627
  }
@@ -4149,7 +4639,7 @@ function summarizePlan(plan) {
4149
4639
  return `NOT payable: ${_nullishCoalesce(plan.fundingHint, () => ( `no settleable rail on ${plan.network}`))}`;
4150
4640
  }
4151
4641
  function explainDecline(err) {
4152
- if (!(err instanceof _chunkVPENSVOIcjs.PipRailError)) {
4642
+ if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) {
4153
4643
  return `Payment failed: ${err instanceof Error ? err.message : String(err)}`;
4154
4644
  }
4155
4645
  switch (err.code) {
@@ -4172,9 +4662,12 @@ function explainDecline(err) {
4172
4662
  }
4173
4663
  function formatSpendReport(summary) {
4174
4664
  if (summary.count === 0) return "No payments yet.";
4175
- return summary.byAsset.map(
4665
+ const perAsset = summary.byAsset.map(
4176
4666
  (a) => `${a.totalFormatted} ${_nullishCoalesce(a.symbol, () => ( a.asset))} on ${a.network} (${a.count} payment${a.count === 1 ? "" : "s"})`
4177
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}`;
4178
4671
  }
4179
4672
  function describeChallenge(challenge) {
4180
4673
  const first = challenge.accepts[0];
@@ -4253,9 +4746,11 @@ Branch on \`code\` (always reliable). Key cases:
4253
4746
  once (enable the exact scheme); report it, don't retry the same call blindly.
4254
4747
 
4255
4748
  ## Knowing your leash \u2014 call piprail_budget
4256
- piprail_budget tells you how much budget and time you have left, per
4257
- (network, asset), plus your spend so far. Read-only; moves no funds. Use it in
4258
- Mode A to self-check before paying.
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.
4259
4754
 
4260
4755
  ## Two modes
4261
4756
  - Mode A (headless, default): you run FREE inside a pre-set budget + time
@@ -4266,10 +4761,15 @@ Mode A to self-check before paying.
4266
4761
  do NOT retry it as if it were a transient error.
4267
4762
 
4268
4763
  ## Hard facts
4269
- - Spend caps are PER (network, asset). There is no single cross-token dollar cap \u2014
4270
- budgets aren't summed across tokens (no price oracle).
4271
- - Spend totals and the time envelope live IN-MEMORY for THIS process; they reset on restart
4272
- (a convenience, not a durable ledger).
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.
4273
4773
  `;
4274
4774
  function agentGuide() {
4275
4775
  return PIPRAIL_AGENT_GUIDE;
@@ -4282,19 +4782,19 @@ async function readBody(res) {
4282
4782
  if (!text) return null;
4283
4783
  try {
4284
4784
  return JSON.parse(text);
4285
- } catch (e31) {
4785
+ } catch (e34) {
4286
4786
  return text;
4287
4787
  }
4288
4788
  }
4289
4789
  function toToolError(err) {
4290
- if (!(err instanceof _chunkVPENSVOIcjs.PipRailError)) throw err;
4790
+ if (!(err instanceof _chunkMWBT7MCEcjs.PipRailError)) throw err;
4291
4791
  const out = {
4292
4792
  ok: false,
4293
4793
  code: err.code,
4294
4794
  reason: err.message,
4295
4795
  explain: explainDecline(err)
4296
4796
  };
4297
- if (err instanceof _chunkVPENSVOIcjs.PaymentDeclinedError) {
4797
+ if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
4298
4798
  out.declined = true;
4299
4799
  if (err.reasonCode) out.reasonCode = err.reasonCode;
4300
4800
  }
@@ -4508,14 +5008,14 @@ function paymentTools(client) {
4508
5008
  receipt: parseReceipt(res)
4509
5009
  };
4510
5010
  } catch (err) {
4511
- if (err instanceof _chunkVPENSVOIcjs.PipRailError) {
5011
+ if (err instanceof _chunkMWBT7MCEcjs.PipRailError) {
4512
5012
  const out = {
4513
5013
  ok: false,
4514
5014
  code: err.code,
4515
5015
  reason: err.message,
4516
5016
  explain: explainDecline(err)
4517
5017
  };
4518
- if (err instanceof _chunkVPENSVOIcjs.PaymentDeclinedError) {
5018
+ if (err instanceof _chunkMWBT7MCEcjs.PaymentDeclinedError) {
4519
5019
  out.declined = true;
4520
5020
  if (err.reasonCode) out.reasonCode = err.reasonCode;
4521
5021
  }
@@ -4584,11 +5084,11 @@ function paymentTools(client) {
4584
5084
  },
4585
5085
  {
4586
5086
  name: "piprail_budget",
4587
- description: "Read how much of your spend budget and time leash is left \u2014 per (network, asset) remaining, the session time envelope, and your spend so far. Use it in Mode A (headless) to self-check BEFORE paying, so you never discover the leash by hitting a decline. Read-only; moves no funds. NOTE: totals and the time envelope are in-memory for THIS process and reset on restart.",
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.",
4588
5088
  annotations: {
4589
5089
  title: "Check remaining budget",
4590
5090
  readOnlyHint: true,
4591
- // reads the in-memory ledger + policy; never pays
5091
+ // reads the ledger + policy; never pays
4592
5092
  idempotentHint: true
4593
5093
  // a pure read
4594
5094
  },
@@ -4601,7 +5101,13 @@ function paymentTools(client) {
4601
5101
  return {
4602
5102
  spent,
4603
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)
4604
5108
  session: budget.session,
5109
+ policy: _nullishCoalesce(client.policy(), () => ( null)),
5110
+ // the configured leash, read back
4605
5111
  report: formatSpendReport(spent)
4606
5112
  };
4607
5113
  } catch (err) {
@@ -4675,7 +5181,7 @@ function buildBazaarExtension(descriptor = {}) {
4675
5181
  function pathOf(url) {
4676
5182
  try {
4677
5183
  return new URL(url).pathname || "/";
4678
- } catch (e32) {
5184
+ } catch (e35) {
4679
5185
  return url.startsWith("/") ? url : `/${url}`;
4680
5186
  }
4681
5187
  }
@@ -4784,28 +5290,28 @@ async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
4784
5290
  const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
4785
5291
  if (!res.ok) return void 0;
4786
5292
  const body = await res.json();
4787
- const kinds = Array.isArray(_optionalChain([body, 'optionalAccess', _71 => _71.kinds])) ? body.kinds : [];
5293
+ const kinds = Array.isArray(_optionalChain([body, 'optionalAccess', _86 => _86.kinds])) ? body.kinds : [];
4788
5294
  const want = normalizeNetwork(network);
4789
- const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _72 => _72.scheme]) === "exact" && normalizeNetwork(String(_nullishCoalesce(_optionalChain([k, 'optionalAccess', _73 => _73.network]), () => ( "")))) === want);
4790
- const fp = _optionalChain([kind, 'optionalAccess', _74 => _74.extra, 'optionalAccess', _75 => _75.feePayer]);
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]);
4791
5297
  return typeof fp === "string" ? fp : void 0;
4792
- } catch (e33) {
5298
+ } catch (e36) {
4793
5299
  return void 0;
4794
5300
  } finally {
4795
5301
  clearTimeout(timer);
4796
5302
  }
4797
5303
  }
4798
5304
  function parseFacilitatorSupported(body) {
4799
- const kinds = _optionalChain([body, 'optionalAccess', _76 => _76.kinds]);
5305
+ const kinds = _optionalChain([body, 'optionalAccess', _91 => _91.kinds]);
4800
5306
  if (!Array.isArray(kinds)) return [];
4801
5307
  const out = [];
4802
5308
  for (const k of kinds) {
4803
5309
  if (!k || typeof k !== "object") continue;
4804
5310
  const o = k;
4805
5311
  if (typeof o.scheme !== "string" || typeof o.network !== "string") continue;
4806
- const fp = _optionalChain([o, 'access', _77 => _77.extra, 'optionalAccess', _78 => _78.feePayer]);
5312
+ const fp = _optionalChain([o, 'access', _92 => _92.extra, 'optionalAccess', _93 => _93.feePayer]);
4807
5313
  const ver = o.x402Version;
4808
- const method = _optionalChain([o, 'access', _79 => _79.extra, 'optionalAccess', _80 => _80.assetTransferMethod]);
5314
+ const method = _optionalChain([o, 'access', _94 => _94.extra, 'optionalAccess', _95 => _95.assetTransferMethod]);
4809
5315
  out.push({
4810
5316
  scheme: o.scheme,
4811
5317
  network: o.network,
@@ -4824,7 +5330,7 @@ async function facilitatorCoverage(url, timeoutMs = 8e3) {
4824
5330
  const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
4825
5331
  if (!res.ok) return [];
4826
5332
  return parseFacilitatorSupported(await res.json());
4827
- } catch (e34) {
5333
+ } catch (e37) {
4828
5334
  return [];
4829
5335
  } finally {
4830
5336
  clearTimeout(timer);
@@ -4853,7 +5359,7 @@ async function post(url, body, headers) {
4853
5359
  let json = null;
4854
5360
  try {
4855
5361
  json = await res.json();
4856
- } catch (e35) {
5362
+ } catch (e38) {
4857
5363
  }
4858
5364
  return { status: res.status, json };
4859
5365
  }
@@ -4869,13 +5375,13 @@ async function settleViaFacilitator(input) {
4869
5375
  try {
4870
5376
  verify = await post(`${base2}/verify`, body, auth);
4871
5377
  } catch (err) {
4872
- throw new (0, _chunkVPENSVOIcjs.SettlementError)(
5378
+ throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
4873
5379
  `exact settle (facilitator ${base2}): /verify request failed (${err instanceof Error ? err.message : String(err)}).`,
4874
5380
  { cause: err }
4875
5381
  );
4876
5382
  }
4877
5383
  if (verify.status !== 200) {
4878
- throw new (0, _chunkVPENSVOIcjs.SettlementError)(
5384
+ throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
4879
5385
  `exact settle (facilitator ${base2}): /verify returned HTTP ${verify.status} (transport/auth error).`
4880
5386
  );
4881
5387
  }
@@ -4891,13 +5397,13 @@ async function settleViaFacilitator(input) {
4891
5397
  try {
4892
5398
  settle = await post(`${base2}/settle`, body, auth);
4893
5399
  } catch (err) {
4894
- throw new (0, _chunkVPENSVOIcjs.SettlementError)(
5400
+ throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
4895
5401
  `exact settle (facilitator ${base2}): /settle request failed (${err instanceof Error ? err.message : String(err)}).`,
4896
5402
  { cause: err }
4897
5403
  );
4898
5404
  }
4899
5405
  if (settle.status !== 200) {
4900
- throw new (0, _chunkVPENSVOIcjs.SettlementError)(
5406
+ throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
4901
5407
  `exact settle (facilitator ${base2}): /settle returned HTTP ${settle.status} (transport/auth error).`
4902
5408
  );
4903
5409
  }
@@ -5290,7 +5796,7 @@ function createPaymentGate(options) {
5290
5796
  }
5291
5797
  net.assertValidPayTo(payTo);
5292
5798
  const { asset, decimals, symbol } = net.resolveToken(a.token);
5293
- const amountBase = _chunkVPENSVOIcjs.parseUnits.call(void 0, a.amount, decimals);
5799
+ const amountBase = _chunkMWBT7MCEcjs.parseUnits.call(void 0, a.amount, decimals);
5294
5800
  const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
5295
5801
  if (exactOption) {
5296
5802
  const outcome = await resolveExactRail(net, asset);
@@ -5303,7 +5809,7 @@ function createPaymentGate(options) {
5303
5809
  if (exactOption && !specs.some((s) => s.exact)) {
5304
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.";
5305
5811
  if (exactOption.settle === "keyless") {
5306
- if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess', _81 => _81.env, 'optionalAccess', _82 => _82.PIPRAIL_NO_HINTS])) {
5812
+ if (typeof process === "undefined" || !_optionalChain([process, 'optionalAccess', _96 => _96.env, 'optionalAccess', _97 => _97.PIPRAIL_NO_HINTS])) {
5307
5813
  console.warn(
5308
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.)`
5309
5815
  );
@@ -5331,7 +5837,7 @@ function createPaymentGate(options) {
5331
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).`
5332
5838
  };
5333
5839
  }
5334
- if (typeof process === "undefined" || _optionalChain([process, 'optionalAccess', _83 => _83.env, 'optionalAccess', _84 => _84.NODE_ENV]) !== "production" && !_optionalChain([process, 'optionalAccess', _85 => _85.env, 'optionalAccess', _86 => _86.PIPRAIL_NO_HINTS])) {
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])) {
5335
5841
  console.warn(
5336
5842
  `[piprail] exact: keyless rail on ${net.network} auto-settles via ${picked.url} (zero-config; pin \`exact.settle.facilitator\` in production).`
5337
5843
  );
@@ -5465,7 +5971,7 @@ function createPaymentGate(options) {
5465
5971
  instruction: describeChallenge({ x402Version: 2, resource: { url: resourceUrl }, accepts }),
5466
5972
  ...endpointInfo ? { endpoint: endpointInfo } : {}
5467
5973
  });
5468
- const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _87 => _87.extensions]), () => ( {}));
5974
+ const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _102 => _102.extensions]), () => ( {}));
5469
5975
  const rejectionPiprail = _nullishCoalesce(rejectionExt.piprail, () => ( {}));
5470
5976
  const bodyPiprail = { ..._nullishCoalesce(selfDescribe, () => ( {})), ...rejectionPiprail };
5471
5977
  const bodyExtensions = {
@@ -5485,7 +5991,7 @@ function createPaymentGate(options) {
5485
5991
  ...options.mimeType ? { mimeType: options.mimeType } : {}
5486
5992
  },
5487
5993
  accepts,
5488
- ..._optionalChain([opts, 'optionalAccess', _88 => _88.error]) ? { error: opts.error } : {},
5994
+ ..._optionalChain([opts, 'optionalAccess', _103 => _103.error]) ? { error: opts.error } : {},
5489
5995
  ...Object.keys(bodyExtensions).length > 0 ? { extensions: bodyExtensions } : {}
5490
5996
  };
5491
5997
  const headerChallenge = {
@@ -5516,8 +6022,8 @@ function createPaymentGate(options) {
5516
6022
  function enrichReceipt(spec, receipt) {
5517
6023
  let amountFormatted = receipt.amount;
5518
6024
  try {
5519
- amountFormatted = _chunkVPENSVOIcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
5520
- } catch (e36) {
6025
+ amountFormatted = _chunkMWBT7MCEcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
6026
+ } catch (e39) {
5521
6027
  }
5522
6028
  return {
5523
6029
  ...receipt,
@@ -5531,7 +6037,7 @@ function createPaymentGate(options) {
5531
6037
  if (!options.onPaidError) return;
5532
6038
  try {
5533
6039
  options.onPaidError(error, receipt);
5534
- } catch (e37) {
6040
+ } catch (e40) {
5535
6041
  }
5536
6042
  }
5537
6043
  function fireOnPaid(receipt) {
@@ -5556,7 +6062,7 @@ function createPaymentGate(options) {
5556
6062
  if (!options.onFailedError) return;
5557
6063
  try {
5558
6064
  options.onFailedError(error, failure);
5559
- } catch (e38) {
6065
+ } catch (e41) {
5560
6066
  }
5561
6067
  }
5562
6068
  function fireOnFailed(failure) {
@@ -5656,28 +6162,28 @@ function createPaymentGate(options) {
5656
6162
  nonce = [exact.payload.transaction, exact.payload.senderAuth].map((t) => {
5657
6163
  try {
5658
6164
  return Buffer.from(t, "base64").toString("base64");
5659
- } catch (e39) {
6165
+ } catch (e42) {
5660
6166
  return t;
5661
6167
  }
5662
6168
  }).join("|");
5663
6169
  } else if ("transaction" in exact.payload) {
5664
6170
  try {
5665
6171
  nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
5666
- } catch (e40) {
6172
+ } catch (e43) {
5667
6173
  nonce = exact.payload.transaction;
5668
6174
  }
5669
6175
  } else if ("paymentGroup" in exact.payload) {
5670
6176
  nonce = exact.payload.paymentGroup.map((t) => {
5671
6177
  try {
5672
6178
  return Buffer.from(t, "base64").toString("base64");
5673
- } catch (e41) {
6179
+ } catch (e44) {
5674
6180
  return t;
5675
6181
  }
5676
6182
  }).join("|");
5677
6183
  } else if ("signedDelegateAction" in exact.payload) {
5678
6184
  try {
5679
6185
  nonce = Buffer.from(exact.payload.signedDelegateAction, "base64").toString("base64");
5680
- } catch (e42) {
6186
+ } catch (e45) {
5681
6187
  nonce = exact.payload.signedDelegateAction;
5682
6188
  }
5683
6189
  } else if ("permit2Authorization" in exact.payload) {
@@ -5700,7 +6206,7 @@ function createPaymentGate(options) {
5700
6206
  const ftMethod = accept.extra.assetTransferMethod;
5701
6207
  const needsFeePayer = ftMethod === "svm" || ftMethod === "algorand" || ftMethod === "aptos" || ftMethod === "near";
5702
6208
  if (needsFeePayer && !accept.extra.feePayer) {
5703
- throw new (0, _chunkVPENSVOIcjs.SettlementError)(
6209
+ throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
5704
6210
  `exact settle: the ${ftMethod} facilitator rail is missing extra.feePayer (the gas sponsor) \u2014 cannot settle.`
5705
6211
  );
5706
6212
  }
@@ -5765,7 +6271,7 @@ function requirePayment(options) {
5765
6271
  try {
5766
6272
  result = await gate.verify(_nullishCoalesce(req.headers[HEADER_SIGNATURE], () => ( req.headers[HEADER_SIGNATURE_V1])));
5767
6273
  } catch (err) {
5768
- if (err instanceof _chunkVPENSVOIcjs.SettlementError) {
6274
+ if (err instanceof _chunkMWBT7MCEcjs.SettlementError) {
5769
6275
  res.status(502);
5770
6276
  res.json({
5771
6277
  x402Version: 2,
@@ -5813,7 +6319,7 @@ function isRetryableStatus(status) {
5813
6319
  }
5814
6320
  var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
5815
6321
  async function signBody(secret, body) {
5816
- const subtle = _optionalChain([globalThis, 'access', _89 => _89.crypto, 'optionalAccess', _90 => _90.subtle]);
6322
+ const subtle = _optionalChain([globalThis, 'access', _104 => _104.crypto, 'optionalAccess', _105 => _105.subtle]);
5817
6323
  if (!subtle) return null;
5818
6324
  try {
5819
6325
  const enc = new TextEncoder();
@@ -5823,7 +6329,7 @@ async function signBody(secret, body) {
5823
6329
  const sig = await subtle.sign("HMAC", key, enc.encode(body));
5824
6330
  const hex = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
5825
6331
  return `sha256=${hex}`;
5826
- } catch (e43) {
6332
+ } catch (e46) {
5827
6333
  return null;
5828
6334
  }
5829
6335
  }
@@ -5883,8 +6389,8 @@ async function deliverReceipt(receipt, options) {
5883
6389
  const retryable = status === void 0 ? true : isRetryableStatus(status);
5884
6390
  const willRetry = !ok && retryable && attempt < maxAttempts;
5885
6391
  try {
5886
- _optionalChain([onAttempt, 'optionalCall', _91 => _91({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
5887
- } catch (e44) {
6392
+ _optionalChain([onAttempt, 'optionalCall', _106 => _106({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
6393
+ } catch (e47) {
5888
6394
  }
5889
6395
  if (ok) return { delivered: true, attempts: attempt, status };
5890
6396
  if (!willRetry) {
@@ -6003,4 +6509,9 @@ async function deliverReceipt(receipt, options) {
6003
6509
 
6004
6510
 
6005
6511
 
6006
- exports.BRAND = BRAND; exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkVPENSVOIcjs.ConfirmationTimeoutError; 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 = _chunkVPENSVOIcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkVPENSVOIcjs.InvalidEnvelopeError; exports.KNOWN_FACILITATORS = KNOWN_FACILITATORS; exports.MaxRetriesExceededError = _chunkVPENSVOIcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkVPENSVOIcjs.MissingDriverError; exports.MultiChainPayer = MultiChainPayer; exports.NoCompatibleAcceptError = _chunkVPENSVOIcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkVPENSVOIcjs.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 = _chunkVPENSVOIcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkVPENSVOIcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkVPENSVOIcjs.PipRailError; exports.REGISTER_ATTRIBUTION = REGISTER_ATTRIBUTION; exports.RecipientNotReadyError = _chunkVPENSVOIcjs.RecipientNotReadyError; exports.SettlementError = _chunkVPENSVOIcjs.SettlementError; exports.UnknownTokenError = _chunkVPENSVOIcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkVPENSVOIcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkVPENSVOIcjs.UnsupportedSchemeError; exports.WalletRequiredError = _chunkVPENSVOIcjs.WalletRequiredError; exports.WrongChainError = _chunkVPENSVOIcjs.WrongChainError; exports.WrongFamilyError = _chunkVPENSVOIcjs.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.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.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 = _chunkVPENSVOIcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
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;