@openfort/react 1.2.0 → 1.4.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 (146) hide show
  1. package/build/assets/logos.d.ts +3 -0
  2. package/build/assets/logos.js +2 -0
  3. package/build/assets/logos.js.map +1 -1
  4. package/build/components/Common/Modal/styles.js +3 -0
  5. package/build/components/Common/Modal/styles.js.map +1 -1
  6. package/build/components/Common/ScrollArea/index.d.ts +5 -1
  7. package/build/components/Common/ScrollArea/index.js +2 -2
  8. package/build/components/Common/ScrollArea/styles.d.ts +4 -1
  9. package/build/components/Common/ScrollArea/styles.js +19 -4
  10. package/build/components/Common/ScrollArea/styles.js.map +1 -1
  11. package/build/components/Common/SolanaChain/index.d.ts +8 -0
  12. package/build/components/Common/SolanaChain/index.js +40 -0
  13. package/build/components/Common/SolanaChain/index.js.map +1 -0
  14. package/build/components/ConnectModal/index.js +17 -5
  15. package/build/components/ConnectModal/index.js.map +1 -1
  16. package/build/components/Openfort/types.d.ts +17 -11
  17. package/build/components/Openfort/types.js +1 -0
  18. package/build/components/Openfort/types.js.map +1 -1
  19. package/build/components/PageContent/index.d.ts +2 -1
  20. package/build/components/PageContent/index.js +2 -2
  21. package/build/components/Pages/Buy/coinbaseApi.d.ts +1 -1
  22. package/build/components/Pages/Buy/coinbaseApi.js +2 -13
  23. package/build/components/Pages/Buy/coinbaseApi.js.map +1 -1
  24. package/build/components/Pages/Buy/evmCurrencies.d.ts +11 -0
  25. package/build/components/Pages/Buy/evmCurrencies.js +27 -0
  26. package/build/components/Pages/Buy/evmCurrencies.js.map +1 -0
  27. package/build/components/Pages/Buy/index.js +8 -1
  28. package/build/components/Pages/Buy/index.js.map +1 -1
  29. package/build/components/Pages/Buy/onrampApi.d.ts +8 -1
  30. package/build/components/Pages/Buy/onrampApi.js +24 -14
  31. package/build/components/Pages/Buy/onrampApi.js.map +1 -1
  32. package/build/components/Pages/Buy/solanaCurrencies.d.ts +9 -0
  33. package/build/components/Pages/Buy/solanaCurrencies.js +25 -0
  34. package/build/components/Pages/Buy/solanaCurrencies.js.map +1 -0
  35. package/build/components/Pages/Buy/stripeApi.d.ts +1 -1
  36. package/build/components/Pages/Buy/stripeApi.js +2 -13
  37. package/build/components/Pages/Buy/stripeApi.js.map +1 -1
  38. package/build/components/Pages/BuyComplete/index.js +7 -1
  39. package/build/components/Pages/BuyComplete/index.js.map +1 -1
  40. package/build/components/Pages/BuyProcessing/index.js +9 -5
  41. package/build/components/Pages/BuyProcessing/index.js.map +1 -1
  42. package/build/components/Pages/BuyProviderSelect/styles.d.ts +2 -1
  43. package/build/components/Pages/BuySelectProvider/index.js +10 -5
  44. package/build/components/Pages/BuySelectProvider/index.js.map +1 -1
  45. package/build/components/Pages/Connected/EthereumConnected.js +7 -3
  46. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  47. package/build/components/Pages/Connected/SolanaConnected.js +3 -2
  48. package/build/components/Pages/Connected/SolanaConnected.js.map +1 -1
  49. package/build/components/Pages/Deposit/AssetChainLogo.d.ts +5 -2
  50. package/build/components/Pages/Deposit/AssetChainLogo.js +21 -4
  51. package/build/components/Pages/Deposit/AssetChainLogo.js.map +1 -1
  52. package/build/components/Pages/Deposit/DepositAddressBlock.js +1 -1
  53. package/build/components/Pages/Deposit/DepositProgress.js +3 -2
  54. package/build/components/Pages/Deposit/DepositProgress.js.map +1 -1
  55. package/build/components/Pages/Deposit/DepositSuccess.js +2 -1
  56. package/build/components/Pages/Deposit/DepositSuccess.js.map +1 -1
  57. package/build/components/Pages/Deposit/RouteSelectors.js +11 -1
  58. package/build/components/Pages/Deposit/RouteSelectors.js.map +1 -1
  59. package/build/components/Pages/Deposit/SameChainDepositStatus.d.ts +7 -0
  60. package/build/components/Pages/Deposit/SameChainDepositStatus.js +34 -0
  61. package/build/components/Pages/Deposit/SameChainDepositStatus.js.map +1 -0
  62. package/build/components/Pages/Deposit/SameChainDepositSuccess.d.ts +11 -0
  63. package/build/components/Pages/Deposit/SameChainDepositSuccess.js +30 -0
  64. package/build/components/Pages/Deposit/SameChainDepositSuccess.js.map +1 -0
  65. package/build/components/Pages/Deposit/TestnetNotice.d.ts +10 -0
  66. package/build/components/Pages/Deposit/TestnetNotice.js +131 -0
  67. package/build/components/Pages/Deposit/TestnetNotice.js.map +1 -0
  68. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.d.ts +12 -0
  69. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js +111 -0
  70. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js.map +1 -0
  71. package/build/components/Pages/Deposit/index.js +35 -5
  72. package/build/components/Pages/Deposit/index.js.map +1 -1
  73. package/build/components/Pages/Deposit/paymentOptions.d.ts +6 -0
  74. package/build/components/Pages/Deposit/paymentOptions.js +10 -4
  75. package/build/components/Pages/Deposit/paymentOptions.js.map +1 -1
  76. package/build/components/Pages/Deposit/useDepositRoute.d.ts +2 -1
  77. package/build/components/Pages/Deposit/useDepositRoute.js +36 -13
  78. package/build/components/Pages/Deposit/useDepositRoute.js.map +1 -1
  79. package/build/components/Pages/Deposit/useFundingTarget.d.ts +2 -4
  80. package/build/components/Pages/Deposit/useFundingTarget.js +3 -5
  81. package/build/components/Pages/Deposit/useFundingTarget.js.map +1 -1
  82. package/build/components/Pages/Deposit/useSameChainArrival.d.ts +19 -0
  83. package/build/components/Pages/Deposit/useSameChainArrival.js +86 -0
  84. package/build/components/Pages/Deposit/useSameChainArrival.js.map +1 -0
  85. package/build/components/Pages/DepositCex/index.js +7 -8
  86. package/build/components/Pages/DepositCex/index.js.map +1 -1
  87. package/build/components/Pages/DepositCrypto/index.js +26 -3
  88. package/build/components/Pages/DepositCrypto/index.js.map +1 -1
  89. package/build/components/Pages/DepositWallet/DepositWalletDesktop.d.ts +4 -1
  90. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js +19 -27
  91. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js.map +1 -1
  92. package/build/components/Pages/DepositWallet/index.d.ts +5 -5
  93. package/build/components/Pages/DepositWallet/index.js +82 -39
  94. package/build/components/Pages/DepositWallet/index.js.map +1 -1
  95. package/build/components/Pages/DepositWallet/walletDeeplinks.d.ts +6 -4
  96. package/build/components/Pages/DepositWallet/walletDeeplinks.js +3 -1
  97. package/build/components/Pages/DepositWallet/walletDeeplinks.js.map +1 -1
  98. package/build/components/Pages/SelectToken/SolanaSelectToken.d.ts +1 -0
  99. package/build/components/Pages/SelectToken/SolanaSelectToken.js +50 -0
  100. package/build/components/Pages/SelectToken/SolanaSelectToken.js.map +1 -0
  101. package/build/components/Pages/SelectToken/index.js +13 -2
  102. package/build/components/Pages/SelectToken/index.js.map +1 -1
  103. package/build/components/Pages/SelectToken/styles.d.ts +2 -1
  104. package/build/components/Pages/Send/SolanaSend.js +32 -31
  105. package/build/components/Pages/Send/SolanaSend.js.map +1 -1
  106. package/build/components/Pages/Send/utils.js +4 -1
  107. package/build/components/Pages/Send/utils.js.map +1 -1
  108. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +57 -13
  109. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js.map +1 -1
  110. package/build/components/Pages/SendConfirmation/index.js +1 -1
  111. package/build/components/Pages/SendConfirmation/styles.d.ts +0 -5
  112. package/build/components/Pages/SendConfirmation/styles.js +1 -39
  113. package/build/components/Pages/SendConfirmation/styles.js.map +1 -1
  114. package/build/constants/chainConfigs.js +32 -32
  115. package/build/constants/logos.d.ts +4 -0
  116. package/build/constants/logos.js +25 -1
  117. package/build/constants/logos.js.map +1 -1
  118. package/build/ethereum/hooks/useEthereumWalletAssets.js +39 -3
  119. package/build/ethereum/hooks/useEthereumWalletAssets.js.map +1 -1
  120. package/build/hooks/openfort/useFunding.js +7 -7
  121. package/build/hooks/openfort/useFundingChains.d.ts +6 -0
  122. package/build/hooks/openfort/useFundingChains.js +22 -10
  123. package/build/hooks/openfort/useFundingChains.js.map +1 -1
  124. package/build/shared/hooks/useAsyncData.js +15 -2
  125. package/build/shared/hooks/useAsyncData.js.map +1 -1
  126. package/build/shared/utils/explorer.js +6 -4
  127. package/build/shared/utils/explorer.js.map +1 -1
  128. package/build/solana/transfer.d.ts +35 -6
  129. package/build/solana/transfer.js +112 -18
  130. package/build/solana/transfer.js.map +1 -1
  131. package/build/solana/types.d.ts +8 -0
  132. package/build/utils/validation.d.ts +9 -0
  133. package/build/utils/validation.js +14 -1
  134. package/build/utils/validation.js.map +1 -1
  135. package/build/version.d.ts +1 -1
  136. package/build/version.js +1 -1
  137. package/build/wagmi/components/ChainSelect/index.js +3 -2
  138. package/build/wagmi/components/ChainSelect/index.js.map +1 -1
  139. package/build/wagmi/components/ChainSelectList/index.js +3 -2
  140. package/build/wagmi/components/ChainSelectList/index.js.map +1 -1
  141. package/build/wagmi/components/SwitchNetworks/index.js +3 -2
  142. package/build/wagmi/components/SwitchNetworks/index.js.map +1 -1
  143. package/build/wagmi/useSwitchChainFiltered.d.ts +199 -0
  144. package/build/wagmi/useSwitchChainFiltered.js +53 -0
  145. package/build/wagmi/useSwitchChainFiltered.js.map +1 -0
  146. package/package.json +5 -1
