@piprail/sdk 1.15.0 → 1.17.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/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
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; } var _class; var _class2;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
2
2
 
3
3
 
4
4
 
@@ -136,9 +136,15 @@ var CHAINS = {
136
136
  bnb: {
137
137
  chain: _chains.bsc,
138
138
  tokens: {
139
- // Binance-Peg tokens on BNB Chain are 18 decimals (not the usual 6).
139
+ // Binance-Peg tokens on BNB Chain are 18 decimals (not the usual 6). USDC/USDT here are
140
+ // Binance-Peg (NOT EIP-3009) → the `exact` rail uses Permit2.
140
141
  USDC: { address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", decimals: 18, symbol: "USDC" },
141
- USDT: { address: "0x55d398326f99059fF775485246999027B3197955", decimals: 18, symbol: "USDT" }
142
+ USDT: { address: "0x55d398326f99059fF775485246999027B3197955", decimals: 18, symbol: "USDT" },
143
+ // FDUSD + USD1 ARE EIP-3009 (transferWithAuthorization) → the `exact` rail uses the gasless,
144
+ // no-Permit2-approve path. Both hardcode EIP-712 domain version "1" (no version() — the SDK
145
+ // derives it from DOMAIN_SEPARATOR). Verified on-chain (symbol/decimals/domain match).
146
+ FDUSD: { address: "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409", decimals: 18, symbol: "FDUSD" },
147
+ USD1: { address: "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d", decimals: 18, symbol: "USD1" }
142
148
  }
143
149
  },
144
150
  avalanche: {
@@ -498,6 +504,9 @@ function sumTransfersTo(logs, asset, payTo) {
498
504
 
499
505
 
500
506
 
507
+
508
+
509
+
501
510
  var EXACT_NETWORK_SLUGS = {
502
511
  ethereum: 1,
503
512
  base: 8453,
@@ -505,7 +514,9 @@ var EXACT_NETWORK_SLUGS = {
505
514
  arbitrum: 42161,
506
515
  optimism: 10,
507
516
  polygon: 137,
508
- avalanche: 43114
517
+ avalanche: 43114,
518
+ bnb: 56,
519
+ bsc: 56
509
520
  };
510
521
  function chainIdForExactNetwork(slug) {
511
522
  return _nullishCoalesce(EXACT_NETWORK_SLUGS[slug], () => ( null));
@@ -690,10 +701,25 @@ var eip3009Abi = [
690
701
  outputs: [{ type: "bool" }]
691
702
  },
692
703
  { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
693
- { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] }
704
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
705
+ // EIP-3009 tokens that DON'T expose version() still expose DOMAIN_SEPARATOR — we match it to
706
+ // DERIVE the hardcoded domain version (e.g. FDUSD / USD1 on BNB Chain both use "1").
707
+ { type: "function", name: "DOMAIN_SEPARATOR", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] }
694
708
  ];
695
709
  var ZERO_ADDR = "0x0000000000000000000000000000000000000000";
696
710
  var ZERO_NONCE = `0x${"00".repeat(32)}`;
711
+ var EIP712_DOMAIN_TYPEHASH = _viem.keccak256.call(void 0,
712
+ _viem.toHex.call(void 0, "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
713
+ );
714
+ var EXACT_DOMAIN_VERSION_CANDIDATES = ["1", "2"];
715
+ function eip712DomainSeparator(name, version, chainId, verifyingContract) {
716
+ return _viem.keccak256.call(void 0,
717
+ _viem.encodeAbiParameters.call(void 0,
718
+ [{ type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "uint256" }, { type: "address" }],
719
+ [EIP712_DOMAIN_TYPEHASH, _viem.keccak256.call(void 0, _viem.toHex.call(void 0, name)), _viem.keccak256.call(void 0, _viem.toHex.call(void 0, version)), BigInt(chainId), verifyingContract]
720
+ )
721
+ );
722
+ }
697
723
  async function readExactDomain(publicClient, asset) {
698
724
  if (asset === "native") return null;
699
725
  let token;
@@ -702,12 +728,10 @@ async function readExactDomain(publicClient, asset) {
702
728
  } catch (e7) {
703
729
  return null;
704
730
  }
731
+ let name;
705
732
  try {
706
- const [name, version] = await Promise.all([
733
+ const [n] = await Promise.all([
707
734
  publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "name" }),
708
- publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "version" }),
709
- // The EIP-3009 probe: this view exists only on EIP-3009 tokens; it reverts on
710
- // a plain ERC-20 / USDT, marking the token as not exact-payable.
711
735
  publicClient.readContract({
712
736
  address: token,
713
737
  abi: eip3009Abi,
@@ -715,11 +739,34 @@ async function readExactDomain(publicClient, asset) {
715
739
  args: [ZERO_ADDR, ZERO_NONCE]
716
740
  })
717
741
  ]);
718
- if (typeof name !== "string" || typeof version !== "string" || !name || !version) return null;
719
- return { name, version };
742
+ if (typeof n !== "string" || !n) return null;
743
+ name = n;
720
744
  } catch (e8) {
721
745
  return null;
722
746
  }
747
+ try {
748
+ const version = await publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "version" });
749
+ if (typeof version === "string" && version) return { name, version };
750
+ } catch (e9) {
751
+ }
752
+ return deriveExactDomainVersion(publicClient, token, name);
753
+ }
754
+ async function deriveExactDomainVersion(publicClient, token, name) {
755
+ let onchain;
756
+ let chainId;
757
+ try {
758
+ onchain = await publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "DOMAIN_SEPARATOR" });
759
+ chainId = await _asyncNullishCoalesce(await _asyncOptionalChain([publicClient, 'access', async _6 => _6.chain, 'optionalAccess', async _7 => _7.id]), async () => ( await publicClient.getChainId()));
760
+ } catch (e10) {
761
+ return null;
762
+ }
763
+ const target = onchain.toLowerCase();
764
+ for (const version of EXACT_DOMAIN_VERSION_CANDIDATES) {
765
+ if (eip712DomainSeparator(name, version, chainId, token).toLowerCase() === target) {
766
+ return { name, version };
767
+ }
768
+ }
769
+ return null;
723
770
  }
