@piprail/sdk 2.9.0 → 2.10.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.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  memorySpendStore
3
3
  } from "./chunk-SK3CB7UA.js";
4
+ import {
5
+ signReceiptEvm
6
+ } from "./chunk-3SBTJKAG.js";
4
7
  import {
5
8
  ConfirmationTimeoutError,
6
9
  InsufficientFundsError,
@@ -71,7 +74,7 @@ function resolveNetwork(opts) {
71
74
  }
72
75
 
73
76
  // src/drivers/evm/index.ts
74
- import { BaseError, createPublicClient, erc20Abi as erc20Abi4, getAddress as getAddress4, http as http2, isAddress } from "viem";
77
+ import { BaseError, createPublicClient, erc20Abi as erc20Abi4, getAddress as getAddress5, http as http2, isAddress } from "viem";
75
78
 
76
79
  // src/drivers/evm/chains.ts
77
80
  import { defineChain } from "viem";
@@ -1357,6 +1360,361 @@ async function verifyAndSettlePermit2Evm(input) {
1357
1360
  };
1358
1361
  }
1359
1362
 
1363
+ // src/drivers/evm/upto.ts
1364
+ import {
1365
+ getAddress as getAddress4,
1366
+ recoverTypedDataAddress as recoverTypedDataAddress3
1367
+ } from "viem";
1368
+ var X402_UPTO_PERMIT2_PROXY = "0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002";
1369
+ var UPTO_PROXY_CHAIN_IDS = /* @__PURE__ */ new Set([
1370
+ 1,
1371
+ // Ethereum
1372
+ 8453,
1373
+ // Base
1374
+ 42161,
1375
+ // Arbitrum
1376
+ 10,
1377
+ // Optimism
1378
+ 137,
1379
+ // Polygon
1380
+ 56
1381
+ // BNB
1382
+ ]);
1383
+ function isUptoProxyChain(chainId) {
1384
+ return UPTO_PROXY_CHAIN_IDS.has(chainId);
1385
+ }
1386
+ var PERMIT2_UPTO_WITNESS_TYPES = {
1387
+ PermitWitnessTransferFrom: PERMIT2_WITNESS_TYPES.PermitWitnessTransferFrom,
1388
+ TokenPermissions: PERMIT2_WITNESS_TYPES.TokenPermissions,
1389
+ Witness: [
1390
+ { name: "to", type: "address" },
1391
+ { name: "facilitator", type: "address" },
1392
+ { name: "validAfter", type: "uint256" }
1393
+ ]
1394
+ };
1395
+ var x402UptoProxyAbi = [
1396
+ {
1397
+ type: "function",
1398
+ name: "settle",
1399
+ stateMutability: "nonpayable",
1400
+ outputs: [],
1401
+ inputs: [
1402
+ {
1403
+ name: "permit",
1404
+ type: "tuple",
1405
+ components: [
1406
+ {
1407
+ name: "permitted",
1408
+ type: "tuple",
1409
+ components: [
1410
+ { name: "token", type: "address" },
1411
+ { name: "amount", type: "uint256" }
1412
+ ]
1413
+ },
1414
+ { name: "nonce", type: "uint256" },
1415
+ { name: "deadline", type: "uint256" }
1416
+ ]
1417
+ },
1418
+ { name: "amount", type: "uint256" },
1419
+ { name: "owner", type: "address" },
1420
+ {
1421
+ name: "witness",
1422
+ type: "tuple",
1423
+ components: [
1424
+ { name: "to", type: "address" },
1425
+ { name: "facilitator", type: "address" },
1426
+ { name: "validAfter", type: "uint256" }
1427
+ ]
1428
+ },
1429
+ { name: "signature", type: "bytes" }
1430
+ ]
1431
+ }
1432
+ ];
1433
+ function shorten3(msg) {
1434
+ const oneLine = msg.replace(/\s+/g, " ").trim();
1435
+ return oneLine.length > 200 ? `${oneLine.slice(0, 200)}\u2026` : oneLine;
1436
+ }
1437
+ function randomPermit2Nonce2() {
1438
+ const g = globalThis.crypto;
1439
+ if (!g?.getRandomValues) {
1440
+ throw new UnsupportedSchemeError(
1441
+ "this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the upto rail needs a CSPRNG nonce."
1442
+ );
1443
+ }
1444
+ const raw = new Uint8Array(32);
1445
+ g.getRandomValues(raw);
1446
+ return BigInt(`0x${[...raw].map((b) => b.toString(16).padStart(2, "0")).join("")}`);
1447
+ }
1448
+ async function payUptoEvm(input) {
1449
+ const { publicClient, walletClient, account, chainId, chain, accept } = input;
1450
+ let code;
1451
+ try {
1452
+ code = await publicClient.getCode({ address: account.address });
1453
+ } catch {
1454
+ code = void 0;
1455
+ }
1456
+ if (code && code !== "0x") {
1457
+ throw new UnsupportedSchemeError(
1458
+ `upto buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account. Pay via onchain-proof.`
1459
+ );
1460
+ }
1461
+ const facilitatorAddress = accept.extra?.facilitatorAddress;
1462
+ if (typeof facilitatorAddress !== "string" || facilitatorAddress.length === 0) {
1463
+ throw new UnsupportedSchemeError(
1464
+ `upto: the rail carries no extra.facilitatorAddress to bind into witness.facilitator \u2014 refusing to sign.`
1465
+ );
1466
+ }
1467
+ const token = getAddress4(accept.asset);
1468
+ const payTo = getAddress4(accept.payTo);
1469
+ const facilitator = getAddress4(facilitatorAddress);
1470
+ const value = BigInt(accept.amount);
1471
+ const approvalTx = await ensurePermit2Allowance({
1472
+ publicClient,
1473
+ walletClient,
1474
+ account,
1475
+ chain,
1476
+ token,
1477
+ amount: value
1478
+ });
1479
+ const nonce = randomPermit2Nonce2();
1480
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + accept.maxTimeoutSeconds);
1481
+ const validAfter = 0n;
1482
+ const spender = getAddress4(X402_UPTO_PERMIT2_PROXY);
1483
+ const from = account.address;
1484
+ const signature = await walletClient.signTypedData({
1485
+ account,
1486
+ domain: { name: "Permit2", chainId, verifyingContract: PERMIT2_ADDRESS },
1487
+ types: PERMIT2_UPTO_WITNESS_TYPES,
1488
+ primaryType: "PermitWitnessTransferFrom",
1489
+ message: {
1490
+ permitted: { token, amount: value },
1491
+ spender,
1492
+ nonce,
1493
+ deadline,
1494
+ witness: { to: payTo, facilitator, validAfter }
1495
+ }
1496
+ });
1497
+ const permit2Authorization = {
1498
+ permitted: { token, amount: value.toString() },
1499
+ from,
1500
+ spender,
1501
+ nonce: nonce.toString(),
1502
+ deadline: deadline.toString(),
1503
+ witness: { to: payTo, facilitator, validAfter: validAfter.toString() }
1504
+ };
1505
+ return {
1506
+ payload: { signature, permit2Authorization },
1507
+ payerFrom: from,
1508
+ nonce: nonce.toString(),
1509
+ ...approvalTx ? { approvalTx } : {}
1510
+ };
1511
+ }
1512
+ function resolveUptoRailEvm(input) {
1513
+ const { asset, relayerAddress, proxySupported, domain } = input;
1514
+ if (asset === "native") return null;
1515
+ if (!proxySupported()) return null;
1516
+ const extra = { facilitatorAddress: getAddress4(relayerAddress) };
1517
+ if (domain) {
1518
+ extra.name = domain.name;
1519
+ extra.version = domain.version;
1520
+ }
1521
+ return { method: "permit2-upto", extra };
1522
+ }
1523
+ async function verifyAndSettleUptoEvm(input) {
1524
+ const { publicClient, walletClient, account, chain, payload, accept, settleAmount } = input;
1525
+ const token = getAddress4(accept.asset);
1526
+ const payTo = getAddress4(accept.payTo);
1527
+ const maxAmount = BigInt(accept.amount);
1528
+ const proxy = getAddress4(X402_UPTO_PERMIT2_PROXY);
1529
+ const relayerAddress = getAddress4(account.address);
1530
+ let from;
1531
+ let spender;
1532
+ let permittedToken;
1533
+ let witnessTo;
1534
+ let witnessFacilitator;
1535
+ let permittedAmount;
1536
+ let nonce;
1537
+ let deadline;
1538
+ let validAfter;
1539
+ const signature = payload.signature;
1540
+ try {
1541
+ const pa = payload.permit2Authorization;
1542
+ from = getAddress4(pa.from);
1543
+ spender = getAddress4(pa.spender);
1544
+ permittedToken = getAddress4(pa.permitted.token);
1545
+ witnessTo = getAddress4(pa.witness.to);
1546
+ witnessFacilitator = getAddress4(pa.witness.facilitator);
1547
+ permittedAmount = BigInt(pa.permitted.amount);
1548
+ nonce = BigInt(pa.nonce);
1549
+ deadline = BigInt(pa.deadline);
1550
+ validAfter = BigInt(pa.witness.validAfter);
1551
+ if (!/^0x[0-9a-fA-F]+$/.test(signature)) throw new Error("signature must be hex");
1552
+ } catch (err) {
1553
+ return {
1554
+ ok: false,
1555
+ error: "signature_invalid",
1556
+ detail: `Malformed upto authorization: ${err instanceof Error ? err.message : String(err)}.`
1557
+ };
1558
+ }
1559
+ if (witnessTo !== payTo) {
1560
+ return { ok: false, error: "wrong_recipient", detail: `Authorization pays witness.to ${witnessTo}, not ${payTo}.` };
1561
+ }
1562
+ if (permittedToken !== token) {
1563
+ return { ok: false, error: "signature_invalid", detail: `Authorization permits token ${permittedToken}, not the rail's ${token}.` };
1564
+ }
1565
+ if (spender !== proxy) {
1566
+ return { ok: false, error: "signature_invalid", detail: `Authorization spender ${spender} is not the x402UptoPermit2Proxy ${proxy}; it can't be settled here.` };
1567
+ }
1568
+ if (witnessFacilitator !== relayerAddress) {
1569
+ return { ok: false, error: "signature_invalid", detail: `Authorization binds facilitator ${witnessFacilitator}, not this relayer ${relayerAddress}; only the bound facilitator can settle.` };
1570
+ }
1571
+ if (permittedAmount < maxAmount) {
1572
+ return { ok: false, error: "amount_too_low", detail: `Permitted (signed MAX) ${permittedAmount} is below the rail max ${maxAmount}.` };
1573
+ }
1574
+ if (permittedAmount > maxAmount) {
1575
+ return { ok: false, error: "upto_settle_exceeds_max", detail: `Permitted (signed MAX) ${permittedAmount} exceeds the advertised rail max ${maxAmount}; sign exactly the ceiling.` };
1576
+ }
1577
+ if (settleAmount < 0n) {
1578
+ return { ok: false, error: "upto_settle_exceeds_max", detail: `Settle amount ${settleAmount} is negative.` };
1579
+ }
1580
+ if (settleAmount > permittedAmount) {
1581
+ return { ok: false, error: "upto_settle_exceeds_max", detail: `Settle amount ${settleAmount} exceeds the signed MAX ${permittedAmount}.` };
1582
+ }
1583
+ const now = BigInt(Math.floor(Date.now() / 1e3));
1584
+ if (deadline <= now) {
1585
+ return { ok: false, error: "payment_expired", detail: `Permit2 deadline ${deadline} <= now ${now}.` };
1586
+ }
1587
+ let fromCode;
1588
+ try {
1589
+ fromCode = await publicClient.getCode({ address: from });
1590
+ } catch {
1591
+ return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
1592
+ }
1593
+ if (!(fromCode && fromCode !== "0x")) {
1594
+ let recovered;
1595
+ try {
1596
+ recovered = await recoverTypedDataAddress3({
1597
+ domain: { name: "Permit2", chainId: chain.id, verifyingContract: PERMIT2_ADDRESS },
1598
+ types: PERMIT2_UPTO_WITNESS_TYPES,
1599
+ primaryType: "PermitWitnessTransferFrom",
1600
+ message: {
1601
+ permitted: { token: permittedToken, amount: permittedAmount },
1602
+ spender,
1603
+ nonce,
1604
+ deadline,
1605
+ witness: { to: witnessTo, facilitator: witnessFacilitator, validAfter }
1606
+ },
1607
+ signature
1608
+ });
1609
+ } catch (err) {
1610
+ return { ok: false, error: "signature_invalid", detail: `Not a valid EIP-712 signature: ${shorten3(err instanceof Error ? err.message : String(err))}.` };
1611
+ }
1612
+ if (recovered !== from) {
1613
+ return { ok: false, error: "signature_invalid", detail: `Signature recovered to ${recovered}, not the authorizer ${from}.` };
1614
+ }
1615
+ }
1616
+ if (settleAmount === 0n) {
1617
+ return {
1618
+ ok: true,
1619
+ receipt: {
1620
+ scheme: "upto",
1621
+ success: true,
1622
+ network: accept.network,
1623
+ transaction: "",
1624
+ asset: accept.asset,
1625
+ amount: "0",
1626
+ payer: from,
1627
+ payTo: accept.payTo,
1628
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1629
+ }
1630
+ };
1631
+ }
1632
+ try {
1633
+ const word = nonce >> 8n;
1634
+ const bit = nonce & 0xffn;
1635
+ const bitmap = await publicClient.readContract({
1636
+ address: PERMIT2_ADDRESS,
1637
+ abi: permit2NonceBitmapAbi,
1638
+ functionName: "nonceBitmap",
1639
+ args: [from, word]
1640
+ });
1641
+ if ((bitmap >> bit & 1n) === 1n) {
1642
+ return { ok: false, error: "tx_already_used", detail: `Permit2 nonce ${nonce} already used or invalidated for ${from}.` };
1643
+ }
1644
+ } catch {
1645
+ return { ok: false, error: "tx_not_found", detail: "Could not read the Permit2 nonce bitmap (transient RPC) \u2014 retry." };
1646
+ }
1647
+ const settleArgs = [
1648
+ { permitted: { token: permittedToken, amount: permittedAmount }, nonce, deadline },
1649
+ settleAmount,
1650
+ from,
1651
+ { to: witnessTo, facilitator: witnessFacilitator, validAfter },
1652
+ signature
1653
+ ];
1654
+ try {
1655
+ await publicClient.simulateContract({
1656
+ account,
1657
+ address: proxy,
1658
+ abi: x402UptoProxyAbi,
1659
+ functionName: "settle",
1660
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1661
+ args: settleArgs
1662
+ });
1663
+ } catch (err) {
1664
+ const msg = err instanceof Error ? err.message : String(err);
1665
+ if (/nonce|invalidated|used/i.test(msg)) return { ok: false, error: "tx_already_used", detail: "Permit2 nonce is used or invalidated." };
1666
+ if (/expired|deadline|too early|not yet/i.test(msg)) return { ok: false, error: "payment_expired", detail: shorten3(msg) };
1667
+ if (/exceeds.*permitted|AmountExceeds/i.test(msg)) return { ok: false, error: "upto_settle_exceeds_max", detail: shorten3(msg) };
1668
+ if (/facilitator|Unauthorized/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten3(msg) };
1669
+ if (/signature/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten3(msg) };
1670
+ return { ok: false, error: "tx_reverted", detail: `upto settle would revert: ${shorten3(msg)}` };
1671
+ }
1672
+ let txHash;
1673
+ try {
1674
+ txHash = await walletClient.writeContract({
1675
+ account,
1676
+ chain,
1677
+ address: proxy,
1678
+ abi: x402UptoProxyAbi,
1679
+ functionName: "settle",
1680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1681
+ args: settleArgs
1682
+ });
1683
+ } catch (err) {
1684
+ throw new SettlementError(
1685
+ `upto settle: the merchant relayer failed to broadcast the proxy settle (${shorten3(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.`,
1686
+ { cause: err }
1687
+ );
1688
+ }
1689
+ try {
1690
+ const confirmations = accept.extra.minConfirmations ?? 1;
1691
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, confirmations });
1692
+ if (receipt.status !== "success") {
1693
+ return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
1694
+ }
1695
+ } catch (err) {
1696
+ throw new SettlementError(
1697
+ `upto settle: broadcast ${txHash} but couldn't confirm it (${shorten3(err instanceof Error ? err.message : String(err))}).`,
1698
+ { cause: err }
1699
+ );
1700
+ }
1701
+ return {
1702
+ ok: true,
1703
+ receipt: {
1704
+ scheme: "upto",
1705
+ success: true,
1706
+ network: accept.network,
1707
+ transaction: txHash,
1708
+ asset: accept.asset,
1709
+ // The ACTUAL settled amount (≤ max) — what the buyer reads back to record metered spend.
1710
+ amount: settleAmount.toString(),
1711
+ payer: from,
1712
+ payTo: accept.payTo,
1713
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1714
+ }
1715
+ };
1716
+ }
1717
+
1360
1718
  // src/x402.ts
