From c38bca9932c1aa6f47180cef04ed4160d7a9aec0 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Fri, 14 Oct 2022 14:41:32 +0800 Subject: [PATCH] Explorer: Refactor account provider data types (#28390) --- .../src/__tests__/parseNFTokenAccounts.ts | 8 +- .../components/account/AnchorAccountCard.tsx | 7 +- .../src/components/account/RewardsCard.tsx | 17 +- .../account/StakeAccountSection.tsx | 2 +- .../account/TokenAccountSection.tsx | 4 +- .../components/account/UnknownAccountCard.tsx | 38 ++-- .../UpgradeableLoaderAccountSection.tsx | 54 +++--- .../AddressLookupTableAccountSection.tsx | 2 +- .../account/nftoken/isNFTokenAccount.ts | 13 +- explorer/src/pages/AccountDetailsPage.tsx | 180 +++++++++--------- .../pages/inspector/AddressWithContext.tsx | 26 ++- explorer/src/providers/accounts/index.tsx | 110 ++++++----- .../providers/accounts/utils/isMetaplexNFT.ts | 12 +- explorer/src/utils/index.ts | 2 +- 14 files changed, 243 insertions(+), 232 deletions(-) diff --git a/explorer/src/__tests__/parseNFTokenAccounts.ts b/explorer/src/__tests__/parseNFTokenAccounts.ts index 4b6fa1b800..b815732a6f 100644 --- a/explorer/src/__tests__/parseNFTokenAccounts.ts +++ b/explorer/src/__tests__/parseNFTokenAccounts.ts @@ -23,8 +23,12 @@ describe("parseNFTokenAccounts", () => { ]); const nftAccount = parseNFTokenNFTAccount({ pubkey: new PublicKey("FagABcRBhZH27JDtu6A1Jo9woXyoznP28QujLkxkN9Hj"), - details: { rawData: buffer, owner: new PublicKey(NFTOKEN_ADDRESS) }, - } as any); + space: buffer.length, + lamports: 1, + executable: false, + data: { raw: buffer as Buffer }, + owner: new PublicKey(NFTOKEN_ADDRESS), + }); expect(nftAccount!.metadata_url).to.eq( "https://cdn.glow.app/n/88/78ef17c1-2b5a-468e-ae8f-7403856e9f00.json" ); diff --git a/explorer/src/components/account/AnchorAccountCard.tsx b/explorer/src/components/account/AnchorAccountCard.tsx index 60920577ec..adc3398a04 100644 --- a/explorer/src/components/account/AnchorAccountCard.tsx +++ b/explorer/src/components/account/AnchorAccountCard.tsx @@ -10,11 +10,8 @@ import { useAnchorProgram } from "providers/anchor"; export function AnchorAccountCard({ account }: { account: Account }) { const { lamports } = account; const { url } = useCluster(); - const anchorProgram = useAnchorProgram( - account.details?.owner.toString() || "", - url - ); - const rawData = account?.details?.rawData; + const anchorProgram = useAnchorProgram(account.owner.toString(), url); + const rawData = account.data.raw; const programName = getAnchorProgramName(anchorProgram) || "Unknown Program"; const { decodedAccountData, accountDef } = useMemo(() => { diff --git a/explorer/src/components/account/RewardsCard.tsx b/explorer/src/components/account/RewardsCard.tsx index 8fd2bea043..1fe82cbb2d 100644 --- a/explorer/src/components/account/RewardsCard.tsx +++ b/explorer/src/components/account/RewardsCard.tsx @@ -7,22 +7,27 @@ import { ErrorCard } from "components/common/ErrorCard"; import { Slot } from "components/common/Slot"; import { lamportsToSolString } from "utils"; import { useAccountInfo } from "providers/accounts"; -import BN from "bn.js"; import { Epoch } from "components/common/Epoch"; -const MAX_EPOCH = new BN(2).pow(new BN(64)).sub(new BN(1)); +const U64_MAX = BigInt("0xffffffffffffffff"); export function RewardsCard({ pubkey }: { pubkey: PublicKey }) { const address = React.useMemo(() => pubkey.toBase58(), [pubkey]); const info = useAccountInfo(address); const account = info?.data; - const data = account?.details?.data?.parsed.info; + const parsedData = account?.data.parsed; const highestEpoch = React.useMemo(() => { - if (data.stake && !data.stake.delegation.deactivationEpoch.eq(MAX_EPOCH)) { - return data.stake.delegation.deactivationEpoch.toNumber(); + if (!parsedData) return; + if (parsedData.program !== "stake") return; + const stakeInfo = parsedData.parsed.info.stake; + if ( + stakeInfo !== null && + stakeInfo.delegation.deactivationEpoch !== U64_MAX + ) { + return Number(stakeInfo.delegation.deactivationEpoch); } - }, [data]); + }, [parsedData]); const rewards = useRewards(address); const fetchRewards = useFetchRewards(); diff --git a/explorer/src/components/account/StakeAccountSection.tsx b/explorer/src/components/account/StakeAccountSection.tsx index fd2bcf2801..88584a2567 100644 --- a/explorer/src/components/account/StakeAccountSection.tsx +++ b/explorer/src/components/account/StakeAccountSection.tsx @@ -125,7 +125,7 @@ function OverviewCard({ Balance (SOL) - + diff --git a/explorer/src/components/account/TokenAccountSection.tsx b/explorer/src/components/account/TokenAccountSection.tsx index 930bf1a720..7213998dfe 100644 --- a/explorer/src/components/account/TokenAccountSection.tsx +++ b/explorer/src/components/account/TokenAccountSection.tsx @@ -54,11 +54,11 @@ export function TokenAccountSection({ case "mint": { const info = create(tokenAccount.info, MintAccountInfo); - if (isMetaplexNFT(account.details?.data, info)) { + if (isMetaplexNFT(account.data.parsed, info)) { return ( ); diff --git a/explorer/src/components/account/UnknownAccountCard.tsx b/explorer/src/components/account/UnknownAccountCard.tsx index 22c73e2cf0..a8826d45d3 100644 --- a/explorer/src/components/account/UnknownAccountCard.tsx +++ b/explorer/src/components/account/UnknownAccountCard.tsx @@ -8,10 +8,8 @@ import { useCluster } from "providers/cluster"; import { useTokenRegistry } from "providers/mints/token-registry"; export function UnknownAccountCard({ account }: { account: Account }) { - const { details, lamports } = account; const { cluster } = useCluster(); const { tokenRegistry } = useTokenRegistry(); - if (lamports === undefined) return null; const label = addressLabel(account.pubkey.toBase58(), cluster, tokenRegistry); return ( @@ -36,32 +34,26 @@ export function UnknownAccountCard({ account }: { account: Account }) { Balance (SOL) - + - {details?.space !== undefined && ( - - Allocated Data Size - {details.space} byte(s) - - )} + + Allocated Data Size + {account.space} byte(s) + - {details && ( - - Assigned Program Id - -
- - - )} + + Assigned Program Id + +
+ + - {details && ( - - Executable - {details.executable ? "Yes" : "No"} - - )} + + Executable + {account.executable ? "Yes" : "No"} + ); diff --git a/explorer/src/components/account/UpgradeableLoaderAccountSection.tsx b/explorer/src/components/account/UpgradeableLoaderAccountSection.tsx index 73450019ce..8a52c0588b 100644 --- a/explorer/src/components/account/UpgradeableLoaderAccountSection.tsx +++ b/explorer/src/components/account/UpgradeableLoaderAccountSection.tsx @@ -104,7 +104,7 @@ export function UpgradeableProgramSection({ Balance (SOL) - + @@ -235,22 +235,20 @@ export function UpgradeableProgramDataSection({ Balance (SOL) - + + + + + Data Size (Bytes) + + + {account.space} + - {account.details?.space !== undefined && ( - - Data (Bytes) - - - {account.details.space} - - - - )} Upgradeable @@ -309,15 +307,13 @@ export function UpgradeableProgramBufferSection({ Balance (SOL) - + - {account.details?.space !== undefined && ( - - Data (Bytes) - {account.details.space} - - )} + + Data Size (Bytes) + {account.space} + {programBuffer.authority !== null && ( Deploy Authority @@ -326,14 +322,12 @@ export function UpgradeableProgramBufferSection({ )} - {account.details && ( - - Owner - -
- - - )} + + Owner + +
+ + ); diff --git a/explorer/src/components/account/address-lookup-table/AddressLookupTableAccountSection.tsx b/explorer/src/components/account/address-lookup-table/AddressLookupTableAccountSection.tsx index e4bd5b0285..9cce7f98ef 100644 --- a/explorer/src/components/account/address-lookup-table/AddressLookupTableAccountSection.tsx +++ b/explorer/src/components/account/address-lookup-table/AddressLookupTableAccountSection.tsx @@ -56,7 +56,7 @@ export function AddressLookupTableAccountSection( Balance (SOL) - + diff --git a/explorer/src/components/account/nftoken/isNFTokenAccount.ts b/explorer/src/components/account/nftoken/isNFTokenAccount.ts index cd37bd8a92..47890ee8f9 100644 --- a/explorer/src/components/account/nftoken/isNFTokenAccount.ts +++ b/explorer/src/components/account/nftoken/isNFTokenAccount.ts @@ -5,8 +5,7 @@ import { NftokenTypes } from "./nftoken-types"; export function isNFTokenAccount(account: Account): boolean { return Boolean( - account.details?.owner.toBase58() === NFTOKEN_ADDRESS && - account.details.rawData + account.owner.toBase58() === NFTOKEN_ADDRESS && account.data.raw ); } @@ -20,9 +19,7 @@ export const parseNFTokenNFTAccount = ( } try { - const parsed = NftokenTypes.nftAccountLayout.decode( - account!.details!.rawData! - ); + const parsed = NftokenTypes.nftAccountLayout.decode(account.data.raw!); if (!parsed) { return null; @@ -36,7 +33,7 @@ export const parseNFTokenNFTAccount = ( } return { - address: account!.pubkey.toBase58(), + address: account.pubkey.toBase58(), holder: new PublicKey(parsed.holder).toBase58(), authority: new PublicKey(parsed.authority).toBase58(), authority_can_update: Boolean(parsed.authority_can_update), @@ -62,7 +59,7 @@ export const parseNFTokenCollectionAccount = ( try { const parsed = NftokenTypes.collectionAccountLayout.decode( - account!.details!.rawData! + account.data.raw! ); if (!parsed) { @@ -76,7 +73,7 @@ export const parseNFTokenCollectionAccount = ( } return { - address: account!.pubkey.toBase58(), + address: account.pubkey.toBase58(), authority: parsed.authority, authority_can_update: Boolean(parsed.authority_can_update), metadata_url: parsed.metadata_url?.replace(/\0/g, "") ?? null, diff --git a/explorer/src/pages/AccountDetailsPage.tsx b/explorer/src/pages/AccountDetailsPage.tsx index 932921a3dc..45e154ffef 100644 --- a/explorer/src/pages/AccountDetailsPage.tsx +++ b/explorer/src/pages/AccountDetailsPage.tsx @@ -181,7 +181,7 @@ export function AccountDetailsPage({ address, tab }: Props) {
- +
{!pubkey ? ( @@ -195,22 +195,22 @@ export function AccountDetailsPage({ address, tab }: Props) { export function AccountHeader({ address, - info, + account, }: { address: string; - info?: CacheEntry; + account?: Account; }) { const { tokenRegistry } = useTokenRegistry(); const tokenDetails = tokenRegistry.get(address); const mintInfo = useMintAccountInfo(address); - const account = info?.data; - const data = account?.details?.data; - const isToken = data?.program === "spl-token" && data?.parsed.type === "mint"; + const parsedData = account?.data.parsed; + const isToken = + parsedData?.program === "spl-token" && parsedData?.parsed.type === "mint"; - if (isMetaplexNFT(data, mintInfo)) { + if (isMetaplexNFT(parsedData, mintInfo)) { return ( ); @@ -226,12 +226,14 @@ export function AccountHeader({ let unverified = false; // Fall back to legacy token list when there is stub metadata (blank uri), updatable by default by the mint authority - if (!data?.nftData?.metadata.data.uri && tokenDetails) { + if (!parsedData?.nftData?.metadata.data.uri && tokenDetails) { token = tokenDetails; - } else if (data?.nftData) { + } else if (parsedData?.nftData) { token = { - logoURI: data?.nftData?.json?.image, - name: data?.nftData?.json?.name ?? data?.nftData.metadata.data.name, + logoURI: parsedData?.nftData?.json?.image, + name: + parsedData?.nftData?.json?.name ?? + parsedData?.nftData.metadata.data.name, }; unverified = true; } else if (tokenDetails) { @@ -339,62 +341,68 @@ function DetailsSections({ } function InfoSection({ account }: { account: Account }) { - const details = account?.details; - const data = details?.data; + const parsedData = account.data.parsed; + const rawData = account.data.raw; - if (data && data.program === "bpf-upgradeable-loader") { + if (parsedData && parsedData.program === "bpf-upgradeable-loader") { return ( ); - } else if (data && data.program === "stake") { + } else if (parsedData && parsedData.program === "stake") { return ( ); - } else if (account.details?.owner.toBase58() === NFTOKEN_ADDRESS) { + } else if (account.owner.toBase58() === NFTOKEN_ADDRESS) { return ; - } else if (data && data.program === "spl-token") { - return ; - } else if (data && data.program === "nonce") { - return ; - } else if (data && data.program === "vote") { - return ; - } else if (data && data.program === "sysvar") { + } else if (parsedData && parsedData.program === "spl-token") { return ( - + ); - } else if (data && data.program === "config") { + } else if (parsedData && parsedData.program === "nonce") { return ( - + + ); + } else if (parsedData && parsedData.program === "vote") { + return ( + + ); + } else if (parsedData && parsedData.program === "sysvar") { + return ( + + ); + } else if (parsedData && parsedData.program === "config") { + return ( + ); } else if ( - data && - data.program === "address-lookup-table" && - data.parsed.type === "lookupTable" + parsedData && + parsedData.program === "address-lookup-table" && + parsedData.parsed.type === "lookupTable" ) { return ( ); - } else if ( - details?.rawData && - isAddressLookupTableAccount(details.owner, details.rawData) - ) { + } else if (rawData && isAddressLookupTableAccount(account.owner, rawData)) { return ( - + ); } else { return ; @@ -442,8 +450,8 @@ function MoreSection({ tabs: (JSX.Element | null)[]; }) { const pubkey = account.pubkey; - const details = account?.details; - const data = details?.data; + const parsedData = account.data.parsed; + const rawData = account.data.raw; return ( <> @@ -465,27 +473,27 @@ function MoreSection({ {tab === "instructions" && } {tab === "largest" && } {tab === "rewards" && } - {tab === "vote-history" && data?.program === "vote" && ( - + {tab === "vote-history" && parsedData?.program === "vote" && ( + )} {tab === "slot-hashes" && - data?.program === "sysvar" && - data.parsed.type === "slotHashes" && ( - + parsedData?.program === "sysvar" && + parsedData.parsed.type === "slotHashes" && ( + )} {tab === "stake-history" && - data?.program === "sysvar" && - data.parsed.type === "stakeHistory" && ( - + parsedData?.program === "sysvar" && + parsedData.parsed.type === "stakeHistory" && ( + )} {tab === "blockhashes" && - data?.program === "sysvar" && - data.parsed.type === "recentBlockhashes" && ( - + parsedData?.program === "sysvar" && + parsedData.parsed.type === "recentBlockhashes" && ( + )} {tab === "metadata" && ( )} {tab === "nftoken-collection-nfts" && ( @@ -497,13 +505,14 @@ function MoreSection({ )} {tab === "attributes" && ( )} {tab === "domains" && } - {tab === "security" && data?.program === "bpf-upgradeable-loader" && ( - - )} + {tab === "security" && + parsedData?.program === "bpf-upgradeable-loader" && ( + + )} {tab === "anchor-program" && ( } @@ -521,14 +530,14 @@ function MoreSection({ )} {tab === "entries" && - details?.rawData && - isAddressLookupTableAccount(details.owner, details.rawData) && ( - + rawData && + isAddressLookupTableAccount(account.owner, rawData) && ( + )} {tab === "entries" && - data?.program === "address-lookup-table" && - data.parsed.type === "lookupTable" && ( - + parsedData?.program === "address-lookup-table" && + parsedData.parsed.type === "lookupTable" && ( + )} ); @@ -536,7 +545,7 @@ function MoreSection({ function getTabs(pubkey: PublicKey, account: Account): TabComponent[] { const address = pubkey.toBase58(); - const data = account.details?.data; + const parsedData = account.data.parsed; const tabs: Tab[] = [ { slug: "history", @@ -546,31 +555,31 @@ function getTabs(pubkey: PublicKey, account: Account): TabComponent[] { ]; let programTypeKey = ""; - if (data && "parsed" in data && "type" in data.parsed) { - programTypeKey = `${data.program}:${data.parsed.type}`; + if (parsedData) { + programTypeKey = `${parsedData.program}:${parsedData.parsed.type}`; } - if (data && data.program in TABS_LOOKUP) { - tabs.push(...TABS_LOOKUP[data.program]); + if (parsedData && parsedData.program in TABS_LOOKUP) { + tabs.push(...TABS_LOOKUP[parsedData.program]); } - if (data && programTypeKey in TABS_LOOKUP) { + if (parsedData && programTypeKey in TABS_LOOKUP) { tabs.push(...TABS_LOOKUP[programTypeKey]); } // Add the key for address lookup tables if ( - account.details?.rawData && - isAddressLookupTableAccount(account.details.owner, account.details.rawData) + account.data.raw && + isAddressLookupTableAccount(account.owner, account.data.raw) ) { tabs.push(...TABS_LOOKUP["address-lookup-table"]); } // Add the key for Metaplex NFTs if ( - data && + parsedData && programTypeKey === "spl-token:mint" && - (data as TokenProgramData).nftData + (parsedData as TokenProgramData).nftData ) { tabs.push(...TABS_LOOKUP[`${programTypeKey}:metaplexNFT`]); } @@ -589,9 +598,9 @@ function getTabs(pubkey: PublicKey, account: Account): TabComponent[] { if ( !isNFToken && - (!data || + (!parsedData || !( - TOKEN_TABS_HIDDEN.includes(data.program) || + TOKEN_TABS_HIDDEN.includes(parsedData.program) || TOKEN_TABS_HIDDEN.includes(programTypeKey) )) ) { @@ -657,7 +666,7 @@ function getAnchorTabs(pubkey: PublicKey, account: Account) { ), @@ -676,7 +685,7 @@ function AnchorProgramLink({ pubkey: PublicKey; }) { const { url } = useCluster(); - const anchorProgram = useAnchorProgram(pubkey.toString() ?? "", url); + const anchorProgram = useAnchorProgram(pubkey.toString(), url); if (!anchorProgram) { return null; @@ -702,13 +711,10 @@ function AccountDataLink({ }: { address: string; tab: Tab; - programId: PublicKey | undefined; + programId: PublicKey; }) { const { url } = useCluster(); - const accountAnchorProgram = useAnchorProgram( - programId?.toString() ?? "", - url - ); + const accountAnchorProgram = useAnchorProgram(programId.toString(), url); if (!accountAnchorProgram) { return null; diff --git a/explorer/src/pages/inspector/AddressWithContext.tsx b/explorer/src/pages/inspector/AddressWithContext.tsx index 057de61f69..70a9b900b2 100644 --- a/explorer/src/pages/inspector/AddressWithContext.tsx +++ b/explorer/src/pages/inspector/AddressWithContext.tsx @@ -17,12 +17,11 @@ export const createFeePayerValidator = ( feeLamports: number ): AccountValidator => { return (account: Account): string | undefined => { - if (account.details === undefined) return "Account doesn't exist"; - if (!account.details.owner.equals(SystemProgram.programId)) + if (account.lamports === 0) return "Account doesn't exist"; + if (!account.owner.equals(SystemProgram.programId)) return "Only system-owned accounts can pay fees"; // TODO: Actually nonce accounts can pay fees too - if (account.details.space > 0) - return "Only unallocated accounts can pay fees"; + if (account.space > 0) return "Only unallocated accounts can pay fees"; if (account.lamports < feeLamports) { return "Insufficient funds for fees"; } @@ -31,9 +30,8 @@ export const createFeePayerValidator = ( }; export const programValidator = (account: Account): string | undefined => { - if (account.details === undefined) return "Account doesn't exist"; - if (!account.details.executable) - return "Only executable accounts can be invoked"; + if (account.lamports === 0) return "Account doesn't exist"; + if (!account.executable) return "Only executable accounts can be invoked"; return; }; @@ -108,7 +106,8 @@ function AccountInfo({ } }, [address, status]); // eslint-disable-line react-hooks/exhaustive-deps - if (!info?.data) + const account = info?.data; + if (!account) return ( @@ -116,23 +115,22 @@ function AccountInfo({ ); - const errorMessage = validator && validator(info.data); + const errorMessage = validator && validator(account); if (errorMessage) return {errorMessage}; - if (!info.data.details) { + if (account.lamports === 0) { return Account doesn't exist; } - const owner = info.data.details.owner; - const ownerAddress = owner.toBase58(); + const ownerAddress = account.owner.toBase58(); const ownerLabel = addressLabel(ownerAddress, cluster); return ( {`Owned by ${ownerLabel || ownerAddress}.`} - {` Balance is ${lamportsToSolString(info.data.lamports)} SOL.`} + {` Balance is ${lamportsToSolString(account.lamports)} SOL.`} {` Size is ${new Intl.NumberFormat("en-US").format( - info.data.details.space + account.space )} byte(s).`} ); diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx index 77b29aa406..d10c6928a0 100644 --- a/explorer/src/providers/accounts/index.tsx +++ b/explorer/src/providers/accounts/index.tsx @@ -6,6 +6,7 @@ import { StakeActivationData, AddressLookupTableAccount, AddressLookupTableProgram, + SystemProgram, } from "@solana/web3.js"; import { useCluster, Cluster } from "../cluster"; import { HistoryProvider } from "./history"; @@ -88,7 +89,7 @@ export type AddressLookupTableProgramData = { parsed: ParsedAddressLookupTableAccount; }; -export type ProgramData = +export type ParsedData = | UpgradeableLoaderAccountData | StakeProgramData | TokenProgramData @@ -98,18 +99,18 @@ export type ProgramData = | ConfigProgramData | AddressLookupTableProgramData; -export interface Details { - executable: boolean; - owner: PublicKey; - space: number; - data?: ProgramData; - rawData?: Buffer; +export interface AccountData { + parsed?: ParsedData; + raw?: Buffer; } export interface Account { pubkey: PublicKey; lamports: number; - details?: Details; + executable: boolean; + owner: PublicKey; + space: number; + data: AccountData; } type State = Cache.State; @@ -162,12 +163,17 @@ async function fetchAccountInfo( const connection = new Connection(url, "confirmed"); const result = (await connection.getParsedAccountInfo(pubkey)).value; - let lamports, details; + let account: Account; if (result === null) { - lamports = 0; + account = { + pubkey, + lamports: 0, + owner: SystemProgram.programId, + space: 0, + executable: false, + data: { raw: Buffer.alloc(0) }, + }; } else { - lamports = result.lamports; - // Only save data in memory if we can decode it let space: number; if (!("parsed" in result.data)) { @@ -176,7 +182,7 @@ async function fetchAccountInfo( space = result.data.space; } - let data: ProgramData | undefined; + let parsedData: ParsedData | undefined; if ("parsed" in result.data) { try { const info = create(result.data.parsed, ParsedInfo); @@ -200,7 +206,7 @@ async function fetchAccountInfo( } } - data = { + parsedData = { program: result.data.program, parsed, programData, @@ -215,7 +221,7 @@ async function fetchAccountInfo( ? await connection.getStakeActivation(pubkey) : undefined; - data = { + parsedData = { program: result.data.program, parsed, activation, @@ -223,25 +229,25 @@ async function fetchAccountInfo( break; } case "vote": - data = { + parsedData = { program: result.data.program, parsed: create(info, VoteAccount), }; break; case "nonce": - data = { + parsedData = { program: result.data.program, parsed: create(info, NonceAccount), }; break; case "sysvar": - data = { + parsedData = { program: result.data.program, parsed: create(info, SysvarAccount), }; break; case "config": - data = { + parsedData = { program: result.data.program, parsed: create(info, ConfigAccount), }; @@ -250,7 +256,7 @@ async function fetchAccountInfo( case "address-lookup-table": { const parsed = create(info, ParsedAddressLookupTableAccount); - data = { + parsedData = { program: result.data.program, parsed, }; @@ -291,14 +297,14 @@ async function fetchAccountInfo( // unable to find NFT metadata account } - data = { + parsedData = { program: result.data.program, parsed, nftData, }; break; default: - data = undefined; + parsedData = undefined; } } catch (error) { reportError(error, { url, address: pubkey.toBase58() }); @@ -308,19 +314,23 @@ async function fetchAccountInfo( // If we cannot parse account layout as native spl account // then keep raw data for other components to decode let rawData: Buffer | undefined; - if (!data && !("parsed" in result.data)) { + if (!parsedData && !("parsed" in result.data)) { rawData = result.data; } - details = { + account = { + pubkey, + lamports: result.lamports, space, executable: result.executable, owner: result.owner, - data, - rawData, + data: { + parsed: parsedData, + raw: rawData, + }, }; } - data = { pubkey, lamports, details }; + data = account; fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { @@ -413,16 +423,20 @@ export function useMintAccountInfo( ): MintAccountInfo | undefined { const accountInfo = useAccountInfo(address); return React.useMemo(() => { - if (address === undefined) return; + if (address === undefined || accountInfo?.data === undefined) return; + const account = accountInfo.data; try { - const data = accountInfo?.data?.details?.data; - if (!data) return; - if (data.program !== "spl-token" || data.parsed.type !== "mint") { + const parsedData = account.data.parsed; + if (!parsedData) return; + if ( + parsedData.program !== "spl-token" || + parsedData.parsed.type !== "mint" + ) { return; } - return create(data.parsed.info, MintAccountInfo); + return create(parsedData.parsed.info, MintAccountInfo); } catch (err) { reportError(err, { address }); } @@ -433,16 +447,20 @@ export function useTokenAccountInfo( address: string | undefined ): TokenAccountInfo | undefined { const accountInfo = useAccountInfo(address); - if (address === undefined) return; + if (address === undefined || accountInfo?.data === undefined) return; + const account = accountInfo.data; try { - const data = accountInfo?.data?.details?.data; - if (!data) return; - if (data.program !== "spl-token" || data.parsed.type !== "account") { + const parsedData = account.data.parsed; + if (!parsedData) return; + if ( + parsedData.program !== "spl-token" || + parsedData.parsed.type !== "account" + ) { return; } - return create(data.parsed.info, TokenAccountInfo); + return create(parsedData.parsed.info, TokenAccountInfo); } catch (err) { reportError(err, { address }); } @@ -452,24 +470,24 @@ export function useAddressLookupTable( address: string | undefined ): AddressLookupTableAccount | undefined | string { const accountInfo = useAccountInfo(address); - if (address === undefined) return; - if (accountInfo?.data?.details === undefined) return; - if (accountInfo.data.lamports === 0) return "Lookup Table Not Found"; - const { data, rawData } = accountInfo.data.details; + if (address === undefined || accountInfo?.data === undefined) return; + const account = accountInfo.data; + if (account.lamports === 0) return "Lookup Table Not Found"; + const { parsed: parsedData, raw: rawData } = account.data; const key = new PublicKey(address); - if (data && data.program === "address-lookup-table") { - if (data.parsed.type === "lookupTable") { + if (parsedData && parsedData.program === "address-lookup-table") { + if (parsedData.parsed.type === "lookupTable") { return new AddressLookupTableAccount({ key, - state: data.parsed.info, + state: parsedData.parsed.info, }); - } else if (data.parsed.type === "uninitialized") { + } else if (parsedData.parsed.type === "uninitialized") { return "Lookup Table Uninitialized"; } } else if ( rawData && - accountInfo.data.details.owner.equals(AddressLookupTableProgram.programId) + account.owner.equals(AddressLookupTableProgram.programId) ) { try { return new AddressLookupTableAccount({ diff --git a/explorer/src/providers/accounts/utils/isMetaplexNFT.ts b/explorer/src/providers/accounts/utils/isMetaplexNFT.ts index 4b246674b0..00b763a004 100644 --- a/explorer/src/providers/accounts/utils/isMetaplexNFT.ts +++ b/explorer/src/providers/accounts/utils/isMetaplexNFT.ts @@ -1,16 +1,16 @@ import { MintAccountInfo } from "validators/accounts/token"; -import { ProgramData } from ".."; +import { ParsedData } from ".."; export default function isMetaplexNFT( - data?: ProgramData, + parsedData?: ParsedData, mintInfo?: MintAccountInfo ) { return ( - data?.program === "spl-token" && - data?.parsed.type === "mint" && - data?.nftData && + parsedData?.program === "spl-token" && + parsedData?.parsed.type === "mint" && + parsedData?.nftData && mintInfo?.decimals === 0 && (parseInt(mintInfo.supply) === 1 || - data?.nftData?.metadata?.tokenStandard === 1) + parsedData?.nftData?.metadata?.tokenStandard === 1) ); } diff --git a/explorer/src/utils/index.ts b/explorer/src/utils/index.ts index 5683938c22..c4da2efe49 100644 --- a/explorer/src/utils/index.ts +++ b/explorer/src/utils/index.ts @@ -169,7 +169,7 @@ export function abbreviatedNumber(value: number, fixed = 1) { } export const pubkeyToString = (key: PublicKey | string = "") => { - return typeof key === "string" ? key : key?.toBase58() || ""; + return typeof key === "string" ? key : key.toBase58(); }; export const getLast = (arr: string[]) => {