724
771
  function shorten(msg) {
725
772
  const oneLine = msg.replace(/\s+/g, " ").trim();
@@ -765,7 +812,7 @@ async function verifyAndSettleExactEvm(input) {
765
812
  let fromCode;
766
813
  try {
767
814
  fromCode = await publicClient.getCode({ address: from });
768
- } catch (e9) {
815
+ } catch (e11) {
769
816
  return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
770
817
  }
771
818
  const isContractWallet = Boolean(fromCode && fromCode !== "0x");
@@ -801,7 +848,7 @@ async function verifyAndSettleExactEvm(input) {
801
848
  if (used) {
802
849
  return { ok: false, error: "tx_already_used", detail: `Authorization nonce ${nonce} already used or canceled on-chain.` };
803
850
  }
804
- } catch (e10) {
851
+ } catch (e12) {
805
852
  return { ok: false, error: "tx_not_found", detail: "Could not read authorizationState (transient RPC) \u2014 retry." };
806
853
  }
807
854
  const baseArgs = [from, to, value, validAfter, validBefore, nonce];
@@ -874,6 +921,346 @@ async function verifyAndSettleExactEvm(input) {
874
921
  };
875
922
  }
876
923
 
924
+ // src/drivers/evm/permit2.ts
925
+
926
+
927
+
928
+
929
+
930
+
931
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
932
+ var X402_EXACT_PERMIT2_PROXY = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001";
933
+ var PERMIT2_WITNESS_TYPES = {
934
+ PermitWitnessTransferFrom: [
935
+ { name: "permitted", type: "TokenPermissions" },
936
+ { name: "spender", type: "address" },
937
+ { name: "nonce", type: "uint256" },
938
+ { name: "deadline", type: "uint256" },
939
+ { name: "witness", type: "Witness" }
940
+ ],
941
+ TokenPermissions: [
942
+ { name: "token", type: "address" },
943
+ { name: "amount", type: "uint256" }
944
+ ],
945
+ Witness: [
946
+ { name: "to", type: "address" },
947
+ { name: "validAfter", type: "uint256" }
948
+ ]
949
+ };
950
+ var x402Permit2ProxyAbi = [
951
+ {
952
+ type: "function",
953
+ name: "settle",
954
+ stateMutability: "nonpayable",
955
+ outputs: [],
956
+ inputs: [
957
+ {
958
+ name: "permit",
959
+ type: "tuple",
960
+ components: [
961
+ {
962
+ name: "permitted",
963
+ type: "tuple",
964
+ components: [
965
+ { name: "token", type: "address" },
966
+ { name: "amount", type: "uint256" }
967
+ ]
968
+ },
969
+ { name: "nonce", type: "uint256" },
970
+ { name: "deadline", type: "uint256" }
971
+ ]
972
+ },
973
+ { name: "owner", type: "address" },
974
+ {
975
+ name: "witness",
976
+ type: "tuple",
977
+ components: [
978
+ { name: "to", type: "address" },
979
+ { name: "validAfter", type: "uint256" }
980
+ ]
981
+ },
982
+ { name: "signature", type: "bytes" }
983
+ ]
984
+ }
985
+ ];
986
+ var permit2NonceBitmapAbi = [
987
+ {
988
+ type: "function",
989
+ name: "nonceBitmap",
990
+ stateMutability: "view",
991
+ inputs: [
992
+ { name: "owner", type: "address" },
993
+ { name: "word", type: "uint256" }
994
+ ],
995
+ outputs: [{ type: "uint256" }]
996
+ }
997
+ ];
998
+ function shorten2(msg) {
999
+ const oneLine = msg.replace(/\s+/g, " ").trim();
1000
+ return oneLine.length > 200 ? `${oneLine.slice(0, 200)}\u2026` : oneLine;
1001
+ }
1002
+ function randomPermit2Nonce() {
1003
+ const g = globalThis.crypto;
1004
+ if (!_optionalChain([g, 'optionalAccess', _8 => _8.getRandomValues])) {
1005
+ throw new (0, _chunkPA6YD3HLcjs.UnsupportedSchemeError)(
1006
+ "this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the permit2 rail needs a CSPRNG nonce."
1007
+ );
1008
+ }
1009
+ const raw = new Uint8Array(32);
1010
+ g.getRandomValues(raw);
1011
+ return BigInt(`0x${[...raw].map((b) => b.toString(16).padStart(2, "0")).join("")}`);
1012
+ }
1013
+ async function ensurePermit2Allowance(input) {
1014
+ const { publicClient, walletClient, account, chain, token, amount } = input;
1015
+ let allowance;
1016
+ try {
1017
+ allowance = await publicClient.readContract({
1018
+ address: token,
1019
+ abi: _viem.erc20Abi,
1020
+ functionName: "allowance",
1021
+ args: [account.address, PERMIT2_ADDRESS]
1022
+ });
1023
+ } catch (err) {
1024
+ throw new (0, _chunkPA6YD3HLcjs.UnsupportedSchemeError)(
1025
+ `permit2: couldn't read the Permit2 allowance for ${token} (${shorten2(err instanceof Error ? err.message : String(err))}).`
1026
+ );
1027
+ }
1028
+ if (allowance >= amount) return void 0;
1029
+ try {
1030
+ const hash = await walletClient.writeContract({
1031
+ account,
1032
+ chain,
1033
+ address: token,
1034
+ abi: _viem.erc20Abi,
1035
+ functionName: "approve",
1036
+ args: [PERMIT2_ADDRESS, _viem.maxUint256]
1037
+ });
1038
+ await publicClient.waitForTransactionReceipt({ hash, confirmations: 1 });
1039
+ return hash;
1040
+ } catch (err) {
1041
+ throw _nullishCoalesce(_chunkPA6YD3HLcjs.toInsufficientFundsError.call(void 0, err), () => ( new (0, _chunkPA6YD3HLcjs.SettlementError)(
1042
+ `permit2: the one-time Permit2 approval for ${token} failed to broadcast (${shorten2(err instanceof Error ? err.message : String(err))}).`,
1043
+ { cause: err }
1044
+ )));
1045
+ }
1046
+ }
1047
+ async function payPermit2Evm(input) {
1048
+ const { publicClient, walletClient, account, chainId, chain, accept } = input;
1049
+ let code;
1050
+ try {
1051
+ code = await publicClient.getCode({ address: account.address });
1052
+ } catch (e13) {
1053
+ code = void 0;
1054
+ }
1055
+ if (code && code !== "0x") {
1056
+ throw new (0, _chunkPA6YD3HLcjs.UnsupportedSchemeError)(
1057
+ `permit2 buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
1058
+ );
1059
+ }
1060
+ const token = _viem.getAddress.call(void 0, accept.asset);
1061
+ const payTo = _viem.getAddress.call(void 0, accept.payTo);
1062
+ const value = BigInt(accept.amount);
1063
+ const approvalTx = await ensurePermit2Allowance({
1064
+ publicClient,
1065
+ walletClient,
1066
+ account,
1067
+ chain,
1068
+ token,
1069
+ amount: value
1070
+ });
1071
+ const nonce = randomPermit2Nonce();
1072
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + accept.maxTimeoutSeconds);
1073
+ const validAfter = 0n;
1074
+ const spender = _viem.getAddress.call(void 0, X402_EXACT_PERMIT2_PROXY);
1075
+ const from = account.address;
1076
+ const signature = await walletClient.signTypedData({
1077
+ account,
1078
+ domain: { name: "Permit2", chainId, verifyingContract: PERMIT2_ADDRESS },
1079
+ types: PERMIT2_WITNESS_TYPES,
1080
+ primaryType: "PermitWitnessTransferFrom",
1081
+ message: {
1082
+ permitted: { token, amount: value },
1083
+ spender,
1084
+ nonce,
1085
+ deadline,
1086
+ witness: { to: payTo, validAfter }
1087
+ }
1088
+ });
1089
+ const permit2Authorization = {
1090
+ permitted: { token, amount: value.toString() },
1091
+ from,
1092
+ spender,
1093
+ nonce: nonce.toString(),
1094
+ deadline: deadline.toString(),
1095
+ witness: { to: payTo, validAfter: validAfter.toString() }
1096
+ };
1097
+ return {
1098
+ payload: { signature, permit2Authorization },
1099
+ payerFrom: from,
1100
+ nonce: nonce.toString(),
1101
+ ...approvalTx ? { approvalTx } : {}
1102
+ };
1103
+ }
1104
+ async function verifyAndSettlePermit2Evm(input) {
1105
+ const { publicClient, walletClient, account, chain, payload, accept } = input;
1106
+ const token = _viem.getAddress.call(void 0, accept.asset);
1107
+ const payTo = _viem.getAddress.call(void 0, accept.payTo);
1108
+ const requiredAmount = BigInt(accept.amount);
1109
+ const proxy = _viem.getAddress.call(void 0, X402_EXACT_PERMIT2_PROXY);
1110
+ let from;
1111
+ let spender;
1112
+ let permittedToken;
1113
+ let witnessTo;
1114
+ let permittedAmount;
1115
+ let nonce;
1116
+ let deadline;
1117
+ let validAfter;
1118
+ const signature = payload.signature;
1119
+ try {
1120
+ const pa = payload.permit2Authorization;
1121
+ from = _viem.getAddress.call(void 0, pa.from);
1122
+ spender = _viem.getAddress.call(void 0, pa.spender);
1123
+ permittedToken = _viem.getAddress.call(void 0, pa.permitted.token);
1124
+ witnessTo = _viem.getAddress.call(void 0, pa.witness.to);
1125
+ permittedAmount = BigInt(pa.permitted.amount);
1126
+ nonce = BigInt(pa.nonce);
1127
+ deadline = BigInt(pa.deadline);
1128
+ validAfter = BigInt(pa.witness.validAfter);
1129
+ if (!/^0x[0-9a-fA-F]+$/.test(signature)) throw new Error("signature must be hex");
1130
+ } catch (err) {
1131
+ return {
1132
+ ok: false,
1133
+ error: "signature_invalid",
1134
+ detail: `Malformed permit2 authorization: ${err instanceof Error ? err.message : String(err)}.`
1135
+ };
1136
+ }
1137
+ if (witnessTo !== payTo) {
1138
+ return { ok: false, error: "wrong_recipient", detail: `Authorization pays witness.to ${witnessTo}, not ${payTo}.` };
1139
+ }
1140
+ if (permittedToken !== token) {
1141
+ return { ok: false, error: "signature_invalid", detail: `Authorization permits token ${permittedToken}, not the rail's ${token}.` };
1142
+ }
1143
+ if (spender !== proxy) {
1144
+ return { ok: false, error: "signature_invalid", detail: `Authorization spender ${spender} is not the x402ExactPermit2Proxy ${proxy}; it can't be settled here.` };
1145
+ }
1146
+ if (permittedAmount < requiredAmount) {
1147
+ return { ok: false, error: "amount_too_low", detail: `Permitted ${permittedAmount}, required ${requiredAmount}.` };
1148
+ }
1149
+ const now = BigInt(Math.floor(Date.now() / 1e3));
1150
+ if (deadline <= now) {
1151
+ return { ok: false, error: "payment_expired", detail: `Permit2 deadline ${deadline} <= now ${now}.` };
1152
+ }
1153
+ let fromCode;
1154
+ try {
1155
+ fromCode = await publicClient.getCode({ address: from });
1156
+ } catch (e14) {
1157
+ return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
1158
+ }
1159
+ if (!(fromCode && fromCode !== "0x")) {
1160
+ let recovered;
1161
+ try {
1162
+ recovered = await _viem.recoverTypedDataAddress.call(void 0, {
1163
+ domain: { name: "Permit2", chainId: chain.id, verifyingContract: PERMIT2_ADDRESS },
1164
+ types: PERMIT2_WITNESS_TYPES,
1165
+ primaryType: "PermitWitnessTransferFrom",
1166
+ message: {
1167
+ permitted: { token: permittedToken, amount: permittedAmount },
1168
+ spender,
1169
+ nonce,
1170
+ deadline,
1171
+ witness: { to: witnessTo, validAfter }
1172
+ },
1173
+ signature
1174
+ });
1175
+ } catch (err) {
1176
+ return { ok: false, error: "signature_invalid", detail: `Not a valid EIP-712 signature: ${shorten2(err instanceof Error ? err.message : String(err))}.` };
1177
+ }
1178
+ if (recovered !== from) {
1179
+ return { ok: false, error: "signature_invalid", detail: `Signature recovered to ${recovered}, not the authorizer ${from}.` };
1180
+ }
1181
+ }
1182
+ try {
1183
+ const word = nonce >> 8n;
1184
+ const bit = nonce & 0xffn;
1185
+ const bitmap = await publicClient.readContract({
1186
+ address: PERMIT2_ADDRESS,
1187
+ abi: permit2NonceBitmapAbi,
1188
+ functionName: "nonceBitmap",
1189
+ args: [from, word]
1190
+ });
1191
+ if ((bitmap >> bit & 1n) === 1n) {
1192
+ return { ok: false, error: "tx_already_used", detail: `Permit2 nonce ${nonce} already used or invalidated for ${from}.` };
1193
+ }
1194
+ } catch (e15) {
1195
+ return { ok: false, error: "tx_not_found", detail: "Could not read the Permit2 nonce bitmap (transient RPC) \u2014 retry." };
1196
+ }
1197
+ const settleArgs = [
1198
+ { permitted: { token: permittedToken, amount: permittedAmount }, nonce, deadline },
1199
+ from,
1200
+ { to: witnessTo, validAfter },
1201
+ signature
1202
+ ];
1203
+ try {
1204
+ await publicClient.simulateContract({
1205
+ account,
1206
+ address: proxy,
1207
+ abi: x402Permit2ProxyAbi,
1208
+ functionName: "settle",
1209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1210
+ args: settleArgs
1211
+ });
1212
+ } catch (err) {
1213
+ const msg = err instanceof Error ? err.message : String(err);
1214
+ if (/nonce|invalidated|used/i.test(msg)) return { ok: false, error: "tx_already_used", detail: "Permit2 nonce is used or invalidated." };
1215
+ if (/expired|deadline|too early|not yet/i.test(msg)) return { ok: false, error: "payment_expired", detail: shorten2(msg) };
1216
+ if (/signature/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten2(msg) };
1217
+ return { ok: false, error: "tx_reverted", detail: `permit2 settle would revert: ${shorten2(msg)}` };
1218
+ }
1219
+ let txHash;
1220
+ try {
1221
+ txHash = await walletClient.writeContract({
1222
+ account,
1223
+ chain,
1224
+ address: proxy,
1225
+ abi: x402Permit2ProxyAbi,
1226
+ functionName: "settle",
1227
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1228
+ args: settleArgs
1229
+ });
1230
+ } catch (err) {
1231
+ throw new (0, _chunkPA6YD3HLcjs.SettlementError)(
1232
+ `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.`,
1233
+ { cause: err }
1234
+ );
1235
+ }
1236
+ try {
1237
+ const confirmations = _nullishCoalesce(accept.extra.minConfirmations, () => ( 1));
1238
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, confirmations });
1239
+ if (receipt.status !== "success") {
1240
+ return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
1241
+ }
1242
+ } catch (err) {
1243
+ throw new (0, _chunkPA6YD3HLcjs.SettlementError)(
1244
+ `permit2 settle: broadcast ${txHash} but couldn't confirm it (${shorten2(err instanceof Error ? err.message : String(err))}).`,
1245
+ { cause: err }
1246
+ );
1247
+ }
1248
+ return {
1249
+ ok: true,
1250
+ receipt: {
1251
+ scheme: "exact",
1252
+ success: true,
1253
+ network: accept.network,
1254
+ transaction: txHash,
1255
+ asset: accept.asset,
1256
+ amount: accept.amount,
1257
+ payer: from,
1258
+ payTo: accept.payTo,
1259
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1260
+ }
1261
+ };
1262
+ }
1263
+
877
1264
  // src/x402.ts
