14.05.2026 rip
This commit is contained in:
@@ -151,3 +151,39 @@ export async function getPortfolio(): Promise<PortfolioData> {
|
|||||||
const res = await walletGet<{ success: boolean; data: PortfolioData }>('/api/wallets/portfolio')
|
const res = await walletGet<{ success: boolean; data: PortfolioData }>('/api/wallets/portfolio')
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TokenInfo {
|
||||||
|
chain: string
|
||||||
|
symbol: string
|
||||||
|
name: string
|
||||||
|
contract: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RelayQuotePayload {
|
||||||
|
user: string
|
||||||
|
recipient: string
|
||||||
|
originChainId: number
|
||||||
|
destinationChainId: number
|
||||||
|
originCurrency: string
|
||||||
|
destinationCurrency: string
|
||||||
|
amount: string
|
||||||
|
tradeType: 'EXACT_INPUT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RelayQuoteResponse {
|
||||||
|
details: {
|
||||||
|
currencyOut: {
|
||||||
|
amountFormatted: string
|
||||||
|
amountUsd: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTokensList(): Promise<TokenInfo[]> {
|
||||||
|
const res = await walletGet<{ success: boolean; data: TokenInfo[] }>('/api/tokens')
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRelayQuote(payload: RelayQuotePayload): Promise<RelayQuoteResponse> {
|
||||||
|
return walletPost<RelayQuoteResponse>('/api/relay/quote', payload)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance, usePortfolio } from './model/useWalletData'
|
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance, usePortfolio, useTokensList, useRelayQuote } from './model/useWalletData'
|
||||||
export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress, PortfolioData, PortfolioChain, PortfolioNative, PortfolioToken } 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'
|
||||||
|
|||||||
@@ -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, CHAINS, type Chain, type SendWalletPayload } from '../api/walletApi'
|
import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, getPortfolio, getTokensList, getRelayQuote, CHAINS, type Chain, type SendWalletPayload, type RelayQuotePayload } from '../api/walletApi'
|
||||||
|
|
||||||
export function useWalletBalance(chain: Chain) {
|
export function useWalletBalance(chain: Chain) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -49,3 +49,23 @@ export function usePortfolio() {
|
|||||||
staleTime: 30_000,
|
staleTime: 30_000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useTokensList() {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['wallet', 'tokens'],
|
||||||
|
queryFn: getTokensList,
|
||||||
|
staleTime: 10 * 60 * 1000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRelayQuote(payload: RelayQuotePayload | null) {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['relay', 'quote',
|
||||||
|
payload?.originChainId, payload?.originCurrency,
|
||||||
|
payload?.destinationCurrency, payload?.amount,
|
||||||
|
],
|
||||||
|
queryFn: () => getRelayQuote(payload!),
|
||||||
|
enabled: !!payload,
|
||||||
|
staleTime: 10_000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,16 +14,17 @@ export interface Token {
|
|||||||
network: string
|
network: string
|
||||||
balance: number
|
balance: number
|
||||||
usdRate: number
|
usdRate: number
|
||||||
|
decimals: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOKENS: Record<string, Token> = {
|
const TOKENS: Record<string, Token> = {
|
||||||
BTC: { symbol: 'BTC', letter: '₿', logo: btc, color: '#F7931A', network: 'BITCOIN', balance: 0, usdRate: 67412 },
|
BTC: { symbol: 'BTC', letter: '₿', logo: btc, color: '#F7931A', network: 'BITCOIN', balance: 0, usdRate: 67412, decimals: 8 },
|
||||||
ETH: { symbol: 'ETH', letter: 'E', logo: eth, color: '#627EEA', network: 'ETHEREUM', balance: 0, usdRate: 3521 },
|
ETH: { symbol: 'ETH', letter: 'E', logo: eth, color: '#627EEA', network: 'ETHEREUM', balance: 0, usdRate: 3521, decimals: 18 },
|
||||||
SOL: { symbol: 'SOL', letter: 'S', logo: sol, color: '#9945FF', network: 'SOLANA', balance: 0.994, usdRate: 163.84 },
|
SOL: { symbol: 'SOL', letter: 'S', logo: sol, color: '#9945FF', network: 'SOLANA', balance: 0.994, usdRate: 163.84, decimals: 9 },
|
||||||
TRX: { symbol: 'TRX', letter: 'T', logo: trx, color: '#FF060A', network: 'TRON', balance: 0, usdRate: 0.12 },
|
TRX: { symbol: 'TRX', letter: 'T', logo: trx, color: '#FF060A', network: 'TRON', balance: 0, usdRate: 0.12, decimals: 6 },
|
||||||
ARB: { symbol: 'ARB', letter: 'A', logo: arb, color: '#4A6DFF', network: 'ARBITRUM', balance: 0, usdRate: 0.92 },
|
ARB: { symbol: 'ARB', letter: 'A', logo: arb, color: '#4A6DFF', network: 'ARBITRUM', balance: 0, usdRate: 0.92, decimals: 18 },
|
||||||
USDC: { symbol: 'USDC', letter: '$', color: '#2775CA', network: 'SOLANA', balance: 0, usdRate: 1 },
|
USDC: { symbol: 'USDC', letter: '$', color: '#2775CA', network: 'SOLANA', balance: 0, usdRate: 1, decimals: 6 },
|
||||||
USDT: { symbol: 'USDT', letter: '$', color: '#26A17B', network: 'ETHEREUM', balance: 0, usdRate: 1 },
|
USDT: { symbol: 'USDT', letter: '$', color: '#26A17B', network: 'ETHEREUM', balance: 0, usdRate: 1, decimals: 6 },
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TOKENS_LIST: Token[] = Object.values(TOKENS)
|
export const TOKENS_LIST: Token[] = Object.values(TOKENS)
|
||||||
@@ -80,6 +81,7 @@ export function buildTokensFromBalance(data: WalletBalanceData): Token[] {
|
|||||||
network: data.chain,
|
network: data.chain,
|
||||||
balance: parseFloat(data.native.formatted),
|
balance: parseFloat(data.native.formatted),
|
||||||
usdRate: data.native.usdPrice,
|
usdRate: data.native.usdPrice,
|
||||||
|
decimals: data.native.decimals,
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const [sym, info] of Object.entries(data.tokens)) {
|
for (const [sym, info] of Object.entries(data.tokens)) {
|
||||||
@@ -92,6 +94,7 @@ export function buildTokensFromBalance(data: WalletBalanceData): Token[] {
|
|||||||
network: data.chain,
|
network: data.chain,
|
||||||
balance: parseFloat(info.formatted),
|
balance: parseFloat(info.formatted),
|
||||||
usdRate: info.usdPrice,
|
usdRate: info.usdPrice,
|
||||||
|
decimals: info.decimals,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { PrimaryButton } from '@shared/ui'
|
import { PrimaryButton } from '@shared/ui'
|
||||||
import { useWalletBalance, type Chain } from '@features/wallet'
|
import { useWalletBalance, useWalletAddresses, useTokensList, useRelayQuote, type Chain } from '@features/wallet'
|
||||||
|
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 { RateRow } from './RateRow'
|
||||||
import { SwapCard } from './SwapCard'
|
import { SwapCard } from './SwapCard'
|
||||||
@@ -10,6 +11,15 @@ import styles from './SwapForm.module.css'
|
|||||||
|
|
||||||
const RATE = 82.2578
|
const RATE = 82.2578
|
||||||
|
|
||||||
|
const CHAIN_ID: Record<string, number> = { ETH: 1, BSC: 56, SOL: 792703809 }
|
||||||
|
const NATIVE_ADDR: Record<string, string> = {
|
||||||
|
SOL: '11111111111111111111111111111111',
|
||||||
|
DEFAULT: '0x0000000000000000000000000000000000000000',
|
||||||
|
}
|
||||||
|
function nativeAddr(chain: string) {
|
||||||
|
return NATIVE_ADDR[chain] ?? NATIVE_ADDR.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
export function SwapForm() {
|
export function SwapForm() {
|
||||||
const {
|
const {
|
||||||
fromAmount, toAmount, fromUsd, toUsd,
|
fromAmount, toAmount, fromUsd, toUsd,
|
||||||
@@ -30,6 +40,34 @@ export function SwapForm() {
|
|||||||
setToToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? (tokenOptions[1] ?? tokenOptions[0]))
|
setToToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? (tokenOptions[1] ?? tokenOptions[0]))
|
||||||
}, [walletData, fromNetwork])
|
}, [walletData, fromNetwork])
|
||||||
|
|
||||||
|
const debouncedAmount = useDebounce(fromAmount, 500)
|
||||||
|
const { data: addresses } = useWalletAddresses()
|
||||||
|
const { data: tokensList } = useTokensList()
|
||||||
|
|
||||||
|
const chainId = CHAIN_ID[fromNetwork]
|
||||||
|
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 toContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === toToken.symbol)?.contract ?? nativeAddr(fromNetwork)
|
||||||
|
|
||||||
|
const parsedAmount = parseFloat(debouncedAmount)
|
||||||
|
const quotePayload = chainId && walletAddress && parsedAmount > 0
|
||||||
|
? {
|
||||||
|
user: walletAddress,
|
||||||
|
recipient: walletAddress,
|
||||||
|
originChainId: chainId,
|
||||||
|
destinationChainId: chainId,
|
||||||
|
originCurrency: fromContract,
|
||||||
|
destinationCurrency: toContract,
|
||||||
|
amount: Math.round(parsedAmount * Math.pow(10, fromToken.decimals)).toString(),
|
||||||
|
tradeType: 'EXACT_INPUT' as const,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
|
||||||
|
const { data: quoteData } = useRelayQuote(quotePayload)
|
||||||
|
|
||||||
|
const displayToAmount = quoteData?.details.currencyOut.amountFormatted ?? toAmount
|
||||||
|
const displayToUsd = quoteData?.details.currencyOut.amountUsd ?? toUsd
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<SwapCard
|
<SwapCard
|
||||||
@@ -51,8 +89,8 @@ export function SwapForm() {
|
|||||||
mode="to"
|
mode="to"
|
||||||
token={toToken}
|
token={toToken}
|
||||||
tokenOptions={tokenOptions}
|
tokenOptions={tokenOptions}
|
||||||
amount={toAmount}
|
amount={displayToAmount}
|
||||||
usd={toUsd}
|
usd={displayToUsd}
|
||||||
slippage="−0.16%"
|
slippage="−0.16%"
|
||||||
onTokenChange={setToToken}
|
onTokenChange={setToToken}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user