From e8f3f51507dc1c72b185fc0d61d8cf9d563788c7 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Wed, 17 Apr 2024 13:19:39 -0500 Subject: [PATCH] fix: runes ui --- .../runes-asset-item.layout.tsx | 6 +-- .../runes-asset-list/runes-asset-list.tsx | 2 +- src/app/components/loaders/runes-loader.tsx | 6 +-- src/app/query/bitcoin/bitcoin-client.ts | 41 ++++++++++++++++++- .../bitcoin/runes/runes-ticker-info.query.ts | 24 +++++++++++ src/app/query/bitcoin/runes/runes.hooks.ts | 33 ++++++++++++--- .../bitcoin/stamps/stamps-by-address.hooks.ts | 4 +- 7 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 src/app/query/bitcoin/runes/runes-ticker-info.query.ts diff --git a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx b/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx index 01501396b3d..130885b777d 100644 --- a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx @@ -18,8 +18,8 @@ export function RunesAssetItemLayout({ rune }: RunesAssetItemLayoutProps) { } - titleLeft={rune.rune_name.toUpperCase()} - captionLeft="RUNE" + titleLeft={rune.spaced_rune_name ?? rune.rune_name} + captionLeft="Runes" titleRight={ - {formattedBalance.value} + {formattedBalance.value} {rune.symbol} } diff --git a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx index a862ad36d8d..deaebbcbe66 100644 --- a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx +++ b/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx @@ -6,5 +6,5 @@ interface RunesAssetListProps { runes: RuneToken[]; } export function RunesAssetList({ runes }: RunesAssetListProps) { - return runes.map(rune => ); + return runes.map((rune, i) => ); } diff --git a/src/app/components/loaders/runes-loader.tsx b/src/app/components/loaders/runes-loader.tsx index 333be639ba7..18e81e21d15 100644 --- a/src/app/components/loaders/runes-loader.tsx +++ b/src/app/components/loaders/runes-loader.tsx @@ -8,8 +8,6 @@ interface RunesLoaderProps { children(runes: RuneToken[]): React.ReactNode; } export function RunesLoader({ addresses, children }: RunesLoaderProps) { - const runes = useRuneTokens(addresses) - .flatMap(query => query.data) - .filter(isDefined); - return children(runes); + const runes = useRuneTokens(addresses); + return children(runes.filter(isDefined)); } diff --git a/src/app/query/bitcoin/bitcoin-client.ts b/src/app/query/bitcoin/bitcoin-client.ts index 5f86e5633bb..0b9ffaa42d5 100644 --- a/src/app/query/bitcoin/bitcoin-client.ts +++ b/src/app/query/bitcoin/bitcoin-client.ts @@ -116,7 +116,37 @@ interface RunesWalletBalancesResponse { data: RuneBalance[]; } -export interface RuneToken extends RuneBalance { +export interface RuneTickerInfo { + rune_id: string; + rune_number: string; + rune_name: string; + spaced_rune_name: string; + symbol: string; + decimals: number; + per_mint_amount: string; + mint_cnt: string; + mint_cnt_limit: string; + premined_supply: string; + total_minted_supply: string; + burned_supply: string; + circulating_supply: string; + mint_progress: number; + mint_start_block: number | null; + mint_end_block: number | null; + genesis_block: number; + deploy_ts: string; + deploy_txid: string; + auto_upgrade: boolean; + holder_count: number; + event_count: number; + mintable: boolean; +} +interface RunesTickerInfoResponse { + block_height: number; + data: RuneTickerInfo; +} + +export interface RuneToken extends RuneBalance, RuneTickerInfo { balance: Money; } @@ -181,6 +211,15 @@ class BestinslotApi { ); return resp.data.data; } + + async getRunesTickerInfo(runeName: string, network: BitcoinNetworkModes) { + const baseUrl = network === 'mainnet' ? this.url : this.testnetUrl; + const resp = await axios.get( + `${baseUrl}/runes/ticker_info?rune_name=${runeName}`, + { ...this.defaultOptions } + ); + return resp.data.data; + } } class HiroApi { diff --git a/src/app/query/bitcoin/runes/runes-ticker-info.query.ts b/src/app/query/bitcoin/runes/runes-ticker-info.query.ts new file mode 100644 index 00000000000..9f5a3b2ebc0 --- /dev/null +++ b/src/app/query/bitcoin/runes/runes-ticker-info.query.ts @@ -0,0 +1,24 @@ +import { type UseQueryResult, useQueries } from '@tanstack/react-query'; + +import { useConfigRunesEnabled } from '@app/query/common/remote-config/remote-config.query'; +import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; +import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; + +import type { RuneTickerInfo } from '../bitcoin-client'; + +export function useGetRunesTickerInfoQuery(runeNames: string[]): UseQueryResult[] { + const client = useBitcoinClient(); + const network = useCurrentNetwork(); + const runesEnabled = useConfigRunesEnabled(); + + return useQueries({ + queries: runeNames.map(runeName => { + return { + enabled: !!runeName && (network.chain.bitcoin.bitcoinNetwork === 'testnet' || runesEnabled), + queryKey: ['runes-ticker-info', runeName], + queryFn: () => + client.BestinslotApi.getRunesTickerInfo(runeName, network.chain.bitcoin.bitcoinNetwork), + }; + }), + }); +} diff --git a/src/app/query/bitcoin/runes/runes.hooks.ts b/src/app/query/bitcoin/runes/runes.hooks.ts index 1d74ee1b94c..3262162f43a 100644 --- a/src/app/query/bitcoin/runes/runes.hooks.ts +++ b/src/app/query/bitcoin/runes/runes.hooks.ts @@ -1,17 +1,38 @@ +import { logger } from '@shared/logger'; import { createMoney } from '@shared/models/money.model'; +import { isDefined } from '@shared/utils'; -import type { RuneBalance, RuneToken } from '../bitcoin-client'; +import type { RuneBalance, RuneTickerInfo, RuneToken } from '../bitcoin-client'; +import { useGetRunesTickerInfoQuery } from './runes-ticker-info.query'; import { useGetRunesWalletBalancesByAddressesQuery } from './runes-wallet-balances.query'; -function makeRuneToken(rune: RuneBalance): RuneToken { +function makeRuneToken(runeBalance: RuneBalance, tickerInfo: RuneTickerInfo): RuneToken { return { - ...rune, - balance: createMoney(Number(rune.total_balance), rune.rune_name, 0), + ...runeBalance, + ...tickerInfo, + balance: createMoney( + Number(runeBalance.total_balance), + tickerInfo.rune_name, + tickerInfo.decimals + ), }; } export function useRuneTokens(addresses: string[]) { - return useGetRunesWalletBalancesByAddressesQuery(addresses, { - select: resp => resp.map(makeRuneToken), + const runesBalances = useGetRunesWalletBalancesByAddressesQuery(addresses) + .flatMap(query => query.data) + .filter(isDefined); + + const runesTickerInfo = useGetRunesTickerInfoQuery(runesBalances.map(r => r.rune_name)) + .flatMap(query => query.data) + .filter(isDefined); + + return runesBalances.map(r => { + const tickerInfo = runesTickerInfo.find(t => t.rune_name === r.rune_name); + if (!tickerInfo) { + logger.error('No ticker info found for Rune'); + return; + } + return makeRuneToken(r, tickerInfo); }); } diff --git a/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts b/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts index 5e958187b93..1eeb7efca3b 100644 --- a/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts +++ b/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts @@ -3,7 +3,7 @@ import { useStampsByAddressQuery } from './stamps-by-address.query'; export function useStampsByAddress(address: string) { return useStampsByAddressQuery(address, { select(data) { - return data.data.stamps; + return data.data?.stamps; }, }); } @@ -11,7 +11,7 @@ export function useStampsByAddress(address: string) { export function useSrc20TokensByAddress(address: string) { return useStampsByAddressQuery(address, { select(data) { - return data.data.src20; + return data.data?.src20; }, }); }