@openfort/react 1.1.4 → 1.3.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 (182) hide show
  1. package/build/assets/icons.js +1 -1
  2. package/build/assets/logos.d.ts +15 -0
  3. package/build/assets/logos.js +9 -0
  4. package/build/assets/logos.js.map +1 -1
  5. package/build/components/Common/CopyToClipboard/CopyIconButton.d.ts +3 -1
  6. package/build/components/Common/CopyToClipboard/CopyIconButton.js +4 -4
  7. package/build/components/Common/CustomQRCode/index.d.ts +1 -1
  8. package/build/components/Common/CustomQRCode/index.js +3 -3
  9. package/build/components/Common/CustomQRCode/styles.d.ts +1 -0
  10. package/build/components/Common/CustomQRCode/styles.js +4 -4
  11. package/build/components/Common/CustomQRCode/types.d.ts +2 -0
  12. package/build/components/Common/Modal/styles.js +4 -1
  13. package/build/components/Common/Modal/styles.js.map +1 -1
  14. package/build/components/Common/SolanaChain/index.d.ts +8 -0
  15. package/build/components/Common/SolanaChain/index.js +40 -0
  16. package/build/components/Common/SolanaChain/index.js.map +1 -0
  17. package/build/components/ConnectModal/index.js +16 -2
  18. package/build/components/ConnectModal/index.js.map +1 -1
  19. package/build/components/Openfort/OpenfortProvider.js +4 -1
  20. package/build/components/Openfort/OpenfortProvider.js.map +1 -1
  21. package/build/components/Openfort/types.d.ts +92 -0
  22. package/build/components/Openfort/types.js +23 -1
  23. package/build/components/Openfort/types.js.map +1 -1
  24. package/build/components/Pages/AssetInventory/SolanaAssetInventory.d.ts +6 -0
  25. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js +42 -0
  26. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js.map +1 -0
  27. package/build/components/Pages/Buy/coinbaseApi.d.ts +1 -1
  28. package/build/components/Pages/Buy/coinbaseApi.js +2 -13
  29. package/build/components/Pages/Buy/coinbaseApi.js.map +1 -1
  30. package/build/components/Pages/Buy/evmCurrencies.d.ts +11 -0
  31. package/build/components/Pages/Buy/evmCurrencies.js +27 -0
  32. package/build/components/Pages/Buy/evmCurrencies.js.map +1 -0
  33. package/build/components/Pages/Buy/index.js +11 -3
  34. package/build/components/Pages/Buy/index.js.map +1 -1
  35. package/build/components/Pages/Buy/onrampApi.d.ts +8 -1
  36. package/build/components/Pages/Buy/onrampApi.js +24 -14
  37. package/build/components/Pages/Buy/onrampApi.js.map +1 -1
  38. package/build/components/Pages/Buy/solanaCurrencies.d.ts +9 -0
  39. package/build/components/Pages/Buy/solanaCurrencies.js +25 -0
  40. package/build/components/Pages/Buy/solanaCurrencies.js.map +1 -0
  41. package/build/components/Pages/Buy/stripeApi.d.ts +1 -1
  42. package/build/components/Pages/Buy/stripeApi.js +2 -13
  43. package/build/components/Pages/Buy/stripeApi.js.map +1 -1
  44. package/build/components/Pages/BuyComplete/index.js +7 -1
  45. package/build/components/Pages/BuyComplete/index.js.map +1 -1
  46. package/build/components/Pages/BuyProcessing/index.js +9 -5
  47. package/build/components/Pages/BuyProcessing/index.js.map +1 -1
  48. package/build/components/Pages/BuySelectProvider/index.js +11 -6
  49. package/build/components/Pages/BuySelectProvider/index.js.map +1 -1
  50. package/build/components/Pages/Connected/EthereumConnected.js +8 -32
  51. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  52. package/build/components/Pages/Connected/SolanaConnected.js +11 -5
  53. package/build/components/Pages/Connected/SolanaConnected.js.map +1 -1
  54. package/build/components/Pages/Deposit/AddressPageLink.d.ts +7 -0
  55. package/build/components/Pages/Deposit/AddressPageLink.js +17 -0
  56. package/build/components/Pages/Deposit/AddressPageLink.js.map +1 -0
  57. package/build/components/Pages/Deposit/AssetChainLogo.d.ts +9 -0
  58. package/build/components/Pages/Deposit/AssetChainLogo.js +24 -0
  59. package/build/components/Pages/Deposit/AssetChainLogo.js.map +1 -0
  60. package/build/components/Pages/Deposit/DepositAddressBlock.d.ts +21 -0
  61. package/build/components/Pages/Deposit/DepositAddressBlock.js +28 -0
  62. package/build/components/Pages/Deposit/DepositAddressBlock.js.map +1 -0
  63. package/build/components/Pages/Deposit/DepositProgress.d.ts +15 -0
  64. package/build/components/Pages/Deposit/DepositProgress.js +111 -0
  65. package/build/components/Pages/Deposit/DepositProgress.js.map +1 -0
  66. package/build/components/Pages/Deposit/DepositStatus.d.ts +9 -0
  67. package/build/components/Pages/Deposit/DepositStatus.js +43 -0
  68. package/build/components/Pages/Deposit/DepositStatus.js.map +1 -0
  69. package/build/components/Pages/Deposit/DepositSuccess.d.ts +6 -0
  70. package/build/components/Pages/Deposit/DepositSuccess.js +25 -0
  71. package/build/components/Pages/Deposit/DepositSuccess.js.map +1 -0
  72. package/build/components/Pages/Deposit/Details.d.ts +12 -0
  73. package/build/components/Pages/Deposit/Details.js +40 -0
  74. package/build/components/Pages/Deposit/Details.js.map +1 -0
  75. package/build/components/Pages/Deposit/LogoSelect.d.ts +12 -0
  76. package/build/components/Pages/Deposit/LogoSelect.js +95 -0
  77. package/build/components/Pages/Deposit/LogoSelect.js.map +1 -0
  78. package/build/components/Pages/Deposit/OrDivider.d.ts +2 -0
  79. package/build/components/Pages/Deposit/OrDivider.js +10 -0
  80. package/build/components/Pages/Deposit/OrDivider.js.map +1 -0
  81. package/build/components/Pages/Deposit/RouteSelectors.d.ts +13 -0
  82. package/build/components/Pages/Deposit/RouteSelectors.js +19 -0
  83. package/build/components/Pages/Deposit/RouteSelectors.js.map +1 -0
  84. package/build/components/Pages/Deposit/cexChains.d.ts +9 -0
  85. package/build/components/Pages/Deposit/cexChains.js +23 -0
  86. package/build/components/Pages/Deposit/cexChains.js.map +1 -0
  87. package/build/components/Pages/Deposit/formStyles.d.ts +24 -0
  88. package/build/components/Pages/Deposit/formStyles.js +83 -0
  89. package/build/components/Pages/Deposit/formStyles.js.map +1 -0
  90. package/build/components/Pages/Deposit/index.d.ts +7 -0
  91. package/build/components/Pages/Deposit/index.js +114 -0
  92. package/build/components/Pages/Deposit/index.js.map +1 -0
  93. package/build/components/Pages/Deposit/paymentOptions.d.ts +49 -0
  94. package/build/components/Pages/Deposit/paymentOptions.js +63 -0
  95. package/build/components/Pages/Deposit/paymentOptions.js.map +1 -0
  96. package/build/components/Pages/Deposit/sources.d.ts +17 -0
  97. package/build/components/Pages/Deposit/sources.js +22 -0
  98. package/build/components/Pages/Deposit/sources.js.map +1 -0
  99. package/build/components/Pages/Deposit/styles.d.ts +25 -0
  100. package/build/components/Pages/Deposit/styles.js +167 -0
  101. package/build/components/Pages/Deposit/styles.js.map +1 -0
  102. package/build/components/Pages/Deposit/useDepositRoute.d.ts +34 -0
  103. package/build/components/Pages/Deposit/useDepositRoute.js +106 -0
  104. package/build/components/Pages/Deposit/useDepositRoute.js.map +1 -0
  105. package/build/components/Pages/Deposit/useFundingTarget.d.ts +11 -0
  106. package/build/components/Pages/Deposit/useFundingTarget.js +25 -0
  107. package/build/components/Pages/Deposit/useFundingTarget.js.map +1 -0
  108. package/build/components/Pages/DepositCex/index.d.ts +11 -0
  109. package/build/components/Pages/DepositCex/index.js +229 -0
  110. package/build/components/Pages/DepositCex/index.js.map +1 -0
  111. package/build/components/Pages/DepositCrypto/index.d.ts +8 -0
  112. package/build/components/Pages/DepositCrypto/index.js +31 -0
  113. package/build/components/Pages/DepositCrypto/index.js.map +1 -0
  114. package/build/components/Pages/DepositWallet/DepositWalletDesktop.d.ts +20 -0
  115. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js +139 -0
  116. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js.map +1 -0
  117. package/build/components/Pages/DepositWallet/index.d.ts +9 -0
  118. package/build/components/Pages/DepositWallet/index.js +107 -0
  119. package/build/components/Pages/DepositWallet/index.js.map +1 -0
  120. package/build/components/Pages/DepositWallet/walletDeeplinks.d.ts +50 -0
  121. package/build/components/Pages/DepositWallet/walletDeeplinks.js +109 -0
  122. package/build/components/Pages/DepositWallet/walletDeeplinks.js.map +1 -0
  123. package/build/components/Pages/ExportKey/index.js +10 -2
  124. package/build/components/Pages/ExportKey/index.js.map +1 -1
  125. package/build/components/Pages/NoAssetsAvailable/index.js +5 -21
  126. package/build/components/Pages/NoAssetsAvailable/index.js.map +1 -1
  127. package/build/components/Pages/SelectToken/SolanaSelectToken.d.ts +1 -0
  128. package/build/components/Pages/SelectToken/SolanaSelectToken.js +50 -0
  129. package/build/components/Pages/SelectToken/SolanaSelectToken.js.map +1 -0
  130. package/build/components/Pages/SelectToken/index.js +13 -2
  131. package/build/components/Pages/SelectToken/index.js.map +1 -1
  132. package/build/components/Pages/SelectToken/styles.js +1 -1
  133. package/build/components/Pages/Send/SolanaSend.d.ts +1 -0
  134. package/build/components/Pages/Send/SolanaSend.js +89 -0
  135. package/build/components/Pages/Send/SolanaSend.js.map +1 -0
  136. package/build/components/Pages/Send/index.d.ts +2 -1
  137. package/build/components/Pages/Send/index.js +0 -1
  138. package/build/components/Pages/Send/index.js.map +1 -1
  139. package/build/components/Pages/Send/utils.js +4 -1
  140. package/build/components/Pages/Send/utils.js.map +1 -1
  141. package/build/components/Pages/SendConfirmation/EstimatedFees.js +5 -3
  142. package/build/components/Pages/SendConfirmation/EstimatedFees.js.map +1 -1
  143. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.d.ts +1 -0
  144. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +121 -0
  145. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js.map +1 -0
  146. package/build/components/Pages/SendConfirmation/index.js +4 -3
  147. package/build/components/Pages/SendConfirmation/index.js.map +1 -1
  148. package/build/constants/logos.js +1 -0
  149. package/build/constants/logos.js.map +1 -1
  150. package/build/ethereum/hooks/useEthereumWalletAssets.js +212 -95
  151. package/build/ethereum/hooks/useEthereumWalletAssets.js.map +1 -1
  152. package/build/hooks/openfort/fundingClient.d.ts +34 -0
  153. package/build/hooks/openfort/fundingClient.js +60 -0
  154. package/build/hooks/openfort/fundingClient.js.map +1 -0
  155. package/build/hooks/openfort/useFunding.d.ts +159 -0
  156. package/build/hooks/openfort/useFunding.js +204 -0
  157. package/build/hooks/openfort/useFunding.js.map +1 -0
  158. package/build/hooks/openfort/useFundingChains.d.ts +49 -0
  159. package/build/hooks/openfort/useFundingChains.js +100 -0
  160. package/build/hooks/openfort/useFundingChains.js.map +1 -0
  161. package/build/hooks/useBalance.js +6 -1
  162. package/build/hooks/useBalance.js.map +1 -1
  163. package/build/index.d.ts +4 -1
  164. package/build/index.js +2 -1
  165. package/build/index.js.map +1 -1
  166. package/build/shared/hooks/useAsyncData.d.ts +11 -0
  167. package/build/shared/hooks/useAsyncData.js +73 -13
  168. package/build/shared/hooks/useAsyncData.js.map +1 -1
  169. package/build/solana/hooks/useSolanaWalletAssets.d.ts +24 -0
  170. package/build/solana/hooks/useSolanaWalletAssets.js +86 -0
  171. package/build/solana/hooks/useSolanaWalletAssets.js.map +1 -0
  172. package/build/solana/transfer.d.ts +61 -0
  173. package/build/solana/transfer.js +219 -0
  174. package/build/solana/transfer.js.map +1 -0
  175. package/build/solana/types.d.ts +8 -0
  176. package/build/utils/index.d.ts +2 -1
  177. package/build/utils/index.js +1 -1
  178. package/build/version.d.ts +1 -1
  179. package/build/version.js +1 -1
  180. package/build/wagmi/defaultConnectors.js +5 -1
  181. package/build/wagmi/defaultConnectors.js.map +1 -1
  182. package/package.json +13 -1
