14.05.2026 rip
This commit is contained in:
@@ -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 type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress } from './api/walletApi'
|
||||||
export { CHAINS } from './api/walletApi'
|
export { CHAINS } from './api/walletApi'
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
|
import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
|
||||||
import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, CHAINS, type Chain, type SendWalletPayload } from '../api/walletApi'
|
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() {
|
export function useAllWalletBalances() {
|
||||||
return useQueries({
|
return useQueries({
|
||||||
queries: CHAINS.map(chain => ({
|
queries: CHAINS.map(chain => ({
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import eth from '@shared/assets/eth.svg'
|
|||||||
import sol from '@shared/assets/sol.svg'
|
import sol from '@shared/assets/sol.svg'
|
||||||
import trx from '@shared/assets/trx.svg'
|
import trx from '@shared/assets/trx.svg'
|
||||||
import arb from '@shared/assets/arb.svg'
|
import arb from '@shared/assets/arb.svg'
|
||||||
|
import type { WalletBalanceData } from '@features/wallet'
|
||||||
|
|
||||||
export interface Token {
|
export interface Token {
|
||||||
symbol: string
|
symbol: string
|
||||||
@@ -27,6 +28,76 @@ const TOKENS: Record<string, Token> = {
|
|||||||
|
|
||||||
export const TOKENS_LIST: Token[] = Object.values(TOKENS)
|
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
|
const RATE = 82.2578
|
||||||
|
|
||||||
export function useSwapForm() {
|
export function useSwapForm() {
|
||||||
|
|||||||
@@ -50,6 +50,37 @@
|
|||||||
font-weight: 500;
|
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) ── */
|
/* ── Pills inside card (desktop only) ── */
|
||||||
.pillsInner {
|
.pillsInner {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -12,13 +12,18 @@ interface Props {
|
|||||||
onTokenChange: (token: Token) => void
|
onTokenChange: (token: Token) => void
|
||||||
onAmountChange?: (v: string) => void
|
onAmountChange?: (v: string) => void
|
||||||
onSetPercent?: (p: number) => void
|
onSetPercent?: (p: number) => void
|
||||||
|
selectedNetwork?: string
|
||||||
|
onNetworkChange?: (network: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NETWORKS = ['BTC', 'ETH', 'BSC', 'TRX', 'SOL']
|
||||||
|
|
||||||
const PERCENTS = [25, 50, 100]
|
const PERCENTS = [25, 50, 100]
|
||||||
|
|
||||||
export function SwapCard({
|
export function SwapCard({
|
||||||
mode, token, tokenOptions, amount, usd, slippage,
|
mode, token, tokenOptions, amount, usd, slippage,
|
||||||
onTokenChange, onAmountChange, onSetPercent,
|
onTokenChange, onAmountChange, onSetPercent,
|
||||||
|
selectedNetwork, onNetworkChange,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [intPart, decPart] = amount.split('.')
|
const [intPart, decPart] = amount.split('.')
|
||||||
|
|
||||||
@@ -42,7 +47,21 @@ export function SwapCard({
|
|||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className={styles.top}>
|
<div className={styles.top}>
|
||||||
<div className={styles.label}>
|
<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>
|
<span className={styles.network}>{token.network}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
import { PrimaryButton } from '@shared/ui'
|
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 { RateRow } from './RateRow'
|
||||||
import { SwapCard } from './SwapCard'
|
import { SwapCard } from './SwapCard'
|
||||||
import { SwapDirectionButton } from './SwapDirectionButton'
|
import { SwapDirectionButton } from './SwapDirectionButton'
|
||||||
@@ -17,17 +19,31 @@ export function SwapForm() {
|
|||||||
setFromToken, setToToken,
|
setFromToken, setToToken,
|
||||||
} = useSwapForm()
|
} = 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 (
|
return (
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<SwapCard
|
<SwapCard
|
||||||
mode="from"
|
mode="from"
|
||||||
token={fromToken}
|
token={fromToken}
|
||||||
tokenOptions={TOKENS_LIST}
|
tokenOptions={tokenOptions}
|
||||||
amount={fromAmount}
|
amount={fromAmount}
|
||||||
usd={fromUsd}
|
usd={fromUsd}
|
||||||
onAmountChange={setFromAmount}
|
onAmountChange={setFromAmount}
|
||||||
onSetPercent={setPercent}
|
onSetPercent={setPercent}
|
||||||
onTokenChange={setFromToken}
|
onTokenChange={setFromToken}
|
||||||
|
selectedNetwork={fromNetwork}
|
||||||
|
onNetworkChange={setFromNetwork}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SwapDirectionButton onClick={swapTokens} />
|
<SwapDirectionButton onClick={swapTokens} />
|
||||||
@@ -35,7 +51,7 @@ export function SwapForm() {
|
|||||||
<SwapCard
|
<SwapCard
|
||||||
mode="to"
|
mode="to"
|
||||||
token={toToken}
|
token={toToken}
|
||||||
tokenOptions={TOKENS_LIST}
|
tokenOptions={tokenOptions}
|
||||||
amount={toAmount}
|
amount={toAmount}
|
||||||
usd={toUsd}
|
usd={toUsd}
|
||||||
slippage="−0.16%"
|
slippage="−0.16%"
|
||||||
|
|||||||
Reference in New Issue
Block a user