diff --git a/VoterWeightPlugins/clients/PythVoterWeightPluginClient.ts b/VoterWeightPlugins/clients/PythVoterWeightPluginClient.ts index 9c7c39836b..238783579d 100644 --- a/VoterWeightPlugins/clients/PythVoterWeightPluginClient.ts +++ b/VoterWeightPlugins/clients/PythVoterWeightPluginClient.ts @@ -1,13 +1,13 @@ import {Client} from "@solana/governance-program-library"; import {PublicKey, TransactionInstruction} from "@solana/web3.js"; import BN from "bn.js"; -import {PythClient, StakeAccount, StakeConnection} from "@pythnetwork/staking"; -import {Provider, Wallet} from "@coral-xyz/anchor"; +import {Program, Provider, Wallet} from "@coral-xyz/anchor"; import {VoterWeightAction} from "@solana/spl-governance"; import {convertVoterWeightActionToType} from "../lib/utils"; import queryClient from "@hooks/queries/queryClient"; +import { getMaxVoterWeightRecordAddress, getVoterWeightRecordAddress, PythStakingClient, StakeAccountPositions } from "@pythnetwork/staking-sdk"; -// A wrapper for the PythClient from @pythnetwork/staking, that implements the generic plugin client interface +// A wrapper for the PythClient from @pythnetwork/staking-sdk, that implements the generic plugin client interface export class PythVoterWeightPluginClient extends Client { readonly requiresInputVoterWeight = false; // The pyth plugin does not have a registrar account @@ -16,23 +16,36 @@ export class PythVoterWeightPluginClient extends Client { } async getMaxVoterWeightRecordPDA() { - const maxVoterWeightPk = (await this.client.program.methods.updateMaxVoterWeight().pubkeys()).maxVoterRecord - - if (!maxVoterWeightPk) return null; + const [address, bump] = getMaxVoterWeightRecordAddress(); return { - maxVoterWeightPk, - maxVoterWeightRecordBump: 0 // This is wrong for Pyth - but it doesn't matter as it is not used + maxVoterWeightPk: address, + maxVoterWeightRecordBump: bump, } } + async getMaxVoterWeightRecord(realm: PublicKey, mint: PublicKey) { + const {maxVoterWeightPk} = await this.getMaxVoterWeightRecordPDA(); + return this.client.stakingProgram.account.maxVoterWeightRecord.fetch( + maxVoterWeightPk, + ); + } + async getVoterWeightRecordPDA(realm: PublicKey, mint: PublicKey, voter: PublicKey) { - const { voterWeightAccount } = await this.getUpdateVoterWeightPks([], voter, VoterWeightAction.CastVote, PublicKey.default); + const stakeAccount = await this.getStakeAccount(voter) + const [address, bump] = getVoterWeightRecordAddress(stakeAccount); return { - voterWeightPk: voterWeightAccount, - voterWeightRecordBump: 0 // This is wrong for Pyth - but it doesn't matter as it is not used - }; + voterWeightPk: address, + voterWeightRecordBump: bump, + } + } + + async getVoterWeightRecord(realm: PublicKey, mint: PublicKey, walletPk: PublicKey) { + const {voterWeightPk} = await this.getVoterWeightRecordPDA(realm, mint, walletPk); + return this.client.stakingProgram.account.voterWeightRecord.fetch( + voterWeightPk, + ); } // NO-OP Pyth records are created through the Pyth dApp. @@ -45,54 +58,64 @@ export class PythVoterWeightPluginClient extends Client { return null; } - private async getStakeAccount(voter: PublicKey): Promise { + private async getStakeAccount(voter: PublicKey): Promise { return queryClient.fetchQuery({ - queryKey: ['pyth getStakeAccount', voter], - queryFn: () => this.client.getMainAccount(voter), + queryKey: ['pyth getStakeAccount', voter.toBase58()], + queryFn: () => this.client.getMainStakeAccount(voter).then(x => x?.stakeAccountPosition), }) } - private async getUpdateVoterWeightPks(instructions: TransactionInstruction[], voter: PublicKey, action: VoterWeightAction, target?: PublicKey) { + async updateVoterWeightRecord( + voter: PublicKey, + realm: PublicKey, + mint: PublicKey, + action: VoterWeightAction, + inputRecordCallback?: () => Promise, + target?: PublicKey + ) { const stakeAccount = await this.getStakeAccount(voter) - if (!stakeAccount) throw new Error("Stake account not found for voter " + voter.toString()); - return this.client.withUpdateVoterWeight( - instructions, + const ix = await this.client.getUpdateVoterWeightInstruction( stakeAccount, { [convertVoterWeightActionToType(action)]: {} } as any, - target - ); - } - - async updateVoterWeightRecord(voter: PublicKey, realm: PublicKey, mint: PublicKey, action: VoterWeightAction, inputRecordCallback?: () => Promise, target?: PublicKey) { - const instructions: TransactionInstruction[] = []; - await this.getUpdateVoterWeightPks(instructions, voter, action, target); - - return { pre: instructions }; + target, + ) + + return { pre: [ix] }; } // NO-OP async updateMaxVoterWeightRecord(): Promise { return null; } async calculateVoterWeight(voter: PublicKey): Promise { - const stakeAccount = await this.getStakeAccount(voter) - - if (stakeAccount) { - return stakeAccount.getVoterWeight(await this.client.getTime()).toBN() - } else { - return new BN(0) - } + const voterWeight = await this.client.getVoterWeight(voter); + return new BN(voterWeight.toString()); } - constructor(program: typeof PythClient.prototype.program, private client: StakeConnection, devnet:boolean) { - super(program, devnet); + + constructor( + program: Program, + private client: PythStakingClient + ) { + super(program); } - static async connect(provider: Provider, devnet = false, wallet: Wallet): Promise { - const pythClient = await PythClient.connect( - provider.connection, - wallet - ) + static async connect(provider: Provider, programId: PublicKey, wallet: Wallet): Promise { + const pythClient = new PythStakingClient({ + connection: provider.connection, + wallet, + }) + + const dummyProgram = new Program( + { + version: "", + name: 'unrecognised', + accounts: [], + instructions: [] + }, + programId, + provider + ); - return new PythVoterWeightPluginClient(pythClient.program, pythClient, devnet); + return new PythVoterWeightPluginClient(dummyProgram, pythClient); } } \ No newline at end of file diff --git a/VoterWeightPlugins/clients/index.ts b/VoterWeightPlugins/clients/index.ts index b24542a753..8c8a1a4325 100644 --- a/VoterWeightPlugins/clients/index.ts +++ b/VoterWeightPlugins/clients/index.ts @@ -36,7 +36,7 @@ export const loadClient = ( case 'gateway': return GatewayClient.connect(provider) case 'pyth': - return PythVoterWeightPluginClient.connect(provider, undefined, signer) + return PythVoterWeightPluginClient.connect(provider, programId, signer) case 'VSR': return VsrClient.connect(provider, programId) case 'HeliumVSR': diff --git a/components/App.tsx b/components/App.tsx index 97d67b43e3..a199520b87 100644 --- a/components/App.tsx +++ b/components/App.tsx @@ -37,6 +37,7 @@ import { tryParsePublicKey } from '@tools/core/pubkey' import { useAsync } from 'react-async-hook' import { useVsrClient } from '../VoterWeightPlugins/useVsrClient' import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins' +import TermsPopupModal from './TermsPopup' const Notifications = dynamic(() => import('../components/Notification'), { ssr: false, @@ -327,6 +328,7 @@ export function AppContents(props: Props) { {props.children} + diff --git a/components/Footer.tsx b/components/Footer.tsx index 892a9166ff..26ea1dba66 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -67,7 +67,7 @@ const Footer = () => {
- © 2023 Solana Technology Services LLC + © 2024 Realms Today Ltd
| diff --git a/components/TermsPopup.tsx b/components/TermsPopup.tsx new file mode 100644 index 0000000000..a2360e0c6e --- /dev/null +++ b/components/TermsPopup.tsx @@ -0,0 +1,57 @@ +import Modal from "./Modal" +import Button, { SecondaryButton } from './Button' +import { useEffect, useState } from "react" +import { useRouter } from "next/router" + +const TermsPopupModal = () => { + const [openModal, setOpenModal] = useState(true) + const [isClient, setIsClient] = useState(false) + const router = useRouter() + + useEffect(() => { + setIsClient(true) + }, []) + + useEffect(() => { + if (localStorage) { + const isTermAccepted = typeof window !== "undefined" ? + localStorage.getItem("accept-terms") === "true" : + false + + if (isTermAccepted) { + setOpenModal(false) + } + } + }) + + const acceptTerms = () => { + localStorage.setItem("accept-terms", "true") + setOpenModal(false) + } + + const rejectTerms = () => { + localStorage.setItem("accept-terms", "false") + router.push("https://realms.today?terms=rejected") + } + + return ( + <> + {isClient && openModal ? + ( setOpenModal(false)} bgClickClose={false} hideClose={true}> +

+ The operating entity of this site and owner of the related intellectual property has + changed. The new operator is Realms Today Ltd. (the New Operator). We have accordingly + amended the Terms and the Private Policy governing the relationship between our users + and the New Operator. By clicking "accept", you represent and warrant that you agree to + the revised Terms and Private Policy. +

+
+ + Reject +
+
) : null + } + ) +} + +export default TermsPopupModal; \ No newline at end of file diff --git a/hooks/PythNetwork/useScalingFactor.ts b/hooks/PythNetwork/useScalingFactor.ts index 7f76becfe7..b14f0ecb82 100644 --- a/hooks/PythNetwork/useScalingFactor.ts +++ b/hooks/PythNetwork/useScalingFactor.ts @@ -1,10 +1,10 @@ import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; import { determineVotingPowerType } from "@hooks/queries/governancePower"; import useSelectedRealmPubkey from "@hooks/selectedRealm/useSelectedRealmPubkey"; -import { PythClient } from "@pythnetwork/staking"; import { useConnection } from "@solana/wallet-adapter-react"; import { useAsync } from "react-async-hook"; import { useQuery } from "@tanstack/react-query"; +import { PythStakingClient } from "@pythnetwork/staking-sdk"; /** * Returns undefined for everything except the Pyth DAO @@ -20,7 +20,9 @@ export default function usePythScalingFactor(): number | undefined { const { data: scalingFactor } = useQuery(["pyth-scaling-factor"], async (): Promise => { - const pythClient = await PythClient.connect(connection, {} as NodeWallet) + const pythClient = new PythStakingClient({ + connection, + }) return pythClient.getScalingFactor() }, { enabled: plugin == "pyth" }) diff --git a/hooks/queries/governancePower.tsx b/hooks/queries/governancePower.tsx index 12918efad4..8e3fdebf18 100644 --- a/hooks/queries/governancePower.tsx +++ b/hooks/queries/governancePower.tsx @@ -16,7 +16,7 @@ import { StakeConnection } from "@parcl-oss/staking" import { LegacyVoterWeightAdapter, } from '@models/voteWeights' -import { PythClient } from '@pythnetwork/staking' +import { PythStakingClient } from "@pythnetwork/staking-sdk"; import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet' import { findPluginName } from '@constants/plugins' import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins' @@ -102,17 +102,12 @@ export const getPythGovPower = async ( ): Promise => { if (!user) return new BN(0) - const pythClient = await PythClient.connect( + const pythClient = new PythStakingClient({ connection, - new NodeWallet(new Keypair()) - ) - const stakeAccount = await pythClient.getMainAccount(user) + }) + const voterWeight = await pythClient.getVoterWeight(user) - if (stakeAccount) { - return stakeAccount.getVoterWeight(await pythClient.getTime()).toBN() - } else { - return new BN(0) - } + return new BN(voterWeight.toString()) } export const getParclGovPower = async ( 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/hub/components/GlobalFooter/index.tsx b/hub/components/GlobalFooter/index.tsx index 00dd0ab813..1aef768a90 100644 --- a/hub/components/GlobalFooter/index.tsx +++ b/hub/components/GlobalFooter/index.tsx @@ -42,7 +42,7 @@ export function GlobalFooter(props: Props) { 'sm:text-sm', )} > -
© 2022 Solana Technology Services LLC
+
© 2024 Realms Today Ltd
|
Terms diff --git a/package.json b/package.json index 651344a47c..e15a22cc32 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@blockworks-foundation/mango-mints-redemption": "0.0.11", - "@blockworks-foundation/mango-v4": "0.33.4", + "@blockworks-foundation/mango-v4": "0.33.5", "@blockworks-foundation/mango-v4-settings": "0.14.24", "@blockworks-foundation/mangolana": "0.0.18", "@bonfida/spl-name-service": "0.1.47", @@ -69,7 +69,8 @@ "@project-serum/serum": "0.13.65", "@project-serum/sol-wallet-adapter": "0.2.6", "@pythnetwork/client": "2.17.0", - "@pythnetwork/staking": "2.3.1", + "@pythnetwork/staking-sdk": "0.0.2", + "@pythnetwork/staking-wasm": "0.3.5", "@radix-ui/react-accordion": "1.0.0", "@radix-ui/react-aspect-ratio": "1.0.0", "@radix-ui/react-dialog": "1.0.0", @@ -228,6 +229,7 @@ "@types/bn.js": "5.1.0", "@project-serum/sol-wallet-adapter": "0.2.6", "@project-serum/serum": "0.13.65", + "@pythnetwork/staking-sdk/@coral-xyz/anchor": "0.30.1", "@coral-xyz/anchor": "0.29.0", "@coral-xyz/borsh": "0.27.0", "bignumber.js": "9.0.2", diff --git a/pages/dao/[symbol]/proposal/components/instructions/Mango/MangoV4/EditToken.tsx b/pages/dao/[symbol]/proposal/components/instructions/Mango/MangoV4/EditToken.tsx index b28b6f93cf..191bf7b5d2 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Mango/MangoV4/EditToken.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Mango/MangoV4/EditToken.tsx @@ -436,7 +436,7 @@ const EditToken = ({ const currentToken = mangoGroup!.banksMapByMint.get(formTokenPk)![0] const groupInsuranceFund = mangoGroup.mintInfosMapByMint.get(formTokenPk) ?.groupInsuranceFund - + console.log(currentToken.tier) const vals = { oraclePk: currentToken.oracle.toBase58(), fallbackOracle: currentToken.fallbackOracle.toBase58(), @@ -488,6 +488,7 @@ const EditToken = ({ platformLiquidationFee: currentToken.platformLiquidationFee.toNumber(), collateralFeePerDay: currentToken.collateralFeePerDay, disableAssetLiquidation: !currentToken.allowAssetLiquidation, + tier: currentToken.tier, } setForm((prevForm) => ({ ...prevForm, @@ -917,6 +918,13 @@ const EditToken = ({ type: InstructionInputType.SWITCH, name: 'forceWithdraw', }, + { + label: keyToLabel['tier'], + subtitle: getAdditionalLabelInfo('tier'), + initialValue: form.tier, + type: InstructionInputType.INPUT, + name: 'tier', + }, ] return ( diff --git a/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythRecoverAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythRecoverAccount.tsx index 22c6f2c5b6..2ddcac791a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythRecoverAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythRecoverAccount.tsx @@ -13,8 +13,7 @@ import { NewProposalContext } from '../../../new' import useLegacyConnectionContext from '@hooks/useLegacyConnectionContext' import { PublicKey } from '@solana/web3.js' import { AssetAccount } from '@utils/uiTypes/assets' -import { StakeConnection, STAKING_ADDRESS } from '@pythnetwork/staking' -import { Wallet } from '@coral-xyz/anchor' +import { PythStakingClient } from '@pythnetwork/staking-sdk' export interface PythRecoverAccountForm { governedAccount: AssetAccount | null @@ -52,14 +51,12 @@ const PythRecoverAccount = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { - const stakeConnection = await StakeConnection.createStakeConnection( - connection.current, - {} as Wallet, - STAKING_ADDRESS - ) + const pythClient = new PythStakingClient({ + connection: connection.current, + }) const stakeAccountPublicKey = new PublicKey(form.stakeAccount) - const instruction = await stakeConnection.buildRecoverAccountInstruction( + const instruction = await pythClient.getRecoverAccountInstruction( stakeAccountPublicKey, form.governedAccount.governance.pubkey ) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythUpdatePoolAuthority.tsx b/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythUpdatePoolAuthority.tsx index 5d5a32fff7..6a050cfc68 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythUpdatePoolAuthority.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Pyth/PythUpdatePoolAuthority.tsx @@ -13,8 +13,7 @@ import { NewProposalContext } from '../../../new' import useLegacyConnectionContext from '@hooks/useLegacyConnectionContext' import { PublicKey, TransactionInstruction } from '@solana/web3.js' import { AssetAccount } from '@utils/uiTypes/assets' -import { StakeConnection, STAKING_ADDRESS } from '@pythnetwork/staking' -import { Wallet } from '@coral-xyz/anchor' +import { PythStakingClient, getConfigAddress } from '@pythnetwork/staking-sdk' export interface PythUpdatePoolAuthorityForm { governedAccount: AssetAccount | null @@ -54,19 +53,19 @@ const PythUpdatePoolAuthority = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { - const stakeConnection = await StakeConnection.createStakeConnection( - connection.current, - {} as Wallet, - STAKING_ADDRESS - ) + const pythClient = new PythStakingClient({ + connection: connection.current, + }) + + const [configAddress, _] = getConfigAddress(); const poolAuthorityPublicKey = new PublicKey(form.poolAuthority) const instruction : TransactionInstruction = { keys: [ {pubkey : form.governedAccount.governance.pubkey, isSigner: true, isWritable: false}, - {pubkey : stakeConnection.configAddress, isSigner: false, isWritable: true}, + {pubkey : configAddress, isSigner: false, isWritable: true}, ], - programId : stakeConnection.program.programId, + programId : pythClient.stakingProgram.programId, data : Buffer.concat([INSTRUCTION_DISCRIMINATOR, poolAuthorityPublicKey.toBuffer()]) } diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index c7b4c4aac1..49bfafc658 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -749,6 +749,18 @@ "website": "https://app.goblin.gold/", "twitter": "@goblingold_fi" }, + { + "symbol": "pumpkinSOL", + "displayName": "Pumpkin's Pool", + "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "realmId": "EpvSHe3dnTQvz5qK8ks7zEbzA3ZABqaXW7fJfr8ULFiy", + "ogImage": "/realms/pumpkinspool/pumpkin_logo_cropped.png", + "bannerImage": "/realms/pumpkinspool/pumpkin_header.png", + "website": "https:/pumpkinspool.eco", + "twitter": "@pumpkinspool", + "discord": "https://discord.gg/pF6pMHApwP", + "shortDescription": "Pumpkin’s Pool delivers top staking returns while supporting animal welfare." + }, { "symbol": "DAINV", "displayName": "Digital Asset Investing", @@ -3660,5 +3672,16 @@ "website": "https://ikb-token.co", "twitter": "@ikb_token", "telegram": "https://t.me/+8Fzp2CFx2yZiMDI5" - } -] + }, + { + "symbol": "MRGY", + "displayName": "Mergey", + "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "realmId": "GgXxU6HPCMFmwYkuFdbkm8QAvVhWiexraZAy9CX4CYRU", + "communityMint": "4i1ZW16fF5EHunJ9hCV4nqz9ckmeM6hg11BREy5x2Knw", + "ogImage": "/realms/mergey/logo.png", + "shortDescription": "Acquire. Merge. Divest. Permissionless memecoin M&A on Solana.", + "website": "https://mergey.fun", + "twitter": "@mergeydotfun", + "telegram": "https://t.me/+vUtROD2-n_4xNzZh" + } diff --git a/public/realms/mergey/logo.png b/public/realms/mergey/logo.png new file mode 100644 index 0000000000..e0f64a8935 Binary files /dev/null and b/public/realms/mergey/logo.png differ diff --git a/public/realms/pumpkinspool/pumpkin_header.png b/public/realms/pumpkinspool/pumpkin_header.png new file mode 100644 index 0000000000..0017c2bb15 Binary files /dev/null and b/public/realms/pumpkinspool/pumpkin_header.png differ diff --git a/public/realms/pumpkinspool/pumpkin_logo_cropped.png b/public/realms/pumpkinspool/pumpkin_logo_cropped.png new file mode 100644 index 0000000000..6b0c0cca3f Binary files /dev/null and b/public/realms/pumpkinspool/pumpkin_logo_cropped.png differ diff --git a/utils/Mango/listingTools.ts b/utils/Mango/listingTools.ts index bce0cecc14..a995aa4f23 100644 --- a/utils/Mango/listingTools.ts +++ b/utils/Mango/listingTools.ts @@ -644,7 +644,7 @@ export const getFormattedBankValues = (group: Group, bank: Bank) => { vault: bank.vault.toBase58(), oracle: bank.oracle.toBase58(), fallbackOracle: bank.fallbackOracle.toBase58(), - stablePrice: group.toUiPrice( + stablePrice: toUiDecimals( I80F48.fromNumber(bank.stablePriceModel.stablePrice), bank.mintDecimals ), 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, + } +} diff --git a/verify-wallet/components/footer.tsx b/verify-wallet/components/footer.tsx index d5265f6a12..19b89c6b26 100644 --- a/verify-wallet/components/footer.tsx +++ b/verify-wallet/components/footer.tsx @@ -33,7 +33,7 @@ export function GlobalFooter(props: Props) { 'sm:text-sm', )} > -
© {year} Solana Technology Services LLC
+
© {year} Realms Today Ltd
|
Terms diff --git a/yarn.lock b/yarn.lock index 7dd9d510ff..58880c4719 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,10 +350,10 @@ bn.js "^5.2.1" eslint-config-prettier "^9.0.0" -"@blockworks-foundation/mango-v4@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.33.4.tgz#11f17eb066e1e3456f93b67e67b2920ee9a2872c" - integrity sha512-ASsxGYoXTFIYx2ougn9OseUi/y8Vd+KZJ4tFknhB8VtfV8RQ8iTcDVaaVhyZ6zfLJ2D0JBcwFS/DIfY7kzQKug== +"@blockworks-foundation/mango-v4@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.33.5.tgz#56ff1c3d6120acfb1abc6af937017b2517ee2348" + integrity sha512-MNACUyUpWlLRDh4EAYdXaPlioQYaN2wBsn2gLfvJllTsmyWq4CDCUZv6gtvcTe9XFxxLQpzMBrgm7nADmzCEHQ== dependencies: "@blockworks-foundation/mango-v4-settings" "0.14.24" "@blockworks-foundation/mangolana" "0.1.0" @@ -709,7 +709,7 @@ dependencies: ajv "^8.12.0" -"@coral-xyz/anchor-30@npm:@coral-xyz/anchor@0.30.1", "switchboard-anchor@npm:@coral-xyz/anchor@0.30.1": +"@coral-xyz/anchor-30@npm:@coral-xyz/anchor@0.30.1", "@coral-xyz/anchor@0.30.1", "@coral-xyz/anchor@^0.30.1", "switchboard-anchor@npm:@coral-xyz/anchor@0.30.1": version "0.30.1" resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.1.tgz#17f3e9134c28cd0ea83574c6bab4e410bcecec5d" integrity sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ== @@ -3213,7 +3213,7 @@ "@coral-xyz/borsh" "^0.28.0" buffer "^6.0.1" -"@pythnetwork/solana-utils@^0.4.1": +"@pythnetwork/solana-utils@0.4.2", "@pythnetwork/solana-utils@^0.4.1": version "0.4.2" resolved "https://registry.yarnpkg.com/@pythnetwork/solana-utils/-/solana-utils-0.4.2.tgz#3e220eed518c02ad702ebb023488afd7c5649a87" integrity sha512-hKo7Bcs/kDWA5Fnqhg9zJSB94NMoUDIDjHjSi/uvZOzwizISUQI6oY3LWd2CXzNh4f8djjY2BS5iNHaM4cm8Bw== @@ -3223,26 +3223,21 @@ bs58 "^5.0.0" jito-ts "^3.0.1" -"@pythnetwork/staking-wasm@*": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@pythnetwork/staking-wasm/-/staking-wasm-0.3.4.tgz#29c0f47467249a4c66814a8aab9eadf2cc008add" - integrity sha512-0ZdaWmueVO5hucdVH4UDfHyBuxtW6UDcrpEFtD/3pq4naQjcgu1u6rK8iL2pgKi8W2UlsB4vwJqay2Sf1sA4mw== - -"@pythnetwork/staking@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@pythnetwork/staking/-/staking-2.3.1.tgz#53f7ae9f9941fbea7e1464fe822fad60d2be8b63" - integrity sha512-TX5irBFy+1dhOxdRjC6NbcSCw33ZiAq9lbTb32mA/dnD6HgK9qPNg1TyflM3Bqa76VqlBP8uiGGKQYSuaCFAWw== +"@pythnetwork/staking-sdk@0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@pythnetwork/staking-sdk/-/staking-sdk-0.0.2.tgz#38dfd6f8356c6544875c6d06aafa55886b7bc1a5" + integrity sha512-IfHJIJ5ZwF8x86/10/5Gs9SUAj4fgW013OiWekfzXhogI2MT27TFQqpaCGj8ndJ4XVlzMk3MDGRuOmujtGCVGA== dependencies: - "@coral-xyz/anchor" "^0.29.0" - "@pythnetwork/solana-utils" "^0.4.1" - "@pythnetwork/staking-wasm" "*" - "@solana/spl-governance" "0.3.26" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "^1.87.5" - encoding "^0.1.13" - ethers "^6.10.0" - ts-node "^10.7.0" - typescript "^4.3.5" + "@coral-xyz/anchor" "^0.30.1" + "@pythnetwork/solana-utils" "0.4.2" + "@solana/spl-governance" "^0.3.28" + "@solana/spl-token" "^0.3.7" + "@solana/web3.js" "^1.95.3" + +"@pythnetwork/staking-wasm@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@pythnetwork/staking-wasm/-/staking-wasm-0.3.5.tgz#a5e524a721df9613a1f33a1a1c708de0bfcfe89f" + integrity sha512-iQpPPn6kPuc9Q1MO+PMAKAhkuv6AOUHiAZJizCbPm3aO7kS2YxRKsRSMsky9FY1GHHatSx13LX9epsJXi/MmKg== "@radix-ui/number@1.0.0": version "1.0.0" @@ -4853,7 +4848,7 @@ "@solana/spl-token-metadata" "^0.1.3" buffer "^6.0.3" -"@solana/spl-token@^0.3.11", "@solana/spl-token@^0.3.4", "@solana/spl-token@^0.3.5", "@solana/spl-token@^0.3.6", "@solana/spl-token@^0.3.8", "@solana/spl-token@^0.3.9": +"@solana/spl-token@^0.3.11", "@solana/spl-token@^0.3.4", "@solana/spl-token@^0.3.5", "@solana/spl-token@^0.3.6", "@solana/spl-token@^0.3.7", "@solana/spl-token@^0.3.8", "@solana/spl-token@^0.3.9": version "0.3.11" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.11.tgz#cdc10f9472b29b39c8983c92592cadd06627fb9a" integrity sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ== @@ -5368,7 +5363,7 @@ "@solana/wallet-standard-core" "^1.1.1" "@solana/wallet-standard-wallet-adapter" "^1.1.2" -"@solana/web3.js@1.56.0", "@solana/web3.js@1.78.8", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.31.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.35.1", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.43.4", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.50.1", "@solana/web3.js@^1.53.0", "@solana/web3.js@^1.54.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.59.1", "@solana/web3.js@^1.63.0", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.0", "@solana/web3.js@^1.73.2", "@solana/web3.js@^1.77.3", "@solana/web3.js@^1.87.5", "@solana/web3.js@^1.87.6", "@solana/web3.js@^1.89.1", "@solana/web3.js@^1.90.0", "@solana/web3.js@^1.91.6", "@solana/web3.js@^1.91.8", "@solana/web3.js@^1.93.0", "@solana/web3.js@^1.95.0", "@solana/web3.js@^1.95.2", "@solana/web3.js@~1.77.3": +"@solana/web3.js@1.56.0", "@solana/web3.js@1.78.8", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.31.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.35.1", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.43.4", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.50.1", "@solana/web3.js@^1.53.0", "@solana/web3.js@^1.54.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.59.1", "@solana/web3.js@^1.63.0", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.0", "@solana/web3.js@^1.73.2", "@solana/web3.js@^1.77.3", "@solana/web3.js@^1.87.5", "@solana/web3.js@^1.87.6", "@solana/web3.js@^1.89.1", "@solana/web3.js@^1.90.0", "@solana/web3.js@^1.91.6", "@solana/web3.js@^1.91.8", "@solana/web3.js@^1.93.0", "@solana/web3.js@^1.95.0", "@solana/web3.js@^1.95.2", "@solana/web3.js@^1.95.3", "@solana/web3.js@~1.77.3": version "1.78.8" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.78.8.tgz#d635bcccaac9e36270c6b16340bfeb5502737831" integrity sha512-y6kMa0ohRjamBGtxIGX4TkdAzL8Cs2bzM4JDPCyYLFPdo7kWk0Cx+BkbhX8hEV4IfvCONF92KIniV7hDvHuq8A==