uvd-x402-sdk 2.102.0 → 2.104.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 (66) hide show
  1. package/README.md +72 -8
  2. package/dist/adapters/index.js +3 -1
  3. package/dist/adapters/index.js.map +1 -1
  4. package/dist/adapters/index.mjs +3 -1
  5. package/dist/adapters/index.mjs.map +1 -1
  6. package/dist/backend/index.d.mts +1 -1
  7. package/dist/backend/index.d.ts +1 -1
  8. package/dist/backend/index.js +267 -167
  9. package/dist/backend/index.js.map +1 -1
  10. package/dist/backend/index.mjs +267 -168
  11. package/dist/backend/index.mjs.map +1 -1
  12. package/dist/{index-Di8sCYHx.d.ts → escrow-contracts-C59jszp3.d.ts} +167 -33
  13. package/dist/{index-DKUUBqZO.d.mts → escrow-contracts-Dgcyu7Fh.d.mts} +167 -33
  14. package/dist/index.d.mts +56 -4
  15. package/dist/index.d.ts +56 -4
  16. package/dist/index.js +509 -75
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +505 -76
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/providers/algorand/index.js +3 -1
  21. package/dist/providers/algorand/index.js.map +1 -1
  22. package/dist/providers/algorand/index.mjs +3 -1
  23. package/dist/providers/algorand/index.mjs.map +1 -1
  24. package/dist/providers/evm/index.js +3 -1
  25. package/dist/providers/evm/index.js.map +1 -1
  26. package/dist/providers/evm/index.mjs +3 -1
  27. package/dist/providers/evm/index.mjs.map +1 -1
  28. package/dist/providers/near/index.js +3 -1
  29. package/dist/providers/near/index.js.map +1 -1
  30. package/dist/providers/near/index.mjs +3 -1
  31. package/dist/providers/near/index.mjs.map +1 -1
  32. package/dist/providers/solana/index.js +3 -1
  33. package/dist/providers/solana/index.js.map +1 -1
  34. package/dist/providers/solana/index.mjs +3 -1
  35. package/dist/providers/solana/index.mjs.map +1 -1
  36. package/dist/providers/stellar/index.js +3 -1
  37. package/dist/providers/stellar/index.js.map +1 -1
  38. package/dist/providers/stellar/index.mjs +3 -1
  39. package/dist/providers/stellar/index.mjs.map +1 -1
  40. package/dist/providers/sui/index.js +3 -1
  41. package/dist/providers/sui/index.js.map +1 -1
  42. package/dist/providers/sui/index.mjs +3 -1
  43. package/dist/providers/sui/index.mjs.map +1 -1
  44. package/dist/providers/xrpl/index.js +3 -1
  45. package/dist/providers/xrpl/index.js.map +1 -1
  46. package/dist/providers/xrpl/index.mjs +3 -1
  47. package/dist/providers/xrpl/index.mjs.map +1 -1
  48. package/dist/react/index.js +3 -1
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/react/index.mjs +3 -1
  51. package/dist/react/index.mjs.map +1 -1
  52. package/dist/react/picker/index.js +3 -1
  53. package/dist/react/picker/index.js.map +1 -1
  54. package/dist/react/picker/index.mjs +3 -1
  55. package/dist/react/picker/index.mjs.map +1 -1
  56. package/dist/utils/index.js +4 -2
  57. package/dist/utils/index.js.map +1 -1
  58. package/dist/utils/index.mjs +4 -2
  59. package/dist/utils/index.mjs.map +1 -1
  60. package/package.json +2 -1
  61. package/src/backend/index.ts +325 -212
  62. package/src/chains/index.ts +3 -1
  63. package/src/escrow-contracts.ts +254 -0
  64. package/src/escrow-preauth.ts +376 -16
  65. package/src/index.ts +9 -0
  66. package/src/utils/x402.ts +5 -2
package/README.md CHANGED
@@ -675,6 +675,7 @@ import { buildEscrowPreAuth, EnvKeyAdapter } from 'uvd-x402-sdk';
675
675
  // (e.g. Execution Market's GET /api/v1/h2a/payment-config).