@@ -0,0 +1,86 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { createPublicClient, http } from 'viem';
3
+ import { invalidateBalance } from '../../../hooks/useBalance.js';
4
+ import { getDefaultEthereumRpcUrl } from '../../../utils/rpc.js';
5
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
6
+
7
+ /**
8
+ * Backoff schedule (ms) for the waiting poll: brisk at first, easing off so a
9
+ * long wait doesn't hammer the RPC. The last entry is the steady-state cap.
10
+ */
11
+ const POLL_BACKOFF_MS = [2000, 2000, 3000, 5000, 8000, 12000, 20000];
12
+ /**
13
+ * Watches a wallet's native balance and reports when it increases past the first
14
+ * observed value — i.e. a same-chain deposit landed. Same-chain "transfer from
15
+ * address" has no rail session to poll (the deposit address is the wallet
16
+ * itself), so arrival is read straight from the chain RPC.
17
+ *
18
+ * Self-contained single timeout chain (no shared query cache), so it can't fan
19
+ * out into a request storm: one read in flight at a time, with an interval that
20
+ * grows on each tick and resets to brisk when the user returns to the tab (the
21
+ * moment a deposit was likely just made). On arrival, cached balances are
22
+ * invalidated so the asset list refreshes, and polling stops.
23
+ */
24
+ function useSameChainArrival({ address, chainId, enabled, }) {
25
+ var _a, _b, _c;
26
+ const { walletConfig } = useOpenfort();
27
+ const rpcUrl = (_c = (_b = (_a = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _a === void 0 ? void 0 : _a.rpcUrls) === null || _b === void 0 ? void 0 : _b[chainId]) !== null && _c !== void 0 ? _c : getDefaultEthereumRpcUrl(chainId);
28
+ const [arrived, setArrived] = useState(false);
29
+ const baselineRef = useRef(null);
30
+ useEffect(() => {
31
+ if (!enabled || !address)
32
+ return;
33
+ const client = createPublicClient({ transport: http(rpcUrl) });
34
+ let cancelled = false;
35
+ let timer = null;
36
+ let step = 0;
37
+ baselineRef.current = null;
38
+ const read = async () => {
39
+ try {
40
+ const balance = await client.getBalance({ address: address });
41
+ if (cancelled)
42
+ return;
43
+ if (baselineRef.current === null) {
44
+ baselineRef.current = balance;
45
+ }
46
+ else if (balance > baselineRef.current) {
47
+ setArrived(true);
48
+ invalidateBalance();
49
+ return; // deposit landed — stop polling
50
+ }
51
+ }
52
+ catch {
53
+ // transient RPC error — keep polling on the same schedule
54
+ }
55
+ if (cancelled)
56
+ return;
57
+ const delay = POLL_BACKOFF_MS[Math.min(step, POLL_BACKOFF_MS.length - 1)];
58
+ step += 1;
59
+ timer = setTimeout(read, delay);
60
+ };
61
+ // Re-check immediately when the user returns to the tab (deposit likely just
62
+ // made), and reset the schedule back to brisk.
63
+ const onReturn = () => {
64
+ if (cancelled || document.visibilityState !== 'visible')
65
+ return;
66
+ if (timer)
67
+ clearTimeout(timer);
68
+ step = 0;
69
+ read();
70
+ };
71
+ read();
72
+ document.addEventListener('visibilitychange', onReturn);
73
+ window.addEventListener('focus', onReturn);
74
+ return () => {
75
+ cancelled = true;
76
+ if (timer)
77
+ clearTimeout(timer);
78
+ document.removeEventListener('visibilitychange', onReturn);
79
+ window.removeEventListener('focus', onReturn);
80
+ };
81
+ }, [enabled, address, chainId, rpcUrl]);
82
+ return { arrived };
83
+ }
84
+
85
+ export { useSameChainArrival };
86
+ //# sourceMappingURL=useSameChainArrival.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSameChainArrival.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -17,7 +17,7 @@ import { isCexDeliverable, CEX_CHAIN_NAMES } from '../Deposit/cexChains.js';
17
17
  import { isDepositFlowActive, DepositProgress } from '../Deposit/DepositProgress.js';
18
18
  import { DepositStatus } from '../Deposit/DepositStatus.js';
19
19
  import { walletListBtn } from '../Deposit/formStyles.js';
20
- import { DEST_USDC } from '../Deposit/sources.js';
20
+ import { isSolana, DEST_USDC } from '../Deposit/sources.js';
21
21
  import { StepDivider, ButtonLogo } from '../Deposit/styles.js';
22
22
  import { useFundingTarget } from '../Deposit/useFundingTarget.js';
23
23
  import { sanitizeForParsing, sanitizeAmountInput } from '../Send/utils.js';
@@ -55,7 +55,7 @@ const hideBrokenLogo = (e) => {
55
55
  * success / failed screen. No Relay routing; Binance is gated until its rail lands.
56
56
  */
57
57
  const DepositCex = () => {
58
- var _a, _b, _c, _d, _e, _f, _g;
58
+ var _a, _b, _c, _d, _e, _f;
59
59
  const { triggerResize } = useOpenfort();
60
60
  const target = useFundingTarget();
61
61
  // CEX (Coinbase pay-link + session) is served by the Openfort API, not the
@@ -73,20 +73,19 @@ const DepositCex = () => {
73
73
  // wallet, Solana targets the Solana (SVM) embedded account — never cross families
74
74
  // (an EVM address on a Solana target would be rejected / mis-delivered). Accounts
75
75
  // come from the core store so EVM-only apps don't need the Solana React context.
76
- const isEvmTarget = target.chain.startsWith('eip155:');
77
76
  const solanaAddress = (_a = embeddedAccounts === null || embeddedAccounts === void 0 ? void 0 : embeddedAccounts.find((acc) => acc.chainType === ChainTypeEnum.SVM)) === null || _a === void 0 ? void 0 : _a.address;
78
- const address = (_b = target.address) !== null && _b !== void 0 ? _b : (isEvmTarget ? wallet.address : solanaAddress);
77
+ const address = isSolana(target.chain) ? solanaAddress : wallet.address;
79
78
  const chainSupported = isCexDeliverable(target.chain);
80
79
  // Resolve the destination asset + chain for the "Arrives as …" line. The live
81
80
  // chain list is curated for source selection, so the destination may be absent;
82
81
  // only claim a symbol we resolved, or USDC for the zero-config default.
83
82
  const destChain = chains.find((c) => c.id === target.chain);
84
- const destChainName = (_d = (_c = destChain === null || destChain === void 0 ? void 0 : destChain.name) !== null && _c !== void 0 ? _c : CEX_CHAIN_NAMES[target.chain]) !== null && _d !== void 0 ? _d : target.chain;
83
+ const destChainName = (_c = (_b = destChain === null || destChain === void 0 ? void 0 : destChain.name) !== null && _b !== void 0 ? _b : CEX_CHAIN_NAMES[target.chain]) !== null && _c !== void 0 ? _c : target.chain;
85
84
  const destAsset = destChain === null || destChain === void 0 ? void 0 : destChain.currencies.find((c) => c.address.toLowerCase() === target.currency.toLowerCase());
86
85
  const isDefaultUsdc = target.currency.toLowerCase() === DEST_USDC.toLowerCase();
87
- const destAssetLabel = (_e = destAsset === null || destAsset === void 0 ? void 0 : destAsset.symbol) !== null && _e !== void 0 ? _e : (isDefaultUsdc ? 'USDC' : null);
88
- const destAssetLogo = (_f = destAsset === null || destAsset === void 0 ? void 0 : destAsset.logo) !== null && _f !== void 0 ? _f : null;
89
- const destChainLogo = (_g = destChain === null || destChain === void 0 ? void 0 : destChain.logo) !== null && _g !== void 0 ? _g : null;
86
+ const destAssetLabel = (_d = destAsset === null || destAsset === void 0 ? void 0 : destAsset.symbol) !== null && _d !== void 0 ? _d : (isDefaultUsdc ? 'USDC' : null);
87
+ const destAssetLogo = (_e = destAsset === null || destAsset === void 0 ? void 0 : destAsset.logo) !== null && _e !== void 0 ? _e : null;
88
+ const destChainLogo = (_f = destChain === null || destChain === void 0 ? void 0 : destChain.logo) !== null && _f !== void 0 ? _f : null;
90
89
  // Mint the destination-bound session up-front (per target wallet), so the click
91
90
  // that opens Coinbase is a single fast pay-link call and stays popup-safe.
92
91
  //
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +1,6 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useEffect } from 'react';
3
+ import { currencyLogoUrl, chainLogoUrl } from '../../../constants/logos.js';
3
4
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
4
5
  import { routes } from '../../Openfort/types.js';
5
6
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
@@ -7,7 +8,14 @@ import { PageContent } from '../../PageContent/index.js';
7
8
  import { DepositAddressBlock } from '../Deposit/DepositAddressBlock.js';
8
9
  import { isDepositFlowActive, DepositProgress } from '../Deposit/DepositProgress.js';
9
10
  import { RouteSelectors } from '../Deposit/RouteSelectors.js';
11
+ import { SameChainDepositStatus } from '../Deposit/SameChainDepositStatus.js';
12
+ import { SameChainDepositSuccess } from '../Deposit/SameChainDepositSuccess.js';
13
+ import { isSolana } from '../Deposit/sources.js';
14
+ import { TestnetNotice } from '../Deposit/TestnetNotice.js';
15
+ import { UnsupportedNetworkNotice } from '../Deposit/UnsupportedNetworkNotice.js';
10
16
  import { useDepositRoute } from '../Deposit/useDepositRoute.js';
17
+ import { useSameChainArrival } from '../Deposit/useSameChainArrival.js';
18
+ import { caipToChainId } from '../DepositWallet/walletDeeplinks.js';
11
19
 
12
20
  /**
13
21
  * Transfer from address — choose a source chain + token and send to the deposit
@@ -16,15 +24,30 @@ import { useDepositRoute } from '../Deposit/useDepositRoute.js';
16
24
  * "Transfer from wallet" tab.
17
25
  */
18
26
  const DepositCrypto = () => {
19
- var _a, _b, _c, _d;
27
+ var _a, _b, _c, _d, _e;
20
28
  const { triggerResize } = useOpenfort();
21
29
  const route = useDepositRoute('crypto');
22
30
  useEffect(() => {
23
31
  triggerResize();
24
32
  }, [route.receiverAddress, route.loading, route.status, triggerResize]);
33
+ // Same-chain "transfer from address" lands in the wallet itself, so there's no
34
+ // rail session — watch the native balance directly and swap to the success
35
+ // screen once the deposit arrives.
36
+ const sameChainEnabled = route.sameChain && !!route.receiverAddress && !isSolana(route.target.chain);
37
+ const sameChainAddress = (_a = route.receiverAddress) !== null && _a !== void 0 ? _a : '';
38
+ const sameChainId = Number(route.target.chain.split(':')[1]);
39
+ const { arrived } = useSameChainArrival({
40
+ address: sameChainAddress,
41
+ chainId: sameChainId,
42
+ enabled: sameChainEnabled,
43
+ });
25
44
  if (isDepositFlowActive(route.status))
26
45
  return jsx(DepositProgress, { status: route.status });
27
- return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from address" }), jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsx(DepositAddressBlock, { assetLogo: (_b = (_a = route.activeCurrency) === null || _a === void 0 ? void 0 : _a.logo) !== null && _b !== void 0 ? _b : null, chainLogo: (_d = (_c = route.activeChain) === null || _c === void 0 ? void 0 : _c.logo) !== null && _d !== void 0 ? _d : null, receiverAddress: route.receiverAddress, pm: route.pm, sourceCurrency: route.activeCurrency ? { symbol: route.activeCurrency.symbol, decimals: route.activeCurrency.decimals } : null, sameChain: route.sameChain, loading: route.loading, status: route.status }), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] }));
46
+ if (sameChainEnabled && arrived)
47
+ return jsx(SameChainDepositSuccess, { address: sameChainAddress, chainId: sameChainId });
48
+ return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from address" }), jsx(TestnetNotice, {}), route.targetUnsupported ? (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })) : (jsxs(Fragment, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsx(DepositAddressBlock, { assetLogo: currencyLogoUrl((_b = route.activeCurrency) === null || _b === void 0 ? void 0 : _b.symbol, (_c = route.activeCurrency) === null || _c === void 0 ? void 0 : _c.logo), chainLogo: chainLogoUrl(caipToChainId((_d = route.activeChain) === null || _d === void 0 ? void 0 : _d.id), (_e = route.activeChain) === null || _e === void 0 ? void 0 : _e.logo), receiverAddress: route.receiverAddress, pm: route.pm, sourceCurrency: route.activeCurrency
49
+ ? { symbol: route.activeCurrency.symbol, decimals: route.activeCurrency.decimals }
50
+ : null, sameChain: route.sameChain, loading: route.loading, status: route.status }), sameChainEnabled && jsx(SameChainDepositStatus, {}), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] }))] }));
28
51
  };
