Fix owned token validation in explorer (#11581)

This commit is contained in:
Justin Starry 2020-08-12 20:11:38 +08:00 committed by GitHub
parent c300748884
commit 8ddb116659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 45 deletions

View File

@ -4,7 +4,6 @@ import { FetchStatus } from "providers/accounts";
import { import {
useFetchAccountOwnedTokens, useFetchAccountOwnedTokens,
useAccountOwnedTokens, useAccountOwnedTokens,
TokenAccountData,
} from "providers/accounts/tokens"; } from "providers/accounts/tokens";
import { ErrorCard } from "components/common/ErrorCard"; import { ErrorCard } from "components/common/ErrorCard";
import { LoadingCard } from "components/common/LoadingCard"; import { LoadingCard } from "components/common/LoadingCard";
@ -43,26 +42,27 @@ export function OwnedTokensCard({ pubkey }: { pubkey: PublicKey }) {
); );
} }
const mappedTokens = new Map<string, TokenAccountData>(); const mappedTokens = new Map<string, number>();
for (const token of tokens) { for (const { info: token } of tokens) {
const mintAddress = token.mint.toBase58(); const mintAddress = token.mint.toBase58();
const tokenInfo = mappedTokens.get(mintAddress); const totalByMint = mappedTokens.get(mintAddress);
if (tokenInfo) {
tokenInfo.amount += token.amount; let amount = token?.amount || (token?.tokenAmount?.uiAmount as number);
} else { if (totalByMint !== undefined) {
mappedTokens.set(mintAddress, { ...token }); amount += totalByMint;
} }
mappedTokens.set(mintAddress, amount);
} }
const detailsList: React.ReactNode[] = []; const detailsList: React.ReactNode[] = [];
mappedTokens.forEach((tokenInfo, mintAddress) => { mappedTokens.forEach((totalByMint, mintAddress) => {
const balance = tokenInfo.amount;
detailsList.push( detailsList.push(
<tr key={mintAddress}> <tr key={mintAddress}>
<td> <td>
<Address pubkey={new PublicKey(mintAddress)} link /> <Address pubkey={new PublicKey(mintAddress)} link />
</td> </td>
<td>{balance}</td> <td>{totalByMint}</td>
</tr> </tr>
); );
}); });

View File

@ -11,7 +11,7 @@ import {
} from "providers/accounts/history"; } from "providers/accounts/history";
import { import {
useAccountOwnedTokens, useAccountOwnedTokens,
TokenAccountData, TokenInfoWithPubkey,
} from "providers/accounts/tokens"; } from "providers/accounts/tokens";
import { ErrorCard } from "components/common/ErrorCard"; import { ErrorCard } from "components/common/ErrorCard";
import { LoadingCard } from "components/common/LoadingCard"; import { LoadingCard } from "components/common/LoadingCard";
@ -40,7 +40,7 @@ export function TokenHistoryCard({ pubkey }: { pubkey: PublicKey }) {
return <TokenHistoryTable tokens={tokens} />; return <TokenHistoryTable tokens={tokens} />;
} }
function TokenHistoryTable({ tokens }: { tokens: TokenAccountData[] }) { function TokenHistoryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
const accountHistories = useAccountHistories(); const accountHistories = useAccountHistories();
const fetchAccountHistory = useFetchAccountHistory(); const fetchAccountHistory = useFetchAccountHistory();
@ -77,7 +77,7 @@ function TokenHistoryTable({ tokens }: { tokens: TokenAccountData[] }) {
const mintAndTxs = tokens const mintAndTxs = tokens
.map((token) => ({ .map((token) => ({
mint: token.mint, mint: token.info.mint,
history: accountHistories[token.pubkey.toBase58()], history: accountHistories[token.pubkey.toBase58()],
})) }))
.filter(({ history }) => { .filter(({ history }) => {

View File

@ -1,31 +1,18 @@
import React from "react"; import React from "react";
import { Connection, PublicKey } from "@solana/web3.js"; import { Connection, PublicKey } from "@solana/web3.js";
import { FetchStatus } from "./index"; import { FetchStatus } from "./index";
import { TokenAccountInfo } from "validators/accounts/token";
import { useCluster } from "../cluster"; import { useCluster } from "../cluster";
import { number, string, boolean, coerce, object, nullable } from "superstruct"; import { coerce } from "superstruct";
export type TokenAccountData = { export type TokenInfoWithPubkey = {
info: TokenAccountInfo;
pubkey: PublicKey; pubkey: PublicKey;
mint: PublicKey;
owner: PublicKey;
amount: number;
isInitialized: boolean;
isNative: boolean;
}; };
const TokenAccountInfo = object({
mint: string(),
owner: string(),
amount: number(),
delegate: nullable(string()),
delegatedAmount: number(),
isInitialized: boolean(),
isNative: boolean(),
});
interface AccountTokens { interface AccountTokens {
status: FetchStatus; status: FetchStatus;
tokens?: TokenAccountData[]; tokens?: TokenInfoWithPubkey[];
} }
interface Update { interface Update {
@ -33,7 +20,7 @@ interface Update {
url: string; url: string;
pubkey: PublicKey; pubkey: PublicKey;
status: FetchStatus; status: FetchStatus;
tokens?: TokenAccountData[]; tokens?: TokenInfoWithPubkey[];
} }
interface Clear { interface Clear {
@ -129,14 +116,7 @@ async function fetchAccountTokens(
tokens = value.map((accountInfo) => { tokens = value.map((accountInfo) => {
const parsedInfo = accountInfo.account.data.parsed.info; const parsedInfo = accountInfo.account.data.parsed.info;
const info = coerce(parsedInfo, TokenAccountInfo); const info = coerce(parsedInfo, TokenAccountInfo);
return { return { info, pubkey: accountInfo.pubkey };
pubkey: accountInfo.pubkey,
mint: new PublicKey(info.mint),
owner: new PublicKey(info.owner),
amount: info.amount,
isInitialized: info.isInitialized,
isNative: info.isNative,
};
}); });
status = FetchStatus.Fetched; status = FetchStatus.Fetched;
} catch (error) { } catch (error) {

View File

@ -8,6 +8,7 @@ import {
boolean, boolean,
string, string,
array, array,
pick,
nullable, nullable,
} from "superstruct"; } from "superstruct";
import { Pubkey } from "validators/pubkey"; import { Pubkey } from "validators/pubkey";
@ -16,7 +17,9 @@ export type TokenAccountType = StructType<typeof TokenAccountType>;
export const TokenAccountType = enums(["mint", "account", "multisig"]); export const TokenAccountType = enums(["mint", "account", "multisig"]);
export type TokenAccountInfo = StructType<typeof TokenAccountInfo>; export type TokenAccountInfo = StructType<typeof TokenAccountInfo>;
export const TokenAccountInfo = object({ export const TokenAccountInfo = pick({
isInitialized: boolean(),
isNative: boolean(),
mint: Pubkey, mint: Pubkey,
owner: Pubkey, owner: Pubkey,
amount: optional(number()), // TODO remove when ui amount is deployed amount: optional(number()), // TODO remove when ui amount is deployed
@ -28,9 +31,7 @@ export const TokenAccountInfo = object({
}) })
), ),
delegate: nullable(optional(Pubkey)), delegate: nullable(optional(Pubkey)),
isInitialized: boolean(), delegatedAmount: optional(number()),
isNative: boolean(),
delegatedAmount: number(),
}); });
export type MintAccountInfo = StructType<typeof MintAccountInfo>; export type MintAccountInfo = StructType<typeof MintAccountInfo>;