diff --git a/explorer/src/components/instruction/bpf-loader/BpfLoaderDetailsCard.tsx b/explorer/src/components/instruction/bpf-loader/BpfLoaderDetailsCard.tsx new file mode 100644 index 0000000000..44ea0e0b80 --- /dev/null +++ b/explorer/src/components/instruction/bpf-loader/BpfLoaderDetailsCard.tsx @@ -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 ; + case "finalize": + return ; + default: + return ; + } + } catch (error) { + reportError(error, { + signature: props.tx.signatures[0], + }); + return ; + } +} + +type Props = { + ix: ParsedInstruction; + index: number; + result: SignatureResult; + info: any; +}; + +export function BpfLoaderWriteDetailsCard(props: Props) { + const { ix, index, result, info } = props; + + return ( + + + Program + + + + + + + Account + + + + + + + + Bytes (base 64) + + + {info.bytes} + + + + + Offset + {info.offset} + + + ); +} + +export function BpfLoaderFinalizeDetailsCard(props: Props) { + const { ix, index, result, info } = props; + + return ( + + + Program + + + + + + + Account + + + + + + ); +} diff --git a/explorer/src/components/instruction/bpf-loader/types.ts b/explorer/src/components/instruction/bpf-loader/types.ts new file mode 100644 index 0000000000..1ec7b20d06 --- /dev/null +++ b/explorer/src/components/instruction/bpf-loader/types.ts @@ -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, +}; diff --git a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx index 012d0f611d..425e0b2616 100644 --- a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx @@ -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 ; - } - - 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 ( Stake Address - + Old Authority Address - + New Authority Address - + Authority Type - {authorizationType} + {info.authorityType} ); diff --git a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx index 8d9f96d426..27f0b4af75 100644 --- a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Stake Address - + Authority Address - + diff --git a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx index 2102cdeece..63bd40f4db 100644 --- a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Stake Address - + Delegated Vote Address - + Authority Address - + diff --git a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx index 638bfffbb9..556861cf6a 100644 --- a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Stake Address - + Stake Authority Address - + Withdraw Authority Address - + - {params.lockup.epoch > 0 && ( + {info.lockup.epoch > 0 && ( Lockup Expiry Epoch - {params.lockup.epoch} + {info.lockup.epoch} )} - {params.lockup.unixTimestamp > 0 && ( + {info.lockup.unixTimestamp > 0 && ( Lockup Expiry Timestamp - {new Date(params.lockup.unixTimestamp * 1000).toUTCString()} + {new Date(info.lockup.unixTimestamp * 1000).toUTCString()} )} - {!params.lockup.custodian.equals(SystemProgram.programId) && ( + {!info.lockup.custodian.equals(SystemProgram.programId) && ( Lockup Custodian Address - + )} diff --git a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx index b728f5f341..f403a992ad 100644 --- a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( @@ -37,29 +28,27 @@ export function SplitDetailsCard(props: { Stake Address - + Authority Address - + New Stake Address - + Split Amount (SOL) - - {lamportsToSolString(params.lamports)} - + {lamportsToSolString(info.lamports)} ); diff --git a/explorer/src/components/instruction/stake/StakeDetailsCard.tsx b/explorer/src/components/instruction/stake/StakeDetailsCard.tsx index d9e5e9e628..3abae17321 100644 --- a/explorer/src/components/instruction/stake/StakeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/StakeDetailsCard.tsx @@ -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 ; + case "delegate": + return ; + case "authorize": + return ; + case "split": + return ; + case "withdraw": + return ; + case "deactivate": + return ; + default: + return ; + } + } catch (error) { + reportError(error, { + signature: props.tx.signatures[0], + }); return ; } - - switch (stakeInstructionType) { - case "Initialize": - return ; - case "Delegate": - return ; - case "Authorize": - return ; - case "Split": - return ; - case "Withdraw": - return ; - case "Deactivate": - return ; - default: - return ; - } } diff --git a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx index d9a5cc3e2d..7f7faa75c4 100644 --- a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Stake Address - + Authority Address - + To Address - + Withdraw Amount (SOL) - - {lamportsToSolString(params.lamports)} - + {lamportsToSolString(info.lamports)} ); diff --git a/explorer/src/components/instruction/stake/types.ts b/explorer/src/components/instruction/stake/types.ts new file mode 100644 index 0000000000..3cb9067c85 --- /dev/null +++ b/explorer/src/components/instruction/stake/types.ts @@ -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; +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, +}; diff --git a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx index 81e06ae7bd..135822de80 100644 --- a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Account Address - + Allocated Space (Bytes) - {params.space} + {info.space} ); diff --git a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx index a33f8f29f5..88ca44b8e6 100644 --- a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Account Address - + Base Address - + Seed - - {params.seed} + + {info.seed} Allocated Space (Bytes) - {params.space} + {info.space} Assigned Owner - + diff --git a/explorer/src/components/instruction/system/AssignDetailsCard.tsx b/explorer/src/components/instruction/system/AssignDetailsCard.tsx index d45112828c..40599e4e3f 100644 --- a/explorer/src/components/instruction/system/AssignDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Account Address - + Assigned Owner - + diff --git a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx index 60cd247f8e..4123e254ab 100644 --- a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Account Address - + Base Address - + Seed - - {params.seed} + + {info.seed} @@ -65,7 +56,7 @@ export function AssignWithSeedDetailsCard(props: { Assigned Owner - + diff --git a/explorer/src/components/instruction/system/CreateDetailsCard.tsx b/explorer/src/components/instruction/system/CreateDetailsCard.tsx index dcc0a8f93a..478d9173e8 100644 --- a/explorer/src/components/instruction/system/CreateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( From Address - + New Address - + Transfer Amount (SOL) - - {lamportsToSolString(params.lamports)} - + {lamportsToSolString(info.lamports)} Allocated Space (Bytes) - {params.space} + {info.space} Assigned Owner - + diff --git a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx index cce3ecd153..41ab86f0d8 100644 --- a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( From Address - + New Address - + Base Address - + Seed - - {params.seed} + + {info.seed} Transfer Amount (SOL) - - {lamportsToSolString(params.lamports)} - + {lamportsToSolString(info.lamports)} Allocated Space (Bytes) - {params.space} + {info.space} Assigned Owner - + diff --git a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx index 5047bf6d9e..942f3e468a 100644 --- a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Nonce Address - + Authority Address - + diff --git a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx index 3d0d794212..dddff72080 100644 --- a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Nonce Address - + Old Authority Address - + New Authority Address - + diff --git a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx index 0e3c5208d5..dd1b1a0db3 100644 --- a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Nonce Address - + Authority Address - + diff --git a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx index 2729fd699f..605d4a94dd 100644 --- a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( Nonce Address - + Authority Address - + To Address - + Withdraw Amount (SOL) - - {lamportsToSolString(params.lamports)} - + {lamportsToSolString(info.lamports)} ); diff --git a/explorer/src/components/instruction/system/SystemDetailsCard.tsx b/explorer/src/components/instruction/system/SystemDetailsCard.tsx index 9b0d77ba78..1e9b2d2c69 100644 --- a/explorer/src/components/instruction/system/SystemDetailsCard.tsx +++ b/explorer/src/components/instruction/system/SystemDetailsCard.tsx @@ -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 ; + case "createAccountWithSeed": + return ; + case "allocate": + return ; + case "allocateWithSeed": + return ; + case "assign": + return ; + case "assignWithSeed": + return ; + case "transfer": + return ; + case "advanceNonceAccount": + return ; + case "withdrawNonceAccount": + return ; + case "authorizeNonceAccount": + return ; + case "initializeNonceAccount": + return ; + default: + return ; + } + } catch (error) { + reportError(error, { + signature: props.tx.signatures[0], + }); return ; } - - switch (systemInstructionType) { - case "Create": - return ; - case "CreateWithSeed": - return ; - case "Allocate": - return ; - case "AllocateWithSeed": - return ; - case "Assign": - return ; - case "AssignWithSeed": - return ; - case "Transfer": - return ; - case "AdvanceNonceAccount": - return ; - case "WithdrawNonceAccount": - return ; - case "AuthorizeNonceAccount": - return ; - case "InitializeNonceAccount": - return ; - default: - return ; - } } diff --git a/explorer/src/components/instruction/system/TransferDetailsCard.tsx b/explorer/src/components/instruction/system/TransferDetailsCard.tsx index 02c7050a2e..bb3823238a 100644 --- a/explorer/src/components/instruction/system/TransferDetailsCard.tsx +++ b/explorer/src/components/instruction/system/TransferDetailsCard.tsx @@ -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 ; - } + const { ix, index, result, info } = props; return ( @@ -37,22 +28,20 @@ export function TransferDetailsCard(props: { From Address - + To Address - + Transfer Amount (SOL) - - {lamportsToSolString(transfer.lamports)} - + {lamportsToSolString(info.lamports)} ); diff --git a/explorer/src/components/instruction/system/types.ts b/explorer/src/components/instruction/system/types.ts new file mode 100644 index 0000000000..12ff1a9699 --- /dev/null +++ b/explorer/src/components/instruction/system/types.ts @@ -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; +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 +}; diff --git a/explorer/src/pages/TransactionDetailsPage.tsx b/explorer/src/pages/TransactionDetailsPage.tsx index b27ac1970a..c01f3ad18e 100644 --- a/explorer/src/pages/TransactionDetailsPage.tsx +++ b/explorer/src/pages/TransactionDetailsPage.tsx @@ -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 ( - - ); + switch (next.program) { + case "spl-token": + return ( + + ); + case "bpf-loader": + return ( + + ); + case "system": + return ( + + ); + case "stake": + return ( + + ); + default: + const props = { ix: next, result, index }; + return ; } - - const props = { ix: next, result, index }; - return ; } const ix = intoTransactionInstruction(transaction, index); + if (!ix) { return ( ; - } else if (StakeProgram.programId.equals(ix.programId)) { - return ; - } else if (isSerumInstruction(ix)) { + + if (isSerumInstruction(ix)) { return ; } else { return ;
{info.bytes}
{params.seed}
{info.seed}