@openfort/react 1.1.3 → 1.2.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 (154) hide show
  1. package/build/assets/icons.js +1 -1
  2. package/build/assets/logos.d.ts +12 -0
  3. package/build/assets/logos.js +7 -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/ConnectModal/index.js +14 -2
  15. package/build/components/ConnectModal/index.js.map +1 -1
  16. package/build/components/Openfort/OpenfortProvider.js +4 -1
  17. package/build/components/Openfort/OpenfortProvider.js.map +1 -1
  18. package/build/components/Openfort/types.d.ts +86 -0
  19. package/build/components/Openfort/types.js +22 -1
  20. package/build/components/Openfort/types.js.map +1 -1
  21. package/build/components/Pages/AssetInventory/SolanaAssetInventory.d.ts +6 -0
  22. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js +42 -0
  23. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js.map +1 -0
  24. package/build/components/Pages/Buy/index.js +3 -2
  25. package/build/components/Pages/Buy/index.js.map +1 -1
  26. package/build/components/Pages/BuySelectProvider/index.js +1 -1
  27. package/build/components/Pages/Connected/EthereumConnected.js +8 -32
  28. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  29. package/build/components/Pages/Connected/SolanaConnected.js +9 -4
  30. package/build/components/Pages/Connected/SolanaConnected.js.map +1 -1
  31. package/build/components/Pages/Deposit/AddressPageLink.d.ts +7 -0
  32. package/build/components/Pages/Deposit/AddressPageLink.js +17 -0
  33. package/build/components/Pages/Deposit/AddressPageLink.js.map +1 -0
  34. package/build/components/Pages/Deposit/AssetChainLogo.d.ts +9 -0
  35. package/build/components/Pages/Deposit/AssetChainLogo.js +24 -0
  36. package/build/components/Pages/Deposit/AssetChainLogo.js.map +1 -0
  37. package/build/components/Pages/Deposit/DepositAddressBlock.d.ts +21 -0
  38. package/build/components/Pages/Deposit/DepositAddressBlock.js +28 -0
  39. package/build/components/Pages/Deposit/DepositAddressBlock.js.map +1 -0
  40. package/build/components/Pages/Deposit/DepositProgress.d.ts +15 -0
  41. package/build/components/Pages/Deposit/DepositProgress.js +110 -0
  42. package/build/components/Pages/Deposit/DepositProgress.js.map +1 -0
  43. package/build/components/Pages/Deposit/DepositStatus.d.ts +9 -0
  44. package/build/components/Pages/Deposit/DepositStatus.js +43 -0
  45. package/build/components/Pages/Deposit/DepositStatus.js.map +1 -0
  46. package/build/components/Pages/Deposit/DepositSuccess.d.ts +6 -0
  47. package/build/components/Pages/Deposit/DepositSuccess.js +24 -0
  48. package/build/components/Pages/Deposit/DepositSuccess.js.map +1 -0
  49. package/build/components/Pages/Deposit/Details.d.ts +12 -0
  50. package/build/components/Pages/Deposit/Details.js +40 -0
  51. package/build/components/Pages/Deposit/Details.js.map +1 -0
  52. package/build/components/Pages/Deposit/LogoSelect.d.ts +12 -0
  53. package/build/components/Pages/Deposit/LogoSelect.js +95 -0
  54. package/build/components/Pages/Deposit/LogoSelect.js.map +1 -0
  55. package/build/components/Pages/Deposit/OrDivider.d.ts +2 -0
  56. package/build/components/Pages/Deposit/OrDivider.js +10 -0
  57. package/build/components/Pages/Deposit/OrDivider.js.map +1 -0
  58. package/build/components/Pages/Deposit/RouteSelectors.d.ts +13 -0
  59. package/build/components/Pages/Deposit/RouteSelectors.js +19 -0
  60. package/build/components/Pages/Deposit/RouteSelectors.js.map +1 -0
  61. package/build/components/Pages/Deposit/cexChains.d.ts +9 -0
  62. package/build/components/Pages/Deposit/cexChains.js +23 -0
  63. package/build/components/Pages/Deposit/cexChains.js.map +1 -0
  64. package/build/components/Pages/Deposit/formStyles.d.ts +24 -0
  65. package/build/components/Pages/Deposit/formStyles.js +83 -0
  66. package/build/components/Pages/Deposit/formStyles.js.map +1 -0
  67. package/build/components/Pages/Deposit/index.d.ts +7 -0
  68. package/build/components/Pages/Deposit/index.js +100 -0
  69. package/build/components/Pages/Deposit/index.js.map +1 -0
  70. package/build/components/Pages/Deposit/paymentOptions.d.ts +49 -0
  71. package/build/components/Pages/Deposit/paymentOptions.js +63 -0
  72. package/build/components/Pages/Deposit/paymentOptions.js.map +1 -0
  73. package/build/components/Pages/Deposit/sources.d.ts +17 -0
  74. package/build/components/Pages/Deposit/sources.js +22 -0
  75. package/build/components/Pages/Deposit/sources.js.map +1 -0
  76. package/build/components/Pages/Deposit/styles.d.ts +25 -0
  77. package/build/components/Pages/Deposit/styles.js +167 -0
  78. package/build/components/Pages/Deposit/styles.js.map +1 -0
  79. package/build/components/Pages/Deposit/useDepositRoute.d.ts +35 -0
  80. package/build/components/Pages/Deposit/useDepositRoute.js +107 -0
  81. package/build/components/Pages/Deposit/useDepositRoute.js.map +1 -0
  82. package/build/components/Pages/Deposit/useFundingTarget.d.ts +13 -0
  83. package/build/components/Pages/Deposit/useFundingTarget.js +27 -0
  84. package/build/components/Pages/Deposit/useFundingTarget.js.map +1 -0
  85. package/build/components/Pages/DepositCex/index.d.ts +11 -0
  86. package/build/components/Pages/DepositCex/index.js +230 -0
  87. package/build/components/Pages/DepositCex/index.js.map +1 -0
  88. package/build/components/Pages/DepositCrypto/index.d.ts +8 -0
  89. package/build/components/Pages/DepositCrypto/index.js +31 -0
  90. package/build/components/Pages/DepositCrypto/index.js.map +1 -0
  91. package/build/components/Pages/DepositWallet/DepositWalletDesktop.d.ts +17 -0
  92. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js +148 -0
  93. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js.map +1 -0
  94. package/build/components/Pages/DepositWallet/index.d.ts +9 -0
  95. package/build/components/Pages/DepositWallet/index.js +102 -0
  96. package/build/components/Pages/DepositWallet/index.js.map +1 -0
  97. package/build/components/Pages/DepositWallet/walletDeeplinks.d.ts +48 -0
  98. package/build/components/Pages/DepositWallet/walletDeeplinks.js +107 -0
  99. package/build/components/Pages/DepositWallet/walletDeeplinks.js.map +1 -0
  100. package/build/components/Pages/ExportKey/index.js +10 -2
  101. package/build/components/Pages/ExportKey/index.js.map +1 -1
  102. package/build/components/Pages/NoAssetsAvailable/index.js +5 -21
  103. package/build/components/Pages/NoAssetsAvailable/index.js.map +1 -1
  104. package/build/components/Pages/SelectToken/styles.js +1 -1
  105. package/build/components/Pages/Send/SolanaSend.d.ts +1 -0
  106. package/build/components/Pages/Send/SolanaSend.js +88 -0
  107. package/build/components/Pages/Send/SolanaSend.js.map +1 -0
  108. package/build/components/Pages/Send/index.d.ts +2 -1
  109. package/build/components/Pages/Send/index.js +0 -1
  110. package/build/components/Pages/Send/index.js.map +1 -1
  111. package/build/components/Pages/SendConfirmation/EstimatedFees.js +5 -3
  112. package/build/components/Pages/SendConfirmation/EstimatedFees.js.map +1 -1
  113. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.d.ts +1 -0
  114. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +77 -0
  115. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js.map +1 -0
  116. package/build/components/Pages/SendConfirmation/index.js +4 -3
  117. package/build/components/Pages/SendConfirmation/index.js.map +1 -1
  118. package/build/components/Pages/SendConfirmation/styles.d.ts +5 -0
  119. package/build/components/Pages/SendConfirmation/styles.js +39 -1
  120. package/build/components/Pages/SendConfirmation/styles.js.map +1 -1
  121. package/build/constants/logos.js +1 -0
  122. package/build/constants/logos.js.map +1 -1
  123. package/build/ethereum/hooks/useEthereumWalletAssets.js +212 -95
  124. package/build/ethereum/hooks/useEthereumWalletAssets.js.map +1 -1
  125. package/build/hooks/openfort/fundingClient.d.ts +34 -0
  126. package/build/hooks/openfort/fundingClient.js +60 -0
  127. package/build/hooks/openfort/fundingClient.js.map +1 -0
  128. package/build/hooks/openfort/useFunding.d.ts +159 -0
  129. package/build/hooks/openfort/useFunding.js +204 -0
  130. package/build/hooks/openfort/useFunding.js.map +1 -0
  131. package/build/hooks/openfort/useFundingChains.d.ts +49 -0
  132. package/build/hooks/openfort/useFundingChains.js +102 -0
  133. package/build/hooks/openfort/useFundingChains.js.map +1 -0
  134. package/build/hooks/useBalance.js +6 -1
  135. package/build/hooks/useBalance.js.map +1 -1
  136. package/build/index.d.ts +4 -1
  137. package/build/index.js +2 -1
  138. package/build/index.js.map +1 -1
  139. package/build/shared/hooks/useAsyncData.d.ts +11 -0
  140. package/build/shared/hooks/useAsyncData.js +60 -13
  141. package/build/shared/hooks/useAsyncData.js.map +1 -1
  142. package/build/solana/hooks/useSolanaWalletAssets.d.ts +24 -0
  143. package/build/solana/hooks/useSolanaWalletAssets.js +86 -0
  144. package/build/solana/hooks/useSolanaWalletAssets.js.map +1 -0
  145. package/build/solana/transfer.d.ts +32 -0
  146. package/build/solana/transfer.js +125 -0
  147. package/build/solana/transfer.js.map +1 -0
  148. package/build/utils/index.d.ts +2 -1
  149. package/build/utils/index.js +1 -1
  150. package/build/version.d.ts +1 -1
  151. package/build/version.js +1 -1
  152. package/build/wagmi/defaultConnectors.js +5 -1
  153. package/build/wagmi/defaultConnectors.js.map +1 -1
  154. package/package.json +10 -2
