204 lines
4.9 KiB
TypeScript
204 lines
4.9 KiB
TypeScript
import { getCsrfToken } from '@shared/api/csrf'
|
|
import { tokenStore, refreshAccessToken } from '@shared/api/tokenStore'
|
|
|
|
const WALLET_API_URL = 'https://app.cryptowallet.elcsa.ru'
|
|
|
|
export type Chain = 'ETH' | 'BSC' | 'BTC' | 'TRX' | 'SOL'
|
|
|
|
export interface FormattedAmount {
|
|
raw: string
|
|
formatted: string
|
|
decimals: number
|
|
usdPrice: number
|
|
usdValue: number
|
|
}
|
|
|
|
export interface WalletBalanceData {
|
|
chain: Chain
|
|
address: string
|
|
native: FormattedAmount
|
|
tokens: Record<string, FormattedAmount>
|
|
}
|
|
|
|
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 interface WalletAddress {
|
|
chain: Chain
|
|
address: string
|
|
derivationPath: string
|
|
}
|
|
|
|
export interface PortfolioToken {
|
|
symbol: string
|
|
amountFormatted: string
|
|
usd: number
|
|
}
|
|
|
|
export interface PortfolioNative {
|
|
amount: string
|
|
amountFormatted: string
|
|
usd: number
|
|
}
|
|
|
|
export interface PortfolioChain {
|
|
address: string
|
|
stale: boolean
|
|
native: PortfolioNative
|
|
tokens: PortfolioToken[]
|
|
totalUsd: number
|
|
}
|
|
|
|
export interface PortfolioData {
|
|
totalUsd: number
|
|
asOfMs: number
|
|
chains: Record<Chain, PortfolioChain>
|
|
}
|
|
|
|
export const CHAINS: Chain[] = ['ETH', 'BSC', 'BTC', 'TRX', 'SOL']
|
|
|
|
async function walletGet<T>(path: string, allowRetry: boolean = true): Promise<T> {
|
|
const csrf = await getCsrfToken()
|
|
const bearer = tokenStore.get()
|
|
|
|
const res = await fetch(`${WALLET_API_URL}${path}`, {
|
|
credentials: 'include',
|
|
headers: {
|
|
'X-CSRF-Token': csrf,
|
|
...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
|
|
},
|
|
})
|
|
|
|
if (res.status === 401 && allowRetry) {
|
|
try {
|
|
await refreshAccessToken()
|
|
return walletGet<T>(path, false)
|
|
} catch {
|
|
tokenStore.clear()
|
|
throw new Error('Unauthorized')
|
|
}
|
|
}
|
|
|
|
const data = await res.json()
|
|
if (!res.ok) throw data
|
|
return data as T
|
|
}
|
|
|
|
async function walletPost<T>(path: string, body: unknown, allowRetry: boolean = true): Promise<T> {
|
|
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<T>(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 getWalletAddresses(): Promise<WalletAddress[]> {
|
|
const res = await walletGet<{ success: boolean; data: WalletAddress[] }>('/api/wallets')
|
|
return res.data
|
|
}
|
|
|
|
export async function getWalletBalance(chain: Chain): Promise<WalletBalanceData> {
|
|
const res = await walletGet<{ success: boolean; data: WalletBalanceData }>(`/api/wallets/${chain}/balance`)
|
|
return res.data
|
|
}
|
|
|
|
export async function getPrices(symbols: string[]): Promise<Record<string, PriceEntry>> {
|
|
const res = await walletGet<{ success: boolean; data: Record<string, PriceEntry> }>(
|
|
`/api/prices?symbols=${symbols.join(',')}`
|
|
)
|
|
return res.data
|
|
}
|
|
|
|
export async function sendWallet(chain: Chain, payload: SendWalletPayload): Promise<SendWalletResponse> {
|
|
return walletPost<SendWalletResponse>(`/api/wallets/${chain}/send`, payload)
|
|
}
|
|
|
|
export async function getPortfolio(): Promise<PortfolioData> {
|
|
const res = await walletGet<{ success: boolean; data: PortfolioData }>('/api/wallets/portfolio')
|
|
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
|
|
}
|
|
}
|
|
fees: {
|
|
gas: {
|
|
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)
|
|
}
|
|
|
|
export async function createWallet(): Promise<void> {
|
|
await walletPost<unknown>('/api/wallets/create', {})
|
|
}
|
|
|
|
export async function revealMnemonic(): Promise<string> {
|
|
const res = await walletPost<{ success: boolean; data: { mnemonic: string } }>('/api/wallets/mnemonic/reveal', { confirm: 'I_UNDERSTAND_SEED_IS_SECRET' })
|
|
return res.data.mnemonic
|
|
}
|