diff --git a/src/features/wallet/api/walletApi.ts b/src/features/wallet/api/walletApi.ts index e62e2c3..68f8c23 100644 --- a/src/features/wallet/api/walletApi.ts +++ b/src/features/wallet/api/walletApi.ts @@ -24,6 +24,17 @@ export interface PriceEntry { usd: number } +export interface SendWalletPayload { + to: string + amount: string + token?: string + feeTier?: 'slow' | 'normal' | 'fast' +} + +export interface SendWalletResponse { + data: { txid: string; chain: Chain } +} + export const CHAINS: Chain[] = ['ETH', 'BSC', 'BTC', 'TRX', 'SOL'] async function walletGet(path: string, allowRetry: boolean = true): Promise { @@ -53,6 +64,36 @@ async function walletGet(path: string, allowRetry: boolean = true): Promise(path: string, body: unknown, allowRetry: boolean = true): Promise { + const csrf = await getCsrfToken() + const bearer = tokenStore.get() + + const res = await fetch(`${WALLET_API_URL}${path}`, { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrf, + ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}), + }, + body: JSON.stringify(body), + }) + + if (res.status === 401 && allowRetry) { + try { + await refreshAccessToken() + return walletPost(path, body, false) + } catch { + tokenStore.clear() + throw new Error('Unauthorized') + } + } + + const data = await res.json() + if (!res.ok) throw data + return data as T +} + export async function getWalletBalance(chain: Chain): Promise { const res = await walletGet<{ success: boolean; data: WalletBalanceData }>(`/api/wallets/${chain}/balance`) return res.data @@ -64,3 +105,7 @@ export async function getPrices(symbols: string[]): Promise { + return walletPost(`/api/wallets/${chain}/send`, payload) +} diff --git a/src/features/wallet/index.ts b/src/features/wallet/index.ts index 2e351d0..749d36a 100644 --- a/src/features/wallet/index.ts +++ b/src/features/wallet/index.ts @@ -1,3 +1,3 @@ -export { useAllWalletBalances, usePrices } from './model/useWalletData' -export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry } from './api/walletApi' +export { useAllWalletBalances, usePrices, useSendWallet } from './model/useWalletData' +export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse } from './api/walletApi' export { CHAINS } from './api/walletApi' diff --git a/src/features/wallet/model/useWalletData.ts b/src/features/wallet/model/useWalletData.ts index f773f8a..1bb1ec5 100644 --- a/src/features/wallet/model/useWalletData.ts +++ b/src/features/wallet/model/useWalletData.ts @@ -1,5 +1,5 @@ -import { useQuery, useQueries } from '@tanstack/react-query' -import { getWalletBalance, getPrices, CHAINS } from '../api/walletApi' +import { useQuery, useQueries, useMutation } from '@tanstack/react-query' +import { getWalletBalance, getPrices, sendWallet, CHAINS, type Chain, type SendWalletPayload } from '../api/walletApi' export function useAllWalletBalances() { return useQueries({ @@ -18,3 +18,10 @@ export function usePrices(symbols: string[]) { staleTime: 5 * 60 * 1000, }) } + +export function useSendWallet() { + return useMutation({ + mutationFn: ({ chain, ...payload }: { chain: Chain } & SendWalletPayload) => + sendWallet(chain, payload), + }) +} diff --git a/src/widgets/send-modal/index.ts b/src/widgets/send-modal/index.ts index 84b74d5..e879b8a 100644 --- a/src/widgets/send-modal/index.ts +++ b/src/widgets/send-modal/index.ts @@ -1,2 +1,4 @@ export { SendModal } from './ui/SendModal' export type { SendModalToken } from './ui/SendModal' +export type { Chain, FeeTier, SendRequest, SendResponse, SendEthRequest, SendBscRequest, SendBtcRequest, SendTrxRequest, SendSolRequest } from './model/sendTypes' +export { CHAIN_CONFIG, CHAIN_TOKENS, TICKER_TO_CHAIN } from './model/sendTypes' diff --git a/src/widgets/send-modal/model/sendTypes.ts b/src/widgets/send-modal/model/sendTypes.ts new file mode 100644 index 0000000..cdb0e17 --- /dev/null +++ b/src/widgets/send-modal/model/sendTypes.ts @@ -0,0 +1,150 @@ +// ─── Chain identifiers ─────────────────────────────────────────────────────── + +export type Chain = 'ETH' | 'BSC' | 'BTC' | 'TRX' | 'SOL' +export type FeeTier = 'slow' | 'normal' | 'fast' + +// ─── Per-chain token lists (excluding native) ──────────────────────────────── + +export type EthToken = 'USDT' | 'USDC' | 'DAI' | 'WBTC' | 'LINK' | 'UNI' +export type BscToken = 'USDT' | 'USDC' | 'BUSD' | 'WBNB' | 'DOGE' +export type TrxToken = 'USDT' | 'USDC' +// SOL has 14 SPL tokens in the registry +export type SolToken = + | 'USDC' | 'USDT' | 'RAY' | 'BONK' | 'JTO' | 'PYTH' + | 'WIF' | 'JUP' | 'ORCA' | 'MNGO' | 'MSOL' | 'STSOL' + | 'SAMO' | 'ATLAS' + +export const CHAIN_TOKENS = { + ETH: ['USDT', 'USDC', 'DAI', 'WBTC', 'LINK', 'UNI'] as const satisfies readonly EthToken[], + BSC: ['USDT', 'USDC', 'BUSD', 'WBNB', 'DOGE'] as const satisfies readonly BscToken[], + BTC: [] as const, + TRX: ['USDT', 'USDC'] as const satisfies readonly TrxToken[], + SOL: ['USDC', 'USDT', 'RAY', 'BONK', 'JTO', 'PYTH', 'WIF', 'JUP', 'ORCA', 'MNGO', 'MSOL', 'STSOL', 'SAMO', 'ATLAS'] as const satisfies readonly SolToken[], +} satisfies Record + +// ─── Per-chain UI / behaviour config ──────────────────────────────────────── + +export interface ChainConfig { + label: string + nativeSymbol: string + /** Whether the chain supports token-level sends (ERC20 / BEP20 / TRC20 / SPL) */ + hasToken: boolean + /** Whether the API accepts a feeTier parameter */ + hasFeeTier: boolean + /** Placeholder for the recipient address input */ + addressPlaceholder: string + /** Smallest amount unit name (for tooltip / docs) */ + unit: string + /** Accent color used for the chain dot in dropdowns */ + color: string +} + +export const CHAIN_CONFIG: Record = { + ETH: { + label: 'Ethereum', + nativeSymbol: 'ETH', + hasToken: true, + hasFeeTier: true, + addressPlaceholder: '0x…', + unit: 'wei', + color: '#627EEA', + }, + BSC: { + label: 'BNB Chain', + nativeSymbol: 'BNB', + hasToken: true, + hasFeeTier: false, + addressPlaceholder: '0x…', + unit: 'wei', + color: '#F3BA2F', + }, + BTC: { + label: 'Bitcoin', + nativeSymbol: 'BTC', + hasToken: false, + hasFeeTier: true, + addressPlaceholder: 'bc1q…', + unit: 'satoshi', + color: '#F7931A', + }, + TRX: { + label: 'Tron', + nativeSymbol: 'TRX', + hasToken: true, + hasFeeTier: false, + addressPlaceholder: 'T…', + unit: 'sun', + color: '#FF060A', + }, + SOL: { + label: 'Solana', + nativeSymbol: 'SOL', + hasToken: true, + hasFeeTier: false, + addressPlaceholder: 'Fg3R…', + unit: 'lamport', + color: '#9945FF', + }, +} + +// ─── Wallet ticker → chain mapping (for TokenTable integration) ────────────── +// ARB is an ERC-20 on Ethereum, not a separate chain. + +export const TICKER_TO_CHAIN: Record = { + BTC: 'BTC', + ETH: 'ETH', + SOL: 'SOL', + TRX: 'TRX', + BNB: 'BSC', + ARB: 'ETH', +} + +// ─── API request / response types ─────────────────────────────────────────── + +export interface SendEthRequest { + to: string + amount: string // raw wei / token base units (string to avoid JS precision loss) + token?: EthToken + feeTier?: FeeTier +} + +export interface SendBscRequest { + to: string + amount: string // raw wei / 18-dec base units (DOGE = 8 dec) + token?: BscToken + // ⚠️ USDT on BSC = 18 dec, not 6 like on ETH/TRX. DOGE = 8 dec. +} + +export interface SendBtcRequest { + to: string + amount: string // satoshi (8 dec). Min dust = 294 sat. Floor fee = 2 sat/vB. + feeTier?: FeeTier // slow = ~144 blocks, normal = ~6, fast = ~1 +} + +export interface SendTrxRequest { + to: string + amount: string // sun (6 dec for TRX; TRC-20 USDT also 6 dec) + token?: TrxToken + // ⚠️ TRC-20 transfer burns ~15–30 TRX Energy when no frozen Energy available. +} + +export interface SendSolRequest { + to: string // base58 public key + amount: string // lamports (9 dec for SOL) + token?: SolToken +} + +// Discriminated union — pick the right one based on `chain` +export type SendRequest = + | ({ chain: 'ETH' } & SendEthRequest) + | ({ chain: 'BSC' } & SendBscRequest) + | ({ chain: 'BTC' } & SendBtcRequest) + | ({ chain: 'TRX' } & SendTrxRequest) + | ({ chain: 'SOL' } & SendSolRequest) + +export interface SendResponse { + data: { + txid: string + chain: Chain + } +} diff --git a/src/widgets/send-modal/ui/SendModal.module.css b/src/widgets/send-modal/ui/SendModal.module.css index 2c34060..27664b0 100644 --- a/src/widgets/send-modal/ui/SendModal.module.css +++ b/src/widgets/send-modal/ui/SendModal.module.css @@ -328,6 +328,19 @@ text-decoration: underline; } +/* ─── Error message ─────────────────────────── */ + +.errorMsg { + background: rgba(255, 68, 102, 0.1); + border: 1px solid rgba(255, 68, 102, 0.3); + border-radius: 10px; + padding: 10px 14px; + font-size: 13px; + color: var(--error); + margin-bottom: 12px; + line-height: 1.4; +} + /* ─── Submit button ─────────────────────────── */ .submitBtn { diff --git a/src/widgets/send-modal/ui/SendModal.tsx b/src/widgets/send-modal/ui/SendModal.tsx index 3b6aa86..b019e8d 100644 --- a/src/widgets/send-modal/ui/SendModal.tsx +++ b/src/widgets/send-modal/ui/SendModal.tsx @@ -1,4 +1,6 @@ import { useState, useEffect } from 'react' +import { useSendWallet } from '@features/wallet' +import { CHAIN_CONFIG, CHAIN_TOKENS, type Chain, type FeeTier } from '../model/sendTypes' import styles from './SendModal.module.css' export interface SendModalToken { @@ -12,61 +14,100 @@ export interface SendModalToken { interface Props { open: boolean onClose: () => void - tokens: SendModalToken[] - initialTicker?: string + network: Chain + /** Wallet token rows — used only for balance lookup in the "Макс" hint */ + tokens?: SendModalToken[] + /** Pre-select a token in the network token list (ticker or empty string = native) */ + initialToken?: string } -type Speed = 'slow' | 'normal' | 'fast' - -const SPEEDS: { value: Speed; label: string }[] = [ - { value: 'slow', label: 'Медленно' }, +const SPEEDS: { value: FeeTier; label: string }[] = [ + { value: 'slow', label: 'Медленно' }, { value: 'normal', label: 'Нормально' }, - { value: 'fast', label: 'Быстро' }, + { value: 'fast', label: 'Быстро' }, ] -export function SendModal({ open, onClose, tokens, initialTicker }: Props) { - const [ticker, setTicker] = useState(initialTicker ?? tokens[0]?.ticker ?? '') - const [speed, setSpeed] = useState('normal') - const [address, setAddress] = useState('') - const [amount, setAmount] = useState('') - const [openDropdown, setOpenDropdown] = useState<'token' | 'speed' | null>(null) +function extractErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message + if (err && typeof err === 'object') { + const e = err as Record + if (typeof e.message === 'string') return e.message + if (typeof e.error === 'string') return e.error + if (Array.isArray(e.detail) && (e.detail[0] as Record)?.msg) + return String((e.detail[0] as Record).msg) + } + return 'Что-то пошло не так. Попробуйте ещё раз.' +} - const token = tokens.find((t) => t.ticker === ticker) ?? tokens[0] - const speedLabel = SPEEDS.find((s) => s.value === speed)?.label ?? 'Нормально' +export function SendModal({ open, onClose, network, tokens = [], initialToken = '' }: Props) { + const cfg = CHAIN_CONFIG[network] + const networkTokens = CHAIN_TOKENS[network] as readonly string[] + + const [selectedToken, setSelectedToken] = useState(initialToken) + const [speed, setSpeed] = useState('normal') + const [address, setAddress] = useState('') + const [amount, setAmount] = useState('') + const [openDropdown, setOpenDropdown] = useState<'token' | 'speed' | null>(null) + + const mutation = useSendWallet() + + const speedLabel = SPEEDS.find((s) => s.value === speed)?.label ?? 'Нормально' + const tokenLabel = selectedToken === '' ? cfg.nativeSymbol : selectedToken + const walletMatch = tokens.find( + (t) => t.ticker === (selectedToken === '' ? cfg.nativeSymbol : selectedToken), + ) + + useEffect(() => { setSelectedToken(initialToken) }, [initialToken]) + useEffect(() => { setSelectedToken('') }, [network]) useEffect(() => { - if (initialTicker) setTicker(initialTicker) - }, [initialTicker]) + if (mutation.isSuccess) onClose() + }, [mutation.isSuccess, onClose]) useEffect(() => { if (!open) { setAddress('') setAmount('') setOpenDropdown(null) + mutation.reset() return } - const handler = (e: KeyboardEvent) => { - if (e.key === 'Escape') onClose() - } + const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() } document.addEventListener('keydown', handler) return () => document.removeEventListener('keydown', handler) - }, [open, onClose]) + }, [open, onClose]) // eslint-disable-line react-hooks/exhaustive-deps if (!open) return null + const showToken = cfg.hasToken + const showFeeTier = cfg.hasFeeTier + const showSelects = showToken || showFeeTier + const selectsCols = showToken && showFeeTier ? 2 : 1 + function handleOverlayClick() { - if (openDropdown) { - setOpenDropdown(null) - } else { - onClose() - } + if (openDropdown) { setOpenDropdown(null) } else { onClose() } + } + + function handleSubmit() { + mutation.mutate({ + chain: network, + to: address, + amount, + ...(selectedToken ? { token: selectedToken } : {}), + ...(cfg.hasFeeTier ? { feeTier: speed } : {}), + }) } return (
e.stopPropagation()}> + + {/* Header */}
- Отправить +
+ + Отправить · {cfg.label} +
-
-
- - - {openDropdown === 'token' && ( -
- {tokens.map((t) => ( - - ))} + + + + {openDropdown === 'token' && ( +
+ + {networkTokens.map((tok) => ( + + ))} +
+ )} +
+ )} + + {showFeeTier && ( +
+ + + {openDropdown === 'speed' && ( +
+ {SPEEDS.map((s) => ( + + ))} +
+ )}
)}
+ )} -
- - - {openDropdown === 'speed' && ( -
- {SPEEDS.map((s) => ( - - ))} -
- )} -
-
- + {/* Address */}
setAddress(e.target.value)} autoComplete="off" @@ -168,6 +222,7 @@ export function SendModal({ open, onClose, tokens, initialTicker }: Props) { />
+ {/* Amount */}
@@ -180,24 +235,36 @@ export function SendModal({ open, onClose, tokens, initialTicker }: Props) { min="0" step="any" /> - {token && {token.ticker}} + {tokenLabel}
- {token && ( + {walletMatch && (
Макс:{' '}
)}
-
diff --git a/src/widgets/token-table/ui/TokenTable.tsx b/src/widgets/token-table/ui/TokenTable.tsx index d2edf52..74c68b2 100644 --- a/src/widgets/token-table/ui/TokenTable.tsx +++ b/src/widgets/token-table/ui/TokenTable.tsx @@ -1,15 +1,19 @@ import { useState } from 'react' import { useTokenRows } from '../model/useTokenRows' -import { SendModal } from '@widgets/send-modal' +import { SendModal, TICKER_TO_CHAIN, type Chain } from '@widgets/send-modal' import styles from './TokenTable.module.css' export function TokenTable() { const { rows, isLoading } = useTokenRows() const [favs, setFavs] = useState(() => rows.map((t) => t.fav)) - const [sendModal, setSendModal] = useState<{ open: boolean; ticker: string }>({ open: false, ticker: '' }) + const [sendModal, setSendModal] = useState<{ open: boolean; network: Chain }>({ + open: false, + network: 'ETH', + }) function openSend(ticker: string) { - setSendModal({ open: true, ticker }) + const network = TICKER_TO_CHAIN[ticker] ?? 'ETH' + setSendModal({ open: true, network }) } function closeSend() { @@ -143,8 +147,8 @@ export function TokenTable() { ) diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index 6651967..72375e1 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/app/app.tsx","./src/app/providers/guestroute.tsx","./src/app/providers/protectedroute.tsx","./src/app/providers/queryprovider.tsx","./src/app/providers/routerprovider.tsx","./src/app/providers/scrolltotop.tsx","./src/app/providers/index.ts","./src/features/auth/index.ts","./src/features/auth/api/profileapi.ts","./src/features/auth/api/registrationapi.ts","./src/features/auth/hooks/useauth.ts","./src/features/auth/hooks/useisauthenticated.ts","./src/features/auth/hooks/useme.ts","./src/features/kyc/api/kycapi.ts","./src/features/payment/index.ts","./src/features/payment/api/paymentapi.ts","./src/features/payment/hooks/usecreateorder.ts","./src/features/payment/hooks/usepaymentconfig.ts","./src/features/payment/hooks/usepaymentquote.ts","./src/features/wallet/index.ts","./src/features/wallet/api/walletapi.ts","./src/features/wallet/model/usewalletdata.ts","./src/pages/converter/index.ts","./src/pages/converter/ui/converterpage.tsx","./src/pages/home/index.ts","./src/pages/home/ui/homepage.tsx","./src/pages/kyc/index.ts","./src/pages/kyc/ui/kycpage.tsx","./src/pages/login/index.ts","./src/pages/login/ui/loginpage.tsx","./src/pages/profile/index.ts","./src/pages/profile/ui/profilepage.tsx","./src/pages/register/index.ts","./src/pages/register/ui/registerpage.tsx","./src/pages/seed-phrase/index.ts","./src/pages/seed-phrase/ui/seedphrasepage.tsx","./src/pages/swap/index.ts","./src/pages/swap/ui/swappage.tsx","./src/pages/wallet/index.ts","./src/pages/wallet/ui/walletpage.tsx","./src/shared/api/base.ts","./src/shared/api/csrf.ts","./src/shared/api/tokenstore.ts","./src/shared/api/types.ts","./src/shared/config/constants.ts","./src/shared/config/env.ts","./src/shared/config/routes.ts","./src/shared/lib/hooks/usedebounce.ts","./src/shared/lib/hooks/uselocalstorage.ts","./src/shared/lib/utils/cn.ts","./src/shared/types/index.ts","./src/shared/ui/index.ts","./src/shared/ui/button/button.tsx","./src/shared/ui/button/index.ts","./src/shared/ui/formfield/formfield.tsx","./src/shared/ui/formfield/index.ts","./src/shared/ui/notification/notification.tsx","./src/shared/ui/notification/index.ts","./src/shared/ui/pill/pill.tsx","./src/shared/ui/pill/index.ts","./src/shared/ui/primarybutton/primarybutton.tsx","./src/shared/ui/primarybutton/index.ts","./src/shared/ui/title/title.tsx","./src/shared/ui/tokenicon/tokenicon.tsx","./src/shared/ui/tokenicon/index.ts","./src/widgets/about/index.ts","./src/widgets/about/ui/about.tsx","./src/widgets/balance-card/index.ts","./src/widgets/balance-card/ui/balancecard.tsx","./src/widgets/converter-page/index.ts","./src/widgets/converter-page/ui/agreementcheck.tsx","./src/widgets/converter-page/ui/commissionpanel.tsx","./src/widgets/converter-page/ui/convertersection.tsx","./src/widgets/currency-converter/index.ts","./src/widgets/currency-converter/model/tiers.ts","./src/widgets/currency-converter/model/useconverter.ts","./src/widgets/currency-converter/ui/agreementcheckbox.tsx","./src/widgets/currency-converter/ui/commissiontable.tsx","./src/widgets/currency-converter/ui/converter.tsx","./src/widgets/currency-converter/ui/tiers.tsx","./src/widgets/footer/index.ts","./src/widgets/footer/ui/footer.tsx","./src/widgets/header/index.ts","./src/widgets/header/ui/header.tsx","./src/widgets/hero/index.ts","./src/widgets/hero/lib/usecountdown.ts","./src/widgets/hero/ui/conversionflow.tsx","./src/widgets/hero/ui/countdown.tsx","./src/widgets/hero/ui/exchangecard.tsx","./src/widgets/hero/ui/hero.tsx","./src/widgets/kyc-verification/index.ts","./src/widgets/kyc-verification/model/usekyc.ts","./src/widgets/kyc-verification/ui/kycmodal.tsx","./src/widgets/kyc-verification/ui/kycwidget.tsx","./src/widgets/login-form/index.ts","./src/widgets/login-form/model/useloginform.ts","./src/widgets/login-form/ui/loginform.tsx","./src/widgets/networks-table/index.ts","./src/widgets/networks-table/model/networks.ts","./src/widgets/networks-table/ui/networkstable.tsx","./src/widgets/profile/index.ts","./src/widgets/profile/ui/profileavatar.tsx","./src/widgets/profile/ui/profilesection.tsx","./src/widgets/register-form/index.ts","./src/widgets/register-form/model/useregisterform.ts","./src/widgets/register-form/ui/registerform.tsx","./src/widgets/seed-phrase/index.ts","./src/widgets/seed-phrase/model/useseedphrase.ts","./src/widgets/seed-phrase/ui/seedphrasewidget.tsx","./src/widgets/send-modal/index.ts","./src/widgets/send-modal/ui/sendmodal.tsx","./src/widgets/swap-form/index.ts","./src/widgets/swap-form/model/useswapform.ts","./src/widgets/swap-form/ui/raterow.tsx","./src/widgets/swap-form/ui/swapcard.tsx","./src/widgets/swap-form/ui/swapdirectionbutton.tsx","./src/widgets/swap-form/ui/swapform.tsx","./src/widgets/swap-form/ui/swapinfopanel.tsx","./src/widgets/swap-form/ui/tokenselect.tsx","./src/widgets/token-table/index.ts","./src/widgets/token-table/model/tokens.ts","./src/widgets/token-table/model/usetokenrows.ts","./src/widgets/token-table/ui/tokentable.tsx","./src/widgets/wallet-header/index.ts","./src/widgets/wallet-header/ui/walletheader.tsx"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/app/app.tsx","./src/app/providers/guestroute.tsx","./src/app/providers/protectedroute.tsx","./src/app/providers/queryprovider.tsx","./src/app/providers/routerprovider.tsx","./src/app/providers/scrolltotop.tsx","./src/app/providers/index.ts","./src/features/auth/index.ts","./src/features/auth/api/profileapi.ts","./src/features/auth/api/registrationapi.ts","./src/features/auth/hooks/useauth.ts","./src/features/auth/hooks/useisauthenticated.ts","./src/features/auth/hooks/useme.ts","./src/features/kyc/api/kycapi.ts","./src/features/payment/index.ts","./src/features/payment/api/paymentapi.ts","./src/features/payment/hooks/usecreateorder.ts","./src/features/payment/hooks/usepaymentconfig.ts","./src/features/payment/hooks/usepaymentquote.ts","./src/features/wallet/index.ts","./src/features/wallet/api/walletapi.ts","./src/features/wallet/model/usewalletdata.ts","./src/pages/converter/index.ts","./src/pages/converter/ui/converterpage.tsx","./src/pages/home/index.ts","./src/pages/home/ui/homepage.tsx","./src/pages/kyc/index.ts","./src/pages/kyc/ui/kycpage.tsx","./src/pages/login/index.ts","./src/pages/login/ui/loginpage.tsx","./src/pages/profile/index.ts","./src/pages/profile/ui/profilepage.tsx","./src/pages/register/index.ts","./src/pages/register/ui/registerpage.tsx","./src/pages/seed-phrase/index.ts","./src/pages/seed-phrase/ui/seedphrasepage.tsx","./src/pages/swap/index.ts","./src/pages/swap/ui/swappage.tsx","./src/pages/wallet/index.ts","./src/pages/wallet/ui/walletpage.tsx","./src/shared/api/base.ts","./src/shared/api/csrf.ts","./src/shared/api/tokenstore.ts","./src/shared/api/types.ts","./src/shared/config/constants.ts","./src/shared/config/env.ts","./src/shared/config/routes.ts","./src/shared/lib/hooks/usedebounce.ts","./src/shared/lib/hooks/uselocalstorage.ts","./src/shared/lib/utils/cn.ts","./src/shared/types/index.ts","./src/shared/ui/index.ts","./src/shared/ui/button/button.tsx","./src/shared/ui/button/index.ts","./src/shared/ui/formfield/formfield.tsx","./src/shared/ui/formfield/index.ts","./src/shared/ui/notification/notification.tsx","./src/shared/ui/notification/index.ts","./src/shared/ui/pill/pill.tsx","./src/shared/ui/pill/index.ts","./src/shared/ui/primarybutton/primarybutton.tsx","./src/shared/ui/primarybutton/index.ts","./src/shared/ui/title/title.tsx","./src/shared/ui/tokenicon/tokenicon.tsx","./src/shared/ui/tokenicon/index.ts","./src/widgets/about/index.ts","./src/widgets/about/ui/about.tsx","./src/widgets/balance-card/index.ts","./src/widgets/balance-card/ui/balancecard.tsx","./src/widgets/converter-page/index.ts","./src/widgets/converter-page/ui/agreementcheck.tsx","./src/widgets/converter-page/ui/commissionpanel.tsx","./src/widgets/converter-page/ui/convertersection.tsx","./src/widgets/currency-converter/index.ts","./src/widgets/currency-converter/model/tiers.ts","./src/widgets/currency-converter/model/useconverter.ts","./src/widgets/currency-converter/ui/agreementcheckbox.tsx","./src/widgets/currency-converter/ui/commissiontable.tsx","./src/widgets/currency-converter/ui/converter.tsx","./src/widgets/currency-converter/ui/tiers.tsx","./src/widgets/footer/index.ts","./src/widgets/footer/ui/footer.tsx","./src/widgets/header/index.ts","./src/widgets/header/ui/header.tsx","./src/widgets/hero/index.ts","./src/widgets/hero/lib/usecountdown.ts","./src/widgets/hero/ui/conversionflow.tsx","./src/widgets/hero/ui/countdown.tsx","./src/widgets/hero/ui/exchangecard.tsx","./src/widgets/hero/ui/hero.tsx","./src/widgets/kyc-verification/index.ts","./src/widgets/kyc-verification/model/usekyc.ts","./src/widgets/kyc-verification/ui/kycmodal.tsx","./src/widgets/kyc-verification/ui/kycwidget.tsx","./src/widgets/login-form/index.ts","./src/widgets/login-form/model/useloginform.ts","./src/widgets/login-form/ui/loginform.tsx","./src/widgets/networks-table/index.ts","./src/widgets/networks-table/model/networks.ts","./src/widgets/networks-table/ui/networkstable.tsx","./src/widgets/profile/index.ts","./src/widgets/profile/ui/profileavatar.tsx","./src/widgets/profile/ui/profilesection.tsx","./src/widgets/register-form/index.ts","./src/widgets/register-form/model/useregisterform.ts","./src/widgets/register-form/ui/registerform.tsx","./src/widgets/seed-phrase/index.ts","./src/widgets/seed-phrase/model/useseedphrase.ts","./src/widgets/seed-phrase/ui/seedphrasewidget.tsx","./src/widgets/send-modal/index.ts","./src/widgets/send-modal/model/sendtypes.ts","./src/widgets/send-modal/ui/sendmodal.tsx","./src/widgets/swap-form/index.ts","./src/widgets/swap-form/model/useswapform.ts","./src/widgets/swap-form/ui/raterow.tsx","./src/widgets/swap-form/ui/swapcard.tsx","./src/widgets/swap-form/ui/swapdirectionbutton.tsx","./src/widgets/swap-form/ui/swapform.tsx","./src/widgets/swap-form/ui/swapinfopanel.tsx","./src/widgets/swap-form/ui/tokenselect.tsx","./src/widgets/token-table/index.ts","./src/widgets/token-table/model/tokens.ts","./src/widgets/token-table/model/usetokenrows.ts","./src/widgets/token-table/ui/tokentable.tsx","./src/widgets/wallet-header/index.ts","./src/widgets/wallet-header/ui/walletheader.tsx"],"version":"5.6.3"} \ No newline at end of file