explorer: 11938 display owner in token distribution table (#11953)
* include owner on largest token distribution tab * run format:fix
This commit is contained in:
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user