diff --git a/explorer/src/components/account/TokenLargestAccountsCard.tsx b/explorer/src/components/account/TokenLargestAccountsCard.tsx index 52f2dda68ac439..a403a1d8e311a4 100644 --- a/explorer/src/components/account/TokenLargestAccountsCard.tsx +++ b/explorer/src/components/account/TokenLargestAccountsCard.tsx @@ -1,11 +1,12 @@ import React from "react"; -import { PublicKey, TokenAccountBalancePair } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { LoadingCard } from "components/common/LoadingCard"; import { ErrorCard } from "components/common/ErrorCard"; import { Address } from "components/common/Address"; import { useTokenLargestTokens, useFetchTokenLargestAccounts, + TokenAccountBalancePairWithOwner, } from "providers/mints/largest"; import { FetchStatus } from "providers/cache"; import { TokenRegistry } from "tokenRegistry"; @@ -64,11 +65,12 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
- +
+ @@ -86,7 +88,7 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) { } const renderAccountRow = ( - account: TokenAccountBalancePair, + account: TokenAccountBalancePairWithOwner, index: number, supply: number ) => { @@ -99,8 +101,13 @@ const renderAccountRow = ( + diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx index 3bdd225d182c1a..78b4c92f72feb9 100644 --- a/explorer/src/providers/accounts/index.tsx +++ b/explorer/src/providers/accounts/index.tsx @@ -127,6 +127,7 @@ async function fetchAccountInfo( try { const info = coerce(result.data.parsed, ParsedInfo); const parsed = coerce(info, TokenAccount); + data = { program: "spl-token", parsed, diff --git a/explorer/src/providers/mints/largest.tsx b/explorer/src/providers/mints/largest.tsx index 17780b9eddb7f1..00cae426925af0 100644 --- a/explorer/src/providers/mints/largest.tsx +++ b/explorer/src/providers/mints/largest.tsx @@ -7,10 +7,14 @@ import { PublicKey, Connection, TokenAccountBalancePair, + ParsedAccountData, } from "@solana/web3.js"; +import { TokenAccountInfo, TokenAccount } from "validators/accounts/token"; +import { ParsedInfo } from "validators"; +import { coerce } from "superstruct"; type LargestAccounts = { - largest: TokenAccountBalancePair[]; + largest: TokenAccountBalancePairWithOwner[]; }; type State = Cache.State; @@ -38,6 +42,13 @@ export function LargestAccountsProvider({ children }: ProviderProps) { ); } +type OptionalOwner = { + owner?: PublicKey; +}; + +export type TokenAccountBalancePairWithOwner = TokenAccountBalancePair & + OptionalOwner; + async function fetchLargestAccounts( dispatch: Dispatch, pubkey: PublicKey, @@ -59,6 +70,33 @@ async function fetchLargestAccounts( await new Connection(url, "single").getTokenLargestAccounts(pubkey) ).value, }; + + data.largest = await Promise.all( + data.largest.map( + async (account): Promise => { + try { + const accountInfo = ( + await new Connection(url, "single").getParsedAccountInfo( + account.address + ) + ).value; + if (accountInfo && "parsed" in accountInfo.data) { + const info = coerceParsedAccountInfo(accountInfo.data); + return { + ...account, + owner: info.owner, + }; + } + } catch (error) { + if (cluster !== Cluster.Custom) { + Sentry.captureException(error, { tags: { url } }); + } + } + return account; + } + ) + ); + fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { @@ -105,3 +143,15 @@ export function useTokenLargestTokens( return context.entries[address]; } + +function coerceParsedAccountInfo( + parsedData: ParsedAccountData +): TokenAccountInfo { + try { + const data = coerce(parsedData.parsed, ParsedInfo); + const parsed = coerce(data, TokenAccount); + return coerce(parsed.info, TokenAccountInfo); + } catch (error) { + throw error; + } +}
Rank AddressOwner Balance {unitLabel} % of Total Supply
{index + 1} +
+
-
+ {account.owner && ( +
+ )}
{account.uiAmount} {percent}