From ceea0617ef5d5bcf0f1d71dd0d050ffa9238873e Mon Sep 17 00:00:00 2001 From: Rit Rafa Date: Tue, 24 Sep 2024 01:35:12 -0400 Subject: [PATCH] Updated jup token fetch to directly load unverified tokens (#2403) --- .../useTreasuryInfo/convertAccountToAsset.tsx | 4 +- utils/services/tokenPrice.tsx | 47 ++++++++++++++ utils/treasuryTools.tsx | 64 +++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) diff --git a/hooks/useTreasuryInfo/convertAccountToAsset.tsx b/hooks/useTreasuryInfo/convertAccountToAsset.tsx index 86611b7f62..32b5d9bf20 100644 --- a/hooks/useTreasuryInfo/convertAccountToAsset.tsx +++ b/hooks/useTreasuryInfo/convertAccountToAsset.tsx @@ -2,7 +2,7 @@ import { BigNumber } from 'bignumber.js' import { AccountType, AssetAccount } from '@utils/uiTypes/assets' import { AssetType, Asset } from '@models/treasury/Asset' -import { getTreasuryAccountItemInfoV2 } from '@utils/treasuryTools' +import { getTreasuryAccountItemInfoV2Async } from '@utils/treasuryTools' import TokenIcon from '@components/treasuryV2/icons/TokenIcon' import { WSOL_MINT } from '@components/instructions/tools' import { abbreviateAddress } from '@utils/formatting' @@ -16,7 +16,7 @@ export const convertAccountToAsset = async ( councilMintAddress?: string, communityMintAddress?: string ): Promise => { - const info = getTreasuryAccountItemInfoV2(account) + const info = await getTreasuryAccountItemInfoV2Async(account) switch (account.type) { case AccountType.AUXILIARY_TOKEN: diff --git a/utils/services/tokenPrice.tsx b/utils/services/tokenPrice.tsx index 1cc3bf6f09..0ff20be263 100644 --- a/utils/services/tokenPrice.tsx +++ b/utils/services/tokenPrice.tsx @@ -12,6 +12,7 @@ import { USDC_MINT } from '@blockworks-foundation/mango-v4' //decimals from metadata can be different from the realm on chain one const priceEndpoint = 'https://price.jup.ag/v4/price' const tokenListUrl = 'https://token.jup.ag/strict' +//const tokenListUrl = 'https://tokens.jup.ag/tokens' // The full list is available but takes much longer to load export type TokenInfoWithoutDecimals = Omit @@ -21,9 +22,11 @@ class TokenPriceService { _tokenPriceToUSDlist: { [mintAddress: string]: Price } + _unverifiedTokenCache: { [mintAddress: string]: TokenInfoWithoutDecimals }; constructor() { this._tokenList = [] this._tokenPriceToUSDlist = {} + this._unverifiedTokenCache = {} } async fetchSolanaTokenList() { try { @@ -114,6 +117,50 @@ class TokenPriceService { ) return tokenListRecord } + + // This async method is used to lookup additional tokens not on JUP's strict list + async getTokenInfoAsync(mintAddress: string): Promise { + if (!mintAddress || mintAddress.trim() === '') { + return undefined; + } + // Check the strict token list first + let tokenListRecord = this._tokenList?.find((x) => x.address === mintAddress); + if (tokenListRecord) { + return tokenListRecord; + } + + // Check the unverified token list cache next to avoid repeatedly loading token metadata + if (this._unverifiedTokenCache[mintAddress]) { + return this._unverifiedTokenCache[mintAddress]; + } + + // Get the token data from JUP's api + const requestURL = `https://tokens.jup.ag/token/${mintAddress}` + const response = await axios.get(requestURL); + + if (response.data) { + // Remove decimals and add chainId to match the TokenInfoWithoutDecimals struct + const { decimals, ...tokenInfoWithoutDecimals } = response.data; + const finalTokenInfo = { + ...tokenInfoWithoutDecimals, + chainId: 101 + }; + + // Add to unverified token cache + this._unverifiedTokenCache[mintAddress] = finalTokenInfo; + + return finalTokenInfo; + } else { + console.error(`Metadata retrieving failed for ${mintAddress}`); + return undefined; + } + } catch (e) { + notify({ + type: 'error', + message: 'Unable to fetch token information', + }); + return undefined; + } /** * For decimals use on chain tryGetMint */ diff --git a/utils/treasuryTools.tsx b/utils/treasuryTools.tsx index dcb18f659f..a1b68a0fde 100644 --- a/utils/treasuryTools.tsx +++ b/utils/treasuryTools.tsx @@ -69,3 +69,67 @@ export const getTreasuryAccountItemInfoV2 = (account: AssetAccount) => { totalPrice, } } + +//This async method added to add a lookup for token metadata not available in JUP's strict list +export const getTreasuryAccountItemInfoV2Async = async (account: AssetAccount) => { + const mintAddress = + account.type === AccountType.SOL + ? WSOL_MINT + : account.extensions.mint?.publicKey.toBase58() + + const amount = + account.extensions.amount && account.extensions.mint + ? getMintDecimalAmountFromNatural( + account.extensions.mint.account, + new BN( + account.isSol + ? account.extensions.solAccount!.lamports + : account.extensions.amount + ) + ).toNumber() + : 0 + const price = tokenPriceService.getUSDTokenPrice(mintAddress!) + const totalPrice = amount * price + const totalPriceFormatted = amount + ? new BigNumber(totalPrice).toFormat(0) + : '' + const info = await tokenPriceService.getTokenInfoAsync(mintAddress!) + + const symbol = + account.type === AccountType.NFT + ? 'NFTS' + : account.type === AccountType.SOL + ? 'SOL' + : info?.symbol + ? info.address === WSOL_MINT + ? 'wSOL' + : info?.symbol + : account.extensions.mint + ? abbreviateAddress(account.extensions.mint.publicKey) + : '' + const amountFormatted = new BigNumber(amount).toFormat() + + const logo = info?.logoURI || '' + const accountName = account.pubkey ? getAccountName(account.pubkey) : '' + const name = accountName + ? accountName + : account.extensions.transferAddress + ? abbreviateAddress(account.extensions.transferAddress as PublicKey) + : '' + + const displayPrice = + totalPriceFormatted && totalPriceFormatted !== '0' + ? totalPriceFormatted + : '' + + return { + accountName, + amountFormatted, + logo, + name, + displayPrice, + info, + symbol, + totalPrice, + } +}