uvd-x402-sdk 2.104.1 → 2.104.3

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 (69) hide show
  1. package/README.md +88 -1
  2. package/dist/adapters/index.d.mts +2 -2
  3. package/dist/adapters/index.d.ts +2 -2
  4. package/dist/adapters/index.js +135 -5
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/index.mjs +135 -5
  7. package/dist/adapters/index.mjs.map +1 -1
  8. package/dist/backend/index.d.mts +3 -3
  9. package/dist/backend/index.d.ts +3 -3
  10. package/dist/backend/index.js +760 -26
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/index.mjs +760 -28
  13. package/dist/backend/index.mjs.map +1 -1
  14. package/dist/{escrow-contracts-C59jszp3.d.ts → escrow-contracts-Bf3PU_Ub.d.ts} +496 -4
  15. package/dist/{escrow-contracts-Dgcyu7Fh.d.mts → escrow-contracts-amyDktMD.d.mts} +496 -4
  16. package/dist/{index-QswKm6BF.d.ts → index-CZHgEr58.d.ts} +2 -2
  17. package/dist/{index-DagQ74ZQ.d.ts → index-DARXHuQl.d.mts} +1 -1
  18. package/dist/{index-DagQ74ZQ.d.mts → index-DARXHuQl.d.ts} +1 -1
  19. package/dist/{index-CS4qStqy.d.mts → index-jNJd8jEb.d.mts} +2 -2
  20. package/dist/index.d.mts +22 -429
  21. package/dist/index.d.ts +22 -429
  22. package/dist/index.js +645 -33
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +644 -34
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/{ows-BB6Sgj0W.d.ts → ows-CW-g7qGw.d.ts} +9 -2
  27. package/dist/{ows-DazT4KQH.d.mts → ows-DJu424iz.d.mts} +9 -2
  28. package/dist/providers/algorand/index.d.mts +1 -1
  29. package/dist/providers/algorand/index.d.ts +1 -1
  30. package/dist/providers/evm/index.d.mts +1 -1
  31. package/dist/providers/evm/index.d.ts +1 -1
  32. package/dist/providers/evm/index.js.map +1 -1
  33. package/dist/providers/evm/index.mjs.map +1 -1
  34. package/dist/providers/hedera/index.d.mts +1 -1
  35. package/dist/providers/hedera/index.d.ts +1 -1
  36. package/dist/providers/near/index.d.mts +1 -1
  37. package/dist/providers/near/index.d.ts +1 -1
  38. package/dist/providers/solana/index.d.mts +1 -1
  39. package/dist/providers/solana/index.d.ts +1 -1
  40. package/dist/providers/stellar/index.d.mts +1 -1
  41. package/dist/providers/stellar/index.d.ts +1 -1
  42. package/dist/providers/stellar/index.js.map +1 -1
  43. package/dist/providers/stellar/index.mjs.map +1 -1
  44. package/dist/providers/sui/index.d.mts +1 -1
  45. package/dist/providers/sui/index.d.ts +1 -1
  46. package/dist/providers/sui/index.js.map +1 -1
  47. package/dist/providers/sui/index.mjs.map +1 -1
  48. package/dist/providers/xrpl/index.d.mts +1 -1
  49. package/dist/providers/xrpl/index.d.ts +1 -1
  50. package/dist/react/index.d.mts +4 -4
  51. package/dist/react/index.d.ts +4 -4
  52. package/dist/react/index.js.map +1 -1
  53. package/dist/react/index.mjs.map +1 -1
  54. package/dist/react/picker/index.d.mts +1 -1
  55. package/dist/react/picker/index.d.ts +1 -1
  56. package/dist/{receipts-8XsMeo1y.d.ts → receipts-CMuWVtO0.d.ts} +1 -1
  57. package/dist/{receipts-COPJ_BY9.d.mts → receipts-DsOSK0yB.d.mts} +1 -1
  58. package/dist/utils/index.d.mts +2 -2
  59. package/dist/utils/index.d.ts +2 -2
  60. package/dist/{validity-BpwXbfdj.d.ts → validity-BshzF_Ga.d.ts} +1 -1
  61. package/dist/{validity-eGGXxzpi.d.mts → validity-y_M1oNCt.d.mts} +1 -1
  62. package/package.json +1 -1
  63. package/src/adapters/env-key.ts +9 -2
  64. package/src/adapters/ows.ts +12 -3
  65. package/src/backend/durable.ts +582 -0
  66. package/src/backend/index.ts +149 -5
  67. package/src/escrow-preauth.ts +38 -33
  68. package/src/index.ts +7 -0
  69. package/src/utils/uint.ts +211 -3
