@swype-org/react-sdk 0.2.364 → 0.2.375

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
@@ -523,6 +523,14 @@ function isVisibleUsdAmountAtTwoDecimals(amount) {
523
523
  function isSelectableDepositSourceAmountUsd(amount, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
524
524
  return Number.isFinite(amount) && amount >= minTransferAmountUsd;
525
525
  }
526
+ function buildNativeUnsupportedEntries(options) {
527
+ return options.map((option) => ({
528
+ tokenSymbol: option.tokenSymbol,
529
+ chainName: option.chainName,
530
+ amount: parseRawBalance(option.rawBalance, option.decimals),
531
+ logoURI: option.logoURI ?? null
532
+ })).filter((entry) => entry.amount > 0);
533
+ }
526
534
  function buildSelectSourceChoices(options, minTransferAmountUsd = DEFAULT_MIN_SEND_AMOUNT_USD) {
527
535
  const chainChoices = [];
528
536
  const chainIndexByName = /* @__PURE__ */ new Map();
@@ -636,6 +644,21 @@ function resolveSelectSourceOption(choices, options, chainName, tokenSymbol, rec
636
644
  ));
637
645
  }
638
646
 
647
+ // src/walletFlow.ts
648
+ var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
649
+ function isMobileUserAgent(userAgent) {
650
+ if (!userAgent) {
651
+ return false;
652
+ }
653
+ return MOBILE_USER_AGENT_PATTERN.test(userAgent);
654
+ }
655
+ function shouldUseWalletConnector(options) {
656
+ return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
657
+ }
658
+ function resolveCoinbasePreferenceOptions(userAgent) {
659
+ return isMobileUserAgent(userAgent) ? "all" : "eoaOnly";
660
+ }
661
+
639
662
  // src/walletBridge/protocol.ts
640
663
  var BRIDGE_PROTOCOL_VERSION = 1;
641
664
  function parseBridgeMessage(data) {
@@ -1001,23 +1024,29 @@ var BLINK_PRIVY_APP_ID = "cmlil87uv004n0ck0blwumwek";
1001
1024
  var BLINK_WC_PROJECT_ID = "f6c60234412f94ef9108dc8f67f4362c";
1002
1025
  var BLINK_FINGERPRINT_API_KEY = "nr4FvTsZ6M5z2141FqrM";
1003
1026
  var BLINK_FINGERPRINT_REGION = "us";
1004
- function FingerprintVisitorPing() {
1005
- react$1.useVisitorData({ immediate: true });
1006
- return null;
1007
- }
1008
1027
  function buildStaticConnectors() {
1028
+ const userAgent = typeof navigator === "undefined" ? null : navigator.userAgent;
1009
1029
  return [
1010
1030
  // `unstable_shimAsyncInject` covers wallets whose content scripts wire
1011
1031
  // up after page load (Phantom, Trust, MetaMask in the iframe).
1012
1032
  connectors.injected({ unstable_shimAsyncInject: 2e3 }),
1013
- // `preference: 'all'` (default) lets the Coinbase Wallet SDK prefer an
1014
- // injected provider when one is present. This matters inside Base's
1015
- // mobile in-app WebView: with `smartWalletOnly`, the SDK routes signing
1016
- // through `keys.coinbase.com`, which in an iOS WKWebView navigates the
1017
- // webview itself (full page reload after each signature). With `all`,
1018
- // the SDK uses the Base WebView's injected `window.ethereum` directly,
1019
- // keeping signing in-process. Desktop/extension users are unaffected.
1020
- connectors.coinbaseWallet({ appName: "Swype", preference: { options: "all" } })
1033
+ // Coinbase Wallet SDK connection preference is platform-specific
1034
+ // (see `resolveCoinbasePreferenceOptions`):
1035
+ // Desktop → `eoaOnly`: in smart-wallet mode the Base extension hands
1036
+ // the dapp a separate, often-empty smart-wallet account instead of the
1037
+ // user's funded EOA "no assets". `eoaOnly` binds the funded EOA.
1038
+ // Mobile (Base in-app WebView) `all`: signing must use the injected
1039
+ // `window.ethereum`; the SDK path reloads the iOS WKWebView after each
1040
+ // signature.
1041
+ //
1042
+ // Exactly ONE Coinbase connector: two `@coinbase/wallet-sdk` instances in
1043
+ // one page collide on shared `window` listeners + the Communicator (Privy
1044
+ // connector-init timeout, listener leaks, duplicated `client-project-name`
1045
+ // that 400s the connect), so we never register a second one.
1046
+ connectors.coinbaseWallet({
1047
+ appName: "Blink",
1048
+ preference: { options: resolveCoinbasePreferenceOptions(userAgent) }
1049
+ })
1021
1050
  ];
1022
1051
  }
1023
1052
  function buildWagmiConfig(bridgedWallets) {
@@ -1103,30 +1132,30 @@ function BlinkProvider({
1103
1132
  );
1104
1133
  return (
1105
1134
  // Outermost wrapper: loads the Fingerprint browser agent once per
1106
- // provider mount, independent of wagmi/privy/query state. Visitor data
1107
- // collection starts here; no hook is exposed yet (see SDK barrel).
1108
- /* @__PURE__ */ jsxRuntime.jsxs(react$1.FingerprintProvider, { apiKey: BLINK_FINGERPRINT_API_KEY, region: BLINK_FINGERPRINT_REGION, children: [
1109
- /* @__PURE__ */ jsxRuntime.jsx(FingerprintVisitorPing, {}),
1110
- /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClientRef.current, children: /* @__PURE__ */ jsxRuntime.jsx(wagmi.WagmiProvider, { config: wagmiConfig, reconnectOnMount: false, children: /* @__PURE__ */ jsxRuntime.jsx(
1111
- reactAuth.PrivyProvider,
1112
- {
1113
- appId: privyAppId ?? BLINK_PRIVY_APP_ID,
1114
- config: {
1115
- appearance: {
1116
- theme: getThemeBase(theme),
1117
- accentColor: getTheme(theme).accent
1118
- },
1119
- intl: {
1120
- defaultCountry: "US"
1121
- },
1122
- embeddedWallets: {
1123
- showWalletUIs: false
1124
- }
1135
+ // provider mount, independent of wagmi/privy/query state. The agent loads
1136
+ // here (including during the hidden preload warm-up), but identification is
1137
+ // deferred to explicit flow events fired from `BlinkPayment` see
1138
+ // `useFingerprintFlowEvents` so a preload that never opens is never
1139
+ // identified.
1140
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.FingerprintProvider, { apiKey: BLINK_FINGERPRINT_API_KEY, region: BLINK_FINGERPRINT_REGION, children: /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClientRef.current, children: /* @__PURE__ */ jsxRuntime.jsx(wagmi.WagmiProvider, { config: wagmiConfig, reconnectOnMount: false, children: /* @__PURE__ */ jsxRuntime.jsx(
1141
+ reactAuth.PrivyProvider,
1142
+ {
1143
+ appId: privyAppId ?? BLINK_PRIVY_APP_ID,
1144
+ config: {
1145
+ appearance: {
1146
+ theme: getThemeBase(theme),
1147
+ accentColor: getTheme(theme).accent
1125
1148
  },
1126
- children: /* @__PURE__ */ jsxRuntime.jsx(BlinkContext.Provider, { value, children })
1127
- }
1128
- ) }) })
1129
- ] })
1149
+ intl: {
1150
+ defaultCountry: "US"
1151
+ },
1152
+ embeddedWallets: {
1153
+ showWalletUIs: false
1154
+ }
1155
+ },
1156
+ children: /* @__PURE__ */ jsxRuntime.jsx(BlinkContext.Provider, { value, children })
1157
+ }
1158
+ ) }) }) })
1130
1159
  );
