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.
- package/README.md +34 -5
- package/dist/adapters/index.d.mts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +2 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +2 -1
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/backend/index.d.mts +3 -2
- package/dist/backend/index.d.ts +3 -2
- package/dist/backend/index.js +498 -30
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/index.mjs +475 -31
- package/dist/backend/index.mjs.map +1 -1
- package/dist/{index-CF2HBEAJ.d.mts → index--bevvYU9.d.ts} +3 -1
- package/dist/{index-CkHu2s9r.d.ts → index-B2i1mGpW.d.mts} +3 -1
- package/dist/{index-DnxubrYX.d.mts → index-BsKsZzP3.d.mts} +1 -1
- package/dist/{index-DnxubrYX.d.ts → index-BsKsZzP3.d.ts} +1 -1
- package/dist/{index-CIUGPbu8.d.mts → index-ClOAmprt.d.mts} +232 -18
- package/dist/{index-DTbVW7EU.d.ts → index-DNTFp6WT.d.ts} +232 -18
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +512 -43
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +489 -44
- package/dist/index.mjs.map +1 -1
- package/dist/providers/algorand/index.d.mts +1 -1
- package/dist/providers/algorand/index.d.ts +1 -1
- package/dist/providers/algorand/index.js +2 -1
- package/dist/providers/algorand/index.js.map +1 -1
- package/dist/providers/algorand/index.mjs +2 -1
- package/dist/providers/algorand/index.mjs.map +1 -1
- package/dist/providers/evm/index.d.mts +1 -1
- package/dist/providers/evm/index.d.ts +1 -1
- package/dist/providers/evm/index.js +2 -1
- package/dist/providers/evm/index.js.map +1 -1
- package/dist/providers/evm/index.mjs +2 -1
- package/dist/providers/evm/index.mjs.map +1 -1
- package/dist/providers/hedera/index.d.mts +1 -1
- package/dist/providers/hedera/index.d.ts +1 -1
- package/dist/providers/near/index.d.mts +1 -1
- package/dist/providers/near/index.d.ts +1 -1
- package/dist/providers/near/index.js +2 -1
- package/dist/providers/near/index.js.map +1 -1
- package/dist/providers/near/index.mjs +2 -1
- package/dist/providers/near/index.mjs.map +1 -1
- package/dist/providers/solana/index.d.mts +1 -1
- package/dist/providers/solana/index.d.ts +1 -1
- package/dist/providers/solana/index.js +2 -1
- package/dist/providers/solana/index.js.map +1 -1
- package/dist/providers/solana/index.mjs +2 -1
- package/dist/providers/solana/index.mjs.map +1 -1
- package/dist/providers/stellar/index.d.mts +1 -1
- package/dist/providers/stellar/index.d.ts +1 -1
- package/dist/providers/stellar/index.js +2 -1
- package/dist/providers/stellar/index.js.map +1 -1
- package/dist/providers/stellar/index.mjs +2 -1
- package/dist/providers/stellar/index.mjs.map +1 -1
- package/dist/providers/sui/index.d.mts +1 -1
- package/dist/providers/sui/index.d.ts +1 -1
- package/dist/providers/sui/index.js +2 -1
- package/dist/providers/sui/index.js.map +1 -1
- package/dist/providers/sui/index.mjs +2 -1
- package/dist/providers/sui/index.mjs.map +1 -1
- package/dist/providers/xrpl/index.d.mts +1 -1
- package/dist/providers/xrpl/index.d.ts +1 -1
- package/dist/providers/xrpl/index.js +2 -1
- package/dist/providers/xrpl/index.js.map +1 -1
- package/dist/providers/xrpl/index.mjs +2 -1
- package/dist/providers/xrpl/index.mjs.map +1 -1
- package/dist/react/index.d.mts +4 -3
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +153 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +153 -1
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/picker/index.d.mts +1 -1
- package/dist/react/picker/index.d.ts +1 -1
- package/dist/react/picker/index.js +2 -1
- package/dist/react/picker/index.js.map +1 -1
- package/dist/react/picker/index.mjs +2 -1
- package/dist/react/picker/index.mjs.map +1 -1
- package/dist/receipts-Co6C9FPO.d.ts +111 -0
- package/dist/receipts-D0zg-8uK.d.mts +111 -0
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +2 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +2 -1
- package/dist/utils/index.mjs.map +1 -1
- package/dist/{validity-SD6nwegA.d.mts → validity-BcPTzuK8.d.mts} +1 -1
- package/dist/{validity-BrvXzCEF.d.ts → validity-CoCrj9pZ.d.ts} +1 -1
- package/package.json +1 -1
- package/src/backend/facilitator-error.ts +87 -2
- package/src/backend/index.ts +445 -41
- package/src/chains/index.ts +2 -1
- package/src/client/X402Client.ts +5 -0
- package/src/fixtures/facilitator-receipts-v1.json +299 -0
- package/src/index.ts +18 -0
- package/src/receipts.ts +237 -0
package/dist/react/index.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { P as PaymentInfo, a as PaymentResult, N as NetworkType,
|
|
2
|
-
export {
|
|
1
|
+
import { P as PaymentInfo, a as PaymentResult, N as NetworkType, c as X402ClientConfig, C as ChainConfig, d as NetworkBalance } from '../index-BsKsZzP3.mjs';
|
|
2
|
+
export { e as WalletState } from '../index-BsKsZzP3.mjs';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
|
-
import { g as getChainByName, X as X402Client } from '../index-
|
|
5
|
+
import { g as getChainByName, X as X402Client } from '../index-B2i1mGpW.mjs';
|
|
6
6
|
export { FAMILY_LABEL, NetworkOption, NetworkPicker, NetworkPickerProps, SIGNABLE_IN_BROWSER, buildNetworkOptions, resolveChain } from './picker/index.mjs';
|
|
7
|
+
import '../receipts-D0zg-8uK.mjs';
|
|
7
8
|
|
|
8
9
|
/** One entry of the `accepts` array of a 402 response. */
|
|
9
10
|
interface PaymentAccept {
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { P as PaymentInfo, a as PaymentResult, N as NetworkType,
|
|
2
|
-
export {
|
|
1
|
+
import { P as PaymentInfo, a as PaymentResult, N as NetworkType, c as X402ClientConfig, C as ChainConfig, d as NetworkBalance } from '../index-BsKsZzP3.js';
|
|
2
|
+
export { e as WalletState } from '../index-BsKsZzP3.js';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
|
-
import { g as getChainByName, X as X402Client } from '../index
|
|
5
|
+
import { g as getChainByName, X as X402Client } from '../index--bevvYU9.js';
|
|
6
6
|
export { FAMILY_LABEL, NetworkOption, NetworkPicker, NetworkPickerProps, SIGNABLE_IN_BROWSER, buildNetworkOptions, resolveChain } from './picker/index.js';
|
|
7
|
+
import '../receipts-Co6C9FPO.js';
|
|
7
8
|
|
|
8
9
|
/** One entry of the `accepts` array of a 402 response. */
|
|
9
10
|
interface PaymentAccept {
|
package/dist/react/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var ethers = require('ethers');
|
|
4
4
|
var react = require('react');
|
|
5
|
+
var ed25519 = require('@noble/curves/ed25519');
|
|
6
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
7
|
+
var utils = require('@noble/hashes/utils');
|
|
5
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
9
|
|
|
7
10
|
var __defProp = Object.defineProperty;
|
|
@@ -805,7 +808,8 @@ var init_chains = __esm({
|
|
|
805
808
|
// Pinned by src/arc-testnet.test.ts.
|
|
806
809
|
//
|
|
807
810
|
// Mainnet (5042) and testnet (5042002) were verified independently against
|
|
808
|
-
// their official RPCs on 2026-09-16. EURC domains checked on 2026-09-17;
|
|
811
|
+
// their official RPCs on 2026-09-16. EURC domains checked on 2026-09-17; funded EURC payments settled on Arc mainnet on
|
|
812
|
+
// 2026-09-22 (x402 v1/v2, docs/networks/arc.md); testnet pending.
|
|
809
813
|
arc: {
|
|
810
814
|
chainId: 5042,
|
|
811
815
|
chainIdHex: "0x13b2",
|
|
@@ -1805,6 +1809,151 @@ var init_solana = __esm({
|
|
|
1805
1809
|
solana_default = SVMProvider;
|
|
1806
1810
|
}
|
|
1807
1811
|
});
|
|
1812
|
+
var RECEIPT_ISSUER = "https://facilitator.ultravioletadao.xyz";
|
|
1813
|
+
var encoder = new TextEncoder();
|
|
1814
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1815
|
+
var digest = (value) => utils.bytesToHex(sha256.sha256(value));
|
|
1816
|
+
function canonicalReceiptJson(value) {
|
|
1817
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
1818
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return String(value);
|
|
1819
|
+
if (Array.isArray(value)) return `[${value.map(canonicalReceiptJson).join(",")}]`;
|
|
1820
|
+
if (typeof value === "object" && value) {
|
|
1821
|
+
const object = value;
|
|
1822
|
+
const keys = Object.keys(object).sort();
|
|
1823
|
+
if (keys.some((key) => !/^[\x00-\x7f]*$/.test(key))) throw new Error("non-ASCII canonical key");
|
|
1824
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalReceiptJson(object[key])}`).join(",")}}`;
|
|
1825
|
+
}
|
|
1826
|
+
throw new Error("unsupported canonical value");
|
|
1827
|
+
}
|
|
1828
|
+
function receiptCommitment(domain, value) {
|
|
1829
|
+
return digest(encoder.encode(`${domain}
|
|
1830
|
+
${canonicalReceiptJson(value)}`));
|
|
1831
|
+
}
|
|
1832
|
+
function encode64(bytes) {
|
|
1833
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
1834
|
+
return btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join(""));
|
|
1835
|
+
}
|
|
1836
|
+
function decode64(value) {
|
|
1837
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value) || value.length % 4 !== 0) throw new Error("invalid base64");
|
|
1838
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
|
|
1839
|
+
return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
|
|
1840
|
+
}
|
|
1841
|
+
function decodeUrl64(value) {
|
|
1842
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
1843
|
+
return decode64(padded + "=".repeat((4 - padded.length % 4) % 4));
|
|
1844
|
+
}
|
|
1845
|
+
function parseFacilitatorReceipt(value) {
|
|
1846
|
+
if (value === null || value === void 0) return null;
|
|
1847
|
+
const r = value;
|
|
1848
|
+
if (r.schemaVersion !== 1 || !["verified", "pending", "confirmed", "rejected", "unknown"].includes(r.status)) throw new Error("unsupported receipt version or state");
|
|
1849
|
+
for (const key of ["receiptId", "issuer", "operation", "network", "scheme", "asset", "amount", "payTo", "paymentRequestHash", "authorizationId"]) {
|
|
1850
|
+
if (typeof r[key] !== "string" || !r[key]) throw new Error(`invalid receipt ${key}`);
|
|
1851
|
+
}
|
|
1852
|
+
if (!Number.isSafeInteger(r.revision) || r.revision < 1 || !r.request || !r.retry) throw new Error("incomplete receipt");
|
|
1853
|
+
if (r.requestHashVersion !== "uvd-x402-request-v1" || r.requestHash !== receiptCommitment(r.requestHashVersion, r.request)) throw new Error("receipt request hash mismatch");
|
|
1854
|
+
for (const key of ["network", "scheme", "asset", "amount", "payTo", "purchaseId"]) {
|
|
1855
|
+
if (r[key] !== r.request[key]) throw new Error("receipt terms mismatch");
|
|
1856
|
+
}
|
|
1857
|
+
if (!/^[0-9]+$/.test(r.amount)) throw new Error("receipt amount must be an atomic integer string");
|
|
1858
|
+
if (r.status === "confirmed" && (r.operation !== "settle" || !r.settlement?.id)) throw new Error("confirmation has no settlement");
|
|
1859
|
+
return r;
|
|
1860
|
+
}
|
|
1861
|
+
function verifyFacilitatorReceipt(receipt, jwks, issuer = RECEIPT_ISSUER) {
|
|
1862
|
+
try {
|
|
1863
|
+
parseFacilitatorReceipt(receipt);
|
|
1864
|
+
if (receipt.issuer !== issuer || receipt.proof?.type !== "jws" || !receipt.proof.jws) return false;
|
|
1865
|
+
const parts = receipt.proof.jws.split(".");
|
|
1866
|
+
if (parts.length !== 3) return false;
|
|
1867
|
+
const [protectedHeader, payload, signature] = parts;
|
|
1868
|
+
const header = JSON.parse(decoder.decode(decodeUrl64(protectedHeader)));
|
|
1869
|
+
if (header.alg !== "EdDSA" || header.typ !== "uvd-facilitator-receipt+jws") return false;
|
|
1870
|
+
const keys = jwks.keys.filter((k) => k.kid === header.kid && k.kty === "OKP" && k.crv === "Ed25519");
|
|
1871
|
+
if (keys.length !== 1 || !keys[0].x) return false;
|
|
1872
|
+
if (decoder.decode(decodeUrl64(payload)) !== canonicalReceiptJson({ ...receipt, proof: null })) return false;
|
|
1873
|
+
return ed25519.ed25519.verify(decodeUrl64(signature), encoder.encode(`${protectedHeader}.${payload}`), decodeUrl64(keys[0].x));
|
|
1874
|
+
} catch {
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
function purchaseContextHeader(context) {
|
|
1879
|
+
return encode64(encoder.encode(canonicalReceiptJson({
|
|
1880
|
+
purchaseId: context.purchaseId,
|
|
1881
|
+
accessToken: context.accessToken,
|
|
1882
|
+
method: context.method,
|
|
1883
|
+
url: context.url,
|
|
1884
|
+
bodySha256: context.bodySha256
|
|
1885
|
+
})));
|
|
1886
|
+
}
|
|
1887
|
+
function receiptFromResponse(response) {
|
|
1888
|
+
const values = [response.headers.get("PAYMENT-RESPONSE"), response.headers.get("X-PAYMENT-RESPONSE")].filter((v) => v !== null);
|
|
1889
|
+
if (!values.length) return null;
|
|
1890
|
+
if (new Set(values).size !== 1 || values[0].includes(",") || values[0].length > 32768) throw new Error("ambiguous or oversized payment response header");
|
|
1891
|
+
return parseFacilitatorReceipt(JSON.parse(decoder.decode(decode64(values[0]))).receipt);
|
|
1892
|
+
}
|
|
1893
|
+
async function fetchWithReceipt(fetchClient, url, options) {
|
|
1894
|
+
const { context, persist, trustedKeys, issuer = RECEIPT_ISSUER } = options;
|
|
1895
|
+
const transport = options.fetchImpl || globalThis.fetch;
|
|
1896
|
+
const method = (options.method || "GET").toUpperCase();
|
|
1897
|
+
const request = new Request(url, { ...options.init, method });
|
|
1898
|
+
const bytes = new Uint8Array(await request.arrayBuffer());
|
|
1899
|
+
const bodyHash = digest(bytes);
|
|
1900
|
+
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");
|
|
1901
|
+
context.method = method;
|
|
1902
|
+
context.url = request.url;
|
|
1903
|
+
context.bodySha256 = bodyHash;
|
|
1904
|
+
const init = { ...options.init, method, headers: new Headers(request.headers), body: ["GET", "HEAD"].includes(method) ? void 0 : bytes };
|
|
1905
|
+
let receipt = parseFacilitatorReceipt(context.receipt);
|
|
1906
|
+
let transportError;
|
|
1907
|
+
const persistCopy = async () => {
|
|
1908
|
+
await persist(JSON.parse(JSON.stringify(context)));
|
|
1909
|
+
};
|
|
1910
|
+
const send = async (input, sendOptions) => {
|
|
1911
|
+
const headers = new Headers(sendOptions?.headers);
|
|
1912
|
+
const paid = headers.get("X-PAYMENT") || headers.get("PAYMENT-SIGNATURE");
|
|
1913
|
+
if (paid) {
|
|
1914
|
+
const paymentHeaders = {};
|
|
1915
|
+
for (const name of ["X-PAYMENT", "PAYMENT-SIGNATURE"]) {
|
|
1916
|
+
const value = headers.get(name);
|
|
1917
|
+
if (value) paymentHeaders[name] = value;
|
|
1918
|
+
}
|
|
1919
|
+
if (context.paymentHeaders && JSON.stringify(context.paymentHeaders) !== JSON.stringify(paymentHeaders)) throw new Error("refusing a new authorization for an existing purchase");
|
|
1920
|
+
context.paymentHeaders = paymentHeaders;
|
|
1921
|
+
headers.set("X-UVD-Purchase", purchaseContextHeader(context));
|
|
1922
|
+
await persistCopy();
|
|
1923
|
+
}
|
|
1924
|
+
try {
|
|
1925
|
+
return await transport(input, { ...sendOptions, headers });
|
|
1926
|
+
} catch (error) {
|
|
1927
|
+
transportError = error;
|
|
1928
|
+
throw error;
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
try {
|
|
1932
|
+
let response;
|
|
1933
|
+
if (context.paymentHeaders) {
|
|
1934
|
+
const headers = new Headers(init.headers);
|
|
1935
|
+
for (const [name, value] of Object.entries(context.paymentHeaders)) headers.set(name, value);
|
|
1936
|
+
response = await send(request.url, { ...init, headers });
|
|
1937
|
+
} else {
|
|
1938
|
+
response = await fetchClient(request.url, { ...options, method, init, fetchImpl: send });
|
|
1939
|
+
}
|
|
1940
|
+
const incoming = receiptFromResponse(response);
|
|
1941
|
+
if (incoming) {
|
|
1942
|
+
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");
|
|
1943
|
+
if (receipt?.operation === "settle" && (incoming.receiptId !== receipt.receiptId || incoming.revision < receipt.revision)) throw new Error("receipt revision conflict");
|
|
1944
|
+
if (trustedKeys && !verifyFacilitatorReceipt(incoming, trustedKeys, issuer)) throw new Error("receipt signature verification failed");
|
|
1945
|
+
receipt = incoming;
|
|
1946
|
+
context.receipt = incoming;
|
|
1947
|
+
await persistCopy();
|
|
1948
|
+
}
|
|
1949
|
+
const proofVerified = !!(receipt && trustedKeys && verifyFacilitatorReceipt(receipt, trustedKeys, issuer));
|
|
1950
|
+
if (receipt && trustedKeys && !proofVerified) throw new Error("receipt signature verification failed");
|
|
1951
|
+
return { response, receipt, paymentState: receipt ? receipt.status : context.paymentHeaders ? "unknown" : "not_required", context, proofVerified };
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
if (!transportError) throw error;
|
|
1954
|
+
return { response: null, receipt, paymentState: "unknown", context, proofVerified: false, error };
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1808
1957
|
|
|
1809
1958
|
// src/client/X402Client.ts
|
|
1810
1959
|
init_types();
|
|
@@ -2437,6 +2586,9 @@ var X402Client = class _X402Client {
|
|
|
2437
2586
|
* const data = await res.json();
|
|
2438
2587
|
* ```
|
|
2439
2588
|
*/
|
|
2589
|
+
async fetchWithReceipt(url, options) {
|
|
2590
|
+
return fetchWithReceipt((target, init) => this.fetch(target, init), url, options);
|
|
2591
|
+
}
|
|
2440
2592
|
async fetch(url, options = {}) {
|
|
2441
2593
|
if (!this.connectedAddress) {
|
|
2442
2594
|
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|