uvd-x402-sdk 2.101.0 → 2.103.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 (77) hide show
  1. package/README.md +70 -7
  2. package/dist/adapters/index.d.mts +1 -1
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/adapters/index.js +120 -10
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/index.mjs +120 -10
  7. package/dist/adapters/index.mjs.map +1 -1
  8. package/dist/backend/index.d.mts +1 -1
  9. package/dist/backend/index.d.ts +1 -1
  10. package/dist/backend/index.js +317 -100
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/index.mjs +317 -101
  13. package/dist/backend/index.mjs.map +1 -1
  14. package/dist/{index-49h53dGB.d.ts → index-CLlhFKbm.d.ts} +83 -7
  15. package/dist/{index-Bc91DzIt.d.mts → index-fImniLTl.d.mts} +83 -7
  16. package/dist/index.d.mts +8 -3
  17. package/dist/index.d.ts +8 -3
  18. package/dist/index.js +327 -100
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +327 -101
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{ows-Tt33WE0s.d.ts → ows-BB6Sgj0W.d.ts} +3 -0
  23. package/dist/{ows-9uNT7ZSZ.d.mts → ows-DazT4KQH.d.mts} +3 -0
  24. package/dist/providers/algorand/index.js +3 -1
  25. package/dist/providers/algorand/index.js.map +1 -1
  26. package/dist/providers/algorand/index.mjs +3 -1
  27. package/dist/providers/algorand/index.mjs.map +1 -1
  28. package/dist/providers/evm/index.js +34 -2
  29. package/dist/providers/evm/index.js.map +1 -1
  30. package/dist/providers/evm/index.mjs +34 -2
  31. package/dist/providers/evm/index.mjs.map +1 -1
  32. package/dist/providers/near/index.js +3 -1
  33. package/dist/providers/near/index.js.map +1 -1
  34. package/dist/providers/near/index.mjs +3 -1
  35. package/dist/providers/near/index.mjs.map +1 -1
  36. package/dist/providers/solana/index.js +3 -1
  37. package/dist/providers/solana/index.js.map +1 -1
  38. package/dist/providers/solana/index.mjs +3 -1
  39. package/dist/providers/solana/index.mjs.map +1 -1
  40. package/dist/providers/stellar/index.js +31 -1
  41. package/dist/providers/stellar/index.js.map +1 -1
  42. package/dist/providers/stellar/index.mjs +31 -1
  43. package/dist/providers/stellar/index.mjs.map +1 -1
  44. package/dist/providers/sui/index.js +30 -1
  45. package/dist/providers/sui/index.js.map +1 -1
  46. package/dist/providers/sui/index.mjs +30 -1
  47. package/dist/providers/sui/index.mjs.map +1 -1
  48. package/dist/providers/xrpl/index.js +3 -1
  49. package/dist/providers/xrpl/index.js.map +1 -1
  50. package/dist/providers/xrpl/index.mjs +3 -1
  51. package/dist/providers/xrpl/index.mjs.map +1 -1
  52. package/dist/react/index.js +52 -5
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react/index.mjs +52 -5
  55. package/dist/react/index.mjs.map +1 -1
  56. package/dist/react/picker/index.js +3 -1
  57. package/dist/react/picker/index.js.map +1 -1
  58. package/dist/react/picker/index.mjs +3 -1
  59. package/dist/react/picker/index.mjs.map +1 -1
  60. package/dist/utils/index.js +4 -2
  61. package/dist/utils/index.js.map +1 -1
  62. package/dist/utils/index.mjs +4 -2
  63. package/dist/utils/index.mjs.map +1 -1
  64. package/package.json +2 -1
  65. package/src/adapters/env-key.ts +5 -1
  66. package/src/adapters/ows.ts +3 -0
  67. package/src/backend/index.ts +353 -63
  68. package/src/chains/index.ts +3 -1
  69. package/src/client/X402Client.ts +23 -3
  70. package/src/escrow-preauth.ts +32 -4
  71. package/src/index.ts +6 -0
  72. package/src/lifecycle-auth.ts +37 -31
  73. package/src/providers/evm/index.ts +9 -1
  74. package/src/providers/stellar/index.ts +5 -0
  75. package/src/providers/sui/index.ts +4 -0
  76. package/src/utils/uint.ts +252 -0
  77. package/src/utils/x402.ts +5 -2
