@piprail/sdk 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHAINS.md +3 -2
  2. package/CHANGELOG.md +49 -0
  3. package/ERRORS.md +2 -1
  4. package/README.md +19 -2
  5. package/STANDARDS.md +2 -0
  6. package/dist/{algorand-WGVF4KTU.js → algorand-7EUZYL2Z.js} +1 -1
  7. package/dist/{algorand-MXUSKX46.cjs → algorand-OIHGJN5S.cjs} +17 -17
  8. package/dist/{aptos-LPBLSEIQ.js → aptos-CDEYDDM5.js} +1 -1
  9. package/dist/{aptos-YT7SXWPF.cjs → aptos-WDWZOU25.cjs} +16 -16
  10. package/dist/{chunk-MDLZJGLY.cjs → chunk-FTKVCP6K.cjs} +24 -13
  11. package/dist/{chunk-SVMGHASK.js → chunk-H3A4KWLJ.js} +12 -1
  12. package/dist/index.cjs +399 -145
  13. package/dist/index.d.cts +209 -31
  14. package/dist/index.d.ts +209 -31
  15. package/dist/index.js +308 -54
  16. package/dist/{near-K6BDBABG.js → near-DT6LRIKB.js} +1 -1
  17. package/dist/{near-7ZDNISUX.cjs → near-FUH3VAXT.cjs} +19 -19
  18. package/dist/{solana-S3UFI3FE.js → solana-3TRYD4QB.js} +1 -1
  19. package/dist/{solana-PU7N2M64.cjs → solana-QUVXPKBZ.cjs} +14 -14
  20. package/dist/{stellar-VDQOFQEO.cjs → stellar-APZEBFAD.cjs} +21 -21
  21. package/dist/{stellar-Q5PO23SC.js → stellar-IK3UML6O.js} +1 -1
  22. package/dist/{sui-FKSMLKRF.cjs → sui-L7BQNJWO.cjs} +17 -17
  23. package/dist/{sui-WOXRKJXS.js → sui-VSE63WQM.js} +1 -1
  24. package/dist/{ton-VK6KRJHP.cjs → ton-5DLKKOFE.cjs} +14 -14
  25. package/dist/{ton-WPTXGLVK.js → ton-QHGQLJX2.js} +1 -1
  26. package/dist/{tron-6GXBXTR4.js → tron-2N2GA62O.js} +1 -1
  27. package/dist/{tron-WLOF5OUV.cjs → tron-HHIT6WKY.cjs} +24 -24
  28. package/dist/{xrpl-HEAPEXAM.js → xrpl-2GZMDYW5.js} +1 -1
  29. package/dist/{xrpl-CMNI25BV.cjs → xrpl-USEG4AHX.cjs} +21 -21
  30. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -21,7 +21,8 @@
21
21
 
22
22
 
23
23
 
24
- var _chunkMDLZJGLYcjs = require('./chunk-MDLZJGLY.cjs');
24
+
25
+ var _chunkFTKVCP6Kcjs = require('./chunk-FTKVCP6K.cjs');
25
26
 
26
27
  // src/drivers/registry.ts
27
28
  var byFamily = /* @__PURE__ */ new Map();
@@ -50,13 +51,13 @@ function resolveNetwork(opts) {
50
51
  const family = familyForChain(opts.chain);
51
52
  const driver = byFamily.get(family);
52
53
  if (!driver) {
53
- throw new (0, _chunkMDLZJGLYcjs.UnsupportedNetworkError)(
54
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedNetworkError)(
54
55
  `No driver registered for the "${family}" family \u2014 it may not be mounted yet (use the async resolveNetwork()).`
55
56
  );
56
57
  }
57
58
  const net = driver.resolve(opts);
58
59
  if (!net) {
59
- throw new (0, _chunkMDLZJGLYcjs.UnsupportedNetworkError)(
60
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedNetworkError)(
60
61
  `The ${family} driver didn't recognise this chain input.`
61
62
  );
62
63
  }
@@ -97,13 +98,18 @@ var CHAINS = {
97
98
  defaultRpc: "https://ethereum-rpc.publicnode.com",
98
99
  tokens: {
99
100
  USDC: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6, symbol: "USDC" },
100
- USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, symbol: "USDT" }
101
+ USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, symbol: "USDT" },
102
+ // Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "Euro Coin" here
103
+ // (NOT "EURC"); the buyer re-derives it on-chain, so the symbol below is display-only.
104
+ EURC: { address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c", decimals: 6, symbol: "EURC" }
101
105
  }
102
106
  },
103
107
  base: {
104
108
  chain: _chains.base,
105
109
  tokens: {
106
- USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6, symbol: "USDC" }
110
+ USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6, symbol: "USDC" },
111
+ // Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "EURC" here.
112
+ EURC: { address: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42", decimals: 6, symbol: "EURC" }
107
113
  }
108
114
  },
109
115
  arbitrum: {
@@ -139,7 +145,9 @@ var CHAINS = {
139
145
  chain: _chains.avalanche,
140
146
  tokens: {
141
147
  USDC: { address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", decimals: 6, symbol: "USDC" },
142
- USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6, symbol: "USDT" }
148
+ USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6, symbol: "USDT" },
149
+ // Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "Euro Coin" here.
150
+ EURC: { address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD", decimals: 6, symbol: "EURC" }
143
151
  }
144
152
  },
145
153
  // ── More popular EVM mainnets. Every address below was verified on-chain
@@ -312,12 +320,12 @@ function createWalletAdapter(config, resolved) {
312
320
  }
313
321
  const wc = config.walletClient;
314
322
  if (!wc.account) {
315
- throw new (0, _chunkMDLZJGLYcjs.WrongFamilyError)(
323
+ throw new (0, _chunkFTKVCP6Kcjs.WrongFamilyError)(
316
324
  "chain is EVM; the provided walletClient has no attached account. Use `createWalletClient({ account, chain, transport })`, or pass { privateKey }."
317
325
  );
318
326
  }
319
327
  if (wc.chain && wc.chain.id !== resolved.chainId) {
320
- throw new (0, _chunkMDLZJGLYcjs.WrongChainError)(
328
+ throw new (0, _chunkFTKVCP6Kcjs.WrongChainError)(
321
329
  `PipRailClient: walletClient is on chain ${wc.chain.id} but the SDK was configured with chain ${resolved.chainId}. They must match.`
322
330
  );
323
331
  }
@@ -591,6 +599,53 @@ function encodeXPaymentHeader(input) {
591
599
  };
592
600
  return base64(JSON.stringify(payload));
593
601
  }
602
+ async function payExactEvm(input) {
603
+ const { publicClient, walletClient, account, chainId, accept } = input;
604
+ let code;
605
+ try {
606
+ code = await publicClient.getCode({ address: account.address });
607
+ } catch (e6) {
608
+ code = void 0;
609
+ }
610
+ if (code && code !== "0x") {
611
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedSchemeError)(
612
+ `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.`
613
+ );
614
+ }
615
+ const domain = await readExactDomain(publicClient, accept.asset);
616
+ if (!domain) {
617
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedSchemeError)(
618
+ `exact: ${accept.asset} on ${accept.network} isn't an EIP-3009 token (USDT needs Permit2; native coin and plain ERC-20s aren't exact-payable). Pay via onchain-proof.`
619
+ );
620
+ }
621
+ const g = globalThis.crypto;
622
+ if (!_optionalChain([g, 'optionalAccess', _5 => _5.getRandomValues])) {
623
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedSchemeError)(
624
+ "this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the exact rail needs a CSPRNG nonce."
625
+ );
626
+ }
627
+ const raw = new Uint8Array(32);
628
+ g.getRandomValues(raw);
629
+ const nonce = `0x${[...raw].map((b) => b.toString(16).padStart(2, "0")).join("")}`;
630
+ const now = Math.floor(Date.now() / 1e3);
631
+ const from = account.address;
632
+ const to = _viem.getAddress.call(void 0, accept.payTo);
633
+ const value = accept.amount;
634
+ const validAfter = "0";
635
+ const validBefore = String(now + accept.maxTimeoutSeconds);
636
+ const signature = await walletClient.signTypedData({
637
+ account,
638
+ domain: { name: domain.name, version: domain.version, chainId, verifyingContract: _viem.getAddress.call(void 0, accept.asset) },
639
+ types: EIP3009_TYPES,
640
+ primaryType: "TransferWithAuthorization",
641
+ message: { from, to, value: BigInt(value), validAfter: 0n, validBefore: BigInt(validBefore), nonce }
642
+ });
643
+ return {
644
+ payload: { signature, authorization: { from, to, value, validAfter, validBefore, nonce } },
645
+ payerFrom: from,
646
+ nonce
647
+ };
648
+ }
594
649
  var eip3009Abi = [
595
650
  {
596
651
  type: "function",
@@ -644,7 +699,7 @@ async function readExactDomain(publicClient, asset) {
644
699
  let token;
645
700
  try {
646
701
  token = _viem.getAddress.call(void 0, asset);
647
- } catch (e6) {
702
+ } catch (e7) {
648
703
  return null;
649
704
  }
650
705
  try {
@@ -662,7 +717,7 @@ async function readExactDomain(publicClient, asset) {
662
717
  ]);
663
718
  if (typeof name !== "string" || typeof version !== "string" || !name || !version) return null;
664
719
  return { name, version };
665
- } catch (e7) {
720
+ } catch (e8) {
666
721
  return null;
667
722
  }
668
723
  }
@@ -710,7 +765,7 @@ async function verifyAndSettleExactEvm(input) {
710
765
  let fromCode;
711
766
  try {
712
767
  fromCode = await publicClient.getCode({ address: from });
713
- } catch (e8) {
768
+ } catch (e9) {
714
769
  return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
715
770
  }
716
771
  const isContractWallet = Boolean(fromCode && fromCode !== "0x");
@@ -746,7 +801,7 @@ async function verifyAndSettleExactEvm(input) {
746
801
  if (used) {
747
802
  return { ok: false, error: "tx_already_used", detail: `Authorization nonce ${nonce} already used or canceled on-chain.` };
748
803
  }
749
- } catch (e9) {
804
+ } catch (e10) {
750
805
  return { ok: false, error: "tx_not_found", detail: "Could not read authorizationState (transient RPC) \u2014 retry." };
751
806
  }
752
807
  const baseArgs = [from, to, value, validAfter, validBefore, nonce];
@@ -786,7 +841,7 @@ async function verifyAndSettleExactEvm(input) {
786
841
  args: writeArgs
787
842
  });
788
843
  } catch (err) {
789
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
844
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
790
845
  `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.`,
791
846
  { cause: err }
792
847
  );