29
52
 
30
53
  export { DepositCrypto as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -5,6 +5,9 @@ type Props = {
5
5
  activeChain: FundingChain | undefined;
6
6
  activeCurrency: FundingCurrency | undefined;
7
7
  loading: boolean;
8
+ /** Amount to send, in the source token's units. Owned by the parent so the
9
+ * amount input + presets are shared with the mobile (deeplink) path. */
10
+ amount: string;
8
11
  };
9
12
  /**
10
13
  * Desktop "transfer from wallet": send the source token to the Relay deposit
@@ -13,5 +16,5 @@ type Props = {
13
16
  * Openfort embedded account (that one is the destination, not the source).
14
17
  * Once the deposit lands, the funding session's status polling drives progress.
15
18
  */
16
- export declare function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading }: Props): import("react/jsx-runtime").JSX.Element | null;
19
+ export declare function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading, amount }: Props): import("react/jsx-runtime").JSX.Element | null;
17
20
  export {};
@@ -1,10 +1,12 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState } from 'react';
2
+ import { useState, useEffect } from 'react';
3
3
  import { parseUnits, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
4
4
  import { WalletIcon } from '../../../assets/icons.js';
5
5
  import Logos from '../../../assets/logos.js';
6
6
  import { useEthereumBridge } from '../../../ethereum/OpenfortEthereumBridgeContext.js';
7
7
  import { ModalBody } from '../../Common/Modal/styles.js';
8
+ import { ScrollArea } from '../../Common/ScrollArea/index.js';
9
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
8
10
  import { DepositStatus } from '../Deposit/DepositStatus.js';
9
11
  import { walletListBtn } from '../Deposit/formStyles.js';
10
12
  import { ButtonLogo } from '../Deposit/styles.js';
@@ -39,22 +41,6 @@ function walletErrorMessage(e) {
39
41
  }
40
42
  return typeof e === 'string' ? e : 'Transaction failed';
41
43
  }