@@ -0,0 +1,17 @@
1
+ import type { FundingChain, FundingCurrency } from '../../../hooks/openfort/useFundingChains';
2
+ type Props = {
3
+ /** Relay deposit address the source funds are sent to. */
4
+ receiverAddress: string | null;
5
+ activeChain: FundingChain | undefined;
6
+ activeCurrency: FundingCurrency | undefined;
7
+ loading: boolean;
8
+ };
9
+ /**
10
+ * Desktop "transfer from wallet": send the source token to the Relay deposit
11
+ * address straight from the user's EXTERNAL browser-extension wallet
12
+ * (MetaMask/Rabby/…), via that connector's own provider — never the active
13
+ * Openfort embedded account (that one is the destination, not the source).
14
+ * Once the deposit lands, the funding session's status polling drives progress.
15
+ */
16
+ export declare function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading }: Props): import("react/jsx-runtime").JSX.Element | null;
17
+ export {};
@@ -0,0 +1,148 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { parseUnits, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
4
+ import { WalletIcon } from '../../../assets/icons.js';
5
+ import Logos from '../../../assets/logos.js';
6
+ import { useEthereumBridge } from '../../../ethereum/OpenfortEthereumBridgeContext.js';
7
+ import { ModalBody } from '../../Common/Modal/styles.js';
8
+ import { DepositStatus } from '../Deposit/DepositStatus.js';
9
+ import { walletListBtn } from '../Deposit/formStyles.js';
10
+ import { ButtonLogo } from '../Deposit/styles.js';
11
+ import { caipToChainId } from './walletDeeplinks.js';
12
+
13
+ /** Hardcoded brand logo for a connector, matched by id/name; falls back to its own icon, then a generic. */
14
+ function brandLogo(connector) {
15
+ const k = `${connector.id} ${connector.name}`.toLowerCase();
16
+ if (k.includes('metamask'))
17
+ return jsx(Logos.MetaMask, { background: true });
18
+ if (k.includes('rabby'))
19
+ return jsx(Logos.Rabby, {});
20
+ if (k.includes('coinbase'))
21
+ return jsx(Logos.Coinbase, { background: true });
22
+ if (k.includes('phantom'))
23
+ return jsx(Logos.Phantom, { background: true });
24
+ if (k.includes('trust'))
25
+ return jsx(Logos.Trust, {});
26
+ if (k.includes('rainbow'))
27
+ return jsx(Logos.Rainbow, { round: true });
28
+ if (connector.icon)
29
+ return jsx("img", { src: connector.icon, alt: "", width: 20, height: 20, style: { borderRadius: 6 } });
30
+ return jsx(WalletIcon, {});
31
+ }
32
+ /** Readable message from a wallet/provider error; null for a user rejection (4001). */
33
+ function walletErrorMessage(e) {
34
+ if (e && typeof e === 'object') {
35
+ const o = e;
36
+ if (o.code === 4001)
37
+ return null; // user rejected — nothing to flag
38
+ return o.shortMessage || o.message || o.details || 'Transaction failed';
39
+ }
40
+ return typeof e === 'string' ? e : 'Transaction failed';
41
+ }
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
+ /**
59
+ * Desktop "transfer from wallet": send the source token to the Relay deposit
60
+ * address straight from the user's EXTERNAL browser-extension wallet
61
+ * (MetaMask/Rabby/…), via that connector's own provider — never the active
62
+ * Openfort embedded account (that one is the destination, not the source).
63
+ * Once the deposit lands, the funding session's status polling drives progress.
64
+ */
65
+ function DepositWalletDesktop({ receiverAddress, activeChain, activeCurrency, loading }) {
66
+ const bridge = useEthereumBridge();
67
+ const [amount, setAmount] = useState('');
68
+ const [busyId, setBusyId] = useState(null);
69
+ const [error, setError] = useState(null);
70
+ const [sent, setSent] = useState(false);
71
+ if (!bridge)
72
+ return null; // EVM-only (no wagmi) — nothing to send through
73
+ // bridge.connectors already excludes the Openfort embedded connector, so these
74
+ // are the user's external wallets. Drop WalletConnect: its provider needs a
75
+ // connect()/session flow our direct request() path doesn't support.
76
+ const connectors = bridge.connectors.filter((c) => !c.id.toLowerCase().includes('walletconnect'));
77
+ const srcChainId = caipToChainId(activeChain === null || activeChain === void 0 ? void 0 : activeChain.id);
78
+ const amountValid = Number.parseFloat(amount) > 0;
79
+ const sendVia = async (c) => {
80
+ var _a, _b;
81
+ if (!receiverAddress || !activeCurrency || !srcChainId || !amountValid)
82
+ return;
83
+ setBusyId(c.id);
84
+ setError(null);
85
+ setSent(false);
86
+ try {
87
+ const provider = (await ((_a = c.getProvider) === null || _a === void 0 ? void 0 : _a.call(c)));
88
+ if (!provider)
89
+ throw new Error('Wallet provider unavailable');
90
+ const accounts = (await provider.request({ method: 'eth_requestAccounts' }));
91
+ const from = accounts === null || accounts === void 0 ? void 0 : accounts[0];
92
+ if (!from)
93
+ throw new Error('No account in wallet');
94
+ // Ask the wallet to switch to the source chain (ignored if already there).
95
+ await provider
96
+ .request({ method: 'wallet_switchEthereumChain', params: [{ chainId: `0x${srcChainId.toString(16)}` }] })
97
+ .catch(() => { });
98
+ const value = parseUnits(amount, activeCurrency.decimals);
99
+ const to = receiverAddress;
100
+ // Guard: confirm the wallet holds enough on this chain. Otherwise the transfer
101
+ // just reverts (wasted gas) and the deposit never arrives — the bug we hit.
102
+ const balance = activeCurrency.native
103
+ ? BigInt((await provider.request({ method: 'eth_getBalance', params: [from, 'latest'] })))
104
+ : BigInt((await provider.request({
105
+ method: 'eth_call',
106
+ params: [
107
+ { to: activeCurrency.address, data: `0x70a08231${from.slice(2).padStart(64, '0')}` },
108
+ 'latest',
109
+ ],
110
+ })) || '0x0');
111
+ if (balance < value) {
112
+ setError(`Not enough ${activeCurrency.symbol} on ${(_b = activeChain === null || activeChain === void 0 ? void 0 : activeChain.name) !== null && _b !== void 0 ? _b : 'this chain'} — you have ${formatUnits(balance, activeCurrency.decimals)}.`);
113
+ return;
114
+ }
115
+ if (activeCurrency.native) {
116
+ await provider.request({
117
+ method: 'eth_sendTransaction',
118
+ params: [{ from, to, value: `0x${value.toString(16)}` }],
119
+ });
120
+ }
121
+ else {
122
+ const data = encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, value] });
123
+ await provider.request({
124
+ method: 'eth_sendTransaction',
125
+ params: [{ from, to: activeCurrency.address, data }],
126
+ });
127
+ }
128
+ setSent(true);
129
+ }
130
+ catch (e) {
131
+ setError(walletErrorMessage(e));
132
+ }
133
+ finally {
134
+ setBusyId(null);
135
+ }
136
+ };
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 })] }));
145
+ }
146
+
147
+ export { DepositWalletDesktop };
148
+ //# sourceMappingURL=DepositWalletDesktop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DepositWalletDesktop.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,9 @@
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.
7
+ */
8
+ declare const DepositWallet: () => import("react/jsx-runtime").JSX.Element;
9
+ export default DepositWallet;
@@ -0,0 +1,102 @@
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 { AddressPageLink } from '../Deposit/AddressPageLink.js';
12
+ import { isDepositFlowActive, DepositProgress } from '../Deposit/DepositProgress.js';
13
+ import { walletListBtn } from '../Deposit/formStyles.js';
14
+ import { RouteSelectors } from '../Deposit/RouteSelectors.js';
15
+ import { StepDivider, Skeleton, ButtonLogo } from '../Deposit/styles.js';
16
+ import { useDepositRoute } from '../Deposit/useDepositRoute.js';
17
+ import { DepositWalletDesktop } from './DepositWalletDesktop.js';
18
+ import { OPENFORT_DEPOSIT_PAGE_URL, caipToChainId, buildDepositPageUrl, buildOpenDappLinks } from './walletDeeplinks.js';
19
+
20
+ /** Wallet-app brand logos keyed by the deeplink `app` id. */
21
+ const WALLET_LOGO = {
22
+ metamask: jsx(Logos.MetaMask, { background: true }),
23
+ coinbase: jsx(Logos.Coinbase, { background: true }),
24
+ phantom: jsx(Logos.Phantom, { background: true }),
25
+ trust: jsx(Logos.Trust, {}),
26
+ rainbow: jsx(Logos.Rainbow, { round: true }),
27
+ rabby: jsx(Logos.Rabby, {}),
28
+ };
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
+ }
46
+ /**
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.
52
+ */
53
+ const DepositWallet = () => {
54
+ var _a, _b, _c, _d, _e, _f, _g, _h;
55
+ const { triggerResize, uiConfig } = useOpenfort();
56
+ const isMobile = useIsMobile();
57
+ const route = useDepositRoute('crypto');
58
+ const [amount, setAmount] = useState('');
59
+ 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);
61
+ const amountValid = Number.parseFloat(amount) > 0;
62
+ // Open-dApp deeplinks: prefer backend-provided ones; otherwise build them from
63
+ // the hosted deposit page URL (if the integrator configured one) carrying the
64
+ // resolved transfer params. The hosted page sends via the wallet's injected
65
+ // provider, so no backend wiring is required for the link itself.
66
+ const pageUrl = depositPageUrl && route.receiverAddress && route.activeCurrency && srcChainId
67
+ ? buildDepositPageUrl(depositPageUrl, {
68
+ to: route.receiverAddress,
69
+ chainId: srcChainId,
70
+ token: route.activeCurrency.native ? undefined : route.activeCurrency.address,
71
+ decimals: route.activeCurrency.decimals,
72
+ symbol: route.activeCurrency.symbol,
73
+ chain: (_d = route.activeChain) === null || _d === void 0 ? void 0 : _d.name,
74
+ amount: amountValid ? parseUnits(amount, route.activeCurrency.decimals).toString() : undefined,
75
+ })
76
+ : null;
77
+ const allDeeplinks = ((_f = (_e = route.pm) === null || _e === void 0 ? void 0 : _e.deeplinks) === null || _f === void 0 ? void 0 : _f.length)
78
+ ? route.pm.deeplinks
79
+ : pageUrl
80
+ ? buildOpenDappLinks(pageUrl, (_h = (_g = route.activeChain) === null || _g === void 0 ? void 0 : _g.vmType) !== null && _h !== void 0 ? _h : 'evm')
81
+ : [];
82
+ // Trust's in-app dApp browser was removed on iOS (Apple, 2021) — the link
83
+ // dead-ends there, so hide it on iOS while keeping it on Android.
84
+ const deeplinks = isIOS() ? allDeeplinks.filter((d) => d.app !== 'trust') : allDeeplinks;
85
+ useEffect(() => {
86
+ triggerResize();
87
+ }, [route.receiverAddress, route.loading, route.status, deeplinks.length, triggerResize]);
88
+ if (isDepositFlowActive(route.status))
89
+ 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 })] }));
99
+ };
100
+
101
+ export { DepositWallet as default };
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,48 @@
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
+ /** Relay deposit address the source funds go to. */
27
+ 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. */
31
+ token?: string;
32
+ decimals: number;
33
+ symbol: string;
34
+ /** Human-readable chain name shown on the page. */
35
+ chain?: string;
36
+ /** Optional preset amount in base units (uint256 string). */
37
+ amount?: string;
38
+ };
39
+ /** Build the deposit "send" page URL with the transfer params encoded. */
40
+ export declare function buildDepositPageUrl(baseUrl: string, p: DepositPageParams): string;
41
+ /** "eip155:8453" -> 8453 (undefined if not a numeric CAIP-2 reference). */
42
+ export declare function caipToChainId(caip2?: string): number | undefined;
43
+ /**
44
+ * Open-dApp deeplinks for every wallet matching `vmType`, each wrapping the
45
+ * deposit send page so it loads in that wallet's in-app browser.
46
+ */
47
+ export declare function buildOpenDappLinks(pageUrl: string, vmType: VmType): WalletDeeplink[];
48
+ export {};
@@ -0,0 +1,107 @@
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('to', p.to);
23
+ url.searchParams.set('chainId', String(p.chainId));
24
+ if (p.token)
25
+ url.searchParams.set('token', p.token);
26
+ url.searchParams.set('decimals', String(p.decimals));
27
+ url.searchParams.set('symbol', p.symbol);
28
+ if (p.chain)
29
+ url.searchParams.set('chain', p.chain);
30
+ if (p.amount)
31
+ url.searchParams.set('amount', p.amount);
32
+ return url.toString();
33
+ }
34
+ /** "eip155:8453" -> 8453 (undefined if not a numeric CAIP-2 reference). */
35
+ function caipToChainId(caip2) {
36
+ if (!caip2)
37
+ return undefined;
38
+ const n = Number(caip2.split(':')[1]);
39
+ return Number.isFinite(n) ? n : undefined;
40
+ }
41
+ const stripScheme = (u) => u.replace(/^https?:\/\//, '');
42
+ const enc = encodeURIComponent;
43
+ const originOf = (u) => {
44
+ try {
45
+ return new URL(u).origin;
46
+ }
47
+ catch {
48
+ return '';
49
+ }
50
+ };
51
+ // Open-dApp universal links. Each format verified (2026) against the wallet's
52
+ // docs/source plus a second independent source. `pageUrl` is the deposit send
53
+ // page; most want it URL-encoded, MetaMask wants the https:// scheme stripped.
54
+ const WALLETS = [
55
+ {
56
+ app: 'metamask',
57
+ label: 'Open MetaMask',
58
+ vms: ['evm'],
59
+ build: (u) => `https://link.metamask.io/dapp/${stripScheme(u)}`,
60
+ },
61
+ {
62
+ app: 'coinbase',
63
+ label: 'Open Coinbase Wallet',
64
+ vms: ['evm'],
65
+ build: (u) => `https://go.cb-w.com/dapp?cb_url=${enc(u)}`,
66
+ },
67
+ {
68
+ app: 'trust',
69
+ label: 'Open Trust Wallet',
70
+ vms: ['evm'],
71
+ // coin_id=60 = Ethereum (SLIP-44). In-app dApp browser is Android-only;
72
+ // iOS shows "Deep Link not Supported" (Apple removed it in 2021).
73
+ build: (u) => `https://link.trustwallet.com/open_url?coin_id=60&url=${enc(u)}`,
74
+ },
75
+ {
76
+ app: 'rainbow',
77
+ label: 'Open Rainbow',
78
+ vms: ['evm'],
79
+ build: (u) => `https://rainbow.me/dapp?url=${enc(u)}`,
80
+ },
81
+ {
82
+ app: 'rabby',
83
+ label: 'Open Rabby',
84
+ vms: ['evm'],
85
+ build: (u) => `https://go.rabby.io/mobile/?_cmd=open-dapp&dapp=${enc(u)}`,
86
+ },
87
+ {
88
+ app: 'phantom',
89
+ label: 'Open Phantom',
90
+ vms: ['evm', 'svm'],
91
+ build: (u) => `https://phantom.app/ul/browse/${enc(u)}?ref=${enc(originOf(u))}`,
92
+ },
93
+ ];
94
+ /**
95
+ * Open-dApp deeplinks for every wallet matching `vmType`, each wrapping the
96
+ * deposit send page so it loads in that wallet's in-app browser.
97
+ */
98
+ function buildOpenDappLinks(pageUrl, vmType) {
99
+ return WALLETS.filter((w) => w.vms.includes(vmType)).map((w) => ({
100
+ app: w.app,
101
+ label: w.label,
102
+ url: w.build(pageUrl),
103
+ }));
104
+ }
105
+
106
+ export { OPENFORT_DEPOSIT_PAGE_URL, buildDepositPageUrl, buildOpenDappLinks, caipToChainId };
107
+ //# 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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,88 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { parseUnits, formatUnits } from 'viem';
4
+ import { fetchSolanaBalance } from '../../../hooks/useBalance.js';
5
+ import { useAsyncData } from '../../../shared/hooks/useAsyncData.js';
6
+ import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
7
+ import Button from '../../Common/Button/index.js';
8
+ import Input from '../../Common/Input/index.js';
9
+ import { ModalHeading } from '../../Common/Modal/styles.js';
10
+ import { routes } from '../../Openfort/types.js';
11
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
12
+ import { PageContent } from '../../PageContent/index.js';
13
+ import { Form, Field, FieldLabel, AmountInputWrapper, MaxButton, HelperText, ErrorText } from './styles.js';
14
+ import { sanitizeForParsing, formatBalance, sanitizeAmountInput } from './utils.js';
15
+
16
+ const SOL_DECIMALS = 9;
17
+ /** Cheap base58 shape check for the form; the RPC validates the real address on send. */
18
+ function isLikelySolanaAddress(value) {
19
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
20
+ }
21
+ const SolanaSend = () => {
22
+ var _a;
23
+ const { sendForm, setSendForm, setRoute } = useOpenfort();
24
+ const wallet = useSolanaEmbeddedWallet();
25
+ const address = wallet.status === 'connected' ? wallet.address : undefined;
26
+ const rpcUrl = wallet.rpcUrl;
27
+ const balanceResult = useAsyncData({
28
+ queryKey: ['solana-balance', address, rpcUrl],
29
+ queryFn: async () => {
30
+ if (!address || !rpcUrl)
31
+ return null;
32
+ const { value } = await fetchSolanaBalance(address, rpcUrl, 'confirmed');
33
+ return value;
34
+ },
35
+ enabled: Boolean(address && rpcUrl),
36
+ });
37
+ const balanceLamports = (_a = balanceResult.data) !== null && _a !== void 0 ? _a : undefined;
38
+ const parsedAmount = useMemo(() => {
39
+ const raw = sanitizeForParsing(sendForm.amount);
40
+ if (!raw)
41
+ return null;
42
+ try {
43
+ return parseUnits(raw, SOL_DECIMALS);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }, [sendForm.amount]);
49
+ const recipientValid = isLikelySolanaAddress(sendForm.recipient.trim());
50
+ const insufficientBalance = parsedAmount !== null && balanceLamports !== undefined ? parsedAmount > balanceLamports : false;
51
+ const amountValid = parsedAmount !== null && parsedAmount > BigInt(0) && !insufficientBalance;
52
+ const canProceed = recipientValid && amountValid;
53
+ const availableLabel = formatBalance(balanceLamports, SOL_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
+ setSendForm((prev) => ({
62
+ ...prev,
63
+ recipient: prev.recipient.trim(),
64
+ amount: normalized,
65
+ asset: {
66
+ type: 'native',
67
+ balance: balanceLamports !== null && balanceLamports !== void 0 ? balanceLamports : BigInt(0),
68
+ metadata: { symbol: 'SOL', decimals: SOL_DECIMALS, fiat: { value: 0, currency: 'USD' } },
69
+ },
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 (balanceLamports === undefined)
81
+ return;
82
+ setSendForm((prev) => ({ ...prev, amount: formatUnits(balanceLamports, SOL_DECIMALS) }));
83
+ };
84
+ return (jsxs(PageContent, { onBack: routes.SOL_CONNECTED, children: [jsx(ModalHeading, { children: "Send SOL" }), jsxs(Form, { onSubmit: handleSubmit, children: [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: balanceLamports === undefined, children: "Max" })] }), jsxs(HelperText, { children: ["Available: ", availableLabel, " SOL"] }), sendForm.amount && parsedAmount === null && jsx(ErrorText, { children: "Enter a valid amount." }), insufficientBalance && jsx(ErrorText, { children: "Insufficient SOL 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" })] })] }));
85
+ };
86
+
87
+ export { SolanaSend };
88
+ //# sourceMappingURL=SolanaSend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolanaSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}