@@ -798,7 +853,7 @@ async function verifyAndSettleExactEvm(input) {
798
853
  return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
799
854
  }
800
855
  } catch (err) {
801
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
856
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
802
857
  `exact settle: broadcast ${txHash} but couldn't confirm it (${shorten(err instanceof Error ? err.message : String(err))}).`,
803
858
  { cause: err }
804
859
  );
@@ -848,7 +903,7 @@ function decodeBase64(b64) {
848
903
  function fromBase64Json(b64) {
849
904
  try {
850
905
  return JSON.parse(decodeBase64(b64));
851
- } catch (e10) {
906
+ } catch (e11) {
852
907
  return null;
853
908
  }
854
909
  }
@@ -873,6 +928,9 @@ function buildReceiptHeader(receipt) {
873
928
  function buildSignatureHeader(signature) {
874
929
  return toBase64Json(signature);
875
930
  }
931
+ function buildExactSignatureHeader(input) {
932
+ return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
933
+ }
876
934
  async function parseChallenge(response) {
877
935
  const headerValue = response.headers.get(HEADER_REQUIRED);
878
936
  if (headerValue) {
@@ -882,22 +940,35 @@ async function parseChallenge(response) {
882
940
  try {
883
941
  const body = await response.clone().json();
884
942
  if (isValidChallenge(body)) return body;
885
- } catch (e11) {
943
+ } catch (e12) {
886
944
  }
887
945
  return null;
888
946
  }
889
947
  function parseReceipt(response) {
890
- const headerValue = response.headers.get(HEADER_RESPONSE);
948
+ const headerValue = _nullishCoalesce(response.headers.get(HEADER_RESPONSE), () => ( response.headers.get(HEADER_RESPONSE_V1)));
891
949
  if (!headerValue) return null;
892
950
  const parsed = fromBase64Json(headerValue);
893
951
  return isValidReceipt(parsed) ? parsed : null;
894
952
  }
953
+ function parseSettleResponse(response) {
954
+ const headerValue = _nullishCoalesce(response.headers.get(HEADER_RESPONSE), () => ( response.headers.get(HEADER_RESPONSE_V1)));
955
+ if (!headerValue) return null;
956
+ const parsed = fromBase64Json(headerValue);
957
+ if (!parsed || typeof parsed !== "object" || typeof parsed.success !== "boolean") return null;
958
+ return {
959
+ success: parsed.success,
960
+ ...typeof parsed.transaction === "string" ? { transaction: parsed.transaction } : {},
961
+ ...typeof parsed.network === "string" ? { network: parsed.network } : {},
962
+ ...typeof parsed.payer === "string" ? { payer: parsed.payer } : {},
963
+ ...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {}
964
+ };
965
+ }
895
966
  function parseSignatureHeader(value) {
896
967
  const parsed = fromBase64Json(value);
897
968
  if (!parsed || typeof parsed !== "object") return null;
898
969
  const v = parsed;
899
970
  const accepted = v.accepted;
900
- const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _5 => _5.scheme]), () => ( v.scheme));
971
+ const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _6 => _6.scheme]), () => ( v.scheme));
901
972
  if (scheme !== "onchain-proof") return null;
902
973
  const payload = v.payload;
903
974
  if (!payload || typeof payload.txHash !== "string" || typeof payload.nonce !== "string") {
@@ -910,9 +981,9 @@ function parseExactPaymentHeader(value) {
910
981
  if (!parsed || typeof parsed !== "object") return null;
911
982
  const v = parsed;
912
983
  const accepted = _nullishCoalesce(v.accepted, () => ( null));
913
- const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _6 => _6.scheme]), () => ( v.scheme));
984
+ const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _7 => _7.scheme]), () => ( v.scheme));
914
985
  if (scheme !== "exact") return null;
915
- const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _7 => _7.network]), () => ( v.network));
986
+ const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _8 => _8.network]), () => ( v.network));
916
987
  if (typeof network !== "string") return null;
917
988
  const payload = v.payload;
918
989
  if (!payload || typeof payload !== "object") return null;