42
- /** Keep only digits and a single decimal point. */
43
- function sanitizeAmount(v) {
44
- const cleaned = v.replace(/[^0-9.]/g, '');
45
- const [whole, ...rest] = cleaned.split('.');
46
- return rest.length ? `${whole}.${rest.join('')}` : cleaned;
47
- }
48
- const inputStyle = {
49
- width: '100%',
50
- padding: '12px 14px',
51
- borderRadius: 12,
52
- border: '1px solid var(--ck-body-divider, #e4e4e7)',
53
- background: 'var(--ck-body-background-secondary, #fafafa)',
54
- color: 'var(--ck-body-color, #111)',
55
- fontSize: 16,
56
- outline: 'none',
57
- };
58
44
  /**
59
45
  * Desktop "transfer from wallet": send the source token to the Relay deposit
60
46
  * address straight from the user's EXTERNAL browser-extension wallet
@@ -62,12 +48,18 @@ const inputStyle = {
62
48
  * Openfort embedded account (that one is the destination, not the source).
63
49
  * Once the deposit lands, the funding session's status polling drives progress.
64
50
  */
65
- function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading }) {
51
+ function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading, amount }) {
66
52
  const bridge = useEthereumBridge();
67
- const [amount, setAmount] = useState('');
53
+ const { triggerResize } = useOpenfort();
68
54
  const [busyId, setBusyId] = useState(null);
69
55
  const [error, setError] = useState(null);
70
56
  const [sent, setSent] = useState(false);
57
+ // Grow the modal when the busy / error / sent feedback toggles, so the "Confirm
58
+ // in your wallet…" and insufficient-balance messages are revealed instead of
59
+ // clipped below the fold (which reads as "nothing happened" after a click).
60
+ useEffect(() => {
61
+ triggerResize();
62
+ }, [busyId, error, sent, triggerResize]);
71
63
  if (!bridge)
72
64
  return null; // EVM-only (no wagmi) — nothing to send through
73
65
  // bridge.connectors already excludes the Openfort embedded connector, so these
@@ -134,14 +126,14 @@ function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, lo
134
126
  setBusyId(null);
135
127
  }