package/README.md CHANGED
@@ -684,7 +684,7 @@ const paymentAuth = await buildEscrowPreAuth(new EnvKeyAdapter(), {
684
684
  // Send as the X-Payment-Auth header (raw JSON, NOT base64).
685
685
  ```
686
686
 
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
+ 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`, `maxFeeBps` against the chain's ceiling in `ESCROW_MAX_FEE_BPS`, and the USDC EIP-712 domain (`usdc_domain_name` / `usdc_domain_version`) against the token's on-chain domain in `VERIFIED_USDC_DOMAINS`. 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 four 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.
688
688
 
689
689
  ## Multi-Stablecoin (EVM)
690
690
 
@@ -889,6 +889,8 @@ app.post('/api/premium', async (req, res) => {
889
889
  });
890
890
  ```
891
891
 
892
+ To be listed in the facilitator's Bazaar after a paid call, flag the accepts as discoverable: `discoverable: true` in the options of `createHonoMiddleware()` or `createFetchPaywall()` writes `extra.discoverable: true` on every accept, in the 402 and in the requirements sent to `/verify` and `/settle`; the other `extra` fields (`name`, `version`) stay. `false` writes `discoverable: false`, overriding a hand-written `true`, as the Python SDK's `False` does (for the facilitator, `false` and no flag both mean "not listed"); without the option the accepts go out as given. Native Hedera accepts go out as given with either value: the facilitator allows only `feePayer` in their `extra`, so a Hedera settle is never listed. For one accept, or for `create402Response`, use `withDiscoverable(accept)` (`withDiscoverable(accept, false)` to opt out); it throws for a native Hedera accept. Neither modifies the object it is given. A `discoverable` that is not a boolean and an `extra` that is not a plain object throw when the paywall is built, before any request.
893
+
892
894
  `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
895
 
894
896
  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).
@@ -2050,6 +2052,91 @@ const result = await anchorEvidence(body, {
2050
2052
  res.setHeader('X-Durable-Evidence', evidenceHeader(result));
2051
2053
  ```
2052
2054
 
2055
+ ### Seller: from the paywall itself (`durable`)
2056
+
2057
+ `createFetchPaywall`, `createHonoMiddleware` and `createPaymentMiddleware`
2058
+ (Express) take an opt-in `durable`. With it, a paid `2xx` answer that the
2059
+ handler did not anchor itself is captured once, sealed and anchored through
2060
+ `anchorEvidence`, and goes out with `X-Durable-Evidence`:
2061
+
2062
+ ```ts
2063
+ import { TypedDataEncoder, getBytes } from 'ethers';
2064
+ import { createFetchPaywall, type DurableEvidenceContext } from 'uvd-x402-sdk/backend';
2065
+ import { caip2ToChain, getChainByName, payerKeyFromEvmSignature } from 'uvd-x402-sdk';
2066
+
2067
+ const TRANSFER_WITH_AUTHORIZATION = {
2068
+ TransferWithAuthorization: [
2069
+ { name: 'from', type: 'address' }, { name: 'to', type: 'address' },
2070
+ { name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' },
2071
+ { name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' },
2072
+ ],
2073
+ };
2074
+
2075
+ // The key behind an EIP-3009 USDC payment: recovered from the signature over the
2076
+ // digest the buyer signed, with the token domain of the chain registry.
2077
+ const evmPayerKey = ({ payment }: DurableEvidenceContext) => {
2078
+ const payload = payment.payment.payload;
2079
+ if (!('authorization' in payload)) return undefined; // not an EVM payment: skipped
2080
+ const network = payment.requirements.network;
2081
+ const chain = getChainByName(caip2ToChain(network) ?? network);
2082
+ if (!chain) return undefined;
2083
+ const digest = TypedDataEncoder.hash(
2084
+ { name: chain.usdc.name, version: chain.usdc.version, chainId: chain.chainId, verifyingContract: payment.requirements.asset },
2085
+ TRANSFER_WITH_AUTHORIZATION,
2086
+ payload.authorization,
2087
+ );
2088
+ return payerKeyFromEvmSignature(payload.signature, getBytes(digest));
2089
+ };
2090
+
2091
+ const paywall = createFetchPaywall({
2092
+ accepts,
2093
+ durable: {
2094
+ payerKey: evmPayerKey, // Solana: ({ payment }) => payerKeyFromSolanaAddress(payment.verifyResult.payer!)
2095
+ maxBodyBytes: 64 * 1024, // default ANCHOR_MAX_REQUEST_BYTES
2096
+ timeoutMs: 5000, // default DURABLE_DEFAULT_TIMEOUT_MS
2097
+ },
2098
+ });
2099
+ ```
2100
+
2101
+ A wrong digest does not throw: it recovers another valid key, and the evidence
2102
+ would be sealed to it. `src/backend/durable-evidence.test.ts` checks this
2103
+ derivation against a signature made with a synthetic key.
2104
+
2105
+ Without `durable` nothing changes and nothing is anchored. It anchors only a
2106
+ payment this request settled with a transaction hash (`'manual'`: once
2107
+ `x402.settle()` resolved successfully before the answer); a replayed settle, or
2108
+ one whose receipt is `pending`, `unknown` or `rejected`, is not anchored. The body is read once
2109
+ and held at most `maxBodyBytes`; past that, past `timeoutMs`, for an event
2110
+ stream, for a body already locked or read, or when the handler's own stream
2111
+ fails, the response goes out whole and unchanged except for a skip notice in
2112
+ the header, and nothing is anchored. A failed anchor is a skip notice too: the
2113
+ response and the payment stand. A `HEAD`, a non-`2xx` answer and a response that
2114
+ already has `X-Durable-Evidence` are left alone. Every field of `durable` but
2115
+ `payerKey`, `maxBodyBytes` and `timeoutMs` is the `anchorEvidence` option of the
2116
+ same name; `facilitator` defaults to the paywall's facilitator.
2117
+
2118
+ The skip notices the paywall adds, besides those of `anchorEvidence`:
2119
+
2120
+ | `skipped` | When |
2121
+ |---|---|
2122
+ | `no_payer_key` | `payerKey` returned `undefined` or an empty key, or threw |
2123
+ | `too_large` | the body is over `maxBodyBytes` (declared or read) |
2124
+ | `not_capturable` | an event stream, a body already sent, a chunk that is not bytes, or a handler stream that failed |
2125
+ | `timeout` | capture, `payerKey`, upload or anchor did not finish within `timeoutMs` |
2126
+ | `anchor_failed` | the anchor failed |
2127
+
2128
+ In Express, the answer is held from the handler's `end()` until the anchor
2129
+ returns (at most `timeoutMs`). In that window `res.headersSent` and
2130
+ `res.writableEnded` are `true`, a further `write()` is refused as a write after
2131
+ end, a further `end()` only queues its callback, and headers can no longer be
2132
+ set: the paid answer goes out as the handler ended it. A close the server asks
2133
+ for in that window without an error (`res.destroy()` or `req.socket.destroy()`,
2134
+ which is what Express's final handler does on an error once headers are sent)
2135
+ waits until the body is written and then closes the connection. A close with an
2136
+ error, or one once the connection can no longer write, is not held. A client
2137
+ that hangs up its side during the window does not get the body: the window
2138
+ lengthens (up to `timeoutMs`) the time in which that happens.
2139
+
2053
2140
  **It never throws.** Every failure resolves to `result.skipped`, because
2054
2141
  evidence is an addition to the payment path and must never be a gate in front of
2055
2142
  it. An unreachable facilitator costs the receipt, never the sale.
@@ -1,5 +1,5 @@
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-DazT4KQH.mjs';
1
+ import { X as X402Version, a as PaymentResult } from '../index-DARXHuQl.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-DJu424iz.mjs';
3
3
  import '../wallet-T0NFUUkT.mjs';
4
4
  import 'ethers';
5
5
 
@@ -1,5 +1,5 @@
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-BB6Sgj0W.js';
1
+ import { X as X402Version, a as PaymentResult } from '../index-DARXHuQl.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-CW-g7qGw.js';
3
3
  import '../wallet-T0NFUUkT.js';
4
4
  import 'ethers';
5
5
 
@@ -1545,8 +1545,124 @@ function assertTypedDataIntegersExact(doc, where = "typed data", code = "INVALID
1545
1545
  }
1546
1546
  assertNoInexactNumber(message, `${where} message`, code);
1547
1547
  }
1548
+ var MIN_EXACT_DIGITS = 22;
1549
+ var MAX_EXACT_DIGITS = 78;
1550
+ var MAX_DOUBLE_DIGITS = 17;
1551
+ function doubleShaped(literal) {
1552
+ const significant = literal.replace(/^-/, "").replace(/0+$/, "");
1553
+ return significant.length <= MAX_DOUBLE_DIGITS || BigInt(literal) === BigInt(Number(literal));
1554
+ }
1555
+ var JSON_NUMBER = /-?(?:0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/y;
1556
+ var JSON_SPACE = /[ \t\n\r]*/y;
1557
+ function dataDescriptor(value) {
1558
+ const descriptor = /* @__PURE__ */ Object.create(null);
1559
+ descriptor.value = value;
1560
+ descriptor.writable = true;
1561
+ descriptor.enumerable = true;
1562
+ descriptor.configurable = true;
1563
+ return descriptor;
1564
+ }
1565
+ function parseJsonExact(input) {
1566
+ const text = String(input);
1567
+ let pos = 0;
1568
+ let wide = false;
1569
+ const fail = (what) => {
1570
+ throw new SyntaxError(`${what} in JSON at position ${pos}`);
1571
+ };
1572
+ const space = () => {
1573
+ JSON_SPACE.lastIndex = pos;
1574
+ if (JSON_SPACE.exec(text)) pos = JSON_SPACE.lastIndex;
1575
+ };
1576
+ const string = () => {
1577
+ if (text[pos] !== '"') fail("Expected a string");
1578
+ let end = pos + 1;
1579
+ while (end < text.length && text[end] !== '"') end += text[end] === "\\" ? 2 : 1;
1580
+ if (end >= text.length) fail("Unterminated string");
1581
+ const value = JSON.parse(text.slice(pos, end + 1));
1582
+ pos = end + 1;
1583
+ return value;
1584
+ };
1585
+ const key = () => {
1586
+ const name = string();
1587
+ space();
1588
+ if (text[pos] !== ":") fail("Expected ':'");
1589
+ pos += 1;
1590
+ space();
1591
+ return name;
1592
+ };
1593
+ const scalar = () => {
1594
+ if (text[pos] === '"') return string();
1595
+ for (const [word, value] of [["true", true], ["false", false], ["null", null]]) {
1596
+ if (text.startsWith(word, pos)) {
1597
+ pos += word.length;
1598
+ return value;
1599
+ }
1600
+ }
1601
+ JSON_NUMBER.lastIndex = pos;
1602
+ const match = JSON_NUMBER.exec(text);
1603
+ if (!match) return fail("Unexpected token");
1604
+ pos = JSON_NUMBER.lastIndex;
1605
+ const literal = match[0];
1606
+ const digits = literal.length - (literal.startsWith("-") ? 1 : 0);
1607
+ if (match[1] === void 0 && match[2] === void 0 && digits >= MIN_EXACT_DIGITS && digits <= MAX_EXACT_DIGITS && !doubleShaped(literal)) {
1608
+ wide = true;
1609
+ return BigInt(literal);
1610
+ }
1611
+ return Number(literal);
1612
+ };
1613
+ const stack = [];
1614
+ space();
1615
+ for (; ; ) {
1616
+ let value;
1617
+ if (text[pos] === "[") {
1618
+ pos += 1;
1619
+ space();
1620
+ if (text[pos] !== "]") {
1621
+ stack.push({ kind: "array", items: [] });
1622
+ continue;
1623
+ }
1624
+ pos += 1;
1625
+ value = [];
1626
+ } else if (text[pos] === "{") {
1627
+ pos += 1;
1628
+ space();
1629
+ if (text[pos] !== "}") {
1630
+ stack.push({ kind: "object", object: {}, key: key() });
1631
+ continue;
1632
+ }
1633
+ pos += 1;
1634
+ value = {};
1635
+ } else {
1636
+ value = scalar();
1637
+ }
1638
+ for (; ; ) {
1639
+ space();
1640
+ const top = stack[stack.length - 1];
1641
+ if (top === void 0) {
1642
+ if (pos !== text.length) fail("Unexpected non-whitespace character after JSON");
1643
+ return wide ? value : JSON.parse(text);
1644
+ }
1645
+ const close = top.kind === "array" ? "]" : "}";
1646
+ if (top.kind === "array") {
1647
+ top.items.push(value);
1648
+ } else {
1649
+ Object.defineProperty(top.object, top.key, dataDescriptor(value));
1650
+ }
1651
+ if (text[pos] === ",") {
1652
+ pos += 1;
1653
+ space();
1654
+ if (top.kind === "object") top.key = key();
1655
+ break;
1656
+ }
1657
+ if (text[pos] !== close) fail(`Expected ',' or '${close}'`);
1658
+ pos += 1;
1659
+ stack.pop();
1660
+ value = top.kind === "array" ? top.items : top.object;
1661
+ }
1662
+ }
1663
+ }
1548
1664
  function parseTypedDataJson(text, where = "typed data", code = "INVALID_CONFIG") {
1549
- const doc = JSON.parse(text);
1665
+ const doc = parseJsonExact(text);
1550
1666
  assertTypedDataIntegersExact(doc, where, code);
1551
1667
  return doc;
1552
1668
  }
@@ -1589,11 +1705,18 @@ var EnvKeyAdapter = class {
1589
1705
  /**
1590
1706
  * Sign EIP-712 typed structured data.
1591
1707
  *
1708
+ * An integer of 22 to 78 digits written as a JSON number (a 32-byte salt)
1709
+ * is read with every digit, as Python's `json` reads it, not rounded the
1710
+ * way `JSON.parse` rounds it.
1711
+ *
1592
1712
  * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1593
1713
  * @returns Object with signature and v/r/s components
1594
1714
  * @throws {X402Error} `INVALID_CONFIG` when an integer field of `message`
1595
- * (or `domain.chainId`) is a JSON number above 2**53 - 1: `JSON.parse`
1596
- * rounded it, so it is refused instead of signed. Write uints as strings.
1715
+ * (or `domain.chainId`) is a JSON number above 2**53 - 1 of up to 21
1716
+ * digits (a JavaScript hop may have rounded it and written it back in
1717
+ * digits) or written with an exponent or a fraction (`7.76e+76`): it is
1718
+ * refused instead of signed. Write uints as strings. An integer out of
1719
+ * its field's range is refused by ethers (`value out-of-bounds`).
1597
1720
  */
1598
1721
  async signTypedData(typedData) {
1599
1722
  const parsed = parseTypedDataJson(typedData);
@@ -1856,7 +1979,14 @@ function prepareTypedData(input) {
1856
1979
  EIP712Domain: EIP712_DOMAIN_FIELDS.filter(([name]) => domain[name] != null).map(
1857
1980
  ([name, type]) => ({ name, type })
1858
1981
  ),
1859
- ...structs
1982
+ // Each field as ethers hashes it, name and type: any other key (a wide
1983
+ // number in it is a bigint) never reaches OWS or JSON.stringify.
1984
+ ...Object.fromEntries(
1985
+ Object.entries(structs).map(([struct, fields]) => [
1986
+ struct,
1987
+ fields.map(({ name, type }) => ({ name, type }))
1988
+ ])
1989
+ )
1860
1990
  };
1861
1991
  const owsDomain = owsValue("EIP712Domain", domain, all, "domain");
1862
1992
  const owsMessage = owsValue(root, message, all, "message");
@@ -1988,7 +2118,7 @@ var OWSWalletAdapter = class {
1988
2118
  async signTypedData(typedData) {
1989
2119
  let parsed;
1990
2120
  try {
1991
- parsed = JSON.parse(typedData);
2121
+ parsed = parseJsonExact(typedData);
1992
2122
  } catch {
1993
2123
  throw refuse("typedData is not a JSON string");
1994
2124
  }