@naculus/connect-appkit-core 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -44,6 +44,7 @@ __export(src_exports, {
44
44
  createNotification: () => createNotification,
45
45
  createNotificationSettingsStorage: () => createNotificationSettingsStorage,
46
46
  createSolanaSessionKeyFlow: () => createSolanaSessionKeyFlow,
47
+ createX402WalletSigner: () => createX402WalletSigner,
47
48
  decodeRevertReason: () => decodeRevertReason,
48
49
  defaultSiwxDomain: () => defaultSiwxDomain,
49
50
  defaultSiwxUri: () => defaultSiwxUri,
@@ -56,6 +57,7 @@ __export(src_exports, {
56
57
  encodeErc20TransferFrom: () => encodeErc20TransferFrom,
57
58
  explainSpend: () => explainSpend,
58
59
  fetchUserOperationReceipt: () => fetchUserOperationReceipt,
60
+ findX402Provider: () => findX402Provider,
59
61
  formatAuthorizationAmount: () => formatAuthorizationAmount,
60
62
  getResolver: () => getResolver,
61
63
  getSharedSessionKeyManager: () => getSharedSessionKeyManager,
@@ -86,6 +88,7 @@ __export(src_exports, {
86
88
  resolveRevertReason: () => resolveRevertReason,
87
89
  resolveSimulationEndpoint: () => resolveSimulationEndpoint,
88
90
  resolveUserOpChain: () => resolveUserOpChain,
91
+ resolveX402Wallet: () => resolveX402Wallet,
89
92
  sameExecutionIntent: () => sameExecutionIntent,
90
93
  saveNotificationSettings: () => saveNotificationSettings,
91
94
  selectChainCapabilities: () => selectChainCapabilities,
@@ -1381,35 +1384,51 @@ function createDelegationPolicyFlow(deps) {
1381
1384
 
1382
1385
  // src/authorization.ts
1383
1386
  var import_connect_core17 = require("@naculus/connect-core");
1384
- function assetAddress(asset) {
1385
- return asset.split(/[:/]/).at(-1);
1386
- }
1387
1387
  function groupDigits(value) {
1388
- return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
1388
+ let out = "";
1389
+ for (let i = 0; i < value.length; i++)
1390
+ out += (i && (value.length - i) % 3 === 0 ? "," : "") + value[i];
1391
+ return out;
1392
+ }
1393
+ function trustedMetadata(asset, options) {
1394
+ const trust = !options.trustedAssets ? "unchecked" : options.trustedAssets.some(
1395
+ (t) => canonicalAssetId(t) === canonicalAssetId(asset)
1396
+ ) ? "trusted" : "unverified";
1397
+ return [trust, trust === "unverified" ? void 0 : options.assets?.[asset]];
1398
+ }
1399
+ function canonicalAssetId(asset) {
1400
+ return asset.replace(
1401
+ /^(eip155:[^/]+\/erc20:)(.+)$/,
1402
+ (_, prefix, reference) => prefix + reference.toLowerCase()
1403
+ );
1389
1404
  }
1390
1405
  function formatAuthorizationAmount(amount, metadata) {
1391
1406
  const baseUnits = amount.toString();
1392
- if (!metadata || !metadata.symbol || !Number.isSafeInteger(metadata.decimals) || metadata.decimals < 0) {
1407
+ if (!metadata?.symbol || !Number.isSafeInteger(metadata.decimals) || metadata.decimals < 0 || metadata.decimals > 255) {
1393
1408
  return {
1394
1409
  formatted: `${groupDigits(baseUnits)} base units`
1395
1410
  };
1396
1411
  }
1397
1412
  const decimals = metadata.decimals;
1398
1413
  const padded = baseUnits.padStart(decimals + 1, "0");
1399
- const integer = decimals === 0 ? padded : padded.slice(0, -decimals);
1400
- const fraction = decimals === 0 ? "" : padded.slice(-decimals).replace(/0+$/, "");
1401
- const value = `${groupDigits(integer)}${fraction ? `.${fraction}` : ""}`;
1402
- return { formatted: `${value} ${metadata.symbol}` };
1414
+ const integer = padded.slice(0, decimals ? -decimals : void 0);
1415
+ let fraction = decimals ? padded.slice(-decimals) : "";
1416
+ while (fraction.endsWith("0")) fraction = fraction.slice(0, -1);
1417
+ return {
1418
+ formatted: `${groupDigits(integer)}${fraction ? `.${fraction}` : ""} ${metadata.symbol}`
1419
+ };
1403
1420
  }
1404
1421
  function relativeExpiry(expiresAt, now, locale) {
1405
1422
  const seconds = expiresAt - now;
1406
1423
  const absolute = Math.abs(seconds);
1407
- const divisor = absolute >= 31536e3 ? 31536e3 : absolute >= 2592e3 ? 2592e3 : absolute >= 86400 ? 86400 : absolute >= 3600 ? 3600 : absolute >= 60 ? 60 : 1;
1408
- const unit = divisor === 31536e3 ? "year" : divisor === 2592e3 ? "month" : divisor === 86400 ? "day" : divisor === 3600 ? "hour" : divisor === 60 ? "minute" : "second";
1409
- return new Intl.RelativeTimeFormat(locale).format(
1410
- Math.round(seconds / divisor),
1411
- unit
1412
- );
1424
+ const [divisor, unit] = absolute >= 31536e3 ? [31536e3, "year"] : absolute >= 2592e3 ? [2592e3, "month"] : absolute >= 86400 ? [86400, "day"] : absolute >= 3600 ? [3600, "hour"] : absolute >= 60 ? [60, "minute"] : [1, "second"];
1425
+ const value = Math.round(seconds / divisor);
1426
+ if (typeof Intl.RelativeTimeFormat !== "function") {
1427
+ const count = Math.abs(value);
1428
+ const phrase = `${count} ${unit}${count === 1 ? "" : "s"}`;
1429
+ return value < 0 || Object.is(value, -0) ? `${phrase} ago` : `in ${phrase}`;
1430
+ }
1431
+ return new Intl.RelativeTimeFormat(locale).format(value, unit);
1413
1432
  }
1414
1433
  function describeAuthorization(input, options = {}) {
1415
1434
  const now = options.now ?? Math.floor(Date.now() / 1e3);
@@ -1420,10 +1439,11 @@ function describeAuthorization(input, options = {}) {
1420
1439
  label,
1421
1440
  principal: input.principal ?? null,
1422
1441
  grants: input.grants.map((grant) => {
1423
- const metadata = options.assets?.[grant.asset];
1442
+ const [assetTrust, metadata] = trustedMetadata(grant.asset, options);
1424
1443
  return {
1425
1444
  asset: grant.asset,
1426
- assetLabel: metadata?.symbol || assetAddress(grant.asset),
1445
+ assetTrust,
1446
+ assetLabel: metadata?.symbol || grant.asset.split(":").at(-1),
1427
1447
  recipients: grant.recipients.map((full) => ({
1428
1448
  shortened: full.length <= 14 ? full : `${full.slice(0, 6)}\u2026${full.slice(-6)}`,
1429
1449
  full
@@ -1476,7 +1496,7 @@ function explainSpend(authorization, request, options = {}) {
1476
1496
  const remainingTotal = authorization.grants[verdict.grant].maxTotal - request.spentSoFar - request.amount;
1477
1497
  const remainingTotalFormatted = formatAuthorizationAmount(
1478
1498
  remainingTotal,
1479
- options.assets?.[authorization.grants[verdict.grant].asset]
1499
+ trustedMetadata(authorization.grants[verdict.grant].asset, options)[1]
1480
1500
  );
1481
1501
  return {
1482
1502
  allowed: true,
@@ -1558,6 +1578,57 @@ function describePayment(input, result) {
1558
1578
  };
1559
1579
  }
1560
1580
 
1581
+ // src/x402-wallet.ts
1582
+ var import_connect_core18 = require("@naculus/connect-core");
1583
+ var import_payments_x402 = require("@naculus/payments-x402");
1584
+ function findX402Provider(session, sources) {
1585
+ if (!session) return null;
1586
+ if (session.walletType === "eip6963" || session.id.startsWith("eip6963-")) {
1587
+ return sources.injected?.find((wallet) => wallet.id === session.walletId)?.provider ?? null;
1588
+ }
1589
+ if (session.walletType === "walletconnect") {
1590
+ return sources.walletConnect ?? null;
1591
+ }
1592
+ return null;
1593
+ }
1594
+ function unavailable(reason) {
1595
+ return { provider: null, address: null, reason };
1596
+ }
1597
+ function resolveX402Wallet(session, provider) {
1598
+ if (!session) return unavailable("no-session");
1599
+ const namespace = session.namespaces.eip155;
1600
+ if (!namespace) return unavailable("not-evm");
1601
+ const isInjected = session.walletType === "eip6963" || session.id.startsWith("eip6963-");
1602
+ const isWalletConnect = session.walletType === "walletconnect";
1603
+ if (!isInjected && !isWalletConnect || !provider?.request) {
1604
+ return unavailable("unsupported-wallet-type");
1605
+ }
1606
+ for (const account of namespace.accounts) {
1607
+ const parsed = (0, import_connect_core18.parseCaip10)(account);
1608
+ const address = parsed?.namespace === "eip155" ? parsed.address : account;
1609
+ if ((0, import_connect_core18.isEvmAddress)(address)) {
1610
+ return {
1611
+ provider,
1612
+ address,
1613
+ reason: null
1614
+ };
1615
+ }
1616
+ }
1617
+ return unavailable("not-evm");
1618
+ }
1619
+ function createX402WalletSigner(session, provider, switchChain) {
1620
+ const resolved = resolveX402Wallet(session, provider);
1621
+ if (resolved.reason) return { signer: null, reason: resolved.reason };
1622
+ return {
1623
+ signer: (0, import_payments_x402.walletX402Signer)({
1624
+ provider: resolved.provider,
1625
+ address: resolved.address,
1626
+ switchChain
1627
+ }),
1628
+ reason: null
1629
+ };
1630
+ }
1631
+
1561
1632
  // src/mpp-session.ts
1562
1633
  function record2(value) {
1563
1634
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
@@ -1589,11 +1660,11 @@ function readMppRecoveryChannelId(error) {
1589
1660
  }
1590
1661
 
1591
1662
  // src/solana-session-key.ts
1592
- var import_connect_core18 = require("@naculus/connect-core");
1663
+ var import_connect_core19 = require("@naculus/connect-core");
1593
1664
  function send(rpc) {
1594
1665
  const sendTransaction = rpc.sendTransaction;
1595
1666
  if (!sendTransaction) {
1596
- throw new import_connect_core18.WalletError(
1667
+ throw new import_connect_core19.WalletError(
1597
1668
  "invalid_input",
1598
1669
  "The Solana RPC must implement sendTransaction to approve or revoke."
1599
1670
  );
@@ -1631,7 +1702,7 @@ function createSolanaSessionKeyFlow(deps) {
1631
1702
  } catch (cause) {
1632
1703
  await manager.revoke(draft.id).catch(() => {
1633
1704
  });
1634
- throw new import_connect_core18.WalletError(
1705
+ throw new import_connect_core19.WalletError(
1635
1706
  "rpc_error",
1636
1707
  `The approval could not be confirmed as sent (${cause instanceof Error ? cause.message : String(cause)}); the key is revoked here. Call revoke("${draft.id}") to send the on-chain Revoke in case it landed.`
1637
1708
  );
@@ -1642,7 +1713,7 @@ function createSolanaSessionKeyFlow(deps) {
1642
1713
  async revoke(id) {
1643
1714
  const key = (await list()).find((k) => k.id === id);
1644
1715
  if (!key) {
1645
- throw new import_connect_core18.WalletError(
1716
+ throw new import_connect_core19.WalletError(
1646
1717
  "invalid_input",
1647
1718
  `No Solana session key ${id} for this owner.`
1648
1719
  );
@@ -1654,7 +1725,7 @@ function createSolanaSessionKeyFlow(deps) {
1654
1725
  rpc
1655
1726
  );
1656
1727
  const signed = await owner.signTransaction(transaction);
1657
- const revocation = (0, import_connect_core18.verifySignedOwnerTransaction)(signed, {
1728
+ const revocation = (0, import_connect_core19.verifySignedOwnerTransaction)(signed, {
1658
1729
  kind: "revoke",
1659
1730
  revocation: {
1660
1731
  owner: key.owner,
@@ -1669,7 +1740,7 @@ function createSolanaSessionKeyFlow(deps) {
1669
1740
  }
1670
1741
 
1671
1742
  // src/web3-store.ts
1672
- var import_connect_core19 = require("@naculus/connect-core");
1743
+ var import_connect_core20 = require("@naculus/connect-core");
1673
1744
  function withTimeout(promise, ms, label = "Operation") {
1674
1745
  let timeoutId;
1675
1746
  const timeout = new Promise((_, reject) => {
@@ -1691,7 +1762,7 @@ async function withRetry(fn, options) {
1691
1762
  lastError = error;
1692
1763
  if (attempt < options.maxRetries) {
1693
1764
  const delay = options.baseDelay * 2 ** attempt;
1694
- import_connect_core19.logger.warn(
1765
+ import_connect_core20.logger.warn(
1695
1766
  "appkit/core",
1696
1767
  `Retry attempt ${attempt + 1}/${options.maxRetries} after ${delay}ms`
1697
1768
  );
@@ -1829,6 +1900,7 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
1829
1900
  createNotification,
1830
1901
  createNotificationSettingsStorage,
1831
1902
  createSolanaSessionKeyFlow,
1903
+ createX402WalletSigner,
1832
1904
  decodeRevertReason,
1833
1905
  defaultSiwxDomain,
1834
1906
  defaultSiwxUri,
@@ -1841,6 +1913,7 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
1841
1913
  encodeErc20TransferFrom,
1842
1914
  explainSpend,
1843
1915
  fetchUserOperationReceipt,
1916
+ findX402Provider,
1844
1917
  formatAuthorizationAmount,
1845
1918
  getResolver,
1846
1919
  getSharedSessionKeyManager,
@@ -1871,6 +1944,7 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
1871
1944
  resolveRevertReason,
1872
1945
  resolveSimulationEndpoint,
1873
1946
  resolveUserOpChain,
1947
+ resolveX402Wallet,
1874
1948
  sameExecutionIntent,
1875
1949
  saveNotificationSettings,
1876
1950
  selectChainCapabilities,