Explorer: Refactor account provider data types (#28390)
This commit is contained in:
parent
d40875fbec
commit
c38bca9932
|
@ -23,8 +23,12 @@ describe("parseNFTokenAccounts", () => {
|
||||||
]);
|
]);
|
||||||
const nftAccount = parseNFTokenNFTAccount({
|
const nftAccount = parseNFTokenNFTAccount({
|
||||||
pubkey: new PublicKey("FagABcRBhZH27JDtu6A1Jo9woXyoznP28QujLkxkN9Hj"),
|
pubkey: new PublicKey("FagABcRBhZH27JDtu6A1Jo9woXyoznP28QujLkxkN9Hj"),
|
||||||
details: { rawData: buffer, owner: new PublicKey(NFTOKEN_ADDRESS) },
|
space: buffer.length,
|
||||||
} as any);
|
lamports: 1,
|
||||||
|
executable: false,
|
||||||
|
data: { raw: buffer as Buffer },
|
||||||
|
owner: new PublicKey(NFTOKEN_ADDRESS),
|
||||||
|
});
|
||||||
expect(nftAccount!.metadata_url).to.eq(
|
expect(nftAccount!.metadata_url).to.eq(
|
||||||
"https://cdn.glow.app/n/88/78ef17c1-2b5a-468e-ae8f-7403856e9f00.json"
|
"https://cdn.glow.app/n/88/78ef17c1-2b5a-468e-ae8f-7403856e9f00.json"
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,11 +10,8 @@ import { useAnchorProgram } from "providers/anchor";
|
||||||
export function AnchorAccountCard({ account }: { account: Account }) {
|
export function AnchorAccountCard({ account }: { account: Account }) {
|
||||||
const { lamports } = account;
|
const { lamports } = account;
|
||||||
const { url } = useCluster();
|
const { url } = useCluster();
|
||||||
const anchorProgram = useAnchorProgram(
|
const anchorProgram = useAnchorProgram(account.owner.toString(), url);
|
||||||
account.details?.owner.toString() || "",
|
const rawData = account.data.raw;
|
||||||
url
|
|
||||||
);
|
|
||||||
const rawData = account?.details?.rawData;
|
|
||||||
const programName = getAnchorProgramName(anchorProgram) || "Unknown Program";
|
const programName = getAnchorProgramName(anchorProgram) || "Unknown Program";
|
||||||
|
|
||||||
const { decodedAccountData, accountDef } = useMemo(() => {
|
const { decodedAccountData, accountDef } = useMemo(() => {
|
||||||
|
|
|
@ -7,22 +7,27 @@ import { ErrorCard } from "components/common/ErrorCard";
|
||||||
import { Slot } from "components/common/Slot";
|
import { Slot } from "components/common/Slot";
|
||||||
import { lamportsToSolString } from "utils";
|
import { lamportsToSolString } from "utils";
|
||||||
import { useAccountInfo } from "providers/accounts";
|
import { useAccountInfo } from "providers/accounts";
|
||||||
import BN from "bn.js";
|
|
||||||
import { Epoch } from "components/common/Epoch";
|
import { Epoch } from "components/common/Epoch";
|
||||||
|
|
||||||
const MAX_EPOCH = new BN(2).pow(new BN(64)).sub(new BN(1));
|
const U64_MAX = BigInt("0xffffffffffffffff");
|
||||||
|
|
||||||
export function RewardsCard({ pubkey }: { pubkey: PublicKey }) {
|
export function RewardsCard({ pubkey }: { pubkey: PublicKey }) {
|
||||||
const address = React.useMemo(() => pubkey.toBase58(), [pubkey]);
|
const address = React.useMemo(() => pubkey.toBase58(), [pubkey]);
|
||||||
const info = useAccountInfo(address);
|
const info = useAccountInfo(address);
|
||||||
const account = info?.data;
|
const account = info?.data;
|
||||||
const data = account?.details?.data?.parsed.info;
|
const parsedData = account?.data.parsed;
|
||||||
|
|
||||||
const highestEpoch = React.useMemo(() => {
|
const highestEpoch = React.useMemo(() => {
|
||||||
if (data.stake && !data.stake.delegation.deactivationEpoch.eq(MAX_EPOCH)) {
|
if (!parsedData) return;
|
||||||
return data.stake.delegation.deactivationEpoch.toNumber();
|
if (parsedData.program !== "stake") return;
|
||||||
|
const stakeInfo = parsedData.parsed.info.stake;
|
||||||
|
if (
|
||||||
|
stakeInfo !== null &&
|
||||||
|
stakeInfo.delegation.deactivationEpoch !== U64_MAX
|
||||||
|
) {
|
||||||
|
return Number(stakeInfo.delegation.deactivationEpoch);
|
||||||
}
|
}
|
||||||
}, [data]);
|
}, [parsedData]);
|
||||||
|
|
||||||
const rewards = useRewards(address);
|
const rewards = useRewards(address);
|
||||||
const fetchRewards = useFetchRewards();
|
const fetchRewards = useFetchRewards();
|
||||||
|
|
|
@ -125,7 +125,7 @@ function OverviewCard({
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end text-uppercase">
|
<td className="text-lg-end text-uppercase">
|
||||||
<SolBalance lamports={account.lamports || 0} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -54,11 +54,11 @@ export function TokenAccountSection({
|
||||||
case "mint": {
|
case "mint": {
|
||||||
const info = create(tokenAccount.info, MintAccountInfo);
|
const info = create(tokenAccount.info, MintAccountInfo);
|
||||||
|
|
||||||
if (isMetaplexNFT(account.details?.data, info)) {
|
if (isMetaplexNFT(account.data.parsed, info)) {
|
||||||
return (
|
return (
|
||||||
<NonFungibleTokenMintAccountCard
|
<NonFungibleTokenMintAccountCard
|
||||||
account={account}
|
account={account}
|
||||||
nftData={(account.details!.data as TokenProgramData).nftData!}
|
nftData={(account.data.parsed as TokenProgramData).nftData!}
|
||||||
mintInfo={info}
|
mintInfo={info}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,10 +8,8 @@ import { useCluster } from "providers/cluster";
|
||||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||||
|
|
||||||
export function UnknownAccountCard({ account }: { account: Account }) {
|
export function UnknownAccountCard({ account }: { account: Account }) {
|
||||||
const { details, lamports } = account;
|
|
||||||
const { cluster } = useCluster();
|
const { cluster } = useCluster();
|
||||||
const { tokenRegistry } = useTokenRegistry();
|
const { tokenRegistry } = useTokenRegistry();
|
||||||
if (lamports === undefined) return null;
|
|
||||||
|
|
||||||
const label = addressLabel(account.pubkey.toBase58(), cluster, tokenRegistry);
|
const label = addressLabel(account.pubkey.toBase58(), cluster, tokenRegistry);
|
||||||
return (
|
return (
|
||||||
|
@ -36,32 +34,26 @@ export function UnknownAccountCard({ account }: { account: Account }) {
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end">
|
<td className="text-lg-end">
|
||||||
<SolBalance lamports={lamports} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{details?.space !== undefined && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Allocated Data Size</td>
|
<td>Allocated Data Size</td>
|
||||||
<td className="text-lg-end">{details.space} byte(s)</td>
|
<td className="text-lg-end">{account.space} byte(s)</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
|
|
||||||
{details && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Assigned Program Id</td>
|
<td>Assigned Program Id</td>
|
||||||
<td className="text-lg-end">
|
<td className="text-lg-end">
|
||||||
<Address pubkey={details.owner} alignRight link />
|
<Address pubkey={account.owner} alignRight link />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
|
|
||||||
{details && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Executable</td>
|
<td>Executable</td>
|
||||||
<td className="text-lg-end">{details.executable ? "Yes" : "No"}</td>
|
<td className="text-lg-end">{account.executable ? "Yes" : "No"}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
</TableCardBody>
|
</TableCardBody>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -104,7 +104,7 @@ export function UpgradeableProgramSection({
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end text-uppercase">
|
<td className="text-lg-end text-uppercase">
|
||||||
<SolBalance lamports={account.lamports || 0} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -235,22 +235,20 @@ export function UpgradeableProgramDataSection({
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end text-uppercase">
|
<td className="text-lg-end text-uppercase">
|
||||||
<SolBalance lamports={account.lamports || 0} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{account.details?.space !== undefined && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Data (Bytes)</td>
|
<td>Data Size (Bytes)</td>
|
||||||
<td className="text-lg-end">
|
<td className="text-lg-end">
|
||||||
<Downloadable
|
<Downloadable
|
||||||
data={programData.data[0]}
|
data={programData.data[0]}
|
||||||
filename={`${account.pubkey.toString()}.bin`}
|
filename={`${account.pubkey.toString()}.bin`}
|
||||||
>
|
>
|
||||||
<span className="me-2">{account.details.space}</span>
|
<span className="me-2">{account.space}</span>
|
||||||
</Downloadable>
|
</Downloadable>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Upgradeable</td>
|
<td>Upgradeable</td>
|
||||||
<td className="text-lg-end">
|
<td className="text-lg-end">
|
||||||
|
@ -309,15 +307,13 @@ export function UpgradeableProgramBufferSection({
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end text-uppercase">
|
<td className="text-lg-end text-uppercase">
|
||||||
<SolBalance lamports={account.lamports || 0} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{account.details?.space !== undefined && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Data (Bytes)</td>
|
<td>Data Size (Bytes)</td>
|
||||||
<td className="text-lg-end">{account.details.space}</td>
|
<td className="text-lg-end">{account.space}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
{programBuffer.authority !== null && (
|
{programBuffer.authority !== null && (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Deploy Authority</td>
|
<td>Deploy Authority</td>
|
||||||
|
@ -326,14 +322,12 @@ export function UpgradeableProgramBufferSection({
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
{account.details && (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Owner</td>
|
<td>Owner</td>
|
||||||
<td className="text-lg-end">
|
<td className="text-lg-end">
|
||||||
<Address pubkey={account.details.owner} alignRight link />
|
<Address pubkey={account.owner} alignRight link />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
|
||||||
</TableCardBody>
|
</TableCardBody>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -56,7 +56,7 @@ export function AddressLookupTableAccountSection(
|
||||||
<tr>
|
<tr>
|
||||||
<td>Balance (SOL)</td>
|
<td>Balance (SOL)</td>
|
||||||
<td className="text-lg-end text-uppercase">
|
<td className="text-lg-end text-uppercase">
|
||||||
<SolBalance lamports={account.lamports || 0} />
|
<SolBalance lamports={account.lamports} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { NftokenTypes } from "./nftoken-types";
|
||||||
|
|
||||||
export function isNFTokenAccount(account: Account): boolean {
|
export function isNFTokenAccount(account: Account): boolean {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
account.details?.owner.toBase58() === NFTOKEN_ADDRESS &&
|
account.owner.toBase58() === NFTOKEN_ADDRESS && account.data.raw
|
||||||
account.details.rawData
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +19,7 @@ export const parseNFTokenNFTAccount = (
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = NftokenTypes.nftAccountLayout.decode(
|
const parsed = NftokenTypes.nftAccountLayout.decode(account.data.raw!);
|
||||||
account!.details!.rawData!
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -36,7 +33,7 @@ export const parseNFTokenNFTAccount = (
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
address: account!.pubkey.toBase58(),
|
address: account.pubkey.toBase58(),
|
||||||
holder: new PublicKey(parsed.holder).toBase58(),
|
holder: new PublicKey(parsed.holder).toBase58(),
|
||||||
authority: new PublicKey(parsed.authority).toBase58(),
|
authority: new PublicKey(parsed.authority).toBase58(),
|
||||||
authority_can_update: Boolean(parsed.authority_can_update),
|
authority_can_update: Boolean(parsed.authority_can_update),
|
||||||
|
@ -62,7 +59,7 @@ export const parseNFTokenCollectionAccount = (
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = NftokenTypes.collectionAccountLayout.decode(
|
const parsed = NftokenTypes.collectionAccountLayout.decode(
|
||||||
account!.details!.rawData!
|
account.data.raw!
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
|
@ -76,7 +73,7 @@ export const parseNFTokenCollectionAccount = (
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
address: account!.pubkey.toBase58(),
|
address: account.pubkey.toBase58(),
|
||||||
authority: parsed.authority,
|
authority: parsed.authority,
|
||||||
authority_can_update: Boolean(parsed.authority_can_update),
|
authority_can_update: Boolean(parsed.authority_can_update),
|
||||||
metadata_url: parsed.metadata_url?.replace(/\0/g, "") ?? null,
|
metadata_url: parsed.metadata_url?.replace(/\0/g, "") ?? null,
|
||||||
|
|
|
@ -181,7 +181,7 @@ export function AccountDetailsPage({ address, tab }: Props) {
|
||||||
<div className="container mt-n3">
|
<div className="container mt-n3">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="header-body">
|
<div className="header-body">
|
||||||
<AccountHeader address={address} info={info} />
|
<AccountHeader address={address} account={info?.data} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!pubkey ? (
|
{!pubkey ? (
|
||||||
|
@ -195,22 +195,22 @@ export function AccountDetailsPage({ address, tab }: Props) {
|
||||||
|
|
||||||
export function AccountHeader({
|
export function AccountHeader({
|
||||||
address,
|
address,
|
||||||
info,
|
account,
|
||||||
}: {
|
}: {
|
||||||
address: string;
|
address: string;
|
||||||
info?: CacheEntry<Account>;
|
account?: Account;
|
||||||
}) {
|
}) {
|
||||||
const { tokenRegistry } = useTokenRegistry();
|
const { tokenRegistry } = useTokenRegistry();
|
||||||
const tokenDetails = tokenRegistry.get(address);
|
const tokenDetails = tokenRegistry.get(address);
|
||||||
const mintInfo = useMintAccountInfo(address);
|
const mintInfo = useMintAccountInfo(address);
|
||||||
const account = info?.data;
|
const parsedData = account?.data.parsed;
|
||||||
const data = account?.details?.data;
|
const isToken =
|
||||||
const isToken = data?.program === "spl-token" && data?.parsed.type === "mint";
|
parsedData?.program === "spl-token" && parsedData?.parsed.type === "mint";
|
||||||
|
|
||||||
if (isMetaplexNFT(data, mintInfo)) {
|
if (isMetaplexNFT(parsedData, mintInfo)) {
|
||||||
return (
|
return (
|
||||||
<MetaplexNFTHeader
|
<MetaplexNFTHeader
|
||||||
nftData={(data as TokenProgramData).nftData!}
|
nftData={(parsedData as TokenProgramData).nftData!}
|
||||||
address={address}
|
address={address}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -226,12 +226,14 @@ export function AccountHeader({
|
||||||
let unverified = false;
|
let unverified = false;
|
||||||
|
|
||||||
// Fall back to legacy token list when there is stub metadata (blank uri), updatable by default by the mint authority
|
// Fall back to legacy token list when there is stub metadata (blank uri), updatable by default by the mint authority
|
||||||
if (!data?.nftData?.metadata.data.uri && tokenDetails) {
|
if (!parsedData?.nftData?.metadata.data.uri && tokenDetails) {
|
||||||
token = tokenDetails;
|
token = tokenDetails;
|
||||||
} else if (data?.nftData) {
|
} else if (parsedData?.nftData) {
|
||||||
token = {
|
token = {
|
||||||
logoURI: data?.nftData?.json?.image,
|
logoURI: parsedData?.nftData?.json?.image,
|
||||||
name: data?.nftData?.json?.name ?? data?.nftData.metadata.data.name,
|
name:
|
||||||
|
parsedData?.nftData?.json?.name ??
|
||||||
|
parsedData?.nftData.metadata.data.name,
|
||||||
};
|
};
|
||||||
unverified = true;
|
unverified = true;
|
||||||
} else if (tokenDetails) {
|
} else if (tokenDetails) {
|
||||||
|
@ -339,62 +341,68 @@ function DetailsSections({
|
||||||
}
|
}
|
||||||
|
|
||||||
function InfoSection({ account }: { account: Account }) {
|
function InfoSection({ account }: { account: Account }) {
|
||||||
const details = account?.details;
|
const parsedData = account.data.parsed;
|
||||||
const data = details?.data;
|
const rawData = account.data.raw;
|
||||||
|
|
||||||
if (data && data.program === "bpf-upgradeable-loader") {
|
if (parsedData && parsedData.program === "bpf-upgradeable-loader") {
|
||||||
return (
|
return (
|
||||||
<UpgradeableLoaderAccountSection
|
<UpgradeableLoaderAccountSection
|
||||||
account={account}
|
account={account}
|
||||||
parsedData={data.parsed}
|
parsedData={parsedData.parsed}
|
||||||
programData={data.programData}
|
programData={parsedData.programData}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (data && data.program === "stake") {
|
} else if (parsedData && parsedData.program === "stake") {
|
||||||
return (
|
return (
|
||||||
<StakeAccountSection
|
<StakeAccountSection
|
||||||
account={account}
|
account={account}
|
||||||
stakeAccount={data.parsed.info}
|
stakeAccount={parsedData.parsed.info}
|
||||||
activation={data.activation}
|
activation={parsedData.activation}
|
||||||
stakeAccountType={data.parsed.type}
|
stakeAccountType={parsedData.parsed.type}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (account.details?.owner.toBase58() === NFTOKEN_ADDRESS) {
|
} else if (account.owner.toBase58() === NFTOKEN_ADDRESS) {
|
||||||
return <NFTokenAccountSection account={account} />;
|
return <NFTokenAccountSection account={account} />;
|
||||||
} else if (data && data.program === "spl-token") {
|
} else if (parsedData && parsedData.program === "spl-token") {
|
||||||
return <TokenAccountSection account={account} tokenAccount={data.parsed} />;
|
|
||||||
} else if (data && data.program === "nonce") {
|
|
||||||
return <NonceAccountSection account={account} nonceAccount={data.parsed} />;
|
|
||||||
} else if (data && data.program === "vote") {
|
|
||||||
return <VoteAccountSection account={account} voteAccount={data.parsed} />;
|
|
||||||
} else if (data && data.program === "sysvar") {
|
|
||||||
return (
|
return (
|
||||||
<SysvarAccountSection account={account} sysvarAccount={data.parsed} />
|
<TokenAccountSection account={account} tokenAccount={parsedData.parsed} />
|
||||||
);
|
);
|
||||||
} else if (data && data.program === "config") {
|
} else if (parsedData && parsedData.program === "nonce") {
|
||||||
return (
|
return (
|
||||||
<ConfigAccountSection account={account} configAccount={data.parsed} />
|
<NonceAccountSection account={account} nonceAccount={parsedData.parsed} />
|
||||||
|
);
|
||||||
|
} else if (parsedData && parsedData.program === "vote") {
|
||||||
|
return (
|
||||||
|
<VoteAccountSection account={account} voteAccount={parsedData.parsed} />
|
||||||
|
);
|
||||||
|
} else if (parsedData && parsedData.program === "sysvar") {
|
||||||
|
return (
|
||||||
|
<SysvarAccountSection
|
||||||
|
account={account}
|
||||||
|
sysvarAccount={parsedData.parsed}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (parsedData && parsedData.program === "config") {
|
||||||
|
return (
|
||||||
|
<ConfigAccountSection
|
||||||
|
account={account}
|
||||||
|
configAccount={parsedData.parsed}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
data &&
|
parsedData &&
|
||||||
data.program === "address-lookup-table" &&
|
parsedData.program === "address-lookup-table" &&
|
||||||
data.parsed.type === "lookupTable"
|
parsedData.parsed.type === "lookupTable"
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<AddressLookupTableAccountSection
|
<AddressLookupTableAccountSection
|
||||||
account={account}
|
account={account}
|
||||||
lookupTableAccount={data.parsed.info}
|
lookupTableAccount={parsedData.parsed.info}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (rawData && isAddressLookupTableAccount(account.owner, rawData)) {
|
||||||
details?.rawData &&
|
|
||||||
isAddressLookupTableAccount(details.owner, details.rawData)
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<AddressLookupTableAccountSection
|
<AddressLookupTableAccountSection account={account} data={rawData} />
|
||||||
account={account}
|
|
||||||
data={details.rawData}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <UnknownAccountCard account={account} />;
|
return <UnknownAccountCard account={account} />;
|
||||||
|
@ -442,8 +450,8 @@ function MoreSection({
|
||||||
tabs: (JSX.Element | null)[];
|
tabs: (JSX.Element | null)[];
|
||||||
}) {
|
}) {
|
||||||
const pubkey = account.pubkey;
|
const pubkey = account.pubkey;
|
||||||
const details = account?.details;
|
const parsedData = account.data.parsed;
|
||||||
const data = details?.data;
|
const rawData = account.data.raw;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -465,27 +473,27 @@ function MoreSection({
|
||||||
{tab === "instructions" && <TokenInstructionsCard pubkey={pubkey} />}
|
{tab === "instructions" && <TokenInstructionsCard pubkey={pubkey} />}
|
||||||
{tab === "largest" && <TokenLargestAccountsCard pubkey={pubkey} />}
|
{tab === "largest" && <TokenLargestAccountsCard pubkey={pubkey} />}
|
||||||
{tab === "rewards" && <RewardsCard pubkey={pubkey} />}
|
{tab === "rewards" && <RewardsCard pubkey={pubkey} />}
|
||||||
{tab === "vote-history" && data?.program === "vote" && (
|
{tab === "vote-history" && parsedData?.program === "vote" && (
|
||||||
<VotesCard voteAccount={data.parsed} />
|
<VotesCard voteAccount={parsedData.parsed} />
|
||||||
)}
|
)}
|
||||||
{tab === "slot-hashes" &&
|
{tab === "slot-hashes" &&
|
||||||
data?.program === "sysvar" &&
|
parsedData?.program === "sysvar" &&
|
||||||
data.parsed.type === "slotHashes" && (
|
parsedData.parsed.type === "slotHashes" && (
|
||||||
<SlotHashesCard sysvarAccount={data.parsed} />
|
<SlotHashesCard sysvarAccount={parsedData.parsed} />
|
||||||
)}
|
)}
|
||||||
{tab === "stake-history" &&
|
{tab === "stake-history" &&
|
||||||
data?.program === "sysvar" &&
|
parsedData?.program === "sysvar" &&
|
||||||
data.parsed.type === "stakeHistory" && (
|
parsedData.parsed.type === "stakeHistory" && (
|
||||||
<StakeHistoryCard sysvarAccount={data.parsed} />
|
<StakeHistoryCard sysvarAccount={parsedData.parsed} />
|
||||||
)}
|
)}
|
||||||
{tab === "blockhashes" &&
|
{tab === "blockhashes" &&
|
||||||
data?.program === "sysvar" &&
|
parsedData?.program === "sysvar" &&
|
||||||
data.parsed.type === "recentBlockhashes" && (
|
parsedData.parsed.type === "recentBlockhashes" && (
|
||||||
<BlockhashesCard blockhashes={data.parsed.info} />
|
<BlockhashesCard blockhashes={parsedData.parsed.info} />
|
||||||
)}
|
)}
|
||||||
{tab === "metadata" && (
|
{tab === "metadata" && (
|
||||||
<MetaplexMetadataCard
|
<MetaplexMetadataCard
|
||||||
nftData={(account.details?.data as TokenProgramData).nftData!}
|
nftData={(account.data.parsed as TokenProgramData).nftData!}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tab === "nftoken-collection-nfts" && (
|
{tab === "nftoken-collection-nfts" && (
|
||||||
|
@ -497,12 +505,13 @@ function MoreSection({
|
||||||
)}
|
)}
|
||||||
{tab === "attributes" && (
|
{tab === "attributes" && (
|
||||||
<MetaplexNFTAttributesCard
|
<MetaplexNFTAttributesCard
|
||||||
nftData={(account.details?.data as TokenProgramData).nftData!}
|
nftData={(account.data.parsed as TokenProgramData).nftData!}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tab === "domains" && <DomainsCard pubkey={pubkey} />}
|
{tab === "domains" && <DomainsCard pubkey={pubkey} />}
|
||||||
{tab === "security" && data?.program === "bpf-upgradeable-loader" && (
|
{tab === "security" &&
|
||||||
<SecurityCard data={data} />
|
parsedData?.program === "bpf-upgradeable-loader" && (
|
||||||
|
<SecurityCard data={parsedData} />
|
||||||
)}
|
)}
|
||||||
{tab === "anchor-program" && (
|
{tab === "anchor-program" && (
|
||||||
<React.Suspense
|
<React.Suspense
|
||||||
|
@ -521,14 +530,14 @@ function MoreSection({
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
)}
|
)}
|
||||||
{tab === "entries" &&
|
{tab === "entries" &&
|
||||||
details?.rawData &&
|
rawData &&
|
||||||
isAddressLookupTableAccount(details.owner, details.rawData) && (
|
isAddressLookupTableAccount(account.owner, rawData) && (
|
||||||
<LookupTableEntriesCard lookupTableAccountData={details?.rawData} />
|
<LookupTableEntriesCard lookupTableAccountData={rawData} />
|
||||||
)}
|
)}
|
||||||
{tab === "entries" &&
|
{tab === "entries" &&
|
||||||
data?.program === "address-lookup-table" &&
|
parsedData?.program === "address-lookup-table" &&
|
||||||
data.parsed.type === "lookupTable" && (
|
parsedData.parsed.type === "lookupTable" && (
|
||||||
<LookupTableEntriesCard parsedLookupTable={data.parsed.info} />
|
<LookupTableEntriesCard parsedLookupTable={parsedData.parsed.info} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -536,7 +545,7 @@ function MoreSection({
|
||||||
|
|
||||||
function getTabs(pubkey: PublicKey, account: Account): TabComponent[] {
|
function getTabs(pubkey: PublicKey, account: Account): TabComponent[] {
|
||||||
const address = pubkey.toBase58();
|
const address = pubkey.toBase58();
|
||||||
const data = account.details?.data;
|
const parsedData = account.data.parsed;
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
{
|
{
|
||||||
slug: "history",
|
slug: "history",
|
||||||
|
@ -546,31 +555,31 @@ function getTabs(pubkey: PublicKey, account: Account): TabComponent[] {
|
||||||
];
|
];
|
||||||
|
|
||||||
let programTypeKey = "";
|
let programTypeKey = "";
|
||||||
if (data && "parsed" in data && "type" in data.parsed) {
|
if (parsedData) {
|
||||||
programTypeKey = `${data.program}:${data.parsed.type}`;
|
programTypeKey = `${parsedData.program}:${parsedData.parsed.type}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data && data.program in TABS_LOOKUP) {
|
if (parsedData && parsedData.program in TABS_LOOKUP) {
|
||||||
tabs.push(...TABS_LOOKUP[data.program]);
|
tabs.push(...TABS_LOOKUP[parsedData.program]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data && programTypeKey in TABS_LOOKUP) {
|
if (parsedData && programTypeKey in TABS_LOOKUP) {
|
||||||
tabs.push(...TABS_LOOKUP[programTypeKey]);
|
tabs.push(...TABS_LOOKUP[programTypeKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the key for address lookup tables
|
// Add the key for address lookup tables
|
||||||
if (
|
if (
|
||||||
account.details?.rawData &&
|
account.data.raw &&
|
||||||
isAddressLookupTableAccount(account.details.owner, account.details.rawData)
|
isAddressLookupTableAccount(account.owner, account.data.raw)
|
||||||
) {
|
) {
|
||||||
tabs.push(...TABS_LOOKUP["address-lookup-table"]);
|
tabs.push(...TABS_LOOKUP["address-lookup-table"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the key for Metaplex NFTs
|
// Add the key for Metaplex NFTs
|
||||||
if (
|
if (
|
||||||
data &&
|
parsedData &&
|
||||||
programTypeKey === "spl-token:mint" &&
|
programTypeKey === "spl-token:mint" &&
|
||||||
(data as TokenProgramData).nftData
|
(parsedData as TokenProgramData).nftData
|
||||||
) {
|
) {
|
||||||
tabs.push(...TABS_LOOKUP[`${programTypeKey}:metaplexNFT`]);
|
tabs.push(...TABS_LOOKUP[`${programTypeKey}:metaplexNFT`]);
|
||||||
}
|
}
|
||||||
|
@ -589,9 +598,9 @@ function getTabs(pubkey: PublicKey, account: Account): TabComponent[] {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isNFToken &&
|
!isNFToken &&
|
||||||
(!data ||
|
(!parsedData ||
|
||||||
!(
|
!(
|
||||||
TOKEN_TABS_HIDDEN.includes(data.program) ||
|
TOKEN_TABS_HIDDEN.includes(parsedData.program) ||
|
||||||
TOKEN_TABS_HIDDEN.includes(programTypeKey)
|
TOKEN_TABS_HIDDEN.includes(programTypeKey)
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
|
@ -657,7 +666,7 @@ function getAnchorTabs(pubkey: PublicKey, account: Account) {
|
||||||
<AccountDataLink
|
<AccountDataLink
|
||||||
tab={accountDataTab}
|
tab={accountDataTab}
|
||||||
address={pubkey.toString()}
|
address={pubkey.toString()}
|
||||||
programId={account.details?.owner}
|
programId={account.owner}
|
||||||
/>
|
/>
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
),
|
),
|
||||||
|
@ -676,7 +685,7 @@ function AnchorProgramLink({
|
||||||
pubkey: PublicKey;
|
pubkey: PublicKey;
|
||||||
}) {
|
}) {
|
||||||
const { url } = useCluster();
|
const { url } = useCluster();
|
||||||
const anchorProgram = useAnchorProgram(pubkey.toString() ?? "", url);
|
const anchorProgram = useAnchorProgram(pubkey.toString(), url);
|
||||||
|
|
||||||
if (!anchorProgram) {
|
if (!anchorProgram) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -702,13 +711,10 @@ function AccountDataLink({
|
||||||
}: {
|
}: {
|
||||||
address: string;
|
address: string;
|
||||||
tab: Tab;
|
tab: Tab;
|
||||||
programId: PublicKey | undefined;
|
programId: PublicKey;
|
||||||
}) {
|
}) {
|
||||||
const { url } = useCluster();
|
const { url } = useCluster();
|
||||||
const accountAnchorProgram = useAnchorProgram(
|
const accountAnchorProgram = useAnchorProgram(programId.toString(), url);
|
||||||
programId?.toString() ?? "",
|
|
||||||
url
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!accountAnchorProgram) {
|
if (!accountAnchorProgram) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -17,12 +17,11 @@ export const createFeePayerValidator = (
|
||||||
feeLamports: number
|
feeLamports: number
|
||||||
): AccountValidator => {
|
): AccountValidator => {
|
||||||
return (account: Account): string | undefined => {
|
return (account: Account): string | undefined => {
|
||||||
if (account.details === undefined) return "Account doesn't exist";
|
if (account.lamports === 0) return "Account doesn't exist";
|
||||||
if (!account.details.owner.equals(SystemProgram.programId))
|
if (!account.owner.equals(SystemProgram.programId))
|
||||||
return "Only system-owned accounts can pay fees";
|
return "Only system-owned accounts can pay fees";
|
||||||
// TODO: Actually nonce accounts can pay fees too
|
// TODO: Actually nonce accounts can pay fees too
|
||||||
if (account.details.space > 0)
|
if (account.space > 0) return "Only unallocated accounts can pay fees";
|
||||||
return "Only unallocated accounts can pay fees";
|
|
||||||
if (account.lamports < feeLamports) {
|
if (account.lamports < feeLamports) {
|
||||||
return "Insufficient funds for fees";
|
return "Insufficient funds for fees";
|
||||||
}
|
}
|
||||||
|
@ -31,9 +30,8 @@ export const createFeePayerValidator = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const programValidator = (account: Account): string | undefined => {
|
export const programValidator = (account: Account): string | undefined => {
|
||||||
if (account.details === undefined) return "Account doesn't exist";
|
if (account.lamports === 0) return "Account doesn't exist";
|
||||||
if (!account.details.executable)
|
if (!account.executable) return "Only executable accounts can be invoked";
|
||||||
return "Only executable accounts can be invoked";
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,7 +106,8 @@ function AccountInfo({
|
||||||
}
|
}
|
||||||
}, [address, status]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [address, status]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
if (!info?.data)
|
const account = info?.data;
|
||||||
|
if (!account)
|
||||||
return (
|
return (
|
||||||
<span className="text-muted">
|
<span className="text-muted">
|
||||||
<span className="spinner-grow spinner-grow-sm me-2"></span>
|
<span className="spinner-grow spinner-grow-sm me-2"></span>
|
||||||
|
@ -116,23 +115,22 @@ function AccountInfo({
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const errorMessage = validator && validator(info.data);
|
const errorMessage = validator && validator(account);
|
||||||
if (errorMessage) return <span className="text-warning">{errorMessage}</span>;
|
if (errorMessage) return <span className="text-warning">{errorMessage}</span>;
|
||||||
|
|
||||||
if (!info.data.details) {
|
if (account.lamports === 0) {
|
||||||
return <span className="text-muted">Account doesn't exist</span>;
|
return <span className="text-muted">Account doesn't exist</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const owner = info.data.details.owner;
|
const ownerAddress = account.owner.toBase58();
|
||||||
const ownerAddress = owner.toBase58();
|
|
||||||
const ownerLabel = addressLabel(ownerAddress, cluster);
|
const ownerLabel = addressLabel(ownerAddress, cluster);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="text-muted">
|
<span className="text-muted">
|
||||||
{`Owned by ${ownerLabel || ownerAddress}.`}
|
{`Owned by ${ownerLabel || ownerAddress}.`}
|
||||||
{` Balance is ${lamportsToSolString(info.data.lamports)} SOL.`}
|
{` Balance is ${lamportsToSolString(account.lamports)} SOL.`}
|
||||||
{` Size is ${new Intl.NumberFormat("en-US").format(
|
{` Size is ${new Intl.NumberFormat("en-US").format(
|
||||||
info.data.details.space
|
account.space
|
||||||
)} byte(s).`}
|
)} byte(s).`}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
StakeActivationData,
|
StakeActivationData,
|
||||||
AddressLookupTableAccount,
|
AddressLookupTableAccount,
|
||||||
AddressLookupTableProgram,
|
AddressLookupTableProgram,
|
||||||
|
SystemProgram,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { useCluster, Cluster } from "../cluster";
|
import { useCluster, Cluster } from "../cluster";
|
||||||
import { HistoryProvider } from "./history";
|
import { HistoryProvider } from "./history";
|
||||||
|
@ -88,7 +89,7 @@ export type AddressLookupTableProgramData = {
|
||||||
parsed: ParsedAddressLookupTableAccount;
|
parsed: ParsedAddressLookupTableAccount;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProgramData =
|
export type ParsedData =
|
||||||
| UpgradeableLoaderAccountData
|
| UpgradeableLoaderAccountData
|
||||||
| StakeProgramData
|
| StakeProgramData
|
||||||
| TokenProgramData
|
| TokenProgramData
|
||||||
|
@ -98,18 +99,18 @@ export type ProgramData =
|
||||||
| ConfigProgramData
|
| ConfigProgramData
|
||||||
| AddressLookupTableProgramData;
|
| AddressLookupTableProgramData;
|
||||||
|
|
||||||
export interface Details {
|
export interface AccountData {
|
||||||
executable: boolean;
|
parsed?: ParsedData;
|
||||||
owner: PublicKey;
|
raw?: Buffer;
|
||||||
space: number;
|
|
||||||
data?: ProgramData;
|
|
||||||
rawData?: Buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Account {
|
export interface Account {
|
||||||
pubkey: PublicKey;
|
pubkey: PublicKey;
|
||||||
lamports: number;
|
lamports: number;
|
||||||
details?: Details;
|
executable: boolean;
|
||||||
|
owner: PublicKey;
|
||||||
|
space: number;
|
||||||
|
data: AccountData;
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = Cache.State<Account>;
|
type State = Cache.State<Account>;
|
||||||
|
@ -162,12 +163,17 @@ async function fetchAccountInfo(
|
||||||
const connection = new Connection(url, "confirmed");
|
const connection = new Connection(url, "confirmed");
|
||||||
const result = (await connection.getParsedAccountInfo(pubkey)).value;
|
const result = (await connection.getParsedAccountInfo(pubkey)).value;
|
||||||
|
|
||||||
let lamports, details;
|
let account: Account;
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
lamports = 0;
|
account = {
|
||||||
|
pubkey,
|
||||||
|
lamports: 0,
|
||||||
|
owner: SystemProgram.programId,
|
||||||
|
space: 0,
|
||||||
|
executable: false,
|
||||||
|
data: { raw: Buffer.alloc(0) },
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
lamports = result.lamports;
|
|
||||||
|
|
||||||
// Only save data in memory if we can decode it
|
// Only save data in memory if we can decode it
|
||||||
let space: number;
|
let space: number;
|
||||||
if (!("parsed" in result.data)) {
|
if (!("parsed" in result.data)) {
|
||||||
|
@ -176,7 +182,7 @@ async function fetchAccountInfo(
|
||||||
space = result.data.space;
|
space = result.data.space;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data: ProgramData | undefined;
|
let parsedData: ParsedData | undefined;
|
||||||
if ("parsed" in result.data) {
|
if ("parsed" in result.data) {
|
||||||
try {
|
try {
|
||||||
const info = create(result.data.parsed, ParsedInfo);
|
const info = create(result.data.parsed, ParsedInfo);
|
||||||
|
@ -200,7 +206,7 @@ async function fetchAccountInfo(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed,
|
parsed,
|
||||||
programData,
|
programData,
|
||||||
|
@ -215,7 +221,7 @@ async function fetchAccountInfo(
|
||||||
? await connection.getStakeActivation(pubkey)
|
? await connection.getStakeActivation(pubkey)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed,
|
parsed,
|
||||||
activation,
|
activation,
|
||||||
|
@ -223,25 +229,25 @@ async function fetchAccountInfo(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "vote":
|
case "vote":
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed: create(info, VoteAccount),
|
parsed: create(info, VoteAccount),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "nonce":
|
case "nonce":
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed: create(info, NonceAccount),
|
parsed: create(info, NonceAccount),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "sysvar":
|
case "sysvar":
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed: create(info, SysvarAccount),
|
parsed: create(info, SysvarAccount),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "config":
|
case "config":
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed: create(info, ConfigAccount),
|
parsed: create(info, ConfigAccount),
|
||||||
};
|
};
|
||||||
|
@ -250,7 +256,7 @@ async function fetchAccountInfo(
|
||||||
case "address-lookup-table": {
|
case "address-lookup-table": {
|
||||||
const parsed = create(info, ParsedAddressLookupTableAccount);
|
const parsed = create(info, ParsedAddressLookupTableAccount);
|
||||||
|
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed,
|
parsed,
|
||||||
};
|
};
|
||||||
|
@ -291,14 +297,14 @@ async function fetchAccountInfo(
|
||||||
// unable to find NFT metadata account
|
// unable to find NFT metadata account
|
||||||
}
|
}
|
||||||
|
|
||||||
data = {
|
parsedData = {
|
||||||
program: result.data.program,
|
program: result.data.program,
|
||||||
parsed,
|
parsed,
|
||||||
nftData,
|
nftData,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
data = undefined;
|
parsedData = undefined;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reportError(error, { url, address: pubkey.toBase58() });
|
reportError(error, { url, address: pubkey.toBase58() });
|
||||||
|
@ -308,19 +314,23 @@ async function fetchAccountInfo(
|
||||||
// If we cannot parse account layout as native spl account
|
// If we cannot parse account layout as native spl account
|
||||||
// then keep raw data for other components to decode
|
// then keep raw data for other components to decode
|
||||||
let rawData: Buffer | undefined;
|
let rawData: Buffer | undefined;
|
||||||
if (!data && !("parsed" in result.data)) {
|
if (!parsedData && !("parsed" in result.data)) {
|
||||||
rawData = result.data;
|
rawData = result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
details = {
|
account = {
|
||||||
|
pubkey,
|
||||||
|
lamports: result.lamports,
|
||||||
space,
|
space,
|
||||||
executable: result.executable,
|
executable: result.executable,
|
||||||
owner: result.owner,
|
owner: result.owner,
|
||||||
data,
|
data: {
|
||||||
rawData,
|
parsed: parsedData,
|
||||||
|
raw: rawData,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
data = { pubkey, lamports, details };
|
data = account;
|
||||||
fetchStatus = FetchStatus.Fetched;
|
fetchStatus = FetchStatus.Fetched;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (cluster !== Cluster.Custom) {
|
if (cluster !== Cluster.Custom) {
|
||||||
|
@ -413,16 +423,20 @@ export function useMintAccountInfo(
|
||||||
): MintAccountInfo | undefined {
|
): MintAccountInfo | undefined {
|
||||||
const accountInfo = useAccountInfo(address);
|
const accountInfo = useAccountInfo(address);
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
if (address === undefined) return;
|
if (address === undefined || accountInfo?.data === undefined) return;
|
||||||
|
const account = accountInfo.data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = accountInfo?.data?.details?.data;
|
const parsedData = account.data.parsed;
|
||||||
if (!data) return;
|
if (!parsedData) return;
|
||||||
if (data.program !== "spl-token" || data.parsed.type !== "mint") {
|
if (
|
||||||
|
parsedData.program !== "spl-token" ||
|
||||||
|
parsedData.parsed.type !== "mint"
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return create(data.parsed.info, MintAccountInfo);
|
return create(parsedData.parsed.info, MintAccountInfo);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reportError(err, { address });
|
reportError(err, { address });
|
||||||
}
|
}
|
||||||
|
@ -433,16 +447,20 @@ export function useTokenAccountInfo(
|
||||||
address: string | undefined
|
address: string | undefined
|
||||||
): TokenAccountInfo | undefined {
|
): TokenAccountInfo | undefined {
|
||||||
const accountInfo = useAccountInfo(address);
|
const accountInfo = useAccountInfo(address);
|
||||||
if (address === undefined) return;
|
if (address === undefined || accountInfo?.data === undefined) return;
|
||||||
|
const account = accountInfo.data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = accountInfo?.data?.details?.data;
|
const parsedData = account.data.parsed;
|
||||||
if (!data) return;
|
if (!parsedData) return;
|
||||||
if (data.program !== "spl-token" || data.parsed.type !== "account") {
|
if (
|
||||||
|
parsedData.program !== "spl-token" ||
|
||||||
|
parsedData.parsed.type !== "account"
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return create(data.parsed.info, TokenAccountInfo);
|
return create(parsedData.parsed.info, TokenAccountInfo);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reportError(err, { address });
|
reportError(err, { address });
|
||||||
}
|
}
|
||||||
|
@ -452,24 +470,24 @@ export function useAddressLookupTable(
|
||||||
address: string | undefined
|
address: string | undefined
|
||||||
): AddressLookupTableAccount | undefined | string {
|
): AddressLookupTableAccount | undefined | string {
|
||||||
const accountInfo = useAccountInfo(address);
|
const accountInfo = useAccountInfo(address);
|
||||||
if (address === undefined) return;
|
if (address === undefined || accountInfo?.data === undefined) return;
|
||||||
if (accountInfo?.data?.details === undefined) return;
|
const account = accountInfo.data;
|
||||||
if (accountInfo.data.lamports === 0) return "Lookup Table Not Found";
|
if (account.lamports === 0) return "Lookup Table Not Found";
|
||||||
const { data, rawData } = accountInfo.data.details;
|
const { parsed: parsedData, raw: rawData } = account.data;
|
||||||
|
|
||||||
const key = new PublicKey(address);
|
const key = new PublicKey(address);
|
||||||
if (data && data.program === "address-lookup-table") {
|
if (parsedData && parsedData.program === "address-lookup-table") {
|
||||||
if (data.parsed.type === "lookupTable") {
|
if (parsedData.parsed.type === "lookupTable") {
|
||||||
return new AddressLookupTableAccount({
|
return new AddressLookupTableAccount({
|
||||||
key,
|
key,
|
||||||
state: data.parsed.info,
|
state: parsedData.parsed.info,
|
||||||
});
|
});
|
||||||
} else if (data.parsed.type === "uninitialized") {
|
} else if (parsedData.parsed.type === "uninitialized") {
|
||||||
return "Lookup Table Uninitialized";
|
return "Lookup Table Uninitialized";
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
rawData &&
|
rawData &&
|
||||||
accountInfo.data.details.owner.equals(AddressLookupTableProgram.programId)
|
account.owner.equals(AddressLookupTableProgram.programId)
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return new AddressLookupTableAccount({
|
return new AddressLookupTableAccount({
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { MintAccountInfo } from "validators/accounts/token";
|
import { MintAccountInfo } from "validators/accounts/token";
|
||||||
import { ProgramData } from "..";
|
import { ParsedData } from "..";
|
||||||
|
|
||||||
export default function isMetaplexNFT(
|
export default function isMetaplexNFT(
|
||||||
data?: ProgramData,
|
parsedData?: ParsedData,
|
||||||
mintInfo?: MintAccountInfo
|
mintInfo?: MintAccountInfo
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
data?.program === "spl-token" &&
|
parsedData?.program === "spl-token" &&
|
||||||
data?.parsed.type === "mint" &&
|
parsedData?.parsed.type === "mint" &&
|
||||||
data?.nftData &&
|
parsedData?.nftData &&
|
||||||
mintInfo?.decimals === 0 &&
|
mintInfo?.decimals === 0 &&
|
||||||
(parseInt(mintInfo.supply) === 1 ||
|
(parseInt(mintInfo.supply) === 1 ||
|
||||||
data?.nftData?.metadata?.tokenStandard === 1)
|
parsedData?.nftData?.metadata?.tokenStandard === 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ export function abbreviatedNumber(value: number, fixed = 1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const pubkeyToString = (key: PublicKey | string = "") => {
|
export const pubkeyToString = (key: PublicKey | string = "") => {
|
||||||
return typeof key === "string" ? key : key?.toBase58() || "";
|
return typeof key === "string" ? key : key.toBase58();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLast = (arr: string[]) => {
|
export const getLast = (arr: string[]) => {
|
||||||
|
|
Loading…
Reference in New Issue