878
1265
  var HEADER_REQUIRED = "payment-required";
879
1266
  var HEADER_SIGNATURE = "payment-signature";
@@ -903,7 +1290,7 @@ function decodeBase64(b64) {
903
1290
  function fromBase64Json(b64) {
904
1291
  try {
905
1292
  return JSON.parse(decodeBase64(b64));
906
- } catch (e11) {
1293
+ } catch (e16) {
907
1294
  return null;
908
1295
  }
909
1296
  }
@@ -940,7 +1327,7 @@ async function parseChallenge(response) {
940
1327
  try {
941
1328
  const body = await response.clone().json();
942
1329
  if (isValidChallenge(body)) return body;
943
- } catch (e12) {
1330
+ } catch (e17) {
944
1331
  }
945
1332
  return null;
946
1333
  }
@@ -968,7 +1355,7 @@ function parseSignatureHeader(value) {
968
1355
  if (!parsed || typeof parsed !== "object") return null;
969
1356
  const v = parsed;
970
1357
  const accepted = v.accepted;
971
- const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _6 => _6.scheme]), () => ( v.scheme));
1358
+ const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _9 => _9.scheme]), () => ( v.scheme));
972
1359
  if (scheme !== "onchain-proof") return null;
973
1360
  const payload = v.payload;
974
1361
  if (!payload || typeof payload.txHash !== "string" || typeof payload.nonce !== "string") {
@@ -981,30 +1368,45 @@ function parseExactPaymentHeader(value) {
981
1368
  if (!parsed || typeof parsed !== "object") return null;
982
1369
  const v = parsed;
983
1370
  const accepted = _nullishCoalesce(v.accepted, () => ( null));
984
- const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _7 => _7.scheme]), () => ( v.scheme));
1371
+ const scheme = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _10 => _10.scheme]), () => ( v.scheme));
985
1372
  if (scheme !== "exact") return null;
986
- const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _8 => _8.network]), () => ( v.network));
1373
+ const network = _nullishCoalesce(_optionalChain([accepted, 'optionalAccess', _11 => _11.network]), () => ( v.network));
987
1374
  if (typeof network !== "string") return null;
988
1375
  const payload = v.payload;
989
1376
  if (!payload || typeof payload !== "object") return null;
990
1377
  const signature = payload.signature;
991
- const authorization = payload.authorization;
992
- if (typeof signature !== "string" || !authorization || typeof authorization !== "object") return null;
993
- for (const k of ["from", "to", "value", "validAfter", "validBefore", "nonce"]) {
994
- if (typeof authorization[k] !== "string") return null;
995
- }
1378
+ if (typeof signature !== "string") return null;
996
1379
  const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
997
1380
  const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
