explorer: Update instruction details cards to handle parsed instructions for system, stake, and bpf-loader (#13247)

* update instruction cards to support system, stake, and bpf-loader parsed instructions
This commit is contained in:
Josh 2020-10-28 15:28:12 -07:00 committed by GitHub
parent db9ddc7e5b
commit 413dfb01d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 580 additions and 390 deletions

View File

@ -0,0 +1,117 @@
import React from "react";
import {
SignatureResult,
ParsedInstruction,
ParsedTransaction,
BPF_LOADER_PROGRAM_ID,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { coerce } from "superstruct";
import { ParsedInfo } from "validators";
import { IX_STRUCTS } from "./types";
import { reportError } from "utils/sentry";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
type DetailsProps = {
tx: ParsedTransaction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
};
export function BpfLoaderDetailsCard(props: DetailsProps) {
try {
const parsed = coerce(props.ix.parsed, ParsedInfo);
const info = coerce(parsed.info, IX_STRUCTS[parsed.type]);
switch (parsed.type) {
case "write":
return <BpfLoaderWriteDetailsCard info={info} {...props} />;
case "finalize":
return <BpfLoaderFinalizeDetailsCard info={info} {...props} />;
default:
return <UnknownDetailsCard {...props} />;
}
} catch (error) {
reportError(error, {
signature: props.tx.signatures[0],
});
return <UnknownDetailsCard {...props} />;
}
}
type Props = {
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
};
export function BpfLoaderWriteDetailsCard(props: Props) {
const { ix, index, result, info } = props;
return (
<InstructionCard
ix={ix}
index={index}
result={result}
title="BPF Loader 2: Write"
>
<tr>
<td>Program</td>
<td className="text-lg-right">
<Address pubkey={BPF_LOADER_PROGRAM_ID} alignRight link />
</td>
</tr>
<tr>
<td>Account</td>
<td className="text-lg-right">
<Address pubkey={info.account} alignRight link />
</td>
</tr>
<tr>
<td>
Bytes <span className="text-muted">(base 64)</span>
</td>
<td className="text-lg-right">
<code className="d-inline-block">{info.bytes}</code>
</td>
</tr>
<tr>
<td>Offset</td>
<td className="text-lg-right">{info.offset}</td>
</tr>
</InstructionCard>
);
}
export function BpfLoaderFinalizeDetailsCard(props: Props) {
const { ix, index, result, info } = props;
return (
<InstructionCard
ix={ix}
index={index}
result={result}
title="BPF Loader 2: Finalize"
>
<tr>
<td>Program</td>
<td className="text-lg-right">
<Address pubkey={BPF_LOADER_PROGRAM_ID} alignRight link />
</td>
</tr>
<tr>
<td>Account</td>
<td className="text-lg-right">
<Address pubkey={info.account} alignRight link />
</td>
</tr>
</InstructionCard>
);
}

View File

@ -0,0 +1,24 @@
/* eslint-disable @typescript-eslint/no-redeclare */
import { enums, number, pick, string, StructType } from "superstruct";
import { Pubkey } from "validators/pubkey";
const Write = pick({
account: Pubkey,
bytes: string(),
offset: number(),
});
const Finalize = pick({
account: Pubkey,
});
export type BpfLoaderInstructionType = StructType<
typeof BpfLoaderInstructionType
>;
export const BpfLoaderInstructionType = enums(["write", "finalize"]);
export const IX_STRUCTS: { [id: string]: any } = {
write: Write,
finalize: Finalize,
};

View File

@ -1,40 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function AuthorizeDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeAuthorize(ix);
} catch (err) {
return <UnknownDetailsCard {...props} />;
}
let authorizationType;
switch (params.stakeAuthorizationType.index) {
case 0:
authorizationType = "Staker";
break;
case 1:
authorizationType = "Withdrawer";
break;
default:
authorizationType = "Invalid";
break;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -53,27 +32,27 @@ export function AuthorizeDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Old Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.authority} alignRight link />
</td>
</tr>
<tr>
<td>New Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.newAuthorizedPubkey} alignRight link />
<Address pubkey={info.newAuthority} alignRight link />
</td>
</tr>
<tr>
<td>Authority Type</td>
<td className="text-lg-right">{authorizationType}</td>
<td className="text-lg-right">{info.authorityType}</td>
</tr>
</InstructionCard>
);

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function DeactivateDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeDeactivate(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,14 +32,14 @@ export function DeactivateDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.stakeAuthority} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function DelegateDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeDelegate(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,21 +32,21 @@ export function DelegateDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Delegated Vote Address</td>
<td className="text-lg-right">
<Address pubkey={params.votePubkey} alignRight link />
<Address pubkey={info.voteAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.stakeAuthority} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
SystemProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function InitializeDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeInitialize(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,45 +33,45 @@ export function InitializeDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Stake Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorized.staker} alignRight link />
<Address pubkey={info.authorized.staker} alignRight link />
</td>
</tr>
<tr>
<td>Withdraw Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorized.withdrawer} alignRight link />
<Address pubkey={info.authorized.withdrawer} alignRight link />
</td>
</tr>
{params.lockup.epoch > 0 && (
{info.lockup.epoch > 0 && (
<tr>
<td>Lockup Expiry Epoch</td>
<td className="text-lg-right">{params.lockup.epoch}</td>
<td className="text-lg-right">{info.lockup.epoch}</td>
</tr>
)}
{params.lockup.unixTimestamp > 0 && (
{info.lockup.unixTimestamp > 0 && (
<tr>
<td>Lockup Expiry Timestamp</td>
<td className="text-lg-right">
{new Date(params.lockup.unixTimestamp * 1000).toUTCString()}
{new Date(info.lockup.unixTimestamp * 1000).toUTCString()}
</td>
</tr>
)}
{!params.lockup.custodian.equals(SystemProgram.programId) && (
{!info.lockup.custodian.equals(SystemProgram.programId) && (
<tr>
<td>Lockup Custodian Address</td>
<td className="text-lg-right">
<Address pubkey={params.lockup.custodian} alignRight link />
<Address pubkey={info.lockup.custodian} alignRight link />
</td>
</tr>
)}

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function SplitDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeSplit(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard ix={ix} index={index} result={result} title="Split Stake">
@ -37,29 +28,27 @@ export function SplitDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.stakeAuthority} alignRight link />
</td>
</tr>
<tr>
<td>New Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.splitStakePubkey} alignRight link />
<Address pubkey={info.newSplitAccount} alignRight link />
</td>
</tr>
<tr>
<td>Split Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(params.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
</InstructionCard>
);

View File

@ -1,8 +1,8 @@
import React from "react";
import {
StakeInstruction,
TransactionInstruction,
SignatureResult,
ParsedTransaction,
ParsedInstruction,
} from "@solana/web3.js";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
@ -12,36 +12,43 @@ import { AuthorizeDetailsCard } from "./AuthorizeDetailsCard";
import { SplitDetailsCard } from "./SplitDetailsCard";
import { WithdrawDetailsCard } from "./WithdrawDetailsCard";
import { DeactivateDetailsCard } from "./DeactivateDetailsCard";
import { ParsedInfo } from "validators";
import { reportError } from "utils/sentry";
import { coerce } from "superstruct";
import { IX_STRUCTS } from "./types";
type DetailsProps = {
ix: TransactionInstruction;
tx: ParsedTransaction;
ix: ParsedInstruction;
result: SignatureResult;
index: number;
};
export function StakeDetailsCard(props: DetailsProps) {
let stakeInstructionType;
try {
stakeInstructionType = StakeInstruction.decodeInstructionType(props.ix);
} catch (err) {
console.error(err);
const parsed = coerce(props.ix.parsed, ParsedInfo);
const info = coerce(parsed.info, IX_STRUCTS[parsed.type]);
switch (parsed.type) {
case "initialize":
return <InitializeDetailsCard info={info} {...props} />;
case "delegate":
return <DelegateDetailsCard info={info} {...props} />;
case "authorize":
return <AuthorizeDetailsCard info={info} {...props} />;
case "split":
return <SplitDetailsCard info={info} {...props} />;
case "withdraw":
return <WithdrawDetailsCard info={info} {...props} />;
case "deactivate":
return <DeactivateDetailsCard info={info} {...props} />;
default:
return <UnknownDetailsCard {...props} />;
}
} catch (error) {
reportError(error, {
signature: props.tx.signatures[0],
});
return <UnknownDetailsCard {...props} />;
}
switch (stakeInstructionType) {
case "Initialize":
return <InitializeDetailsCard {...props} />;
case "Delegate":
return <DelegateDetailsCard {...props} />;
case "Authorize":
return <AuthorizeDetailsCard {...props} />;
case "Split":
return <SplitDetailsCard {...props} />;
case "Withdraw":
return <WithdrawDetailsCard {...props} />;
case "Deactivate":
return <DeactivateDetailsCard {...props} />;
default:
return <UnknownDetailsCard {...props} />;
}
}

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SignatureResult,
StakeInstruction,
StakeProgram,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function WithdrawDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = StakeInstruction.decodeWithdraw(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,29 +33,27 @@ export function WithdrawDetailsCard(props: {
<tr>
<td>Stake Address</td>
<td className="text-lg-right">
<Address pubkey={params.stakePubkey} alignRight link />
<Address pubkey={info.stakeAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.withdrawAuthority} alignRight link />
</td>
</tr>
<tr>
<td>To Address</td>
<td className="text-lg-right">
<Address pubkey={params.toPubkey} alignRight link />
<Address pubkey={info.destination} alignRight link />
</td>
</tr>
<tr>
<td>Withdraw Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(params.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
</InstructionCard>
);

View File

@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/no-redeclare */
import { enums, number, pick, string, StructType } from "superstruct";
import { Pubkey } from "validators/pubkey";
const Initialize = pick({
stakeAccount: Pubkey,
authorized: pick({
staker: Pubkey,
withdrawer: Pubkey,
}),
lockup: pick({
epoch: number(),
unixTimestamp: number(),
custodian: Pubkey,
}),
});
const Delegate = pick({
stakeAccount: Pubkey,
voteAccount: Pubkey,
stakeAuthority: Pubkey,
});
const Authorize = pick({
authorityType: string(),
stakeAccount: Pubkey,
authority: Pubkey,
newAuthority: Pubkey,
});
const Split = pick({
stakeAccount: Pubkey,
stakeAuthority: Pubkey,
newSplitAccount: Pubkey,
lamports: number(),
});
const Withdraw = pick({
stakeAccount: Pubkey,
withdrawAuthority: Pubkey,
destination: Pubkey,
lamports: number(),
});
const Deactivate = pick({
stakeAccount: Pubkey,
stakeAuthority: Pubkey,
});
export type StakeInstructionType = StructType<typeof StakeInstructionType>;
export const StakeInstructionType = enums([
"initialize",
"delegate",
"authorize",
"split",
"withdraw",
"deactivate",
]);
export const IX_STRUCTS: { [id: string]: any } = {
initialize: Initialize,
delegate: Delegate,
authorize: Authorize,
split: Split,
withdraw: Withdraw,
deactivate: Deactivate,
};

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function AllocateDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeAllocate(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,13 +32,13 @@ export function AllocateDetailsCard(props: {
<tr>
<td>Account Address</td>
<td className="text-lg-right">
<Address pubkey={params.accountPubkey} alignRight link />
<Address pubkey={info.account} alignRight link />
</td>
</tr>
<tr>
<td>Allocated Space (Bytes)</td>
<td className="text-lg-right">{params.space}</td>
<td className="text-lg-right">{info.space}</td>
</tr>
</InstructionCard>
);

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { Copyable } from "components/common/Copyable";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function AllocateWithSeedDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeAllocateWithSeed(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,35 +33,35 @@ export function AllocateWithSeedDetailsCard(props: {
<tr>
<td>Account Address</td>
<td className="text-lg-right">
<Address pubkey={params.accountPubkey} alignRight link />
<Address pubkey={info.account} alignRight link />
</td>
</tr>
<tr>
<td>Base Address</td>
<td className="text-lg-right">
<Address pubkey={params.basePubkey} alignRight link />
<Address pubkey={info.base} alignRight link />
</td>
</tr>
<tr>
<td>Seed</td>
<td className="text-lg-right">
<Copyable right text={params.seed}>
<code>{params.seed}</code>
<Copyable right text={info.seed}>
<code>{info.seed}</code>
</Copyable>
</td>
</tr>
<tr>
<td>Allocated Space (Bytes)</td>
<td className="text-lg-right">{params.space}</td>
<td className="text-lg-right">{info.space}</td>
</tr>
<tr>
<td>Assigned Owner</td>
<td className="text-lg-right">
<Address pubkey={params.programId} alignRight link />
<Address pubkey={info.owner} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function AssignDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeAssign(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,14 +32,14 @@ export function AssignDetailsCard(props: {
<tr>
<td>Account Address</td>
<td className="text-lg-right">
<Address pubkey={params.accountPubkey} alignRight link />
<Address pubkey={info.account} alignRight link />
</td>
</tr>
<tr>
<td>Assigned Owner</td>
<td className="text-lg-right">
<Address pubkey={params.programId} alignRight link />
<Address pubkey={info.owner} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { Copyable } from "components/common/Copyable";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function AssignWithSeedDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeAssignWithSeed(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,22 +33,22 @@ export function AssignWithSeedDetailsCard(props: {
<tr>
<td>Account Address</td>
<td className="text-lg-right">
<Address pubkey={params.accountPubkey} alignRight link />
<Address pubkey={info.account} alignRight link />
</td>
</tr>
<tr>
<td>Base Address</td>
<td className="text-lg-right">
<Address pubkey={params.basePubkey} alignRight link />
<Address pubkey={info.base} alignRight link />
</td>
</tr>
<tr>
<td>Seed</td>
<td className="text-lg-right">
<Copyable right text={params.seed}>
<code>{params.seed}</code>
<Copyable right text={info.seed}>
<code>{info.seed}</code>
</Copyable>
</td>
</tr>
@ -65,7 +56,7 @@ export function AssignWithSeedDetailsCard(props: {
<tr>
<td>Assigned Owner</td>
<td className="text-lg-right">
<Address pubkey={params.programId} alignRight link />
<Address pubkey={info.owner} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function CreateDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeCreateAccount(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,33 +33,31 @@ export function CreateDetailsCard(props: {
<tr>
<td>From Address</td>
<td className="text-lg-right">
<Address pubkey={params.fromPubkey} alignRight link />
<Address pubkey={info.source} alignRight link />
</td>
</tr>
<tr>
<td>New Address</td>
<td className="text-lg-right">
<Address pubkey={params.newAccountPubkey} alignRight link />
<Address pubkey={info.newAccount} alignRight link />
</td>
</tr>
<tr>
<td>Transfer Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(params.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
<tr>
<td>Allocated Space (Bytes)</td>
<td className="text-lg-right">{params.space}</td>
<td className="text-lg-right">{info.space}</td>
</tr>
<tr>
<td>Assigned Owner</td>
<td className="text-lg-right">
<Address pubkey={params.programId} alignRight link />
<Address pubkey={info.owner} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,30 +1,21 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { Copyable } from "components/common/Copyable";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function CreateWithSeedDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeCreateWithSeed(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -43,49 +34,47 @@ export function CreateWithSeedDetailsCard(props: {
<tr>
<td>From Address</td>
<td className="text-lg-right">
<Address pubkey={params.fromPubkey} alignRight link />
<Address pubkey={info.source} alignRight link />
</td>
</tr>
<tr>
<td>New Address</td>
<td className="text-lg-right">
<Address pubkey={params.newAccountPubkey} alignRight link />
<Address pubkey={info.newAccount} alignRight link />
</td>
</tr>
<tr>
<td>Base Address</td>
<td className="text-lg-right">
<Address pubkey={params.basePubkey} alignRight link />
<Address pubkey={info.base} alignRight link />
</td>
</tr>
<tr>
<td>Seed</td>
<td className="text-lg-right">
<Copyable right text={params.seed}>
<code>{params.seed}</code>
<Copyable right text={info.seed}>
<code>{info.seed}</code>
</Copyable>
</td>
</tr>
<tr>
<td>Transfer Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(params.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
<tr>
<td>Allocated Space (Bytes)</td>
<td className="text-lg-right">{params.space}</td>
<td className="text-lg-right">{info.space}</td>
</tr>
<tr>
<td>Assigned Owner</td>
<td className="text-lg-right">
<Address pubkey={params.programId} alignRight link />
<Address pubkey={info.owner} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function NonceAdvanceDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeNonceAdvance(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,14 +32,14 @@ export function NonceAdvanceDetailsCard(props: {
<tr>
<td>Nonce Address</td>
<td className="text-lg-right">
<Address pubkey={params.noncePubkey} alignRight link />
<Address pubkey={info.nonceAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.nonceAuthority} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function NonceAuthorizeDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeNonceAuthorize(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,21 +32,21 @@ export function NonceAuthorizeDetailsCard(props: {
<tr>
<td>Nonce Address</td>
<td className="text-lg-right">
<Address pubkey={params.noncePubkey} alignRight link />
<Address pubkey={info.nonceAccount} alignRight link />
</td>
</tr>
<tr>
<td>Old Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.nonceAuthority} alignRight link />
</td>
</tr>
<tr>
<td>New Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.newAuthorizedPubkey} alignRight link />
<Address pubkey={info.newAuthorized} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,28 +1,19 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function NonceInitializeDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeNonceInitialize(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -41,14 +32,14 @@ export function NonceInitializeDetailsCard(props: {
<tr>
<td>Nonce Address</td>
<td className="text-lg-right">
<Address pubkey={params.noncePubkey} alignRight link />
<Address pubkey={info.nonceAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.nonceAuthority} alignRight link />
</td>
</tr>
</InstructionCard>

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function NonceWithdrawDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let params;
try {
params = SystemInstruction.decodeNonceWithdraw(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard
@ -42,29 +33,27 @@ export function NonceWithdrawDetailsCard(props: {
<tr>
<td>Nonce Address</td>
<td className="text-lg-right">
<Address pubkey={params.noncePubkey} alignRight link />
<Address pubkey={info.nonceAccount} alignRight link />
</td>
</tr>
<tr>
<td>Authority Address</td>
<td className="text-lg-right">
<Address pubkey={params.authorizedPubkey} alignRight link />
<Address pubkey={info.nonceAuthority} alignRight link />
</td>
</tr>
<tr>
<td>To Address</td>
<td className="text-lg-right">
<Address pubkey={params.toPubkey} alignRight link />
<Address pubkey={info.destination} alignRight link />
</td>
</tr>
<tr>
<td>Withdraw Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(params.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
</InstructionCard>
);

View File

@ -1,8 +1,8 @@
import React from "react";
import {
SystemInstruction,
TransactionInstruction,
SignatureResult,
ParsedInstruction,
ParsedTransaction,
} from "@solana/web3.js";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
@ -17,46 +17,53 @@ import { NonceInitializeDetailsCard } from "./NonceInitializeDetailsCard";
import { NonceAdvanceDetailsCard } from "./NonceAdvanceDetailsCard";
import { NonceWithdrawDetailsCard } from "./NonceWithdrawDetailsCard";
import { NonceAuthorizeDetailsCard } from "./NonceAuthorizeDetailsCard";
import { ParsedInfo } from "validators";
import { coerce } from "superstruct";
import { reportError } from "utils/sentry";
import { IX_STRUCTS } from "./types";
type DetailsProps = {
ix: TransactionInstruction;
tx: ParsedTransaction;
ix: ParsedInstruction;
result: SignatureResult;
index: number;
};
export function SystemDetailsCard(props: DetailsProps) {
let systemInstructionType;
try {
systemInstructionType = SystemInstruction.decodeInstructionType(props.ix);
} catch (err) {
console.error(err);
const parsed = coerce(props.ix.parsed, ParsedInfo);
const info = coerce(parsed.info, IX_STRUCTS[parsed.type]);
switch (parsed.type) {
case "createAccount":
return <CreateDetailsCard info={info} {...props} />;
case "createAccountWithSeed":
return <CreateWithSeedDetailsCard info={info} {...props} />;
case "allocate":
return <AllocateDetailsCard info={info} {...props} />;
case "allocateWithSeed":
return <AllocateWithSeedDetailsCard info={info} {...props} />;
case "assign":
return <AssignDetailsCard info={info} {...props} />;
case "assignWithSeed":
return <AssignWithSeedDetailsCard info={info} {...props} />;
case "transfer":
return <TransferDetailsCard info={info} {...props} />;
case "advanceNonceAccount":
return <NonceAdvanceDetailsCard info={info} {...props} />;
case "withdrawNonceAccount":
return <NonceWithdrawDetailsCard info={info} {...props} />;
case "authorizeNonceAccount":
return <NonceAuthorizeDetailsCard info={info} {...props} />;
case "initializeNonceAccount":
return <NonceInitializeDetailsCard info={info} {...props} />;
default:
return <UnknownDetailsCard {...props} />;
}
} catch (error) {
reportError(error, {
signature: props.tx.signatures[0],
});
return <UnknownDetailsCard {...props} />;
}
switch (systemInstructionType) {
case "Create":
return <CreateDetailsCard {...props} />;
case "CreateWithSeed":
return <CreateWithSeedDetailsCard {...props} />;
case "Allocate":
return <AllocateDetailsCard {...props} />;
case "AllocateWithSeed":
return <AllocateWithSeedDetailsCard {...props} />;
case "Assign":
return <AssignDetailsCard {...props} />;
case "AssignWithSeed":
return <AssignWithSeedDetailsCard {...props} />;
case "Transfer":
return <TransferDetailsCard {...props} />;
case "AdvanceNonceAccount":
return <NonceAdvanceDetailsCard {...props} />;
case "WithdrawNonceAccount":
return <NonceWithdrawDetailsCard {...props} />;
case "AuthorizeNonceAccount":
return <NonceAuthorizeDetailsCard {...props} />;
case "InitializeNonceAccount":
return <NonceInitializeDetailsCard {...props} />;
default:
return <UnknownDetailsCard {...props} />;
}
}

View File

@ -1,29 +1,20 @@
import React from "react";
import {
TransactionInstruction,
SystemProgram,
SignatureResult,
SystemInstruction,
ParsedInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { InstructionCard } from "../InstructionCard";
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { Address } from "components/common/Address";
export function TransferDetailsCard(props: {
ix: TransactionInstruction;
ix: ParsedInstruction;
index: number;
result: SignatureResult;
info: any;
}) {
const { ix, index, result } = props;
let transfer;
try {
transfer = SystemInstruction.decodeTransfer(ix);
} catch (err) {
console.error(err);
return <UnknownDetailsCard {...props} />;
}
const { ix, index, result, info } = props;
return (
<InstructionCard ix={ix} index={index} result={result} title="Transfer">
@ -37,22 +28,20 @@ export function TransferDetailsCard(props: {
<tr>
<td>From Address</td>
<td className="text-lg-right">
<Address pubkey={transfer.fromPubkey} alignRight link />
<Address pubkey={info.source} alignRight link />
</td>
</tr>
<tr>
<td>To Address</td>
<td className="text-lg-right">
<Address pubkey={transfer.toPubkey} alignRight link />
<Address pubkey={info.destination} alignRight link />
</td>
</tr>
<tr>
<td>Transfer Amount (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(transfer.lamports)}
</td>
<td className="text-lg-right">{lamportsToSolString(info.lamports)}</td>
</tr>
</InstructionCard>
);

View File

@ -0,0 +1,115 @@
/* eslint-disable @typescript-eslint/no-redeclare */
import { enums, number, pick, string, StructType } from "superstruct";
import { Pubkey } from "validators/pubkey";
const CreateAccount = pick({
source: Pubkey,
newAccount: Pubkey,
lamports: number(),
space: number(),
owner: Pubkey,
});
const Assign = pick({
account: Pubkey,
owner: Pubkey,
});
const Transfer = pick({
source: Pubkey,
destination: Pubkey,
lamports: number(),
});
const CreateAccountWithSeed = pick({
source: Pubkey,
newAccount: Pubkey,
base: Pubkey,
seed: string(),
lamports: number(),
space: number(),
owner: Pubkey,
});
const AdvanceNonceAccount = pick({
nonceAccount: Pubkey,
nonceAuthority: Pubkey,
});
const WithdrawNonceAccount = pick({
nonceAccount: Pubkey,
destination: Pubkey,
nonceAuthority: Pubkey,
lamports: number(),
});
const InitializeNonceAccount = pick({
nonceAccount: Pubkey,
nonceAuthority: Pubkey,
});
const AuthorizeNonceAccount = pick({
nonceAccount: Pubkey,
nonceAuthority: Pubkey,
newAuthorized: Pubkey,
});
const Allocate = pick({
account: Pubkey,
space: number(),
});
const AllocateWithSeed = pick({
account: Pubkey,
base: Pubkey,
seed: string(),
space: number(),
owner: Pubkey,
});
const AssignWithSeed = pick({
account: Pubkey,
base: Pubkey,
seed: string(),
owner: Pubkey,
});
const TransferWithSeed = pick({
source: Pubkey,
sourceBase: Pubkey,
destination: Pubkey,
lamports: number(),
sourceSeed: string(),
sourceOwner: Pubkey,
});
export type SystemInstructionType = StructType<typeof SystemInstructionType>;
export const SystemInstructionType = enums([
"createAccount",
"createAccountWithSeed",
"allocate",
"allocateWithSeed",
"assign",
"assignWithSeed",
"transfer",
"advanceNonceAccount",
"withdrawNonceAccount",
"authorizeNonceAccount",
"initializeNonceAccount",
]);
export const IX_STRUCTS: { [id: string]: any } = {
createAccount: CreateAccount,
createAccountWithSeed: CreateAccountWithSeed,
allocate: Allocate,
allocateWithSeed: AllocateWithSeed,
assign: Assign,
assignWithSeed: AssignWithSeed,
transfer: Transfer,
advanceNonceAccount: AdvanceNonceAccount,
withdrawNonceAccount: WithdrawNonceAccount,
authorizeNonceAccount: AuthorizeNonceAccount,
initializeNonceAccount: InitializeNonceAccount,
transferWithSeed: TransferWithSeed, // TODO: Add support for transfer with seed
};

View File

@ -10,13 +10,13 @@ import { useCluster, ClusterStatus } from "providers/cluster";
import {
TransactionSignature,
SystemProgram,
StakeProgram,
SystemInstruction,
} from "@solana/web3.js";
import { lamportsToSolString } from "utils";
import { UnknownDetailsCard } from "components/instruction/UnknownDetailsCard";
import { SystemDetailsCard } from "components/instruction/system/SystemDetailsCard";
import { StakeDetailsCard } from "components/instruction/stake/StakeDetailsCard";
import { BpfLoaderDetailsCard } from "components/instruction/bpf-loader/BpfLoaderDetailsCard";
import { ErrorCard } from "components/common/ErrorCard";
import { LoadingCard } from "components/common/LoadingCard";
import { TableCardBody } from "components/common/TableCardBody";
@ -405,23 +405,55 @@ function InstructionsSection({ signature }: SignatureProps) {
const instructionDetails = transaction.message.instructions.map(
(next, index) => {
if ("parsed" in next) {
if (next.program === "spl-token") {
return (
<TokenDetailsCard
key={index}
tx={transaction}
ix={next}
result={result}
index={index}
/>
);
switch (next.program) {
case "spl-token":
return (
<TokenDetailsCard
key={index}
tx={transaction}
ix={next}
result={result}
index={index}
/>
);
case "bpf-loader":
return (
<BpfLoaderDetailsCard
key={index}
tx={transaction}
ix={next}
result={result}
index={index}
/>
);
case "system":
return (
<SystemDetailsCard
key={index}
tx={transaction}
ix={next}
result={result}
index={index}
/>
);
case "stake":
return (
<StakeDetailsCard
key={index}
tx={transaction}
ix={next}
result={result}
index={index}
/>
);
default:
const props = { ix: next, result, index };
return <UnknownDetailsCard key={index} {...props} />;
}
const props = { ix: next, result, index };
return <UnknownDetailsCard key={index} {...props} />;
}
const ix = intoTransactionInstruction(transaction, index);
if (!ix) {
return (
<ErrorCard
@ -432,11 +464,8 @@ function InstructionsSection({ signature }: SignatureProps) {
}
const props = { ix, result, index, signature };
if (SystemProgram.programId.equals(ix.programId)) {
return <SystemDetailsCard key={index} {...props} />;
} else if (StakeProgram.programId.equals(ix.programId)) {
return <StakeDetailsCard key={index} {...props} />;
} else if (isSerumInstruction(ix)) {
if (isSerumInstruction(ix)) {
return <SerumDetailsCard key={index} {...props} />;
} else {
return <UnknownDetailsCard key={index} {...props} />;