1131
1160
  }
1132
1161
  function useBlinkConfig() {
@@ -1167,9 +1196,10 @@ function buildTargetMatchers(target) {
1167
1196
  aliases.add("io.metamask");
1168
1197
  }
1169
1198
  if (value === "base" || value === "base account" || value === "base app" || value.includes("coinbase")) {
1170
- aliases.add("base");
1171
1199
  aliases.add("coinbase");
1172
1200
  aliases.add("coinbasewalletsdk");
1201
+ aliases.add("com.coinbase.wallet");
1202
+ aliases.add("baseaccount");
1173
1203
  }
1174
1204
  if (value.includes("trust")) {
1175
1205
  aliases.add("trust");
@@ -1243,19 +1273,70 @@ async function withTimeout(promise, ms, label) {
1243
1273
  if (timer !== void 0) clearTimeout(timer);
1244
1274
  }
1245
1275
  }
1246
- function isReloadingCoinbaseConnector(connector) {
1276
+ function isCoinbaseSdkConnector(connector) {
1247
1277
  if (!connector) return false;
1248
1278
  return connectorMatchesWallet(
1249
1279
  { id: connector.id, name: connector.name ?? "" },
1250
1280
  { wagmiConnectorId: "coinbaseWalletSDK" }
1251
1281
  );
1252
1282
  }
1283
+ var COINBASE_SDK_STORAGE_PREFIXES = ["-CBWSDK", "-walletlink"];
1284
+ var COINBASE_SIGNER_TYPE_STORAGE_KEY = "-CBWSDK:SignerConfigurator:SignerType";
1285
+ function loadCoinbaseSignerType() {
1286
+ if (typeof window === "undefined" || !window.localStorage) return null;
1287
+ try {
1288
+ return window.localStorage.getItem(COINBASE_SIGNER_TYPE_STORAGE_KEY);
1289
+ } catch {
1290
+ return null;
1291
+ }
1292
+ }
1293
+ function clearCoinbaseWalletSdkSession() {
1294
+ if (typeof window === "undefined" || !window.localStorage) return;
1295
+ try {
1296
+ const store = window.localStorage;
1297
+ const keysToRemove = [];
1298
+ for (let i = 0; i < store.length; i += 1) {
1299
+ const key = store.key(i);
1300
+ if (key && COINBASE_SDK_STORAGE_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1301
+ keysToRemove.push(key);
1302
+ }
1303
+ }
1304
+ keysToRemove.forEach((key) => store.removeItem(key));
1305
+ console.info("[blink-sdk][disconnect] cleared Coinbase Wallet SDK session", {
1306
+ clearedKeys: keysToRemove.length
1307
+ });
1308
+ } catch (err) {
1309
+ console.info("[blink-sdk][disconnect] failed to clear Coinbase Wallet SDK session", err);
1310
+ }
1311
+ }
1312
+ function resetWagmiConnectionInMemory(wagmiConfig, connector) {
1313
+ if (!connector) return;
1314
+ try {
1315
+ const target = connector;
1316
+ const connections = [...wagmiConfig.state.connections.values()];
1317
+ const match = connections.find((conn) => {
1318
+ const live2 = conn.connector;
1319
+ if (target.uid && live2.uid) return live2.uid === target.uid;
1320
+ return live2.id === target.id;
1321
+ });
1322
+ const live = match?.connector;
1323
+ if (!live?.emitter) return;
1324
+ live.emitter.emit("disconnect");
1325
+ console.info("[blink-sdk][disconnect] reset wagmi in-memory connection via emitter", {
1326
+ connectorId: live.id
1327
+ });
1328
+ } catch (err) {
1329
+ console.info("[blink-sdk][disconnect] failed to reset wagmi in-memory connection", err);
1330
+ }
1331
+ }
1253
1332
  async function safeDisconnect(wagmiConfig, connector) {
1254
- if (isReloadingCoinbaseConnector(connector)) {
1333
+ if (isCoinbaseSdkConnector(connector)) {
1255
1334
  console.info(
1256
- "[blink-sdk][disconnect] skipping wagmi disconnect for Coinbase WalletLink connector",
1335
+ "[blink-sdk][disconnect] clearing Coinbase Wallet SDK session instead of wagmi disconnect",
1257
1336
  { connectorId: connector?.id }
1258
1337
  );
1338
+ clearCoinbaseWalletSdkSession();
1339
+ resetWagmiConnectionInMemory(wagmiConfig, connector);
1259
1340
  return;
1260
1341
  }
1261
1342
  await core.disconnect(wagmiConfig, { connector }).catch(() => {
@@ -3909,6 +3990,23 @@ function isUserRejection(msg) {
3909
3990
  const lower = msg.toLowerCase();
3910
3991
  return lower.includes("rejected") || lower.includes("denied");
3911
3992
  }
3993
+ var EmptyConnectionAccountError = class extends Error {
3994
+ constructor(message = "Wallet connected but returned no account address.") {
3995
+ super(message);
3996
+ this.name = "EmptyConnectionAccountError";
3997
+ }
3998
+ };
3999
+ var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
4000
+ function assertNonEmptyConnectedAddress(address, ctx) {
4001
+ if (typeof address === "string" && EVM_ADDRESS_RE.test(address)) {
4002
+ return;
4003
+ }
4004
+ appendDebug("error", "OPEN_PROVIDER: empty-or-invalid-connected-address", {
4005
+ ...ctx,
4006
+ address: address ?? null
4007
+ });
4008
+ throw new EmptyConnectionAccountError();
4009
+ }
3912
4010
  function requiresExplicitEvmNonce(account) {
3913
4011
  return connectorMatchesWallet(account?.connector, { providerName: "trust" }) || connectorMatchesWallet(account?.connector, { providerName: "phantom" });
3914
4012
  }
@@ -4142,7 +4240,8 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4142
4240
  accountConnectorId: account.connector?.id ?? null,
4143
4241
  accountConnectorName: account.connector?.name ?? null,
4144
4242
  targetId: targetId ?? null,
4145
- resolvedConnectorId: connector?.id ?? null
4243
+ resolvedConnectorId: connector?.id ?? null,
4244
+ availableConnectorIds: connectors.map((c) => c.id)
4146
4245
  };
4147
4246
  let disconnectedMismatchedConnector = false;
4148
4247
  if (account.isConnected && account.address) {
@@ -4150,6 +4249,7 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4150
4249
  if (connectorMatchesTarget) {
4151
4250
  const hexChainId2 = account.chainId ? `0x${account.chainId.toString(16)}` : void 0;
4152
4251
  const branch = !targetId ? "early-return-no-target" : "early-return-connector-match";
4252
+ assertNonEmptyConnectedAddress(account.address, { ...logContext, branch });
4153
4253
  console.info("[blink-sdk][open-provider] Skipping connectAsync; wagmi already connected.", {
4154
4254
  ...logContext,
4155
4255
  branch
@@ -4173,6 +4273,20 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4173
4273
  disconnectedMismatchedConnector = true;
4174
4274
  }
4175
4275
  if (!disconnectedMismatchedConnector) {
4276
+ const targetIsCoinbase = connectorMatchesWallet(connector, {
4277
+ wagmiConnectorId: "coinbaseWalletSDK"
4278
+ });
4279
+ const isDesktopUa = !isMobileUserAgent(
4280
+ typeof navigator === "undefined" ? null : navigator.userAgent
4281
+ );
4282
+ if (targetIsCoinbase && isDesktopUa && loadCoinbaseSignerType() === "scw") {
4283
+ console.info(
4284
+ "[blink-sdk][open-provider] Clearing stale Coinbase Smart Wallet (scw) session before reconnect so eoaOnly binds the EOA.",
4285
+ logContext
4286
+ );
4287
+ appendDebug("info", "OPEN_PROVIDER: clearing stale scw session before reconnect", logContext);
4288
+ clearCoinbaseWalletSdkSession();
4289
+ }
4176
4290
  console.info("[blink-sdk][open-provider] Attempting silent reconnect.", logContext);
4177
4291
  appendDebug("info", "OPEN_PROVIDER: attempting silent reconnect", logContext);
4178
4292
  const reconnections = await core.reconnect(wagmiConfig).catch(() => []);
@@ -4187,6 +4301,7 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4187
4301
  reconnectedConnectorId: reconnectedAccount.connector?.id ?? null,
4188
4302
  reconnectionCount: reconnections.length
4189
4303
  };
4304
+ assertNonEmptyConnectedAddress(reconnectedAccount.address, reconnectLogContext);
4190
4305
  console.info(
4191
4306
  "[blink-sdk][open-provider] Silent reconnect succeeded; skipping connectAsync.",
4192
4307
  reconnectLogContext
@@ -4254,6 +4369,10 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4254
4369
  });
4255
4370
  const result = await connectAsync({ connector });
4256
4371
  const hexChainId = `0x${result.chainId.toString(16)}`;
4372
+ assertNonEmptyConnectedAddress(result.accounts[0], {
4373
+ ...logContext,
4374
+ branch: "connectAsync-result"
4375
+ });
4257
4376
  return actionSuccess(
4258
4377
  action,
4259
4378
  `Connected to ${connector.name}. Account: ${result.accounts[0]}, Chain: ${hexChainId}`,
@@ -4261,15 +4380,23 @@ async function executeOpenProvider(action, wagmiConfig, connectors, connectAsync
4261
4380
  );
4262
4381
  } catch (err) {
4263
4382
  const msg = err instanceof Error ? err.message : "Failed to connect wallet";
4264
- if (options?.externalAuthorizationAvailable && isUserRejection(msg) && action.metadata?.chainFamily !== "svm") {
4265
- appendDebug("info", "OPEN_PROVIDER: user-rejection-soft-halt", {
4383
+ const emptyAccount = err instanceof EmptyConnectionAccountError;
4384
+ if (options?.externalAuthorizationAvailable && (isUserRejection(msg) || emptyAccount) && action.metadata?.chainFamily !== "svm") {
4385
+ appendDebug("info", "OPEN_PROVIDER: soft-halt", {
4266
4386
  actionId: action.id,
4267
- externalAuthorizationAvailable: true
4387
+ externalAuthorizationAvailable: true,
4388
+ reason: emptyAccount ? "empty-account" : "user-rejection"
4268
4389
  });
4269
4390
  return actionPending(
4270
4391
  action,
4271
4392
  "awaiting-external-authorization",
4272
- "Wallet connection prompt dismissed \u2014 awaiting completion via cross-device authorization."
4393
+ emptyAccount ? "Wallet returned no account \u2014 awaiting completion via cross-device authorization." : "Wallet connection prompt dismissed \u2014 awaiting completion via cross-device authorization."
4394
+ );
4395
+ }
4396
+ if (emptyAccount) {
4397
+ return actionError(
4398
+ action,
4399
+ "We couldn't read an address from that wallet. Please reconnect and make sure an account is selected."
4273
4400
  );
4274
4401
  }
4275
4402
  if (action.metadata?.chainFamily === "svm") {
@@ -6975,18 +7102,6 @@ function updateTrackedSession(sessions, ownerSessionId, reportedSession, actionS
6975
7102
  }
6976
7103
  }
6977
7104
 
6978
- // src/walletFlow.ts
6979
- var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
6980
- function isMobileUserAgent(userAgent) {
6981
- if (!userAgent) {
6982
- return false;
6983
- }
6984
- return MOBILE_USER_AGENT_PATTERN.test(userAgent);
6985
- }
6986
- function shouldUseWalletConnector(options) {
6987
- return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
6988
- }
6989
-
6990
7105
  // src/enterAmountInput.ts
6991
7106
  var MAX_FRACTION_DIGITS = 2;
6992
7107
  function isDigit(value) {
@@ -8586,6 +8701,52 @@ var buttonStyle = (color, hovered) => ({
8586
8701
  flexShrink: 0,
8587
8702
  transition: "background 0.15s ease"
8588
8703
  });
8704
+ var INTERCOM_HELP_URL = "https://intercom.help/blinkcash/en/";
8705
+ var TERMS_URL = "https://blink.cash/terms";
8706
+ function SupportFooter() {
8707
+ const { tokens } = useBlinkConfig();
8708
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle2(tokens.textMuted), children: [
8709
+ /* @__PURE__ */ jsxRuntime.jsx(
8710
+ "a",
8711
+ {
8712
+ href: INTERCOM_HELP_URL,
8713
+ target: "_blank",
8714
+ rel: "noopener noreferrer",
8715
+ style: linkStyle(tokens.textMuted),
8716
+ children: "Help"
8717
+ }
8718
+ ),
8719
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: dotStyle(tokens.textTertiary), children: "\u2022" }),
8720
+ /* @__PURE__ */ jsxRuntime.jsx(
8721
+ "a",
8722
+ {
8723
+ href: TERMS_URL,
8724
+ target: "_blank",
8725
+ rel: "noopener noreferrer",
8726
+ style: linkStyle(tokens.textMuted),
8727
+ children: "Terms"
8728
+ }
8729
+ )
8730
+ ] });
8731
+ }
8732
+ var containerStyle2 = (color) => ({
8733
+ display: "flex",
8734
+ alignItems: "center",
8735
+ justifyContent: "center",
8736
+ gap: 10,
8737
+ fontSize: "0.8rem",
8738
+ color,
8739
+ padding: "4px 0"
8740
+ });
8741
+ var linkStyle = (color) => ({
8742
+ color,
8743
+ fontWeight: 500,
8744
+ textDecoration: "none"
8745
+ });
8746
+ var dotStyle = (color) => ({
8747
+ color,
8748
+ fontSize: "0.8rem"
8749
+ });
8589
8750
  function SettingsMenu({ onLogout }) {
8590
8751
  const { tokens } = useBlinkConfig();
8591
8752
  const [open, setOpen] = react.useState(false);
@@ -8601,7 +8762,7 @@ function SettingsMenu({ onLogout }) {
8601
8762
  document.addEventListener("mousedown", handleClickOutside);
8602
8763
  return () => document.removeEventListener("mousedown", handleClickOutside);
8603
8764
  }, [open]);
8604
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, style: containerStyle2, children: [
8765
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, style: containerStyle3, children: [
8605
8766
  /* @__PURE__ */ jsxRuntime.jsx(IconButton, { onClick: toggle, "aria-label": "Settings", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [
8606
8767
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "2", fill: "currentColor" }),
8607
8768
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "2", fill: "currentColor" }),
@@ -8611,7 +8772,7 @@ function SettingsMenu({ onLogout }) {
8611
8772
  /* @__PURE__ */ jsxRuntime.jsxs(
8612
8773
  "a",
8613
8774
  {
8614
- href: "https://intercom.help/blinkcash/en/",
8775
+ href: INTERCOM_HELP_URL,
8615
8776
  target: "_blank",
8616
8777
  rel: "noopener noreferrer",
8617
8778
  onClick: () => setOpen(false),
@@ -8648,7 +8809,7 @@ function SettingsMenu({ onLogout }) {
8648
8809
  ] })
8649
8810
  ] });
8650
8811
  }
8651
- var containerStyle2 = {
8812
+ var containerStyle3 = {
8652
8813
  position: "relative"
8653
8814
  };
8654
8815
  var dropdownStyle = (tokens) => ({
@@ -8738,7 +8899,7 @@ var badgeStyle = (color) => ({
8738
8899
  });
8739
8900
  function PoweredByFooter() {
8740
8901
  const { tokens } = useBlinkConfig();
8741
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle3(tokens.textMuted), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowStyle, children: [
8902
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle4(tokens.textMuted), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowStyle, children: [
8742
8903
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
8743
8904
  "path",
8744
8905
  {
@@ -8749,7 +8910,7 @@ function PoweredByFooter() {
8749
8910
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Powered by Blink" })
8750
8911
  ] }) });
8751
8912
  }
8752
- var containerStyle3 = (color) => ({
8913
+ var containerStyle4 = (color) => ({
8753
8914
  display: "flex",
8754
8915
  flexDirection: "column",
8755
8916
  alignItems: "center",
@@ -9169,6 +9330,7 @@ var TETHER_LOGO = svgToDataUri(TETHER_SVG);
9169
9330
  var USDH_LOGO = svgToDataUri(USDH_SVG);
9170
9331
  var ETH_LOGO = "https://assets.relay.link/icons/currencies/eth.png";
9171
9332
  var SOL_LOGO = "https://assets.relay.link/icons/currencies/sol.png";
9333
+ var MON_LOGO = "https://assets.relay.link/icons/currencies/mon.png";
9172
9334
  var KNOWN_LOGOS = {
9173
9335
  metamask: METAMASK_LOGO,
9174
9336
  base: BASE_LOGO,
@@ -9186,7 +9348,8 @@ var TOKEN_LOGOS = {
9186
9348
  USDM: USDM_LOGO,
9187
9349
  USDH: USDH_LOGO,
9188
9350
  ETH: ETH_LOGO,
9189
- SOL: SOL_LOGO
9351
+ SOL: SOL_LOGO,
9352
+ MON: MON_LOGO
9190
9353
  };
9191
9354
  var CHAIN_LOGOS = {
9192
9355
  base: BASE_CHAIN_LOGO,
@@ -9459,12 +9622,12 @@ var defaultIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "
9459
9622
  ) });
9460
9623
  function InfoBanner({ children, icon }) {
9461
9624
  const { tokens } = useBlinkConfig();
9462
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle4(tokens.accent), children: [
9625
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle5(tokens.accent), children: [
9463
9626
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: iconStyle, children: icon ?? defaultIcon }),
9464
9627
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: textStyle, children })
9465
9628
  ] });
9466
9629
  }
9467
- var containerStyle4 = (accent) => ({
9630
+ var containerStyle5 = (accent) => ({
9468
9631
  display: "flex",
9469
9632
  alignItems: "flex-start",
9470
9633
  gap: 10,
@@ -9482,7 +9645,7 @@ var iconStyle = {
9482
9645
  };
9483
9646
  var textStyle = { flex: 1 };
9484
9647
  function WarningBanner({ title, children }) {
9485
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle5, children: [
9648
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle6, children: [
9486
9649
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle2, children: [
9487
9650
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", style: iconStyle2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z", fill: "#F57C00" }) }),
9488
9651
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: title })
@@ -9490,7 +9653,7 @@ function WarningBanner({ title, children }) {
9490
9653
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: bodyStyle2, children })
9491
9654
  ] });
9492
9655
  }
9493
- var containerStyle5 = {
9656
+ var containerStyle6 = {
9494
9657
  padding: "14px 16px",
9495
9658
  background: "#FFF8E1",
9496
9659
  border: "1px solid #FFE082",
@@ -9540,7 +9703,7 @@ function NotificationBanner({
9540
9703
  }) {
9541
9704
  const { tokens } = useBlinkConfig();
9542
9705
  const color = variant === "negative" ? NEGATIVE_FG : tokens.text;
9543
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle6(tokens.bgRecessed), children: [
9706
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle7(tokens.bgRecessed), children: [
9544
9707
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...iconWrapStyle2, color }, children: icon ?? defaultIcon2 }),
9545
9708
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...textColStyle, color }, children: [
9546
9709
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: titleStyle3, children: title }),
@@ -9548,7 +9711,7 @@ function NotificationBanner({
9548
9711
  ] })
9549
9712
  ] });
