Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token Logo fix. #34

Merged
merged 10 commits into from
Nov 19, 2024
2 changes: 1 addition & 1 deletion components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function AppContents(props: Props) {
handleRouterHistory()
useHandleGovernanceAssetsStore()
useEffect(() => {
tokenPriceService.fetchSolanaTokenList()
tokenPriceService.fetchSolanaTokenListV2()
}, [])

const { getOwnedDeposits, resetDepositState } = useDepositStore()
Expand Down
45 changes: 28 additions & 17 deletions components/TreasuryAccount/AccountItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { getTreasuryAccountItemInfoV2 } from '@utils/treasuryTools'
import { useEffect, useState, useMemo } from 'react'
import { getTreasuryAccountItemInfoV2Async } from '@utils/treasuryTools'
import { AssetAccount } from '@utils/uiTypes/assets'
import TokenIcon from '@components/treasuryV2/icons/TokenIcon'
import { useTokenMetadata } from '@hooks/queries/tokenMetadata'
Expand All @@ -9,23 +9,38 @@ const AccountItem = ({
}: {
governedAccountTokenAccount: AssetAccount
}) => {
const {
amountFormatted,
logo,
name,
symbol,
displayPrice,
} = getTreasuryAccountItemInfoV2(governedAccountTokenAccount)
const [accountAssetInfo, setAccountAssetInfo] = useState({
amountFormatted: '',
logo: '',
name: '',
symbol: '',
displayPrice: '',
})

useEffect(() => {
const fetchAccounAssetInfo = async () => {
try {
const info = await getTreasuryAccountItemInfoV2Async(governedAccountTokenAccount)
setAccountAssetInfo(info)
} catch (error) {
console.error('Error fetching treasury account info:', error)
}
}

fetchAccounAssetInfo()
}, [governedAccountTokenAccount])

const { data } = useTokenMetadata(
governedAccountTokenAccount.extensions.mint?.publicKey,
!logo
!accountAssetInfo.logo
)

const symbolFromMeta = useMemo(() => {
return data?.symbol
}, [data?.symbol])

const { amountFormatted, logo, name, symbol, displayPrice } = accountAssetInfo

return (
<div className="flex items-center w-full p-3 border rounded-lg text-fgd-1 border-fgd-4">
{logo ? (
Expand All @@ -51,14 +66,10 @@ const AccountItem = ({
<div className="text-xs text-fgd-3">
{amountFormatted} {symbolFromMeta ? symbolFromMeta : symbol}
</div>
{displayPrice ? (
<div className="mt-0.5 text-fgd-3 text-xs">≈${displayPrice}</div>
) : (
''
)}
<div className="mt-0.5 text-fgd-3 text-xs">≈${displayPrice || 0}</div>
</div>
</div>
)
}

export default AccountItem
export default AccountItem
81 changes: 56 additions & 25 deletions components/TreasuryAccount/AccountsItems.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,70 @@
import useGovernanceAssets from '@hooks/useGovernanceAssets'
import { getTreasuryAccountItemInfoV2 } from '@utils/treasuryTools'
import React from 'react'
import { getTreasuryAccountItemInfoV2Async } from '@utils/treasuryTools'
import React, { useEffect, useState } from 'react'
import AccountItem from './AccountItem'
import { AssetAccount } from '@utils/uiTypes/assets'
import Loading from '@components/Loading'

const AccountsItems = () => {
const {
governedTokenAccountsWithoutNfts,
auxiliaryTokenAccounts,
} = useGovernanceAssets()
const accounts = [
...governedTokenAccountsWithoutNfts,
...auxiliaryTokenAccounts,
]
const accountsSorted = accounts
.sort((a, b) => {
const infoA = getTreasuryAccountItemInfoV2(a)
const infoB = getTreasuryAccountItemInfoV2(b)
return infoB.totalPrice - infoA.totalPrice
})
.splice(
0,
Number(process?.env?.MAIN_VIEW_SHOW_MAX_TOP_TOKENS_NUM || accounts.length)
)

const [sortedAccounts, setSortedAccounts] = useState<AssetAccount[]>([])
const [isLoading, setIsLoading] = useState(true)

useEffect(() => {
const sortAccounts = async () => {
try {
setIsLoading(true)
const accounts = [
...governedTokenAccountsWithoutNfts,
...auxiliaryTokenAccounts,
]

// Get all account info in parallel
const accountsWithInfo = await Promise.all(
accounts.map(async (account) => ({
account,
info: await getTreasuryAccountItemInfoV2Async(account)
}))
)

// Sort based on the fetched info
const sorted = accountsWithInfo
.sort((a, b) => b.info.totalPrice - a.info.totalPrice)
.map(({ account }) => account)
.splice(
0,
Number(process?.env?.MAIN_VIEW_SHOW_MAX_TOP_TOKENS_NUM || accounts.length)
)

setSortedAccounts(sorted)
} catch (error) {
console.error('Error sorting accounts:', error)
} finally {
setIsLoading(false)
}
}

sortAccounts()
}, [])

if (isLoading) {
return <div className="space-y-3"><Loading></Loading></div>
}

return (
<div className="space-y-3">
{accountsSorted.map((account) => {
return (
<AccountItem
governedAccountTokenAccount={account}
key={account?.extensions.transferAddress?.toBase58()}
/>
)
})}
{sortedAccounts.map((account) => (
<AccountItem
governedAccountTokenAccount={account}
key={account?.extensions.transferAddress?.toBase58()}
/>
))}
</div>
)
}

export default AccountsItems
export default AccountsItems
7 changes: 6 additions & 1 deletion components/instructions/programs/stake.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ export const STAKE_INSTRUCTIONS = {
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]).decode(Buffer.from(_data))
const rent = await _connection.getMinimumBalanceForRentExemption(200)

// const rent = await _connection.getMinimumBalanceForRentExemption(200)

// > solana rent 200 --lamports
// Rent-exempt minimum: 2282880 lamports
const rent = 2282880
return (
<>
<p>
Expand Down
79 changes: 68 additions & 11 deletions hooks/queries/jupiterPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,51 @@ import { PublicKey } from '@solana/web3.js'
import { useQuery } from '@tanstack/react-query'
import queryClient from './queryClient'

const URL = 'https://price.jup.ag/v4/price'
const URL = 'https://api.jup.ag/price/v2'

/* example query
GET https://price.jup.ag/v4/price?ids=SOL
response: {"data":{"SOL":{"id":"So11111111111111111111111111111111111111112","mintSymbol":"SOL","vsToken":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","vsTokenSymbol":"USDC","price":26.649616441}},"timeTaken":0.0002587199999766199}
# Unit price of 1 JUP & 1 SOL based on the Derived Price in USDC
https://api.jup.ag/price/v2?ids=JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN,So11111111111111111111111111111111111111112

{
"data": {
"So11111111111111111111111111111111111111112": {
"id": "So11111111111111111111111111111111111111112",
"type": "derivedPrice",
"price": "133.890945000"
},
"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": {
"id": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
"type": "derivedPrice",
"price": "0.751467"
}
},
"timeTaken": 0.00395219
}
*/
/* example intentionally broken query
GET https://price.jup.ag/v4/price?ids=bingus
response: {"data":{},"timeTaken":0.00010941000005004753}
curl -X 'GET' 'https://api.jup.ag/price/v2?ids=So11111111111111111111111111111111111111112&showExtraInfo=true'
{
"data": {
"So11111111111111111111111111111111111111112": {
"id": "So11111111111111111111111111111111111111112",
"type": "derivedPrice",
"price": "134.170633378"
},
"8agCopCHWdpj7mHk3JUWrzt8pHAxMiPX5hLVDJh9TXWv": null
},
"timeTaken": 0.003186833
}
*/

type Price = {
id: string // pubkey,
mintSymbol: string
vsToken: string // pubkey,
vsTokenSymbol: string
// price is in USD
price: number
// removed in v2 API
// mintSymbol: string
// vsToken: string // pubkey,
// vsTokenSymbol: string
}
type Response = {
data: Record<string, Price> //uses whatever you input (so, pubkey OR symbol). no entry if data not found
Expand Down Expand Up @@ -76,11 +104,13 @@ export const getJupiterPriceSync = (mint: PublicKey) =>

export const useJupiterPricesByMintsQuery = (mints: PublicKey[]) => {
const enabled = mints.length > 0
const deduped = new Set(mints)
const dedupedMints = Array.from(deduped)
return useQuery({
enabled,
queryKey: jupiterPriceQueryKeys.byMints(mints),
queryKey: jupiterPriceQueryKeys.byMints(dedupedMints),
queryFn: async () => {
const batches = [...chunks(mints, 100)]
const batches = [...chunks(dedupedMints, 100)]
const responses = await Promise.all(
batches.map(async (batch) => {
const x = await fetch(`${URL}?ids=${batch.join(',')}`)
Expand All @@ -106,7 +136,7 @@ export const useJupiterPricesByMintsQuery = (mints: PublicKey[]) => {
return data
},
onSuccess: (data) => {
mints.forEach((mint) =>
dedupedMints.forEach((mint) =>
queryClient.setQueryData(
jupiterPriceQueryKeys.byMint(mint),
data[mint.toString()]
Expand All @@ -117,3 +147,30 @@ export const useJupiterPricesByMintsQuery = (mints: PublicKey[]) => {
},
})
}

// function is used to get fresh token prices
export const getJupiterPricesByMintStrings = async (mints: string[]) => {
if (mints.length === 0) return {}
const deduped = new Set(mints)
const dedupedMints = Array.from(deduped)
try {
const x = await fetch(`${URL}?ids=${dedupedMints.join(',')}`)
const response = (await x.json()) as Response
const data = response.data

//override chai price if its broken
const chaiMint = '3jsFX1tx2Z8ewmamiwSU851GzyzM2DJMq7KWW5DM8Py3'
const chaiData = data[chaiMint]

if (chaiData?.price && (chaiData.price > 1.3 || chaiData.price < 0.9)) {
data[chaiMint] = {
...chaiData,
price: 1,
}
}
return data
} catch (error) {
console.error('Error fetching Jupiter prices:', error)
throw error
}
}
33 changes: 33 additions & 0 deletions hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
type UseStorageReturnValue = {
getItem: (key: string) => string;
setItem: (key: string, value: string) => boolean;
removeItem: (key: string) => void;
};

export const useLocalStorage = (): UseStorageReturnValue => {
const isBrowser: boolean = ((): boolean => typeof window !== "undefined")();

const getItem = (key: string): string => {
return isBrowser ? window.localStorage[key] : "";
};

const setItem = (key: string, value: string): boolean => {
if (isBrowser) {
window.localStorage.setItem(key, value);
return true;
}

return false;
};

const removeItem = (key: string): void => {
window.localStorage.removeItem(key);
};

return {
getItem,
setItem,
removeItem,
};
};

11 changes: 5 additions & 6 deletions hooks/useTreasuryInfo/convertAccountToAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ 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'

import { getAccountAssetCount } from './getAccountAssetCount'
import { fetchJupiterPrice } from '@hooks/queries/jupiterPrice'
import { getAccountValue, getStakeAccountValue } from './getAccountValue'
import tokenPriceService from '@utils/services/tokenPrice'

export const convertAccountToAsset = async (
account: AssetAccount,
councilMintAddress?: string,
communityMintAddress?: string
): Promise<Asset | null> => {
const info = getTreasuryAccountItemInfoV2(account)
const info = await getTreasuryAccountItemInfoV2Async(account)

switch (account.type) {
case AccountType.AUXILIARY_TOKEN:
Expand Down Expand Up @@ -65,8 +66,7 @@ export const convertAccountToAsset = async (
),
price: account.extensions.mint
? new BigNumber(
(await fetchJupiterPrice(account.extensions.mint.publicKey))
.result?.price ?? 0
await tokenPriceService.fetchTokenPrice(account.extensions.mint.publicKey.toString()) ?? 0
)
: undefined,
raw: account,
Expand All @@ -89,8 +89,7 @@ export const convertAccountToAsset = async (
name: info.accountName || info.info?.name || info.name || info.symbol,
price: account.extensions.mint
? new BigNumber(
(await fetchJupiterPrice(account.extensions.mint.publicKey))
.result?.price ?? 0
await tokenPriceService.fetchTokenPrice(account.extensions.mint.publicKey.toString()) ?? 0
)
: undefined,
raw: account,
Expand Down
1 change: 0 additions & 1 deletion hooks/useTreasuryInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export default function useTreasuryInfo(
if (!loadingGovernedAccounts && accounts.length && getNftsAndDomains) {
setDomainsLoading(true)
setBuildingWallets(true)

getDomains(
accounts.filter((acc) => acc.isSol),
connection.current
Expand Down
Loading
Loading