136
128
  };
137
- return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 14 }, children: [jsx("input", { value: amount, onChange: (e) => setAmount(sanitizeAmount(e.target.value)), placeholder: `Amount${activeCurrency ? ` in ${activeCurrency.symbol}` : ''}`, inputMode: "decimal", style: inputStyle }), connectors.length === 0 && jsx(ModalBody, { children: "No browser wallet detected. Install MetaMask or Rabby." }), connectors.map((c) => (jsxs("button", { type: "button", disabled: !amountValid || busyId !== null || loading, style: {
138
- ...walletListBtn,
139
- display: 'flex',
140
- alignItems: 'center',
141
- justifyContent: 'flex-start',
142
- gap: 10,
143
- opacity: !amountValid || busyId !== null ? 0.55 : 1,
144
- }, onClick: () => sendVia(c), children: [jsx(ButtonLogo, { children: brandLogo(c) }), jsx("span", { children: busyId === c.id ? 'Confirm in your wallet…' : `Send via ${c.name}` })] }, c.id))), sent && jsx(DepositStatus, { status: "waiting_payment" }), error && jsx(ModalBody, { style: { color: '#dc2626' }, children: error })] }));
129
+ return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 10, flex: '1 1 auto', minHeight: 0 }, children: [connectors.length === 0 && jsx(ModalBody, { children: "No browser wallet detected. Install MetaMask or Rabby." }), connectors.length > 0 && (jsx(ScrollArea, { fill: true, children: jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 10 }, children: connectors.map((c) => (jsxs("button", { type: "button", disabled: !amountValid || busyId !== null || loading, style: {
130
+ ...walletListBtn,
131
+ display: 'flex',
132
+ alignItems: 'center',
133
+ justifyContent: 'flex-start',
134
+ gap: 10,
135
+ opacity: !amountValid || busyId !== null ? 0.55 : 1,
136
+ }, onClick: () => sendVia(c), children: [jsx(ButtonLogo, { children: brandLogo(c) }), jsx("span", { children: busyId === c.id ? 'Confirm in your wallet…' : `Send via ${c.name}` })] }, c.id))) }) })), sent && jsx(DepositStatus, { status: "waiting_payment" }), error && jsx(ModalBody, { style: { color: '#dc2626' }, children: error })] }));
145
137
  }
146
138
 
147
139
  export { DepositWalletDesktop };