9550
9713
  }
9551
- var containerStyle6 = (bg) => ({
9714
+ var containerStyle7 = (bg) => ({
9552
9715
  display: "flex",
9553
9716
  alignItems: "flex-start",
9554
9717
  gap: 16,
@@ -9643,7 +9806,7 @@ function OtpInput({ value, onChange, length = 6, disabled }) {
9643
9806
  onChange(pasted);
9644
9807
  focusInput(Math.min(pasted.length, length - 1));
9645
9808
  }, [onChange, length, focusInput]);
9646
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle7, children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
9809
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle8, children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
9647
9810
  "input",
9648
9811
  {
9649
9812
  ref: (el) => {
@@ -9664,7 +9827,7 @@ function OtpInput({ value, onChange, length = 6, disabled }) {
9664
9827
  i
9665
9828
  )) });
9666
9829
  }
9667
- var containerStyle7 = {
9830
+ var containerStyle8 = {
9668
9831
  display: "flex",
9669
9832
  gap: 8,
9670
9833
  justifyContent: "center",
@@ -10211,6 +10374,7 @@ function TokenSourceRow({
10211
10374
  chainName,
10212
10375
  tokenLogoUri,
10213
10376
  balance,
10377
+ balanceLabel,
10214
10378
  selected,
10215
10379
  requiresAuth,
10216
10380
  notSupported,
@@ -10219,9 +10383,10 @@ function TokenSourceRow({
10219
10383
  const { tokens } = useBlinkConfig();
10220
10384
  const [hovered, setHovered] = react.useState(false);
10221
10385
  const tokenLogo = tokenLogoUri ?? TOKEN_LOGOS[symbol];
10386
+ const balanceText = balanceLabel ?? (balance != null ? `$${formatUsdTwoDecimals2(balance)}` : null);
10222
10387
  const ariaLabel = [
10223
10388
  `${symbol} on ${chainName}`,
10224
- balance != null ? `$${formatUsdTwoDecimals2(balance)} balance` : null,
10389
+ balanceText != null ? `${balanceText} balance` : null,
10225
10390
  requiresAuth ? "authorisation required" : null,
10226
10391
  notSupported ? NOT_SUPPORTED_LABEL.toLowerCase() : null
10227
10392
  ].filter(Boolean).join(", ");
@@ -10255,15 +10420,12 @@ function TokenSourceRow({
10255
10420
  selected && /* @__PURE__ */ jsxRuntime.jsx(SelectedBadge, { accent: tokens.accent, fill: tokens.textInverse })
10256
10421
  ] }),
10257
10422
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: textColStyle2, children: [
10258
- notSupported ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: symbolLineStyle, children: [
10259
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: symbolStyle(tokens.textMuted), children: symbol }),
10260
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: notSupportedTagStyle(tokens.bgInput, tokens.border, tokens.textMuted), children: NOT_SUPPORTED_BADGE_TEXT })
10261
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: symbolStyle(tokens.text), children: symbol }),
10423
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: symbolStyle(notSupported ? tokens.textMuted : tokens.text), children: symbol }),
10262
10424
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: chainStyle(tokens.textMuted), children: chainName })
10263
10425
  ] }),
