uvd-x402-sdk 2.95.0 → 2.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +34 -5
  2. package/dist/adapters/index.d.mts +1 -1
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/adapters/index.js +2 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/index.mjs +2 -1
  7. package/dist/adapters/index.mjs.map +1 -1
  8. package/dist/backend/index.d.mts +3 -2
  9. package/dist/backend/index.d.ts +3 -2
  10. package/dist/backend/index.js +498 -30
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/index.mjs +475 -31
  13. package/dist/backend/index.mjs.map +1 -1
  14. package/dist/{index-CF2HBEAJ.d.mts → index--bevvYU9.d.ts} +3 -1
  15. package/dist/{index-CkHu2s9r.d.ts → index-B2i1mGpW.d.mts} +3 -1
  16. package/dist/{index-DnxubrYX.d.mts → index-BsKsZzP3.d.mts} +1 -1
  17. package/dist/{index-DnxubrYX.d.ts → index-BsKsZzP3.d.ts} +1 -1
  18. package/dist/{index-CIUGPbu8.d.mts → index-ClOAmprt.d.mts} +232 -18
  19. package/dist/{index-DTbVW7EU.d.ts → index-DNTFp6WT.d.ts} +232 -18
  20. package/dist/index.d.mts +5 -4
  21. package/dist/index.d.ts +5 -4
  22. package/dist/index.js +512 -43
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +489 -44
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/providers/algorand/index.d.mts +1 -1
  27. package/dist/providers/algorand/index.d.ts +1 -1
  28. package/dist/providers/algorand/index.js +2 -1
  29. package/dist/providers/algorand/index.js.map +1 -1
  30. package/dist/providers/algorand/index.mjs +2 -1
  31. package/dist/providers/algorand/index.mjs.map +1 -1
  32. package/dist/providers/evm/index.d.mts +1 -1
  33. package/dist/providers/evm/index.d.ts +1 -1
  34. package/dist/providers/evm/index.js +2 -1
  35. package/dist/providers/evm/index.js.map +1 -1
  36. package/dist/providers/evm/index.mjs +2 -1
  37. package/dist/providers/evm/index.mjs.map +1 -1
  38. package/dist/providers/hedera/index.d.mts +1 -1
  39. package/dist/providers/hedera/index.d.ts +1 -1
  40. package/dist/providers/near/index.d.mts +1 -1
  41. package/dist/providers/near/index.d.ts +1 -1
  42. package/dist/providers/near/index.js +2 -1
  43. package/dist/providers/near/index.js.map +1 -1
  44. package/dist/providers/near/index.mjs +2 -1
  45. package/dist/providers/near/index.mjs.map +1 -1
  46. package/dist/providers/solana/index.d.mts +1 -1
  47. package/dist/providers/solana/index.d.ts +1 -1
  48. package/dist/providers/solana/index.js +2 -1
  49. package/dist/providers/solana/index.js.map +1 -1
  50. package/dist/providers/solana/index.mjs +2 -1
  51. package/dist/providers/solana/index.mjs.map +1 -1
  52. package/dist/providers/stellar/index.d.mts +1 -1
  53. package/dist/providers/stellar/index.d.ts +1 -1
  54. package/dist/providers/stellar/index.js +2 -1
  55. package/dist/providers/stellar/index.js.map +1 -1
  56. package/dist/providers/stellar/index.mjs +2 -1
  57. package/dist/providers/stellar/index.mjs.map +1 -1
  58. package/dist/providers/sui/index.d.mts +1 -1
  59. package/dist/providers/sui/index.d.ts +1 -1
  60. package/dist/providers/sui/index.js +2 -1
  61. package/dist/providers/sui/index.js.map +1 -1
  62. package/dist/providers/sui/index.mjs +2 -1
  63. package/dist/providers/sui/index.mjs.map +1 -1
  64. package/dist/providers/xrpl/index.d.mts +1 -1
  65. package/dist/providers/xrpl/index.d.ts +1 -1
  66. package/dist/providers/xrpl/index.js +2 -1
  67. package/dist/providers/xrpl/index.js.map +1 -1
  68. package/dist/providers/xrpl/index.mjs +2 -1
  69. package/dist/providers/xrpl/index.mjs.map +1 -1
  70. package/dist/react/index.d.mts +4 -3
  71. package/dist/react/index.d.ts +4 -3
  72. package/dist/react/index.js +153 -1
  73. package/dist/react/index.js.map +1 -1
  74. package/dist/react/index.mjs +153 -1
  75. package/dist/react/index.mjs.map +1 -1
  76. package/dist/react/picker/index.d.mts +1 -1
  77. package/dist/react/picker/index.d.ts +1 -1
  78. package/dist/react/picker/index.js +2 -1
  79. package/dist/react/picker/index.js.map +1 -1
  80. package/dist/react/picker/index.mjs +2 -1
  81. package/dist/react/picker/index.mjs.map +1 -1
  82. package/dist/receipts-Co6C9FPO.d.ts +111 -0
  83. package/dist/receipts-D0zg-8uK.d.mts +111 -0
  84. package/dist/utils/index.d.mts +2 -2
  85. package/dist/utils/index.d.ts +2 -2
  86. package/dist/utils/index.js +2 -1
  87. package/dist/utils/index.js.map +1 -1
  88. package/dist/utils/index.mjs +2 -1
  89. package/dist/utils/index.mjs.map +1 -1
  90. package/dist/{validity-SD6nwegA.d.mts → validity-BcPTzuK8.d.mts} +1 -1
  91. package/dist/{validity-BrvXzCEF.d.ts → validity-CoCrj9pZ.d.ts} +1 -1
  92. package/package.json +1 -1
  93. package/src/backend/facilitator-error.ts +87 -2
  94. package/src/backend/index.ts +445 -41
  95. package/src/chains/index.ts +2 -1
  96. package/src/client/X402Client.ts +5 -0
  97. package/src/fixtures/facilitator-receipts-v1.json +299 -0
  98. package/src/index.ts +18 -0
  99. package/src/receipts.ts +237 -0