package/README.md CHANGED
@@ -888,11 +888,74 @@ app.post('/api/premium', async (req, res) => {
888
888
  });
889
889
  ```
890
890
 
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).
891
+ `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).
892
+
893
+ 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).
894
+
895
+ ### Astro, Next.js, Vercel and Cloudflare (`createFetchPaywall`)
896
+
897
+ 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.
898
+
899
+ **Astro** (`src/pages/api/premium/[id].ts`, on-demand rendering with any adapter, e.g. `@astrojs/vercel`):
900
+
901
+ ```typescript
902
+ import type { APIRoute } from 'astro';
903
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
904
+
905
+ // Run on every request: a prerendered endpoint is called once, at build time, without a payment.
906
+ export const prerender = false;
907
+
908
+ const paywall = createFetchPaywall({
909
+ accepts: [{
910
+ network: 'base',
911
+ asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
912
+ amount: '10000', // $0.01 (6 decimals)
913
+ payTo: process.env.RECEIVING_ADDRESS as string,
914
+ }],
915
+ });
916
+
917
+ export const GET: APIRoute = paywall(async (context, x402) => {
918
+ return Response.json({ id: context.params.id, payer: x402.verifyResult.payer });
919
+ });
920
+ ```
921
+
922
+ **Next.js** (App Router, `app/api/articles/[slug]/route.ts`):
923
+
924
+ ```typescript
925
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
926
+
927
+ const paywall = createFetchPaywall({ accepts: [/* as above */] });
928
+
929
+ export const GET = paywall(async (request: Request, x402, { params }: { params: Promise<{ slug: string }> }) => {
930
+ const { slug } = await params;
931
+ const settled = await x402.settle(); // the settle already made before the handler ran
932
+ return Response.json({ slug, transaction: settled.transactionHash });
933
+ });
934
+ ```
935
+
936
+ **Vercel Functions and Cloudflare Workers** take the `Request` itself; whatever else the platform passes (`env`, `ctx`) comes after the payment:
937
+
938
+ ```typescript
939
+ import { createFetchPaywall } from 'uvd-x402-sdk/backend';
940
+
941
+ const paywall = createFetchPaywall({
942
+ accepts: [{ network: 'base', asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', amount: '10000', payTo: '0xYourWallet' }],
943
+ });
944
+
945
+ export default {
946
+ fetch: paywall(async (request: Request, x402, env: Env, ctx: ExecutionContext) => new Response('paid content')),
947
+ };
948
+ ```
949
+
950
+ - 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.
951
+ - The advertised `resource` is `request.url`. Behind a proxy that rewrites the host, set `resource` in the accept.
952
+ - 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.
953
+ - 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.
954
+ - 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
955
 
893
956
  ### Stack key (services run by Ultravioleta DAO)
894
957
 
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.
958
+ 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
959
 
897
960
  - **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
961
  - **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 +1311,7 @@ anti-double-settle guard, adopted here.
1248
1311
 
1249
1312
  ### Middleware
1250
1313
 
1251
- `createPaymentMiddleware` and `createHonoMiddleware` answer **503 with a
1314
+ `createPaymentMiddleware`, `createHonoMiddleware` and `createFetchPaywall` answer **503 with a
1252
1315
  `Retry-After` header** — not `402`, not `500` — whenever the facilitator reached
1253
1316
  no verdict, and keep answering `402` for genuine rejections.
1254
1317
 
@@ -1260,7 +1323,7 @@ the buyer's client can reconcile instead of paying again.
1260
1323
  An X-PAYMENT the facilitator already admitted for another request is answered
