From 0b47cd1c6757b756192a9b5787ebc28dadf309f1 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 29 Aug 2020 20:50:45 +0800 Subject: [PATCH] Add token2 support to explorer (#11905) --- .../account/TokenAccountSection.tsx | 106 ++++++++++-------- .../account/TokenLargestAccountsCard.tsx | 15 ++- .../instruction/token/TokenDetailsCard.tsx | 56 +++++++++ .../src/components/instruction/token/types.ts | 46 +++++--- explorer/src/pages/AccountDetailsPage.tsx | 6 +- explorer/src/providers/accounts/index.tsx | 69 ++++++++++-- explorer/src/providers/accounts/tokens.tsx | 2 +- explorer/src/providers/mints/index.tsx | 7 +- explorer/src/providers/mints/supply.tsx | 82 -------------- explorer/src/utils/index.tsx | 10 ++ explorer/src/utils/tx.ts | 7 +- explorer/src/validators/accounts/token.ts | 36 +++--- 12 files changed, 257 insertions(+), 185 deletions(-) delete mode 100644 explorer/src/providers/mints/supply.tsx diff --git a/explorer/src/components/account/TokenAccountSection.tsx b/explorer/src/components/account/TokenAccountSection.tsx index a8444686f..4b1917f0f 100644 --- a/explorer/src/components/account/TokenAccountSection.tsx +++ b/explorer/src/components/account/TokenAccountSection.tsx @@ -11,10 +11,9 @@ import { coerce } from "superstruct"; import { TableCardBody } from "components/common/TableCardBody"; import { Address } from "components/common/Address"; import { UnknownAccountCard } from "./UnknownAccountCard"; -import { useFetchTokenSupply, useTokenSupply } from "providers/mints/supply"; -import { FetchStatus } from "providers/cache"; import { TokenRegistry } from "tokenRegistry"; import { useCluster } from "providers/cluster"; +import { normalizeTokenAmount } from "utils"; export function TokenAccountSection({ account, @@ -58,35 +57,7 @@ function MintAccountCard({ const { cluster } = useCluster(); const mintAddress = account.pubkey.toBase58(); const fetchInfo = useFetchAccountInfo(); - const supply = useTokenSupply(mintAddress); - const fetchSupply = useFetchTokenSupply(); - const refreshSupply = () => fetchSupply(account.pubkey); - const refresh = () => { - fetchInfo(account.pubkey); - refreshSupply(); - }; - - let renderSupply; - const supplyTotal = supply?.data?.uiAmount; - if (supplyTotal === undefined) { - if (!supply || supply?.status === FetchStatus.Fetching) { - renderSupply = ( - <> - - Loading - - ); - } else { - renderSupply = "Fetch failed"; - } - } else { - const unit = TokenRegistry.get(mintAddress, cluster)?.symbol; - renderSupply = unit ? `${supplyTotal} ${unit}` : supplyTotal; - } - - React.useEffect(() => { - if (!supply) refreshSupply(); - }, [mintAddress]); // eslint-disable-line react-hooks/exhaustive-deps + const refresh = () => fetchInfo(account.pubkey); const tokenInfo = TokenRegistry.get(mintAddress, cluster); return ( @@ -109,8 +80,14 @@ function MintAccountCard({ - Total Supply - {renderSupply} + + {info.mintAuthority === null ? "Fixed Supply" : "Current Supply"} + + + {normalizeTokenAmount(info.supply, info.decimals).toFixed( + info.decimals + )} + {tokenInfo && ( @@ -127,6 +104,22 @@ function MintAccountCard({ )} + {info.mintAuthority && ( + + Mint Authority + +
+ + + )} + {info.freezeAuthority && ( + + Freeze Authority + +
+ + + )} Decimals {info.decimals} @@ -137,14 +130,6 @@ function MintAccountCard({ Uninitialized )} - {info.owner && ( - - Owner - -
- - - )} ); @@ -160,9 +145,23 @@ function TokenAccountCard({ const refresh = useFetchAccountInfo(); const { cluster } = useCluster(); - const balance = info.tokenAmount?.uiAmount; - const unit = - TokenRegistry.get(info.mint.toBase58(), cluster)?.symbol || "tokens"; + let unit, balance; + if (info.isNative) { + unit = "SOL"; + balance = ( + <> + ◎ + + {new Intl.NumberFormat("en-US", { maximumFractionDigits: 9 }).format( + info.tokenAmount.uiAmount + )} + + + ); + } else { + balance = <>{info.tokenAmount.uiAmount}; + unit = TokenRegistry.get(info.mint.toBase58(), cluster)?.symbol || "tokens"; + } return (
@@ -199,15 +198,30 @@ function TokenAccountCard({ - Balance ({unit}) + Token balance ({unit}) {balance} - {!info.isInitialized && ( + {info.state === "uninitialized" && ( Status Uninitialized )} + {info.rentExemptReserve && ( + + Rent-exempt reserve (SOL) + + <> + ◎ + + {new Intl.NumberFormat("en-US", { + maximumFractionDigits: 9, + }).format(info.rentExemptReserve.uiAmount)} + + + + + )}
); diff --git a/explorer/src/components/account/TokenLargestAccountsCard.tsx b/explorer/src/components/account/TokenLargestAccountsCard.tsx index 509fae8da..52f2dda68 100644 --- a/explorer/src/components/account/TokenLargestAccountsCard.tsx +++ b/explorer/src/components/account/TokenLargestAccountsCard.tsx @@ -3,7 +3,6 @@ import { PublicKey, TokenAccountBalancePair } from "@solana/web3.js"; import { LoadingCard } from "components/common/LoadingCard"; import { ErrorCard } from "components/common/ErrorCard"; import { Address } from "components/common/Address"; -import { useTokenSupply } from "providers/mints/supply"; import { useTokenLargestTokens, useFetchTokenLargestAccounts, @@ -11,10 +10,12 @@ import { import { FetchStatus } from "providers/cache"; import { TokenRegistry } from "tokenRegistry"; import { useCluster } from "providers/cluster"; +import { useMintAccountInfo } from "providers/accounts"; +import { normalizeTokenAmount } from "utils"; export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) { const mintAddress = pubkey.toBase58(); - const supply = useTokenSupply(mintAddress); + const mintInfo = useMintAccountInfo(mintAddress); const largestAccounts = useTokenLargestTokens(mintAddress); const fetchLargestAccounts = useFetchTokenLargestAccounts(); const refreshLargest = () => fetchLargestAccounts(pubkey); @@ -26,10 +27,11 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) { if (!largestAccounts) refreshLargest(); }, [mintAddress]); // eslint-disable-line react-hooks/exhaustive-deps - const supplyTotal = supply?.data?.uiAmount; - if (supplyTotal === undefined || !largestAccounts) { - return null; - } + // Largest accounts hasn't started fetching + if (largestAccounts === undefined) return null; + + // This is not a mint account + if (mintInfo === undefined) return null; if (largestAccounts?.data === undefined) { if (largestAccounts.status === FetchStatus.Fetching) { @@ -49,6 +51,7 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) { return ; } + const supplyTotal = normalizeTokenAmount(mintInfo.supply, mintInfo.decimals); return ( <>
diff --git a/explorer/src/components/instruction/token/TokenDetailsCard.tsx b/explorer/src/components/instruction/token/TokenDetailsCard.tsx index d1634d293..69b1a9d43 100644 --- a/explorer/src/components/instruction/token/TokenDetailsCard.tsx +++ b/explorer/src/components/instruction/token/TokenDetailsCard.tsx @@ -13,6 +13,12 @@ import { InstructionCard } from "../InstructionCard"; import { Address } from "components/common/Address"; import { IX_STRUCTS, TokenInstructionType, IX_TITLES } from "./types"; import { ParsedInfo } from "validators"; +import { + useTokenAccountInfo, + useMintAccountInfo, + useFetchAccountInfo, +} from "providers/accounts"; +import { normalizeTokenAmount } from "utils"; type DetailsProps = { tx: ParsedTransaction; @@ -48,6 +54,50 @@ type InfoProps = { }; function TokenInstruction(props: InfoProps) { + const { mintAddress: infoMintAddress, tokenAddress } = React.useMemo(() => { + let mintAddress: string | undefined; + let tokenAddress: string | undefined; + + // No sense fetching accounts if we don't need to convert an amount + if (!("amount" in props.info)) return {}; + + if ("mint" in props.info && props.info.mint instanceof PublicKey) { + mintAddress = props.info.mint.toBase58(); + } else if ( + "account" in props.info && + props.info.account instanceof PublicKey + ) { + tokenAddress = props.info.account.toBase58(); + } else if ( + "source" in props.info && + props.info.source instanceof PublicKey + ) { + tokenAddress = props.info.source.toBase58(); + } + return { + mintAddress, + tokenAddress, + }; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const tokenInfo = useTokenAccountInfo(tokenAddress); + const mintAddress = infoMintAddress || tokenInfo?.mint.toBase58(); + const mintInfo = useMintAccountInfo(mintAddress); + const fetchAccountInfo = useFetchAccountInfo(); + + React.useEffect(() => { + if (tokenAddress && !tokenInfo) { + fetchAccountInfo(new PublicKey(tokenAddress)); + } + }, [fetchAccountInfo, tokenAddress]); // eslint-disable-line react-hooks/exhaustive-deps + + React.useEffect(() => { + if (mintAddress && !mintInfo) { + fetchAccountInfo(new PublicKey(mintAddress)); + } + }, [fetchAccountInfo, mintAddress]); // eslint-disable-line react-hooks/exhaustive-deps + + const decimals = mintInfo?.decimals; const attributes = []; for (let key in props.info) { const value = props.info[key]; @@ -56,6 +106,12 @@ function TokenInstruction(props: InfoProps) { let tag; if (value instanceof PublicKey) { tag =
; + } else if (key === "amount") { + if (decimals === undefined) { + tag = <>(raw) {value}; + } else { + tag = <>{normalizeTokenAmount(value, decimals).toFixed(decimals)}; + } } else { tag = <>{value}; } diff --git a/explorer/src/components/instruction/token/types.ts b/explorer/src/components/instruction/token/types.ts index 2c81ba184..721988abc 100644 --- a/explorer/src/components/instruction/token/types.ts +++ b/explorer/src/components/instruction/token/types.ts @@ -5,25 +5,29 @@ import { number, optional, array, + pick, + nullable, } from "superstruct"; import { Pubkey } from "validators/pubkey"; -const InitializeMint = object({ +const InitializeMint = pick({ mint: Pubkey, - amount: number(), decimals: number(), - owner: optional(Pubkey), - account: optional(Pubkey), + mintAuthority: Pubkey, + rentSysvar: Pubkey, + freezeAuthority: optional(Pubkey), }); -const InitializeAccount = object({ +const InitializeAccount = pick({ account: Pubkey, mint: Pubkey, owner: Pubkey, + rentSysvar: Pubkey, }); -const InitializeMultisig = object({ +const InitializeMultisig = pick({ multisig: Pubkey, + rentSysvar: Pubkey, signers: array(Pubkey), m: number(), }); @@ -53,11 +57,20 @@ const Revoke = object({ signers: optional(array(Pubkey)), }); -const SetOwner = object({ - owned: Pubkey, - newOwner: Pubkey, - owner: optional(Pubkey), - multisigOwner: optional(Pubkey), +const AuthorityType = enums([ + "mintTokens", + "freezeAccount", + "accountOwner", + "closeAccount", +]); + +const SetAuthority = object({ + mint: optional(Pubkey), + account: optional(Pubkey), + authorityType: AuthorityType, + newAuthority: nullable(Pubkey), + authority: optional(Pubkey), + multisigAuthority: optional(Pubkey), signers: optional(array(Pubkey)), }); @@ -65,13 +78,14 @@ const MintTo = object({ mint: Pubkey, account: Pubkey, amount: number(), - owner: optional(Pubkey), - multisigOwner: optional(Pubkey), + mintAuthority: optional(Pubkey), + multisigMintAuthority: optional(Pubkey), signers: optional(array(Pubkey)), }); const Burn = object({ account: Pubkey, + mint: Pubkey, amount: number(), authority: optional(Pubkey), multisigAuthority: optional(Pubkey), @@ -94,7 +108,7 @@ export const TokenInstructionType = enums([ "transfer", "approve", "revoke", - "setOwner", + "setAuthority", "mintTo", "burn", "closeAccount", @@ -107,7 +121,7 @@ export const IX_STRUCTS = { transfer: Transfer, approve: Approve, revoke: Revoke, - setOwner: SetOwner, + setAuthority: SetAuthority, mintTo: MintTo, burn: Burn, closeAccount: CloseAccount, @@ -120,7 +134,7 @@ export const IX_TITLES = { transfer: "Transfer", approve: "Approve", revoke: "Revoke", - setOwner: "Set Owner", + setAuthority: "Set Authority", mintTo: "Mint To", burn: "Burn", closeAccount: "Close Account", diff --git a/explorer/src/pages/AccountDetailsPage.tsx b/explorer/src/pages/AccountDetailsPage.tsx index f534fb2e3..ff9810f9a 100644 --- a/explorer/src/pages/AccountDetailsPage.tsx +++ b/explorer/src/pages/AccountDetailsPage.tsx @@ -108,7 +108,7 @@ function DetailsSections({ pubkey, tab }: { pubkey: PublicKey; tab?: string }) { }, ]; - if (data && data?.name === "spl-token") { + if (data && data?.program === "spl-token") { if (data.parsed.type === "mint") { tabs.push({ slug: "largest", @@ -141,7 +141,7 @@ function DetailsSections({ pubkey, tab }: { pubkey: PublicKey; tab?: string }) { function InfoSection({ account }: { account: Account }) { const data = account?.details?.data; - if (data && data.name === "stake") { + if (data && data.program === "stake") { let stakeAccountType, stakeAccount; if ("accountType" in data.parsed) { stakeAccount = data.parsed; @@ -158,7 +158,7 @@ function InfoSection({ account }: { account: Account }) { stakeAccountType={stakeAccountType} /> ); - } else if (data && data.name === "spl-token") { + } else if (data && data.program === "spl-token") { return ; } else { return ; diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx index a0f9e3eca..2a5414cd0 100644 --- a/explorer/src/providers/accounts/index.tsx +++ b/explorer/src/providers/accounts/index.tsx @@ -8,18 +8,22 @@ import { TokensProvider, TOKEN_PROGRAM_ID } from "./tokens"; import { coerce } from "superstruct"; import { ParsedInfo } from "validators"; import { StakeAccount } from "validators/accounts/stake"; -import { TokenAccount } from "validators/accounts/token"; +import { + TokenAccount, + MintAccountInfo, + TokenAccountInfo, +} from "validators/accounts/token"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; export { useAccountHistory } from "./history"; export type StakeProgramData = { - name: "stake"; + program: "stake"; parsed: StakeAccount | StakeAccountWasm; }; export type TokenProgramData = { - name: "spl-token"; + program: "spl-token"; parsed: TokenAccount; }; @@ -108,7 +112,7 @@ async function fetchAccountInfo( parsed = wasm.StakeAccount.fromAccountData(result.data); } data = { - name: "stake", + program: "stake", parsed, }; } catch (err) { @@ -123,7 +127,7 @@ async function fetchAccountInfo( const info = coerce(result.data.parsed, ParsedInfo); const parsed = coerce(info, TokenAccount); data = { - name: "spl-token", + program: "spl-token", parsed, }; } catch (err) { @@ -166,17 +170,59 @@ export function useAccounts() { } export function useAccountInfo( - address: string + address: string | undefined ): Cache.CacheEntry | undefined { const context = React.useContext(StateContext); if (!context) { throw new Error(`useAccountInfo must be used within a AccountsProvider`); } - + if (address === undefined) return; return context.entries[address]; } +export function useMintAccountInfo( + address: string | undefined +): MintAccountInfo | undefined { + const accountInfo = useAccountInfo(address); + if (address === undefined) return; + + try { + const data = accountInfo?.data?.details?.data; + if (!data) return; + if (data.program !== "spl-token" || data.parsed.type !== "mint") { + throw new Error("Expected mint"); + } + + return coerce(data.parsed.info, MintAccountInfo); + } catch (err) { + Sentry.captureException(err, { + tags: { address }, + }); + } +} + +export function useTokenAccountInfo( + address: string | undefined +): TokenAccountInfo | undefined { + const accountInfo = useAccountInfo(address); + if (address === undefined) return; + + try { + const data = accountInfo?.data?.details?.data; + if (!data) return; + if (data.program !== "spl-token" || data.parsed.type !== "account") { + throw new Error("Expected token account"); + } + + return coerce(data.parsed.info, TokenAccountInfo); + } catch (err) { + Sentry.captureException(err, { + tags: { address }, + }); + } +} + export function useFetchAccountInfo() { const dispatch = React.useContext(DispatchContext); if (!dispatch) { @@ -186,7 +232,10 @@ export function useFetchAccountInfo() { } const { url } = useCluster(); - return (pubkey: PublicKey) => { - fetchAccountInfo(dispatch, pubkey, url); - }; + return React.useCallback( + (pubkey: PublicKey) => { + fetchAccountInfo(dispatch, pubkey, url); + }, + [dispatch, url] + ); } diff --git a/explorer/src/providers/accounts/tokens.tsx b/explorer/src/providers/accounts/tokens.tsx index eb27f43b4..d04ca5f8a 100644 --- a/explorer/src/providers/accounts/tokens.tsx +++ b/explorer/src/providers/accounts/tokens.tsx @@ -41,7 +41,7 @@ export function TokensProvider({ children }: ProviderProps) { } export const TOKEN_PROGRAM_ID = new PublicKey( - "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o" + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ); async function fetchAccountTokens( diff --git a/explorer/src/providers/mints/index.tsx b/explorer/src/providers/mints/index.tsx index 04d52557e..dbc071b83 100644 --- a/explorer/src/providers/mints/index.tsx +++ b/explorer/src/providers/mints/index.tsx @@ -1,12 +1,7 @@ import React from "react"; -import { SupplyProvider } from "./supply"; import { LargestAccountsProvider } from "./largest"; type ProviderProps = { children: React.ReactNode }; export function MintsProvider({ children }: ProviderProps) { - return ( - - {children} - - ); + return {children}; } diff --git a/explorer/src/providers/mints/supply.tsx b/explorer/src/providers/mints/supply.tsx deleted file mode 100644 index 1d3a45ee0..000000000 --- a/explorer/src/providers/mints/supply.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from "react"; -import * as Sentry from "@sentry/react"; -import { useCluster } from "providers/cluster"; -import * as Cache from "providers/cache"; -import { ActionType, FetchStatus } from "providers/cache"; -import { TokenAmount, PublicKey, Connection } from "@solana/web3.js"; - -type State = Cache.State; -type Dispatch = Cache.Dispatch; - -const StateContext = React.createContext(undefined); -const DispatchContext = React.createContext(undefined); - -type ProviderProps = { children: React.ReactNode }; -export function SupplyProvider({ children }: ProviderProps) { - const { url } = useCluster(); - const [state, dispatch] = Cache.useReducer(url); - - // Clear cache whenever cluster is changed - React.useEffect(() => { - dispatch({ type: ActionType.Clear, url }); - }, [dispatch, url]); - - return ( - - - {children} - - - ); -} - -async function fetchSupply(dispatch: Dispatch, pubkey: PublicKey, url: string) { - dispatch({ - type: ActionType.Update, - key: pubkey.toBase58(), - status: Cache.FetchStatus.Fetching, - url, - }); - - let data; - let fetchStatus; - try { - data = (await new Connection(url, "single").getTokenSupply(pubkey)).value; - - fetchStatus = FetchStatus.Fetched; - } catch (error) { - Sentry.captureException(error, { tags: { url } }); - fetchStatus = FetchStatus.FetchFailed; - } - dispatch({ - type: ActionType.Update, - status: fetchStatus, - data, - key: pubkey.toBase58(), - url, - }); -} - -export function useFetchTokenSupply() { - const dispatch = React.useContext(DispatchContext); - if (!dispatch) { - throw new Error(`useFetchTokenSupply must be used within a MintsProvider`); - } - - const { url } = useCluster(); - return (pubkey: PublicKey) => { - fetchSupply(dispatch, pubkey, url); - }; -} - -export function useTokenSupply( - address: string -): Cache.CacheEntry | undefined { - const context = React.useContext(StateContext); - - if (!context) { - throw new Error(`useTokenSupply must be used within a MintsProvider`); - } - - return context.entries[address]; -} diff --git a/explorer/src/utils/index.tsx b/explorer/src/utils/index.tsx index e2997412a..13fd4a324 100644 --- a/explorer/src/utils/index.tsx +++ b/explorer/src/utils/index.tsx @@ -18,6 +18,16 @@ export function assertUnreachable(x: never): never { throw new Error("Unreachable!"); } +export function normalizeTokenAmount( + raw: string | number, + decimals: number +): number { + let rawTokens: number; + if (typeof raw === "string") rawTokens = parseInt(raw); + else rawTokens = raw; + return rawTokens / Math.pow(10, decimals); +} + export function lamportsToSol(lamports: number | BN): number { if (typeof lamports === "number") { return Math.abs(lamports) / LAMPORTS_PER_SOL; diff --git a/explorer/src/utils/tx.ts b/explorer/src/utils/tx.ts index a1d8494e5..d975f0ac4 100644 --- a/explorer/src/utils/tx.ts +++ b/explorer/src/utils/tx.ts @@ -35,7 +35,7 @@ export const PROGRAM_IDS: { [key: string]: ProgramName } = { [SystemProgram.programId.toBase58()]: "System Program", Vest111111111111111111111111111111111111111: "Vest Program", [VOTE_PROGRAM_ID.toBase58()]: "Vote Program", - TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o: "SPL Token", + TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: "SPL Token", }; const LOADER_IDS = { @@ -49,6 +49,10 @@ const SYSVAR_ID: { [key: string]: string } = { Sysvar1111111111111111111111111111111111111: "SYSVAR", }; +const WRAPPED_SOL: { [key: string]: string } = { + So11111111111111111111111111111111111111112: "Wrapped SOL", +}; + export const SYSVAR_IDS = { [SYSVAR_CLOCK_PUBKEY.toBase58()]: "SYSVAR_CLOCK", SysvarEpochSchedu1e111111111111111111111111: "SYSVAR_EPOCH_SCHEDULE", @@ -67,6 +71,7 @@ export function displayAddress(address: string, cluster: Cluster): string { LOADER_IDS[address] || SYSVAR_IDS[address] || SYSVAR_ID[address] || + WRAPPED_SOL[address] || TokenRegistry.get(address, cluster)?.name || address ); diff --git a/explorer/src/validators/accounts/token.ts b/explorer/src/validators/accounts/token.ts index 8b4c40e2f..2f214351e 100644 --- a/explorer/src/validators/accounts/token.ts +++ b/explorer/src/validators/accounts/token.ts @@ -1,5 +1,4 @@ import { - object, StructType, number, optional, @@ -16,30 +15,39 @@ import { Pubkey } from "validators/pubkey"; export type TokenAccountType = StructType; export const TokenAccountType = enums(["mint", "account", "multisig"]); +export type TokenAccountState = StructType; +const AccountState = enums(["initialized", "uninitialized", "frozen"]); + +const TokenAmount = pick({ + decimals: number(), + uiAmount: number(), + amount: string(), +}); + export type TokenAccountInfo = StructType; export const TokenAccountInfo = pick({ - isInitialized: boolean(), - isNative: boolean(), mint: Pubkey, owner: Pubkey, - tokenAmount: pick({ - decimals: number(), - uiAmount: number(), - amount: string(), - }), - delegate: nullable(optional(Pubkey)), - delegatedAmount: optional(number()), + tokenAmount: TokenAmount, + delegate: optional(Pubkey), + state: AccountState, + isNative: boolean(), + rentExemptReserve: optional(TokenAmount), + delegatedAmount: optional(TokenAmount), + closeAuthority: optional(Pubkey), }); export type MintAccountInfo = StructType; -export const MintAccountInfo = object({ +export const MintAccountInfo = pick({ + mintAuthority: nullable(Pubkey), + supply: string(), decimals: number(), isInitialized: boolean(), - owner: nullable(optional(Pubkey)), + freezeAuthority: nullable(Pubkey), }); export type MultisigAccountInfo = StructType; -export const MultisigAccountInfo = object({ +export const MultisigAccountInfo = pick({ numRequiredSigners: number(), numValidSigners: number(), isInitialized: boolean(), @@ -47,7 +55,7 @@ export const MultisigAccountInfo = object({ }); export type TokenAccount = StructType; -export const TokenAccount = object({ +export const TokenAccount = pick({ type: TokenAccountType, info: any(), });