10264
- balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: balanceStyle2(tokens.textMuted), children: [
10265
- "$",
10266
- formatUsdTwoDecimals2(balance)
10426
+ (balanceText != null || notSupported) && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: balanceColStyle, children: [
10427
+ balanceText != null && /* @__PURE__ */ jsxRuntime.jsx("span", { style: balanceStyle2(tokens.textMuted), children: balanceText }),
10428
+ notSupported && /* @__PURE__ */ jsxRuntime.jsx("span", { style: notSupportedTagStyle(tokens.bgInput, tokens.border, tokens.textMuted), children: NOT_SUPPORTED_BADGE_TEXT })
10267
10429
  ] })
10268
10430
  ]
10269
10431
  }
@@ -10372,11 +10534,12 @@ var balanceStyle2 = (color) => ({
10372
10534
  whiteSpace: "nowrap",
10373
10535
  flexShrink: 0
10374
10536
  });
10375
- var symbolLineStyle = {
10537
+ var balanceColStyle = {
10376
10538
  display: "flex",
10377
- alignItems: "center",
10378
- gap: 6,
10379
- minWidth: 0
10539
+ flexDirection: "column",
10540
+ alignItems: "flex-end",
10541
+ gap: 4,
10542
+ flexShrink: 0
10380
10543
  };
10381
10544
  var notSupportedTagStyle = (bg, borderColor, color) => ({
10382
10545
  fontSize: 11,
@@ -11130,7 +11293,8 @@ function LoginScreen({
11130
11293
  style: secondaryTextStyle(tokens, loading),
11131
11294
  children: secondaryLabel
11132
11295
  }
11133
- )
11296
+ ),
11297
+ /* @__PURE__ */ jsxRuntime.jsx(SupportFooter, {})
11134
11298
  ] }),
