14.05.2026 rip

This commit is contained in:
2026-05-14 20:05:35 +03:00
parent 2de30fbde6
commit fcfdac87b4
6 changed files with 150 additions and 5 deletions

View File

@@ -1,3 +1,3 @@
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses } from './model/useWalletData'
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance } from './model/useWalletData'
export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress } from './api/walletApi'
export { CHAINS } from './api/walletApi'

View File

@@ -1,6 +1,14 @@
import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, CHAINS, type Chain, type SendWalletPayload } from '../api/walletApi'
export function useWalletBalance(chain: Chain) {
return useQuery({
queryKey: ['wallet', 'balance', chain],
queryFn: () => getWalletBalance(chain),
staleTime: 30_000,
})
}
export function useAllWalletBalances() {
return useQueries({
queries: CHAINS.map(chain => ({

View File

@@ -4,6 +4,7 @@ import eth from '@shared/assets/eth.svg'
import sol from '@shared/assets/sol.svg'
import trx from '@shared/assets/trx.svg'
import arb from '@shared/assets/arb.svg'
import type { WalletBalanceData } from '@features/wallet'
export interface Token {
symbol: string
@@ -27,6 +28,76 @@ const TOKENS: Record<string, Token> = {
export const TOKENS_LIST: Token[] = Object.values(TOKENS)
export const TOKEN_META: Record<string, { letter: string; color: string; logo?: string }> = {
// Native / major
BTC: { letter: '₿', logo: btc, color: '#F7931A' },
ETH: { letter: 'E', logo: eth, color: '#627EEA' },
BNB: { letter: 'B', color: '#F3BA2F' },
SOL: { letter: 'S', logo: sol, color: '#9945FF' },
TRX: { letter: 'T', logo: trx, color: '#FF060A' },
ARB: { letter: 'A', logo: arb, color: '#4A6DFF' },
// Stablecoins
USDC: { letter: '$', color: '#2775CA' },
USDT: { letter: '$', color: '#26A17B' },
DAI: { letter: 'D', color: '#F5AC37' },
BUSD: { letter: 'B', color: '#F0B90B' },
// ETH tokens
WBTC: { letter: 'W', color: '#F7931A' },
LINK: { letter: 'L', color: '#2A5ADA' },
UNI: { letter: 'U', color: '#FF007A' },
// BSC tokens
WBNB: { letter: 'W', color: '#F3BA2F' },
DOGE: { letter: 'D', color: '#C2A633' },
// SOL tokens
JUP: { letter: 'J', color: '#C7A52D' },
WIF: { letter: 'W', color: '#9333EA' },
BONK: { letter: 'B', color: '#FF8C00' },
RAY: { letter: 'R', color: '#5AC4BE' },
ORCA: { letter: 'O', color: '#1B8EF2' },
PYTH: { letter: 'P', color: '#8B5CF6' },
JTO: { letter: 'J', color: '#06B6D4' },
W: { letter: 'W', color: '#6B7280' },
PUMP: { letter: 'P', color: '#00D4AA' },
POPCAT: { letter: 'P', color: '#FF6B6B' },
TRUMP: { letter: 'T', color: '#E63946' },
PENGU: { letter: 'P', color: '#60A5FA' },
}
const CHAIN_NATIVE: Record<string, string> = {
ETH: 'ETH', BSC: 'BNB', BTC: 'BTC', TRX: 'TRX', SOL: 'SOL',
}
export function buildTokensFromBalance(data: WalletBalanceData): Token[] {
const result: Token[] = []
const nativeSym = CHAIN_NATIVE[data.chain] ?? data.chain
const nativeMeta = TOKEN_META[nativeSym]
result.push({
symbol: nativeSym,
letter: nativeMeta?.letter ?? nativeSym[0],
color: nativeMeta?.color ?? '#888',
logo: nativeMeta?.logo,
network: data.chain,
balance: parseFloat(data.native.formatted),
usdRate: data.native.usdPrice,
})
for (const [sym, info] of Object.entries(data.tokens)) {
const meta = TOKEN_META[sym]
result.push({
symbol: sym,
letter: meta?.letter ?? sym[0],
color: meta?.color ?? '#888',
logo: meta?.logo,
network: data.chain,
balance: parseFloat(info.formatted),
usdRate: info.usdPrice,
})
}
return result
}
const RATE = 82.2578
export function useSwapForm() {

View File

@@ -50,6 +50,37 @@
font-weight: 500;
}
/* ── Network buttons ── */
.networkPills {
display: flex;
align-items: center;
gap: 5px;
}
.networkBtn {
background: rgba(255, 255, 255, 0.07);
color: var(--text-secondary);
border: none;
border-radius: 999px;
padding: 4px 10px;
font-size: 11px;
cursor: pointer;
font-family: var(--font-sans);
font-weight: 600;
transition: all 0.2s;
letter-spacing: 0.4px;
}
.networkBtn:hover {
color: var(--text-primary);
background: rgba(255, 255, 255, 0.13);
}
.networkBtnActive {
background: rgba(255, 255, 255, 0.18);
color: var(--text-primary);
}
/* ── Pills inside card (desktop only) ── */
.pillsInner {
display: flex;

View File

@@ -12,13 +12,18 @@ interface Props {
onTokenChange: (token: Token) => void
onAmountChange?: (v: string) => void
onSetPercent?: (p: number) => void
selectedNetwork?: string
onNetworkChange?: (network: string) => void
}
const NETWORKS = ['BTC', 'ETH', 'BSC', 'TRX', 'SOL']
const PERCENTS = [25, 50, 100]
export function SwapCard({
mode, token, tokenOptions, amount, usd, slippage,
onTokenChange, onAmountChange, onSetPercent,
selectedNetwork, onNetworkChange,
}: Props) {
const [intPart, decPart] = amount.split('.')
@@ -42,7 +47,21 @@ export function SwapCard({
<div className={styles.card}>
<div className={styles.top}>
<div className={styles.label}>
<span className={styles.tag}>{mode === 'from' ? 'ОТ' : 'К'}</span>
{mode === 'from' ? (
<div className={styles.networkPills}>
{NETWORKS.map(n => (
<button
key={n}
className={`${styles.networkBtn} ${n === selectedNetwork ? styles.networkBtnActive : ''}`}
onClick={() => onNetworkChange?.(n)}
>
{n}
</button>
))}
</div>
) : (
<span className={styles.tag}>К</span>
)}
<span className={styles.network}>{token.network}</span>
</div>

View File

@@ -1,5 +1,7 @@
import { useState, useEffect } from 'react'
import { PrimaryButton } from '@shared/ui'
import { TOKENS_LIST, useSwapForm } from '../model/useSwapForm'
import { useWalletBalance, type Chain } from '@features/wallet'
import { TOKENS_LIST, buildTokensFromBalance, useSwapForm } from '../model/useSwapForm'
import { RateRow } from './RateRow'
import { SwapCard } from './SwapCard'
import { SwapDirectionButton } from './SwapDirectionButton'
@@ -17,17 +19,31 @@ export function SwapForm() {
setFromToken, setToToken,
} = useSwapForm()
const [fromNetwork, setFromNetwork] = useState('ETH')
const { data: walletData } = useWalletBalance(fromNetwork as Chain)
const tokenOptions = walletData ? buildTokensFromBalance(walletData) : TOKENS_LIST
useEffect(() => {
if (tokenOptions.length > 0) {
setFromToken(tokenOptions[0])
setToToken(tokenOptions[1] ?? tokenOptions[0])
}
}, [fromNetwork])
return (
<div className={styles.form}>
<SwapCard
mode="from"
token={fromToken}
tokenOptions={TOKENS_LIST}
tokenOptions={tokenOptions}
amount={fromAmount}
usd={fromUsd}
onAmountChange={setFromAmount}
onSetPercent={setPercent}
onTokenChange={setFromToken}
selectedNetwork={fromNetwork}
onNetworkChange={setFromNetwork}
/>
<SwapDirectionButton onClick={swapTokens} />
@@ -35,7 +51,7 @@ export function SwapForm() {
<SwapCard
mode="to"
token={toToken}
tokenOptions={TOKENS_LIST}
tokenOptions={tokenOptions}
amount={toAmount}
usd={toUsd}
slippage="0.16%"