@@ -0,0 +1,9 @@
1
+ /**
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
+ */
8
+ declare const DepositWallet: () => import("react/jsx-runtime").JSX.Element;
9
+ export default DepositWallet;
@@ -0,0 +1,107 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { parseUnits } from 'viem';
4
+ import Logos from '../../../assets/logos.js';
5
+ import useIsMobile from '../../../hooks/useIsMobile.js';
6
+ import { isIOS } from '../../../utils/index.js';
7
+ import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
8
+ import { routes } from '../../Openfort/types.js';
9
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
10
+ import { PageContent } from '../../PageContent/index.js';
11
+ import { Section, SectionLabel, AmountCard, CurrencySymbol, AmountInput, PresetList, PresetButton } from '../Buy/styles.js';
12
+ import { AddressPageLink } from '../Deposit/AddressPageLink.js';
13
+ import { isDepositFlowActive, DepositProgress } from '../Deposit/DepositProgress.js';
14
+ import { walletListBtn } from '../Deposit/formStyles.js';
15
+ import { RouteSelectors } from '../Deposit/RouteSelectors.js';
16
+ import { isSolana } from '../Deposit/sources.js';
17
+ import { StepDivider, Skeleton, ButtonLogo } from '../Deposit/styles.js';
18
+ import { useDepositRoute } from '../Deposit/useDepositRoute.js';
19
+ import { sanitizeAmountInput } from '../Send/utils.js';
20
+ import { DepositWalletDesktop } from './DepositWalletDesktop.js';
21
+ import { OPENFORT_DEPOSIT_PAGE_URL, caipToChainId, buildDepositPageUrl, buildOpenDappLinks } from './walletDeeplinks.js';
22
+
23
+ /** Wallet-app brand logos keyed by the deeplink `app` id. */
24
+ const WALLET_LOGO = {
25
+ metamask: jsx(Logos.MetaMask, { background: true }),
26
+ coinbase: jsx(Logos.Coinbase, { background: true }),
27
+ phantom: jsx(Logos.Phantom, { background: true }),
28
+ trust: jsx(Logos.Trust, {}),
29
+ rainbow: jsx(Logos.Rainbow, { round: true }),
30
+ rabby: jsx(Logos.Rabby, {}),
31
+ };
32
+ /** Preset deposit amounts, in the selected source token's units. */
33
+ const PRESETS = [10, 25, 50];
34
+ /**
35
+ * Transfer from wallet. Pick a source chain/token and an amount, then choose the
36
+ * wallet to send from. On mobile that's open-dApp deeplinks into the wallet app's
37
+ * in-app browser (address/chain/token/amount prefilled); on desktop it's a direct
38
+ * send from a browser-extension wallet. The manual deposit-address / QR path stays
39
+ * available below.
40
+ */
41
+ const DepositWallet = () => {
42
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
43
+ const { triggerResize, uiConfig } = useOpenfort();
44
+ const isMobile = useIsMobile();
45
+ const route = useDepositRoute('crypto');
46
+ const [amount, setAmount] = useState('');
47
+ const [pressedPreset, setPressedPreset] = useState(null);
48
+ const depositPageUrl = (_b = (_a = uiConfig.funding) === null || _a === void 0 ? void 0 : _a.depositPageUrl) !== null && _b !== void 0 ? _b : OPENFORT_DEPOSIT_PAGE_URL;
49
+ // Solana sources have no numeric chain id and no desktop EVM-extension send, so
50
+ // they route through the deeplink (Phantom) on every platform instead of the
51
+ // wagmi-bridge desktop path.
52
+ const isSolanaSrc = isSolana((_d = (_c = route.activeChain) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '');
53
+ const srcChainId = caipToChainId((_e = route.activeChain) === null || _e === void 0 ? void 0 : _e.id);
54
+ const amountValid = Number.parseFloat(amount) > 0;
55
+ const handleAmountChange = (event) => {
56
+ const raw = sanitizeAmountInput(event.target.value);
57
+ if (raw === '' || /^[0-9]*\.?[0-9]*$/.test(raw)) {
58
+ setPressedPreset(null);
59
+ setAmount(raw);
60
+ }
61
+ };
62
+ const handlePreset = (value) => {
63
+ setPressedPreset(value);
64
+ setAmount(String(value));
65
+ };
66
+ // Open-dApp deeplinks: prefer backend-provided ones; otherwise build them from
67
+ // the hosted deposit page URL (if the integrator configured one) carrying the
68
+ // resolved transfer params. The hosted page sends via the wallet's injected
69
+ // provider, so no backend wiring is required for the link itself.
70
+ const pageUrl = depositPageUrl && route.receiverAddress && route.activeCurrency && (isSolanaSrc || srcChainId)
71
+ ? buildDepositPageUrl(depositPageUrl, {
72
+ vm: isSolanaSrc ? 'svm' : 'evm',
73
+ to: route.receiverAddress,
74
+ chainId: isSolanaSrc ? undefined : srcChainId,
75
+ token: route.activeCurrency.native ? undefined : route.activeCurrency.address,
76
+ decimals: route.activeCurrency.decimals,
77
+ symbol: route.activeCurrency.symbol,
78
+ chain: (_f = route.activeChain) === null || _f === void 0 ? void 0 : _f.name,
79
+ amount: amountValid ? parseUnits(amount, route.activeCurrency.decimals).toString() : undefined,
80
+ })
81
+ : null;
82
+ const allDeeplinks = ((_h = (_g = route.pm) === null || _g === void 0 ? void 0 : _g.deeplinks) === null || _h === void 0 ? void 0 : _h.length)
83
+ ? route.pm.deeplinks
84
+ : pageUrl
85
+ ? buildOpenDappLinks(pageUrl, (_k = (_j = route.activeChain) === null || _j === void 0 ? void 0 : _j.vmType) !== null && _k !== void 0 ? _k : 'evm')
86
+ : [];
87
+ // Trust's in-app dApp browser was removed on iOS (Apple, 2021) — the link
88
+ // dead-ends there, so hide it on iOS while keeping it on Android.
89
+ const deeplinks = isIOS() ? allDeeplinks.filter((d) => d.app !== 'trust') : allDeeplinks;
90
+ useEffect(() => {
91
+ triggerResize();
92
+ }, [route.receiverAddress, route.loading, route.status, deeplinks.length, triggerResize]);
93
+ if (isDepositFlowActive(route.status))
94
+ return jsx(DepositProgress, { status: route.status });
95
+ 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." }), 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" }), isMobile || isSolanaSrc ? (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, 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: {
96
+ ...walletListBtn,
97
+ display: 'flex',
98
+ alignItems: 'center',
99
+ justifyContent: 'center',
100
+ gap: 8,
101
+ opacity: amountValid ? 1 : 0.55,
102
+ pointerEvents: amountValid ? 'auto' : 'none',
103
+ }, 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 })), jsx(AddressPageLink, { label: "Or send to a deposit address" }), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] }));
104
+ };
105
+
106
+ export { DepositWallet as default };
107
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Mobile "open-dApp" wallet deeplinks (the Daimo pattern).
3
+ *
4
+ * Each wallet exposes a universal link that opens an arbitrary https URL inside
5
+ * its own in-app dApp browser. We point every one at the standalone deposit
6
+ * "send" page ({@link buildDepositPageUrl}) carrying the deposit address, chain,
7
+ * token and amount as query params. The user taps their wallet, the page opens
8
+ * with `window.ethereum` injected, and confirms the transfer there.
9
+ *
10
+ * The page URL is configured by the integrator via `uiConfig.funding.depositPageUrl`
11
+ * (the deployed `deposit.html`). Without it, no deeplinks are produced.
12
+ */
13
+ type WalletDeeplink = {
14
+ app: string;
15
+ label: string;
16
+ url: string;
17
+ };
18
+ export type VmType = 'evm' | 'svm';
19
+ /**
20
+ * Default hosted deposit "send" page (Openfort-operated). Used when the
21
+ * integrator doesn't set `uiConfig.funding.depositPageUrl`, so one-tap mobile
22
+ * wallet deeplinks work out of the box.
23
+ */
24
+ export declare const OPENFORT_DEPOSIT_PAGE_URL = "https://deposit.openfort.io";
25
+ type DepositPageParams = {
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). */
29
+ to: string;
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. */
33
+ token?: string;
34
+ decimals: number;
35
+ symbol: string;
36
+ /** Human-readable chain name shown on the page. */
37
+ chain?: string;
38
+ /** Optional preset amount in base units (uint256 string). */
39
+ amount?: string;
40
+ };
41
+ /** Build the deposit "send" page URL with the transfer params encoded. */
42
+ export declare function buildDepositPageUrl(baseUrl: string, p: DepositPageParams): string;
43
+ /** "eip155:8453" -> 8453 (undefined if not a numeric CAIP-2 reference). */
44
+ export declare function caipToChainId(caip2?: string): number | undefined;
45
+ /**
46
+ * Open-dApp deeplinks for every wallet matching `vmType`, each wrapping the
47
+ * deposit send page so it loads in that wallet's in-app browser.
48
+ */
49
+ export declare function buildOpenDappLinks(pageUrl: string, vmType: VmType): WalletDeeplink[];
50
+ export {};
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Mobile "open-dApp" wallet deeplinks (the Daimo pattern).
3
+ *
4
+ * Each wallet exposes a universal link that opens an arbitrary https URL inside
5
+ * its own in-app dApp browser. We point every one at the standalone deposit
6
+ * "send" page ({@link buildDepositPageUrl}) carrying the deposit address, chain,
7
+ * token and amount as query params. The user taps their wallet, the page opens
8
+ * with `window.ethereum` injected, and confirms the transfer there.
9
+ *
10
+ * The page URL is configured by the integrator via `uiConfig.funding.depositPageUrl`
11
+ * (the deployed `deposit.html`). Without it, no deeplinks are produced.
12
+ */
13
+ /**
14
+ * Default hosted deposit "send" page (Openfort-operated). Used when the
15
+ * integrator doesn't set `uiConfig.funding.depositPageUrl`, so one-tap mobile
16
+ * wallet deeplinks work out of the box.
17
+ */
18
+ const OPENFORT_DEPOSIT_PAGE_URL = 'https://deposit.openfort.io';
19
+ /** Build the deposit "send" page URL with the transfer params encoded. */
20
+ function buildDepositPageUrl(baseUrl, p) {
21
+ const url = new URL(baseUrl);
22
+ url.searchParams.set('vm', p.vm);
23
+ url.searchParams.set('to', p.to);
24
+ if (p.vm === 'evm' && p.chainId !== undefined)
25
+ url.searchParams.set('chainId', String(p.chainId));
26
+ if (p.token)
27
+ url.searchParams.set('token', p.token);
28
+ url.searchParams.set('decimals', String(p.decimals));
29
+ url.searchParams.set('symbol', p.symbol);
30
+ if (p.chain)
31
+ url.searchParams.set('chain', p.chain);
32
+ if (p.amount)
33
+ url.searchParams.set('amount', p.amount);
34
+ return url.toString();
35
+ }
36
+ /** "eip155:8453" -> 8453 (undefined if not a numeric CAIP-2 reference). */
37
+ function caipToChainId(caip2) {
38
+ if (!caip2)
39
+ return undefined;
40
+ const n = Number(caip2.split(':')[1]);
41
+ return Number.isFinite(n) ? n : undefined;
42
+ }
43
+ const stripScheme = (u) => u.replace(/^https?:\/\//, '');
44
+ const enc = encodeURIComponent;
45
+ const originOf = (u) => {
46
+ try {
47
+ return new URL(u).origin;
48
+ }
49
+ catch {
50
+ return '';
51
+ }
52
+ };
53
+ // Open-dApp universal links. Each format verified (2026) against the wallet's
54
+ // docs/source plus a second independent source. `pageUrl` is the deposit send
55
+ // page; most want it URL-encoded, MetaMask wants the https:// scheme stripped.
56
+ const WALLETS = [
57
+ {
58
+ app: 'metamask',
59
+ label: 'Open MetaMask',
60
+ vms: ['evm'],
61
+ build: (u) => `https://link.metamask.io/dapp/${stripScheme(u)}`,
62
+ },
63
+ {
64
+ app: 'coinbase',
65
+ label: 'Open Coinbase Wallet',
66
+ vms: ['evm'],
67
+ build: (u) => `https://go.cb-w.com/dapp?cb_url=${enc(u)}`,
68
+ },
69
+ {
70
+ app: 'trust',
71
+ label: 'Open Trust Wallet',
72
+ vms: ['evm'],
73
+ // coin_id=60 = Ethereum (SLIP-44). In-app dApp browser is Android-only;
74
+ // iOS shows "Deep Link not Supported" (Apple removed it in 2021).
75
+ build: (u) => `https://link.trustwallet.com/open_url?coin_id=60&url=${enc(u)}`,
76
+ },
77
+ {
78
+ app: 'rainbow',
79
+ label: 'Open Rainbow',
80
+ vms: ['evm'],
81
+ build: (u) => `https://rainbow.me/dapp?url=${enc(u)}`,
82
+ },
83
+ {
84
+ app: 'rabby',
85
+ label: 'Open Rabby',
86
+ vms: ['evm'],
87
+ build: (u) => `https://go.rabby.io/mobile/?_cmd=open-dapp&dapp=${enc(u)}`,
88
+ },
89
+ {
90
+ app: 'phantom',
91
+ label: 'Open Phantom',
92
+ vms: ['evm', 'svm'],
93
+ build: (u) => `https://phantom.app/ul/browse/${enc(u)}?ref=${enc(originOf(u))}`,
94
+ },
95
+ ];
96
+ /**
97
+ * Open-dApp deeplinks for every wallet matching `vmType`, each wrapping the
98
+ * deposit send page so it loads in that wallet's in-app browser.
99
+ */
100
+ function buildOpenDappLinks(pageUrl, vmType) {
101
+ return WALLETS.filter((w) => w.vms.includes(vmType)).map((w) => ({
102
+ app: w.app,
103
+ label: w.label,
104
+ url: w.build(pageUrl),
105
+ }));
106
+ }
107
+
108
+ export { OPENFORT_DEPOSIT_PAGE_URL, buildDepositPageUrl, buildOpenDappLinks, caipToChainId };
109
+ //# sourceMappingURL=walletDeeplinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walletDeeplinks.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -10,7 +10,15 @@ import { PageContent } from '../../PageContent/index.js';
10
10
 
11
11
  // TODO: Localize
12
12
  const ExportKey = () => {
13
- const { exportPrivateKey } = useEthereumEmbeddedWallet();
13
+ var _a, _b;
14
+ const wallet = useEthereumEmbeddedWallet();
15
+ const { exportPrivateKey } = wallet;
16
+ // A smart account is a contract (no private key); the exportable key is its
17
+ // owner/signer EOA — a different address that won't show the account's funds
18
+ // when imported into another wallet.
19
+ const accountAddress = (_a = wallet.activeWallet) === null || _a === void 0 ? void 0 : _a.address;
20
+ const ownerAddress = (_b = wallet.activeWallet) === null || _b === void 0 ? void 0 : _b.ownerAddress;
21
+ const isSmartAccount = Boolean(ownerAddress && accountAddress && ownerAddress.toLowerCase() !== accountAddress.toLowerCase());
14
22
  const [exportedKey, setExportedKey] = useState(null);
15
23
  const [exportError, setExportError] = useState(null);
16
24
  const [showExportedKey, setShowExportedKey] = useState(false);
@@ -37,7 +45,7 @@ const ExportKey = () => {
37
45
  logo: jsx(KeyIcon, {}),
38
46
  }, logoBottomLeft: {
39
47
  logo: jsx(KeyIcon, {}),
40
- } }), jsxs(ModalContent, { children: [jsxs(ModalBody, { children: [jsx("p", { style: { marginBottom: 6 }, children: "With your private key, you can access your account outside this application." }), jsx("p", { children: "Keep it safe and never share it with anyone you don't trust." })] }), !showExportedKey ? (jsx(Button, { onClick: () => setShowExportedKey(true), style: { marginTop: 12 }, children: "Export key" })) : exportError ? (jsx(ModalBody, { style: { marginTop: 12 }, "$error": true, children: exportError })) : exportedKey ? (jsx("div", { style: { marginTop: 12 }, children: jsxs(CopyText, { value: exportedKey, children: [exportedKey.slice(0, 10), "...", exportedKey.slice(-10)] }) })) : (jsx(Fragment, { children: "Loading..." }))] })] }));
48
+ } }), jsxs(ModalContent, { children: [jsxs(ModalBody, { children: [isSmartAccount ? (jsxs("p", { style: { marginBottom: 6 }, children: ["This is your account's ", jsx("strong", { children: "owner (signer) key" }), " \u2014 not the account itself. Your smart account", accountAddress ? ` (${accountAddress.slice(0, 6)}…${accountAddress.slice(-4)})` : '', " is a contract with no private key, so importing this key into another wallet shows the owner address,", ' ', jsx("strong", { children: "not your funds" }), ". To move funds, use Send to withdraw to another wallet."] })) : (jsx("p", { style: { marginBottom: 6 }, children: "With your private key, you can access your account outside this application." })), jsx("p", { children: "Keep it safe and never share it with anyone you don't trust." })] }), !showExportedKey ? (jsx(Button, { onClick: () => setShowExportedKey(true), style: { marginTop: 12 }, children: "Export key" })) : exportError ? (jsx(ModalBody, { style: { marginTop: 12 }, "$error": true, children: exportError })) : exportedKey ? (jsx("div", { style: { marginTop: 12 }, children: jsxs(CopyText, { value: exportedKey, children: [exportedKey.slice(0, 10), "...", exportedKey.slice(-10)] }) })) : (jsx(Fragment, { children: "Loading..." }))] })] }));
41
49
  };