1261
1324
  **`409`** (`authorization_already_settled`, `receipt_request_conflict`: it was
1262
1325
  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
1326
+ `authorization_in_flight` — never `402` and never `500`. All three add
1264
1327
  `PAYMENT-RESPONSE` to an existing `Access-Control-Expose-Headers` and `no-store`
1265
1328
  to an existing `Cache-Control` instead of replacing them, and in `'manual'` mode
1266
1329
  a `settle()` after the handler already answered no longer touches the sent
@@ -1771,7 +1834,7 @@ bazaarExtension({
1771
1834
  });
1772
1835
  ```
1773
1836
 
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.
1837
+ 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
1838
 
1776
1839
  ## x402 v2 requests (`buildVerifyRequestV2` / `buildSettleRequestV2`)
1777
1840
 
@@ -2103,8 +2166,8 @@ the authorization and reuse it after uncertainty. Payment confirmation does not
2103
2166
  prove merchant delivery. See [the receipt guide](docs/facilitator-receipts.md).
2104
2167
 
2105
2168
  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 }`
2169
+ calls of a payment and for their retries: `verifyAndSettle`, both middlewares and
2170
+ `createFetchPaywall` create one per payment, and `verify`/`settle` accept `{ idempotencyKey }`
2108
2171
  (`createIdempotencyKey()`). The facilitator returns an admitted payment's original
2109
2172
  answer (`SettleResponse.replayed`, from `Idempotent-Replayed: true`) only to that
2110
2173
  key or to the buyer's `X-UVD-Purchase`; a resend without them is
@@ -1,5 +1,5 @@
1
1
  import { X as X402Version, a as PaymentResult } from '../index-DagQ74ZQ.mjs';
2
- export { E as EnvKeyAdapter, O as OWSAccountInfo, a as OWSCore, b as OWSSignResult, c as OWSWallet, d as OWSWalletAdapter, e as OWSWalletAdapterOptions } from '../ows-9uNT7ZSZ.mjs';
2
+ export { E as EnvKeyAdapter, O as OWSAccountInfo, a as OWSCore, b as OWSSignResult, c as OWSWallet, d as OWSWalletAdapter, e as OWSWalletAdapterOptions } from '../ows-DazT4KQH.mjs';
3
3
  import '../wallet-T0NFUUkT.mjs';
4
4
  import 'ethers';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { X as X402Version, a as PaymentResult } from '../index-DagQ74ZQ.js';
2
- export { E as EnvKeyAdapter, O as OWSAccountInfo, a as OWSCore, b as OWSSignResult, c as OWSWallet, d as OWSWalletAdapter, e as OWSWalletAdapterOptions } from '../ows-Tt33WE0s.js';
2
+ export { E as EnvKeyAdapter, O as OWSAccountInfo, a as OWSCore, b as OWSSignResult, c as OWSWallet, d as OWSWalletAdapter, e as OWSWalletAdapterOptions } from '../ows-BB6Sgj0W.js';
3
3
  import '../wallet-T0NFUUkT.js';
4
4
  import 'ethers';
5
5
 
@@ -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
  },
@@ -1443,6 +1445,110 @@ function useX402Wagmi(walletClient) {
1443
1445
  createPaymentFull
1444
1446
  };
1445
1447
  }
