import { PublicKey } from "@solana/web3.js"; import { AnchorAccountCard } from "components/account/AnchorAccountCard"; import { AnchorProgramCard } from "components/account/AnchorProgramCard"; import { BlockhashesCard } from "components/account/BlockhashesCard"; import { ConfigAccountSection } from "components/account/ConfigAccountSection"; import { DomainsCard } from "components/account/DomainsCard"; import { TokenInstructionsCard } from "components/account/history/TokenInstructionsCard"; import { TokenTransfersCard } from "components/account/history/TokenTransfersCard"; import { TransactionHistoryCard } from "components/account/history/TransactionHistoryCard"; import { MetaplexMetadataCard } from "components/account/MetaplexMetadataCard"; import { MetaplexNFTAttributesCard } from "components/account/MetaplexNFTAttributesCard"; import { MetaplexNFTHeader } from "components/account/MetaplexNFTHeader"; import { NonceAccountSection } from "components/account/NonceAccountSection"; import { OwnedTokensCard } from "components/account/OwnedTokensCard"; import { RewardsCard } from "components/account/RewardsCard"; import { SecurityCard } from "components/account/SecurityCard"; import { SlotHashesCard } from "components/account/SlotHashesCard"; import { StakeAccountSection } from "components/account/StakeAccountSection"; import { StakeHistoryCard } from "components/account/StakeHistoryCard"; import { SysvarAccountSection } from "components/account/SysvarAccountSection"; import { TokenAccountSection } from "components/account/TokenAccountSection"; import { TokenHistoryCard } from "components/account/TokenHistoryCard"; import { TokenLargestAccountsCard } from "components/account/TokenLargestAccountsCard"; import { UnknownAccountCard } from "components/account/UnknownAccountCard"; import { UpgradeableLoaderAccountSection } from "components/account/UpgradeableLoaderAccountSection"; import { VoteAccountSection } from "components/account/VoteAccountSection"; import { VotesCard } from "components/account/VotesCard"; import { ErrorCard } from "components/common/ErrorCard"; import { Identicon } from "components/common/Identicon"; import { LoadingCard } from "components/common/LoadingCard"; import { Account, TokenProgramData, useAccountInfo, useFetchAccountInfo, useMintAccountInfo, } from "providers/accounts"; import { useFlaggedAccounts } from "providers/accounts/flagged-accounts"; import isMetaplexNFT from "providers/accounts/utils/isMetaplexNFT"; import { useAnchorProgram } from "providers/anchor"; import { CacheEntry, FetchStatus } from "providers/cache"; import { ClusterStatus, useCluster } from "providers/cluster"; import { useTokenRegistry } from "providers/mints/token-registry"; import React, { Suspense } from "react"; import { NavLink, Redirect, useLocation } from "react-router-dom"; import { clusterPath } from "utils/url"; import { NFTokenAccountHeader } from "../components/account/nftoken/NFTokenAccountHeader"; import { NFTokenAccountSection } from "../components/account/nftoken/NFTokenAccountSection"; import { NFTokenCollectionNFTGrid } from "../components/account/nftoken/NFTokenCollectionNFTGrid"; import { NFTOKEN_ADDRESS } from "../components/account/nftoken/nftoken"; import { isNFTokenAccount, parseNFTokenCollectionAccount, } from "../components/account/nftoken/isNFTokenAccount"; import { isAddressLookupTableAccount } from "components/account/address-lookup-table/types"; import { AddressLookupTableAccountSection } from "components/account/address-lookup-table/AddressLookupTableAccountSection"; import { LookupTableEntriesCard } from "components/account/address-lookup-table/LookupTableEntriesCard"; const IDENTICON_WIDTH = 64; const TABS_LOOKUP: { [id: string]: Tab[] } = { "spl-token:mint": [ { slug: "transfers", title: "Transfers", path: "/transfers", }, { slug: "instructions", title: "Instructions", path: "/instructions", }, { slug: "largest", title: "Distribution", path: "/largest", }, ], "spl-token:mint:metaplexNFT": [ { slug: "metadata", title: "Metadata", path: "/metadata", }, { slug: "attributes", title: "Attributes", path: "/attributes", }, ], stake: [ { slug: "rewards", title: "Rewards", path: "/rewards", }, ], vote: [ { slug: "vote-history", title: "Vote History", path: "/vote-history", }, { slug: "rewards", title: "Rewards", path: "/rewards", }, ], "sysvar:recentBlockhashes": [ { slug: "blockhashes", title: "Blockhashes", path: "/blockhashes", }, ], "sysvar:slotHashes": [ { slug: "slot-hashes", title: "Slot Hashes", path: "/slot-hashes", }, ], "sysvar:stakeHistory": [ { slug: "stake-history", title: "Stake History", path: "/stake-history", }, ], "bpf-upgradeable-loader": [ { slug: "security", title: "Security", path: "/security", }, ], "nftoken:collection": [ { slug: "nftoken-collection-nfts", title: "NFTs", path: "/nfts", }, ], "address-lookup-table": [ { slug: "entries", title: "Table Entries", path: "/entries", }, ], }; const TOKEN_TABS_HIDDEN = [ "spl-token:mint", "config", "vote", "sysvar", "config", ]; type Props = { address: string; tab?: string }; export function AccountDetailsPage({ address, tab }: Props) { const fetchAccount = useFetchAccountInfo(); const { status } = useCluster(); const info = useAccountInfo(address); let pubkey: PublicKey | undefined; try { pubkey = new PublicKey(address); } catch (err) {} // Fetch account on load React.useEffect(() => { if (!info && status === ClusterStatus.Connected && pubkey) { fetchAccount(pubkey, "parsed"); } }, [address, status]); // eslint-disable-line react-hooks/exhaustive-deps return (
{!pubkey ? ( ) : ( )}
); } export function AccountHeader({ address, account, }: { address: string; account?: Account; }) { const { tokenRegistry } = useTokenRegistry(); const tokenDetails = tokenRegistry.get(address); const mintInfo = useMintAccountInfo(address); const parsedData = account?.data.parsed; const isToken = parsedData?.program === "spl-token" && parsedData?.parsed.type === "mint"; if (isMetaplexNFT(parsedData, mintInfo)) { return ( ); } const nftokenNFT = account && isNFTokenAccount(account); if (nftokenNFT && account) { return ; } if (isToken) { let token; let unverified = false; // Fall back to legacy token list when there is stub metadata (blank uri), updatable by default by the mint authority if (!parsedData?.nftData?.metadata.data.uri && tokenDetails) { token = tokenDetails; } else if (parsedData?.nftData) { token = { logoURI: parsedData?.nftData?.json?.image, name: parsedData?.nftData?.json?.name ?? parsedData?.nftData.metadata.data.name, }; unverified = true; } else if (tokenDetails) { token = tokenDetails; } return (
{unverified && (
Warning! Token names and logos are not unique. This token may have spoofed its name and logo to look like another token. Verify the token's mint address to ensure it is correct.
)}
{token?.logoURI ? ( token logo ) : ( )}
Token

{token?.name || "Unknown Token"}

); } return ( <>
Details

Account

); } function DetailsSections({ pubkey, tab, info, }: { pubkey: PublicKey; tab?: string; info?: CacheEntry; }) { const fetchAccount = useFetchAccountInfo(); const address = pubkey.toBase58(); const location = useLocation(); const { flaggedAccounts } = useFlaggedAccounts(); if (!info || info.status === FetchStatus.Fetching) { return ; } else if ( info.status === FetchStatus.FetchFailed || info.data?.lamports === undefined ) { return ( fetchAccount(pubkey, "parsed")} text="Fetch Failed" /> ); } const account = info.data; const tabComponents = getTabs(pubkey, account).concat( getAnchorTabs(pubkey, account) ); let moreTab: MoreTabs = "history"; if ( tab && tabComponents.filter((tabComponent) => tabComponent.tab.slug === tab) .length === 0 ) { return ; } else if (tab) { moreTab = tab as MoreTabs; } return ( <> {flaggedAccounts.has(address) && (
Warning! This account has been flagged by the community as a scam account. Please be cautious sending SOL to this account.
)} component)} /> ); } function InfoSection({ account }: { account: Account }) { const parsedData = account.data.parsed; const rawData = account.data.raw; if (parsedData && parsedData.program === "bpf-upgradeable-loader") { return ( ); } else if (parsedData && parsedData.program === "stake") { return ( ); } else if (account.owner.toBase58() === NFTOKEN_ADDRESS) { return ; } else if (parsedData && parsedData.program === "spl-token") { return ( ); } 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 ( parsedData && parsedData.program === "address-lookup-table" && parsedData.parsed.type === "lookupTable" ) { return ( ); } else if (rawData && isAddressLookupTableAccount(account.owner, rawData)) { return ( ); } else { return ; } } type Tab = { slug: MoreTabs; title: string; path: string; }; type TabComponent = { tab: Tab; component: JSX.Element | null; }; export type MoreTabs = | "history" | "tokens" | "nftoken-collection-nfts" | "largest" | "vote-history" | "slot-hashes" | "stake-history" | "blockhashes" | "transfers" | "instructions" | "rewards" | "metadata" | "attributes" | "domains" | "security" | "anchor-program" | "anchor-account" | "entries"; function MoreSection({ account, tab, tabs, }: { account: Account; tab: MoreTabs; tabs: (JSX.Element | null)[]; }) { const pubkey = account.pubkey; const parsedData = account.data.parsed; const rawData = account.data.raw; return ( <>
    {tabs}
{tab === "tokens" && ( <> )} {tab === "history" && } {tab === "transfers" && } {tab === "instructions" && } {tab === "largest" && } {tab === "rewards" && } {tab === "vote-history" && parsedData?.program === "vote" && ( )} {tab === "slot-hashes" && parsedData?.program === "sysvar" && parsedData.parsed.type === "slotHashes" && ( )} {tab === "stake-history" && parsedData?.program === "sysvar" && parsedData.parsed.type === "stakeHistory" && ( )} {tab === "blockhashes" && parsedData?.program === "sysvar" && parsedData.parsed.type === "recentBlockhashes" && ( )} {tab === "metadata" && ( )} {tab === "nftoken-collection-nfts" && ( } > )} {tab === "attributes" && ( )} {tab === "domains" && } {tab === "security" && parsedData?.program === "bpf-upgradeable-loader" && ( )} {tab === "anchor-program" && ( } > )} {tab === "anchor-account" && ( } > )} {tab === "entries" && rawData && isAddressLookupTableAccount(account.owner, rawData) && ( )} {tab === "entries" && parsedData?.program === "address-lookup-table" && parsedData.parsed.type === "lookupTable" && ( )} ); } function getTabs(pubkey: PublicKey, account: Account): TabComponent[] { const address = pubkey.toBase58(); const parsedData = account.data.parsed; const tabs: Tab[] = [ { slug: "history", title: "History", path: "", }, ]; let programTypeKey = ""; if (parsedData) { programTypeKey = `${parsedData.program}:${parsedData.parsed.type}`; } if (parsedData && parsedData.program in TABS_LOOKUP) { tabs.push(...TABS_LOOKUP[parsedData.program]); } if (parsedData && programTypeKey in TABS_LOOKUP) { tabs.push(...TABS_LOOKUP[programTypeKey]); } // Add the key for address lookup tables if ( account.data.raw && isAddressLookupTableAccount(account.owner, account.data.raw) ) { tabs.push(...TABS_LOOKUP["address-lookup-table"]); } // Add the key for Metaplex NFTs if ( parsedData && programTypeKey === "spl-token:mint" && (parsedData as TokenProgramData).nftData ) { tabs.push(...TABS_LOOKUP[`${programTypeKey}:metaplexNFT`]); } const isNFToken = account && isNFTokenAccount(account); if (isNFToken) { const collection = parseNFTokenCollectionAccount(account); if (collection) { tabs.push({ slug: "nftoken-collection-nfts", title: "NFTs", path: "/nftoken-collection-nfts", }); } } if ( !isNFToken && (!parsedData || !( TOKEN_TABS_HIDDEN.includes(parsedData.program) || TOKEN_TABS_HIDDEN.includes(programTypeKey) )) ) { tabs.push({ slug: "tokens", title: "Tokens", path: "/tokens", }); tabs.push({ slug: "domains", title: "Domains", path: "/domains", }); } return tabs.map((tab) => { return { tab, component: (
  • {tab.title}
  • ), }; }); } function getAnchorTabs(pubkey: PublicKey, account: Account) { const tabComponents = []; const anchorProgramTab: Tab = { slug: "anchor-program", title: "Anchor Program IDL", path: "/anchor-program", }; tabComponents.push({ tab: anchorProgramTab, component: ( }> ), }); const accountDataTab: Tab = { slug: "anchor-account", title: "Anchor Data", path: "/anchor-account", }; tabComponents.push({ tab: accountDataTab, component: ( }> ), }); return tabComponents; } function AnchorProgramLink({ tab, address, pubkey, }: { tab: Tab; address: string; pubkey: PublicKey; }) { const { url } = useCluster(); const anchorProgram = useAnchorProgram(pubkey.toString(), url); if (!anchorProgram) { return null; } return (
  • {tab.title}
  • ); } function AccountDataLink({ address, tab, programId, }: { address: string; tab: Tab; programId: PublicKey; }) { const { url } = useCluster(); const accountAnchorProgram = useAnchorProgram(programId.toString(), url); if (!accountAnchorProgram) { return null; } return (
  • {tab.title}
  • ); }