explorer: 11938 display owner in token distribution table (#11953)

* include owner on largest token distribution tab

* run format:fix
This commit is contained in:
Josh
2020-09-01 09:21:47 -07:00
committed by GitHub
parent 839b926e0b
commit 53a900a28c
3 changed files with 63 additions and 5 deletions

View File

@ -1,11 +1,12 @@
import React from "react"; import React from "react";
import { PublicKey, TokenAccountBalancePair } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { LoadingCard } from "components/common/LoadingCard"; import { LoadingCard } from "components/common/LoadingCard";
import { ErrorCard } from "components/common/ErrorCard"; import { ErrorCard } from "components/common/ErrorCard";
import { Address } from "components/common/Address"; import { Address } from "components/common/Address";
import { import {
useTokenLargestTokens, useTokenLargestTokens,
useFetchTokenLargestAccounts, useFetchTokenLargestAccounts,
TokenAccountBalancePairWithOwner,
} from "providers/mints/largest"; } from "providers/mints/largest";
import { FetchStatus } from "providers/cache"; import { FetchStatus } from "providers/cache";
import { TokenRegistry } from "tokenRegistry"; import { TokenRegistry } from "tokenRegistry";
@ -64,11 +65,12 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
</div> </div>
<div className="table-responsive mb-0"> <div className="table-responsive mb-0">
<table className="table table-sm table-nowrap card-table"> <table className="table table-sm card-table">
<thead> <thead>
<tr> <tr>
<th className="text-muted">Rank</th> <th className="text-muted">Rank</th>
<th className="text-muted">Address</th> <th className="text-muted">Address</th>
<th className="text-muted">Owner</th>
<th className="text-muted text-right">Balance {unitLabel}</th> <th className="text-muted text-right">Balance {unitLabel}</th>
<th className="text-muted text-right">% of Total Supply</th> <th className="text-muted text-right">% of Total Supply</th>
</tr> </tr>
@ -86,7 +88,7 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
} }
const renderAccountRow = ( const renderAccountRow = (
account: TokenAccountBalancePair, account: TokenAccountBalancePairWithOwner,
index: number, index: number,
supply: number supply: number
) => { ) => {
@ -99,8 +101,13 @@ const renderAccountRow = (
<td> <td>
<span className="badge badge-soft-gray badge-pill">{index + 1}</span> <span className="badge badge-soft-gray badge-pill">{index + 1}</span>
</td> </td>
<td className="td">
<Address pubkey={account.address} link truncate />
</td>
<td> <td>
<Address pubkey={account.address} link /> {account.owner && (
<Address pubkey={account.owner} alignRight link truncate />
)}
</td> </td>
<td className="text-right">{account.uiAmount}</td> <td className="text-right">{account.uiAmount}</td>
<td className="text-right">{percent}</td> <td className="text-right">{percent}</td>

View File

@ -127,6 +127,7 @@ async function fetchAccountInfo(
try { try {
const info = coerce(result.data.parsed, ParsedInfo); const info = coerce(result.data.parsed, ParsedInfo);
const parsed = coerce(info, TokenAccount); const parsed = coerce(info, TokenAccount);
data = { data = {
program: "spl-token", program: "spl-token",
parsed, parsed,

View File

@ -7,10 +7,14 @@ import {
PublicKey, PublicKey,
Connection, Connection,
TokenAccountBalancePair, TokenAccountBalancePair,
ParsedAccountData,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { TokenAccountInfo, TokenAccount } from "validators/accounts/token";
import { ParsedInfo } from "validators";
import { coerce } from "superstruct";
type LargestAccounts = { type LargestAccounts = {
largest: TokenAccountBalancePair[]; largest: TokenAccountBalancePairWithOwner[];
}; };
type State = Cache.State<LargestAccounts>; type State = Cache.State<LargestAccounts>;
@ -38,6 +42,13 @@ export function LargestAccountsProvider({ children }: ProviderProps) {
); );
} }
type OptionalOwner = {
owner?: PublicKey;
};
export type TokenAccountBalancePairWithOwner = TokenAccountBalancePair &
OptionalOwner;
async function fetchLargestAccounts( async function fetchLargestAccounts(
dispatch: Dispatch, dispatch: Dispatch,
pubkey: PublicKey, pubkey: PublicKey,
@ -59,6 +70,33 @@ async function fetchLargestAccounts(
await new Connection(url, "single").getTokenLargestAccounts(pubkey) await new Connection(url, "single").getTokenLargestAccounts(pubkey)
).value, ).value,
}; };
data.largest = await Promise.all(
data.largest.map(
async (account): Promise<TokenAccountBalancePairWithOwner> => {
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; fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) { if (cluster !== Cluster.Custom) {
@ -105,3 +143,15 @@ export function useTokenLargestTokens(
return context.entries[address]; 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;
}
}