1448
+ var INTEGER_TYPE = /^u?int\d*$/;
1449
+ function isRecord(value) {
1450
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1451
+ }
1452
+ function inexactNumberMessage(field, value) {
1453
+ return `${field} is the JSON number ${String(value)}, larger in magnitude than Number.MAX_SAFE_INTEGER (2**53 - 1): JSON.parse has already rounded it, and signing it would sign a different integer than the one that was written. Send uints as decimal or 0x-hex strings (or a bigint)`;
1454
+ }
1455
+ function numberRefusal(path, value, code) {
1456
+ return new X402Error(
1457
+ Number.isInteger(value) ? inexactNumberMessage(path, value) : `${path} must be an integer, got the JSON number ${String(value)}`,
1458
+ code
1459
+ );
1460
+ }
1461
+ function findInexactNumber(value, path, seen = /* @__PURE__ */ new Set()) {
1462
+ if (typeof value === "number") {
1463
+ return Number.isSafeInteger(value) ? null : { path, value };
1464
+ }
1465
+ if (typeof value !== "object" || value === null || seen.has(value)) return null;
1466
+ seen.add(value);
1467
+ const entries = Array.isArray(value) ? value.map((item, i) => [`${path}[${i}]`, item]) : Object.entries(value).map(([key, item]) => [`${path}.${key}`, item]);
1468
+ for (const [itemPath, item] of entries) {
1469
+ const hit = findInexactNumber(item, itemPath, seen);
1470
+ if (hit) return hit;
1471
+ }
1472
+ return null;
1473
+ }
1474
+ function assertNoInexactNumber(value, path, code = "INVALID_CONFIG") {
1475
+ const hit = findInexactNumber(value, path);
1476
+ if (hit) throw numberRefusal(hit.path, hit.value, code);
1477
+ }
1478
+ function rootStruct(structs, primaryType) {
1479
+ try {
1480
+ return ethers.ethers.TypedDataEncoder.from(
1481
+ structs
1482
+ ).primaryType;
1483
+ } catch {
1484
+ return typeof primaryType === "string" && Array.isArray(structs[primaryType]) ? primaryType : null;
1485
+ }
1486
+ }
1487
+ function checkTyped(type, value, path, structs, code, ancestors = /* @__PURE__ */ new Set()) {
1488
+ if (typeof value === "object" && value !== null) {
1489
+ if (ancestors.has(value)) return;
1490
+ ancestors.add(value);
1491
+ }
1492
+ try {
1493
+ const array = /^(.*)\[(\d*)\]$/.exec(type);
1494
+ if (array) {
1495
+ if (Array.isArray(value)) {
1496
+ value.forEach(
1497
+ (item, i) => checkTyped(array[1], item, `${path}[${i}]`, structs, code, ancestors)
1498
+ );
1499
+ }
1500
+ return;
1501
+ }
1502
+ const struct = structs[type];
1503
+ if (Array.isArray(struct)) {
1504
+ if (!isRecord(value)) return;
1505
+ for (const field of struct) {
1506
+ if (typeof field?.name === "string" && typeof field.type === "string") {
1507
+ checkTyped(
1508
+ field.type,
1509
+ value[field.name],
1510
+ `${path}.${field.name}`,
1511
+ structs,
1512
+ code,
1513
+ ancestors
1514
+ );
1515
+ }
1516
+ }
1517
+ return;
1518
+ }
1519
+ if (INTEGER_TYPE.test(type) && typeof value === "number" && !Number.isSafeInteger(value)) {
1520
+ throw numberRefusal(path, value, code);
1521
+ }
1522
+ } finally {
1523
+ if (typeof value === "object" && value !== null) ancestors.delete(value);
1524
+ }
1525
+ }
1526
+ function assertTypedDataIntegersExact(doc, where = "typed data", code = "INVALID_CONFIG") {
1527
+ if (!isRecord(doc)) return;
1528
+ const { domain, types, message, primaryType } = doc;
1529
+ if (isRecord(domain) && typeof domain.chainId === "number" && !Number.isSafeInteger(domain.chainId)) {
1530
+ throw numberRefusal(`${where} domain.chainId`, domain.chainId, code);
1531
+ }
1532
+ if (!isRecord(message)) return;
1533
+ const structs = isRecord(types) ? { ...types } : {};
1534
+ delete structs["EIP712Domain"];
1535
+ const root = rootStruct(structs, primaryType);
1536
+ if (root) {
1537
+ checkTyped(root, message, `${where} message`, structs, code);
1538
+ if (typeof primaryType === "string" && primaryType !== root && Array.isArray(structs[primaryType])) {
1539
+ checkTyped(primaryType, message, `${where} message`, structs, code);
1540
+ }
1541
+ return;
1542
+ }
1543
+ assertNoInexactNumber(message, `${where} message`, code);
1544
+ }
1545
+ function parseTypedDataJson(text, where = "typed data", code = "INVALID_CONFIG") {
1546
+ const doc = JSON.parse(text);
1547
+ assertTypedDataIntegersExact(doc, where, code);
1548
+ return doc;
1549
+ }
1550
+
1551
+ // src/adapters/env-key.ts
1446
1552
  var EnvKeyAdapter = class {
1447
1553
  wallet;
1448
1554
  /**
@@ -1482,9 +1588,12 @@ var EnvKeyAdapter = class {
1482
1588
  *
1483
1589
  * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1484
1590
  * @returns Object with signature and v/r/s components
1591
+ * @throws {X402Error} `INVALID_CONFIG` when an integer field of `message`
1592
+ * (or `domain.chainId`) is a JSON number above 2**53 - 1: `JSON.parse`
1593
+ * rounded it, so it is refused instead of signed. Write uints as strings.
1485
1594
  */
1486
1595
  async signTypedData(typedData) {
1487
- const parsed = JSON.parse(typedData);
1596
+ const parsed = parseTypedDataJson(typedData);
1488
1597
  const { domain, types, message } = parsed;
1489
1598
  const cleanTypes = { ...types };
1490
1599
  delete cleanTypes["EIP712Domain"];
@@ -1618,7 +1727,7 @@ var ETHERS_NAME_OF = { input: "data", gas: "gasLimit" };
1618
1727
  var TWO_127 = 1n << 127n;
1619
1728
  var TWO_128 = 1n << 128n;
1620
1729
  var TWO_256 = 1n << 256n;
1621
- function isRecord(value) {
1730
+ function isRecord2(value) {
1622
1731
  return typeof value === "object" && value !== null && !Array.isArray(value);
1623
1732
  }
1624
1733
  function shown(value) {
@@ -1635,6 +1744,7 @@ function refuse(message) {
1635
1744
  function toInteger(value, where) {
1636
1745
  if (typeof value === "bigint") return value;
1637
1746
  if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
1747
+ if (typeof value === "number" && Number.isInteger(value)) throw refuse(inexactNumberMessage(where, value));
1638
1748
  if (typeof value === "string" && /^-?(0[xX][0-9a-fA-F]+|[0-9]+)$/.test(value)) {
1639
1749
  return value.startsWith("-") ? -BigInt(value.slice(1)) : BigInt(value);
1640
1750
  }
@@ -1662,7 +1772,7 @@ function owsValue(kind, value, types, where) {
1662
1772
  }
1663
1773
  const struct = types[kind];
1664
1774
  if (struct) {
1665
- if (!isRecord(value)) throw refuse(`${where}: ${kind} needs an object, got ${shown(value)}`);
1775
+ if (!isRecord2(value)) throw refuse(`${where}: ${kind} needs an object, got ${shown(value)}`);
1666
1776
  const out = {};
1667
1777
  for (const field of struct) {
1668
1778
  if (value[field.name] === void 0) throw refuse(`${where}: ${kind} lacks ${field.name}`);
@@ -1716,11 +1826,11 @@ function owsValue(kind, value, types, where) {
1716
1826
  throw refuse(`${where}: unsupported EIP-712 type ${kind}`);
1717
1827
  }
1718
1828
  function prepareTypedData(input) {
1719
- if (!isRecord(input)) throw refuse("typed data must be an object with domain, types and message");
1829
+ if (!isRecord2(input)) throw refuse("typed data must be an object with domain, types and message");
1720
1830
  const { domain, types, message, primaryType } = input;
1721
- if (!isRecord(domain)) throw refuse("typed data needs a domain object");
1722
- if (!isRecord(types)) throw refuse("typed data needs a types object");
1723
- if (!isRecord(message)) throw refuse("typed data needs a message object");
1831
+ if (!isRecord2(domain)) throw refuse("typed data needs a domain object");
1832
+ if (!isRecord2(types)) throw refuse("typed data needs a types object");
1833
+ if (!isRecord2(message)) throw refuse("typed data needs a message object");
1724
1834
  const structs = { ...types };
1725
1835
  delete structs["EIP712Domain"];
1726
1836
  let root;
@@ -1798,7 +1908,7 @@ var OWSWalletAdapter = class {
1798
1908
  #chain;
1799
1909
  #address;
1800
1910
  constructor(ows, options) {
1801
- if (isRecord(ows) && "accounts" in ows && (options === void 0 || typeof options === "number")) {
1911
+ if (isRecord2(ows) && "accounts" in ows && (options === void 0 || typeof options === "number")) {
1802
1912
  throw new X402Error(
1803
1913
  `OWSWalletAdapter no longer takes a wallet object with accounts (the deprecated OWSWallet shape, which never worked with @open-wallet-standard/core 1.x); ${MIGRATION}. Nothing was signed.`,
1804
1914
  "INVALID_CONFIG"
@@ -1813,7 +1923,7 @@ var OWSWalletAdapter = class {
1813
1923
  "INVALID_CONFIG"
1814
1924
  );
1815
1925
  }
1816
- const opts = isRecord(options) ? options : {};
1926
+ const opts = isRecord2(options) ? options : {};
1817
1927
  if (typeof opts.wallet !== "string" || opts.wallet === "") {
1818
1928
  throw new X402Error("OWSWalletAdapter needs options.wallet: the name or id of a wallet in the vault", "INVALID_CONFIG");
1819
1929
  }