@@ -1,5 +1,8 @@
1
1
  import { ethers } from 'ethers';
2
2
  import { createContext, useMemo, useRef, useCallback, useState, useEffect, useContext } from 'react';
3
+ import { ed25519 } from '@noble/curves/ed25519';
4
+ import { sha256 } from '@noble/hashes/sha256';
5
+ import { bytesToHex } from '@noble/hashes/utils';
3
6
  import { jsxs, jsx } from 'react/jsx-runtime';
4
7
 
5
8
  var __defProp = Object.defineProperty;
@@ -803,7 +806,8 @@ var init_chains = __esm({
803
806
  // Pinned by src/arc-testnet.test.ts.
804
807
  //
805
808
  // Mainnet (5042) and testnet (5042002) were verified independently against
806
- // their official RPCs on 2026-09-16. EURC domains checked on 2026-09-17; live payments pending.
809
+ // their official RPCs on 2026-09-16. EURC domains checked on 2026-09-17; funded EURC payments settled on Arc mainnet on
810
+ // 2026-09-22 (x402 v1/v2, docs/networks/arc.md); testnet pending.
807
811
  arc: {
808
812
  chainId: 5042,
809
813
  chainIdHex: "0x13b2",
@@ -1803,6 +1807,151 @@ var init_solana = __esm({
1803
1807
  solana_default = SVMProvider;
1804
1808
  }
1805
1809
  });
1810
+ var RECEIPT_ISSUER = "https://facilitator.ultravioletadao.xyz";
1811
+ var encoder = new TextEncoder();
1812
+ var decoder = new TextDecoder("utf-8", { fatal: true });
1813
+ var digest = (value) => bytesToHex(sha256(value));
1814
+ function canonicalReceiptJson(value) {
1815
+ if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
1816
+ if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return String(value);
1817
+ if (Array.isArray(value)) return `[${value.map(canonicalReceiptJson).join(",")}]`;
1818
+ if (typeof value === "object" && value) {
1819
+ const object = value;
1820
+ const keys = Object.keys(object).sort();
1821
+ if (keys.some((key) => !/^[\x00-\x7f]*$/.test(key))) throw new Error("non-ASCII canonical key");
1822
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalReceiptJson(object[key])}`).join(",")}}`;
1823
+ }
1824
+ throw new Error("unsupported canonical value");
1825
+ }
1826
+ function receiptCommitment(domain, value) {
1827
+ return digest(encoder.encode(`${domain}
1828
+ ${canonicalReceiptJson(value)}`));
1829
+ }
1830
+ function encode64(bytes) {
1831
+ if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
1832
+ return btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join(""));
1833
+ }
1834
+ function decode64(value) {
1835
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value) || value.length % 4 !== 0) throw new Error("invalid base64");
1836
+ if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
1837
+ return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
1838
+ }
1839
+ function decodeUrl64(value) {
1840
+ const padded = value.replace(/-/g, "+").replace(/_/g, "/");
1841
+ return decode64(padded + "=".repeat((4 - padded.length % 4) % 4));
1842
+ }
1843
+ function parseFacilitatorReceipt(value) {
1844
+ if (value === null || value === void 0) return null;
1845
+ const r = value;
1846
+ if (r.schemaVersion !== 1 || !["verified", "pending", "confirmed", "rejected", "unknown"].includes(r.status)) throw new Error("unsupported receipt version or state");
1847
+ for (const key of ["receiptId", "issuer", "operation", "network", "scheme", "asset", "amount", "payTo", "paymentRequestHash", "authorizationId"]) {
1848
+ if (typeof r[key] !== "string" || !r[key]) throw new Error(`invalid receipt ${key}`);
1849
+ }
1850
+ if (!Number.isSafeInteger(r.revision) || r.revision < 1 || !r.request || !r.retry) throw new Error("incomplete receipt");
1851
+ if (r.requestHashVersion !== "uvd-x402-request-v1" || r.requestHash !== receiptCommitment(r.requestHashVersion, r.request)) throw new Error("receipt request hash mismatch");
1852
+ for (const key of ["network", "scheme", "asset", "amount", "payTo", "purchaseId"]) {
1853
+ if (r[key] !== r.request[key]) throw new Error("receipt terms mismatch");
1854
+ }
1855
+ if (!/^[0-9]+$/.test(r.amount)) throw new Error("receipt amount must be an atomic integer string");
1856
+ if (r.status === "confirmed" && (r.operation !== "settle" || !r.settlement?.id)) throw new Error("confirmation has no settlement");
1857
+ return r;
1858
+ }
1859
+ function verifyFacilitatorReceipt(receipt, jwks, issuer = RECEIPT_ISSUER) {
1860
+ try {
1861
+ parseFacilitatorReceipt(receipt);
1862
+ if (receipt.issuer !== issuer || receipt.proof?.type !== "jws" || !receipt.proof.jws) return false;
1863
+ const parts = receipt.proof.jws.split(".");
1864
+ if (parts.length !== 3) return false;
1865
+ const [protectedHeader, payload, signature] = parts;
1866
+ const header = JSON.parse(decoder.decode(decodeUrl64(protectedHeader)));
1867
+ if (header.alg !== "EdDSA" || header.typ !== "uvd-facilitator-receipt+jws") return false;
1868
+ const keys = jwks.keys.filter((k) => k.kid === header.kid && k.kty === "OKP" && k.crv === "Ed25519");
1869
+ if (keys.length !== 1 || !keys[0].x) return false;
1870
+ if (decoder.decode(decodeUrl64(payload)) !== canonicalReceiptJson({ ...receipt, proof: null })) return false;
1871
+ return ed25519.verify(decodeUrl64(signature), encoder.encode(`${protectedHeader}.${payload}`), decodeUrl64(keys[0].x));
1872
+ } catch {
1873
+ return false;
1874
+ }
1875
+ }
1876
+ function purchaseContextHeader(context) {
1877
+ return encode64(encoder.encode(canonicalReceiptJson({
1878
+ purchaseId: context.purchaseId,
1879
+ accessToken: context.accessToken,
1880
+ method: context.method,
1881
+ url: context.url,
1882
+ bodySha256: context.bodySha256
1883
+ })));
1884
+ }
1885
+ function receiptFromResponse(response) {
1886
+ const values = [response.headers.get("PAYMENT-RESPONSE"), response.headers.get("X-PAYMENT-RESPONSE")].filter((v) => v !== null);
1887
+ if (!values.length) return null;
1888
+ if (new Set(values).size !== 1 || values[0].includes(",") || values[0].length > 32768) throw new Error("ambiguous or oversized payment response header");
1889
+ return parseFacilitatorReceipt(JSON.parse(decoder.decode(decode64(values[0]))).receipt);
1890
+ }
1891
+ async function fetchWithReceipt(fetchClient, url, options) {
1892
+ const { context, persist, trustedKeys, issuer = RECEIPT_ISSUER } = options;
1893
+ const transport = options.fetchImpl || globalThis.fetch;
1894
+ const method = (options.method || "GET").toUpperCase();
1895
+ const request = new Request(url, { ...options.init, method });
1896
+ const bytes = new Uint8Array(await request.arrayBuffer());
1897
+ const bodyHash = digest(bytes);
1898
+ if (context.method && (context.method !== method || context.url !== request.url || context.bodySha256 !== bodyHash)) throw new Error("purchase context cannot be reused for another HTTP request");
1899
+ context.method = method;
1900
+ context.url = request.url;
1901
+ context.bodySha256 = bodyHash;
1902
+ const init = { ...options.init, method, headers: new Headers(request.headers), body: ["GET", "HEAD"].includes(method) ? void 0 : bytes };
1903
+ let receipt = parseFacilitatorReceipt(context.receipt);
1904
+ let transportError;
1905
+ const persistCopy = async () => {
1906
+ await persist(JSON.parse(JSON.stringify(context)));
1907
+ };
1908
+ const send = async (input, sendOptions) => {
1909
+ const headers = new Headers(sendOptions?.headers);
1910
+ const paid = headers.get("X-PAYMENT") || headers.get("PAYMENT-SIGNATURE");
1911
+ if (paid) {
1912
+ const paymentHeaders = {};
1913
+ for (const name of ["X-PAYMENT", "PAYMENT-SIGNATURE"]) {
1914
+ const value = headers.get(name);
1915
+ if (value) paymentHeaders[name] = value;
1916
+ }
1917
+ if (context.paymentHeaders && JSON.stringify(context.paymentHeaders) !== JSON.stringify(paymentHeaders)) throw new Error("refusing a new authorization for an existing purchase");
1918
+ context.paymentHeaders = paymentHeaders;
1919
+ headers.set("X-UVD-Purchase", purchaseContextHeader(context));
1920
+ await persistCopy();
1921
+ }
1922
+ try {
1923
+ return await transport(input, { ...sendOptions, headers });
1924
+ } catch (error) {
1925
+ transportError = error;
1926
+ throw error;
1927
+ }
1928
+ };
1929
+ try {
1930
+ let response;
1931
+ if (context.paymentHeaders) {
1932
+ const headers = new Headers(init.headers);
1933
+ for (const [name, value] of Object.entries(context.paymentHeaders)) headers.set(name, value);
1934
+ response = await send(request.url, { ...init, headers });
1935
+ } else {
1936
+ response = await fetchClient(request.url, { ...options, method, init, fetchImpl: send });
1937
+ }
1938
+ const incoming = receiptFromResponse(response);
1939
+ if (incoming) {
1940
+ if (incoming.issuer !== issuer || Object.entries({ purchaseId: context.purchaseId, method, url: context.url, bodySha256: bodyHash }).some(([key, value]) => incoming.request[key] !== value)) throw new Error("receipt does not bind this purchase");
1941
+ if (receipt?.operation === "settle" && (incoming.receiptId !== receipt.receiptId || incoming.revision < receipt.revision)) throw new Error("receipt revision conflict");
1942
+ if (trustedKeys && !verifyFacilitatorReceipt(incoming, trustedKeys, issuer)) throw new Error("receipt signature verification failed");
1943
+ receipt = incoming;
1944
+ context.receipt = incoming;
1945
+ await persistCopy();
1946
+ }
1947
+ const proofVerified = !!(receipt && trustedKeys && verifyFacilitatorReceipt(receipt, trustedKeys, issuer));
1948
+ if (receipt && trustedKeys && !proofVerified) throw new Error("receipt signature verification failed");
1949
+ return { response, receipt, paymentState: receipt ? receipt.status : context.paymentHeaders ? "unknown" : "not_required", context, proofVerified };
1950
+ } catch (error) {
1951
+ if (!transportError) throw error;
1952
+ return { response: null, receipt, paymentState: "unknown", context, proofVerified: false, error };
1953
+ }
1954
+ }
1806
1955
 
1807
1956
  // src/client/X402Client.ts
1808
1957
  init_types();
@@ -2435,6 +2584,9 @@ var X402Client = class _X402Client {
2435
2584
  * const data = await res.json();
2436
2585
  * ```
2437
2586
  */
2587
+ async fetchWithReceipt(url, options) {
2588
+ return fetchWithReceipt((target, init) => this.fetch(target, init), url, options);
2589
+ }
2438
2590
  async fetch(url, options = {}) {
2439
2591
  if (!this.connectedAddress) {
2440
2592
  throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");