14.05.2026 rip

This commit is contained in:
2026-05-14 22:39:06 +03:00
parent 0668ecccf3
commit cdbd9318cf
7 changed files with 81 additions and 32 deletions

View File

@@ -192,3 +192,12 @@ export async function getTokensList(): Promise<TokenInfo[]> {
export async function getRelayQuote(payload: RelayQuotePayload): Promise<RelayQuoteResponse> { export async function getRelayQuote(payload: RelayQuotePayload): Promise<RelayQuoteResponse> {
return walletPost<RelayQuoteResponse>('/api/relay/quote', payload) return walletPost<RelayQuoteResponse>('/api/relay/quote', payload)
} }
export async function createWallet(): Promise<void> {
await walletPost<unknown>('/wallet/create', {})
}
export async function revealMnemonic(): Promise<string> {
const res = await walletPost<{ success: boolean; data: { mnemonic: string } }>('/wallets/mnemonic/reveal', {})
return res.data.mnemonic
}

View File

@@ -1,3 +1,3 @@
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance, usePortfolio, useTokensList, useRelayQuote } from './model/useWalletData' export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance, usePortfolio, useTokensList, useRelayQuote, useCreateWallet, useRevealMnemonic } from './model/useWalletData'
export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress, PortfolioData, PortfolioChain, PortfolioNative, PortfolioToken, TokenInfo, RelayQuotePayload, RelayQuoteResponse } from './api/walletApi' export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress, PortfolioData, PortfolioChain, PortfolioNative, PortfolioToken, TokenInfo, RelayQuotePayload, RelayQuoteResponse } from './api/walletApi'
export { CHAINS } from './api/walletApi' export { CHAINS } from './api/walletApi'

View File