998
- return {
999
- x402Version,
1000
- network,
1001
- ...asset ? { asset } : {},
1002
- payload: {
1003
- signature,
1004
- authorization
1005
- },
1006
- raw: v
1007
- };
1381
+ const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
1382
+ const authorization = payload.authorization;
1383
+ if (authorization && typeof authorization === "object") {
1384
+ for (const k of ["from", "to", "value", "validAfter", "validBefore", "nonce"]) {
1385
+ if (typeof authorization[k] !== "string") return null;
1386
+ }
1387
+ return {
1388
+ ...base2,
1389
+ method: "eip3009",
1390
+ payload: { signature, authorization }
1391
+ };
1392
+ }
1393
+ const p2 = payload.permit2Authorization;
1394
+ if (p2 && typeof p2 === "object") {
1395
+ const permitted = p2.permitted;
1396
+ const witness = p2.witness;
1397
+ if (!permitted || typeof permitted !== "object" || !witness || typeof witness !== "object") return null;
1398
+ if (typeof permitted.token !== "string" || typeof permitted.amount !== "string") return null;
1399
+ if (typeof witness.to !== "string" || typeof witness.validAfter !== "string") return null;
1400
+ for (const k of ["from", "spender", "nonce", "deadline"]) {
1401
+ if (typeof p2[k] !== "string") return null;
1402
+ }
1403
+ return {
1404
+ ...base2,
1405
+ method: "permit2",
1406
+ payload: { signature, permit2Authorization: p2 }
1407
+ };
1408
+ }
1409
+ return null;
1008
1410
  }
1009
1411
  function isValidChallenge(value) {
1010
1412
  if (!value || typeof value !== "object") return false;
@@ -1040,7 +1442,7 @@ var evmDriver = {
1040
1442
  let resolved;
1041
1443
  try {
1042
1444
  resolved = resolveChain(opts.chain, opts.rpcUrl);
1043
- } catch (e13) {
1445
+ } catch (e18) {
1044
1446
  return null;
1045
1447
  }
1046
1448
  return makeEvmNetwork(resolved);
@@ -1096,7 +1498,7 @@ function makeEvmNetwork(resolved) {
1096
1498
  let normalized;
1097
1499
  try {
1098
1500
  normalized = _viem.getAddress.call(void 0, asset);
1099
- } catch (e14) {
1501
+ } catch (e19) {
1100
1502
  return null;
1101
1503
  }
1102
1504
  for (const info of Object.values(resolved.tokens)) {
@@ -1157,12 +1559,13 @@ function makeEvmNetwork(resolved) {
1157
1559
  async estimateCost(accept) {
1158
1560
  const { decimals, symbol } = resolved.chain.nativeCurrency;
1159
1561
  if (accept.scheme === "exact") {
1562
+ const permit2 = accept.extra.assetTransferMethod === "permit2";
1160
1563
  return _chunkPA6YD3HLcjs.nativeCost.call(void 0, {
1161
1564
  symbol,
1162
1565
  decimals,
1163
1566
  fee: 0n,
1164
1567
  basis: "estimated",
1165
- detail: "gasless \u2014 the server/facilitator settles the signed authorization"
1568
+ detail: permit2 ? "gasless after a one-time Permit2 approval; the server/facilitator settles the signed authorization" : "gasless \u2014 the server/facilitator settles the signed authorization"
1166
1569
  });
1167
1570
  }
1168
1571
  const gasLimit = accept.asset === "native" ? 21000n : 65000n;
@@ -1175,7 +1578,7 @@ function makeEvmNetwork(resolved) {
1175
1578
  basis: "estimated",
1176
1579
  detail: `~${gasLimit} gas @ ${gasPrice} wei/gas`
1177
1580
  });
1178
- } catch (e15) {
1581
+ } catch (e20) {
1179
1582
  const gasPrice = 5000000000n;
1180
1583
  return _chunkPA6YD3HLcjs.nativeCost.call(void 0, {
1181
1584
  symbol,
@@ -1198,7 +1601,7 @@ function makeEvmNetwork(resolved) {
1198
1601
  functionName: "balanceOf",
1199
1602
  args: [owner]
1200
1603
  });
1201
- } catch (e16) {
1604
+ } catch (e21) {
1202
1605
  token = null;
1203
1606
  }
1204
1607
  return { token, native };
@@ -1226,12 +1629,24 @@ function makeEvmNetwork(resolved) {
1226
1629
  minConfirmations: accept.extra.minConfirmations
1227
1630
  });
1228
1631
  },
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.
1632
+ // Standard x402 `exact` rail, BUYER side — EVM only. Routes on the rail's
1633
+ // `assetTransferMethod`: `permit2` (any ERC-20 e.g. Binance-Peg USDC on BNB, signs a
1634
+ // Permit2 witness transfer + lazily does the one-time approval) or `eip3009` (re-derives
1635
+ // the token's EIP-712 domain on-chain + signs transferWithAuthorization). Never broadcasts.
1636
+ // Throws UnsupportedSchemeError for a contract signer (or a non-EIP-3009 token on the eip3009 path).
1233
1637
  async payExact(wallet, accept) {
1234
1638
  const a = wallet._native;
1639
+ if (accept.extra.assetTransferMethod === "permit2") {
1640
+ const { payload: payload2, payerFrom: payerFrom2, nonce: nonce2 } = await payPermit2Evm({
1641
+ publicClient,
1642
+ walletClient: a.walletClient,
1643
+ account: a.account,
1644
+ chainId: resolved.chainId,
1645
+ chain: resolved.chain,
1646
+ accept
1647
+ });
1648
+ return { payload: payload2, accepted: accept, payerFrom: payerFrom2, nonce: nonce2 };
1649
+ }
1235
1650
  const { payload, payerFrom, nonce } = await payExactEvm({
1236
1651
  publicClient,
1237
1652
  walletClient: a.walletClient,
@@ -1247,6 +1662,16 @@ function makeEvmNetwork(resolved) {
1247
1662
  },
1248
1663
  async settleExactSelf({ relayer, payload, accept }) {
1249
1664
  const a = relayer._native;
1665
+ if ("permit2Authorization" in payload) {
1666
+ return verifyAndSettlePermit2Evm({
1667
+ publicClient,
1668
+ walletClient: a.walletClient,
1669
+ account: a.account,
1670
+ chain: resolved.chain,
1671
+ payload,
1672
+ accept
1673
+ });
1674
+ }
1250
1675
  return verifyAndSettleExactEvm({
1251
1676
  publicClient,
1252
1677
  walletClient: a.walletClient,
@@ -1486,7 +1911,7 @@ async function searchOpenIndexes(opts = {}) {
1486
1911
  async function safeSearch(run) {
1487
1912
  try {
1488
1913
  return await run();
1489
- } catch (e17) {
1914
+ } catch (e22) {
1490
1915
  return [];
1491
1916
  }
1492
1917
  }
@@ -1592,7 +2017,7 @@ async function register402Index(input) {
1592
2017
  if (res.ok) {
1593
2018
  const body = await res.json().catch(() => ({}));
1594
2019
  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";
2020
+ const live = _optionalChain([body, 'access', _12 => _12.service, 'optionalAccess', _13 => _13.status]) === "active";
1596
2021
  return {
1597
2022
  source: "402index",
1598
2023
  ok: true,
@@ -1619,7 +2044,7 @@ async function readIndexError(res) {
1619
2044
  (p) => typeof p === "string" && p.length > 0
1620
2045
  );
1621
2046
  return parts.length ? [...new Set(parts)].join(" \u2014 ") : void 0;
1622
- } catch (e18) {
2047
+ } catch (e23) {
1623
2048
  return void 0;
1624
2049
  }
1625
2050
  }
@@ -1729,11 +2154,11 @@ async function readSiwxInfo(res) {
1729
2154
  try {
1730
2155
  const body = await res.json();
1731
2156
  const ext = body.extensions;
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]))) {
2157
+ const siwx = _optionalChain([ext, 'optionalAccess', _14 => _14["sign-in-with-x"]]);
2158
+ const info = _nullishCoalesce(_optionalChain([siwx, 'optionalAccess', _15 => _15.info]), () => ( siwx));
2159
+ if (info && info.chainId == null && Array.isArray(_optionalChain([siwx, 'optionalAccess', _16 => _16.supportedChains]))) {
1735
2160
  const evm = siwx.supportedChains.find(
1736
- (c) => typeof _optionalChain([c, 'optionalAccess', _14 => _14.chainId]) === "string" && c.chainId.startsWith("eip155:")
2161
+ (c) => typeof _optionalChain([c, 'optionalAccess', _17 => _17.chainId]) === "string" && c.chainId.startsWith("eip155:")
1737
2162
  );
1738
2163
  if (evm && typeof evm.chainId === "string") info.chainId = evm.chainId;
1739
2164
  }
@@ -1741,7 +2166,7 @@ async function readSiwxInfo(res) {
1741
2166
  return info;
1742
2167
  }
1743
2168
  return null;
1744
- } catch (e19) {
2169
+ } catch (e24) {
1745
2170
  return null;
1746
2171
  }
1747
2172
  }
@@ -1789,7 +2214,7 @@ function mapRails(accepts) {
1789
2214
  }
1790
2215
  function matchesQuery(r, query) {
1791
2216
  const q = query.toLowerCase();
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)));
2217
+ return r.resource.toLowerCase().includes(q) || (_nullishCoalesce(_optionalChain([r, 'access', _18 => _18.name, 'optionalAccess', _19 => _19.toLowerCase, 'call', _20 => _20(), 'access', _21 => _21.includes, 'call', _22 => _22(q)]), () => ( false))) || (_nullishCoalesce(_optionalChain([r, 'access', _23 => _23.description, 'optionalAccess', _24 => _24.toLowerCase, 'call', _25 => _25(), 'access', _26 => _26.includes, 'call', _27 => _27(q)]), () => ( false)));
1793
2218
  }
1794
2219
  function pickString(o, ...keys) {
1795
2220
  for (const k of keys) {
@@ -1822,7 +2247,7 @@ function hostOf(url) {
1822
2247
  try {
1823
2248
  const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(url) ? url : `https://${url}`;
1824
2249
  return new URL(withScheme).hostname || url;
1825
- } catch (e20) {
2250
+ } catch (e25) {
1826
2251
  return url;
1827
2252
  }
1828
2253
  }
@@ -1970,7 +2395,7 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
1970
2395
  }
1971
2396
  /** Running total (base units) already spent on this (network, asset). */
1972
2397
  totalFor(network, asset) {
1973
- return _nullishCoalesce(_optionalChain([this, 'access', _25 => _25.buckets, 'access', _26 => _26.get, 'call', _27 => _27(keyFor(network, asset)), 'optionalAccess', _28 => _28.total]), () => ( 0n));
2398
+ return _nullishCoalesce(_optionalChain([this, 'access', _28 => _28.buckets, 'access', _29 => _29.get, 'call', _30 => _30(keyFor(network, asset)), 'optionalAccess', _31 => _31.total]), () => ( 0n));
1974
2399
  }
1975
2400
  /**
1976
2401
  * Sum of base-unit amounts for (network, asset) whose record `at` (ISO
@@ -2083,7 +2508,7 @@ var PipRailClient = (_class2 = class {
2083
2508
  safeEmit(event) {
2084
2509
  try {
2085
2510
  this.onEvent(event);
2086
- } catch (e21) {
2511
+ } catch (e26) {
2087
2512
  }
2088
2513
  }
2089
2514
  /** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
@@ -2113,7 +2538,7 @@ var PipRailClient = (_class2 = class {
2113
2538
  * as-is) or a plain object (serialised as JSON).
2114
2539
  */
2115
2540
  post(url, body, init) {
2116
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _29 => _29.headers]));
2541
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _32 => _32.headers]));
2117
2542
  let payload;
2118
2543
  if (body === void 0 || body === null) {
2119
2544
  payload = void 0;
@@ -2144,7 +2569,7 @@ var PipRailClient = (_class2 = class {
2144
2569
  * "0.05 USDC on Base, within budget → pay it." No funds move.
2145
2570
  */
2146
2571
  async quote(url, init) {
2147
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _30 => _30.method]), () => ( "GET")) });
2572
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _33 => _33.method]), () => ( "GET")) });
2148
2573
  if (res.status !== 402) return null;