11135
11299
  children: [
11136
11300
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack, right: headerRight }),
@@ -11302,7 +11466,7 @@ function DepositOptionsScreen({
11302
11466
  const { tokens, promoTagText } = useBlinkConfig();
11303
11467
  const [manualHovered, setManualHovered] = react.useState(false);
11304
11468
  const [manualPressed, setManualPressed] = react.useState(false);
11305
- return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { hideScrollbar: true, children: [
11469
+ return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { hideScrollbar: true, footer: /* @__PURE__ */ jsxRuntime.jsx(SupportFooter, {}), children: [
11306
11470
  /* @__PURE__ */ jsxRuntime.jsx(
11307
11471
  ScreenHeader,
11308
11472
  {
@@ -11457,7 +11621,7 @@ function WelcomeBackScreen({
11457
11621
  const [depositPressed, setDepositPressed] = react.useState(false);
11458
11622
  const [manualHovered, setManualHovered] = react.useState(false);
11459
11623
  const [manualPressed, setManualPressed] = react.useState(false);
11460
- return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
11624
+ return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsxRuntime.jsx(SupportFooter, {}), children: [
11461
11625
  /* @__PURE__ */ jsxRuntime.jsx(
11462
11626
  ScreenHeader,
11463
11627
  {
@@ -13217,7 +13381,7 @@ function LinkTokensScreen({
13217
13381
  }
13218
13382
  ),
13219
13383
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
13220
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(t.text), children: "Pick your asset for passkey deposits" }),
13384
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(t.text), children: "Pick your stablecoin for passkey deposits" }),
13221
13385
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "blink-link-tokens-list", style: listCardStyle(t.bgRecessed), children: [
13222
13386
  showShimmer ? Array.from({ length: SHIMMER_ROWS }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
13223
13387
  "div",
@@ -13240,7 +13404,9 @@ function LinkTokensScreen({
13240
13404
  {
13241
13405
  symbol: entry.tokenSymbol,
13242
13406
  chainName: entry.chainName,
13243
- balance: entry.balanceUsd,
13407
+ tokenLogoUri: entry.tokenLogoUri,
13408
+ balance: entry.balanceLabel == null ? entry.balanceUsd : void 0,
13409
+ balanceLabel: entry.balanceLabel,
13244
13410
  selected: i === selectedIndex,
13245
13411
  notSupported: entry.notSupported,
13246
13412
  onClick: () => onSelect(i)
@@ -15170,7 +15336,7 @@ function DepositAddressScreen({
15170
15336
  const waitingForQr = !!depositAddress && !qrReady;
15171
15337
  const awaitingSession = !!selectedOption && !session && !loading;
15172
15338
  const showShimmer = loadingSources || showLoading || waitingForQr || awaitingSession;
15173
- return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
15339
+ return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsxRuntime.jsx(SupportFooter, {}), children: [
15174
15340
  /* @__PURE__ */ jsxRuntime.jsx(
15175
15341
  ScreenHeader,
15176
15342
  {
@@ -16852,6 +17018,14 @@ var genericStackStyle = {
16852
17018
  gap: 14
16853
17019
  };
16854
17020
 
17021
+ // src/feeFormat.ts
17022
+ function formatNativeAmount(value, maxDecimals = 4) {
17023
+ if (!Number.isFinite(value) || value <= 0) return "0";
17024
+ const rounded = Number(value.toFixed(maxDecimals));
17025
+ if (rounded === 0) return `< ${(10 ** -maxDecimals).toFixed(maxDecimals)}`;
17026
+ return rounded.toLocaleString("en-US", { maximumFractionDigits: maxDecimals }).replace(/,/g, "");
17027
+ }
17028
+
16855
17029
  // src/screenViewModels.ts
16856
17030
  function findSelectedWallet(selectedAccount, selectedWalletId) {
16857
17031
  return selectedAccount?.wallets.find((w) => w.id === selectedWalletId);
@@ -17018,7 +17192,11 @@ function buildLinkTokensScreenProps({
17018
17192
  walletId: rawOption?.walletId,
17019
17193
  tokenAddress: rawOption?.tokenAddress,
17020
17194
  chainId: rawOption?.chainId != null ? Number(rawOption.chainId) : void 0,
17021
- notSupported: false
17195
+ notSupported: false,
17196
+ // Server-provided token logo (token_addresses.logo_uri). The component
17197
+ // falls back to the bundled TOKEN_LOGOS map by symbol, so tokens without
17198
+ // a bundled asset (e.g. CASH) rely on this DB-driven URI.
17199
+ tokenLogoUri: t.logoURI ?? void 0
17022
17200
  };
17023
17201
  })
17024
17202
  );
@@ -17028,19 +17206,32 @@ function buildLinkTokensScreenProps({
17028
17206
  symbol: t.tokenSymbol,
17029
17207
  chainName: chain.chainName,
17030
17208
  balanceUsd: t.balance ?? 0,
17031
- walletId: void 0,
17032
- tokenAddress: void 0,
17033
- chainId: void 0,
17034
- notSupported: true
17209
+ notSupported: true,
17210
+ tokenLogoUri: t.logoURI ?? void 0
17035
17211
  }))
17036
17212
  )
17037
17213
  );
17038
- const entries2 = rowContexts.map(({ symbol, chainName, balanceUsd, notSupported }) => ({
17039
- tokenSymbol: symbol,
17040
- chainName,
17041
- balanceUsd,
17042
- notSupported
17043
- }));
17214
+ rowContexts.push(
17215
+ ...derived.selectSourceNativeChoices.map((native) => ({
17216
+ symbol: native.tokenSymbol,
17217
+ chainName: native.chainName,
17218
+ balanceUsd: 0,
17219
+ notSupported: true,
17220
+ tokenLogoUri: native.logoURI ?? void 0,
17221
+ balanceLabel: `${formatNativeAmount(native.amount)} ${native.tokenSymbol}`
17222
+ }))
17223
+ );
17224
+ const entries2 = rowContexts.map(
17225
+ ({ symbol, chainName, balanceUsd, notSupported, tokenLogoUri, balanceLabel }) => ({
17226
+ tokenSymbol: symbol,
17227
+ chainName,
17228
+ balanceUsd,
17229
+ notSupported,
17230
+ // Only present on native rows — keeps stablecoin / WC entries unchanged.
17231
+ ...tokenLogoUri != null ? { tokenLogoUri } : {},
17232
+ ...balanceLabel != null ? { balanceLabel } : {}
17233
+ })
17234
+ );
17044
17235
  const firstSupportedIndex = rowContexts.findIndex((row) => !row.notSupported);
17045
17236
  const selectedTokenSymbol = flow.state.setupDepositToken?.symbol ?? derived.selectSourceRecommended?.tokenSymbol;
17046
17237
  const selectedChainName = flow.state.setupDepositToken?.chainName ?? derived.selectSourceRecommended?.chainName;
@@ -18043,6 +18234,11 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
18043
18234
  const unsupportedOptions = pendingSelectSourceAction.metadata?.unsupportedOptions ?? [];
18044
18235
  return buildSelectSourceChoices(unsupportedOptions, minTransferAmountUsd);
18045
18236
  }, [pendingSelectSourceAction, minTransferAmountUsd]);
18237
+ const selectSourceNativeChoices = react.useMemo(() => {
18238
+ if (!pendingSelectSourceAction) return [];
18239
+ const nativeOptions = pendingSelectSourceAction.metadata?.nativeUnsupportedOptions ?? [];
18240
+ return buildNativeUnsupportedEntries(nativeOptions);
18241
+ }, [pendingSelectSourceAction]);
18046
18242
  const selectSourceRecommended = react.useMemo(() => {
18047
18243
  if (!pendingSelectSourceAction) return null;
18048
18244
  return pendingSelectSourceAction.metadata?.recommended ?? null;
@@ -18094,6 +18290,7 @@ function useSourceSelectionHandlers(_dispatch, orchestrator, options) {
18094
18290
  setSelectSourceTokenSymbol,
18095
18291
  selectSourceChoices,
18096
18292
  selectSourceUnsupportedChoices,
18293
+ selectSourceNativeChoices,
18097
18294
  selectSourceRecommended,
18098
18295
  selectSourceAvailableBalance,
18099
18296
  handleSelectSourceChainChange,
@@ -20625,6 +20822,33 @@ function usePersistAmountToActiveSession({
20625
20822
  });
20626
20823
  }, [apiBaseUrl, sessionId, effectiveDepositAmount]);
20627
20824
  }
20825
+ function useFingerprintFlowEvents(deps) {
20826
+ const { ready, authenticated, merchantId } = deps;
20827
+ const { getData } = react$1.useVisitorData({ immediate: false });
20828
+ const openedSentRef = react.useRef(false);
20829
+ const authenticatedSentRef = react.useRef(false);
20830
+ react.useEffect(() => {
20831
+ if (openedSentRef.current) return;
20832
+ openedSentRef.current = true;
20833
+ void getData({
20834
+ tag: { event: "blink_opened", ...merchantId ? { merchantId } : {} },
20835
+ ...merchantId ? { linkedId: merchantId } : {}
20836
+ }).catch((error) => {
20837
+ console.error("Fingerprint blink_opened event failed", error);
20838
+ });
20839
+ }, [getData, merchantId]);
20840
+ react.useEffect(() => {
20841
+ if (authenticatedSentRef.current) return;
20842
+ if (!ready || !authenticated) return;
20843
+ authenticatedSentRef.current = true;
20844
+ void getData({
20845
+ tag: { event: "blink_authenticated", ...merchantId ? { merchantId } : {} },
20846
+ ...merchantId ? { linkedId: merchantId } : {}
20847
+ }).catch((error) => {
20848
+ console.error("Fingerprint blink_authenticated event failed", error);
20849
+ });
20850
+ }, [getData, ready, authenticated, merchantId]);
20851
+ }
20628
20852
  var NoMatchingExtensionError = class extends Error {
20629
20853
  constructor() {
20630
20854
  super("No installed EIP-6963 extension matches the selected wallet");
@@ -20663,6 +20887,11 @@ function BlinkPaymentInner({
20663
20887
  return true;
20664
20888
  })();
20665
20889
  const effectiveAuthenticated = authenticated || popupAuthValid;
20890
+ useFingerprintFlowEvents({
20891
+ ready,
20892
+ authenticated: effectiveAuthenticated,
20893
+ merchantId: merchantAuthorization?.merchantId
20894
+ });
20666
20895
  const effectiveGetAccessToken = react.useCallback(async () => {
20667
20896
  if (popupAuthRef.current) {
20668
20897
  if (isPopupAuthExpired(popupAuthRef.current.accessToken)) {
@@ -21271,6 +21500,7 @@ function BlinkPaymentInner({
21271
21500
  selectedSource: derived.selectedSource,
21272
21501
  selectSourceChoices: sourceSelection.selectSourceChoices,
21273
21502
  selectSourceUnsupportedChoices: sourceSelection.selectSourceUnsupportedChoices,
21503
+ selectSourceNativeChoices: sourceSelection.selectSourceNativeChoices,
21274
21504
  selectSourceRecommended: sourceSelection.selectSourceRecommended,
21275
21505
  selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance,
21276
21506
  walletConnectChainIdsByAccount
@@ -21341,6 +21571,7 @@ exports.WalletPickerScreen = WalletPickerScreen;
21341
21571
  exports.WelcomeBackScreen = WelcomeBackScreen;
21342
21572
  exports.appendDebug = appendDebug;
21343
21573
  exports.blinkApi = api_exports;
21574
+ exports.buildNativeUnsupportedEntries = buildNativeUnsupportedEntries;
21344
21575
  exports.clearDebugEntries = clearDebugEntries;
21345
21576
  exports.createInitialState = createInitialState;
21346
21577
  exports.credentialIdBase64ToBytes = credentialIdBase64ToBytes;
@@ -21352,6 +21583,7 @@ exports.deviceHasPasskey = deviceHasPasskey;
21352
21583
  exports.encodePermit2ApproveCalldata = encodePermit2ApproveCalldata;
21353
21584
  exports.findDevicePasskey = findDevicePasskey;
21354
21585
  exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
21586
+ exports.formatNativeAmount = formatNativeAmount;
21355
21587
  exports.getAtomicBatchSupportDebugInfo = getAtomicBatchSupportDebugInfo;
21356
21588
  exports.getDebugEntries = getDebugEntries;
21357
21589
  exports.getDeviceBiometricUnlockText = getDeviceBiometricUnlockText;