42
50
 
43
51
  export { ExportKey as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,9 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { ChainTypeEnum } from '@openfort/openfort-js';
3
- import { BuyIcon, DollarIcon, ReceiveIcon } from '../../../assets/icons.js';
4
- import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
5
- import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
6
- import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
2
+ import { BuyIcon, DollarIcon } from '../../../assets/icons.js';
7
3
  import Button from '../../Common/Button/index.js';
8
4
  import { ModalContent, ModalH1, ModalBody } from '../../Common/Modal/styles.js';
9
5
  import { FloatingGraphic } from '../../FloatingGraphic/index.js';
@@ -13,17 +9,7 @@ import { PageContent } from '../../PageContent/index.js';
13
9
  import { ButtonsContainer } from './styles.js';
14
10
 
15
11
  const NoAssetsAvailable = () => {
16
- const { setRoute, chains } = useOpenfort();
17
- const { chainType } = useOpenfortCore();
18
- // Use chain-specific hooks
19
- const ethereumWallet = useEthereumEmbeddedWallet();
20
- const solanaWallet = useSolanaEmbeddedWallet();
21
- const wallet = chainType === ChainTypeEnum.EVM ? ethereumWallet : solanaWallet;
22
- const chainId = wallet.status === 'connected' && chainType === ChainTypeEnum.EVM
23
- ? wallet.chainId
24
- : undefined;
25
- const chain = chains.find((c) => c.id === chainId);
26
- const showBuyOption = chain && !chain.testnet;
12
+ const { setRoute } = useOpenfort();
27
13
  return (jsxs(PageContent, { children: [jsx(FloatingGraphic, { height: "190px", logoCenter: {
28
14
  logo: jsx(BuyIcon, {}),
29
15
  }, logoTopLeft: {
@@ -34,11 +20,9 @@ const NoAssetsAvailable = () => {
34
20
  logo: jsx(DollarIcon, {}),
35
21
  }, logoBottomLeft: {
36
22
  logo: jsx(DollarIcon, {}),
37
- } }), jsxs(ModalContent, { style: { paddingBottom: 0 }, children: [jsx(ModalH1, { "$small": true, children: "No assets available" }), jsxs(ModalBody, { children: [jsx("div", { style: { paddingRight: 12, paddingLeft: 12 }, children: "You currently have no assets available in your wallet." }), jsxs(ButtonsContainer, { children: [jsx(Button, { onClick: () => {
38
- setRoute(routes.RECEIVE);
39
- }, icon: jsx(ReceiveIcon, {}), children: "Get assets" }), showBuyOption && (jsx(Button, { onClick: () => {
40
- setRoute(routes.BUY);
41
- }, icon: jsx(BuyIcon, {}), children: "Buy assets" }))] })] })] })] }));
23
+ } }), jsxs(ModalContent, { style: { paddingBottom: 0 }, children: [jsx(ModalH1, { "$small": true, children: "No assets available" }), jsxs(ModalBody, { children: [jsx("div", { style: { paddingRight: 12, paddingLeft: 12 }, children: "You currently have no assets available in your wallet." }), jsx(ButtonsContainer, { children: jsx(Button, { onClick: () => {
24
+ setRoute(routes.DEPOSIT);
25
+ }, icon: jsx(DollarIcon, {}), children: "Add funds" }) })] })] })] }));
42
26
  };