676
676
  const paymentAuth = await buildEscrowPreAuth(new EnvKeyAdapter(), {
677
677
  networkConfig: config.escrow.networks.base,
678
+ network: 'base', // its chain_id must be the chain this SDK registers for it
678
679
  payerWallet: '0xPublisher...',
679
680
  workerWallet: '0xWorker...', // escrow receiver — committed by the nonce
680
681
  bountyAtomic: '100000', // $0.10 in 6-decimal USDC
@@ -683,7 +684,7 @@ const paymentAuth = await buildEscrowPreAuth(new EnvKeyAdapter(), {
683
684
  // Send as the X-Payment-Auth header (raw JSON, NOT base64).
684
685
  ```
685
686
 
686
- Any `SigningWalletAdapter` works as the signer (only `signTypedData` is used); browser wallets can pass a minimal `{ signTypedData }` wrapper. Validation fails loud instead of falling back: incomplete network config, unknown tier, bounty outside the on-chain deposit limit ($100), or a `maxFeeBps` below the operator's 1300 bps all throw before anything is signed. `computeEscrowNonce` is exported to reproduce `AuthCaptureEscrow.getHash` externally.
687
+ Any `SigningWalletAdapter` works as the signer (only `signTypedData` is used); browser wallets can pass a minimal `{ signTypedData }` wrapper. Validation fails loud instead of falling back: incomplete network config, unknown tier, bounty outside the on-chain deposit limit ($100), or a `maxFeeBps` below the operator's 1300 bps all throw before anything is signed. What the config asserts is also checked against the SDK's own tables before signing, as in the Python SDK: the `chain_id` against the `network` you name (a registry name or `eip155:<id>`), the escrow, token collector and USDC against `ESCROW_CONTRACTS`, the operator against `ESCROW_OPERATORS`, and `maxFeeBps` against the chain's ceiling in `ESCROW_MAX_FEE_BPS`. A contradiction throws `INVALID_CONFIG`, and so do a `network` the SDK does not register and a chain without a row in each of the three tables, with or without `network`; name the network you expect to pay on, from your own code, never one read from the config. A server-published tier window must be an integer from 0 to its `ESCROW_TIER_WINDOW_MAX`, and `authorizationExpiry` at most `ESCROW_MAX_AUTHORIZATION_SEC` after now: past either, the SDK refuses, it never clamps. `computeEscrowNonce` is exported to reproduce `AuthCaptureEscrow.getHash` externally.
687
688
 
688
689
  ## Multi-Stablecoin (EVM)
689
690
 
@@ -888,11 +889,74 @@ app.post('/api/premium', async (req, res) => {
888
889
  });
889
890
  ```
890
891
 
891
- `createPaymentMiddleware()` and `createHonoMiddleware()` verify and settle automatically by default (`before-handler`). Use `settlementStrategy: 'manual'` if you need to control when settlement happens (e.g., settle only after confirming you can fulfill the request).
892
+ `createPaymentMiddleware()`, `createHonoMiddleware()` and `createFetchPaywall()` verify and settle automatically by default (`before-handler`). Use `settlementStrategy: 'manual'` if you need to control when settlement happens (e.g., settle only after confirming you can fulfill the request).
893
+
894
+ A buyer in a browser on another origin reads the terms of a v2 402, in the `PAYMENT-REQUIRED` header, only if the app's CORS exposes that header: list it in `Access-Control-Expose-Headers` (with `hono/cors`, `exposeHeaders: ['PAYMENT-REQUIRED']`) and send `Access-Control-Allow-Origin`. The 402 of `createHonoMiddleware` and `createFetchPaywall` sets no CORS header of its own; `create402Response` lists the header in its `headers` but sets no `Access-Control-Allow-Origin` (spread `getCorsHeaders(origin)` too).
895
+
896
+ ### Astro, Next.js, Vercel and Cloudflare (`createFetchPaywall`)
897
+
898
+ A route that takes a `Request` and returns a `Response` gets the same paywall as `createHonoMiddleware`: same options, same answers, same code underneath. `createFetchPaywall(options)` returns a wrapper; the route it wraps runs only for a verified payment (settled first, unless `settlementStrategy: 'manual'`) and receives it as its second argument. Without a payment the route answers `402` with the accepts, and it never runs for a refused, unverifiable or already used one.
899
+
900
+ **Astro** (`src/pages/api/premium/[id].ts`, on-demand rendering with any adapter, e.g. `@astrojs/vercel`):
901
+
902
+ ```typescript
903
+ import type { APIRoute } from 'astro';
904
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
905
+
906
+ // Run on every request: a prerendered endpoint is called once, at build time, without a payment.
907
+ export const prerender = false;
908
+
909
+ const paywall = createFetchPaywall({
910
+ accepts: [{
911
+ network: 'base',
912
+ asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
913
+ amount: '10000', // $0.01 (6 decimals)
914
+ payTo: process.env.RECEIVING_ADDRESS as string,
915
+ }],
916
+ });
917
+
918
+ export const GET: APIRoute = paywall(async (context, x402) => {
919
+ return Response.json({ id: context.params.id, payer: x402.verifyResult.payer });
920
+ });
921
+ ```
922
+
923
+ **Next.js** (App Router, `app/api/articles/[slug]/route.ts`):
924
+
925
+ ```typescript
926
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
927
+
928
+ const paywall = createFetchPaywall({ accepts: [/* as above */] });
929
+
930
+ export const GET = paywall(async (request: Request, x402, { params }: { params: Promise<{ slug: string }> }) => {
931
+ const { slug } = await params;
932
+ const settled = await x402.settle(); // the settle already made before the handler ran
933
+ return Response.json({ slug, transaction: settled.transactionHash });
934
+ });
935
+ ```
936
+
937
+ **Vercel Functions and Cloudflare Workers** take the `Request` itself; whatever else the platform passes (`env`, `ctx`) comes after the payment:
938
+
939
+ ```typescript
940
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
941
+
942
+ const paywall = createFetchPaywall({
943
+ accepts: [{ network: 'base', asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', amount: '10000', payTo: '0xYourWallet' }],
944
+ });
945
+
946
+ export default {
947
+ fetch: paywall(async (request: Request, x402, env: Env, ctx: ExecutionContext) => new Response('paid content')),
948
+ };
949
+ ```
950
+
951
+ - The route's first argument is the `Request`, or an object that carries it as `request` (Astro, SvelteKit, React Router). The handler gets that same first argument back, then the payment, then the rest.
952
+ - The advertised `resource` is `request.url`. Behind a proxy that rewrites the host, set `resource` in the accept.
953
+ - A facilitator receipt goes out as `PAYMENT-RESPONSE` on the response the handler returns, added to its own `Access-Control-Expose-Headers` and `Cache-Control` rather than replacing them; a `Response.redirect()` works too. In `'manual'` mode, a `settle()` that ends after the handler returned changes nothing in the response.
954
+ - Once a receipt exists, a handler that throws an `Error` gets a `500` that keeps the receipt headers, as Hono's default error handler answers one, and the error goes to `console.error`: the buyer may have paid, and the receipt is their proof. Such an error does not reach the framework's error reporting (Next.js `onRequestError`, a Sentry wrapper); catch it inside the handler if it must. The framework's own control flow passes through untouched: a thrown `Response` (React Router, Remix) is rethrown with the receipt headers, and SvelteKit's `redirect()` / `error()` and Next.js' `redirect()` / `notFound()` are rethrown as they are, so the receipt does not ride on them; to send it with a redirect, return `Response.redirect()` instead. Without a receipt every error reaches the framework as usual.
955
+ - The paywall answers every request that reaches it, `OPTIONS` included. If browsers on another origin pay, answer the preflight before the paywall (an `OPTIONS` export in Astro or Next.js, a method check in a Worker) and add `Access-Control-Allow-Origin` to what the route returns (`getCorsHeaders(origin)`).
892
956
 
893
957
  ### Stack key (services run by Ultravioleta DAO)
894
958
 
895
- Services run by Ultravioleta DAO carry a per-service credential, the stack key, so that the facilitator's rate-limit policy does not answer them `429`. Everything in this SDK that calls the facilitator sends it as `X-UVD-Stack-Key`: the clients `FacilitatorClient` (and the two middlewares), `Erc8004Client`, `BazaarClient`, `EscrowClient` and `AdvancedEscrowClient`, and the functions `anchorEvidence`, `availableBackends`, `streamTrafficEvents` and `getFacilitatorReceipt`, which take the same `stackKey` / `stackKeyHosts` options. It changes nothing else about a payment, and a facilitator that does not know the header ignores it.
959
+ Services run by Ultravioleta DAO carry a per-service credential, the stack key, so that the facilitator's rate-limit policy does not answer them `429`. Everything in this SDK that calls the facilitator sends it as `X-UVD-Stack-Key`: the clients `FacilitatorClient` (and the two middlewares and `createFetchPaywall`), `Erc8004Client`, `BazaarClient`, `EscrowClient` and `AdvancedEscrowClient`, and the functions `anchorEvidence`, `availableBackends`, `streamTrafficEvents` and `getFacilitatorReceipt`, which take the same `stackKey` / `stackKeyHosts` options. It changes nothing else about a payment, and a facilitator that does not know the header ignores it.
896
960
 
897
961
  - **Where it comes from:** the facilitator's operator issues one per service: `uvdsk_` followed by 43-128 base64url characters. The process receives it as `UVD_STACK_KEY` (read by default) or as the `stackKey` option, from a secret store, never from code.
898
962
  - **Not for third parties:** if you integrate this SDK in your own product you have no key and need none. Leave both unset and no header is sent.
@@ -1248,7 +1312,7 @@ anti-double-settle guard, adopted here.
1248
1312
 
1249
1313
  ### Middleware
1250
1314
 
1251
- `createPaymentMiddleware` and `createHonoMiddleware` answer **503 with a
1315
+ `createPaymentMiddleware`, `createHonoMiddleware` and `createFetchPaywall` answer **503 with a
1252
1316
  `Retry-After` header** — not `402`, not `500` — whenever the facilitator reached
1253
1317
  no verdict, and keep answering `402` for genuine rejections.
1254
1318
 
@@ -1260,7 +1324,7 @@ the buyer's client can reconcile instead of paying again.
1260
1324
  An X-PAYMENT the facilitator already admitted for another request is answered
1261
1325
  **`409`** (`authorization_already_settled`, `receipt_request_conflict`: it was
1262
1326
  used, the handler does not run) or **`503` + `Retry-After`** while it is
1263
- `authorization_in_flight` — never `402` and never `500`. Both middlewares add
1327
+ `authorization_in_flight` — never `402` and never `500`. All three add
1264
1328
  `PAYMENT-RESPONSE` to an existing `Access-Control-Expose-Headers` and `no-store`
1265
1329
  to an existing `Cache-Control` instead of replacing them, and in `'manual'` mode
1266
1330
  a `settle()` after the handler already answered no longer touches the sent
@@ -1771,7 +1835,7 @@ bazaarExtension({
1771
1835
  });
1772
1836
  ```
1773
1837
 
1774
- The result is `{ bazaar: { info, schema } }`: `info.input` (`type: "http"`, `method`, then `bodyType` and `body` for a body method, or `queryParams` for a query method, plus optional `headers`), `info.output` (`type`, default `json`, and `example`), and `schema`, the JSON Schema (draft 2020-12) that `info` validates against. `bodyType` is `json` (default), `form-data` or `text` (then `body` is a string). A body method without `body`, a query method with one, or a method the spec does not name (`CONNECT`, `OPTIONS`, ...) throws. `extensions` is a v2 field: `create402Response` throws if the response would be v1 rather than dropping it, and without `extensions` it returns exactly what it returned before.
1838
+ The result is `{ bazaar: { info, schema } }`: `info.input` (`type: "http"`, `method`, then `bodyType` and `body` for a body method, or `queryParams` for a query method, plus optional `headers`), `info.output` (`type`, default `json`, and `example`), and `schema`, the JSON Schema (draft 2020-12) that `info` validates against. `bodyType` is `json` (default), `form-data` or `text` (then `body` is a string). A body method without `body`, a query method with one, or a method the spec does not name (`CONNECT`, `OPTIONS`, ...) throws. `extensions` is a v2 field: `create402Response` throws if the response would be v1 rather than dropping it, and without `extensions` the body has no `extensions` key.
1775
1839
 
1776
1840
  ## x402 v2 requests (`buildVerifyRequestV2` / `buildSettleRequestV2`)
1777
1841
 
@@ -2103,8 +2167,8 @@ the authorization and reuse it after uncertainty. Payment confirmation does not
2103
2167
  prove merchant delivery. See [the receipt guide](docs/facilitator-receipts.md).
2104
2168
 
2105
2169
  Every `/verify` and `/settle` carries an `Idempotency-Key`, the same one for both
2106
- calls of a payment and for their retries: `verifyAndSettle` and both middlewares
2107
- create one per payment, and `verify`/`settle` accept `{ idempotencyKey }`
2170
+ calls of a payment and for their retries: `verifyAndSettle`, both middlewares and
2171
+ `createFetchPaywall` create one per payment, and `verify`/`settle` accept `{ idempotencyKey }`
2108
2172
  (`createIdempotencyKey()`). The facilitator returns an admitted payment's original
2109
2173
  answer (`SettleResponse.replayed`, from `Idempotent-Replayed: true`) only to that
2110
2174
  key or to the buyer's `X-UVD-Purchase`; a resend without them is
@@ -103,10 +103,12 @@ var SUPPORTED_CHAINS = {
103
103
  name: "USD Coin",
104
104
  version: "2"
105
105
  },
106
+ // name() on-chain is "Euro Coin" here and "EURC" on Base: pinned to each
107
+ // contract's DOMAIN_SEPARATOR() by src/eurc-domains.test.ts.
106
108
  eurc: {
107
109
  address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD",
108
110
  decimals: 6,
109
- name: "EURC",
111
+ name: "Euro Coin",
110
112
  version: "2",
111
113
  usdPegged: false
112
114
  },