@@ -1 +1 @@
1
- {"version":3,"file":"DepositWalletDesktop.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"DepositWalletDesktop.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Transfer from wallet. On mobile, leads with open-dApp deeplinks that send the
3
- * user into their wallet app's in-app browser pointed at the hosted deposit page
4
- * (with the address/chain/token/amount prefilled). On desktop, sends straight
5
- * from the browser-extension wallet. Either way the manual deposit-address / QR
6
- * path stays available below.
2
+ * Transfer from wallet. Pick a source chain/token and an amount, then choose the
3
+ * wallet to send from. On mobile that's open-dApp deeplinks into the wallet app's
4
+ * in-app browser (address/chain/token/amount prefilled); on desktop it's a direct
5
+ * send from a browser-extension wallet. The manual deposit-address / QR path stays
6
+ * available below.
7
7
  */
8
8
  declare const DepositWallet: () => import("react/jsx-runtime").JSX.Element;
9
9
  export default DepositWallet;
@@ -2,21 +2,56 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { parseUnits } from 'viem';
4
4
  import Logos from '../../../assets/logos.js';
5
+ import { useEthereumBridge } from '../../../ethereum/OpenfortEthereumBridgeContext.js';
5
6
  import useIsMobile from '../../../hooks/useIsMobile.js';
7
+ import styled from '../../../styles/styled/index.js';
6
8
  import { isIOS } from '../../../utils/index.js';
7
9
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
10
+ import { ScrollArea } from '../../Common/ScrollArea/index.js';
8
11
  import { routes } from '../../Openfort/types.js';
9
12
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
10
13
  import { PageContent } from '../../PageContent/index.js';
14
+ import { Section, SectionLabel, AmountCard, CurrencySymbol, AmountInput, PresetList, PresetButton } from '../Buy/styles.js';
11
15
  import { AddressPageLink } from '../Deposit/AddressPageLink.js';
12
16
  import { isDepositFlowActive, DepositProgress } from '../Deposit/DepositProgress.js';
13
17
  import { walletListBtn } from '../Deposit/formStyles.js';
14
18
  import { RouteSelectors } from '../Deposit/RouteSelectors.js';
19
+ import { isSolana } from '../Deposit/sources.js';
15
20
  import { StepDivider, Skeleton, ButtonLogo } from '../Deposit/styles.js';
21
+ import { TestnetNotice } from '../Deposit/TestnetNotice.js';
22
+ import { UnsupportedNetworkNotice } from '../Deposit/UnsupportedNetworkNotice.js';
16
23
  import { useDepositRoute } from '../Deposit/useDepositRoute.js';
24
+ import { sanitizeAmountInput } from '../Send/utils.js';
17
25
  import { DepositWalletDesktop } from './DepositWalletDesktop.js';
18
26
  import { OPENFORT_DEPOSIT_PAGE_URL, caipToChainId, buildDepositPageUrl, buildOpenDappLinks } from './walletDeeplinks.js';
19
27
 
28
+ // Flex column capped at the modal viewport so the page never overflows
29
+ // InnerContainer (which caps at 88vh and would otherwise scroll the footer off
30
+ // screen). The 112px accounts for the chrome wrapping this element inside the
31
+ // measured PageContents: PageContentStyle's 48px top padding + PageContents'
32
+ // 29px/24px padding (~101px), plus a small safety margin. The providers region
33
+ // is the only part that shrinks/scrolls, which keeps the footer visible.
34
+ const Layout = styled.div `
35
+ display: flex;
36
+ flex-direction: column;
37
+ min-height: 0;
38
+ max-height: calc(88vh - 112px);
39
+ `;
40
+ const TopFixed = styled.div `
41
+ flex-shrink: 0;
42
+ `;
43
+ // Grows to fill the space between the fixed top and footer; its ScrollArea
44
+ // scrolls internally when the wallet list is taller than the available room.
45
+ const ProvidersRegion = styled.div `
46
+ display: flex;
47
+ flex-direction: column;
48
+ flex: 1 1 auto;
49
+ min-height: 0;
50
+ margin-top: 14px;
51
+ `;
52
+ const FixedFooter = styled.div `
53
+ flex-shrink: 0;
54
+ `;
20
55
  /** Wallet-app brand logos keyed by the deeplink `app` id. */
21
56
  const WALLET_LOGO = {
22
57
  metamask: jsx(Logos.MetaMask, { background: true }),
@@ -26,58 +61,66 @@ const WALLET_LOGO = {
26
61
  rainbow: jsx(Logos.Rainbow, { round: true }),
27
62
  rabby: jsx(Logos.Rabby, {}),
28
63
  };
29
- const amountInputStyle = {
30
- width: '100%',
31
- padding: '12px 14px',
32
- borderRadius: 12,
33
- border: '1px solid var(--ck-body-divider, #e4e4e7)',
34
- background: 'var(--ck-body-background-secondary, #fafafa)',
35
- color: 'var(--ck-body-color, #111)',
36
- fontSize: 16,
37
- outline: 'none',
38
- marginTop: 14,
39
- };
40
- /** Keep only digits and a single decimal point. */
41
- function sanitizeAmount(v) {
42
- const cleaned = v.replace(/[^0-9.]/g, '');
43
- const [whole, ...rest] = cleaned.split('.');
44
- return rest.length ? `${whole}.${rest.join('')}` : cleaned;
45
- }
64
+ /** Preset deposit amounts, in the selected source token's units. */
65
+ const PRESETS = [10, 25, 50];
46
66
  /**
47
- * Transfer from wallet. On mobile, leads with open-dApp deeplinks that send the
48
- * user into their wallet app's in-app browser pointed at the hosted deposit page
49
- * (with the address/chain/token/amount prefilled). On desktop, sends straight
50
- * from the browser-extension wallet. Either way the manual deposit-address / QR
51
- * path stays available below.
67
+ * Transfer from wallet. Pick a source chain/token and an amount, then choose the
68
+ * wallet to send from. On mobile that's open-dApp deeplinks into the wallet app's
69
+ * in-app browser (address/chain/token/amount prefilled); on desktop it's a direct
70
+ * send from a browser-extension wallet. The manual deposit-address / QR path stays
71
+ * available below.
52
72
  */
53
73
  const DepositWallet = () => {
54
- var _a, _b, _c, _d, _e, _f, _g, _h;
74
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
55
75
  const { triggerResize, uiConfig } = useOpenfort();
56
76
  const isMobile = useIsMobile();
57
77
  const route = useDepositRoute('crypto');
58
- const [amount, setAmount] = useState('');
78
+ // The desktop send path goes through the wagmi bridge (browser-extension wallets).
79
+ // In Solana-only mode there's no wagmi provider, so fall back to the open-dApp
80
+ // deeplinks for EVM sources too — otherwise the wallet list renders empty.
81
+ const bridge = useEthereumBridge();
82
+ // Prefill a sensible default so the wallet deeplinks are immediately actionable
83
+ // (the funding deposit-address mint uses a fixed nominal amount regardless).
84
+ const [amount, setAmount] = useState('1');
85
+ const [pressedPreset, setPressedPreset] = useState(null);
59
86
  const depositPageUrl = (_b = (_a = uiConfig.funding) === null || _a === void 0 ? void 0 : _a.depositPageUrl) !== null && _b !== void 0 ? _b : OPENFORT_DEPOSIT_PAGE_URL;
60
- const srcChainId = caipToChainId((_c = route.activeChain) === null || _c === void 0 ? void 0 : _c.id);
87
+ // Solana sources have no numeric chain id and no desktop EVM-extension send, so
88
+ // they route through the deeplink (Phantom) on every platform instead of the
89
+ // wagmi-bridge desktop path.
90
+ const isSolanaSrc = isSolana((_d = (_c = route.activeChain) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '');
91
+ const srcChainId = caipToChainId((_e = route.activeChain) === null || _e === void 0 ? void 0 : _e.id);
61
92
  const amountValid = Number.parseFloat(amount) > 0;
93
+ const handleAmountChange = (event) => {
94
+ const raw = sanitizeAmountInput(event.target.value);
95
+ if (raw === '' || /^[0-9]*\.?[0-9]*$/.test(raw)) {
96
+ setPressedPreset(null);
97
+ setAmount(raw);
98
+ }
99
+ };
100
+ const handlePreset = (value) => {
101
+ setPressedPreset(value);
102
+ setAmount(String(value));
103
+ };
62
104
  // Open-dApp deeplinks: prefer backend-provided ones; otherwise build them from
63
105
  // the hosted deposit page URL (if the integrator configured one) carrying the
64
106
  // resolved transfer params. The hosted page sends via the wallet's injected
65
107
  // provider, so no backend wiring is required for the link itself.
66
- const pageUrl = depositPageUrl && route.receiverAddress && route.activeCurrency && srcChainId
108
+ const pageUrl = depositPageUrl && route.receiverAddress && route.activeCurrency && (isSolanaSrc || srcChainId)
67
109
  ? buildDepositPageUrl(depositPageUrl, {
110
+ vm: isSolanaSrc ? 'svm' : 'evm',
68
111
  to: route.receiverAddress,
69
- chainId: srcChainId,
112
+ chainId: isSolanaSrc ? undefined : srcChainId,
70
113
  token: route.activeCurrency.native ? undefined : route.activeCurrency.address,
71
114
  decimals: route.activeCurrency.decimals,
72
115
  symbol: route.activeCurrency.symbol,
73
- chain: (_d = route.activeChain) === null || _d === void 0 ? void 0 : _d.name,
116
+ chain: (_f = route.activeChain) === null || _f === void 0 ? void 0 : _f.name,
74
117
  amount: amountValid ? parseUnits(amount, route.activeCurrency.decimals).toString() : undefined,
75
118
  })
76
119
  : null;
77
- const allDeeplinks = ((_f = (_e = route.pm) === null || _e === void 0 ? void 0 : _e.deeplinks) === null || _f === void 0 ? void 0 : _f.length)
120
+ const allDeeplinks = ((_h = (_g = route.pm) === null || _g === void 0 ? void 0 : _g.deeplinks) === null || _h === void 0 ? void 0 : _h.length)
78
121
  ? route.pm.deeplinks
79
122
  : pageUrl
80
- ? buildOpenDappLinks(pageUrl, (_h = (_g = route.activeChain) === null || _g === void 0 ? void 0 : _g.vmType) !== null && _h !== void 0 ? _h : 'evm')
123
+ ? buildOpenDappLinks(pageUrl, (_k = (_j = route.activeChain) === null || _j === void 0 ? void 0 : _j.vmType) !== null && _k !== void 0 ? _k : 'evm')
81
124
  : [];
82
125
  // Trust's in-app dApp browser was removed on iOS (Apple, 2021) — the link
83
126
  // dead-ends there, so hide it on iOS while keeping it on Android.
@@ -87,15 +130,15 @@ const DepositWallet = () => {
87
130
  }, [route.receiverAddress, route.loading, route.status, deeplinks.length, triggerResize]);
88
131
  if (isDepositFlowActive(route.status))
89
132
  return jsx(DepositProgress, { status: route.status });
90
- return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from wallet" }), jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsx(StepDivider, { children: isMobile ? 'Then open your wallet' : 'Then send from your wallet' }), isMobile ? (jsxs(Fragment, { children: [jsx("input", { value: amount, onChange: (e) => setAmount(sanitizeAmount(e.target.value)), placeholder: `Amount${route.activeCurrency ? ` in ${route.activeCurrency.symbol}` : ''}`, inputMode: "decimal", style: amountInputStyle }), !depositPageUrl && (jsx(ModalBody, { style: { marginTop: 12 }, children: "Use a deposit address below to fund from your wallet." })), route.loading && !route.pm && (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 14 }, children: [jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" })] })), deeplinks.length > 0 && (jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 14 }, children: deeplinks.map((d) => (jsxs("a", { href: amountValid ? d.url : undefined, "aria-disabled": !amountValid, target: "_blank", rel: "noreferrer", style: {
91
- ...walletListBtn,
92
- display: 'flex',
93
- alignItems: 'center',
94
- justifyContent: 'center',
95
- gap: 8,
96
- opacity: amountValid ? 1 : 0.55,
97
- pointerEvents: amountValid ? 'auto' : 'none',
98
- }, children: [WALLET_LOGO[d.app] && jsx(ButtonLogo, { children: WALLET_LOGO[d.app] }), d.label, " \u2197"] }, d.app))) }))] })) : (jsx(DepositWalletDesktop, { receiverAddress: route.receiverAddress, activeChain: route.activeChain, activeCurrency: route.activeCurrency, loading: route.loading })), jsx(AddressPageLink, { label: "Or send to a deposit address" }), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] }));
133
+ return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from wallet" }), jsx(TestnetNotice, {}), route.targetUnsupported && (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })), !route.targetUnsupported && (jsxs(Layout, { children: [jsxs(TopFixed, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsxs(Section, { children: [jsx(SectionLabel, { children: "Amount" }), jsxs(AmountCard, { children: [jsx(CurrencySymbol, { children: (_m = (_l = route.activeCurrency) === null || _l === void 0 ? void 0 : _l.symbol) !== null && _m !== void 0 ? _m : '' }), jsx(AmountInput, { value: amount, onChange: handleAmountChange, placeholder: "0.00", inputMode: "decimal", autoComplete: "off" })] }), jsx(PresetList, { children: PRESETS.map((preset) => (jsx(PresetButton, { type: "button", "$active": pressedPreset === preset, onClick: () => handlePreset(preset), children: preset }, preset))) })] }), jsx(StepDivider, { children: "Then select the wallet you want to use" })] }), jsx(ProvidersRegion, { children: isMobile || isSolanaSrc || !bridge ? (jsxs(Fragment, { children: [!depositPageUrl && (jsx(ModalBody, { style: { marginTop: 12 }, children: "Use a deposit address below to fund from your wallet." })), route.loading && !route.pm && (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" })] })), deeplinks.length > 0 && (jsx(ScrollArea, { fill: true, children: jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: deeplinks.map((d) => (jsxs("a", { href: amountValid ? d.url : undefined, "aria-disabled": !amountValid, target: "_blank", rel: "noreferrer", style: {
134
+ ...walletListBtn,
135
+ display: 'flex',
136
+ alignItems: 'center',
137
+ justifyContent: 'center',
138
+ gap: 8,
139
+ opacity: amountValid ? 1 : 0.55,
140
+ pointerEvents: amountValid ? 'auto' : 'none',
141
+ }, children: [WALLET_LOGO[d.app] && jsx(ButtonLogo, { children: WALLET_LOGO[d.app] }), d.label, " \u2197"] }, d.app))) }) }))] })) : (jsx(DepositWalletDesktop, { receiverAddress: route.receiverAddress, activeChain: route.activeChain, activeCurrency: route.activeCurrency, loading: route.loading, amount: amount })) }), jsxs(FixedFooter, { children: [jsx(AddressPageLink, { label: "Or send to a deposit address" }), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] })] }))] }));
99
142
  };