@@ -969,7 +1040,7 @@ var evmDriver = {
969
1040
  let resolved;
970
1041
  try {
971
1042
  resolved = resolveChain(opts.chain, opts.rpcUrl);
972
- } catch (e12) {
1043
+ } catch (e13) {
973
1044
  return null;
974
1045
  }
975
1046
  return makeEvmNetwork(resolved);
@@ -997,15 +1068,15 @@ function makeEvmNetwork(resolved) {
997
1068
  const info = resolved.tokens[token.toUpperCase()];
998
1069
  if (!info) {
999
1070
  const known = Object.keys(resolved.tokens).join(", ") || "(none built in)";
1000
- throw new (0, _chunkMDLZJGLYcjs.UnknownTokenError)(
1071
+ throw new (0, _chunkFTKVCP6Kcjs.UnknownTokenError)(
1001
1072
  `token "${token}" isn't built in for ${resolved.chain.name} (known: ${known}). Pass { address, decimals } instead, or use 'native'.`
1002
1073
  );
1003
1074
  }
1004
1075
  return { asset: info.address, decimals: info.decimals, symbol: info.symbol };
1005
1076
  }
1006
- _chunkMDLZJGLYcjs.rejectForeignToken.call(void 0, token, "evm", network);
1077
+ _chunkFTKVCP6Kcjs.rejectForeignToken.call(void 0, token, "evm", network);
1007
1078
  if (!("address" in token)) {
1008
- throw new (0, _chunkMDLZJGLYcjs.WrongFamilyError)(
1079
+ throw new (0, _chunkFTKVCP6Kcjs.WrongFamilyError)(
1009
1080
  `chain ${network} is EVM; a custom token must be { address, decimals }.`
1010
1081
  );
1011
1082
  }
@@ -1025,7 +1096,7 @@ function makeEvmNetwork(resolved) {
1025
1096
  let normalized;
1026
1097
  try {
1027
1098
  normalized = _viem.getAddress.call(void 0, asset);
1028
- } catch (e13) {
1099
+ } catch (e14) {
1029
1100
  return null;
1030
1101
  }
1031
1102
  for (const info of Object.values(resolved.tokens)) {
@@ -1037,14 +1108,14 @@ function makeEvmNetwork(resolved) {
1037
1108
  },
1038
1109
  assertValidPayTo(payTo) {
1039
1110
  if (!_viem.isAddress.call(void 0, payTo)) {
1040
- throw new (0, _chunkMDLZJGLYcjs.WrongFamilyError)(
1111
+ throw new (0, _chunkFTKVCP6Kcjs.WrongFamilyError)(
1041
1112
  `chain ${network} is EVM, but payTo "${payTo}" is not a valid 0x address.`
1042
1113
  );
1043
1114
  }
1044
1115
  },
1045
1116
  bindWallet(wallet) {
1046
1117
  if (typeof wallet !== "object" || wallet === null || !("privateKey" in wallet) && !("walletClient" in wallet)) {
1047
- throw new (0, _chunkMDLZJGLYcjs.WrongFamilyError)(
1118
+ throw new (0, _chunkFTKVCP6Kcjs.WrongFamilyError)(
1048
1119
  `chain ${network} is EVM; wallet must be { privateKey } or { walletClient }.`
1049
1120
  );
1050
1121
  }
@@ -1061,12 +1132,12 @@ function makeEvmNetwork(resolved) {
1061
1132
  });
1062
1133
  } catch (err) {
1063
1134
  if (isViemInsufficientFunds(err)) {
1064
- throw new (0, _chunkMDLZJGLYcjs.InsufficientFundsError)(
1135
+ throw new (0, _chunkFTKVCP6Kcjs.InsufficientFundsError)(
1065
1136
  err instanceof Error ? err.message : "Insufficient funds for payment.",
1066
1137
  { cause: err }
1067
1138
  );
1068
1139
  }
1069
- throw _nullishCoalesce(_chunkMDLZJGLYcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
1140
+ throw _nullishCoalesce(_chunkFTKVCP6Kcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
1070
1141
  }
1071
1142
  },
1072
1143
  async confirm(ref, minConfirmations) {
@@ -1077,7 +1148,7 @@ function makeEvmNetwork(resolved) {
1077
1148
  });
1078
1149
  return { height: receipt.blockNumber.toString() };
1079
1150
  } catch (err) {
1080
- throw new (0, _chunkMDLZJGLYcjs.ConfirmationTimeoutError)(
1151
+ throw new (0, _chunkFTKVCP6Kcjs.ConfirmationTimeoutError)(
1081
1152
  `EVM tx ${ref} did not reach ${minConfirmations} confirmation(s) in time.`,
1082
1153
  { cause: err }
1083
1154
  );
@@ -1085,19 +1156,28 @@ function makeEvmNetwork(resolved) {
1085
1156
  },
1086
1157
  async estimateCost(accept) {
1087
1158
  const { decimals, symbol } = resolved.chain.nativeCurrency;
1159
+ if (accept.scheme === "exact") {
1160
+ return _chunkFTKVCP6Kcjs.nativeCost.call(void 0, {
1161
+ symbol,
1162
+ decimals,
1163
+ fee: 0n,
1164
+ basis: "estimated",
1165
+ detail: "gasless \u2014 the server/facilitator settles the signed authorization"
1166
+ });
1167
+ }
1088
1168
  const gasLimit = accept.asset === "native" ? 21000n : 65000n;
1089
1169
  try {
1090
1170
  const gasPrice = await publicClient.getGasPrice();
1091
- return _chunkMDLZJGLYcjs.nativeCost.call(void 0, {
1171
+ return _chunkFTKVCP6Kcjs.nativeCost.call(void 0, {
1092
1172
  symbol,
1093
1173
  decimals,
1094
1174
  fee: gasPrice * gasLimit,
1095
1175
  basis: "estimated",
1096
1176
  detail: `~${gasLimit} gas @ ${gasPrice} wei/gas`
1097
1177
  });
1098
- } catch (e14) {
1178
+ } catch (e15) {
1099
1179
  const gasPrice = 5000000000n;
1100
- return _chunkMDLZJGLYcjs.nativeCost.call(void 0, {
1180
+ return _chunkFTKVCP6Kcjs.nativeCost.call(void 0, {
1101
1181
  symbol,
1102
1182
  decimals,
1103
1183
  fee: gasPrice * gasLimit,
@@ -1118,7 +1198,7 @@ function makeEvmNetwork(resolved) {
1118
1198
  functionName: "balanceOf",
1119
1199
  args: [owner]
1120
1200
  });
1121
- } catch (e15) {
1201
+ } catch (e16) {
1122
1202
  token = null;
1123
1203
  }
1124
1204
  return { token, native };
@@ -1146,6 +1226,21 @@ function makeEvmNetwork(resolved) {
1146
1226
  minConfirmations: accept.extra.minConfirmations
1147
1227
  });
1148
1228
  },
1229
+ // Standard x402 `exact` rail (EIP-3009), BUYER side — EVM only. Re-derives the
1230
+ // token's EIP-712 domain on-chain, signs an authorization with the agent's own
1231
+ // key, and returns it for the client to frame into PAYMENT-SIGNATURE. Never
1232
+ // broadcasts. Throws UnsupportedSchemeError for a non-EIP-3009 token / contract signer.
1233
+ async payExact(wallet, accept) {
1234
+ const a = wallet._native;
1235
+ const { payload, payerFrom, nonce } = await payExactEvm({
1236
+ publicClient,
1237
+ walletClient: a.walletClient,
1238
+ account: a.account,
1239
+ chainId: resolved.chainId,
1240
+ accept
1241
+ });
1242
+ return { payload, accepted: accept, payerFrom, nonce };
1243
+ },
1149
1244
  // Standard x402 `exact` rail (EIP-3009), seller side — EVM only.
1150
1245
  async exactDomain(asset) {
1151
1246
  return readExactDomain(publicClient, asset);
@@ -1176,9 +1271,9 @@ var loaders = {
1176
1271
  solana: async () => {
1177
1272
  let mod;
1178
1273
  try {
1179
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-PU7N2M64.cjs")));
1274
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-QUVXPKBZ.cjs")));
1180
1275
  } catch (cause) {
1181
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1276
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1182
1277
  `Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
1183
1278
  { cause }
1184
1279
  );
@@ -1188,9 +1283,9 @@ var loaders = {
1188
1283
  ton: async () => {
1189
1284
  let mod;
1190
1285
  try {
1191
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-VK6KRJHP.cjs")));
1286
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-5DLKKOFE.cjs")));
1192
1287
  } catch (cause) {
1193
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1288
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1194
1289
  `TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
1195
1290
  { cause }
1196
1291
  );
@@ -1200,9 +1295,9 @@ var loaders = {
1200
1295
  stellar: async () => {
1201
1296
  let mod;
1202
1297
  try {
1203
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-VDQOFQEO.cjs")));
1298
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-APZEBFAD.cjs")));
1204
1299
  } catch (cause) {
1205
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1300
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1206
1301
  `Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
1207
1302
  { cause }
1208
1303
  );
@@ -1212,9 +1307,9 @@ var loaders = {
1212
1307
  xrpl: async () => {
1213
1308
  let mod;
1214
1309
  try {
1215
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-CMNI25BV.cjs")));
1310
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-USEG4AHX.cjs")));
1216
1311
  } catch (cause) {
1217
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1312
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1218
1313
  `XRPL selected, but its package isn't installed. Run: npm install xrpl`,
1219
1314
  { cause }
1220
1315
  );
@@ -1224,9 +1319,9 @@ var loaders = {
1224
1319
  tron: async () => {
1225
1320
  let mod;
1226
1321
  try {
1227
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-WLOF5OUV.cjs")));
1322
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-HHIT6WKY.cjs")));
1228
1323
  } catch (cause) {
1229
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1324
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1230
1325
  `Tron selected, but its package isn't installed. Run: npm install tronweb`,
1231
1326
  { cause }
1232
1327
  );
@@ -1236,9 +1331,9 @@ var loaders = {
1236
1331
  sui: async () => {
1237
1332
  let mod;
1238
1333
  try {
1239
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-FKSMLKRF.cjs")));
1334
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-L7BQNJWO.cjs")));
1240
1335
  } catch (cause) {
1241
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1336
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1242
1337
  `Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
1243
1338
  { cause }
1244
1339
  );
@@ -1248,9 +1343,9 @@ var loaders = {
1248
1343
  near: async () => {
1249
1344
  let mod;
1250
1345
  try {
1251
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-7ZDNISUX.cjs")));
1346
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-FUH3VAXT.cjs")));
1252
1347
  } catch (cause) {
1253
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1348
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1254
1349
  `NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
1255
1350
  { cause }
1256
1351
  );
@@ -1260,9 +1355,9 @@ var loaders = {
1260
1355
  aptos: async () => {
1261
1356
  let mod;
1262
1357
  try {
1263
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-YT7SXWPF.cjs")));
1358
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-WDWZOU25.cjs")));
1264
1359
  } catch (cause) {
1265
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1360
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1266
1361
  `Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
1267
1362
  { cause }
1268
1363
  );
@@ -1272,9 +1367,9 @@ var loaders = {
1272
1367
  algorand: async () => {
1273
1368
  let mod;
1274
1369
  try {
1275
- mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-MXUSKX46.cjs")));
1370
+ mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-OIHGJN5S.cjs")));
1276
1371
  } catch (cause) {
1277
- throw new (0, _chunkMDLZJGLYcjs.MissingDriverError)(
1372
+ throw new (0, _chunkFTKVCP6Kcjs.MissingDriverError)(
1278
1373
  `Algorand selected, but its package isn't installed. Run: npm install algosdk`,
1279
1374
  { cause }
1280
1375
  );
@@ -1339,7 +1434,7 @@ function getDirectoryInfo(source) {
1339
1434
  }
1340
1435
  function decorateOutcome(o) {
1341
1436
  const info = DIRECTORY_INFO[o.source];
1342
- return { ...o, visibility: o.ok ? info.onSuccess : "not-listable", note: info.caveat };
1437
+ return { ...o, visibility: _nullishCoalesce(o.visibility, () => ( (o.ok ? info.onSuccess : "not-listable"))), note: info.caveat };
1343
1438
  }
1344
1439
  var BAZAAR_URL = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources";
1345
1440
  var INDEX402_SEARCH = "https://402index.io/api/v1/services";
@@ -1391,7 +1486,7 @@ async function searchOpenIndexes(opts = {}) {
1391
1486
  async function safeSearch(run) {
1392
1487
  try {
1393
1488
  return await run();
1394
- } catch (e16) {
1489
+ } catch (e17) {
1395
1490
  return [];
1396
1491
  }
1397
1492
  }
@@ -1495,12 +1590,15 @@ async function register402Index(input) {
1495
1590
  body: JSON.stringify(payload)
1496
1591
  });
1497
1592
  if (res.ok) {
1498
- const msg = await readIndexMessage(res);
1593
+ const body = await res.json().catch(() => ({}));
1594
+ const msg = typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
1595
+ const live = _optionalChain([body, 'access', _9 => _9.service, 'optionalAccess', _10 => _10.status]) === "active";
1499
1596
  return {
1500
1597
  source: "402index",
1501
1598
  ok: true,
1502
1599
  status: res.status,
1503
- detail: _nullishCoalesce(msg, () => ( "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval)."))
1600
+ ...live ? { visibility: "live" } : {},
1601
+ detail: _nullishCoalesce(msg, () => ( (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval).")))
1504
1602
  };
1505
1603
  }
1506
1604
  const why = await readIndexError(res);
@@ -1514,14 +1612,6 @@ async function register402Index(input) {
1514
1612
  return { source: "402index", ok: false, detail: errMsg(err) };
1515
1613
  }
1516
1614
  }
1517
- async function readIndexMessage(res) {
1518
- try {
1519
- const body = await res.json();
1520
- return typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
1521
- } catch (e17) {
1522
- return void 0;
1523
- }
1524
- }
1525
1615
  async function readIndexError(res) {
1526
1616
  try {
1527
1617
  const body = await res.json();
@@ -1639,11 +1729,11 @@ async function readSiwxInfo(res) {
1639
1729
  try {
1640
1730
  const body = await res.json();
1641
1731
  const ext = body.extensions;
1642
- const siwx = _optionalChain([ext, 'optionalAccess', _8 => _8["sign-in-with-x"]]);
1643
- const info = _nullishCoalesce(_optionalChain([siwx, 'optionalAccess', _9 => _9.info]), () => ( siwx));
1644
- if (info && info.chainId == null && Array.isArray(_optionalChain([siwx, 'optionalAccess', _10 => _10.supportedChains]))) {
1732
+ const siwx = _optionalChain([ext, 'optionalAccess', _11 => _11["sign-in-with-x"]]);
1733
+ const info = _nullishCoalesce(_optionalChain([siwx, 'optionalAccess', _12 => _12.info]), () => ( siwx));
1734
+ if (info && info.chainId == null && Array.isArray(_optionalChain([siwx, 'optionalAccess', _13 => _13.supportedChains]))) {
1645
1735
  const evm = siwx.supportedChains.find(
1646
- (c) => typeof _optionalChain([c, 'optionalAccess', _11 => _11.chainId]) === "string" && c.chainId.startsWith("eip155:")
1736
+ (c) => typeof _optionalChain([c, 'optionalAccess', _14 => _14.chainId]) === "string" && c.chainId.startsWith("eip155:")
1647
1737
  );
1648
1738
  if (evm && typeof evm.chainId === "string") info.chainId = evm.chainId;
1649
1739
  }
@@ -1699,7 +1789,7 @@ function mapRails(accepts) {
1699
1789
  }
1700
1790
  function matchesQuery(r, query) {
1701
1791
  const q = query.toLowerCase();
1702
- return r.resource.toLowerCase().includes(q) || (_nullishCoalesce(_optionalChain([r, 'access', _12 => _12.name, 'optionalAccess', _13 => _13.toLowerCase, 'call', _14 => _14(), 'access', _15 => _15.includes, 'call', _16 => _16(q)]), () => ( false))) || (_nullishCoalesce(_optionalChain([r, 'access', _17 => _17.description, 'optionalAccess', _18 => _18.toLowerCase, 'call', _19 => _19(), 'access', _20 => _20.includes, 'call', _21 => _21(q)]), () => ( false)));
1792
+ return r.resource.toLowerCase().includes(q) || (_nullishCoalesce(_optionalChain([r, 'access', _15 => _15.name, 'optionalAccess', _16 => _16.toLowerCase, 'call', _17 => _17(), 'access', _18 => _18.includes, 'call', _19 => _19(q)]), () => ( false))) || (_nullishCoalesce(_optionalChain([r, 'access', _20 => _20.description, 'optionalAccess', _21 => _21.toLowerCase, 'call', _22 => _22(), 'access', _23 => _23.includes, 'call', _24 => _24(q)]), () => ( false)));
1703
1793
  }
1704
1794
  function pickString(o, ...keys) {
1705
1795
  for (const k of keys) {
@@ -1797,7 +1887,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase) {
1797
1887
  }
1798
1888
  }
1799
1889
  if (policy.maxAmount !== void 0) {
1800
- const cap = _chunkMDLZJGLYcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
1890
+ const cap = _chunkFTKVCP6Kcjs.floorUnits.call(void 0, policy.maxAmount, intent.decimals);
1801
1891
  if (intent.amountBase > cap) {
1802
1892
  return deny(
1803
1893
  `payment of ${intent.amountBase} base units exceeds policy.maxAmount ` + `(${policy.maxAmount} ${_nullishCoalesce(intent.symbol, () => ( ""))}).`.trimEnd()
@@ -1805,7 +1895,7 @@ function evaluatePolicy(intent, policy, spentForAssetBase) {
1805
1895
  }
1806
1896
  }
1807
1897
  if (policy.maxTotal !== void 0) {
1808
- const cap = _chunkMDLZJGLYcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
1898
+ const cap = _chunkFTKVCP6Kcjs.floorUnits.call(void 0, policy.maxTotal, intent.decimals);
1809
1899
  if (spentForAssetBase + intent.amountBase > cap) {
1810
1900
  return deny(
1811
1901
  `this payment would push spend on ${_nullishCoalesce(intent.symbol, () => ( intent.asset))} past policy.maxTotal (${policy.maxTotal}); already spent ${spentForAssetBase} base units.`
@@ -1843,7 +1933,7 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
1843
1933
  }
1844
1934
  /** Running total (base units) already spent on this (network, asset). */
1845
1935
  totalFor(network, asset) {
1846
- return _nullishCoalesce(_optionalChain([this, 'access', _22 => _22.buckets, 'access', _23 => _23.get, 'call', _24 => _24(keyFor(network, asset)), 'optionalAccess', _25 => _25.total]), () => ( 0n));
1936
+ return _nullishCoalesce(_optionalChain([this, 'access', _25 => _25.buckets, 'access', _26 => _26.get, 'call', _27 => _27(keyFor(network, asset)), 'optionalAccess', _28 => _28.total]), () => ( 0n));
1847
1937
  }
1848
1938
  /** An immutable snapshot of all spend so far. */
1849
1939
  summary() {
@@ -1855,7 +1945,7 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
1855
1945
  symbol: b.symbol,
1856
1946
  decimals: b.decimals,
1857
1947
  totalBase: b.total.toString(),
1858
- totalFormatted: _chunkMDLZJGLYcjs.formatUnits.call(void 0, b.total, b.decimals),
1948
+ totalFormatted: _chunkFTKVCP6Kcjs.formatUnits.call(void 0, b.total, b.decimals),
1859
1949
  count: b.count
1860
1950
  })),
1861
1951
  records: [...this.records]
@@ -1864,6 +1954,7 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
1864
1954
  }, _class);
1865
1955
 
1866
1956
  // src/client.ts
1957
+ var DEFAULT_SCHEMES = ["onchain-proof"];
1867
1958
  var RECIPIENT_FIX = {
1868
1959
  NO_TRUSTLINE: "the recipient needs a one-time trustline for this asset before it can receive",
1869
1960
  NOT_REGISTERED: "the recipient must be storage_deposit-registered on this token (NEP-145, one-time)",
@@ -1906,6 +1997,11 @@ var PipRailClient = (_class2 = class {
1906
1997
  return { net, wallet };
1907
1998
  })();
1908
1999
  }
2000
+ /** Resolve the effective scheme set: a per-call override, else the constructor's
2001
+ * `schemes`, else the `onchain-proof`-only default. */
2002
+ resolveSchemes(perCall) {
2003
+ return _nullishCoalesce(_nullishCoalesce(perCall, () => ( this.opts.schemes)), () => ( DEFAULT_SCHEMES));
2004
+ }
1909
2005
  /** GET that auto-handles 402. Pass a full URL to any x402-gated endpoint. */
1910
2006
  get(url, init) {
1911
2007
  return this.fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: "GET" });
@@ -1917,7 +2013,7 @@ var PipRailClient = (_class2 = class {
1917
2013
  * as-is) or a plain object (serialised as JSON).
1918
2014
  */
1919
2015
  post(url, body, init) {
1920
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _26 => _26.headers]));
2016
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _29 => _29.headers]));
1921
2017
  let payload;
1922
2018
  if (body === void 0 || body === null) {
1923
2019
  payload = void 0;
@@ -1948,9 +2044,9 @@ var PipRailClient = (_class2 = class {
1948
2044
  * "0.05 USDC on Base, within budget → pay it." No funds move.
1949
2045
  */
1950
2046
  async quote(url, init) {
1951
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _27 => _27.method]), () => ( "GET")) });
2047
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _30 => _30.method]), () => ( "GET")) });
1952
2048
  if (res.status !== 402) return null;
1953
- const { quote } = await this.resolveChallenge(url, res);
2049
+ const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
1954
2050
  return quote;
1955
2051
  }
1956
2052
  /**
@@ -1967,9 +2063,9 @@ var PipRailClient = (_class2 = class {
1967
2063
  * on Tron, where a USD₮ transfer can cost real TRX.
1968
2064
  */
1969
2065
  async estimateCost(url, init) {
1970
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _28 => _28.method]), () => ( "GET")) });
2066
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _31 => _31.method]), () => ( "GET")) });
1971
2067
  if (res.status !== 402) return null;
1972
- const { net, accept, quote } = await this.resolveChallenge(url, res);
2068
+ const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
1973
2069
  const cost = await net.estimateCost(accept);
1974
2070
  return { quote, cost };
1975
2071
  }
@@ -1998,14 +2094,14 @@ var PipRailClient = (_class2 = class {
1998
2094
  * the plan yourself. No funds move.
1999
2095
  */
2000
2096
  async planPayment(url, init) {
2001
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _29 => _29.method]), () => ( "GET")) });
2097
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _32 => _32.method]), () => ( "GET")) });
2002
2098
  if (res.status !== 402) return null;
2003
2099
  const challenge = await parseChallenge(res);
2004
2100
  if (!challenge) {
2005
- throw new (0, _chunkMDLZJGLYcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
2101
+ throw new (0, _chunkFTKVCP6Kcjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
2006
2102
  }
2007
2103
  const { net, wallet } = await this.ensure();
2008
- return this.planFromChallenge(net, wallet, challenge, url);
2104
+ return this.planFromChallenge(net, wallet, challenge, url, this.resolveSchemes());
2009
2105
  }
2010
2106
  /**
2011
2107
  * Convenience over {@link planPayment}: can the wallet settle this URL right now?
@@ -2033,8 +2129,8 @@ var PipRailClient = (_class2 = class {
2033
2129
  * - A resource just listed via {@link register} may not appear yet — 402 Index reviews
2034
2130
  * before publishing, so retry with a brief backoff if a fresh listing is missing.
2035
2131
  * - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
2036
- * only `onchain-proof` rails directly (pay `exact` resources with the experimental
2037
- * `drivers/evm/exact.ts`).
2132
+ * `onchain-proof` rails by default, and standard `exact` rails too once you opt in
2133
+ * with `schemes: ['onchain-proof', 'exact']` (EVM + EIP-3009 — USDC/EURC).
2038
2134
  */
2039
2135
  async discover(opts = {}) {
2040
2136
  const found = await searchOpenIndexes({
@@ -2165,29 +2261,33 @@ var PipRailClient = (_class2 = class {
2165
2261
  * streams throw `NonReplayableBodyError`.
2166
2262
  */
2167
2263
  async fetch(url, init) {
2168
- const body = _optionalChain([init, 'optionalAccess', _30 => _30.body]);
2264
+ const body = _optionalChain([init, 'optionalAccess', _33 => _33.body]);
2169
2265
  if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
2170
- throw new (0, _chunkMDLZJGLYcjs.NonReplayableBodyError)(
2266
+ throw new (0, _chunkFTKVCP6Kcjs.NonReplayableBodyError)(
2171
2267
  "fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
2172
2268
  );
2173
2269
  }
2174
2270
  const firstResponse = await fetch(url, init);
2175
2271
  if (firstResponse.status !== 402) return firstResponse;
2176
- const resolved = await this.resolveChallenge(url, firstResponse);
2272
+ const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _34 => _34.schemes]));
2273
+ const resolved = await this.resolveChallenge(url, firstResponse, schemes);
2177
2274
  const { net, wallet, challenge } = resolved;
2178
2275
  let accept = resolved.accept;
2179
2276
  let quote = resolved.quote;
2180
- const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _31 => _31.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
2277
+ const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _35 => _35.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
2181
2278
  if (autoRoute) {
2182
- const plan = await this.planFromChallenge(net, wallet, challenge, url);
2279
+ const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
2183
2280
  if (!plan.best) {
2184
- throw new (0, _chunkMDLZJGLYcjs.PaymentDeclinedError)(_nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment.")));
2281
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentDeclinedError)(_nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment.")));
2185
2282
  }
2186
2283
  accept = plan.best.accept;
2187
2284
  quote = plan.best.quote;
2188
2285
  }
2189
2286
  this.safeEmit({ kind: "payment-required", challenge, accept });
2190
2287
  await this.authorize(quote);
2288
+ if (accept.scheme === "exact") {
2289
+ return this.payExactRail(net, wallet, accept, url, init, quote);
2290
+ }
2191
2291
  const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
2192
2292
  const response = await this.retryWithProof(url, init, accept, ref, confirmed);
2193
2293
  this.recordSpend(quote, ref);
@@ -2199,19 +2299,37 @@ var PipRailClient = (_class2 = class {
2199
2299
  * network, pick the accept the client can pay, and build its quote. Shared by
2200
2300
  * `quote()` (read-only) and `fetch()` (which then authorises + pays).
2201
2301
  */
2202
- async resolveChallenge(url, response) {
2302
+ async resolveChallenge(url, response, schemes) {
2203
2303
  const challenge = await parseChallenge(response);
2204
2304
  if (!challenge) {
2205
- throw new (0, _chunkMDLZJGLYcjs.InvalidEnvelopeError)(
2305
+ throw new (0, _chunkFTKVCP6Kcjs.InvalidEnvelopeError)(
2206
2306
  "402 response did not include a parseable x402 challenge."
2207
2307
  );
2208
2308
  }
2209
2309
  const { net, wallet } = await this.ensure();
2210
- const candidates = this.gatherCandidates(net, challenge);
2310
+ const candidates = this.gatherCandidates(net, challenge, schemes);
2211
2311
  if (candidates.length === 0) {
2212
- const networks = challenge.accepts.map((a) => a.network).join(", ");
2213
- throw new (0, _chunkMDLZJGLYcjs.NoCompatibleAcceptError)(
2214
- `No accepts[] entry for ${net.network} (challenge offered: ${networks || "none"}).`
2312
+ const exactOnNet = challenge.accepts.some(
2313
+ (a) => a.scheme === "exact" && net.supports(a.network)
2314
+ );
2315
+ if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
2316
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedSchemeError)(
2317
+ `This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (EVM + EIP-3009 only), and no 'onchain-proof' rail was offered.`
2318
+ );
2319
+ }
2320
+ if (!schemes.includes("exact") && exactOnNet && typeof net.payExact === "function") {
2321
+ const payable = challenge.accepts.some(
2322
+ (a) => a.scheme === "exact" && net.supports(a.network) && net.describeAsset(a.asset) != null
2323
+ );
2324
+ if (payable) {
2325
+ throw new (0, _chunkFTKVCP6Kcjs.NoCompatibleAcceptError)(
2326
+ `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).`
2327
+ );
2328
+ }
2329
+ }
2330
+ const networks = [...new Set(challenge.accepts.map((a) => a.network))].join(", ");
2331
+ throw new (0, _chunkFTKVCP6Kcjs.NoCompatibleAcceptError)(
2332
+ `No accepts[] entry payable by this client on ${net.network} (schemes: ${schemes.join(", ")}; challenge offered: ${networks || "none"}).`
2215
2333
  );
2216
2334
  }
2217
2335
  const priced = candidates.map((accept) => ({
@@ -2221,20 +2339,37 @@ var PipRailClient = (_class2 = class {
2221
2339
  const chosen = _nullishCoalesce(priced.find((p) => p.quote.withinPolicy), () => ( priced[0]));
2222
2340
  return { net, wallet, accept: chosen.accept, challenge, quote: chosen.quote };
2223
2341
  }
2224
- /** The candidate accepts this client could pay: our scheme, on the bound network.
2225
- * A dual-advertised challenge may also carry standard `exact` rails the PipRail
2226
- * client ignores those (it pays the backendless `onchain-proof` rail); the type
2227
- * predicate narrows the `X402AnyAccept` union to the rails we settle. */
2228
- gatherCandidates(net, challenge) {
2229
- return challenge.accepts.filter(
2230
- (a) => a.scheme === "onchain-proof" && net.supports(a.network)
2231
- );
2342
+ /** The candidate accepts this client could pay, on the bound network. Always the
2343
+ * backendless `onchain-proof` rails; PLUS standard `exact` rails when `schemes`
2344
+ * enables them AND the driver can settle them (EVM `payExact` + a recognised
2345
+ * EIP-3009 token). `onchain-proof` is gathered FIRST so default selection is
2346
+ * unchanged when `exact` is off. */
2347
+ gatherCandidates(net, challenge, schemes) {
2348
+ const out = [];
2349
+ if (schemes.includes("onchain-proof")) {
2350
+ out.push(
2351
+ ...challenge.accepts.filter(
2352
+ (a) => a.scheme === "onchain-proof" && net.supports(a.network)
2353
+ )
2354
+ );
2355
+ }
2356
+ if (schemes.includes("exact")) {
2357
+ out.push(
2358
+ ...challenge.accepts.filter(
2359
+ (a) => a.scheme === "exact" && net.supports(a.network) && typeof net.payExact === "function" && net.describeAsset(a.asset) != null && // a foreign rail's maxTimeoutSeconds must be a usable positive integer, or
2360
+ // signing it would build a NaN/garbage validBefore — drop it silently
2361
+ // (symmetric with an unrecognised token) rather than leak a raw SyntaxError.
2362
+ Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
2363
+ )
2364
+ );
2365
+ }
2366
+ return out;
2232
2367
  }
2233
2368
  /** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
2234
2369
  * net/wallet. Shared by `planPayment` (read-only) and `fetch`'s autoRoute. */
2235
- async planFromChallenge(net, wallet, challenge, url) {
2370
+ async planFromChallenge(net, wallet, challenge, url, schemes) {
2236
2371
  const chainLabel = typeof this.opts.chain === "string" ? this.opts.chain : net.network;
2237
- const candidates = this.gatherCandidates(net, challenge);
2372
+ const candidates = this.gatherCandidates(net, challenge, schemes);
2238
2373
  if (candidates.length === 0) {
2239
2374
  const offered = [...new Set(challenge.accepts.map((a) => a.network))].join(", ") || "none";
2240
2375
  return {
@@ -2274,29 +2409,35 @@ var PipRailClient = (_class2 = class {
2274
2409
  const rr = await net.recipientReady(accept.payTo, accept.asset).catch(() => ({ ready: "unknown" }));
2275
2410
  const amount = BigInt(accept.amount);
2276
2411
  const fee = safeBig(cost.fee);
2412
+ const isExact = accept.scheme === "exact";
2277
2413
  const isNative = accept.asset === "native";
2278
2414
  const blockers = [];
2279
2415
  const warnings = [];
2280
2416
  const shortfall = {};
2281
2417
  if (!quote.withinPolicy) blockers.push("OUTSIDE_POLICY");
2282
2418
  if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
2283
- if (cost.basis === "heuristic") warnings.push("GAS_HEURISTIC");
2419
+ if (!isExact && cost.basis === "heuristic") warnings.push("GAS_HEURISTIC");
2284
2420
  const tokenKnown = bal.token != null;
2285
2421
  const nativeKnown = bal.native != null;
2286
- if (!tokenKnown || !nativeKnown) warnings.push("BALANCE_UNREADABLE");
2287
- if (isNative) {
2422
+ if (isExact ? !tokenKnown : !tokenKnown || !nativeKnown) warnings.push("BALANCE_UNREADABLE");
2423
+ if (isExact) {
2424
+ if (tokenKnown && bal.token < amount) {
2425
+ blockers.push("INSUFFICIENT_TOKEN");
2426
+ shortfall.token = _chunkFTKVCP6Kcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
2427
+ }
2428
+ } else if (isNative) {
2288
2429
  if (nativeKnown && bal.native < amount + fee) {
2289
2430
  blockers.push("INSUFFICIENT_TOKEN");
2290
- shortfall.token = _chunkMDLZJGLYcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
2431
+ shortfall.token = _chunkFTKVCP6Kcjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
2291
2432
  }
2292
2433
  } else {
2293
2434
  if (tokenKnown && bal.token < amount) {
2294
2435
  blockers.push("INSUFFICIENT_TOKEN");
2295
- shortfall.token = _chunkMDLZJGLYcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
2436
+ shortfall.token = _chunkFTKVCP6Kcjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
2296
2437
  }
2297
2438
  if (nativeKnown && bal.native < fee) {
2298
2439
  blockers.push("INSUFFICIENT_GAS");
2299
- shortfall.native = _chunkMDLZJGLYcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
2440
+ shortfall.native = _chunkFTKVCP6Kcjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
2300
2441
  } else if (nativeKnown && fee > 0n && bal.native < fee * 3n / 2n) {
2301
2442
  warnings.push("THIN_GAS_MARGIN");
2302
2443
  }
@@ -2311,7 +2452,7 @@ var PipRailClient = (_class2 = class {
2311
2452
  } else {
2312
2453
  recipient = { ready: rr.ready };
2313
2454
  }
2314
- const unreadable = isNative ? !nativeKnown : !tokenKnown || !nativeKnown;
2455
+ const unreadable = isExact ? !tokenKnown : isNative ? !nativeKnown : !tokenKnown || !nativeKnown;
2315
2456
  const state = blockers.length ? "blocked" : unreadable || rr.ready === "unknown" ? "unknown" : "payable";
2316
2457
  return {
2317
2458
  accept,
@@ -2321,8 +2462,8 @@ var PipRailClient = (_class2 = class {
2321
2462
  blockers,
2322
2463
  warnings,
2323
2464
  balance: {
2324
- token: bal.token != null ? _chunkMDLZJGLYcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
2325
- native: bal.native != null ? _chunkMDLZJGLYcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
2465
+ token: bal.token != null ? _chunkFTKVCP6Kcjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
2466
+ native: bal.native != null ? _chunkFTKVCP6Kcjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
2326
2467
  },
2327
2468
  need: { token: quote.amountFormatted, native: cost.feeFormatted },
2328
2469
  ...shortfall.token || shortfall.native ? { shortfall } : {},
@@ -2333,15 +2474,20 @@ var PipRailClient = (_class2 = class {
2333
2474
  * driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
2334
2475
  buildQuote(net, accept, url, description) {
2335
2476
  if (!/^\d+$/.test(accept.amount)) {
2336
- throw new (0, _chunkMDLZJGLYcjs.InvalidEnvelopeError)(
2477
+ throw new (0, _chunkFTKVCP6Kcjs.InvalidEnvelopeError)(
2337
2478
  `challenge amount "${accept.amount}" is not a base-unit integer.`
2338
2479
  );
2339
2480
  }
2340
2481
  const amountBase = BigInt(accept.amount);
2341
2482
  const described = net.describeAsset(accept.asset);
2342
- const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _32 => _32.decimals]), () => ( accept.extra.decimals));
2343
- const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _33 => _33.symbol]), () => ( accept.extra.symbol));
2344
- const amountFormatted = _chunkMDLZJGLYcjs.formatUnits.call(void 0, amountBase, decimals);
2483
+ const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _36 => _36.decimals]), () => ( accept.extra.decimals));
2484
+ if (decimals === void 0) {
2485
+ throw new (0, _chunkFTKVCP6Kcjs.InvalidEnvelopeError)(
2486
+ `challenge for ${accept.asset} on ${accept.network} states no decimals and the SDK doesn't recognise the token \u2014 refusing to price it.`
2487
+ );
2488
+ }
2489
+ const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _37 => _37.symbol]), () => ( accept.extra.symbol));
2490
+ const amountFormatted = _chunkFTKVCP6Kcjs.formatUnits.call(void 0, amountBase, decimals);
2345
2491
  const intent = {
2346
2492
  host: hostOf2(url),
2347
2493
  chain: this.opts.chain,
@@ -2381,7 +2527,7 @@ var PipRailClient = (_class2 = class {
2381
2527
  * throwing PaymentDeclinedError, before any funds move. */
2382
2528
  async authorize(quote) {
2383
2529
  if (!quote.withinPolicy) {
2384
- throw new (0, _chunkMDLZJGLYcjs.PaymentDeclinedError)(
2530
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentDeclinedError)(
2385
2531
  `Payment refused by policy: ${_nullishCoalesce(quote.policyReason, () => ( "not allowed"))}`
2386
2532
  );
2387
2533
  }
@@ -2391,12 +2537,12 @@ var PipRailClient = (_class2 = class {
2391
2537
  try {
2392
2538
  approved = await hook(quote);
2393
2539
  } catch (err) {
2394
- throw new (0, _chunkMDLZJGLYcjs.PaymentDeclinedError)("onBeforePay threw \u2014 refusing to pay.", {
2540
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentDeclinedError)("onBeforePay threw \u2014 refusing to pay.", {
2395
2541
  cause: err
2396
2542
  });
2397
2543
  }
2398
2544
  if (!approved) {
2399
- throw new (0, _chunkMDLZJGLYcjs.PaymentDeclinedError)(
2545
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentDeclinedError)(
2400
2546
  `onBeforePay declined ${quote.amountFormatted} ${_nullishCoalesce(quote.symbol, () => ( ""))}`.trimEnd() + ` on ${quote.network}.`
2401
2547
  );
2402
2548
  }
@@ -2420,7 +2566,7 @@ var PipRailClient = (_class2 = class {
2420
2566
  }
2421
2567
  async payAndConfirm(net, wallet, accept) {
2422
2568
  if (!net.supports(accept.network)) {
2423
- throw new (0, _chunkMDLZJGLYcjs.WrongChainError)(
2569
+ throw new (0, _chunkFTKVCP6Kcjs.WrongChainError)(
2424
2570
  `Challenge expects ${accept.network} but client is on ${net.network}.`
2425
2571
  );
2426
2572
  }
@@ -2449,7 +2595,7 @@ var PipRailClient = (_class2 = class {
2449
2595
  accepted: accept,
2450
2596
  payload: { nonce: accept.extra.nonce, txHash: ref }
2451
2597
  };
2452
- const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _34 => _34.headers]));
2598
+ const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _38 => _38.headers]));
2453
2599
  headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
2454
2600
  let lastResponse = null;
2455
2601
  let lastReason = null;
@@ -2464,7 +2610,7 @@ var PipRailClient = (_class2 = class {
2464
2610
  () => timeoutController.abort(),
2465
2611
  this.retryTimeoutMs
2466
2612
  );
2467
- const signal = _optionalChain([originalInit, 'optionalAccess', _35 => _35.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
2613
+ const signal = _optionalChain([originalInit, 'optionalAccess', _39 => _39.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
2468
2614
  try {
2469
2615
  lastResponse = await fetch(url, {
2470
2616
  ..._nullishCoalesce(originalInit, () => ( {})),
@@ -2473,7 +2619,7 @@ var PipRailClient = (_class2 = class {
2473
2619
  });
2474
2620
  } catch (err) {
2475
2621
  if (timeoutController.signal.aborted) {
2476
- throw new (0, _chunkMDLZJGLYcjs.PaymentTimeoutError)(
2622
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentTimeoutError)(
2477
2623
  `Server did not respond within ${this.retryTimeoutMs}ms after broadcasting payment ${ref}. Re-verify or re-submit ref=${ref} \u2014 do NOT re-pay.`,
2478
2624
  { cause: err, ref }
2479
2625
  );
@@ -2495,12 +2641,107 @@ var PipRailClient = (_class2 = class {
2495
2641
  kind: "payment-failed",
2496
2642
  reason: `server returned 402 after broadcasting payment ${ref}${unconfirmedNote} (${why})`
2497
2643
  });
2498
- throw new (0, _chunkMDLZJGLYcjs.MaxRetriesExceededError)(
2644
+ throw new (0, _chunkFTKVCP6Kcjs.MaxRetriesExceededError)(
2499
2645
  `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).`,
2500
2646
  { ref }
2501
2647
  );
2502
2648
  }
2649
+ /**
2650
+ * Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative
2651
+ * path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE
2652
+ * (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS
2653
+ * it synchronously, so a blind re-POST of a still-in-flight authorization could
2654
+ * double-BROADCAST it. Hence, unlike the onchain-proof loop:
2655
+ *
2656
+ * • sign exactly once — reuse the SAME header on every retry, never re-sign;
2657
+ * • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded
2658
+ * well under `maxTimeoutSeconds` so the loop can't outlive the authorization;
2659
+ * • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the
2660
+ * nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay);
2661
+ * • a 5xx → return as-is (server settle failure; the authorization stays valid +
2662
+ * its nonce unused) — no settled event, no spend;
2663
+ * • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend;
2664
+ * • the spend is recorded EXACTLY ONCE, on an affirmative settlement only.
2665
+ */
2666
+ async payExactRail(net, wallet, accept, url, init, quote) {
2667
+ if (!net.payExact) {
2668
+ throw new (0, _chunkFTKVCP6Kcjs.UnsupportedSchemeError)(
2669
+ `the ${net.family} family can't pay a standard 'exact' rail (EVM + EIP-3009 only).`
2670
+ );
2671
+ }
2672
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _40 => _40.signal]));
2673
+ const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
2674
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _41 => _41.headers]));
2675
+ headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
2676
+ const rejectDefinitive = (why2) => {
2677
+ this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})` });
2678
+ throw new (0, _chunkFTKVCP6Kcjs.MaxRetriesExceededError)(
2679
+ `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}.`,
2680
+ { ref: nonce }
2681
+ );
2682
+ };
2683
+ const deadline = Date.now() + Math.max(1, Math.floor(accept.maxTimeoutSeconds / 2)) * 1e3;
2684
+ const maxAttempts = Math.min(this.maxRetries, 3);
2685
+ let lastReason = null;
2686
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
2687
+ if (attempt > 0) {
2688
+ if (Date.now() >= deadline) break;
2689
+ await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
2690
+ }
2691
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _42 => _42.signal]));
2692
+ const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
2693
+ if (budget <= 0) break;
2694
+ const timeoutController = new AbortController();
2695
+ const timeoutId = setTimeout(() => timeoutController.abort(), budget);
2696
+ const signal = _optionalChain([init, 'optionalAccess', _43 => _43.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
2697
+ let response;
2698
+ try {
2699
+ response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
2700
+ } catch (err) {
2701
+ throw new (0, _chunkFTKVCP6Kcjs.PaymentTimeoutError)(
2702
+ `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.`,
2703
+ { cause: err, ref: nonce }
2704
+ );
2705
+ } finally {
2706
+ clearTimeout(timeoutId);
2707
+ }
2708
+ const settle = parseSettleResponse(response);
2709
+ if (response.status === 402) {
2710
+ if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the facilitator reported success:false")));
2711
+ lastReason = await _asyncNullishCoalesce(await readInvalidReason(response), async () => ( lastReason));
2712
+ continue;
2713
+ }
2714
+ if (response.ok && !(settle && settle.success === false)) {
2715
+ const receipt = parseReceipt(response);
2716
+ this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
2717
+ const ref = _optionalChain([settle, 'optionalAccess', _44 => _44.transaction]) || _optionalChain([receipt, 'optionalAccess', _45 => _45.transaction]) || `eip3009-nonce:${nonce}`;
2718
+ this.recordSpend(quote, ref);
2719
+ return response;
2720
+ }
2721
+ if (response.status >= 500) {
2722
+ this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
2723
+ return response;
2724
+ }
2725
+ if (settle && settle.success === false) rejectDefinitive(_nullishCoalesce(settle.errorReason, () => ( "the facilitator reported success:false")));
2726
+ this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
2727
+ return response;
2728
+ }
2729
+ const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
2730
+ this.safeEmit({
2731
+ kind: "payment-failed",
2732
+ reason: `exact: 402 after submitting authorization nonce=${nonce} (${why})`
2733
+ });
2734
+ throw new (0, _chunkFTKVCP6Kcjs.MaxRetriesExceededError)(
2735
+ `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}.`,
2736
+ { ref: nonce }
2737
+ );
2738
+ }
2503
2739
  }, _class2);
2740
+ function throwIfAborted(signal) {
2741
+ if (_optionalChain([signal, 'optionalAccess', _46 => _46.aborted])) {
2742
+ throw _nullishCoalesce(signal.reason, () => ( new DOMException("This operation was aborted.", "AbortError")));
2743
+ }
2744
+ }
2504
2745
  function safeBig(s) {
2505
2746
  try {
2506
2747
  return BigInt(s);
@@ -2537,10 +2778,10 @@ function buildFundingHint(options, chainLabel) {
2537
2778
  return `Couldn't fully read your wallet on ${chainLabel} (RPC throttled) \u2014 retry; you may already be able to pay ${target.quote.amountFormatted} ${sym}.`;
2538
2779
  }
2539
2780
  const parts = [];
2540
- if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _36 => _36.shortfall, 'optionalAccess', _37 => _37.token])) {
2781
+ if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _47 => _47.shortfall, 'optionalAccess', _48 => _48.token])) {
2541
2782
  parts.push(`top up ${target.shortfall.token} ${sym}`);
2542
2783
  }
2543
- if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _38 => _38.shortfall, 'optionalAccess', _39 => _39.native])) {
2784
+ if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _49 => _49.shortfall, 'optionalAccess', _50 => _50.native])) {
2544
2785
  parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
2545
2786
  }
2546
2787
  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}.`;
@@ -2554,7 +2795,7 @@ async function planAcross(clients, url, init) {
2554
2795
  const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
2555
2796
  return {
2556
2797
  url,
2557
- network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _40 => _40.accept, 'access', _41 => _41.network]), () => ( live[0].network)),
2798
+ network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _51 => _51.accept, 'access', _52 => _52.network]), () => ( live[0].network)),
2558
2799
  status,
2559
2800
  payable: best !== null,
2560
2801
  best,
@@ -2587,8 +2828,8 @@ function isReplayableBodyInit(value) {
2587
2828
  async function readInvalidReason(response) {
2588
2829
  try {
2589
2830
  const body = await response.clone().json();
2590
- const ext = _optionalChain([body, 'optionalAccess', _42 => _42.extensions]);
2591
- const piprail = _optionalChain([ext, 'optionalAccess', _43 => _43.piprail]);
2831
+ const ext = _optionalChain([body, 'optionalAccess', _53 => _53.extensions]);
2832
+ const piprail = _optionalChain([ext, 'optionalAccess', _54 => _54.piprail]);
2592
2833
  if (piprail && typeof piprail.code === "string") {
2593
2834
  return {
2594
2835
  error: piprail.code,
@@ -2601,8 +2842,16 @@ async function readInvalidReason(response) {
2601
2842
  detail: typeof body.detail === "string" ? body.detail : ""
2602
2843
  };
2603
2844
  }
2845
+ if (body && body.isValid === false && typeof body.invalidReason === "string") {
2846
+ return {
2847
+ error: body.invalidReason,
2848
+ detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
2849
+ };
2850
+ }
2604
2851
  } catch (e24) {
2605
2852
  }
2853
+ const settle = parseSettleResponse(response);
2854
+ if (_optionalChain([settle, 'optionalAccess', _55 => _55.errorReason])) return { error: settle.errorReason, detail: "" };
2606
2855
  return null;
2607
2856
  }
2608
2857
 
@@ -2731,17 +2980,17 @@ function paymentTools(client) {
2731
2980
  },
2732
2981
  {
2733
2982
  name: "piprail_pay_request",
2734
- description: "Fetch an x402 payment-gated URL, automatically paying the required on-chain payment if needed (subject to the spend policy + approval hook). Returns the HTTP status, the response body, and a payment receipt if one settled. If the payment is refused by policy or the approval hook, returns { declined: true, reason } \u2014 no funds moved.",
2983
+ description: "Fetch an x402 payment-gated URL, automatically making the required payment if needed (subject to the spend policy + approval hook). Pays whichever rail the client is configured for \u2014 PipRail's backendless on-chain rail, or, when enabled, the standard `exact` rail (where the buyer signs and the server settles, so no buyer gas). Returns the HTTP status, the response body, and a payment receipt if one settled. If the payment is refused by policy or the approval hook, returns { declined: true, reason } \u2014 no funds moved.",
2735
2984
  annotations: {
2736
2985
  title: "Pay an x402 request",
2737
2986
  readOnlyHint: false,
2738
2987
  // this is the one tool that MOVES FUNDS
2739
2988
  destructiveHint: true,
2740
- // an on-chain payment is value-moving and not reversible
2989
+ // a payment is value-moving and not reversible
2741
2990
  idempotentHint: false,
2742
2991
  // paying twice = two payments
2743
2992
  openWorldHint: true
2744
- // fetches a URL and settles on-chain
2993
+ // fetches a URL and settles a payment
2745
2994
  },
2746
2995
  parameters: {
2747
2996
  type: "object",
@@ -2783,7 +3032,7 @@ function paymentTools(client) {
2783
3032
  receipt: parseReceipt(res)
2784
3033
  };
2785
3034
  } catch (err) {
2786
- if (err instanceof _chunkMDLZJGLYcjs.PaymentDeclinedError) {
3035
+ if (err instanceof _chunkFTKVCP6Kcjs.PaymentDeclinedError) {
2787
3036
  return { declined: true, reason: err.message };
2788
3037
  }
2789
3038
  throw err;
@@ -2947,13 +3196,13 @@ async function settleViaFacilitator(input) {
2947
3196
  try {
2948
3197
  verify = await post(`${base2}/verify`, body, auth);
2949
3198
  } catch (err) {
2950
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
3199
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
2951
3200
  `exact settle (facilitator ${base2}): /verify request failed (${err instanceof Error ? err.message : String(err)}).`,
2952
3201
  { cause: err }
2953
3202
  );
2954
3203
  }
2955
3204
  if (verify.status !== 200) {
2956
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
3205
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
2957
3206
  `exact settle (facilitator ${base2}): /verify returned HTTP ${verify.status} (transport/auth error).`
2958
3207
  );
2959
3208
  }
@@ -2969,13 +3218,13 @@ async function settleViaFacilitator(input) {
2969
3218
  try {
2970
3219
  settle = await post(`${base2}/settle`, body, auth);
2971
3220
  } catch (err) {
2972
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
3221
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
2973
3222
  `exact settle (facilitator ${base2}): /settle request failed (${err instanceof Error ? err.message : String(err)}).`,
2974
3223
  { cause: err }
2975
3224
  );
2976
3225
  }
2977
3226
  if (settle.status !== 200) {
2978
- throw new (0, _chunkMDLZJGLYcjs.SettlementError)(
3227
+ throw new (0, _chunkFTKVCP6Kcjs.SettlementError)(
2979
3228
  `exact settle (facilitator ${base2}): /settle returned HTTP ${settle.status} (transport/auth error).`
2980
3229
  );
2981
3230
  }
@@ -3041,7 +3290,7 @@ function createPaymentGate(options) {
3041
3290
  }
3042
3291
  net.assertValidPayTo(payTo);
3043
3292
  const { asset, decimals, symbol } = net.resolveToken(a.token);
3044
- const amountBase = _chunkMDLZJGLYcjs.parseUnits.call(void 0, a.amount, decimals);
3293
+ const amountBase = _chunkFTKVCP6Kcjs.parseUnits.call(void 0, a.amount, decimals);
3045
3294
  const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
3046
3295
  if (options.exact) spec.exact = await resolveExactRail(net, asset);
3047
3296
  return spec;
@@ -3156,7 +3405,7 @@ function createPaymentGate(options) {
3156
3405
  const specs = await ready();
3157
3406
  const nonce = genNonce();
3158
3407
  const bazaar = options.discovery ? { bazaar: buildBazaarExtension(options.discovery === true ? {} : options.discovery) } : void 0;
3159
- const extensions = { ...bazaar, ..._optionalChain([opts, 'optionalAccess', _44 => _44.extensions]) };
3408
+ const extensions = { ...bazaar, ..._optionalChain([opts, 'optionalAccess', _56 => _56.extensions]) };
3160
3409
  const challenge2 = {
3161
3410
  x402Version: 2,
3162
3411
  resource: {
@@ -3164,7 +3413,7 @@ function createPaymentGate(options) {
3164
3413
  ...options.description ? { description: options.description } : {}
3165
3414
  },
3166
3415
  accepts: buildAccepts(specs, nonce),
3167
- ..._optionalChain([opts, 'optionalAccess', _45 => _45.error]) ? { error: opts.error } : {},
3416
+ ..._optionalChain([opts, 'optionalAccess', _57 => _57.error]) ? { error: opts.error } : {},
3168
3417
  ...Object.keys(extensions).length > 0 ? { extensions } : {}
3169
3418
  };
3170
3419
  return { challenge: challenge2, requiredHeader: buildChallengeHeader(challenge2) };
@@ -3283,7 +3532,9 @@ function createPaymentGate(options) {
3283
3532
  amount: accept.amount,
3284
3533
  payTo: accept.payTo,
3285
3534
  maxTimeoutSeconds: accept.maxTimeoutSeconds,
3286
- extra: { name: accept.extra.name, version: accept.extra.version }
3535
+ // name/version are OPTIONAL on the wire type (a foreign rail may omit them), but the
3536
+ // gate's OWN exact rail always read them on-chain at resolution — so they're present here.
3537
+ extra: { name: _nullishCoalesce(accept.extra.name, () => ( "")), version: _nullishCoalesce(accept.extra.version, () => ( "")) }
3287
3538
  },
3288
3539
  receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
3289
3540
  payerHint: exact.payload.authorization.from
@@ -3321,7 +3572,7 @@ function requirePayment(options) {
3321
3572
  try {
3322
3573
  result = await gate.verify(_nullishCoalesce(req.headers[HEADER_SIGNATURE], () => ( req.headers[HEADER_SIGNATURE_V1])));
3323
3574
  } catch (err) {
3324
- if (err instanceof _chunkMDLZJGLYcjs.SettlementError) {
3575
+ if (err instanceof _chunkFTKVCP6Kcjs.SettlementError) {
3325
3576
  res.status(502);
3326
3577
  res.json({ x402Version: 2, error: "settlement_failed", detail: err.message });
3327
3578
  return;
@@ -3415,4 +3666,7 @@ function normaliseHeader(value) {
3415
3666
 
3416
3667
 
3417
3668
 
3418
- exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkMDLZJGLYcjs.ConfirmationTimeoutError; exports.DIRECTORY_INFO = DIRECTORY_INFO; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.GENERATOR = GENERATOR; exports.HEADER_REQUIRED = HEADER_REQUIRED; exports.HEADER_RESPONSE = HEADER_RESPONSE; exports.HEADER_RESPONSE_V1 = HEADER_RESPONSE_V1; exports.HEADER_SIGNATURE = HEADER_SIGNATURE; exports.HEADER_SIGNATURE_V1 = HEADER_SIGNATURE_V1; exports.InsufficientFundsError = _chunkMDLZJGLYcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkMDLZJGLYcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkMDLZJGLYcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkMDLZJGLYcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkMDLZJGLYcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkMDLZJGLYcjs.NonReplayableBodyError; exports.PaymentDeclinedError = _chunkMDLZJGLYcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkMDLZJGLYcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkMDLZJGLYcjs.PipRailError; exports.RecipientNotReadyError = _chunkMDLZJGLYcjs.RecipientNotReadyError; exports.SettlementError = _chunkMDLZJGLYcjs.SettlementError; exports.UnknownTokenError = _chunkMDLZJGLYcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkMDLZJGLYcjs.UnsupportedNetworkError; exports.WrongChainError = _chunkMDLZJGLYcjs.WrongChainError; exports.WrongFamilyError = _chunkMDLZJGLYcjs.WrongFamilyError; exports.buildBazaarExtension = buildBazaarExtension; exports.buildChallengeHeader = buildChallengeHeader; exports.buildExactAuthorization = buildExactAuthorization; exports.buildOpenApi = buildOpenApi; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSignatureHeader = buildSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.getDirectoryInfo = getDirectoryInfo; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseReceipt = parseReceipt; exports.parseSignatureHeader = parseSignatureHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.readExactDomain = readExactDomain; exports.register402Index = register402Index; exports.registerDriver = registerDriver; exports.registerX402Scan = registerX402Scan; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.searchOpenIndexes = searchOpenIndexes; exports.settleViaFacilitator = settleViaFacilitator; exports.toInsufficientFundsError = _chunkMDLZJGLYcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
3669
+
3670
+
3671
+
3672
+ exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkFTKVCP6Kcjs.ConfirmationTimeoutError; exports.DIRECTORY_INFO = DIRECTORY_INFO; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.GENERATOR = GENERATOR; exports.HEADER_REQUIRED = HEADER_REQUIRED; exports.HEADER_RESPONSE = HEADER_RESPONSE; exports.HEADER_RESPONSE_V1 = HEADER_RESPONSE_V1; exports.HEADER_SIGNATURE = HEADER_SIGNATURE; exports.HEADER_SIGNATURE_V1 = HEADER_SIGNATURE_V1; exports.InsufficientFundsError = _chunkFTKVCP6Kcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkFTKVCP6Kcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkFTKVCP6Kcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkFTKVCP6Kcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkFTKVCP6Kcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkFTKVCP6Kcjs.NonReplayableBodyError; exports.PaymentDeclinedError = _chunkFTKVCP6Kcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkFTKVCP6Kcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkFTKVCP6Kcjs.PipRailError; exports.RecipientNotReadyError = _chunkFTKVCP6Kcjs.RecipientNotReadyError; exports.SettlementError = _chunkFTKVCP6Kcjs.SettlementError; exports.UnknownTokenError = _chunkFTKVCP6Kcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkFTKVCP6Kcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkFTKVCP6Kcjs.UnsupportedSchemeError; exports.WrongChainError = _chunkFTKVCP6Kcjs.WrongChainError; exports.WrongFamilyError = _chunkFTKVCP6Kcjs.WrongFamilyError; exports.buildBazaarExtension = buildBazaarExtension; exports.buildChallengeHeader = buildChallengeHeader; exports.buildExactAuthorization = buildExactAuthorization; exports.buildExactSignatureHeader = buildExactSignatureHeader; exports.buildOpenApi = buildOpenApi; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSignatureHeader = buildSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.getDirectoryInfo = getDirectoryInfo; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseReceipt = parseReceipt; exports.parseSettleResponse = parseSettleResponse; exports.parseSignatureHeader = parseSignatureHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.readExactDomain = readExactDomain; exports.register402Index = register402Index; exports.registerDriver = registerDriver; exports.registerX402Scan = registerX402Scan; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.searchOpenIndexes = searchOpenIndexes; exports.settleViaFacilitator = settleViaFacilitator; exports.toInsufficientFundsError = _chunkFTKVCP6Kcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;