2149
2574
  const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
2150
2575
  return quote;
@@ -2163,7 +2588,7 @@ var PipRailClient = (_class2 = class {
2163
2588
  * on Tron, where a USD₮ transfer can cost real TRX.
2164
2589
  */
2165
2590
  async estimateCost(url, init) {
2166
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _31 => _31.method]), () => ( "GET")) });
2591
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _34 => _34.method]), () => ( "GET")) });
2167
2592
  if (res.status !== 402) return null;
2168
2593
  const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
2169
2594
  const cost = await net.estimateCost(accept);
@@ -2186,8 +2611,8 @@ var PipRailClient = (_class2 = class {
2186
2611
  return {
2187
2612
  session: {
2188
2613
  start,
2189
- expiresAt: _optionalChain([view, 'optionalAccess', _32 => _32.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
2190
- secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _33 => _33.secondsRemaining]), () => ( null))
2614
+ expiresAt: _optionalChain([view, 'optionalAccess', _35 => _35.expiresAt]) != null ? new Date(view.expiresAt).toISOString() : null,
2615
+ secondsRemaining: _nullishCoalesce(_optionalChain([view, 'optionalAccess', _36 => _36.secondsRemaining]), () => ( null))
2191
2616
  },
2192
2617
  byAsset: this.remaining()
2193
2618
  };
@@ -2200,7 +2625,7 @@ var PipRailClient = (_class2 = class {
2200
2625
  * never throws, never sums across tokens (no price oracle). PROCESS-SCOPED.
2201
2626
  */
2202
2627
  remaining() {
2203
- const maxTotal = _optionalChain([this, 'access', _34 => _34.opts, 'access', _35 => _35.policy, 'optionalAccess', _36 => _36.maxTotal]);
2628
+ const maxTotal = _optionalChain([this, 'access', _37 => _37.opts, 'access', _38 => _38.policy, 'optionalAccess', _39 => _39.maxTotal]);
2204
2629
  return this.ledger.assetBuckets().map((b) => {
2205
2630
  const base2 = {
2206
2631
  network: b.network,
@@ -2252,7 +2677,7 @@ var PipRailClient = (_class2 = class {
2252
2677
  * the plan yourself. No funds move.
2253
2678
  */
2254
2679
  async planPayment(url, init) {
2255
- const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _37 => _37.method]), () => ( "GET")) });
2680
+ const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _40 => _40.method]), () => ( "GET")) });
2256
2681
  if (res.status !== 402) return null;
2257
2682
  const challenge = await parseChallenge(res);
2258
2683
  if (!challenge) {
@@ -2419,7 +2844,7 @@ var PipRailClient = (_class2 = class {
2419
2844
  * streams throw `NonReplayableBodyError`.
2420
2845
  */
2421
2846
  async fetch(url, init) {
2422
- const body = _optionalChain([init, 'optionalAccess', _38 => _38.body]);
2847
+ const body = _optionalChain([init, 'optionalAccess', _41 => _41.body]);
2423
2848
  if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
2424
2849
  throw new (0, _chunkPA6YD3HLcjs.NonReplayableBodyError)(
2425
2850
  "fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
@@ -2427,12 +2852,12 @@ var PipRailClient = (_class2 = class {
2427
2852
  }
2428
2853
  const firstResponse = await fetch(url, init);
2429
2854
  if (firstResponse.status !== 402) return firstResponse;
2430
- const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _39 => _39.schemes]));
2855
+ const schemes = this.resolveSchemes(_optionalChain([init, 'optionalAccess', _42 => _42.schemes]));
2431
2856
  const resolved = await this.resolveChallenge(url, firstResponse, schemes);
2432
2857
  const { net, wallet, challenge } = resolved;
2433
2858
  let accept = resolved.accept;
2434
2859
  let quote = resolved.quote;
2435
- const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _40 => _40.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
2860
+ const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _43 => _43.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
2436
2861
  if (autoRoute) {
2437
2862
  const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
2438
2863
  if (!plan.best) {
@@ -2645,13 +3070,13 @@ var PipRailClient = (_class2 = class {
2645
3070
  }
2646
3071
  const amountBase = BigInt(accept.amount);
2647
3072
  const described = net.describeAsset(accept.asset);
2648
- const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _41 => _41.decimals]), () => ( accept.extra.decimals));
3073
+ const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _44 => _44.decimals]), () => ( accept.extra.decimals));
2649
3074
  if (decimals === void 0) {
2650
3075
  throw new (0, _chunkPA6YD3HLcjs.InvalidEnvelopeError)(
2651
3076
  `challenge for ${accept.asset} on ${accept.network} states no decimals and the SDK doesn't recognise the token \u2014 refusing to price it.`
2652
3077
  );
2653
3078
  }
2654
- const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _42 => _42.symbol]), () => ( accept.extra.symbol));
3079
+ const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _45 => _45.symbol]), () => ( accept.extra.symbol));
2655
3080
  const amountFormatted = _chunkPA6YD3HLcjs.formatUnits.call(void 0, amountBase, decimals);
2656
3081
  const intent = {
2657
3082
  host: hostOf2(url),
@@ -2781,7 +3206,7 @@ var PipRailClient = (_class2 = class {
2781
3206
  accepted: accept,
2782
3207
  payload: { nonce: accept.extra.nonce, txHash: ref }
2783
3208
  };
2784
- const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _43 => _43.headers]));
3209
+ const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _46 => _46.headers]));
2785
3210
  headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
