@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.
- package/CHANGELOG.md +62 -0
- package/README.md +5 -3
- package/dist/{algorand-WJL6VRXW.js → algorand-AA3WXKW4.js} +1 -1
- package/dist/{algorand-EKD34T4Q.cjs → algorand-FCEECDG6.cjs} +32 -32
- package/dist/{aptos-CVHLVMAF.cjs → aptos-GHJPO6JJ.cjs} +31 -31
- package/dist/{aptos-WMPFEASY.js → aptos-LY67Q6QF.js} +1 -1
- package/dist/{chunk-VPENSVOI.cjs → chunk-MWBT7MCE.cjs} +7 -3
- package/dist/chunk-O4UQOZ4Z.cjs +14 -0
- package/dist/{chunk-W3HOMXOF.js → chunk-SC2ZYDHD.js} +6 -2
- package/dist/chunk-SK3CB7UA.js +14 -0
- package/dist/index.cjs +748 -237
- package/dist/index.d.cts +2384 -2577
- package/dist/index.d.ts +2384 -2577
- package/dist/index.js +589 -78
- package/dist/ledger-BtzrfO-3.d.cts +658 -0
- package/dist/ledger-BtzrfO-3.d.ts +658 -0
- package/dist/{near-NYFMX2KA.cjs → near-6KAQVVG2.cjs} +26 -26
- package/dist/{near-QV4IOZNX.js → near-FZBUICCS.js} +1 -1
- package/dist/node.cjs +38 -0
- package/dist/node.d.cts +16 -0
- package/dist/node.d.ts +16 -0
- package/dist/node.js +38 -0
- package/dist/{solana-Z34OWXMO.js → solana-ELUWO6N5.js} +1 -1
- package/dist/{solana-L342QCSV.cjs → solana-MYF4HBO4.cjs} +32 -32
- package/dist/{stellar-PWC4LETJ.js → stellar-BEMT7UYF.js} +1 -1
- package/dist/{stellar-YEXRVR3W.cjs → stellar-SUKASK4N.cjs} +20 -20
- package/dist/{sui-VEOGHBYE.js → sui-F5JQ2N6I.js} +1 -1
- package/dist/{sui-EFRSCDWN.cjs → sui-VE5LT7BL.cjs} +16 -16
- package/dist/{ton-N7KSPSAR.js → ton-7GKCTC5H.js} +1 -1
- package/dist/{ton-LZKJWYHV.cjs → ton-AOR3EURW.cjs} +16 -16
- package/dist/{tron-DQU4WTGQ.cjs → tron-EMFXDFHW.cjs} +24 -24
- package/dist/{tron-WKDUQVZR.js → tron-ZZZS3FNN.js} +1 -1
- package/dist/{xrpl-6B4IYRWI.js → xrpl-DD7TJL5L.js} +1 -1
- package/dist/{xrpl-K3U3SBF4.cjs → xrpl-Y6SQNYLC.cjs} +20 -20
- package/package.json +11 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
|
|
2
2
|
|
|
3
|
+
var _chunkO4UQOZ4Zcjs = require('./chunk-O4UQOZ4Z.cjs');
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
|
|
@@ -24,7 +25,10 @@
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
var _chunkMWBT7MCEcjs = require('./chunk-MWBT7MCE.cjs');
|
|
28
32
|
|
|
29
33
|
// src/drivers/registry.ts
|
|
30
34
|
var byFamily = /* @__PURE__ */ new Map();
|
|
@@ -53,13 +57,13 @@ function resolveNetwork(opts) {
|
|
|
53
57
|
const family = familyForChain(opts.chain);
|
|
54
58
|
const driver = byFamily.get(family);
|
|
55
59
|
if (!driver) {
|
|
56
|
-
throw new (0,
|
|
60
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
57
61
|
`No driver registered for the "${family}" family \u2014 it may not be mounted yet (use the async resolveNetwork()).`
|
|
58
62
|
);
|
|
59
63
|
}
|
|
60
64
|
const net = driver.resolve(opts);
|
|
61
65
|
if (!net) {
|
|
62
|
-
throw new (0,
|
|
66
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedNetworkError)(
|
|
63
67
|
`The ${family} driver didn't recognise this chain input.`
|
|
64
68
|
);
|
|
65
69
|
}
|
|
@@ -320,12 +324,12 @@ function resolveChain(input, rpcUrlOverride) {
|
|
|
320
324
|
|
|
321
325
|
var _accounts = require('viem/accounts');
|
|
322
326
|
function createWalletAdapter(config, resolved) {
|
|
323
|
-
|
|
327
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, config, "EVM");
|
|
324
328
|
if ("key" in config) {
|
|
325
329
|
const key = config.key;
|
|
326
330
|
if (typeof key !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(key)) {
|
|
327
331
|
const hint = typeof key === "string" && !key.startsWith("0x") ? " (got a non-0x string \u2014 a base58/seed key belongs to another family; pass { key } as the EVM chain's 0x\u2026 hex secret)." : " (expected 0x followed by 64 hex characters).";
|
|
328
|
-
throw new (0,
|
|
332
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
329
333
|
`chain is EVM; the wallet { key } is not a valid 0x\u2026 32-byte hex private key${hint}`
|
|
330
334
|
);
|
|
331
335
|
}
|
|
@@ -333,7 +337,7 @@ function createWalletAdapter(config, resolved) {
|
|
|
333
337
|
try {
|
|
334
338
|
account = _accounts.privateKeyToAccount.call(void 0, key);
|
|
335
339
|
} catch (err) {
|
|
336
|
-
throw new (0,
|
|
340
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
337
341
|
`chain is EVM; the wallet { key } is not a valid 0x\u2026 32-byte hex private key: ${err instanceof Error ? err.message : String(err)}.`
|
|
338
342
|
);
|
|
339
343
|
}
|
|
@@ -343,12 +347,12 @@ function createWalletAdapter(config, resolved) {
|
|
|
343
347
|
}
|
|
344
348
|
const wc = config.walletClient;
|
|
345
349
|
if (!wc.account) {
|
|
346
|
-
throw new (0,
|
|
350
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
347
351
|
"chain is EVM; the provided walletClient has no attached account. Use `createWalletClient({ account, chain, transport })`, or pass { key }."
|
|
348
352
|
);
|
|
349
353
|
}
|
|
350
354
|
if (wc.chain && wc.chain.id !== resolved.chainId) {
|
|
351
|
-
throw new (0,
|
|
355
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongChainError)(
|
|
352
356
|
`PipRailClient: walletClient is on chain ${wc.chain.id} but the SDK was configured with chain ${resolved.chainId}. They must match.`
|
|
353
357
|
);
|
|
354
358
|
}
|
|
@@ -678,7 +682,7 @@ async function payExactEvm(input) {
|
|
|
678
682
|
code = void 0;
|
|
679
683
|
}
|
|
680
684
|
if (code && code !== "0x") {
|
|
681
|
-
throw new (0,
|
|
685
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
682
686
|
`exact buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account (no recoverable ECDSA signature). Pay via onchain-proof.`
|
|
683
687
|
);
|
|
684
688
|
}
|
|
@@ -688,13 +692,13 @@ async function payExactEvm(input) {
|
|
|
688
692
|
domain = await readExactDomain(publicClient, accept.asset);
|
|
689
693
|
}
|
|
690
694
|
if (!domain) {
|
|
691
|
-
throw new (0,
|
|
695
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
692
696
|
`exact: couldn't derive the EIP-712 domain for ${accept.asset} on ${accept.network}. Either it isn't an EIP-3009 token (USDT needs Permit2; native/plain ERC-20 aren't exact-payable) \u2014 pay via onchain-proof \u2014 OR your RPC couldn't read it (transient): if the gate advertised eip3009, pass a reliable rpcUrl and retry.`
|
|
693
697
|
);
|
|
694
698
|
}
|
|
695
699
|
const g = globalThis.crypto;
|
|
696
700
|
if (!_optionalChain([g, 'optionalAccess', _5 => _5.getRandomValues])) {
|
|
697
|
-
throw new (0,
|
|
701
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
698
702
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the exact rail needs a CSPRNG nonce."
|
|
699
703
|
);
|
|
700
704
|
}
|
|
@@ -951,7 +955,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
951
955
|
args: writeArgs
|
|
952
956
|
});
|
|
953
957
|
} catch (err) {
|
|
954
|
-
throw new (0,
|
|
958
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
955
959
|
`exact settle: the merchant relayer failed to broadcast transferWithAuthorization (${shorten(err instanceof Error ? err.message : String(err))}). The payer's authorization is still valid and unused \u2014 fund/fix the relayer and the payer can retry.`,
|
|
956
960
|
{ cause: err }
|
|
957
961
|
);
|
|
@@ -963,7 +967,7 @@ async function verifyAndSettleExactEvm(input) {
|
|
|
963
967
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
964
968
|
}
|
|
965
969
|
} catch (err) {
|
|
966
|
-
throw new (0,
|
|
970
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
967
971
|
`exact settle: broadcast ${txHash} but couldn't confirm it (${shorten(err instanceof Error ? err.message : String(err))}).`,
|
|
968
972
|
{ cause: err }
|
|
969
973
|
);
|
|
@@ -1094,7 +1098,7 @@ function shorten2(msg) {
|
|
|
1094
1098
|
function randomPermit2Nonce() {
|
|
1095
1099
|
const g = globalThis.crypto;
|
|
1096
1100
|
if (!_optionalChain([g, 'optionalAccess', _8 => _8.getRandomValues])) {
|
|
1097
|
-
throw new (0,
|
|
1101
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1098
1102
|
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the permit2 rail needs a CSPRNG nonce."
|
|
1099
1103
|
);
|
|
1100
1104
|
}
|
|
@@ -1113,7 +1117,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1113
1117
|
args: [account.address, PERMIT2_ADDRESS]
|
|
1114
1118
|
});
|
|
1115
1119
|
} catch (err) {
|
|
1116
|
-
throw new (0,
|
|
1120
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1117
1121
|
`permit2: couldn't read the Permit2 allowance for ${token} (${shorten2(err instanceof Error ? err.message : String(err))}).`
|
|
1118
1122
|
);
|
|
1119
1123
|
}
|
|
@@ -1130,7 +1134,7 @@ async function ensurePermit2Allowance(input) {
|
|
|
1130
1134
|
await publicClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1131
1135
|
return hash;
|
|
1132
1136
|
} catch (err) {
|
|
1133
|
-
throw _nullishCoalesce(
|
|
1137
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1134
1138
|
`permit2: the one-time Permit2 approval for ${token} failed to broadcast (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1135
1139
|
{ cause: err }
|
|
1136
1140
|
)));
|
|
@@ -1145,7 +1149,7 @@ async function payPermit2Evm(input) {
|
|
|
1145
1149
|
code = void 0;
|
|
1146
1150
|
}
|
|
1147
1151
|
if (code && code !== "0x") {
|
|
1148
|
-
throw new (0,
|
|
1152
|
+
throw new (0, _chunkMWBT7MCEcjs.UnsupportedSchemeError)(
|
|
1149
1153
|
`permit2 buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
|
|
1150
1154
|
);
|
|
1151
1155
|
}
|
|
@@ -1320,7 +1324,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1320
1324
|
args: settleArgs
|
|
1321
1325
|
});
|
|
1322
1326
|
} catch (err) {
|
|
1323
|
-
throw new (0,
|
|
1327
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1324
1328
|
`permit2 settle: the merchant relayer failed to broadcast the proxy settle (${shorten2(err instanceof Error ? err.message : String(err))}). The payer's signature is still valid and its nonce unused \u2014 fund/fix the relayer and the payer can retry.`,
|
|
1325
1329
|
{ cause: err }
|
|
1326
1330
|
);
|
|
@@ -1332,7 +1336,7 @@ async function verifyAndSettlePermit2Evm(input) {
|
|
|
1332
1336
|
return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
|
|
1333
1337
|
}
|
|
1334
1338
|
} catch (err) {
|
|
1335
|
-
throw new (0,
|
|
1339
|
+
throw new (0, _chunkMWBT7MCEcjs.SettlementError)(
|
|
1336
1340
|
`permit2 settle: broadcast ${txHash} but couldn't confirm it (${shorten2(err instanceof Error ? err.message : String(err))}).`,
|
|
1337
1341
|
{ cause: err }
|
|
1338
1342
|
);
|
|
@@ -1584,15 +1588,15 @@ function makeEvmNetwork(resolved) {
|
|
|
1584
1588
|
const info = resolved.tokens[token.toUpperCase()];
|
|
1585
1589
|
if (!info) {
|
|
1586
1590
|
const known = Object.keys(resolved.tokens).join(", ") || "(none built in)";
|
|
1587
|
-
throw new (0,
|
|
1591
|
+
throw new (0, _chunkMWBT7MCEcjs.UnknownTokenError)(
|
|
1588
1592
|
`token "${token}" isn't built in for ${resolved.chain.name} (known: ${known}). Pass { address, decimals } instead, or use 'native'.`
|
|
1589
1593
|
);
|
|
1590
1594
|
}
|
|
1591
1595
|
return { asset: info.address, decimals: info.decimals, symbol: info.symbol };
|
|
1592
1596
|
}
|
|
1593
|
-
|
|
1597
|
+
_chunkMWBT7MCEcjs.rejectForeignToken.call(void 0, token, "evm", network);
|
|
1594
1598
|
if (!("address" in token)) {
|
|
1595
|
-
throw new (0,
|
|
1599
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1596
1600
|
`chain ${network} is EVM; a custom token must be { address, decimals }.`
|
|
1597
1601
|
);
|
|
1598
1602
|
}
|
|
@@ -1624,20 +1628,20 @@ function makeEvmNetwork(resolved) {
|
|
|
1624
1628
|
},
|
|
1625
1629
|
assertValidPayTo(payTo) {
|
|
1626
1630
|
if (!_viem.isAddress.call(void 0, payTo)) {
|
|
1627
|
-
throw new (0,
|
|
1631
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1628
1632
|
`chain ${network} is EVM, but payTo "${payTo}" is not a valid 0x address.`
|
|
1629
1633
|
);
|
|
1630
1634
|
}
|
|
1631
1635
|
},
|
|
1632
1636
|
bindWallet(wallet) {
|
|
1633
1637
|
if (typeof wallet !== "object" || wallet === null) {
|
|
1634
|
-
throw new (0,
|
|
1638
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1635
1639
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1636
1640
|
);
|
|
1637
1641
|
}
|
|
1638
|
-
|
|
1642
|
+
_chunkMWBT7MCEcjs.assertNoLegacyWalletKey.call(void 0, wallet, "EVM");
|
|
1639
1643
|
if (!("key" in wallet) && !("walletClient" in wallet)) {
|
|
1640
|
-
throw new (0,
|
|
1644
|
+
throw new (0, _chunkMWBT7MCEcjs.WrongFamilyError)(
|
|
1641
1645
|
`chain ${network} is EVM; wallet must be { key } (0x\u2026 hex) or { walletClient }.`
|
|
1642
1646
|
);
|
|
1643
1647
|
}
|
|
@@ -1654,12 +1658,12 @@ function makeEvmNetwork(resolved) {
|
|
|
1654
1658
|
});
|
|
1655
1659
|
} catch (err) {
|
|
1656
1660
|
if (isViemInsufficientFunds(err)) {
|
|
1657
|
-
throw new (0,
|
|
1661
|
+
throw new (0, _chunkMWBT7MCEcjs.InsufficientFundsError)(
|
|
1658
1662
|
err instanceof Error ? err.message : "Insufficient funds for payment.",
|
|
1659
1663
|
{ cause: err }
|
|
1660
1664
|
);
|
|
1661
1665
|
}
|
|
1662
|
-
throw _nullishCoalesce(
|
|
1666
|
+
throw _nullishCoalesce(_chunkMWBT7MCEcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
|
|
1663
1667
|
}
|
|
1664
1668
|
},
|
|
1665
1669
|
async confirm(ref, minConfirmations) {
|
|
@@ -1670,7 +1674,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1670
1674
|
});
|
|
1671
1675
|
return { height: receipt.blockNumber.toString() };
|
|
1672
1676
|
} catch (err) {
|
|
1673
|
-
throw new (0,
|
|
1677
|
+
throw new (0, _chunkMWBT7MCEcjs.ConfirmationTimeoutError)(
|
|
1674
1678
|
`EVM tx ${ref} did not reach ${minConfirmations} confirmation(s) in time.`,
|
|
1675
1679
|
{ cause: err }
|
|
1676
1680
|
);
|
|
@@ -1680,7 +1684,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1680
1684
|
const { decimals, symbol } = resolved.chain.nativeCurrency;
|
|
1681
1685
|
if (accept.scheme === "exact") {
|
|
1682
1686
|
const permit2 = accept.extra.assetTransferMethod === "permit2";
|
|
1683
|
-
return
|
|
1687
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1684
1688
|
symbol,
|
|
1685
1689
|
decimals,
|
|
1686
1690
|
fee: 0n,
|
|
@@ -1691,7 +1695,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1691
1695
|
const gasLimit = accept.asset === "native" ? 21000n : 65000n;
|
|
1692
1696
|
try {
|
|
1693
1697
|
const gasPrice = await publicClient.getGasPrice();
|
|
1694
|
-
return
|
|
1698
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1695
1699
|
symbol,
|
|
1696
1700
|
decimals,
|
|
1697
1701
|
fee: gasPrice * gasLimit,
|
|
@@ -1700,7 +1704,7 @@ function makeEvmNetwork(resolved) {
|
|
|
1700
1704
|
});
|
|
1701
1705
|
} catch (e20) {
|
|
1702
1706
|
const gasPrice = 5000000000n;
|
|
1703
|
-
return
|
|
1707
|
+
return _chunkMWBT7MCEcjs.nativeCost.call(void 0, {
|
|
1704
1708
|
symbol,
|
|
1705
1709
|
decimals,
|
|
1706
1710
|
fee: gasPrice * gasLimit,
|
|
@@ -1840,9 +1844,9 @@ var loaders = {
|
|
|
1840
1844
|
solana: async () => {
|
|
1841
1845
|
let mod;
|
|
1842
1846
|
try {
|
|
1843
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-
|
|
1847
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-MYF4HBO4.cjs")));
|
|
1844
1848
|
} catch (cause) {
|
|
1845
|
-
throw new (0,
|
|
1849
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1846
1850
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
1847
1851
|
{ cause }
|
|
1848
1852
|
);
|
|
@@ -1852,9 +1856,9 @@ var loaders = {
|
|
|
1852
1856
|
ton: async () => {
|
|
1853
1857
|
let mod;
|
|
1854
1858
|
try {
|
|
1855
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-
|
|
1859
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-AOR3EURW.cjs")));
|
|
1856
1860
|
} catch (cause) {
|
|
1857
|
-
throw new (0,
|
|
1861
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1858
1862
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
1859
1863
|
{ cause }
|
|
1860
1864
|
);
|
|
@@ -1864,9 +1868,9 @@ var loaders = {
|
|
|
1864
1868
|
stellar: async () => {
|
|
1865
1869
|
let mod;
|
|
1866
1870
|
try {
|
|
1867
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-
|
|
1871
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-SUKASK4N.cjs")));
|
|
1868
1872
|
} catch (cause) {
|
|
1869
|
-
throw new (0,
|
|
1873
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1870
1874
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
1871
1875
|
{ cause }
|
|
1872
1876
|
);
|
|
@@ -1876,9 +1880,9 @@ var loaders = {
|
|
|
1876
1880
|
xrpl: async () => {
|
|
1877
1881
|
let mod;
|
|
1878
1882
|
try {
|
|
1879
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-
|
|
1883
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-Y6SQNYLC.cjs")));
|
|
1880
1884
|
} catch (cause) {
|
|
1881
|
-
throw new (0,
|
|
1885
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1882
1886
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
1883
1887
|
{ cause }
|
|
1884
1888
|
);
|
|
@@ -1888,9 +1892,9 @@ var loaders = {
|
|
|
1888
1892
|
tron: async () => {
|
|
1889
1893
|
let mod;
|
|
1890
1894
|
try {
|
|
1891
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-
|
|
1895
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-EMFXDFHW.cjs")));
|
|
1892
1896
|
} catch (cause) {
|
|
1893
|
-
throw new (0,
|
|
1897
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1894
1898
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
1895
1899
|
{ cause }
|
|
1896
1900
|
);
|
|
@@ -1900,9 +1904,9 @@ var loaders = {
|
|
|
1900
1904
|
sui: async () => {
|
|
1901
1905
|
let mod;
|
|
1902
1906
|
try {
|
|
1903
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-
|
|
1907
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-VE5LT7BL.cjs")));
|
|
1904
1908
|
} catch (cause) {
|
|
1905
|
-
throw new (0,
|
|
1909
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1906
1910
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
1907
1911
|
{ cause }
|
|
1908
1912
|
);
|
|
@@ -1912,9 +1916,9 @@ var loaders = {
|
|
|
1912
1916
|
near: async () => {
|
|
1913
1917
|
let mod;
|
|
1914
1918
|
try {
|
|
1915
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-
|
|
1919
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-6KAQVVG2.cjs")));
|
|
1916
1920
|
} catch (cause) {
|
|
1917
|
-
throw new (0,
|
|
1921
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1918
1922
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
1919
1923
|
{ cause }
|
|
1920
1924
|
);
|
|
@@ -1924,9 +1928,9 @@ var loaders = {
|
|
|
1924
1928
|
aptos: async () => {
|
|
1925
1929
|
let mod;
|
|
1926
1930
|
try {
|
|
1927
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-
|
|
1931
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-GHJPO6JJ.cjs")));
|
|
1928
1932
|
} catch (cause) {
|
|
1929
|
-
throw new (0,
|
|
1933
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1930
1934
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
1931
1935
|
{ cause }
|
|
1932
1936
|
);
|
|
@@ -1936,9 +1940,9 @@ var loaders = {
|
|
|
1936
1940
|
algorand: async () => {
|
|
1937
1941
|
let mod;
|
|
1938
1942
|
try {
|
|
1939
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-
|
|
1943
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-FCEECDG6.cjs")));
|
|
1940
1944
|
} catch (cause) {
|
|
1941
|
-
throw new (0,
|
|
1945
|
+
throw new (0, _chunkMWBT7MCEcjs.MissingDriverError)(
|
|
1942
1946
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
1943
1947
|
{ cause }
|
|
1944
1948
|
);
|
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
2691
|
-
/**
|
|
2692
|
-
*
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
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(
|
|
2827
|
+
bucket.total += BigInt(rec.amountBase);
|
|
2699
2828
|
bucket.count += 1;
|
|
2700
|
-
if (!bucket.symbol &&
|
|
2829
|
+
if (!bucket.symbol && rec.symbol) bucket.symbol = rec.symbol;
|
|
2701
2830
|
} else {
|
|
2702
2831
|
this.buckets.set(key, {
|
|
2703
|
-
network:
|
|
2704
|
-
asset:
|
|
2705
|
-
symbol:
|
|
2832
|
+
network: rec.network,
|
|
2833
|
+
asset: rec.asset,
|
|
2834
|
+
symbol: rec.symbol,
|
|
2706
2835
|
decimals,
|
|
2707
|
-
total: BigInt(
|
|
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',
|
|
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
|
|
2727
|
-
|
|
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:
|
|
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 =
|
|
2973
|
+
var PipRailClient = class {
|
|
2775
2974
|
|
|
2776
2975
|
|
|
2777
2976
|
|
|
2778
2977
|
|
|
2779
|
-
// Per-asset tally of everything this client has paid
|
|
2780
|
-
//
|
|
2781
|
-
|
|
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) {
|
|
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
|
-
|
|
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 (
|
|
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',
|
|
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',
|
|
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',
|
|
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,
|
|
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
|
|
2943
|
-
*
|
|
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',
|
|
2952
|
-
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess',
|
|
3245
|
+
expiresAt: _optionalChain([view, 'optionalAccess', _31 => _31.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
|
|
3246
|
+
secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _32 => _32.secondsRemaining]), () => ( null))
|
|
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',
|
|
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 =
|
|
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:
|
|
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',
|
|
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,
|
|
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,
|
|
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',
|
|
3537
|
+
const body = _optionalChain([init, 'optionalAccess', _42 => _42.body]);
|
|
3199
3538
|
if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
|
|
3200
|
-
throw new (0,
|
|
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',
|
|
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,
|
|
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',
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
3425
|
-
native: bal.native != null ?
|
|
3763
|
+
token: bal.token != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
|
|
3764
|
+
native: bal.native != null ? _chunkMWBT7MCEcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
|
|
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,
|
|
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,
|
|
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',
|
|
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,
|
|
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
|
-
|
|
3454
|
-
|
|
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 =
|
|
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',
|
|
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
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
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
|
-
|
|
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.
|
|
3530
|
-
throw new (0, _chunkVPENSVOIcjs.PaymentDeclinedError)(reason, { reasonCode: "APPROVAL" });
|
|
3883
|
+
this.refuse(reason, { reasonCode: "APPROVAL", quote });
|
|
3531
3884
|
}
|
|
3532
3885
|
}
|
|
3533
|
-
/**
|
|
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.
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
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,
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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,
|
|
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,
|
|
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,
|
|
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',
|
|
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',
|
|
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,
|
|
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',
|
|
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',
|
|
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,
|
|
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',
|
|
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,
|
|
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
|
-
}
|
|
4173
|
+
};
|
|
3725
4174
|
function throwIfAborted(signal) {
|
|
3726
|
-
if (_optionalChain([signal, 'optionalAccess',
|
|
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 (
|
|
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',
|
|
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',
|
|
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',
|
|
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,
|
|
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 (
|
|
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',
|
|
3876
|
-
const piprail = _optionalChain([ext, 'optionalAccess',
|
|
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 (
|
|
4347
|
+
} catch (e33) {
|
|
3896
4348
|
}
|
|
3897
4349
|
const settle = parseSettleResponse(response);
|
|
3898
|
-
if (_optionalChain([settle, 'optionalAccess',
|
|
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
|
-
*
|
|
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',
|
|
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',
|
|
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
|
-
/**
|
|
4050
|
-
*
|
|
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
|
-
/**
|
|
4060
|
-
*
|
|
4061
|
-
*
|
|
4062
|
-
*
|
|
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 {
|
|
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',
|
|
4128
|
-
const hasInput = _optionalChain([d, 'optionalAccess',
|
|
4617
|
+
const summary = _nullishCoalesce(_optionalChain([d, 'optionalAccess', _82 => _82.summary]), () => ( input.description));
|
|
4618
|
+
const hasInput = _optionalChain([d, 'optionalAccess', _83 => _83.queryParams]) && Object.keys(d.queryParams).length > 0;
|
|
4129
4619
|
const endpoint = {
|
|
4130
4620
|
...summary ? { summary } : {},
|
|
4131
|
-
..._optionalChain([d, 'optionalAccess',
|
|
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',
|
|
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
|
|
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
|
-
|
|
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
|
|
4257
|
-
|
|
4258
|
-
|
|
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
|
-
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
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 (
|
|
4785
|
+
} catch (e34) {
|
|
4286
4786
|
return text;
|
|
4287
4787
|
}
|
|
4288
4788
|
}
|
|
4289
4789
|
function toToolError(err) {
|
|
4290
|
-
if (!(err instanceof
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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 (
|
|
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',
|
|
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',
|
|
4790
|
-
const fp = _optionalChain([kind, 'optionalAccess',
|
|
5295
|
+
const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _87 => _87.scheme]) === "exact" && normalizeNetwork(String(_nullishCoalesce(_optionalChain([k, 'optionalAccess', _88 => _88.network]), () => ( "")))) === want);
|
|
5296
|
+
const fp = _optionalChain([kind, 'optionalAccess', _89 => _89.extra, 'optionalAccess', _90 => _90.feePayer]);
|
|
4791
5297
|
return typeof fp === "string" ? fp : void 0;
|
|
4792
|
-
} catch (
|
|
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',
|
|
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',
|
|
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',
|
|
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 (
|
|
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 (
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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 =
|
|
5520
|
-
} catch (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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,
|
|
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
|
|
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',
|
|
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 (
|
|
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',
|
|
5887
|
-
} catch (
|
|
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
|
-
|
|
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;
|