UNPKG

@crypto-dex-sdk/parachains-amplitude

Version:

Zenlink Parachains Impl for Amplitude

177 lines (158 loc) 5.85 kB
import type { Currency, Token, Type } from '@crypto-dex-sdk/currency' import type { QueryableStorageEntry } from '@polkadot/api/types' import type { FrameSystemAccountInfo } from '@polkadot/types/lookup' import type { OrmlTokensAccountData } from '@zenlink-types/bifrost/interfaces' import type { PairPrimitivesAssetId } from '../types' import { Pair } from '@crypto-dex-sdk/amm' import { ParachainId } from '@crypto-dex-sdk/chain' import { Amount } from '@crypto-dex-sdk/currency' import { addressToZenlinkAssetId } from '@crypto-dex-sdk/format' import { useApi, useCallMulti } from '@crypto-dex-sdk/polkadot' import { useMemo } from 'react' import { addressToNodeCurrency, isNativeCurrency, PAIR_ADDRESSES } from '../libs' // Explicitly import the @polkadot/api-augment here, to re-apply the base types, // see https://polkadot.js.org/docs/api/FAQ/#since-upgrading-to-the-7x-series-typescript-augmentation-is-missing import '@polkadot/api-augment' export enum PairState { LOADING, NOT_EXISTS, EXISTS, INVALID, } export function getPairs(chainId: number | undefined, currencies: [Currency | undefined, Currency | undefined][]) { return currencies .filter((currencies): currencies is [Type, Type] => { const [currencyA, currencyB] = currencies return Boolean( chainId && (chainId === ParachainId.AMPLITUDE || chainId === ParachainId.PENDULUM) && currencyA && currencyB && currencyA.chainId === currencyB.chainId && chainId === currencyA.chainId && !currencyA.wrapped.equals(currencyB.wrapped), ) }) .reduce<[Token[], Token[], PairPrimitivesAssetId[]]>( (acc, [currencyA, currencyB]) => { const [token0, token1] = currencyA.wrapped.sortsBefore(currencyB.wrapped) ? [currencyA.wrapped, currencyB.wrapped] : [currencyB.wrapped, currencyA.wrapped] acc[0].push(token0) acc[1].push(token1) acc[2].push([ addressToZenlinkAssetId(token0.address), addressToZenlinkAssetId(token1.address), ]) return acc }, [[], [], []], ) } export function uniqePairKey(tokenA: Token, tokenB: Token): string { return `${tokenA.address}-${tokenB.address}` } interface UsePairsReturn { isLoading: boolean isError: boolean data: [PairState, Pair | null][] } export function usePairs( chainId: number | undefined, currencies: [Currency | undefined, Currency | undefined][], enabled = true, ): UsePairsReturn { const api = useApi(chainId) const [tokensA, tokensB] = useMemo(() => getPairs(chainId, currencies), [chainId, currencies]) const [validTokensA, validTokensB, reservesCalls] = useMemo( () => tokensA.reduce<[Token[], Token[], [QueryableStorageEntry<'promise'>, ...unknown[]][]]>( (acc, tokenA, i) => { const tokenB = tokensB[i] const pairKey = uniqePairKey(tokenA, tokenB) const pairAccount = PAIR_ADDRESSES[pairKey]?.account if (pairAccount && api) { acc[0].push(tokenA) acc[1].push(tokenB) if (isNativeCurrency(tokenA)) acc[2].push([api.query.system.account, pairAccount]) else acc[2].push([api.query.tokens.accounts, [pairAccount, addressToNodeCurrency(tokenA.address)]]) if (isNativeCurrency(tokenB)) acc[2].push([api.query.system.account, pairAccount]) else acc[2].push([api.query.tokens.accounts, [pairAccount, addressToNodeCurrency(tokenB.address)]]) } return acc }, [[], [], []], ), [api, tokensA, tokensB], ) const reserves = useCallMulti<(OrmlTokensAccountData | FrameSystemAccountInfo)[]>({ chainId, calls: reservesCalls, options: { defaultValue: [], enabled: enabled && !!api }, }) return useMemo(() => { if (!reservesCalls.length) return { isLoading: true, isError: false, data: [[PairState.NOT_EXISTS, null]] } if (!reserves.length || reserves.length !== validTokensA.length * 2) { return { isLoading: true, isError: false, data: validTokensA.map(() => [PairState.LOADING, null]), } } return { isLoading: Boolean(reserves.length), isError: false, data: validTokensA.map((tokenA, i) => { const tokenB = validTokensB[i] if (!tokenA || !tokenB || tokenA.equals(tokenB)) return [PairState.INVALID, null] const pairKey = uniqePairKey(tokenA, tokenB) const reserve0 = reserves[i * 2] const reserve1 = reserves[i * 2 + 1] const pairAddress = PAIR_ADDRESSES[pairKey]?.address if (!reserve0 || !reserve1 || reserve0.isEmpty || reserve1.isEmpty || !pairAddress) return [PairState.NOT_EXISTS, null] return [ PairState.EXISTS, new Pair( Amount.fromRawAmount( tokenA, ((reserve0 as FrameSystemAccountInfo).data || reserve0).free.toString(), ), Amount.fromRawAmount( tokenB, ((reserve1 as FrameSystemAccountInfo).data || reserve1).free.toString(), ), pairAddress, ), ] }), } }, [reserves, reservesCalls.length, validTokensA, validTokensB]) } interface UsePairReturn { isLoading: boolean isError: boolean data: [PairState, Pair | null] } export function usePair( chainId: number, tokenA?: Currency, tokenB?: Currency, enabled?: boolean, ): UsePairReturn { const inputs: [[Currency | undefined, Currency | undefined]] = useMemo(() => [[tokenA, tokenB]], [tokenA, tokenB]) const { data, isLoading, isError } = usePairs(chainId, inputs, enabled) return useMemo( () => ({ isLoading, isError, data: data?.[0], }), [data, isError, isLoading], ) }