2786
3211
  let lastResponse = null;
2787
3212
  let lastReason = null;
@@ -2796,7 +3221,7 @@ var PipRailClient = (_class2 = class {
2796
3221
  () => timeoutController.abort(),
2797
3222
  this.retryTimeoutMs
2798
3223
  );
2799
- const signal = _optionalChain([originalInit, 'optionalAccess', _44 => _44.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
3224
+ const signal = _optionalChain([originalInit, 'optionalAccess', _47 => _47.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
2800
3225
  try {
2801
3226
  lastResponse = await fetch(url, {
2802
3227
  ..._nullishCoalesce(originalInit, () => ( {})),
@@ -2855,9 +3280,9 @@ var PipRailClient = (_class2 = class {
2855
3280
  `the ${net.family} family can't pay a standard 'exact' rail (EVM + EIP-3009 only).`
2856
3281
  );
2857
3282
  }
2858
- throwIfAborted(_optionalChain([init, 'optionalAccess', _45 => _45.signal]));
3283
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _48 => _48.signal]));
2859
3284
  const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
2860
- const headers = new Headers(_optionalChain([init, 'optionalAccess', _46 => _46.headers]));
3285
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _49 => _49.headers]));
2861
3286
  headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
2862
3287
  const rejectDefinitive = (why2) => {
2863
3288
  this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})` });
@@ -2874,12 +3299,12 @@ var PipRailClient = (_class2 = class {
2874
3299
  if (Date.now() >= deadline) break;
2875
3300
  await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
2876
3301
  }
2877
- throwIfAborted(_optionalChain([init, 'optionalAccess', _47 => _47.signal]));
3302
+ throwIfAborted(_optionalChain([init, 'optionalAccess', _50 => _50.signal]));
2878
3303
  const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
2879
3304
  if (budget <= 0) break;
2880
3305
  const timeoutController = new AbortController();
2881
3306
  const timeoutId = setTimeout(() => timeoutController.abort(), budget);
2882
- const signal = _optionalChain([init, 'optionalAccess', _48 => _48.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
3307
+ const signal = _optionalChain([init, 'optionalAccess', _51 => _51.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
2883
3308
  let response;
2884
3309
  try {
2885
3310
  response = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), headers, signal });
@@ -2900,7 +3325,7 @@ var PipRailClient = (_class2 = class {
2900
3325
  if (response.ok && !(settle && settle.success === false)) {
2901
3326
  const receipt = parseReceipt(response);
2902
3327
  this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
2903
- const ref = _optionalChain([settle, 'optionalAccess', _49 => _49.transaction]) || _optionalChain([receipt, 'optionalAccess', _50 => _50.transaction]) || `eip3009-nonce:${nonce}`;
3328
+ const ref = _optionalChain([settle, 'optionalAccess', _52 => _52.transaction]) || _optionalChain([receipt, 'optionalAccess', _53 => _53.transaction]) || `eip3009-nonce:${nonce}`;
2904
3329
  this.recordSpend(quote, ref);
2905
3330
  return response;
2906
3331
  }
@@ -2924,14 +3349,14 @@ var PipRailClient = (_class2 = class {
2924
3349
  }
2925
3350
  }, _class2);
2926
3351
  function throwIfAborted(signal) {
2927
- if (_optionalChain([signal, 'optionalAccess', _51 => _51.aborted])) {
3352
+ if (_optionalChain([signal, 'optionalAccess', _54 => _54.aborted])) {
2928
3353
  throw _nullishCoalesce(signal.reason, () => ( new DOMException("This operation was aborted.", "AbortError")));
2929
3354
  }
2930
3355
  }
2931
3356
  function safeBig(s) {
2932
3357
  try {
2933
3358
  return BigInt(s);
2934
- } catch (e22) {
3359
+ } catch (e27) {
2935
3360
  return 0n;
2936
3361
  }
2937
3362
  }
@@ -2967,10 +3392,10 @@ function buildFundingHint(options, chainLabel) {
2967
3392
  return `Couldn't fully read your wallet on ${chainLabel} (RPC throttled) \u2014 retry; you may already be able to pay ${target.quote.amountFormatted} ${sym}.`;
2968
3393
  }
2969
3394
  const parts = [];
2970
- if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _52 => _52.shortfall, 'optionalAccess', _53 => _53.token])) {
3395
+ if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _55 => _55.shortfall, 'optionalAccess', _56 => _56.token])) {
2971
3396
  parts.push(`top up ${target.shortfall.token} ${sym}`);
2972
3397
  }
2973
- if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _54 => _54.shortfall, 'optionalAccess', _55 => _55.native])) {
3398
+ if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _57 => _57.shortfall, 'optionalAccess', _58 => _58.native])) {
2974
3399
  parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
2975
3400
  }
2976
3401
  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}.`;
@@ -2984,7 +3409,7 @@ async function planAcross(clients, url, init) {
2984
3409
  const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
2985
3410
  return {
2986
3411
  url,
2987
- network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _56 => _56.accept, 'access', _57 => _57.network]), () => ( live[0].network)),
3412
+ network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _59 => _59.accept, 'access', _60 => _60.network]), () => ( live[0].network)),
2988
3413
  status,
2989
3414
  payable: best !== null,
2990
3415
  best,
@@ -3014,7 +3439,7 @@ function reasonCodeForPolicy(code) {
3014
3439
  function hostOf2(url) {
3015
3440
  try {
3016
3441
  return new URL(url).hostname;
3017
- } catch (e23) {
3442
+ } catch (e28) {
3018
3443
  return url;
3019
3444
  }
3020
3445
  }
@@ -3031,8 +3456,8 @@ function isReplayableBodyInit(value) {
3031
3456
  async function readInvalidReason(response) {
3032
3457
  try {
3033
3458
  const body = await response.clone().json();
3034
- const ext = _optionalChain([body, 'optionalAccess', _58 => _58.extensions]);
3035
- const piprail = _optionalChain([ext, 'optionalAccess', _59 => _59.piprail]);
3459
+ const ext = _optionalChain([body, 'optionalAccess', _61 => _61.extensions]);
3460
+ const piprail = _optionalChain([ext, 'optionalAccess', _62 => _62.piprail]);
3036
3461
  if (piprail && typeof piprail.code === "string") {
3037
3462
  return {
3038
3463
  error: piprail.code,
@@ -3051,10 +3476,10 @@ async function readInvalidReason(response) {
3051
3476
  detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
3052
3477
  };
3053
3478
  }
3054
- } catch (e24) {
3479
+ } catch (e29) {
3055
3480
  }
3056
3481
  const settle = parseSettleResponse(response);
3057
- if (_optionalChain([settle, 'optionalAccess', _60 => _60.errorReason])) return { error: settle.errorReason, detail: "" };
3482
+ if (_optionalChain([settle, 'optionalAccess', _63 => _63.errorReason])) return { error: settle.errorReason, detail: "" };
3058
3483
  return null;
3059
3484
  }
3060
3485
 
@@ -3168,7 +3593,7 @@ async function readBody(res) {
3168
3593
  if (!text) return null;
3169
3594
  try {
3170
3595
  return JSON.parse(text);
3171
- } catch (e25) {
3596
+ } catch (e30) {
3172
3597
  return text;
3173
3598
  }
3174
3599
  }
@@ -3479,7 +3904,7 @@ function buildBazaarExtension(descriptor = {}) {
3479
3904
  function pathOf(url) {
3480
3905
  try {
3481
3906
  return new URL(url).pathname || "/";
3482
- } catch (e26) {
3907
+ } catch (e31) {
3483
3908
  return url.startsWith("/") ? url : `/${url}`;
3484
3909
  }
3485
3910
  }
@@ -3552,7 +3977,7 @@ async function post(url, body, headers) {
3552
3977
  let json = null;
3553
3978
  try {
3554
3979
  json = await res.json();
3555
- } catch (e27) {
3980
+ } catch (e32) {
3556
3981
  }
3557
3982
  return { status: res.status, json };
3558
3983
  }
@@ -3670,7 +4095,7 @@ function createPaymentGate(options) {
3670
4095
  );
3671
4096
  if (options.exact && !specs.some((s) => s.exact)) {
3672
4097
  throw new Error(
3673
- "requirePayment: `exact` was requested but none of the offered rails support it. The standard `exact` rail is EVM + EIP-3009 only (USDC / EURC) \u2014 not native coins, not USDT, not non-EVM chains. Offer an EVM EIP-3009 token, or drop `exact`."
4098
+ "requirePayment: `exact` was requested but none of the offered rails support it. The standard `exact` rail is EVM ERC-20 only \u2014 EIP-3009 (USDC / EURC) or Permit2 (any ERC-20, e.g. Binance-Peg USDC on BNB) \u2014 NOT native coins, NOT non-EVM chains. Offer an EVM ERC-20 token, or drop `exact`."
3674
4099
  );
3675
4100
  }
3676
4101
  return specs;
@@ -3683,26 +4108,39 @@ function createPaymentGate(options) {
3683
4108
  }
3684
4109
  async function resolveExactRail(net, asset) {
3685
4110
  const cfg = options.exact;
3686
- if (net.family !== "evm" || asset === "native" || !net.exactDomain || !net.settleExactSelf) {
4111
+ if (net.family !== "evm" || asset === "native" || !net.settleExactSelf) {
3687
4112
  return void 0;
3688
4113
  }
3689
- const domain = await net.exactDomain(asset);
3690
- if (!domain) {
3691
- throw new Error(
3692
- `requirePayment: \`exact\` requested for asset ${asset} on ${net.network}, but it isn't an EIP-3009 token (couldn't read name()/version()/authorizationState). The exact rail supports USDC / EURC and other EIP-3009 tokens \u2014 USDT and native coins need onchain-proof. (Or check your rpcUrl is reachable.)`
3693
- );
4114
+ const want = _nullishCoalesce(cfg.method, () => ( "auto"));
4115
+ let method;
4116
+ let domain;
4117
+ if (want === "permit2") {
4118
+ method = "permit2";
4119
+ } else {
4120
+ const d = net.exactDomain ? await net.exactDomain(asset) : null;
4121
+ if (d) {
4122
+ method = "eip3009";
4123
+ domain = d;
4124
+ } else if (want === "eip3009") {
4125
+ throw new Error(
4126
+ `requirePayment: exact \`method: 'eip3009'\` requested for ${asset} on ${net.network}, but it isn't an EIP-3009 token (no name()/version()/authorizationState). Use \`method: 'permit2'\` (any ERC-20, e.g. Binance-Peg USDC on BNB) or \`'auto'\`. (Or check your rpcUrl is reachable.)`
4127
+ );
4128
+ } else {
4129
+ method = "permit2";
4130
+ }
3694
4131
  }