43
27
 
44
28
  export { NoAssetsAvailable };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,11 +1,15 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { ChainTypeEnum } from '@openfort/openfort-js';
2
3
  import { useState, useEffect } from 'react';
3
4
  import { formatUnits } from 'viem';
4
5
  import { useEthereumWalletAssets } from '../../../ethereum/hooks/useEthereumWalletAssets.js';
6
+ import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
5
7
  import { Arrow, ArrowChevron, TextLinkButton } from '../../Common/Button/styles.js';
6
8
  import { ModalHeading } from '../../Common/Modal/styles.js';
7
9
  import { routes } from '../../Openfort/types.js';
8
10
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
11
+ import { EVM_BUY_CURRENCIES } from '../Buy/evmCurrencies.js';
12
+ import { SOLANA_BUY_CURRENCIES } from '../Buy/solanaCurrencies.js';
9
13
  import { getAssetSymbol, getAssetDecimals, formatBalanceWithSymbol } from '../Send/utils.js';
10
14
  import { SelectTokenContent, EmptyState, TokenList, TokenButton, TokenInfo, TokenSymbol, TokenName, TokenBalance } from './styles.js';
11
15
 
@@ -22,9 +26,16 @@ const SelectToken = ({ isBuyFlow }) => {
22
26
  useEffect(() => {
23
27
  triggerResize();
24
28
  }, [viewAllAssets]);
29
+ const { chainType } = useOpenfortCore();
25
30
  const { data: walletAssets, isLoading: isBalancesLoading } = useEthereumWalletAssets();
26
- // Show all tokens for both buy and send flows
27
- const selectableTokens = walletAssets || [];
31
+ // Buys pick from a fixed buyable-currency list (USDC first, then native) per
32
+ // chain family, so the picker always has options even for a fresh wallet with no
33
+ // indexed balances. The send flow reads the EVM wallet's actual assets.
34
+ const selectableTokens = isBuyFlow
35
+ ? chainType === ChainTypeEnum.SVM
36
+ ? SOLANA_BUY_CURRENCIES
37
+ : EVM_BUY_CURRENCIES
38
+ : walletAssets || [];
28
39
  const handleSelect = (asset) => {
29
40
  var _a;
30
41
  // In send flow, don't allow selecting tokens with 0 balance
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import styled from '../../../styles/styled/index.js';
2
2
  import { PageContent } from '../../PageContent/index.js';
3
3
 
4
4
  const SelectTokenContent = styled(PageContent) `
5
- min-height: 320px;
5
+ min-height: 400px;
6
6
  display: flex;
7
7
  flex-direction: column;
8
8
  padding-bottom: 16px;
@@ -0,0 +1 @@
1
+ export declare const SolanaSend: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,89 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { parseUnits, formatUnits } from 'viem';
4
+ import { useSolanaWalletAssets } from '../../../solana/hooks/useSolanaWalletAssets.js';
5
+ import Button from '../../Common/Button/index.js';
6
+ import { Arrow, ArrowChevron } from '../../Common/Button/styles.js';
7
+ import Input from '../../Common/Input/index.js';
8
+ import { ModalHeading } from '../../Common/Modal/styles.js';
9
+ import { routes } from '../../Openfort/types.js';
10
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
11
+ import { PageContent } from '../../PageContent/index.js';
12
+ import { Form, Field, FieldLabel, TokenSelectorButton, TokenSelectorContent, TokenSelectorValue, TokenSelectorRight, AmountInputWrapper, MaxButton, HelperText, ErrorText } from './styles.js';
13
+ import { sanitizeForParsing, formatBalance, sanitizeAmountInput } from './utils.js';
14
+
15
+ const SOL_DECIMALS = 9;
16
+ /** Cheap base58 shape check for the form; the RPC validates the real address on send. */
17
+ function isLikelySolanaAddress(value) {
18
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
19
+ }
20
+ /** A native SOL asset with the canonical metadata (the default form asset has none). */
21
+ function solAsset(balance) {
22
+ return {
23
+ type: 'native',
24
+ balance,
25
+ metadata: { symbol: 'SOL', decimals: SOL_DECIMALS, fiat: { value: 0, currency: 'USD' } },
26
+ };
27
+ }
28
+ const SolanaSend = () => {
29
+ const { sendForm, setSendForm, setRoute } = useOpenfort();
30
+ const { data: assets } = useSolanaWalletAssets();
31
+ const asset = sendForm.asset;
32
+ const selected = asset.type === 'spl'
33
+ ? { isSpl: true, mint: asset.address, decimals: asset.metadata.decimals, symbol: asset.metadata.symbol }
34
+ : { isSpl: false, mint: 'native', decimals: SOL_DECIMALS, symbol: 'SOL' };
35
+ // Live balance (base units) for the selected token, matched by mint.
36
+ const liveToken = assets === null || assets === void 0 ? void 0 : assets.find((t) => (selected.isSpl ? t.mint === selected.mint : t.isNative));
37
+ const balanceBase = liveToken === null || liveToken === void 0 ? void 0 : liveToken.amount;
38
+ const parsedAmount = useMemo(() => {
39
+ const raw = sanitizeForParsing(sendForm.amount);
40
+ if (!raw)
41
+ return null;
42
+ try {
43
+ return parseUnits(raw, selected.decimals);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }, [sendForm.amount, selected.decimals]);
49
+ const recipientValid = isLikelySolanaAddress(sendForm.recipient.trim());
50
+ const insufficientBalance = parsedAmount !== null && balanceBase !== undefined ? parsedAmount > balanceBase : false;
51
+ const amountValid = parsedAmount !== null && parsedAmount > BigInt(0) && !insufficientBalance;
52
+ const canProceed = recipientValid && amountValid;
53
+ const availableLabel = formatBalance(balanceBase, selected.decimals);
54
+ const handleSubmit = (event) => {
55
+ event.preventDefault();
56
+ if (!canProceed)
57
+ return;
58
+ const normalized = sanitizeForParsing(sendForm.amount);
59
+ if (!normalized)
60
+ return;
61
+ // Persist the selected token with its live balance so the confirmation reads it.
62
+ const nextAsset = asset.type === 'spl'
63
+ ? { type: 'spl', address: asset.address, balance: balanceBase !== null && balanceBase !== void 0 ? balanceBase : asset.balance, metadata: asset.metadata }
64
+ : solAsset(balanceBase !== null && balanceBase !== void 0 ? balanceBase : BigInt(0));
65
+ setSendForm((prev) => ({
66
+ ...prev,
67
+ recipient: prev.recipient.trim(),
68
+ amount: normalized,
69
+ asset: nextAsset,
70
+ }));
71
+ setRoute(routes.SOL_SEND_CONFIRMATION);
72
+ };
73
+ const handleAmountChange = (event) => {
74
+ const raw = sanitizeAmountInput(event.target.value);
75
+ if (raw === '' || /^[0-9]*\.?[0-9]*$/.test(raw)) {
76
+ setSendForm((prev) => ({ ...prev, amount: raw }));
77
+ }
78
+ };
79
+ const handleMax = () => {
80
+ if (balanceBase === undefined)
81
+ return;
82
+ setSendForm((prev) => ({ ...prev, amount: formatUnits(balanceBase, selected.decimals) }));
83
+ };
84
+ const handleOpenTokenSelector = () => setRoute(routes.SOL_SEND_TOKEN_SELECT);
85
+ return (jsxs(PageContent, { onBack: routes.SOL_CONNECTED, children: [jsx(ModalHeading, { children: "Send assets" }), jsxs(Form, { onSubmit: handleSubmit, children: [jsxs(Field, { children: [jsx(FieldLabel, { children: "Asset" }), jsxs(TokenSelectorButton, { type: "button", onClick: handleOpenTokenSelector, children: [jsx(TokenSelectorContent, { children: jsx(TokenSelectorValue, { "$primary": true, children: selected.symbol }) }), jsxs(TokenSelectorRight, { children: [jsx(TokenSelectorValue, { children: availableLabel === '--' ? '--' : `${availableLabel} ${selected.symbol}` }), jsx(Arrow, { width: "13", height: "12", viewBox: "0 0 13 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx(ArrowChevron, { stroke: "currentColor", d: "M7.51431 1.5L11.757 5.74264M7.5 10.4858L11.7426 6.24314", strokeWidth: "2", strokeLinecap: "round" }) })] })] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Amount" }), jsxs(AmountInputWrapper, { children: [jsx(Input, { placeholder: "0.00", value: sendForm.amount, onChange: handleAmountChange, inputMode: "decimal", autoComplete: "off", style: { paddingRight: '86px' } }), jsx(MaxButton, { type: "button", onClick: handleMax, disabled: balanceBase === undefined, children: "Max" })] }), jsxs(HelperText, { children: ["Available: ", availableLabel, " ", selected.symbol] }), sendForm.amount && parsedAmount === null && jsx(ErrorText, { children: "Enter a valid amount." }), insufficientBalance && jsxs(ErrorText, { children: ["Insufficient ", selected.symbol, " balance for this transfer."] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Recipient address" }), jsx(Input, { placeholder: "Solana address", value: sendForm.recipient, onChange: (e) => setSendForm((prev) => ({ ...prev, recipient: e.target.value })), autoComplete: "off" }), sendForm.recipient && !recipientValid && jsx(ErrorText, { children: "Enter a valid Solana address." })] }), jsx(Button, { variant: "primary", disabled: !canProceed, children: "Review transfer" })] })] }));
86
+ };
87
+
88
+ export { SolanaSend };
89
+ //# sourceMappingURL=SolanaSend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolanaSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Send page router
3
3
  *
4
- * Picks EthereumSend or SolanaSend based on chainType.
4
+ * Renders the EVM send form. Solana is reached via the dedicated `sol:send`
5
+ * route (registered in ConnectModal), so it isn't in this shared registry.
5
6
  */
6
7
  import type React from 'react';
7
8
  declare const Send: React.FC;
@@ -5,7 +5,6 @@ import { EthereumSend } from './EthereumSend.js';
5
5
 
6
6
  const SEND_REGISTRY = {
7
7
  [ChainTypeEnum.EVM]: EthereumSend,
8
- // [ChainTypeEnum.SVM]: SolanaSend,
9
8
  };
10
9
  const Send = () => {
11
10
  const { chainType } = useOpenfortCore();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -44,10 +44,13 @@ const isSameToken = (a, b) => {
44
44
  return false;
45
45
  if (a.type === 'native')
46
46
  return true;
47
- // At this point, a.type === 'erc20' and b.type === 'erc20'
48
47
  if (a.type === 'erc20' && b.type === 'erc20') {
49
48
  return a.address.toLowerCase() === b.address.toLowerCase();
50
49
  }
50
+ // SPL mints are base58 and case-sensitive — compare verbatim.
51
+ if (a.type === 'spl' && b.type === 'spl') {
52
+ return a.address === b.address;
53
+ }
51
54
  return false;
52
55
  };
53
56
  const getAssetSymbol = (asset) => {
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}