uvd-x402-sdk 2.100.0 → 2.102.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 (59) hide show
  1. package/README.md +95 -6
  2. package/dist/adapters/index.d.mts +3 -2
  3. package/dist/adapters/index.d.ts +3 -2
  4. package/dist/adapters/index.js +522 -126
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/index.mjs +522 -126
  7. package/dist/adapters/index.mjs.map +1 -1
  8. package/dist/backend/index.d.mts +2 -2
  9. package/dist/backend/index.d.ts +2 -2
  10. package/dist/backend/index.js +490 -64
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/index.mjs +484 -65
  13. package/dist/backend/index.mjs.map +1 -1
  14. package/dist/erc8128/index.d.mts +1 -1
  15. package/dist/erc8128/index.d.ts +1 -1
  16. package/dist/{index-prkNXpLX.d.mts → index-DKUUBqZO.d.mts} +220 -16
  17. package/dist/{index-CYMXb5eC.d.ts → index-Di8sCYHx.d.ts} +220 -16
  18. package/dist/index.d.mts +11 -5
  19. package/dist/index.d.ts +11 -5
  20. package/dist/index.js +738 -165
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +735 -166
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/ows-BB6Sgj0W.d.ts +342 -0
  25. package/dist/ows-DazT4KQH.d.mts +342 -0
  26. package/dist/providers/evm/index.js +31 -1
  27. package/dist/providers/evm/index.js.map +1 -1
  28. package/dist/providers/evm/index.mjs +31 -1
  29. package/dist/providers/evm/index.mjs.map +1 -1
  30. package/dist/providers/stellar/index.js +28 -0
  31. package/dist/providers/stellar/index.js.map +1 -1
  32. package/dist/providers/stellar/index.mjs +28 -0
  33. package/dist/providers/stellar/index.mjs.map +1 -1
  34. package/dist/providers/sui/index.js +27 -0
  35. package/dist/providers/sui/index.js.map +1 -1
  36. package/dist/providers/sui/index.mjs +27 -0
  37. package/dist/providers/sui/index.mjs.map +1 -1
  38. package/dist/react/index.js +49 -4
  39. package/dist/react/index.js.map +1 -1
  40. package/dist/react/index.mjs +49 -4
  41. package/dist/react/index.mjs.map +1 -1
  42. package/dist/{wallet-w7BnImDG.d.mts → wallet-T0NFUUkT.d.mts} +3 -2
  43. package/dist/{wallet-w7BnImDG.d.ts → wallet-T0NFUUkT.d.ts} +3 -2
  44. package/package.json +3 -2
  45. package/src/adapters/env-key.ts +5 -1
  46. package/src/adapters/index.ts +8 -1
  47. package/src/adapters/ows.ts +664 -154
  48. package/src/backend/index.ts +635 -22
  49. package/src/client/X402Client.ts +23 -3
  50. package/src/escrow-preauth.ts +32 -4
  51. package/src/index.ts +21 -1
  52. package/src/lifecycle-auth.ts +37 -31
  53. package/src/providers/evm/index.ts +9 -1
  54. package/src/providers/stellar/index.ts +5 -0
  55. package/src/providers/sui/index.ts +4 -0
  56. package/src/utils/uint.ts +252 -0
  57. package/src/wallet.ts +3 -2
  58. package/dist/ows-C-KmORG9.d.ts +0 -221
  59. package/dist/ows-Z9v4GxOZ.d.mts +0 -221