3695
4132
  if (cfg.settle === "self") {
3696
4133
  if (cfg.relayer === void 0) {
3697
4134
  throw new Error(
3698
- "requirePayment: exact `settle: 'self'` needs a `relayer` wallet (the gas-paying key that broadcasts transferWithAuthorization), e.g. exact: { settle: 'self', relayer: { privateKey } }."
4135
+ "requirePayment: exact `settle: 'self'` needs a `relayer` wallet (the gas-paying key that broadcasts the settle), e.g. exact: { settle: 'self', relayer: { privateKey } }."
3699
4136
  );
3700
4137
  }
3701
4138
  const relayer = net.bindWallet(cfg.relayer);
3702
- return { domain, mode: { kind: "self", relayer } };
4139
+ return { method, ...domain ? { domain } : {}, mode: { kind: "self", relayer } };
3703
4140
  }
3704
4141
  return {
3705
- domain,
4142
+ method,
4143
+ ...domain ? { domain } : {},
3706
4144
  mode: {
3707
4145
  kind: "facilitator",
3708
4146
  url: cfg.settle.facilitator,
@@ -3746,7 +4184,7 @@ function createPaymentGate(options) {
3746
4184
  };
3747
4185
  }
3748
4186
  function buildExactAccept(s) {
3749
- const d = s.exact.domain;
4187
+ const rail = s.exact;
3750
4188
  return {
3751
4189
  scheme: "exact",
3752
4190
  network: s.net.network,
@@ -3755,9 +4193,8 @@ function createPaymentGate(options) {
3755
4193
  payTo: s.payTo,
3756
4194
  maxTimeoutSeconds,
3757
4195
  extra: {
3758
- assetTransferMethod: "eip3009",
3759
- name: d.name,
3760
- version: d.version,
4196
+ assetTransferMethod: rail.method,
4197
+ ...rail.domain ? { name: rail.domain.name, version: rail.domain.version } : {},
3761
4198
  minConfirmations,
3762
4199
  decimals: s.decimals,
3763
4200
  amountFormatted: s.amountFormatted,
@@ -3777,7 +4214,7 @@ function createPaymentGate(options) {
3777
4214
  const specs = await ready();
3778
4215
  const nonce = genNonce();
3779
4216
  const bazaar = options.discovery ? { bazaar: buildBazaarExtension(options.discovery === true ? {} : options.discovery) } : void 0;
3780
- const extensions = { ...bazaar, ..._optionalChain([opts, 'optionalAccess', _61 => _61.extensions]) };
4217
+ const extensions = { ...bazaar, ..._optionalChain([opts, 'optionalAccess', _64 => _64.extensions]) };
3781
4218
  const challenge2 = {
3782
4219
  x402Version: 2,
3783
4220
  resource: {
@@ -3785,7 +4222,7 @@ function createPaymentGate(options) {
3785
4222
  ...options.description ? { description: options.description } : {}
3786
4223
  },
3787
4224
  accepts: buildAccepts(specs, nonce),
3788
- ..._optionalChain([opts, 'optionalAccess', _62 => _62.error]) ? { error: opts.error } : {},
4225
+ ..._optionalChain([opts, 'optionalAccess', _65 => _65.error]) ? { error: opts.error } : {},
3789
4226
  ...Object.keys(extensions).length > 0 ? { extensions } : {}
3790
4227
  };
3791
4228
  return { challenge: challenge2, requiredHeader: buildChallengeHeader(challenge2) };
@@ -3804,13 +4241,44 @@ function createPaymentGate(options) {
3804
4241
  });
3805
4242
  return { kind: "invalid", error: code, detail, challenge: c, requiredHeader, statusCode: 402 };
3806
4243
  }
4244
+ function enrichReceipt(spec, receipt) {
4245
+ let amountFormatted = receipt.amount;
4246
+ try {
4247
+ amountFormatted = _chunkPA6YD3HLcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
4248
+ } catch (e33) {
4249
+ }
4250
+ return {
4251
+ ...receipt,
4252
+ decimals: spec.decimals,
4253
+ ...spec.symbol ? { symbol: spec.symbol } : {},
4254
+ amountFormatted,
4255
+ idempotencyKey: receipt.transaction
4256
+ };
4257
+ }
4258
+ function reportOnPaidError(error, receipt) {
4259
+ if (!options.onPaidError) return;
4260
+ try {
4261
+ options.onPaidError(error, receipt);
4262
+ } catch (e34) {
4263
+ }
4264
+ }
3807
4265
  function fireOnPaid(receipt) {
3808
- if (options.onPaid) {
3809
- try {
3810
- options.onPaid(receipt);
3811
- } catch (e28) {
3812
- }
4266
+ if (!options.onPaid) return;
4267
+ let outcome;
4268
+ try {
4269
+ outcome = options.onPaid(receipt);
4270
+ } catch (err) {
4271
+ reportOnPaidError(err, receipt);
4272
+ return;
3813
4273
  }
4274
+ if (outcome != null && typeof outcome.then === "function") {
4275
+ return Promise.resolve(outcome).catch((err) => reportOnPaidError(err, receipt));
4276
+ }
4277
+ }
4278
+ async function deliverOnPaid(spec, receipt) {
4279
+ const paid = enrichReceipt(spec, receipt);
4280
+ if (options.awaitOnPaid) await fireOnPaid(paid);
4281
+ else void fireOnPaid(paid);
3814
4282
  }
3815
4283
  async function describe(resourceUrl = "") {
3816
4284
  const specs = await ready();
@@ -3854,7 +4322,7 @@ function createPaymentGate(options) {
3854
4322
  return rejection(result.error, result.detail);
3855
4323
  }
3856
4324
  await settleTx(ref, true);
3857
- fireOnPaid(result.receipt);
4325
+ await deliverOnPaid(spec, result.receipt);
3858
4326
  return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
3859
4327
  }
3860
4328
  async function verifyExact(exact) {
@@ -3877,7 +4345,8 @@ function createPaymentGate(options) {
3877
4345
  `No \`exact\` rail offered for ${exact.network}${exact.asset ? `/${exact.asset}` : ""} (offered: ${exactSpecs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
3878
4346
  );
3879
4347
  }
3880
- const nonce = exact.payload.authorization.nonce;
4348
+ const auth = "permit2Authorization" in exact.payload ? exact.payload.permit2Authorization : exact.payload.authorization;
4349
+ const nonce = auth.nonce;
3881
4350
  if (await claimTx(nonce)) {
3882
4351
  return rejection("tx_already_used", `Authorization nonce ${nonce} was already redeemed.`);
3883
4352
  }
@@ -3909,7 +4378,7 @@ function createPaymentGate(options) {
3909
4378
  extra: { name: _nullishCoalesce(accept.extra.name, () => ( "")), version: _nullishCoalesce(accept.extra.version, () => ( "")) }
3910
4379
  },
3911
4380
  receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
3912
- payerHint: exact.payload.authorization.from
4381
+ payerHint: auth.from
3913
4382
  });
3914
4383
  }
3915
4384
  } catch (err) {
@@ -3921,7 +4390,7 @@ function createPaymentGate(options) {
3921
4390
  return rejection(result.error, result.detail);
3922
4391
  }
3923
4392
  await settleTx(nonce, true);
3924
- fireOnPaid(result.receipt);
4393
+ await deliverOnPaid(spec, result.receipt);
3925
4394
  return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
3926
4395
  }
3927
4396
  async function verify(paymentSignature) {
@@ -3975,6 +4444,114 @@ function normaliseHeader(value) {
3975
4444
  return value;
3976
4445
  }
3977
4446
 
4447
+ // src/receipts.ts
4448
+ var DEFAULT_RETRIES = 5;
4449
+ var DEFAULT_TIMEOUT_MS = 1e4;
4450
+ function defaultBackoff(attempt) {
4451
+ const base2 = Math.min(3e4, 2 ** (attempt - 1) * 500);
4452
+ return Math.round(base2 * (0.5 + Math.random()));
4453
+ }
4454
+ function isRetryableStatus(status) {
4455
+ return status === 408 || status === 429 || status >= 500;
4456
+ }
4457
+ var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
4458
+ async function signBody(secret, body) {
4459
+ const subtle = _optionalChain([globalThis, 'access', _66 => _66.crypto, 'optionalAccess', _67 => _67.subtle]);
4460
+ if (!subtle) return null;
4461
+ try {
4462
+ const enc = new TextEncoder();
4463
+ const key = await subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, [
4464
+ "sign"
4465
+ ]);
4466
+ const sig = await subtle.sign("HMAC", key, enc.encode(body));
4467
+ const hex = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
4468
+ return `sha256=${hex}`;
4469
+ } catch (e35) {
4470
+ return null;
4471
+ }
4472
+ }
4473
+ async function deliverReceipt(receipt, options) {
4474
+ const {
4475
+ url,
4476
+ secret,
4477
+ retries = DEFAULT_RETRIES,
4478
+ timeoutMs = DEFAULT_TIMEOUT_MS,
4479
+ headers = {},
4480
+ signatureHeader = "piprail-signature",
4481
+ idempotencyHeader = "idempotency-key",
4482
+ backoff = defaultBackoff,
4483
+ fetchImpl = globalThis.fetch,
4484
+ onAttempt
4485
+ } = options;
4486
+ if (typeof fetchImpl !== "function") {
4487
+ return { delivered: false, attempts: 0, error: "no fetch implementation available" };
4488
+ }
4489
+ const body = JSON.stringify(receipt);
4490
+ const signature = secret ? await signBody(secret, body) : null;
4491
+ const baseHeaders = {
4492
+ ...headers,
4493
+ "content-type": "application/json",
4494
+ [idempotencyHeader]: receipt.idempotencyKey,
4495
+ ...signature ? { [signatureHeader]: signature } : {}
4496
+ };
4497
+ const maxAttempts = Math.max(1, retries + 1);
4498
+ let lastStatus;
4499
+ let lastError;
4500
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
4501
+ const controller = new AbortController();
4502
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
4503
+ let ok = false;
4504
+ let status;
4505
+ let error;
4506
+ try {
4507
+ const res = await fetchImpl(url, {
4508
+ method: "POST",
4509
+ headers: baseHeaders,
4510
+ body,
4511
+ signal: controller.signal
4512
+ });
4513
+ status = res.status;
4514
+ lastStatus = status;
4515
+ ok = res.ok;
4516
+ if (!ok) {
4517
+ error = `HTTP ${status}`;
4518
+ lastError = error;
4519
+ }
4520
+ } catch (err) {
4521
+ error = err instanceof Error ? err.message : String(err);
4522
+ lastError = error;
4523
+ } finally {
4524
+ clearTimeout(timer);
4525
+ }
4526
+ const retryable = status === void 0 ? true : isRetryableStatus(status);
4527
+ const willRetry = !ok && retryable && attempt < maxAttempts;
4528
+ try {
4529
+ _optionalChain([onAttempt, 'optionalCall', _68 => _68({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
4530
+ } catch (e36) {
4531
+ }
4532
+ if (ok) return { delivered: true, attempts: attempt, status };
4533
+ if (!willRetry) {
4534
+ return {
4535
+ delivered: false,
4536
+ attempts: attempt,
4537
+ ...lastStatus !== void 0 ? { status: lastStatus } : {},
4538
+ ...lastError ? { error: lastError } : {}
4539
+ };
4540
+ }
4541
+ await sleep(backoff(attempt));
4542
+ }
4543
+ return {
4544
+ delivered: false,
4545
+ attempts: maxAttempts,
4546
+ ...lastStatus !== void 0 ? { status: lastStatus } : {},
4547
+ ...lastError ? { error: lastError } : {}
4548
+ };
4549
+ }
4550
+
4551
+
4552
+
4553
+
4554
+
3978
4555
 
3979
4556
 
3980
4557
 
@@ -4047,4 +4624,4 @@ function normaliseHeader(value) {
4047
4624
 
4048
4625
 
4049
4626
 
4050
- exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkPA6YD3HLcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkPA6YD3HLcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkPA6YD3HLcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkPA6YD3HLcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkPA6YD3HLcjs.NonReplayableBodyError; exports.PIPRAIL_AGENT_GUIDE = PIPRAIL_AGENT_GUIDE; exports.PaymentDeclinedError = _chunkPA6YD3HLcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkPA6YD3HLcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkPA6YD3HLcjs.PipRailError; exports.RecipientNotReadyError = _chunkPA6YD3HLcjs.RecipientNotReadyError; exports.SettlementError = _chunkPA6YD3HLcjs.SettlementError; exports.UnknownTokenError = _chunkPA6YD3HLcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkPA6YD3HLcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkPA6YD3HLcjs.UnsupportedSchemeError; exports.WrongChainError = _chunkPA6YD3HLcjs.WrongChainError; exports.WrongFamilyError = _chunkPA6YD3HLcjs.WrongFamilyError; exports.agentGuide = agentGuide; 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.classifyChallenge = classifyChallenge; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.explainDecline = explainDecline; exports.formatSpendReport = formatSpendReport; 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.summarizePlan = summarizePlan; exports.toInsufficientFundsError = _chunkPA6YD3HLcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
4627
+ exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkPA6YD3HLcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkPA6YD3HLcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkPA6YD3HLcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkPA6YD3HLcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkPA6YD3HLcjs.NonReplayableBodyError; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.PERMIT2_WITNESS_TYPES = PERMIT2_WITNESS_TYPES; exports.PIPRAIL_AGENT_GUIDE = PIPRAIL_AGENT_GUIDE; exports.PaymentDeclinedError = _chunkPA6YD3HLcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkPA6YD3HLcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkPA6YD3HLcjs.PipRailError; exports.RecipientNotReadyError = _chunkPA6YD3HLcjs.RecipientNotReadyError; exports.SettlementError = _chunkPA6YD3HLcjs.SettlementError; exports.UnknownTokenError = _chunkPA6YD3HLcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkPA6YD3HLcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkPA6YD3HLcjs.UnsupportedSchemeError; exports.WrongChainError = _chunkPA6YD3HLcjs.WrongChainError; exports.WrongFamilyError = _chunkPA6YD3HLcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_PERMIT2_PROXY; exports.agentGuide = agentGuide; 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.classifyChallenge = classifyChallenge; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.deliverReceipt = deliverReceipt; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.explainDecline = explainDecline; exports.formatSpendReport = formatSpendReport; 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.summarizePlan = summarizePlan; exports.toInsufficientFundsError = _chunkPA6YD3HLcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;