100
143
 
101
144
  export { DepositWallet as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -23,11 +23,13 @@ export type VmType = 'evm' | 'svm';
23
23
  */
24
24
  export declare const OPENFORT_DEPOSIT_PAGE_URL = "https://deposit.openfort.io";
25
25
  type DepositPageParams = {
26
- /** Relay deposit address the source funds go to. */
26
+ /** Source VM family. `evm` drives a window.ethereum send; `svm` a Solana Pay request. */
27
+ vm: VmType;
28
+ /** Relay deposit address the source funds go to (0x for EVM, base58 for Solana). */
27
29
  to: string;
28
- /** Numeric source chain id (e.g. 42161). */
29
- chainId: number;
30
- /** ERC-20 contract, or empty/undefined for the chain's native token. */
30
+ /** Numeric source chain id (e.g. 42161). EVM only. */
31
+ chainId?: number;
32
+ /** ERC-20 contract / SPL mint, or empty/undefined for the chain's native token. */
31
33
  token?: string;
32
34
  decimals: number;
33
35
  symbol: string;
@@ -19,8 +19,10 @@ const OPENFORT_DEPOSIT_PAGE_URL = 'https://deposit.openfort.io';
19
19
  /** Build the deposit "send" page URL with the transfer params encoded. */
20
20
  function buildDepositPageUrl(baseUrl, p) {
21
21
  const url = new URL(baseUrl);
22
+ url.searchParams.set('vm', p.vm);
22
23
  url.searchParams.set('to', p.to);
23
- url.searchParams.set('chainId', String(p.chainId));
24
+ if (p.vm === 'evm' && p.chainId !== undefined)
25
+ url.searchParams.set('chainId', String(p.chainId));
24
26
  if (p.token)
25
27
  url.searchParams.set('token', p.token);
26
28
  url.searchParams.set('decimals', String(p.decimals));
@@ -1 +1 @@
1
- {"version":3,"file":"walletDeeplinks.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"walletDeeplinks.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1 @@
1
+ export declare const SolanaSelectToken: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useEffect, useState } from 'react';
3
+ import { formatUnits } from 'viem';
4
+ import { TOKEN_LOGO, symbolToColor } from '../../../constants/logos.js';
5
+ import { useSolanaWalletAssets } from '../../../solana/hooks/useSolanaWalletAssets.js';
6
+ import { ModalHeading } from '../../Common/Modal/styles.js';
7
+ import { routes } from '../../Openfort/types.js';
8
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
9
+ import { SelectTokenContent, EmptyState, TokenList, TokenButton, TokenLeftGroup, TokenInfo, TokenSymbol, TokenName, TokenBalance, TokenLogoArea, TokenLogoImg, TokenLogoFallback } from './styles.js';
10
+
11
+ const ZERO = BigInt(0);
12
+ function SolanaTokenLogo({ symbol }) {
13
+ var _a;
14
+ const [imgError, setImgError] = useState(false);
15
+ const url = (_a = TOKEN_LOGO[symbol.toUpperCase()]) !== null && _a !== void 0 ? _a : null;
16
+ return (jsx(TokenLogoArea, { children: url && !imgError ? (jsx(TokenLogoImg, { src: url, alt: symbol, onError: () => setImgError(true) })) : (jsx(TokenLogoFallback, { "$bg": symbolToColor(symbol), children: symbol.charAt(0).toUpperCase() })) }));
17
+ }
18
+ const SolanaSelectToken = () => {
19
+ const { setSendForm, setRoute, triggerResize } = useOpenfort();
20
+ const { data, isLoading } = useSolanaWalletAssets();
21
+ const tokens = (data !== null && data !== void 0 ? data : []).filter((t) => t.amount > ZERO);
22
+ useEffect(() => {
23
+ if (!isLoading)
24
+ triggerResize();
25
+ }, [isLoading, triggerResize]);
26
+ const handleSelect = (token) => {
27
+ const asset = token.isNative
28
+ ? {
29
+ type: 'native',
30
+ balance: token.amount,
31
+ metadata: { symbol: 'SOL', decimals: token.decimals, fiat: { value: 0, currency: 'USD' } },
32
+ }
33
+ : {
34
+ type: 'spl',
35
+ address: token.mint,
36
+ balance: token.amount,
37
+ metadata: { symbol: token.symbol, name: token.name, decimals: token.decimals },
38
+ };
39
+ setSendForm((prev) => ({ ...prev, asset, amount: '' }));
40
+ setRoute(routes.SOL_SEND);
41
+ };
42
+ return (jsxs(SelectTokenContent, { onBack: routes.SOL_SEND, children: [jsx(ModalHeading, { children: "Select asset" }), tokens.length === 0 ? (jsx(EmptyState, { children: isLoading ? 'Loading balances…' : 'No assets found' })) : (jsx(TokenList, { children: tokens.map((token) => {
43
+ const amount = Number(formatUnits(token.amount, token.decimals));
44
+ const balanceStr = `${amount.toLocaleString('en-US', { maximumFractionDigits: 4 })} ${token.symbol}`;
45
+ return (jsxs(TokenButton, { type: "button", onClick: () => handleSelect(token), children: [jsxs(TokenLeftGroup, { children: [jsx(SolanaTokenLogo, { symbol: token.symbol }), jsxs(TokenInfo, { style: { textAlign: 'left' }, children: [jsx(TokenSymbol, { children: token.name }), jsx(TokenName, { children: token.symbol })] })] }), jsx(TokenBalance, { children: balanceStr })] }, token.mint));
46
+ }) }))] }));
47
+ };
48
+
49
+ export { SolanaSelectToken };
50
+ //# sourceMappingURL=SolanaSelectToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolanaSelectToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}