1361
1719
  var HEADER_REQUIRED = "payment-required";
1362
1720
  var HEADER_SIGNATURE = "payment-signature";
@@ -1390,6 +1748,9 @@ function fromBase64Json(b64) {
1390
1748
  return null;
1391
1749
  }
1392
1750
  }
1751
+ function decodeBase64Json(value) {
1752
+ return fromBase64Json(value);
1753
+ }
1393
1754
  function toBase64Json(value) {
1394
1755
  return encodeBase64(JSON.stringify(value));
1395
1756
  }
@@ -1405,8 +1766,15 @@ function chainIdFromNetwork(network) {
1405
1766
  function buildChallengeHeader(challenge) {
1406
1767
  return toBase64Json(challenge);
1407
1768
  }
1408
- function buildReceiptHeader(receipt) {
1409
- return toBase64Json(receipt);
1769
+ function buildReceiptHeader(receipt, extensions) {
1770
+ return toBase64Json(extensions ? { ...receipt, extensions } : receipt);
1771
+ }
1772
+ var EXT_OFFER_RECEIPT = "offer-receipt";
1773
+ function buildReceiptExtension(bundle) {
1774
+ const info = { settlement: bundle.receipt, resource: bundle.resource };
1775
+ if (typeof bundle.decimals === "number") info.decimals = bundle.decimals;
1776
+ if (bundle.attestation) info.receipt = bundle.attestation;
1777
+ return { [EXT_OFFER_RECEIPT]: { info } };
1410
1778
  }
1411
1779
  function buildSignatureHeader(signature) {
1412
1780
  return toBase64Json(signature);
@@ -1414,6 +1782,9 @@ function buildSignatureHeader(signature) {
1414
1782
  function buildExactSignatureHeader(input) {
1415
1783
  return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
1416
1784
  }
1785
+ function buildUptoSignatureHeader(input) {
1786
+ return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
1787
+ }
1417
1788
  async function parseChallenge(response) {
1418
1789
  const headerValue = response.headers.get(HEADER_REQUIRED);
1419
1790
  if (headerValue) {
@@ -1433,6 +1804,27 @@ function parseReceipt(response) {
1433
1804
  const parsed = fromBase64Json(headerValue);
1434
1805
  return isValidReceipt(parsed) ? parsed : null;
1435
1806
  }
1807
+ function parseReceiptExtension(response) {
1808
+ const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
1809
+ if (!headerValue) return null;
1810
+ const parsed = fromBase64Json(headerValue);
1811
+ if (!parsed || typeof parsed !== "object") return null;
1812
+ const ext = parsed.extensions;
1813
+ const block = ext?.[EXT_OFFER_RECEIPT];
1814
+ const info = block?.info;
1815
+ if (!info) return null;
1816
+ const settlement = isValidReceipt(info.settlement) ? info.settlement : isValidReceipt(info.receipt) ? info.receipt : void 0;
1817
+ if (!settlement) return null;
1818
+ const attestation = info.receipt && info.receipt !== settlement ? info.receipt : void 0;
1819
+ const res = info.resource;
1820
+ return {
1821
+ piprail: "1",
1822
+ receipt: settlement,
1823
+ resource: { url: res && typeof res.url === "string" ? res.url : "" },
1824
+ ...typeof info.decimals === "number" ? { decimals: info.decimals } : {},
1825
+ ...attestation ? { attestation } : {}
1826
+ };
1827
+ }
1436
1828
  function parseSettleResponse(response) {
1437
1829
  const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
1438
1830
  if (!headerValue) return null;
@@ -1443,11 +1835,13 @@ function parseSettleResponse(response) {
1443
1835
  ...typeof parsed.transaction === "string" ? { transaction: parsed.transaction } : {},
1444
1836
  ...typeof parsed.network === "string" ? { network: parsed.network } : {},
1445
1837
  ...typeof parsed.payer === "string" ? { payer: parsed.payer } : {},
1446
- ...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {}
1838
+ ...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {},
1839
+ // The upto SettleResponse's REQUIRED `amount` (actual settled atomic units) — string
1840
+ // passthrough so the upto buyer records ACTUAL, not MAX. Absent on onchain-proof/exact.
1841
+ ...typeof parsed.amount === "string" ? { amount: parsed.amount } : {}
1447
1842
  };
1448
1843
  }
1449
- function parseSignatureHeader(value) {
1450
- const parsed = fromBase64Json(value);
1844
+ function parseSignatureObject(parsed) {
1451
1845
  if (!parsed || typeof parsed !== "object") return null;
1452
1846
  const v = parsed;
1453
1847
  const accepted = v.accepted;
@@ -1459,8 +1853,13 @@ function parseSignatureHeader(value) {
1459
1853
  }
1460
1854
  return parsed;
1461
1855
  }
1856
+ function parseSignatureHeader(value) {
1857
+ return parseSignatureObject(fromBase64Json(value));
1858
+ }
1462
1859
  function parseExactPaymentHeader(value) {
1463
- const parsed = fromBase64Json(value);
1860
+ return parseExactObject(fromBase64Json(value));
1861
+ }
1862
+ function parseExactObject(parsed) {
1464
1863
  if (!parsed || typeof parsed !== "object") return null;
1465
1864
  const v = parsed;
1466
1865
  const accepted = v.accepted ?? null;
@@ -1525,6 +1924,42 @@ function parseExactPaymentHeader(value) {
1525
1924
  }
1526
1925
  return null;
1527
1926
  }
1927
+ function parseUptoPaymentHeader(value) {
1928
+ return parseUptoObject(fromBase64Json(value));
1929
+ }
1930
+ function parseUptoObject(parsed) {
1931
+ if (!parsed || typeof parsed !== "object") return null;
1932
+ const v = parsed;
1933
+ const accepted = v.accepted ?? null;
1934
+ const scheme = accepted?.scheme ?? v.scheme;
1935
+ if (scheme !== "upto") return null;
1936
+ const network = accepted?.network ?? v.network;
1937
+ if (typeof network !== "string") return null;
1938
+ const payload = v.payload;
1939
+ if (!payload || typeof payload !== "object") return null;
1940
+ const signature = payload.signature;
1941
+ if (typeof signature !== "string") return null;
1942
+ const p2 = payload.permit2Authorization;
1943
+ if (!p2 || typeof p2 !== "object") return null;
1944
+ const permitted = p2.permitted;
1945
+ const witness = p2.witness;
1946
+ if (!permitted || typeof permitted !== "object" || !witness || typeof witness !== "object") return null;
1947
+ if (typeof permitted.token !== "string" || typeof permitted.amount !== "string") return null;
1948
+ if (typeof witness.to !== "string" || typeof witness.facilitator !== "string" || typeof witness.validAfter !== "string") {
1949
+ return null;
1950
+ }
1951
+ for (const k of ["from", "spender", "nonce", "deadline"]) {
1952
+ if (typeof p2[k] !== "string") return null;
1953
+ }
1954
+ const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
1955
+ const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
1956
+ const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
1957
+ return {
1958
+ ...base2,
1959
+ method: "permit2-upto",
1960
+ payload: { signature, permit2Authorization: p2 }
1961
+ };
1962
+ }
1528
1963
  function isValidChallenge(value) {
1529
1964
  if (!value || typeof value !== "object") return false;
1530
1965
  const v = value;
@@ -1537,7 +1972,7 @@ function isValidChallenge(value) {
1537
1972
  function isValidReceipt(value) {
1538
1973
  if (!value || typeof value !== "object") return false;
1539
1974
  const v = value;
1540
- if (v.scheme !== "onchain-proof" && v.scheme !== "exact") return false;
1975
+ if (v.scheme !== "onchain-proof" && v.scheme !== "exact" && v.scheme !== "upto") return false;
1541
1976
  if (typeof v.transaction !== "string" && typeof v.txHash !== "string") return false;
1542
1977
  if (typeof v.payer !== "string") return false;
1543
1978
  return true;
@@ -1615,12 +2050,12 @@ function makeEvmNetwork(resolved) {
1615
2050
  }
1616
2051
  let normalized;
1617
2052
  try {
1618
- normalized = getAddress4(asset);
2053
+ normalized = getAddress5(asset);
1619
2054
  } catch {
1620
2055
  return null;
1621
2056
  }
1622
2057
  for (const info of Object.values(resolved.tokens)) {
1623
- if (getAddress4(info.address) === normalized) {
2058
+ if (getAddress5(info.address) === normalized) {
1624
2059
  return { symbol: info.symbol, decimals: info.decimals };
1625
2060
  }
1626
2061
  }
@@ -1720,7 +2155,7 @@ function makeEvmNetwork(resolved) {
1720
2155
  let token = null;
1721
2156
  try {
1722
2157
  token = await publicClient.readContract({
1723
- address: getAddress4(asset),
2158
+ address: getAddress5(asset),
1724
2159
  abi: erc20Abi4,
1725
2160
  functionName: "balanceOf",
1726
2161
  args: [owner]
@@ -1745,6 +2180,12 @@ function makeEvmNetwork(resolved) {
1745
2180
  signMessage: (message) => a.walletClient.signMessage({ account: a.account, message })
1746
2181
  };
1747
2182
  },
2183
+ // Tier-2 service-delivery attestation (EVM-only) — sign the official offer-receipt
2184
+ // EIP-712 RECEIPT_TYPES with the bound (payTo) wallet. Chain-independent (domain
2185
+ // chainId is hardcoded 1); viem lives in ./receipt.ts (a lazy chunk).
2186
+ signReceipt(wallet, input) {
2187
+ return signReceiptEvm(wallet, input);
2188
+ },
1748
2189
  async verify(ref, accept) {
1749
2190
  return verifyEvm({
1750
2191
  publicClient,
@@ -1828,6 +2269,43 @@ function makeEvmNetwork(resolved) {
1828
2269
  payload,
1829
2270
  accept
1830
2271
  });
2272
+ },
2273
+ // Standard x402 `upto` (metered) rail — EVM-Permit2 ONLY. The metered sibling of the
2274
+ // exact trio. resolveUptoRail advertises (Permit2-only, proxy-gated, relayer = facilitator);
2275
+ // payUpto signs the MAX with witness.facilitator bound; settleUptoSelf self-settles the actual.
2276
+ async resolveUptoRail({ asset, relayer }) {
2277
+ const relayerAddress = relayer._native.account.address;
2278
+ const domain = asset === "native" ? null : await readExactDomain(publicClient, asset).catch(() => null);
2279
+ return resolveUptoRailEvm({
2280
+ asset,
2281
+ relayerAddress,
2282
+ proxySupported: () => isUptoProxyChain(resolved.chainId),
2283
+ domain
2284
+ });
2285
+ },
2286
+ async payUpto(wallet, accept) {
2287
+ const a = wallet._native;
2288
+ const { payload, payerFrom, nonce } = await payUptoEvm({
2289
+ publicClient,
2290
+ walletClient: a.walletClient,
2291
+ account: a.account,
2292
+ chainId: resolved.chainId,
2293
+ chain: resolved.chain,
2294
+ accept
2295
+ });
2296
+ return { payload, accepted: accept, payerFrom, nonce };
2297
+ },
2298
+ async settleUptoSelf({ relayer, payload, accept, settleAmount }) {
2299
+ const a = relayer._native;
2300
+ return verifyAndSettleUptoEvm({
2301
+ publicClient,
2302
+ walletClient: a.walletClient,
2303
+ account: a.account,
2304
+ chain: resolved.chain,
2305
+ payload,
2306
+ accept,
2307
+ settleAmount
2308
+ });
1831
2309
  }
1832
2310
  };
1833
2311
  }
@@ -1868,7 +2346,7 @@ var loaders = {
1868
2346
  stellar: async () => {
1869
2347
  let mod;
1870
2348
  try {
1871
- mod = await import("./stellar-BEMT7UYF.js");
2349
+ mod = await import("./stellar-ASP2THL2.js");
1872
2350
  } catch (cause) {
1873
2351
  throw new MissingDriverError(
1874
2352
  `Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
@@ -1892,7 +2370,7 @@ var loaders = {
1892
2370
  tron: async () => {
1893
2371
  let mod;
1894
2372
  try {
1895
- mod = await import("./tron-ZZZS3FNN.js");
2373
+ mod = await import("./tron-BMCWN5SS.js");
1896
2374
  } catch (cause) {
1897
2375
  throw new MissingDriverError(
1898
2376
  `Tron selected, but its package isn't installed. Run: npm install tronweb`,
@@ -1904,7 +2382,7 @@ var loaders = {
1904
2382
  sui: async () => {
1905
2383
  let mod;
1906
2384
  try {
1907
- mod = await import("./sui-F5JQ2N6I.js");
2385
+ mod = await import("./sui-FZIKZNVI.js");
1908
2386
  } catch (cause) {
1909
2387
  throw new MissingDriverError(
1910
2388
  `Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
@@ -2970,6 +3448,7 @@ var RECIPIENT_FIX = {
2970
3448
  NOT_OPTED_IN: "the recipient must opt into this asset once (a 0-amount self-transfer)",
2971
3449
  INACTIVE: "the recipient account doesn't exist yet \u2014 fund it with the chain's base reserve to activate it"
2972
3450
  };
3451
+ var RECEIPT_VERIFY_WINDOW_SECONDS = 100 * 365 * 24 * 60 * 60;
2973
3452
  var PipRailClient = class {
2974
3453
  opts;
2975
3454
  maxRetries;
@@ -2986,6 +3465,10 @@ var PipRailClient = class {
2986
3465
  // Resolved lazily on first request — this is what lets Solana (and future
2987
3466
  // families) auto-mount with no setup call.
2988
3467
  bound;
3468
+ // The verifiable receipt from the most recent settled fetch (null if the server emitted
3469
+ // none). Captured pure (no chain read) and surfaced via lastReceipt(); the resource URL is
3470
+ // stamped from the URL this client actually fetched (authoritative over the gate's default).
3471
+ lastReceiptValue = null;
2989
3472
  constructor(opts) {
2990
3473
  this.opts = opts;
2991
3474
  this.maxRetries = Math.max(1, opts.maxPaymentRetries ?? 3);
@@ -3132,6 +3615,129 @@ var PipRailClient = class {
3132
3615
  } catch {
3133
3616
  }
3134
3617
  }
3618
+ /**
3619
+ * Capture the verifiable receipt from a settled response (pure — no chain read), stamping
3620
+ * the resource URL this client actually fetched (authoritative over the gate's default ''). A
3621
+ * settled fetch with no receipt extension sets it to `null` so {@link lastReceipt} reflects the
3622
+ * latest fetch. Never throws — a malformed header just yields `null`.
3623
+ */
3624
+ captureReceipt(response, url) {
3625
+ try {
3626
+ const parsed = parseReceiptExtension(response);
3627
+ this.lastReceiptValue = parsed ? { ...parsed, resource: { url } } : null;
3628
+ } catch {
3629
+ this.lastReceiptValue = null;
3630
+ }
3631
+ }
3632
+ /**
3633
+ * The verifiable {@link PipRailReceipt} from the most recent settled `fetch` — the
3634
+ * self-contained record the buyer KEEPS and anyone re-verifies against the chain
3635
+ * (see {@link PipRailClient.verifyReceipt}). `null` when the last settled fetch carried
3636
+ * no receipt (the gate's `receipts` option was off) or no payment has settled yet. Pure.
3637
+ */
3638
+ lastReceipt() {
3639
+ return this.lastReceiptValue;
3640
+ }
3641
+ /**
3642
+ * Re-verify ANY {@link PipRailReceipt} against the chain — the anyone-can-run primitive.
3643
+ * Re-reads `receipt.transaction` via the receipt's own network driver and re-derives
3644
+ * `payTo`/`asset`/`payer` from the tx, **never trusting the receipt's claims**: a forged
3645
+ * `payTo`/`asset`/over-stated `amount` makes the driver's `verify()` fail (`ok:false`); a
3646
+ * forged `payer` surfaces as `matchesClaims:false`. Static + WALLET-FREE — a third party
3647
+ * verifies with only a chain + RPC, no PipRail account. **Never throws** (an RPC error or a
3648
+ * malformed receipt → `{ ok:false, error }`). Viem-free here — the chain read happens inside
3649
+ * the lazily-mounted family driver (the protocol layer pulls no chain libs). Durable for
3650
+ * digest-bound families; recency-bounded for the account-watch families (see
3651
+ * {@link ReceiptVerification}).
3652
+ */
3653
+ static async verifyReceipt(receipt, opts) {
3654
+ const r = receipt?.receipt;
3655
+ if (!r || typeof r !== "object") {
3656
+ return { ok: false, onChain: { payTo: "", asset: "", amount: "", payer: "" }, matchesClaims: false, ageSeconds: 0, error: "tx_not_found" };
3657
+ }
3658
+ const claimed = { payTo: r.payTo ?? "", asset: r.asset ?? "", amount: r.amount ?? "", payer: r.payer ?? "" };
3659
+ const ageSeconds = receiptAgeSeconds(r.verifiedAt);
3660
+ try {
3661
+ const chain = chainSelectorForNetwork(r.network, opts?.rpcUrl);
3662
+ const net = await resolveNetwork2({
3663
+ chain,
3664
+ ...opts?.rpcUrl ? { rpcUrl: opts.rpcUrl } : {}
3665
+ });
3666
+ const accept = {
3667
+ scheme: "onchain-proof",
3668
+ network: r.network,
3669
+ amount: r.amount,
3670
+ asset: r.asset,
3671
+ payTo: r.payTo,
3672
+ maxTimeoutSeconds: RECEIPT_VERIFY_WINDOW_SECONDS,
3673
+ extra: {
3674
+ nonce: r.nonce ?? "",
3675
+ // the challenge/memo nonce — REQUIRED for Template-A re-verify
3676
+ decimals: receipt.decimals ?? 6,
3677
+ // B10c — Stellar/XRPL/TON re-scale by it; 6 = USDC fallback
3678
+ minConfirmations: 0,
3679
+ amountFormatted: ""
3680
+ }
3681
+ };
3682
+ const ref = familyForChain(chain) === "near" ? `${r.payer}:${r.transaction}` : r.transaction;
3683
+ const result = await net.verify(ref, accept);
3684
+ if (!result.ok) {
3685
+ return { ok: false, onChain: claimed, matchesClaims: false, ageSeconds, error: result.error };
3686
+ }
3687
+ const oc = result.receipt;
3688
+ const onChain = { payTo: oc.payTo, asset: oc.asset, amount: oc.amount, payer: oc.payer };
3689
+ const matchesClaims = sameAddress(oc.payer, r.payer);
3690
+ return { ok: true, onChain, matchesClaims, ageSeconds };
3691
+ } catch {
3692
+ return { ok: false, onChain: claimed, matchesClaims: false, ageSeconds, error: "tx_not_found" };
3693
+ }
3694
+ }
3695
+ /**
3696
+ * Verify the OPTIONAL Tier-2 service-delivery attestation on a {@link PipRailReceipt}
3697
+ * — the merchant's signed proof that the resource was actually SERVED (the one thing
3698
+ * the chain can't attest). For an EVM EIP-712 attestation this re-recovers the signer
3699
+ * from the signature over the official `offer-receipt` typed data and checks
3700
+ * `recover === receipt.payTo` (spec §4.5.1 / §5.5) — the classic EIP-712 footgun made
3701
+ * safe (`recoverTypedDataAddress` returns a WRONG address rather than throwing on a bad
3702
+ * signature, so the equality check is the real verification). A tampered signature →
3703
+ * `{ ok:false }`, never a throw.
3704
+ *
3705
+ * Static + wallet-free. **Never throws** (a malformed/absent attestation, an unsupported
3706
+ * format, or a recovery fault → `{ ok:false, reason }`). Viem-free HERE — the recover runs
3707
+ * inside the lazily-imported EVM receipt driver (a lazy chunk), so the protocol layer pulls
3708
+ * no chain libs. The JWS format defers to R3 (`{ ok:false, reason:'jws-not-loaded' }`).
3709
+ */
3710
+ static async verifyAttestation(receipt) {
3711
+ const att = receipt?.attestation;
3712
+ if (!att || typeof att !== "object" || typeof att.signature !== "string") {
3713
+ return { ok: false, reason: "no-attestation" };
3714
+ }
3715
+ if (att.format === "jws") {
3716
+ return { ok: false, reason: "jws-not-loaded" };
3717
+ }
3718
+ const r = receipt.receipt;
3719
+ if (!r || typeof r !== "object") return { ok: false, reason: "no-receipt" };
3720
+ const payload = att.payload ?? {};
3721
+ const network = typeof payload.network === "string" ? payload.network : r.network;
3722
+ const resourceUrl = typeof payload.resourceUrl === "string" ? payload.resourceUrl : receipt.resource?.url ?? "";
3723
+ const payer = typeof payload.payer === "string" ? payload.payer : r.payer;
3724
+ const issuedAt = typeof payload.issuedAt === "number" ? payload.issuedAt : receiptIssuedAtSeconds(r.verifiedAt);
3725
+ const transaction = typeof payload.transaction === "string" ? payload.transaction : r.transaction ?? "";
3726
+ try {
3727
+ const { verifyReceiptAttestationEvm } = await import("./receipt-NNEID77X.js");
3728
+ return await verifyReceiptAttestationEvm({
3729
+ payTo: r.payTo,
3730
+ network,
3731
+ resourceUrl,
3732
+ payer,
3733
+ issuedAt,
3734
+ transaction,
3735
+ signature: att.signature
3736
+ });
3737
+ } catch {
3738
+ return { ok: false, reason: "verify-failed" };
3739
+ }
3740
+ }
3135
3741
  /** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
3136
3742
  ensure() {
3137
3743
  return this.bound ??= (async () => {
@@ -3211,9 +3817,14 @@ var PipRailClient = class {
3211
3817
  async estimateCost(url, init) {
3212
3818
  const res = await fetch(url, { ...init ?? {}, method: init?.method ?? "GET" });
3213
3819
  if (res.status !== 402) return null;
3214
- const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
3215
- const cost = await net.estimateCost(accept);
3216
- return { quote, cost };
3820
+ try {
3821
+ const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
3822
+ const cost = await net.estimateCost(accept);
3823
+ return { quote, cost };
3824
+ } catch (err) {
3825
+ if (err instanceof InvalidEnvelopeError) return null;
3826
+ throw err;
3827
+ }
3217
3828
  }
3218
3829
  /** Aggregated snapshot of every payment this client has settled — total
3219
3830
  * count, cumulative spend per token, cumulative spend per denomination (the
@@ -3565,9 +4176,17 @@ var PipRailClient = class {
3565
4176
  }
3566
4177
  this.safeEmit({ kind: "payment-required", challenge, accept });
3567
4178
  await this.authorize(quote);
4179
+ if (accept.scheme === "upto") {
4180
+ return this.payUptoRail(net, wallet, accept, url, init, quote);
4181
+ }
3568
4182
  if (accept.scheme === "exact") {
3569
4183
  return this.payExactRail(net, wallet, accept, url, init, quote);
3570
4184
  }
4185
+ if (accept.scheme !== "onchain-proof") {
4186
+ throw new UnsupportedSchemeError(
4187
+ `internal: unrouted accept scheme '${accept.scheme}' reached the onchain-proof pay path.`
4188
+ );
4189
+ }
3571
4190
  const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
3572
4191
  const response = await this.retryWithProof(url, init, accept, ref, confirmed);
3573
4192
  this.recordSpend(quote, ref);
@@ -3654,6 +4273,13 @@ var PipRailClient = class {
3654
4273
  )
3655
4274
  );
3656
4275
  }
4276
+ if (schemes.includes("upto")) {
4277
+ out.push(
4278
+ ...challenge.accepts.filter(
4279
+ (a) => a.scheme === "upto" && this.supportsNetwork(net, a.network) && typeof net.payUpto === "function" && net.describeAsset(a.asset) != null && typeof a.extra?.facilitatorAddress === "string" && a.extra.facilitatorAddress.length > 0 && Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
4280
+ )
4281
+ );
4282
+ }
3657
4283
  return out;
3658
4284
  }
3659
4285
  /** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
@@ -3675,11 +4301,11 @@ var PipRailClient = class {
3675
4301
  ...session ? { session } : {}
3676
4302
  };
3677
4303
  }
3678
- const analysed = await Promise.all(
4304
+ const analysed = (await Promise.all(
3679
4305
  candidates.map(
3680
- (accept) => this.analyzeRail(net, wallet, accept, url, challenge.resource.description)
4306
+ (accept) => this.analyzeRail(net, wallet, accept, url, challenge.resource.description).catch(() => null)
3681
4307
  )
3682
- );
4308
+ )).filter((o) => o !== null);
3683
4309
  const options = rankOptions(analysed);
3684
4310
  const best = options.find((o) => o.state === "payable") ?? null;
3685
4311
  const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
@@ -3703,7 +4329,7 @@ var PipRailClient = class {
3703
4329
  const rr = await net.recipientReady(accept.payTo, accept.asset).catch(() => ({ ready: "unknown" }));
3704
4330
  const amount = BigInt(accept.amount);
3705
4331
  const fee = safeBig(cost.fee);
3706
- const isExact = accept.scheme === "exact";
4332
+ const isExact = accept.scheme === "exact" || accept.scheme === "upto";
3707
4333
  const isNative = accept.asset === "native";
3708
4334
  const blockers = [];
3709
4335
  const warnings = [];
@@ -3909,16 +4535,41 @@ var PipRailClient = class {
3909
4535
  * denomination it counts toward in the grand total). Then fire the `onSpend` callback
3910
4536
  * with the record + the post-payment budget, and emit any `warnAtFraction` thresholds
3911
4537
  * this payment just crossed. All observability is isolated — a throwing hook never
3912
- * affects the (already-settled) payment. */
3913
- recordSpend(quote, ref) {
4538
+ * affects the (already-settled) payment.
4539
+ *
4540
+ * `settledAmountBase` is the SINGLE upto ledger-reconciliation seam: the quote (and thus
4541
+ * the policy/budget) gates on the MAX, and for the metered `upto` rail the budgeted amount
4542
+ * RECORDED is ALSO the authorized MAX — the only buyer-provable bound. The merchant's
4543
+ * claimed actual is UNTRUSTED (a malicious merchant can settle the MAX on-chain yet report
4544
+ * a tiny `SettleOutcome.amount`); recording it would let an under-report silently loosen a
4545
+ * cumulative cap (`maxTotal`/`maxTotalPerDenom`/`windowTotal`) past the buyer's real on-chain
4546
+ * spend (POL-1). So the cap-bearing `amountBase` is the MAX; the clamped actual is surfaced
4547
+ * separately on `settledBase`/`settledFormatted` for transparency (it equals the receipt's
4548
+ * amount). When absent (onchain-proof/exact) this is byte-identical to before. */
4549
+ recordSpend(quote, ref, settledAmountBase) {
3914
4550
  const denom = denomOf(quote.symbol, quote.asset, this.opts.policy);
4551
+ const amountBase = quote.amount;
4552
+ const amountFormatted = quote.amountFormatted;
4553
+ let settledBase;
4554
+ let settledFormatted;
4555
+ if (settledAmountBase !== void 0 && /^\d+$/.test(settledAmountBase)) {
4556
+ try {
4557
+ const claimed = BigInt(settledAmountBase);
4558
+ const max = BigInt(quote.amount);
4559
+ const shown = claimed < max ? claimed : max;
4560
+ settledBase = shown.toString();
4561
+ settledFormatted = formatUnits(shown, quote.decimals);
4562
+ } catch {
4563
+ }
4564
+ }
3915
4565
  const record = {
3916
4566
  url: quote.url,
3917
4567
  host: hostOf2(quote.url),
3918
4568
  network: quote.network,
3919
4569
  asset: quote.asset,
3920
- amountBase: quote.amount,
3921
- amountFormatted: quote.amountFormatted,
4570
+ amountBase,
4571
+ amountFormatted,
4572
+ ...settledBase !== void 0 ? { settledBase, settledFormatted } : {},
3922
4573
  ...quote.symbol ? { symbol: quote.symbol } : {},
3923
4574
  decimals: quote.decimals,
3924
4575
  ...denom ? { denom } : {},
@@ -4062,6 +4713,7 @@ var PipRailClient = class {
4062
4713
  }
4063
4714
  if (lastResponse.status !== 402) {
4064
4715
  const receipt = parseReceipt(lastResponse);
4716
+ this.captureReceipt(lastResponse, url);
4065
4717
  this.safeEmit({ kind: "payment-settled", receipt });
4066
4718
  return lastResponse;
4067
4719
  }
@@ -4146,6 +4798,7 @@ var PipRailClient = class {
4146
4798
  }
4147
4799
  if (response.ok && !(settle && settle.success === false)) {
4148
4800
  const receipt = parseReceipt(response);
4801
+ this.captureReceipt(response, url);
4149
4802
  this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
4150
4803
  const ref = settle?.transaction || receipt?.transaction || `eip3009-nonce:${nonce}`;
4151
4804
  this.recordSpend(quote, ref);
@@ -4170,6 +4823,91 @@ var PipRailClient = class {
4170
4823
  { ref: nonce }
4171
4824
  );
4172
4825
  }
4826
+ /**
4827
+ * The standard `upto` (metered) buyer path — a near-clone of {@link payExactRail} with TWO
4828
+ * deltas: (1) it signs a Permit2-upto authorization for the MAX via `payUpto` + frames it
4829
+ * with `buildUptoSignatureHeader`; (2) it records the ACTUAL settled amount (read off the
4830
+ * SettleResponse's required `amount` field, via `recordSpend(quote, ref, settle.amount)`) in
4831
+ * the ledger — the budget gated on the MAX, the ledger records the ACTUAL. A server that omits
4832
+ * `settle.amount` FAILS SAFE to the MAX (over-counts, never under-counts). The buyer SIGNS, the
4833
+ * merchant self-settles — the buyer never broadcasts.
4834
+ */
4835
+ async payUptoRail(net, wallet, accept, url, init, quote) {
4836
+ if (!net.payUpto) {
4837
+ throw new UnsupportedSchemeError(
4838
+ `the ${net.family} family can't pay a standard 'upto' rail (EVM-Permit2 only today).`
4839
+ );
4840
+ }
4841
+ throwIfAborted(init?.signal);
4842
+ const { payload, accepted, payerFrom, nonce } = await net.payUpto(wallet, accept);
4843
+ const headers = new Headers(init?.headers);
4844
+ headers.set(HEADER_SIGNATURE, buildUptoSignatureHeader({ accepted, payload }));
4845
+ const rejectDefinitive = (why2) => {
4846
+ this.safeEmit({ kind: "payment-failed", reason: `upto: facilitator rejected nonce=${nonce} (${why2})`, code: why2 });
4847
+ throw new MaxRetriesExceededError(
4848
+ `upto: the server 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}.`,
4849
+ { ref: nonce }
4850
+ );
4851
+ };
4852
+ const deadline = Date.now() + Math.max(1, Math.floor(accept.maxTimeoutSeconds / 2)) * 1e3;
4853
+ const maxAttempts = Math.min(this.maxRetries, 3);
4854
+ let lastReason = null;
4855
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
4856
+ if (attempt > 0) {
4857
+ if (Date.now() >= deadline) break;
4858
+ await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
4859
+ }
4860
+ throwIfAborted(init?.signal);
4861
+ const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
4862
+ if (budget <= 0) break;
4863
+ const timeoutController = new AbortController();
4864
+ const timeoutId = setTimeout(() => timeoutController.abort(), budget);
4865
+ const signal = init?.signal && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
4866
+ let response;
4867
+ try {
4868
+ response = await fetch(url, { ...init ?? {}, headers, signal });
4869
+ } catch (err) {
4870
+ throw new PaymentTimeoutError(
4871
+ `upto: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The merchant may have already settled it \u2014 verify on-chain before re-presenting; do NOT re-pay.`,
4872
+ { cause: err, ref: nonce }
4873
+ );
4874
+ } finally {
4875
+ clearTimeout(timeoutId);
4876
+ }
4877
+ const settle = parseSettleResponse(response);
4878
+ if (response.status === 402) {
4879
+ if (settle && settle.success === false) rejectDefinitive(settle.errorReason ?? "the server reported success:false");
4880
+ lastReason = await readInvalidReason(response) ?? lastReason;
4881
+ continue;
4882
+ }
4883
+ if (response.ok && !(settle && settle.success === false)) {
4884
+ const receipt = parseReceipt(response);
4885
+ this.captureReceipt(response, url);
4886
+ this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
4887
+ const ref = settle?.transaction || receipt?.transaction || `upto-nonce:${nonce}`;
4888
+ const settledAmount = settle?.amount ?? receipt?.amount;
4889
+ this.recordSpend(quote, ref, settledAmount);
4890
+ return response;
4891
+ }
4892
+ if (response.status >= 500) {
4893
+ this.safeEmit({ kind: "payment-failed", reason: `upto: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
4894
+ return response;
4895
+ }
4896
+ if (settle && settle.success === false) rejectDefinitive(settle.errorReason ?? "the server reported success:false");
4897
+ this.safeEmit({ kind: "payment-failed", reason: `upto: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
4898
+ return response;
4899
+ }
4900
+ const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
4901
+ this.safeEmit({
4902
+ kind: "payment-failed",
4903
+ reason: `upto: 402 after submitting authorization nonce=${nonce} (${why})`,
4904
+ ...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
4905
+ });
4906
+ throw new MaxRetriesExceededError(
4907
+ `upto: 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. ref=${nonce}.`,
4908
+ { ref: nonce }
4909
+ );
4910
+ }
4173
4911
  };
4174
4912
  function throwIfAborted(signal) {
4175
4913
  if (signal?.aborted) {
@@ -4350,6 +5088,38 @@ async function readInvalidReason(response) {
4350
5088
  if (settle?.errorReason) return { error: settle.errorReason, detail: "" };
4351
5089
  return null;
4352
5090
  }
5091
+ var EVM_PRESET_FOR_CHAINID = {
5092
+ 1: "ethereum",
5093
+ 8453: "base",
5094
+ 137: "polygon",
5095
+ 42161: "arbitrum",
5096
+ 10: "optimism",
5097
+ 43114: "avalanche",
5098
+ 56: "bnb"
5099
+ };
5100
+ function chainSelectorForNetwork(network, rpcUrl) {
5101
+ const chainId = chainIdFromNetwork(network);
5102
+ if (chainId !== null) {
5103
+ const preset = EVM_PRESET_FOR_CHAINID[chainId];
5104
+ if (preset) return preset;
5105
+ return { id: chainId, rpcUrl: rpcUrl ?? "" };
5106
+ }
5107
+ const namespace = network.split(":")[0] ?? "";
5108
+ return namespace === "tvm" ? "ton" : namespace;
5109
+ }
5110
+ function receiptAgeSeconds(verifiedAt) {
5111
+ const t = Date.parse(verifiedAt);
5112
+ if (Number.isNaN(t)) return 0;
5113
+ return Math.max(0, Math.round((Date.now() - t) / 1e3));
5114
+ }
5115
+ function receiptIssuedAtSeconds(verifiedAt) {
5116
+ const t = Date.parse(verifiedAt);
5117
+ if (Number.isNaN(t)) return 0;
5118
+ return Math.floor(t / 1e3);
5119
+ }
5120
+ function sameAddress(a, b) {
5121
+ return typeof a === "string" && typeof b === "string" && a.toLowerCase() === b.toLowerCase();
5122
+ }
4353
5123
 
4354
5124
  // src/payer.ts
4355
5125
  var MultiChainPayer = class _MultiChainPayer {
@@ -4582,7 +5352,13 @@ var BRAND = {
4582
5352
  };
4583
5353
  var WHAT = 'This is an x402 "402 Payment Required" endpoint. Pay one of the offered rails to access it.';
4584
5354
  function howFor(scheme) {
4585
- return scheme === "exact" ? "Standard x402 exact rail \u2014 sign an EIP-3009 / Permit2 / SVM authorization; any stock x402 client (e.g. @x402/fetch) can pay this." : "Pay this amount on-chain to payTo, then resubmit with a payment-signature header carrying the proof ref + nonce. Easiest with @piprail/sdk (see sdk.install).";
5355
+ if (scheme === "exact") {
5356
+ return "Standard x402 exact rail \u2014 sign an EIP-3009 / Permit2 / SVM authorization; any stock x402 client (e.g. @x402/fetch) can pay this.";
5357
+ }
5358
+ if (scheme === "upto") {
5359
+ return "Standard x402 upto (metered) rail \u2014 sign a Permit2 authorization for the MAX amount; the server settles the ACTUAL (\u2264 max) after serving. BUDGET AGAINST THE MAX \u2014 the server MAY fully charge it. EVM-Permit2 only; enable with schemes:['upto'].";
5360
+ }
5361
+ return "Pay this amount on-chain to payTo, then resubmit with a payment-signature header carrying the proof ref + nonce. Easiest with @piprail/sdk (see sdk.install).";
4586
5362
  }
4587
5363
  function railOf(a) {
4588
5364
  const extra = a.extra ?? {};
@@ -4609,6 +5385,7 @@ function buildSelfDescription(input) {
4609
5385
  mcp: { run: BRAND.mcpRun, tool: "piprail_pay_request" },
4610
5386
  docs: { home: BRAND.home, agents: BRAND.docs, pay: BRAND.payDocs },
4611
5387
  discovery: { openapi: "/openapi.json", wellKnown: "/.well-known/x402" },
5388
+ ...input.verifiableReceipts ? { verifiableReceipts: true } : {},
4612
5389
  ...input.instruction ? { instruction: input.instruction } : {}
4613
5390
  };
4614
5391
  }
@@ -4708,17 +5485,21 @@ and pay with the tools below.
4708
5485
  Always plan before you pay so you never commit to a payment you cannot finish.
4709
5486
 
4710
5487
  ## Gasless \u2014 the exact rail (zero gas for you)
4711
- A 402 may offer up to two rails; you don't choose per payment \u2014 the client does, automatically:
5488
+ A 402 may offer up to three rails; you don't choose per payment \u2014 the client does, automatically:
4712
5489
  - onchain-proof (PipRail's default): you broadcast the payment yourself and pay the network gas
4713
5490
  (the native coin \u2014 ETH/SOL/\u2026). Works on every chain.
4714
5491
  - exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose
4715
5492
  (e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It
4716
5493
  works on EVM, Solana + Algorand, and the on-chain method (EIP-3009 / Permit2 / SVM / Algorand
4717
5494
  fee-pooled group) is picked automatically.
5495
+ - upto (the metered/variable x402 rail, opt-in, EVM): the amount you see is a MAXIMUM \u2014 you sign
5496
+ a ceiling, the server meters real usage and settles the ACTUAL (<= the max). BUDGET AGAINST THE MAX:
5497
+ the plan/policy treat the ceiling as the spend (a server may charge up to it), so a payable plan
5498
+ means the MAX fits your budget; the settled actual is recorded for reconciliation.
4718
5499
  When the exact scheme is enabled AND balance-aware routing is on, paying picks the cheapest
4719
5500
  settleable rail \u2014 i.e. the gasless exact one. Nothing changes in your loop: quote \u2192 plan \u2192 pay is
4720
- identical. The exact scheme is OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact);
4721
- you can't enable it yourself, but you can report when a 402 needs it (see UNSUPPORTED_SCHEME below).
5501
+ identical. The exact/upto schemes are OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact,upto);
5502
+ you can't enable them yourself, but you can report when a 402 needs one (see UNSUPPORTED_SCHEME below).
4722
5503
 
4723
5504
  ## Reading a refusal \u2014 never crash, never double-spend
4724
5505
  A failed pay returns a STRUCTURED object, never a thrown error you must catch:
@@ -5001,11 +5782,13 @@ function paymentTools(client) {
5001
5782
  }
5002
5783
  res = await client.fetch(url, { method, headers, body });
5003
5784
  }
5785
+ const verifiable = parseReceiptExtension(res);
5004
5786
  return {
5005
5787
  status: res.status,
5006
5788
  ok: res.ok,
5007
5789
  body: await readBody(res),
5008
- receipt: parseReceipt(res)
5790
+ receipt: parseReceipt(res),
5791
+ ...verifiable ? { verifiableReceipt: { ...verifiable, resource: { url } } } : {}
5009
5792
  };
5010
5793
  } catch (err) {
5011
5794
  if (err instanceof PipRailError) {
@@ -5125,6 +5908,38 @@ function paymentTools(client) {
5125
5908
  },
5126
5909
  parameters: { type: "object", properties: {}, additionalProperties: false },
5127
5910
  invoke: async () => ({ guide: PIPRAIL_AGENT_GUIDE })
5911
+ },
5912
+ {
5913
+ name: "piprail_verify_receipt",
5914
+ description: "Re-verify a PipRail VERIFIABLE RECEIPT against the chain \u2014 confirm a payment REALLY settled (the funds provably moved to payTo for AT LEAST the stated amount) WITHOUT trusting whoever handed you the receipt. Read-only and WALLET-FREE: pass the PipRailReceipt JSON (from a prior piprail_pay_request `verifiableReceipt`, or any third party). Returns { ok, onChain:{payTo,asset,amount,payer}, matchesClaims, ageSeconds, error? }: `ok` = the chain confirms the settlement; `onChain.payer` is RE-DERIVED from the tx and `matchesClaims:false` means the receipt forged the payer; `amount` is a verified lower bound. Pass `rpcUrl` for a chain outside the common presets.",
5915
+ annotations: {
5916
+ title: "Verify a payment receipt",
5917
+ readOnlyHint: true,
5918
+ // re-reads the chain; moves nothing, needs no wallet
5919
+ idempotentHint: true,
5920
+ openWorldHint: true
5921
+ // reads an on-chain tx via RPC
5922
+ },
5923
+ parameters: {
5924
+ type: "object",
5925
+ properties: {
5926
+ receipt: {
5927
+ type: "object",
5928
+ description: "The PipRailReceipt JSON ({ piprail, receipt, resource, decimals? }) to re-verify."
5929
+ },
5930
+ rpcUrl: {
5931
+ type: "string",
5932
+ description: "Optional RPC URL for the receipt's chain (required for chains outside the common presets)."
5933
+ }
5934
+ },
5935
+ required: ["receipt"],
5936
+ additionalProperties: false
5937
+ },
5938
+ invoke: async (args) => {
5939
+ const receipt = args.receipt;
5940
+ const opts = args.rpcUrl ? { rpcUrl: String(args.rpcUrl) } : void 0;
5941
+ return await PipRailClient.verifyReceipt(receipt, opts);
5942
+ }
5128
5943
  }
5129
5944
  ];
5130
5945
  }
@@ -5774,17 +6589,40 @@ function normaliseExactOption(exact) {
5774
6589
  return exact;
5775
6590
  }
5776
6591
  var TRANSIENT_VERIFY_CODES = /* @__PURE__ */ new Set(["tx_not_found", "insufficient_confirmations"]);
6592
+ function nowUnixSeconds(verifiedAt) {
6593
+ if (verifiedAt) {
6594
+ const t = Date.parse(verifiedAt);
6595
+ if (!Number.isNaN(t)) return Math.floor(t / 1e3);
6596
+ }
6597
+ return Math.floor(Date.now() / 1e3);
6598
+ }
5777
6599
  function createPaymentGate(options) {
5778
6600
  const minConfirmations = options.minConfirmations ?? 1;
5779
6601
  const maxTimeoutSeconds = options.maxTimeoutSeconds ?? 600;
5780
6602
  const genNonce = options.generateNonce ?? (() => globalThis.crypto.randomUUID());
5781
6603
  const exactOption = normaliseExactOption(options.exact);
6604
+ const receiptsOn = options.receipts !== void 0 && options.receipts !== false;
6605
+ const receiptOpt = typeof options.receipts === "object" ? options.receipts : {};
6606
+ const receiptIncludeTxHash = receiptOpt.includeTxHash !== false;
6607
+ const receiptResourceUrl = receiptOpt.resource ?? "";
6608
+ const attestWallet = receiptOpt.attest && "wallet" in receiptOpt.attest ? receiptOpt.attest.wallet : void 0;
6609
+ const attestJws = receiptOpt.attest && "jws" in receiptOpt.attest;
6610
+ let attestWarned = false;
6611
+ function warnAttestDegrade(reason) {
6612
+ if (attestWarned) return;
6613
+ attestWarned = true;
6614
+ try {
6615
+ console.warn(`[piprail] receipts.attest ${reason}; emitting an unsigned Tier-1 receipt instead.`);
6616
+ } catch {
6617
+ }
6618
+ }
5782
6619
  let resolved;
5783
6620
  function ready() {
5784
6621
  if (resolved) return resolved;
5785
6622
  const p = (async () => {
5786
6623
  const accepts = normaliseAccepts(options);
5787
6624
  const exactSkips = [];
6625
+ const uptoSkips = [];
5788
6626
  const specs = await Promise.all(
5789
6627
  accepts.map(async (a) => {
5790
6628
  const net = await resolveNetwork2({ chain: a.chain, rpcUrl: a.rpcUrl ?? options.rpcUrl });
@@ -5803,9 +6641,19 @@ function createPaymentGate(options) {
5803
6641
  if (outcome.rail) spec.exact = outcome.rail;
5804
6642
  else if (outcome.skipReason) exactSkips.push(outcome.skipReason);
5805
6643
  }
6644
+ if (options.upto) {
6645
+ const upto = await resolveUptoRail(net, asset);
6646
+ if (upto) spec.upto = upto;
6647
+ else uptoSkips.push(`${net.network}/${asset}`);
6648
+ }
5806
6649
  return spec;
5807
6650
  })
5808
6651
  );
6652
+ if (options.upto && !specs.some((s) => s.upto)) {
6653
+ throw new Error(
6654
+ `requirePayment: \`upto\` (the metered rail) was requested but none of the offered rails support it (tried: ${uptoSkips.join(", ") || "none"}). The \`upto\` scheme is EVM-Permit2 ONLY \u2014 an ERC-20 on a chain with the x402UptoPermit2Proxy deployed (Ethereum/Base/Arbitrum/Optimism/Polygon/BNB), NOT a native coin, NOT a non-EVM family. (Native coins + non-Permit2 chains can never carry \`upto\`.)`
6655
+ );
6656
+ }
5809
6657
  if (exactOption && !specs.some((s) => s.exact)) {
5810
6658
  const why = exactSkips.length > 0 ? exactSkips.join(" ") : "The standard `exact` rail is EVM ERC-20 (EIP-3009 \u2014 USDC / EURC \u2014 or Permit2, e.g. Binance-Peg USDC on BNB) or a Solana SPL token (SVM) \u2014 NOT native coins, NOT families without a standard `exact` scheme.";
5811
6659
  if (exactOption.settle === "keyless") {
@@ -5885,7 +6733,27 @@ function createPaymentGate(options) {
5885
6733
  };
5886
6734
  return { rail: { method: info.method, ...info.extra ? { extra: info.extra } : {}, mode } };
5887
6735
  }
5888
- const hasCustomStore = Boolean(options.isUsed || options.markUsed);
6736
+ async function resolveUptoRail(net, asset) {
6737
+ const cfg = options.upto;
6738
+ if (!net.resolveUptoRail) return null;
6739
+ if (cfg.relayer === void 0) {
6740
+ throw new Error(
6741
+ "requirePayment: `upto` needs a `relayer` wallet (the gas-paying key that self-settles the metered actual AND is the bound witness.facilitator), e.g. upto: { relayer: { key }, settleAmount }."
6742
+ );
6743
+ }
6744
+ const relayer = net.bindWallet(cfg.relayer);
6745
+ const info = await net.resolveUptoRail({ asset, relayer });
6746
+ if (!info) return null;
6747
+ return { method: info.method, ...info.extra ? { extra: info.extra } : {}, relayer };
6748
+ }
6749
+ const hasIsUsed = typeof options.isUsed === "function";
6750
+ const hasMarkUsed = typeof options.markUsed === "function";
6751
+ if (hasIsUsed !== hasMarkUsed) {
6752
+ throw new Error(
6753
+ "requirePayment/createPaymentGate: `isUsed` and `markUsed` must be provided TOGETHER \u2014 a custom replay store needs both a read and a write. Supplying only " + (hasIsUsed ? "`isUsed`" : "`markUsed`") + " silently disables replay protection (double-spend). Provide both, or neither (the built-in in-memory store)."
6754
+ );
6755
+ }
6756
+ const hasCustomStore = hasIsUsed && hasMarkUsed;
5889
6757
  const localUsed = /* @__PURE__ */ new Map();
5890
6758
  const replayWindowMs = maxTimeoutSeconds * 1e3;
5891
6759
  function pruneUsed(now) {
@@ -5948,10 +6816,33 @@ function createPaymentGate(options) {
5948
6816
  }
5949
6817
  };
5950
6818
  }
6819
+ function buildUptoAccept(s) {
6820
+ const rail = s.upto;
6821
+ return {
6822
+ scheme: "upto",
6823
+ network: s.net.network,
6824
+ amount: s.amountBase.toString(),
6825
+ // the authorized MAX (base units)
6826
+ asset: s.asset,
6827
+ payTo: s.payTo,
6828
+ maxTimeoutSeconds,
6829
+ extra: {
6830
+ assetTransferMethod: "permit2-upto",
6831
+ // facilitatorAddress comes from rail.extra; the spread below carries it (+ name/version).
6832
+ facilitatorAddress: rail.extra?.facilitatorAddress ?? "",
6833
+ minConfirmations,
6834
+ decimals: s.decimals,
6835
+ amountFormatted: s.amountFormatted,
6836
+ ...s.symbol ? { symbol: s.symbol } : {},
6837
+ ...rail.extra
6838
+ }
6839
+ };
6840
+ }
5951
6841
  function buildAccepts(specs, nonce) {
5952
6842
  const out = [];
5953
6843
  for (const s of specs) {
5954
6844
  if (s.exact) out.push(buildExactAccept(s));
6845
+ if (s.upto) out.push(buildUptoAccept(s));
5955
6846
  out.push(buildAccept(s, nonce));
5956
6847
  }
5957
6848
  return out;
@@ -5969,7 +6860,8 @@ function createPaymentGate(options) {
5969
6860
  const selfDescribe = options.selfDescribe === false ? void 0 : buildSelfDescription({
5970
6861
  accepts,
5971
6862
  instruction: describeChallenge({ x402Version: 2, resource: { url: resourceUrl }, accepts }),
5972
- ...endpointInfo ? { endpoint: endpointInfo } : {}
6863
+ ...endpointInfo ? { endpoint: endpointInfo } : {},
6864
+ ...receiptsOn ? { verifiableReceipts: true } : {}
5973
6865
  });
5974
6866
  const rejectionExt = opts?.extensions ?? {};
5975
6867
  const rejectionPiprail = rejectionExt.piprail ?? {};
@@ -6058,6 +6950,55 @@ function createPaymentGate(options) {
6058
6950
  if (options.awaitOnPaid) await fireOnPaid(paid);
6059
6951
  else void fireOnPaid(paid);
6060
6952
  }
6953
+ async function buildPaidResult(spec, receipt, nonce) {
6954
+ if (!receiptsOn) {
6955
+ return { kind: "paid", receipt, receiptHeader: buildReceiptHeader(receipt) };
6956
+ }
6957
+ try {
6958
+ const stamped = {
6959
+ ...receipt,
6960
+ ...nonce ? { nonce } : {},
6961
+ // §5.3: a suppressed tx is the empty string on the wire, never a missing key.
6962
+ ...receiptIncludeTxHash ? {} : { transaction: "" }
6963
+ };
6964
+ const attestation = await maybeSignAttestation(spec, stamped);
6965
+ const extensions = buildReceiptExtension({
6966
+ receipt: stamped,
6967
+ resource: { url: receiptResourceUrl },
6968
+ decimals: spec.decimals,
6969
+ ...attestation ? { attestation } : {}
6970
+ });
6971
+ return { kind: "paid", receipt: stamped, receiptHeader: buildReceiptHeader(stamped, extensions) };
6972
+ } catch {
6973
+ return { kind: "paid", receipt, receiptHeader: buildReceiptHeader(receipt) };
6974
+ }
6975
+ }
6976
+ async function maybeSignAttestation(spec, stamped) {
6977
+ if (attestJws) {
6978
+ warnAttestDegrade("JWS attestation is not yet implemented (R3)");
6979
+ return void 0;
6980
+ }
6981
+ if (attestWallet === void 0) return void 0;
6982
+ if (typeof spec.net.signReceipt !== "function") {
6983
+ warnAttestDegrade(`is EVM-only; the ${spec.net.family} rail can't sign an EIP-712 attestation`);
6984
+ return void 0;
6985
+ }
6986
+ try {
6987
+ const wallet = spec.net.bindWallet(attestWallet);
6988
+ return await spec.net.signReceipt(wallet, {
6989
+ payTo: spec.payTo,
6990
+ network: spec.net.network,
6991
+ resourceUrl: receiptResourceUrl,
6992
+ payer: stamped.payer,
6993
+ issuedAt: nowUnixSeconds(stamped.verifiedAt),
6994
+ // §5.3: the signed message carries the empty string for a suppressed tx, never omitted.
6995
+ transaction: receiptIncludeTxHash ? stamped.transaction : ""
6996
+ });
6997
+ } catch {
6998
+ warnAttestDegrade("signing failed");
6999
+ return void 0;
7000
+ }
7001
+ }
6061
7002
  function reportOnFailedError(error, failure) {
6062
7003
  if (!options.onFailedError) return;
6063
7004
  try {
@@ -6084,6 +7025,14 @@ function createPaymentGate(options) {
6084
7025
  if (options.awaitOnFailed) await fireOnFailed(failure);
6085
7026
  else void fireOnFailed(failure);
6086
7027
  }
7028
+ function railExtra(full) {
7029
+ const out = {};
7030
+ for (const k of ["assetTransferMethod", "facilitatorAddress", "name", "version"]) {
7031
+ const v = full[k];
7032
+ if (v !== void 0 && v !== "") out[k] = v;
7033
+ }
7034
+ return Object.keys(out).length > 0 ? { extra: out } : {};
7035
+ }
6087
7036
  async function describe(resourceUrl = "") {
6088
7037
  const specs = await ready();
6089
7038
  const accepts = [];
@@ -6098,7 +7047,8 @@ function createPaymentGate(options) {
6098
7047
  maxTimeoutSeconds,
6099
7048
  ...s.symbol ? { symbol: s.symbol } : {}
6100
7049
  };
6101
- if (s.exact) accepts.push({ scheme: "exact", ...base2 });
7050
+ if (s.exact) accepts.push({ scheme: "exact", ...base2, ...railExtra(buildExactAccept(s).extra) });
7051
+ if (s.upto) accepts.push({ scheme: "upto", ...base2, ...railExtra(buildUptoAccept(s).extra) });
6102
7052
  accepts.push({ scheme: "onchain-proof", ...base2 });
6103
7053
  }
6104
7054
  return {
@@ -6134,7 +7084,7 @@ function createPaymentGate(options) {
6134
7084
  }
6135
7085
  await settleTx(ref, true);
6136
7086
  await deliverOnPaid(spec, result.receipt);
6137
- return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
7087
+ return await buildPaidResult(spec, result.receipt, sig.payload.nonce);
6138
7088
  }
6139
7089
  async function verifyExact(exact) {
6140
7090
  const specs = await ready();
@@ -6244,27 +7194,122 @@ function createPaymentGate(options) {
6244
7194
  }
6245
7195
  await settleTx(nonce, true);
6246
7196
  await deliverOnPaid(spec, result.receipt);
6247
- return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
7197
+ return await buildPaidResult(spec, result.receipt);
7198
+ }
7199
+ function resolveSettleAmount(raw, maxAmount, decimals) {
7200
+ if (typeof raw === "bigint") return raw < 0n ? null : raw;
7201
+ const s = raw.trim();
7202
+ if (s.length === 0) return null;
7203
+ try {
7204
+ if (s.endsWith("%")) {
7205
+ const pct = s.slice(0, -1).trim();
7206
+ if (!/^\d+(\.\d+)?$/.test(pct)) return null;
7207
+ const pctScaled = floorUnits(pct, 4);
7208
+ return maxAmount * pctScaled / (100n * 10n ** 4n);
7209
+ }
7210
+ if (s.startsWith("$")) {
7211
+ const amt = s.slice(1).trim();
7212
+ return floorUnits(amt, decimals);
7213
+ }
7214
+ if (/^\d+$/.test(s)) return BigInt(s);
7215
+ return floorUnits(s, decimals);
7216
+ } catch {
7217
+ return null;
7218
+ }
7219
+ }
7220
+ async function verifyUpto(upto) {
7221
+ const specs = await ready();
7222
+ const uptoSpecs = specs.filter((s) => s.upto);
7223
+ if (uptoSpecs.length === 0) {
7224
+ return rejection("transfer_not_found", "This resource offers no standard `upto` (metered) rail.");
7225
+ }
7226
+ const isCaip = upto.network.includes(":");
7227
+ let candidates = isCaip ? uptoSpecs.filter((s) => s.net.network === upto.network) : uptoSpecs;
7228
+ if (upto.asset) {
7229
+ candidates = candidates.filter((s) => s.asset.toLowerCase() === upto.asset.toLowerCase());
7230
+ }
7231
+ let spec = candidates[0];
7232
+ if (!isCaip && !upto.asset && uptoSpecs.length > 1) spec = void 0;
7233
+ if (!spec && !isCaip && !upto.asset && uptoSpecs.length === 1) spec = uptoSpecs[0];
7234
+ if (!spec || !spec.upto) {
7235
+ return rejection(
7236
+ "transfer_not_found",
7237
+ `No \`upto\` rail offered for ${upto.network}${upto.asset ? `/${upto.asset}` : ""} (offered: ${uptoSpecs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
7238
+ );
7239
+ }
7240
+ const nonce = upto.payload.permit2Authorization.nonce;
7241
+ if (await claimTx(nonce)) {
7242
+ return rejection("tx_already_used", `Authorization nonce ${nonce} was already redeemed.`);
7243
+ }
7244
+ const accept = buildUptoAccept(spec);
7245
+ const relayer = spec.upto.relayer;
7246
+ let result;
7247
+ try {
7248
+ const rawAmount = await options.upto.settleAmount({
7249
+ maxAmount: spec.amountBase,
7250
+ asset: spec.asset,
7251
+ network: spec.net.network,
7252
+ decimals: spec.decimals,
7253
+ ...upto.raw ? { request: upto.raw } : {}
7254
+ });
7255
+ const settleAmount = resolveSettleAmount(rawAmount, spec.amountBase, spec.decimals);
7256
+ if (settleAmount === null) {
7257
+ await settleTx(nonce, false);
7258
+ return rejection(
7259
+ "upto_settle_exceeds_max",
7260
+ `The settleAmount callback returned an unparseable amount (${String(rawAmount)}). Return a bigint, a raw/"NN%"/"$X" string, or 0.`
7261
+ );
7262
+ }
7263
+ result = await spec.net.settleUptoSelf({ relayer, payload: upto.payload, accept, settleAmount });
7264
+ } catch (err) {
7265
+ await settleTx(nonce, false);
7266
+ if (err instanceof SettlementError) throw err;
7267
+ return rejection(
7268
+ "tx_reverted",
7269
+ `upto: metering/settle failed (${err instanceof Error ? err.message : String(err)}).`
7270
+ );
7271
+ }
7272
+ if (!result.ok) {
7273
+ await settleTx(nonce, false);
7274
+ return rejection(result.error, result.detail);
7275
+ }
7276
+ await settleTx(nonce, true);
7277
+ await deliverOnPaid(spec, result.receipt);
7278
+ return await buildPaidResult(spec, result.receipt);
6248
7279
  }
6249
7280
  async function verify(paymentSignature) {
6250
- const result = await resolveVerdict(paymentSignature);
7281
+ const raw = normaliseHeader(paymentSignature);
7282
+ const result = await resolveVerdictObject(raw === void 0 ? void 0 : decodeBase64Json(raw));
6251
7283
  if (result.kind === "invalid") await deliverOnFailed(result);
6252
7284
  return result;
6253
7285
  }
6254
- async function resolveVerdict(paymentSignature) {
6255
- const raw = normaliseHeader(paymentSignature);
6256
- if (!raw) return asChallenge();
6257
- const sig = parseSignatureHeader(raw);
7286
+ async function verifyObject(payload) {
7287
+ const result = await resolveVerdictObject(payload);
7288
+ if (result.kind === "invalid") await deliverOnFailed(result);
7289
+ return result;
7290
+ }
7291
+ async function resolveVerdictObject(obj) {
7292
+ if (obj === void 0 || obj === null) return asChallenge();
7293
+ const sig = parseSignatureObject(obj);
6258
7294
  if (sig && sig.accepted && typeof sig.accepted.network === "string" && typeof sig.accepted.asset === "string") {
6259
7295
  return verifyOnchainProof(sig);
6260
7296
  }
6261
- const exact = parseExactPaymentHeader(raw);
7297
+ const upto = parseUptoObject(obj);
7298
+ if (upto) return verifyUpto(upto);
7299
+ const exact = parseExactObject(obj);
6262
7300
  if (exact) return verifyExact(exact);
6263
7301
  return asChallenge();
6264
7302
  }
6265
- return { challenge, verify, describe, landingPage };
7303
+ return { challenge, verify, verifyObject, describe, landingPage };
6266
7304
  }
6267
7305
  function requirePayment(options) {
7306
+ if (options.upto) {
7307
+ throw new class extends PipRailError {
7308
+ code = "UNSUPPORTED_SCHEME";
7309
+ }(
7310
+ "requirePayment: the 'upto' (metered) rail is unsupported through the Express middleware \u2014 it settles before the route handler serves, so metered usage is unknown at settle time. Call gate.verify() directly (createPaymentGate) and meter inside settleAmount; see docs/accepting-payments/upto-rail-seller.md."
7311
+ );
7312
+ }
6268
7313
  const gate = createPaymentGate(options);
6269
7314
  return async (req, res, next) => {
6270
7315
  let result;
@@ -6410,7 +7455,252 @@ async function deliverReceipt(receipt, options) {
6410
7455
  ...lastError ? { error: lastError } : {}
6411
7456
  };
6412
7457
  }
7458
+
7459
+ // src/transports/a2a.ts
7460
+ var A2A_X402_EXTENSION_URI_V01 = "https://github.com/google-a2a/a2a-x402/v0.1";
7461
+ var A2A_X402_EXTENSION_URI_V02 = "https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2";
7462
+ var A2A_STATUS_KEY = "x402.payment.status";
7463
+ var A2A_REQUIRED_KEY = "x402.payment.required";
7464
+ var A2A_PAYLOAD_KEY = "x402.payment.payload";
7465
+ var A2A_RECEIPTS_KEY = "x402.payment.receipts";
7466
+ var A2A_ERROR_KEY = "x402.payment.error";
7467
+ var A2A_EXTENSIONS_HEADER = "X-A2A-Extensions";
7468
+ var MAX_TASK_RECEIPTS = 64;
7469
+ var VERIFY_CODE_TO_A2A_ERROR = {
7470
+ payment_expired: "EXPIRED_PAYMENT",
7471
+ tx_already_used: "DUPLICATE_NONCE",
7472
+ amount_too_low: "INVALID_AMOUNT",
7473
+ upto_settle_exceeds_max: "INVALID_AMOUNT",
7474
+ signature_invalid: "INVALID_SIGNATURE",
7475
+ transfer_not_found: "INVALID_AMOUNT",
7476
+ wrong_recipient: "INVALID_AMOUNT",
7477
+ tx_reverted: "SETTLEMENT_FAILED",
7478
+ tx_not_found: "EXPIRED_PAYMENT",
7479
+ insufficient_confirmations: "EXPIRED_PAYMENT",
7480
+ no_meta: "INVALID_SIGNATURE",
7481
+ // The settlement-side throw code the handler emits for a SettlementError (the merchant's
7482
+ // relayer/facilitator never moved funds) — maps to the spec's SETTLEMENT_FAILED.
7483
+ settlement_failed: "SETTLEMENT_FAILED"
7484
+ };
7485
+ function toA2AErrorCode(code) {
7486
+ return VERIFY_CODE_TO_A2A_ERROR[code] ?? code;
7487
+ }
7488
+ function toA2APaymentRequired(taskId, challenge, parts) {
7489
+ const metadata = {
7490
+ [A2A_STATUS_KEY]: "payment-required",
7491
+ [A2A_REQUIRED_KEY]: challenge
7492
+ };
7493
+ return {
7494
+ kind: "task",
7495
+ id: taskId,
7496
+ status: {
7497
+ state: "input-required",
7498
+ message: { kind: "message", role: "agent", taskId, ...parts ? { parts } : {}, metadata }
7499
+ }
7500
+ };
7501
+ }
7502
+ function toA2APaymentReceipts(receipts) {
7503
+ return { [A2A_RECEIPTS_KEY]: receipts };
7504
+ }
7505
+ function toA2APaymentFailed(code, detail, receipts = [], network) {
7506
+ const entry = {
7507
+ success: false,
7508
+ transaction: "",
7509
+ ...network ? { network } : {},
7510
+ errorReason: `${code}: ${detail}`
7511
+ };
7512
+ return {
7513
+ [A2A_STATUS_KEY]: "payment-failed",
7514
+ [A2A_ERROR_KEY]: toA2AErrorCode(code),
7515
+ [A2A_RECEIPTS_KEY]: [...receipts, entry]
7516
+ };
7517
+ }
7518
+ function fromA2APaymentRequired(task) {
7519
+ const meta = task.status?.message?.metadata;
7520
+ const required = meta?.[A2A_REQUIRED_KEY];
7521
+ if (!required || typeof required !== "object") return null;
7522
+ return required;
7523
+ }
7524
+ function fromA2APaymentPayload(message) {
7525
+ const raw = message.metadata?.[A2A_PAYLOAD_KEY];
7526
+ if (raw === void 0 || raw === null) return null;
7527
+ return { raw, taskId: message.taskId ?? "" };
7528
+ }
7529
+ function defaultTaskStore() {
7530
+ const map = /* @__PURE__ */ new Map();
7531
+ function prune(now) {
7532
+ for (const [key, { expiry }] of map) {
7533
+ if (expiry > now) break;
7534
+ map.delete(key);
7535
+ }
7536
+ }
7537
+ return {
7538
+ get(taskId) {
7539
+ const now = Date.now();
7540
+ prune(now);
7541
+ const hit = map.get(taskId);
7542
+ return hit && hit.expiry > now ? hit.record : void 0;
7543
+ },
7544
+ set(taskId, value, ttlMs) {
7545
+ const now = Date.now();
7546
+ prune(now);
7547
+ map.delete(taskId);
7548
+ map.set(taskId, { record: value, expiry: now + ttlMs });
7549
+ }
7550
+ };
7551
+ }
7552
+ function createA2APaymentHandler(options) {
7553
+ const gate = options.gate ?? createPaymentGate(options);
7554
+ const maxTimeoutSeconds = options.maxTimeoutSeconds ?? 600;
7555
+ const ttlMs = options.taskTtlMs ?? maxTimeoutSeconds * 1e3;
7556
+ const store = options.taskStore ?? defaultTaskStore();
7557
+ function appendReceipt(taskId, entry) {
7558
+ const prior = store.get(taskId)?.receipts ?? [];
7559
+ const isDup = "success" in entry && entry.success === true && prior.some((r) => "transaction" in r && r.transaction === entry.transaction);
7560
+ const next = isDup ? prior : [...prior, entry];
7561
+ const receipts = next.length > MAX_TASK_RECEIPTS ? next.slice(-MAX_TASK_RECEIPTS) : next;
7562
+ store.set(taskId, { receipts }, ttlMs);
7563
+ return receipts;
7564
+ }
7565
+ function completedTask(taskId, receipts, artifacts, status = "payment-completed") {
7566
+ const metadata = { [A2A_STATUS_KEY]: status, ...toA2APaymentReceipts(receipts) };
7567
+ return {
7568
+ kind: "task",
7569
+ id: taskId,
7570
+ status: { state: "completed", message: { kind: "message", role: "agent", taskId, metadata } },
7571
+ ...artifacts.length > 0 ? { artifacts } : {}
7572
+ };
7573
+ }
7574
+ function reChallengeTask(taskId, result, attempted) {
7575
+ if (result.kind !== "invalid") {
7576
+ const metadata2 = {
7577
+ [A2A_STATUS_KEY]: "payment-required",
7578
+ [A2A_REQUIRED_KEY]: result.challenge
7579
+ };
7580
+ return {
7581
+ kind: "task",
7582
+ id: taskId,
7583
+ status: { state: "input-required", message: { kind: "message", role: "agent", taskId, metadata: metadata2 } }
7584
+ };
7585
+ }
7586
+ const network = attempted ?? singleNetworkOf(result.challenge);
7587
+ const receipts = appendReceiptFailed(taskId, result.error, result.detail, network);
7588
+ const metadata = {
7589
+ [A2A_STATUS_KEY]: "payment-rejected",
7590
+ [A2A_ERROR_KEY]: toA2AErrorCode(result.error),
7591
+ [A2A_REQUIRED_KEY]: result.challenge,
7592
+ ...toA2APaymentReceipts(receipts)
7593
+ };
7594
+ return {
7595
+ kind: "task",
7596
+ id: taskId,
7597
+ status: { state: "input-required", message: { kind: "message", role: "agent", taskId, metadata } }
7598
+ };
7599
+ }
7600
+ function failedTask(taskId, code, detail, network) {
7601
+ const receipts = appendReceiptFailed(taskId, code, detail, network);
7602
+ const metadata = {
7603
+ [A2A_STATUS_KEY]: "payment-failed",
7604
+ [A2A_ERROR_KEY]: toA2AErrorCode(code),
7605
+ ...toA2APaymentReceipts(receipts)
7606
+ };
7607
+ return {
7608
+ kind: "task",
7609
+ id: taskId,
7610
+ status: { state: "failed", message: { kind: "message", role: "agent", taskId, metadata } }
7611
+ };
7612
+ }
7613
+ function appendReceiptFailed(taskId, code, detail, network) {
7614
+ return appendReceipt(taskId, {
7615
+ success: false,
7616
+ transaction: "",
7617
+ ...network ? { network } : {},
7618
+ errorReason: `${code}: ${detail}`
7619
+ });
7620
+ }
7621
+ async function handleMessage(message, taskId) {
7622
+ const id = taskId ?? message.taskId ?? newTaskId();
7623
+ const inbound = fromA2APaymentPayload(message);
7624
+ if (!inbound) {
7625
+ const { challenge } = await gate.challenge(resourceUrlFromMessage(message));
7626
+ store.set(id, { receipts: [] }, ttlMs);
7627
+ return toA2APaymentRequired(id, challenge);
7628
+ }
7629
+ const attempted = networkFromPayload(inbound.raw);
7630
+ let result;
7631
+ try {
7632
+ result = await gate.verifyObject(inbound.raw);
7633
+ } catch (err) {
7634
+ if (err instanceof SettlementError) {
7635
+ return failedTask(id, "settlement_failed", err.message, attempted);
7636
+ }
7637
+ const detail = err instanceof Error ? err.message : String(err);
7638
+ return failedTask(id, "tx_reverted", detail, attempted);
7639
+ }
7640
+ switch (result.kind) {
7641
+ case "challenge":
7642
+ return reChallengeTask(id, result, attempted);
7643
+ case "invalid":
7644
+ return reChallengeTask(id, result, attempted);
7645
+ case "paid": {
7646
+ const receipts = appendReceipt(id, result.receipt);
7647
+ let artifacts = [];
7648
+ if (options.fulfill) {
7649
+ try {
7650
+ artifacts = await options.fulfill({ taskId: id, receipt: result.receipt, message }) ?? [];
7651
+ } catch (err) {
7652
+ const detail = err instanceof Error ? err.message : String(err);
7653
+ const annotation = {
7654
+ name: "fulfillment-error",
7655
+ metadata: { "x402.fulfillment.error": detail, "x402.fulfillment.settled": true },
7656
+ parts: [{ kind: "text", text: `Payment settled, but serving the result failed: ${detail}` }]
7657
+ };
7658
+ return completedTask(id, receipts, [annotation]);
7659
+ }
7660
+ }
7661
+ return completedTask(id, receipts, artifacts);
7662
+ }
7663
+ }
7664
+ }
7665
+ function agentCardExtension(opts) {
7666
+ const uri = opts?.version === "v0.2" ? A2A_X402_EXTENSION_URI_V02 : A2A_X402_EXTENSION_URI_V01;
7667
+ return {
7668
+ uri,
7669
+ description: "Supports payments using the x402 protocol for on-chain settlement.",
7670
+ ...opts?.required ? { required: true } : {}
7671
+ };
7672
+ }
7673
+ return { handleMessage, agentCardExtension, gate };
7674
+ }
7675
+ function singleNetworkOf(challenge) {
7676
+ const nets = new Set((challenge.accepts ?? []).map((a) => a.network).filter(Boolean));
7677
+ return nets.size === 1 ? [...nets][0] : void 0;
7678
+ }
7679
+ function networkFromPayload(raw) {
7680
+ const v = raw;
7681
+ if (v && typeof v.accepted?.network === "string") return v.accepted.network;
7682
+ if (v && typeof v.network === "string") return v.network;
7683
+ return void 0;
7684
+ }
7685
+ function newTaskId() {
7686
+ return `task-${globalThis.crypto.randomUUID()}`;
7687
+ }
7688
+ function resourceUrlFromMessage(message) {
7689
+ for (const part of message.parts ?? []) {
7690
+ const data = part.data;
7691
+ if (data && typeof data.url === "string") return data.url;
7692
+ }
7693
+ return "";
7694
+ }
6413
7695
  export {
7696
+ A2A_ERROR_KEY,
7697
+ A2A_EXTENSIONS_HEADER,
7698
+ A2A_PAYLOAD_KEY,
7699
+ A2A_RECEIPTS_KEY,
7700
+ A2A_REQUIRED_KEY,
7701
+ A2A_STATUS_KEY,
7702
+ A2A_X402_EXTENSION_URI_V01,
7703
+ A2A_X402_EXTENSION_URI_V02,
6414
7704
  BRAND,
6415
7705
  BUILTIN_DENOMS,
6416
7706
  CHAINS,
@@ -6419,6 +7709,7 @@ export {
6419
7709
  DIRECTORY_INFO,
6420
7710
  EIP3009_TYPES,
6421
7711
  EXACT_NETWORK_SLUGS,
7712
+ EXT_OFFER_RECEIPT,
6422
7713
  GENERATOR,
6423
7714
  HEADER_REQUIRED,
6424
7715
  HEADER_RESPONSE,
@@ -6435,6 +7726,7 @@ export {
6435
7726
  NonReplayableBodyError,
6436
7727
  PERMIT2_ADDRESS,
6437
7728
  PERMIT2_PROXY_CHAIN_IDS,
7729
+ PERMIT2_UPTO_WITNESS_TYPES,
6438
7730
  PERMIT2_WITNESS_TYPES,
6439
7731
  PIPRAIL_AGENT_GUIDE,
6440
7732
  POWERED_BY,
@@ -6446,13 +7738,16 @@ export {
6446
7738
  RecipientNotReadyError,
6447
7739
  SettlementError,
6448
7740
  SpendLedger,
7741
+ UPTO_PROXY_CHAIN_IDS,
6449
7742
  UnknownTokenError,
6450
7743
  UnsupportedNetworkError,
6451
7744
  UnsupportedSchemeError,
7745
+ VERIFY_CODE_TO_A2A_ERROR,
6452
7746
  WalletRequiredError,
6453
7747
  WrongChainError,
6454
7748
  WrongFamilyError,
6455
7749
  X402_EXACT_PERMIT2_PROXY,
7750
+ X402_UPTO_PERMIT2_PROXY,
6456
7751
  agentGuide,
6457
7752
  appendAttribution,
6458
7753
  appendKeywords,
@@ -6462,15 +7757,19 @@ export {
6462
7757
  buildExactAuthorization,
6463
7758
  buildExactSignatureHeader,
6464
7759
  buildOpenApi,
7760
+ buildReceiptExtension,
6465
7761
  buildReceiptHeader,
6466
7762
  buildSelfDescription,
6467
7763
  buildSignatureHeader,
7764
+ buildUptoSignatureHeader,
6468
7765
  buildWellKnownX402,
6469
7766
  buildX402DnsTxt,
6470
7767
  chainIdForExactNetwork,
6471
7768
  claim402IndexDomain,
6472
7769
  classifyChallenge,
7770
+ createA2APaymentHandler,
6473
7771
  createPaymentGate,
7772
+ decodeBase64Json,
6474
7773
  decorateOutcome,
6475
7774
  deliverReceipt,
6476
7775
  denomOf,
@@ -6484,18 +7783,26 @@ export {
6484
7783
  fetchAcross,
6485
7784
  firstKeylessFacilitator,
6486
7785
  formatSpendReport,
7786
+ fromA2APaymentPayload,
7787
+ fromA2APaymentRequired,
6487
7788
  getDirectoryInfo,
6488
7789
  isPermit2ProxyChain,
7790
+ isUptoProxyChain,
6489
7791
  knownFacilitatorsFor,
6490
7792
  memorySpendStore,
6491
7793
  normalizeNetwork,
6492
7794
  parseChallenge,
7795
+ parseExactObject,
6493
7796
  parseExactPaymentHeader,
6494
7797
  parseExactRequirements,
6495
7798
  parseFacilitatorSupported,
6496
7799
  parseReceipt,
7800
+ parseReceiptExtension,
6497
7801
  parseSettleResponse,
6498
7802
  parseSignatureHeader,
7803
+ parseSignatureObject,
7804
+ parseUptoObject,
7805
+ parseUptoPaymentHeader,
6499
7806
  paymentTools,
6500
7807
  pickAccept,
6501
7808
  planAcross,
@@ -6511,6 +7818,10 @@ export {
6511
7818
  searchOpenIndexes,
6512
7819
  settleViaFacilitator,
6513
7820
  summarizePlan,
7821
+ toA2AErrorCode,
7822
+ toA2APaymentFailed,
7823
+ toA2APaymentReceipts,
7824
+ toA2APaymentRequired,
6514
7825
  toInsufficientFundsError,
6515
7826
  toInvalidBody,
6516
7827
  verify402IndexDomain