@@ -1205,9 +1205,9 @@ function encodeBase64Json(value) {
1205
1205
  // src/utils/x402.ts
1206
1206
  function chainToCAIP2(chainName) {
1207
1207
  const chain = getChainByName(chainName);
1208
- const caip2 = CAIP2_IDENTIFIERS[(chain?.name ?? chainName).toLowerCase()];
1209
- if (caip2) {
1210
- return caip2;
1208
+ const caip22 = CAIP2_IDENTIFIERS[(chain?.name ?? chainName).toLowerCase()];
1209
+ if (caip22) {
1210
+ return caip22;
1211
1211
  }
1212
1212
  if (chain) {
1213
1213
  if (chain.networkType === "evm") {
@@ -1443,6 +1443,110 @@ function useX402Wagmi(walletClient) {
1443
1443
  createPaymentFull
1444
1444
  };
1445
1445
  }
1446
+ var INTEGER_TYPE = /^u?int\d*$/;
1447
+ function isRecord(value) {
1448
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1449
+ }
1450
+ function inexactNumberMessage(field, value) {
1451
+ 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)`;
1452
+ }
1453
+ function numberRefusal(path, value, code) {
1454
+ return new X402Error(
1455
+ Number.isInteger(value) ? inexactNumberMessage(path, value) : `${path} must be an integer, got the JSON number ${String(value)}`,
1456
+ code
1457
+ );
1458
+ }
1459
+ function findInexactNumber(value, path, seen = /* @__PURE__ */ new Set()) {
1460
+ if (typeof value === "number") {
1461
+ return Number.isSafeInteger(value) ? null : { path, value };
1462
+ }
1463
+ if (typeof value !== "object" || value === null || seen.has(value)) return null;
1464
+ seen.add(value);
1465
+ const entries = Array.isArray(value) ? value.map((item, i) => [`${path}[${i}]`, item]) : Object.entries(value).map(([key, item]) => [`${path}.${key}`, item]);
1466
+ for (const [itemPath, item] of entries) {
1467
+ const hit = findInexactNumber(item, itemPath, seen);
1468
+ if (hit) return hit;
1469
+ }
1470
+ return null;
1471
+ }
1472
+ function assertNoInexactNumber(value, path, code = "INVALID_CONFIG") {
1473
+ const hit = findInexactNumber(value, path);
1474
+ if (hit) throw numberRefusal(hit.path, hit.value, code);
1475
+ }
1476
+ function rootStruct(structs, primaryType) {
1477
+ try {
1478
+ return ethers.ethers.TypedDataEncoder.from(
1479
+ structs
1480
+ ).primaryType;
1481
+ } catch {
1482
+ return typeof primaryType === "string" && Array.isArray(structs[primaryType]) ? primaryType : null;
1483
+ }
1484
+ }
1485
+ function checkTyped(type, value, path, structs, code, ancestors = /* @__PURE__ */ new Set()) {
1486
+ if (typeof value === "object" && value !== null) {
1487
+ if (ancestors.has(value)) return;
1488
+ ancestors.add(value);
1489
+ }
1490
+ try {
1491
+ const array = /^(.*)\[(\d*)\]$/.exec(type);
1492
+ if (array) {
1493
+ if (Array.isArray(value)) {
1494
+ value.forEach(
1495
+ (item, i) => checkTyped(array[1], item, `${path}[${i}]`, structs, code, ancestors)
1496
+ );
1497
+ }
1498
+ return;
1499
+ }
1500
+ const struct = structs[type];
1501
+ if (Array.isArray(struct)) {
1502
+ if (!isRecord(value)) return;
1503
+ for (const field of struct) {
1504
+ if (typeof field?.name === "string" && typeof field.type === "string") {
1505
+ checkTyped(
1506
+ field.type,
1507
+ value[field.name],
1508
+ `${path}.${field.name}`,
1509
+ structs,
1510
+ code,
1511
+ ancestors
1512
+ );
1513
+ }
1514
+ }
1515
+ return;
1516
+ }
1517
+ if (INTEGER_TYPE.test(type) && typeof value === "number" && !Number.isSafeInteger(value)) {
1518
+ throw numberRefusal(path, value, code);
1519
+ }
1520
+ } finally {
1521
+ if (typeof value === "object" && value !== null) ancestors.delete(value);
1522
+ }
1523
+ }
1524
+ function assertTypedDataIntegersExact(doc, where = "typed data", code = "INVALID_CONFIG") {
1525
+ if (!isRecord(doc)) return;
1526
+ const { domain, types, message, primaryType } = doc;
1527
+ if (isRecord(domain) && typeof domain.chainId === "number" && !Number.isSafeInteger(domain.chainId)) {
1528
+ throw numberRefusal(`${where} domain.chainId`, domain.chainId, code);
1529
+ }
1530
+ if (!isRecord(message)) return;
1531
+ const structs = isRecord(types) ? { ...types } : {};
1532
+ delete structs["EIP712Domain"];
1533
+ const root = rootStruct(structs, primaryType);
1534
+ if (root) {
1535
+ checkTyped(root, message, `${where} message`, structs, code);
1536
+ if (typeof primaryType === "string" && primaryType !== root && Array.isArray(structs[primaryType])) {
1537
+ checkTyped(primaryType, message, `${where} message`, structs, code);
1538
+ }
1539
+ return;
1540
+ }
1541
+ assertNoInexactNumber(message, `${where} message`, code);
1542
+ }
1543
+ function parseTypedDataJson(text, where = "typed data", code = "INVALID_CONFIG") {
1544
+ const doc = JSON.parse(text);
1545
+ assertTypedDataIntegersExact(doc, where, code);
1546
+ return doc;
1547
+ }
1548
+
1549
+ // src/adapters/env-key.ts
1446
1550
  var EnvKeyAdapter = class {
1447
1551
  wallet;
1448
1552
  /**
@@ -1482,9 +1586,12 @@ var EnvKeyAdapter = class {
1482
1586
  *
1483
1587
  * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1484
1588
  * @returns Object with signature and v/r/s components
1589
+ * @throws {X402Error} `INVALID_CONFIG` when an integer field of `message`
1590
+ * (or `domain.chainId`) is a JSON number above 2**53 - 1: `JSON.parse`
1591
+ * rounded it, so it is refused instead of signed. Write uints as strings.
1485
1592
  */
1486
1593
  async signTypedData(typedData) {
1487
- const parsed = JSON.parse(typedData);
1594
+ const parsed = parseTypedDataJson(typedData);
1488
1595
  const { domain, types, message } = parsed;
1489
1596
  const cleanTypes = { ...types };
1490
1597
  delete cleanTypes["EIP712Domain"];
@@ -1585,121 +1692,342 @@ var EnvKeyAdapter = class {
1585
1692
  };
1586
1693
  }
1587
1694
  };
1695
+ var OWS_FUNCTIONS = ["getWallet", "signMessage", "signTypedData", "signTransaction"];
1696
+ var MIGRATION = "pass the module and the wallet: new OWSWalletAdapter(ows, { wallet: '<name or id>', passphrase }) with import * as ows from '@open-wallet-standard/core'";
1697
+ var EIP712_DOMAIN_FIELDS = [
1698
+ ["name", "string"],
1699
+ ["version", "string"],
1700
+ ["chainId", "uint256"],
1701
+ ["verifyingContract", "address"],
1702
+ ["salt", "bytes32"]
1703
+ ];
1704
+ var TRANSACTION_FIELDS = [
1705
+ "type",
1706
+ "to",
1707
+ "nonce",
1708
+ "gasLimit",
1709
+ "gasPrice",
1710
+ "maxPriorityFeePerGas",
1711
+ "maxFeePerGas",
1712
+ "maxFeePerBlobGas",
1713
+ "data",
1714
+ "value",
1715
+ "chainId",
1716
+ "signature",
1717
+ "accessList",
1718
+ "authorizationList",
1719
+ "blobVersionedHashes",
1720
+ "kzg",
1721
+ "blobWrapperVersion",
1722
+ "blobs"
1723
+ ];
1724
+ var ETHERS_NAME_OF = { input: "data", gas: "gasLimit" };
1725
+ var TWO_127 = 1n << 127n;
1726
+ var TWO_128 = 1n << 128n;
1727
+ var TWO_256 = 1n << 256n;
1728
+ function isRecord2(value) {
1729
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1730
+ }
1731
+ function shown(value) {
1732
+ if (typeof value === "bigint") return `${value}n`;
1733
+ try {
1734
+ return JSON.stringify(value) ?? String(value);
1735
+ } catch {
1736
+ return String(value);
1737
+ }
1738
+ }
1739
+ function refuse(message) {
1740
+ return new X402Error(`${message}; nothing was signed`, "INVALID_CONFIG");
1741
+ }
1742
+ function toInteger(value, where) {
1743
+ if (typeof value === "bigint") return value;
1744
+ if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
1745
+ if (typeof value === "number" && Number.isInteger(value)) throw refuse(inexactNumberMessage(where, value));
1746
+ if (typeof value === "string" && /^-?(0[xX][0-9a-fA-F]+|[0-9]+)$/.test(value)) {
1747
+ return value.startsWith("-") ? -BigInt(value.slice(1)) : BigInt(value);
1748
+ }
1749
+ throw refuse(
1750
+ `${where}: ${shown(value)} is not an integer (a safe-integer number, a decimal string or 0x-hex)`
1751
+ );
1752
+ }
1753
+ function integerWire(n) {
1754
+ if (n >= -TWO_127 && n < TWO_128) return n.toString();
1755
+ if (n > 0n) {
1756
+ const digits = n.toString(16);
1757
+ return `0x${digits.length % 2 ? "0" : ""}${digits}`;
1758
+ }
1759
+ return `0x${(n + TWO_256).toString(16).padStart(64, "0")}`;
1760
+ }
1761
+ function owsValue(kind, value, types, where) {
1762
+ const array = /^(.*)\[(\d*)\]$/.exec(kind);
1763
+ if (array) {
1764
+ const [, inner, size] = array;
1765
+ if (!Array.isArray(value)) throw refuse(`${where}: ${kind} needs an array, got ${shown(value)}`);
1766
+ if (size !== "" && value.length !== Number(size)) {
1767
+ throw refuse(`${where}: ${kind} needs ${size} items, got ${value.length}`);
1768
+ }
1769
+ return value.map((item, i) => owsValue(inner, item, types, `${where}[${i}]`));
1770
+ }
1771
+ const struct = types[kind];
1772
+ if (struct) {
1773
+ if (!isRecord2(value)) throw refuse(`${where}: ${kind} needs an object, got ${shown(value)}`);
1774
+ const out = {};
1775
+ for (const field of struct) {
1776
+ if (value[field.name] === void 0) throw refuse(`${where}: ${kind} lacks ${field.name}`);
1777
+ out[field.name] = owsValue(field.type, value[field.name], types, `${where}.${field.name}`);
1778
+ }
1779
+ return out;
1780
+ }
1781
+ const integer = /^(u?)int(\d+)$/.exec(kind);
1782
+ if (integer) {
1783
+ const bits = BigInt(integer[2]);
1784
+ const n = toInteger(value, where);
1785
+ const [low, high] = integer[1] ? [0n, 1n << bits] : [-(1n << bits - 1n), 1n << bits - 1n];
1786
+ if (n < low || n >= high) {
1787
+ throw new X402Error(
1788
+ `${where}: ${shown(value)} is out of range for ${kind}; nothing was signed`,
1789
+ "INVALID_AMOUNT"
1790
+ );
1791
+ }
1792
+ return integerWire(n);
1793
+ }
1794
+ if (kind === "bool") {
1795
+ if (typeof value !== "boolean") throw refuse(`${where}: bool needs true or false, got ${shown(value)}`);
1796
+ return value;
1797
+ }
1798
+ if (kind === "string") {
1799
+ if (typeof value !== "string") throw refuse(`${where}: string needs a string, got ${shown(value)}`);
1800
+ return value;
1801
+ }
1802
+ if (kind === "address") {
1803
+ try {
1804
+ if (typeof value !== "string") throw new Error();
1805
+ return ethers.ethers.getAddress(value);
1806
+ } catch {
1807
+ throw refuse(`${where}: ${shown(value)} is not an address`);
1808
+ }
1809
+ }
1810
+ const bytes = /^bytes(\d*)$/.exec(kind);
1811
+ if (bytes) {
1812
+ let raw;
1813
+ try {
1814
+ if (typeof value !== "string") throw new Error();
1815
+ raw = ethers.ethers.getBytes(value);
1816
+ } catch {
1817
+ throw refuse(`${where}: ${kind} needs 0x-hex, got ${shown(value)}`);
1818
+ }
1819
+ if (bytes[1] !== "" && raw.length !== Number(bytes[1])) {
1820
+ throw refuse(`${where}: ${kind} needs ${bytes[1]} bytes, got ${raw.length}`);
1821
+ }
1822
+ return ethers.ethers.hexlify(raw);
1823
+ }
1824
+ throw refuse(`${where}: unsupported EIP-712 type ${kind}`);
1825
+ }
1826
+ function prepareTypedData(input) {
1827
+ if (!isRecord2(input)) throw refuse("typed data must be an object with domain, types and message");
1828
+ const { domain, types, message, primaryType } = input;
1829
+ if (!isRecord2(domain)) throw refuse("typed data needs a domain object");
1830
+ if (!isRecord2(types)) throw refuse("typed data needs a types object");
1831
+ if (!isRecord2(message)) throw refuse("typed data needs a message object");
1832
+ const structs = { ...types };
1833
+ delete structs["EIP712Domain"];
1834
+ let root;
1835
+ try {
1836
+ root = ethers.ethers.TypedDataEncoder.from(structs).primaryType;
1837
+ } catch (error) {
1838
+ throw refuse(`typed data types are invalid (${error instanceof Error ? error.message : error})`);
1839
+ }
1840
+ if (primaryType !== void 0 && primaryType !== root) {
1841
+ throw refuse(
1842
+ `primaryType ${shown(primaryType)} is not ${root}, the root struct ethers signs for these types`
1843
+ );
1844
+ }
1845
+ for (const key of Object.keys(domain)) {
1846
+ if (!EIP712_DOMAIN_FIELDS.some(([name]) => name === key)) {
1847
+ throw refuse(`domain.${key} is not an EIP-712 domain field`);
1848
+ }
1849
+ }
1850
+ const all = {
1851
+ EIP712Domain: EIP712_DOMAIN_FIELDS.filter(([name]) => domain[name] != null).map(
1852
+ ([name, type]) => ({ name, type })
1853
+ ),
1854
+ ...structs
1855
+ };
1856
+ const owsDomain = owsValue("EIP712Domain", domain, all, "domain");
1857
+ const owsMessage = owsValue(root, message, all, "message");
1858
+ const chainId = domain["chainId"] != null ? toInteger(domain["chainId"], "domain.chainId") : 0n;
1859
+ return {
1860
+ document: JSON.stringify({ types: all, primaryType: root, domain: owsDomain, message: owsMessage }),
1861
+ domain,
1862
+ types: structs,
1863
+ message,
1864
+ chain: chainId > 0n ? `eip155:${chainId}` : null
1865
+ };
1866
+ }
1867
+ function evmSignature(result, what) {
1868
+ const hex = String(result?.signature ?? "").replace(/^0x/, "");
1869
+ if (!/^[0-9a-fA-F]{130}$/.test(hex)) {
1870
+ throw new X402Error(`OWS ${what} returned a signature that is not 65 bytes of hex`, "PAYMENT_FAILED");
1871
+ }
1872
+ const bytes = ethers.ethers.getBytes(`0x${hex}`);
1873
+ const v = bytes[64] < 27 ? bytes[64] + 27 : bytes[64];
1874
+ if (v !== 27 && v !== 28) {
1875
+ throw new X402Error(`OWS ${what} returned a recovery byte of ${bytes[64]}`, "PAYMENT_FAILED");
1876
+ }
1877
+ return ethers.ethers.Signature.from({
1878
+ r: ethers.ethers.hexlify(bytes.subarray(0, 32)),
1879
+ s: ethers.ethers.hexlify(bytes.subarray(32, 64)),
1880
+ v
1881
+ });
1882
+ }
1883
+ function caip2(network) {
1884
+ if (typeof network !== "string") {
1885
+ throw new X402Error(
1886
+ `OWSWalletAdapter: options.network must be a string (an SDK network name or eip155:<id>), got ${typeof network}`,
1887
+ "INVALID_CONFIG"
1888
+ );
1889
+ }
1890
+ const caip = /^eip155:([1-9][0-9]*)$/.exec(network);
1891
+ if (caip) return network;
1892
+ const chain = getChainByName(network);
1893
+ if (!chain || chain.networkType !== "evm") {
1894
+ throw new X402Error(
1895
+ `OWSWalletAdapter signs for an EVM network; got ${shown(network)}`,
1896
+ "CHAIN_NOT_SUPPORTED"
1897
+ );
1898
+ }
1899
+ return `eip155:${chain.chainId}`;
1900
+ }
1588
1901
  var OWSWalletAdapter = class {
1589
- owsWallet;
1590
- address;
1591
- /**
1592
- * Create an OWSWalletAdapter.
1593
- *
1594
- * @param owsWallet - An object implementing the OWSWallet interface
1595
- * (typically from @open-wallet-standard/core or a compatible provider)
1596
- * @param accountIndex - Which account to use if the wallet has multiple (default: 0)
1597
- * @throws {X402Error} if the wallet has no accounts
1598
- */
1599
- constructor(owsWallet, accountIndex = 0) {
1600
- if (!owsWallet || !owsWallet.accounts || owsWallet.accounts.length === 0) {
1902
+ #ows;
1903
+ #wallet;
1904
+ #passphrase;
1905
+ #vaultPath;
1906
+ #chain;
1907
+ #address;
1908
+ constructor(ows, options) {
1909
+ if (isRecord2(ows) && "accounts" in ows && (options === void 0 || typeof options === "number")) {
1601
1910
  throw new X402Error(
1602
- "OWS wallet has no accounts. Create or import a wallet first.",
1603
- "WALLET_NOT_CONNECTED"
1911
+ `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.`,
1912
+ "INVALID_CONFIG"
1604
1913
  );
1605
1914
  }
1606
- const account = owsWallet.accounts[accountIndex];
1607
- if (!account) {
1915
+ const missing = OWS_FUNCTIONS.filter(
1916
+ (name) => typeof ows?.[name] !== "function"
1917
+ );
1918
+ if (missing.length > 0) {
1608
1919
  throw new X402Error(
1609
- `OWS wallet account index ${accountIndex} does not exist. Wallet has ${owsWallet.accounts.length} account(s).`,
1610
- "WALLET_NOT_CONNECTED"
1920
+ `OWSWalletAdapter takes the @open-wallet-standard/core module (import * as ows from '@open-wallet-standard/core'); it lacks ${missing.join(", ")}. Since the OWS rewrite the adapter no longer takes a wallet object with accounts.`,
1921
+ "INVALID_CONFIG"
1611
1922
  );
1612
1923
  }
1613
- this.owsWallet = owsWallet;
1614
- this.address = ethers.ethers.getAddress(account.address);
1924
+ const opts = isRecord2(options) ? options : {};
1925
+ if (typeof opts.wallet !== "string" || opts.wallet === "") {
1926
+ throw new X402Error("OWSWalletAdapter needs options.wallet: the name or id of a wallet in the vault", "INVALID_CONFIG");
1927
+ }
1928
+ if (opts.passphrase != null && typeof opts.passphrase !== "string") {
1929
+ throw new X402Error("OWSWalletAdapter: options.passphrase must be a string", "INVALID_CONFIG");
1930
+ }
1931
+ this.#ows = ows;
1932
+ this.#wallet = opts.wallet;
1933
+ this.#passphrase = opts.passphrase ?? (typeof process !== "undefined" ? process.env?.OWS_PASSPHRASE : void 0) ?? null;
1934
+ this.#vaultPath = opts.vaultPath ?? null;
1935
+ this.#chain = caip2(opts.network ?? "base");
1936
+ const info = this.#call("getWallet", () => this.#ows.getWallet(this.#wallet, this.#vaultPath), "WALLET_NOT_FOUND");
1937
+ const account = (info?.accounts ?? []).find((a) => String(a.chainId).startsWith("eip155:"));
1938
+ if (!account) {
1939
+ throw new X402Error(`OWS wallet ${shown(this.#wallet)} has no EVM account`, "WALLET_NOT_FOUND");
1940
+ }
1941
+ this.#address = ethers.ethers.getAddress(account.address);
1615
1942
  }
1616
1943
  /**
1617
- * Get the checksummed EVM wallet address.
1944
+ * Get the checksummed EVM wallet address (read from the vault once, when
1945
+ * the adapter was created; no passphrase needed).
1618
1946
  */
1619
1947
  getAddress() {
1620
- return this.address;
1948
+ return this.#address;
1621
1949
  }
1622
1950
  /**
1623
- * Sign a message using EIP-191 personal_sign.
1951
+ * Sign a message using EIP-191 personal_sign (the string as UTF-8, as
1952
+ * `EnvKeyAdapter` signs it).
1624
1953
  *
1625
1954
  * @param message - The message string to sign
1626
- * @returns Hex-encoded signature
1955
+ * @returns Hex-encoded 65-byte signature, v = 27/28
1627
1956
  */
1628
1957
  async signMessage(message) {
1958
+ if (typeof message !== "string") throw refuse(`signMessage takes a string, got ${typeof message}`);
1959
+ const result = this.#call(
1960
+ "signMessage",
1961
+ () => this.#ows.signMessage(this.#wallet, this.#chain, message, this.#passphrase, "utf8", null, this.#vaultPath)
1962
+ );
1963
+ const signature = evmSignature(result, "signMessage").serialized;
1964
+ let signer = "";
1629
1965
  try {
1630
- const result = await this.owsWallet.signMessage({
1631
- account: { address: this.address },
1632
- message
1633
- });
1634
- return result.signature;
1635
- } catch (error) {
1636
- throw new X402Error(
1637
- `OWS signMessage failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1638
- "PAYMENT_FAILED",
1639
- error
1640
- );
1966
+ signer = ethers.ethers.verifyMessage(message, signature);
1967
+ } catch {
1641
1968
  }
1969
+ this.#checkSigner(signer, "signMessage");
1970
+ return signature;
1642
1971
  }
1643
1972
  /**
1644
1973
  * Sign EIP-712 typed structured data.
1645
1974
  *
1646
- * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1975
+ * @param typedData - JSON string with `domain`, `types`, `message` and
1976
+ * optionally `primaryType` (it must then be the root struct)
1647
1977
  * @returns Object with signature and v/r/s components
1978
+ * @throws {X402Error} `INVALID_AMOUNT` for an integer out of its type's
1979
+ * range and `INVALID_CONFIG` for anything else ethers would not encode,
1980
+ * both before signing; `PAYMENT_FAILED` if OWS fails or signs a digest
1981
+ * other than the one ethers computes (the signature is not returned).
1648
1982
  */
1649
1983
  async signTypedData(typedData) {
1650
- const parsed = JSON.parse(typedData);
1984
+ let parsed;
1651
1985
  try {
1652
- const result = await this.owsWallet.signTypedData({
1653
- account: { address: this.address },
1654
- domain: parsed.domain,
1655
- types: parsed.types,
1656
- primaryType: parsed.primaryType,
1657
- message: parsed.message
1658
- });
1659
- const sig = ethers.ethers.Signature.from(result.signature);
1660
- return {
1661
- signature: result.signature,
1662
- v: sig.v,
1663
- r: sig.r,
1664
- s: sig.s
1665
- };
1666
- } catch (error) {
1667
- throw new X402Error(
1668
- `OWS signTypedData failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1669
- "PAYMENT_FAILED",
1670
- error
1671
- );
1986
+ parsed = JSON.parse(typedData);
1987
+ } catch {
1988
+ throw refuse("typedData is not a JSON string");
1672
1989
  }
1990
+ return this.#signTyped(parsed);
1673
1991
  }
1674
1992
  /**
1675
- * Sign a serialized EVM transaction.
1993
+ * Sign an EVM transaction.
1676
1994
  *
1677
- * Delegates to the OWS wallet's signTransaction method.
1678
- * Used by AdvancedEscrowClient for on-chain operations (release,
1679
- * refund, charge) where the wallet signs instead of a raw private key.
1995
+ * OWS signs keccak256 of the bytes it is given and returns only the
1996
+ * signature, so the transaction is parsed and serialised with ethers here,
1997
+ * the way `EnvKeyAdapter` (ethers.Wallet) does it, EIP-155 `v` included.
1680
1998
  *
1681
- * @param unsignedTx - Hex-encoded unsigned transaction
1682
- * @returns Hex-encoded signed raw transaction
1999
+ * A sender, given as `from` or implied by an existing signature, must be
2000
+ * this wallet: it is taken out, as ethers takes it out; any other sender is
2001
+ * refused before signing.
2002
+ *
2003
+ * @param unsignedTx - Hex-encoded transaction (ethers serialized), an
2004
+ * `ethers.Transaction`, or a transaction object ethers' `Transaction.from`
2005
+ * accepts, plus `from`
2006
+ * @returns Hex-encoded signed raw transaction, ready for broadcast
1683
2007
  */
1684
2008
  async signTransaction(unsignedTx) {
1685
- try {
1686
- const result = await this.owsWallet.signTransaction({
1687
- account: { address: this.address },
1688
- transaction: unsignedTx,
1689
- chainId: "1"
1690
- // chainId is embedded in the serialized TX; this is a hint
1691
- });
1692
- return result.signedTransaction;
1693
- } catch (error) {
1694
- throw new X402Error(
1695
- `OWS signTransaction failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1696
- "PAYMENT_FAILED",
1697
- error
1698
- );
1699
- }
2009
+ const tx = this.#unsignedTransaction(unsignedTx);
2010
+ const chain = tx.chainId > 0n ? `eip155:${tx.chainId}` : this.#chain;
2011
+ const result = this.#call(
2012
+ "signTransaction",
2013
+ () => this.#ows.signTransaction(
2014
+ this.#wallet,
2015
+ chain,
2016
+ tx.unsignedSerialized.slice(2),
2017
+ this.#passphrase,
2018
+ null,
2019
+ this.#vaultPath
2020
+ )
2021
+ );
2022
+ tx.signature = evmSignature(result, "signTransaction");
2023
+ const signed = tx.serialized;
2024
+ this.#checkSigner(ethers.ethers.Transaction.from(signed).from ?? "", "signTransaction");
2025
+ return signed;
1700
2026
  }
1701
2027
  /**
1702
- * Sign an EIP-3009 ReceiveWithAuthorization for USDC.
2028
+ * Sign an EIP-3009 TransferWithAuthorization for USDC: the typed data
2029
+ * `EnvKeyAdapter.signEIP3009` builds, signed through {@link signTypedData}
2030
+ * (OWS has no EIP-3009 call of its own).
1703
2031
  *
1704
2032
  * @param params - EIP-3009 parameters
1705
2033
  * @returns Signed authorization ready for facilitator relay
@@ -1717,65 +2045,133 @@ var OWSWalletAdapter = class {
1717
2045
  }
1718
2046
  const chainId = params.chainId ?? chain.chainId;
1719
2047
  const usdcAddress = params.usdcContract ?? chain.usdc.address;
1720
- const from = this.address;
2048
+ const from = this.#address;
1721
2049
  const to = ethers.ethers.getAddress(params.to);
1722
2050
  const value = ethers.ethers.parseUnits(params.amountUsdc.toString(), chain.usdc.decimals);
1723
2051
  const validAfter = params.validAfter ?? 0;
1724
2052
  const validBefore = params.validBefore ?? Math.floor(Date.now() / 1e3) + 300;
1725
2053
  const nonce = ethers.ethers.hexlify(ethers.ethers.randomBytes(32));
1726
- const domain = {
1727
- name: chain.usdc.name,
1728
- version: chain.usdc.version,
1729
- chainId,
1730
- verifyingContract: usdcAddress
1731
- };
1732
- const types = {
1733
- TransferWithAuthorization: [
1734
- { name: "from", type: "address" },
1735
- { name: "to", type: "address" },
1736
- { name: "value", type: "uint256" },
1737
- { name: "validAfter", type: "uint256" },
1738
- { name: "validBefore", type: "uint256" },
1739
- { name: "nonce", type: "bytes32" }
1740
- ]
1741
- };
1742
- const message = {
2054
+ const signed = await this.#signTyped({
2055
+ domain: {
2056
+ name: chain.usdc.name,
2057
+ version: chain.usdc.version,
2058
+ chainId,
2059
+ verifyingContract: usdcAddress
2060
+ },
2061
+ types: {
2062
+ TransferWithAuthorization: [
2063
+ { name: "from", type: "address" },
2064
+ { name: "to", type: "address" },
2065
+ { name: "value", type: "uint256" },
2066
+ { name: "validAfter", type: "uint256" },
2067
+ { name: "validBefore", type: "uint256" },
2068
+ { name: "nonce", type: "bytes32" }
2069
+ ]
2070
+ },
2071
+ primaryType: "TransferWithAuthorization",
2072
+ message: { from, to, value, validAfter, validBefore, nonce }
2073
+ });
2074
+ return {
1743
2075
  from,
1744
2076
  to,
1745
2077
  value: value.toString(),
1746
2078
  validAfter: validAfter.toString(),
1747
2079
  validBefore: validBefore.toString(),
1748
- nonce
2080
+ nonce,
2081
+ v: signed.v,
2082
+ r: signed.r,
2083
+ s: signed.s,
2084
+ signature: signed.signature
1749
2085
  };
1750
- let signatureResult;
2086
+ }
2087
+ // --------------------------------------------------------------------------
2088
+ async #signTyped(input) {
2089
+ const typed = prepareTypedData(input);
2090
+ const result = this.#call(
2091
+ "signTypedData",
2092
+ () => this.#ows.signTypedData(
2093
+ this.#wallet,
2094
+ typed.chain ?? this.#chain,
2095
+ typed.document,
2096
+ this.#passphrase,
2097
+ null,
2098
+ this.#vaultPath
2099
+ )
2100
+ );
2101
+ const sig = evmSignature(result, "signTypedData");
2102
+ let signer;
1751
2103
  try {
1752
- signatureResult = await this.owsWallet.signTypedData({
1753
- account: { address: this.address },
1754
- domain,
1755
- types,
1756
- primaryType: "TransferWithAuthorization",
1757
- message
1758
- });
2104
+ signer = ethers.ethers.verifyTypedData(typed.domain, typed.types, typed.message, sig);
1759
2105
  } catch (error) {
1760
2106
  throw new X402Error(
1761
- `OWS signEIP3009 failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1762
- "PAYMENT_FAILED",
1763
- error
2107
+ `ethers cannot encode this typed data (${error instanceof Error ? error.message : error}); the OWS signature is not returned`,
2108
+ "INVALID_CONFIG"
1764
2109
  );
1765
2110
  }
1766
- const sig = ethers.ethers.Signature.from(signatureResult.signature);
1767
- return {
1768
- from,
1769
- to,
1770
- value: value.toString(),
1771
- validAfter: validAfter.toString(),
1772
- validBefore: validBefore.toString(),
1773
- nonce,
1774
- v: sig.v,
1775
- r: sig.r,
1776
- s: sig.s,
1777
- signature: signatureResult.signature
1778
- };
2111
+ this.#checkSigner(signer, "signTypedData");
2112
+ return { signature: sig.serialized, v: sig.v, r: sig.r, s: sig.s };
2113
+ }
2114
+ #unsignedTransaction(input) {
2115
+ const senders = [];
2116
+ let tx;
2117
+ try {
2118
+ if (typeof input === "string") {
2119
+ tx = ethers.ethers.Transaction.from(input);
2120
+ } else if (typeof input === "object" && input !== null) {
2121
+ const source = input;
2122
+ for (const key of Object.keys(source)) {
2123
+ if (key === "from" || key === "hash" || TRANSACTION_FIELDS.includes(key)) continue;
2124
+ const ethersName = ETHERS_NAME_OF[key];
2125
+ throw refuse(
2126
+ `transaction key ${shown(key)} is not one ethers reads` + (ethersName ? `; ethers calls it ${ethersName}` : "")
2127
+ );
2128
+ }
2129
+ if (source.from != null) senders.push(source.from);
2130
+ const fields = {};
2131
+ for (const key of TRANSACTION_FIELDS) {
2132
+ if (source[key] != null) fields[key] = source[key];
2133
+ }
2134
+ tx = ethers.ethers.Transaction.from(fields);
2135
+ } else {
2136
+ throw new Error(`got ${typeof input}`);
2137
+ }
2138
+ } catch (error) {
2139
+ if (error instanceof X402Error) throw error;
2140
+ throw refuse(
2141
+ `not a transaction ethers can read (${error instanceof Error ? error.message : error})`
2142
+ );
2143
+ }
2144
+ if (tx.signature) senders.push(tx.from);
2145
+ for (const sender of senders) {
2146
+ let address = "";
2147
+ try {
2148
+ address = ethers.ethers.getAddress(sender);
2149
+ } catch {
2150
+ }
2151
+ if (address !== this.#address) {
2152
+ throw refuse(`transaction from ${shown(sender)} is not this wallet (${this.#address})`);
2153
+ }
2154
+ }
2155
+ tx.signature = null;
2156
+ return tx;
2157
+ }
2158
+ #checkSigner(signer, what) {
2159
+ if (signer !== this.#address) {
2160
+ throw new X402Error(
2161
+ `OWS ${what} returned a signature that does not recover to ${this.#address} over the digest ethers computes (the one EnvKeyAdapter signs); it is not returned`,
2162
+ "PAYMENT_FAILED"
2163
+ );
2164
+ }
2165
+ }
2166
+ /** Run one OWS call; its error becomes an X402Error without the passphrase in it. */
2167
+ #call(what, run, code = "PAYMENT_FAILED") {
2168
+ try {
2169
+ return run();
2170
+ } catch (error) {
2171
+ let message = error instanceof Error ? error.message : String(error);
2172
+ if (this.#passphrase) message = message.split(this.#passphrase).join("[redacted]");
2173
+ throw new X402Error(`OWS ${what} failed: ${message}`, code);
2174
+ }
1779
2175
  }
1780
2176
  };
1781
2177