@@ -1,5 +1,5 @@
import { useQuery, useQueries, useMutation } from '@tanstack/react-query' import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, getPortfolio, getTokensList, getRelayQuote, CHAINS, type Chain, type SendWalletPayload, type RelayQuotePayload } from '../api/walletApi' import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, getPortfolio, getTokensList, getRelayQuote, createWallet, revealMnemonic, CHAINS, type Chain, type SendWalletPayload, type RelayQuotePayload } from '../api/walletApi'
export function useWalletBalance(chain: Chain) { export function useWalletBalance(chain: Chain) {
return useQuery({ return useQuery({
@@ -58,6 +58,19 @@ export function useTokensList() {
}) })
} }
export function useCreateWallet() {
return useMutation({ mutationFn: createWallet })
}
export function useRevealMnemonic() {
return useQuery({
queryKey: ['wallet', 'mnemonic'],
queryFn: revealMnemonic,
staleTime: Infinity,
retry: false,
})
}
export function useRelayQuote(payload: RelayQuotePayload | null) { export function useRelayQuote(payload: RelayQuotePayload | null) {
return useQuery({ return useQuery({
queryKey: ['relay', 'quote', queryKey: ['relay', 'quote',

View File

@@ -1,16 +1,18 @@
import { WalletHeader } from '@widgets/wallet-header' import { WalletHeader } from '@widgets/wallet-header'
import { SeedPhraseWidget } from '@widgets/seed-phrase' import { SeedPhraseWidget } from '@widgets/seed-phrase'
import { useRevealMnemonic } from '@features/wallet'
import styles from './SeedPhrasePage.module.css' import styles from './SeedPhrasePage.module.css'
const MOCK_WORDS = ['egg', 'phone', 'long', 'vibe', 'potato', 'soup', 'skirt', 'black', 'phase', 'word', 'num', 'cucumber']
export function SeedPhrasePage() { export function SeedPhrasePage() {
const { data: mnemonic, isLoading } = useRevealMnemonic()
const words = mnemonic ? mnemonic.split(' ') : []
return ( return (
<div className={styles.page}> <div className={styles.page}>
<WalletHeader /> <WalletHeader />
<main className={styles.main}> <main className={styles.main}>
<div className={styles.glow} /> <div className={styles.glow} />
<SeedPhraseWidget words={MOCK_WORDS} /> {!isLoading && <SeedPhraseWidget words={words} />}
</main> </main>
</div> </div>
) )

View File

@@ -35,6 +35,20 @@
font-size: 14px; font-size: 14px;
} }
.noWallet {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
min-height: 300px;
color: var(--text-primary);
font-size: 16px;
text-align: center;
position: relative;
z-index: 1;
}
@media (max-width: 992px) { @media (max-width: 992px) {
.glow { .glow {
width: auto; width: auto;

View File

@@ -1,13 +1,20 @@
import { Navigate } from 'react-router-dom' import { Navigate, useNavigate } from 'react-router-dom'
import { useMe } from '@features/auth' import { useMe } from '@features/auth'
import { ROUTES } from '@shared/config/routes' import { ROUTES } from '@shared/config/routes'
import { usePortfolio, useCreateWallet } from '@features/wallet'
import { BalanceCard } from '@widgets/balance-card' import { BalanceCard } from '@widgets/balance-card'
import { TokenTable } from '@widgets/token-table' import { TokenTable } from '@widgets/token-table'
import { WalletHeader } from '@widgets/wallet-header' import { WalletHeader } from '@widgets/wallet-header'
import { Button } from '@shared/ui'
import styles from './WalletPage.module.css' import styles from './WalletPage.module.css'
export function WalletPage() { export function WalletPage() {
const { data, isLoading, isError } = useMe() const { data, isLoading, isError } = useMe()
const { error: portfolioError } = usePortfolio()
const { mutate: createWallet, isPending } = useCreateWallet()
const navigate = useNavigate()
const noWallet = (portfolioError as any)?.error?.includes('No wallets')
if (isLoading) return null if (isLoading) return null
if (isError) return <div className={styles.error}>Произошла ошибка. Попробуйте обновить страницу.</div> if (isError) return <div className={styles.error}>Произошла ошибка. Попробуйте обновить страницу.</div>
@@ -18,8 +25,23 @@ export function WalletPage() {
<WalletHeader /> <WalletHeader />
<main className={styles.main}> <main className={styles.main}>
<div className={styles.glow} /> <div className={styles.glow} />
<BalanceCard /> {noWallet ? (
<TokenTable /> <div className={styles.noWallet}>
<p>У вас пока нет кошелька. Создайте его, чтобы начать.</p>
<Button
variant="outline"
onClick={() => createWallet(undefined, { onSuccess: () => navigate(ROUTES.SEED_PHRASE) })}
disabled={isPending}
>
{isPending ? 'Создание...' : 'Создать кошелёк'}
</Button>
</div>
) : (
<>
<BalanceCard />
<TokenTable />
</>
)}
</main> </main>
</div> </div>
) )

View File

@@ -3,13 +3,11 @@ import { PrimaryButton } from '@shared/ui'
import { useWalletBalance, useWalletAddresses, useTokensList, useRelayQuote, type Chain } from '@features/wallet' import { useWalletBalance, useWalletAddresses, useTokensList, useRelayQuote, type Chain } from '@features/wallet'
import { useDebounce } from '@shared/lib/hooks/useDebounce' import { useDebounce } from '@shared/lib/hooks/useDebounce'
import { TOKENS_LIST, buildTokensFromBalance, useSwapForm } from '../model/useSwapForm' import { TOKENS_LIST, buildTokensFromBalance, useSwapForm } from '../model/useSwapForm'
import { RateRow } from './RateRow'
import { SwapCard } from './SwapCard' import { SwapCard } from './SwapCard'
import { SwapDirectionButton } from './SwapDirectionButton' import { SwapDirectionButton } from './SwapDirectionButton'
import { SwapInfoPanel } from './SwapInfoPanel' import { SwapInfoPanel } from './SwapInfoPanel'
import styles from './SwapForm.module.css' import styles from './SwapForm.module.css'
const RATE = 82.2578
const CHAIN_ID: Record<string, number> = { ETH: 1, BSC: 56, SOL: 792703809 } const CHAIN_ID: Record<string, number> = { ETH: 1, BSC: 56, SOL: 792703809 }
const NATIVE_ADDR: Record<string, string> = { const NATIVE_ADDR: Record<string, string> = {
@@ -24,8 +22,7 @@ export function SwapForm() {
const { const {
fromAmount, fromUsd, fromAmount, fromUsd,
fromToken, toToken, fromToken, toToken,
isRefreshing, setFromAmount, setPercent, swapTokens,
setFromAmount, setPercent, swapTokens, refreshRate,
setFromToken, setToToken, setFromToken, setToToken,
} = useSwapForm() } = useSwapForm()
@@ -47,27 +44,27 @@ export function SwapForm() {
const chainId = CHAIN_ID[fromNetwork] const chainId = CHAIN_ID[fromNetwork]
const walletAddress = addresses?.find(a => a.chain === fromNetwork)?.address const walletAddress = addresses?.find(a => a.chain === fromNetwork)?.address
const fromContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === fromToken.symbol)?.contract ?? nativeAddr(fromNetwork) const fromContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === fromToken.symbol)?.contract ?? nativeAddr(fromNetwork)
const toContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === toToken.symbol)?.contract ?? nativeAddr(fromNetwork) const toContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === toToken.symbol)?.contract ?? nativeAddr(fromNetwork)
const parsedAmount = parseFloat(debouncedAmount) const parsedAmount = parseFloat(debouncedAmount)
const quotePayload = chainId && walletAddress && parsedAmount > 0 const quotePayload = chainId && walletAddress && parsedAmount > 0
? { ? {
user: walletAddress, user: walletAddress,
recipient: walletAddress, recipient: walletAddress,
originChainId: chainId, originChainId: chainId,
destinationChainId: chainId, destinationChainId: chainId,
originCurrency: fromContract, originCurrency: fromContract,
destinationCurrency: toContract, destinationCurrency: toContract,
amount: Math.round(parsedAmount * Math.pow(10, fromToken.decimals)).toString(), amount: Math.round(parsedAmount * Math.pow(10, fromToken.decimals)).toString(),
tradeType: 'EXACT_INPUT' as const, tradeType: 'EXACT_INPUT' as const,
} }
: null : null
const { data: quoteData } = useRelayQuote(quotePayload) const { data: quoteData } = useRelayQuote(quotePayload)
const displayToAmount = quoteData?.details.currencyOut.amountFormatted ?? '0' const displayToAmount = quoteData?.details.currencyOut.amountFormatted ?? '0'
const displayToUsd = quoteData?.details.currencyOut.amountUsd const displayToUsd = quoteData?.details.currencyOut.amountUsd
const gasFee = quoteData?.fees.gas.amountUsd const gasFee = quoteData?.fees.gas.amountUsd
return ( return (
<div className={styles.form}> <div className={styles.form}>
@@ -95,14 +92,6 @@ export function SwapForm() {
onTokenChange={setToToken} onTokenChange={setToToken}
/> />
<RateRow
fromSymbol={fromToken.symbol}
toSymbol={toToken.symbol}
rate={RATE}
isRefreshing={isRefreshing}
onRefresh={refreshRate}
/>
<SwapInfoPanel gasFee={gasFee} /> <SwapInfoPanel gasFee={gasFee} />
<PrimaryButton /> <PrimaryButton />