diff --git a/explorer/src/components/TransactionDetails.tsx b/explorer/src/components/TransactionDetails.tsx index c5ca73353b..07110d55f1 100644 --- a/explorer/src/components/TransactionDetails.tsx +++ b/explorer/src/components/TransactionDetails.tsx @@ -9,15 +9,25 @@ import { } from "../providers/transactions"; import { fetchDetails } from "providers/transactions/details"; import { useCluster, useClusterModal } from "providers/cluster"; -import { TransactionSignature, SystemInstruction } from "@solana/web3.js"; +import { + TransactionSignature, + SystemInstruction, + SystemProgram +} from "@solana/web3.js"; import ClusterStatusButton from "components/ClusterStatusButton"; import { lamportsToSolString } from "utils"; import { displayAddress } from "utils/tx"; import Copyable from "./Copyable"; import { useHistory, useLocation } from "react-router-dom"; import { TransferDetailsCard } from "./instruction/TransferDetailsCard"; +import { AssignDetailsCard } from "./instruction/AssignDetailsCard"; import { CreateDetailsCard } from "./instruction/CreateDetailsCard"; -import { RawDetailsCard } from "./instruction/RawDetailsCard"; +import { CreateWithSeedDetailsCard } from "./instruction/CreateWithSeedDetailsCard"; +import { UnknownDetailsCard } from "./instruction/UnknownDetailsCard"; +import { NonceInitializeDetailsCard } from "./instruction/NonceInitializeDetailsCard"; +import { NonceAdvanceDetailsCard } from "./instruction/NonceAdvanceDetailsCard"; +import { NonceWithdrawDetailsCard } from "./instruction/NonceWithdrawDetailsCard"; +import { NonceAuthorizeDetailsCard } from "./instruction/NonceAuthorizeDetailsCard"; type Props = { signature: TransactionSignature }; export default function TransactionDetails({ signature }: Props) { @@ -288,21 +298,37 @@ function InstructionsSection({ signature }: Props) { const instructionDetails = transaction.instructions.map((ix, index) => { const props = { ix, result, index }; - let instructionType; - try { - instructionType = SystemInstruction.decodeInstructionType(ix); - } catch (err) { - console.error(err); - return ; + if (!ix.programId.equals(SystemProgram.programId)) { + return ; } - switch (instructionType) { - case "Transfer": - return ; + let systemInstructionType; + try { + systemInstructionType = SystemInstruction.decodeInstructionType(ix); + } catch (err) { + console.error(err); + return ; + } + + switch (systemInstructionType) { case "Create": return ; + case "Assign": + return ; + case "Transfer": + return ; + case "CreateWithSeed": + return ; + case "AdvanceNonceAccount": + return ; + case "WithdrawNonceAccount": + return ; + case "AuthorizeNonceAccount": + return ; + case "InitializeNonceAccount": + return ; default: - return ; + return ; } }); diff --git a/explorer/src/components/instruction/AssignDetailsCard.tsx b/explorer/src/components/instruction/AssignDetailsCard.tsx new file mode 100644 index 0000000000..7c9a05d480 --- /dev/null +++ b/explorer/src/components/instruction/AssignDetailsCard.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { displayAddress } from "utils/tx"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function AssignDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeAssign(ix); + } catch (err) { + console.error(err); + return ; + } + + const from = params.fromPubkey.toBase58(); + const [fromMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
From Address
+ {!fromMeta.isWritable && ( + Readonly + )} + {fromMeta.isSigner && ( + Signer + )} + + + + {from} + + + + + + Assigned Owner + + + {displayAddress(params.programId)} + + + +
+ ); +} diff --git a/explorer/src/components/instruction/CreateDetailsCard.tsx b/explorer/src/components/instruction/CreateDetailsCard.tsx index 21dc57c7f3..862fa44098 100644 --- a/explorer/src/components/instruction/CreateDetailsCard.tsx +++ b/explorer/src/components/instruction/CreateDetailsCard.tsx @@ -9,7 +9,7 @@ import { lamportsToSolString } from "utils"; import { displayAddress } from "utils/tx"; import { InstructionCard } from "./InstructionCard"; import Copyable from "components/Copyable"; -import { RawDetailsCard } from "./RawDetailsCard"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; export function CreateDetailsCard(props: { ix: TransactionInstruction; @@ -18,20 +18,25 @@ export function CreateDetailsCard(props: { }) { const { ix, index, result } = props; - let create; + let params; try { - create = SystemInstruction.decodeCreateAccount(ix); + params = SystemInstruction.decodeCreateAccount(ix); } catch (err) { console.error(err); - return ; + return ; } - const from = create.fromPubkey.toBase58(); - const newKey = create.newAccountPubkey.toBase58(); + const from = params.fromPubkey.toBase58(); + const newKey = params.newAccountPubkey.toBase58(); const [fromMeta, newMeta] = ix.keys; return ( - + Program @@ -77,19 +82,19 @@ export function CreateDetailsCard(props: { Transfer Amount (SOL) - {lamportsToSolString(create.lamports)} + {lamportsToSolString(params.lamports)} Allocated Space (Bytes) - {create.space} + {params.space} Assigned Owner - - {displayAddress(create.programId)} + + {displayAddress(params.programId)} diff --git a/explorer/src/components/instruction/CreateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/CreateWithSeedDetailsCard.tsx new file mode 100644 index 0000000000..fce3d90b22 --- /dev/null +++ b/explorer/src/components/instruction/CreateWithSeedDetailsCard.tsx @@ -0,0 +1,139 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { lamportsToSolString } from "utils"; +import { displayAddress } from "utils/tx"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function CreateWithSeedDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeCreateWithSeed(ix); + } catch (err) { + console.error(err); + return ; + } + + const from = params.fromPubkey.toBase58(); + const newKey = params.newAccountPubkey.toBase58(); + const baseKey = params.basePubkey.toBase58(); + const [fromMeta, newMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
From Address
+ {!fromMeta.isWritable && ( + Readonly + )} + {fromMeta.isSigner && ( + Signer + )} + + + + {from} + + + + + + +
New Address
+ {!newMeta.isWritable && ( + Readonly + )} + {newMeta.isSigner && ( + Signer + )} + + + + {newKey} + + + + + + +
New Address
+ {!newMeta.isWritable && ( + Readonly + )} + {newMeta.isSigner && ( + Signer + )} + + + + {newKey} + + + + + + Base Address + + + {baseKey} + + + + + + Seed + + + {params.seed} + + + + + + Transfer Amount (SOL) + {lamportsToSolString(params.lamports)} + + + + Allocated Space (Bytes) + {params.space} + + + + Assigned Owner + + + {displayAddress(params.programId)} + + + +
+ ); +} diff --git a/explorer/src/components/instruction/InstructionCard.tsx b/explorer/src/components/instruction/InstructionCard.tsx index 3db9ef6e15..5e2ddfbc90 100644 --- a/explorer/src/components/instruction/InstructionCard.tsx +++ b/explorer/src/components/instruction/InstructionCard.tsx @@ -1,20 +1,27 @@ import React from "react"; -import { SignatureResult } from "@solana/web3.js"; +import { TransactionInstruction, SignatureResult } from "@solana/web3.js"; +import { RawDetails } from "./RawDetails"; type InstructionProps = { title: string; - children: React.ReactNode; + children?: React.ReactNode; result: SignatureResult; index: number; + ix: TransactionInstruction; + defaultRaw?: boolean; }; export function InstructionCard({ title, children, result, - index + index, + ix, + defaultRaw }: InstructionProps) { - const [resultClass, errorString] = ixResult(result, index); + const [resultClass] = ixResult(result, index); + const [showRaw, setShowRaw] = React.useState(defaultRaw || false); + return (
@@ -24,15 +31,22 @@ export function InstructionCard({ {title} -

- - {errorString} - -

+ +
- {children} + + {showRaw ? : children} +
diff --git a/explorer/src/components/instruction/NonceAdvanceDetailsCard.tsx b/explorer/src/components/instruction/NonceAdvanceDetailsCard.tsx new file mode 100644 index 0000000000..3d7404eb37 --- /dev/null +++ b/explorer/src/components/instruction/NonceAdvanceDetailsCard.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { displayAddress } from "utils/tx"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function NonceAdvanceDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeNonceAdvance(ix); + } catch (err) { + console.error(err); + return ; + } + + const nonceKey = params.noncePubkey.toBase58(); + const authorizedKey = params.authorizedPubkey.toBase58(); + const [nonceMeta, , authorizedMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
Nonce Address
+ {!nonceMeta.isWritable && ( + Readonly + )} + {nonceMeta.isSigner && ( + Signer + )} + + + + {nonceKey} + + + + + + +
Authorized Address
+ {!authorizedMeta.isWritable && ( + Readonly + )} + {authorizedMeta.isSigner && ( + Signer + )} + + + + {authorizedKey} + + + +
+ ); +} diff --git a/explorer/src/components/instruction/NonceAuthorizeDetailsCard.tsx b/explorer/src/components/instruction/NonceAuthorizeDetailsCard.tsx new file mode 100644 index 0000000000..bc3ff9c2c5 --- /dev/null +++ b/explorer/src/components/instruction/NonceAuthorizeDetailsCard.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { displayAddress } from "utils/tx"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function NonceAuthorizeDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeNonceAuthorize(ix); + } catch (err) { + console.error(err); + return ; + } + + const nonceKey = params.noncePubkey.toBase58(); + const authorizedKey = params.authorizedPubkey.toBase58(); + const newAuthorizedKey = params.newAuthorizedPubkey.toBase58(); + const [nonceMeta, authorizedMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
Nonce Address
+ {!nonceMeta.isWritable && ( + Readonly + )} + {nonceMeta.isSigner && ( + Signer + )} + + + + {nonceKey} + + + + + + +
Authorized Address
+ {!authorizedMeta.isWritable && ( + Readonly + )} + {authorizedMeta.isSigner && ( + Signer + )} + + + + {authorizedKey} + + + + + + New Authorized Address + + + {newAuthorizedKey} + + + +
+ ); +} diff --git a/explorer/src/components/instruction/NonceInitializeDetailsCard.tsx b/explorer/src/components/instruction/NonceInitializeDetailsCard.tsx new file mode 100644 index 0000000000..d11b002cde --- /dev/null +++ b/explorer/src/components/instruction/NonceInitializeDetailsCard.tsx @@ -0,0 +1,75 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { displayAddress } from "utils/tx"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function NonceInitializeDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeNonceInitialize(ix); + } catch (err) { + console.error(err); + return ; + } + + const nonceKey = params.noncePubkey.toBase58(); + const authorizedKey = params.authorizedPubkey.toBase58(); + const [nonceMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
Nonce Address
+ {!nonceMeta.isWritable && ( + Readonly + )} + {nonceMeta.isSigner && ( + Signer + )} + + + + {nonceKey} + + + + + + Authorized Address + + + {authorizedKey} + + + +
+ ); +} diff --git a/explorer/src/components/instruction/NonceWithdrawDetailsCard.tsx b/explorer/src/components/instruction/NonceWithdrawDetailsCard.tsx new file mode 100644 index 0000000000..4d4ac96836 --- /dev/null +++ b/explorer/src/components/instruction/NonceWithdrawDetailsCard.tsx @@ -0,0 +1,108 @@ +import React from "react"; +import { + TransactionInstruction, + SystemProgram, + SignatureResult, + SystemInstruction +} from "@solana/web3.js"; +import { displayAddress } from "utils/tx"; +import { lamportsToSolString } from "utils"; +import { InstructionCard } from "./InstructionCard"; +import Copyable from "components/Copyable"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; + +export function NonceWithdrawDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + const { ix, index, result } = props; + + let params; + try { + params = SystemInstruction.decodeNonceWithdraw(ix); + } catch (err) { + console.error(err); + return ; + } + + const nonceKey = params.noncePubkey.toBase58(); + const toKey = params.toPubkey.toBase58(); + const authorizedKey = params.authorizedPubkey.toBase58(); + const lamports = params.lamports; + const [nonceMeta, toMeta, , , authorizedMeta] = ix.keys; + + return ( + + + Program + + + {displayAddress(SystemProgram.programId)} + + + + + + +
Nonce Address
+ {!nonceMeta.isWritable && ( + Readonly + )} + {nonceMeta.isSigner && ( + Signer + )} + + + + {nonceKey} + + + + + + +
Authorized Address
+ {!authorizedMeta.isWritable && ( + Readonly + )} + {authorizedMeta.isSigner && ( + Signer + )} + + + + {authorizedKey} + + + + + + +
To Address
+ {!toMeta.isWritable && ( + Readonly + )} + {toMeta.isSigner && ( + Signer + )} + + + + {toKey} + + + + + + Withdraw Amount (SOL) + {lamportsToSolString(lamports)} + +
+ ); +} diff --git a/explorer/src/components/instruction/RawDetailsCard.tsx b/explorer/src/components/instruction/RawDetails.tsx similarity index 72% rename from explorer/src/components/instruction/RawDetailsCard.tsx rename to explorer/src/components/instruction/RawDetails.tsx index f5c13de623..e4a86ff81a 100644 --- a/explorer/src/components/instruction/RawDetailsCard.tsx +++ b/explorer/src/components/instruction/RawDetails.tsx @@ -1,21 +1,20 @@ import React from "react"; import bs58 from "bs58"; -import { TransactionInstruction, SignatureResult } from "@solana/web3.js"; +import { TransactionInstruction } from "@solana/web3.js"; import { displayAddress } from "utils/tx"; -import { InstructionCard } from "./InstructionCard"; import Copyable from "components/Copyable"; -export function RawDetailsCard({ - ix, - index, - result -}: { - ix: TransactionInstruction; - index: number; - result: SignatureResult; -}) { +function displayData(data: string) { + if (data.length > 50) { + return `${data.substring(0, 49)}…`; + } + return data; +} + +export function RawDetails({ ix }: { ix: TransactionInstruction }) { + const data = bs58.encode(ix.data); return ( - + <> Program @@ -47,11 +46,11 @@ export function RawDetailsCard({ Raw Data (Base58) - - {bs58.encode(ix.data)} + + {displayData(data)} - + ); } diff --git a/explorer/src/components/instruction/TransferDetailsCard.tsx b/explorer/src/components/instruction/TransferDetailsCard.tsx index 160e06d098..1bd156dace 100644 --- a/explorer/src/components/instruction/TransferDetailsCard.tsx +++ b/explorer/src/components/instruction/TransferDetailsCard.tsx @@ -9,7 +9,7 @@ import { lamportsToSolString } from "utils"; import { displayAddress } from "utils/tx"; import { InstructionCard } from "./InstructionCard"; import Copyable from "components/Copyable"; -import { RawDetailsCard } from "./RawDetailsCard"; +import { UnknownDetailsCard } from "./UnknownDetailsCard"; export function TransferDetailsCard(props: { ix: TransactionInstruction; @@ -23,14 +23,14 @@ export function TransferDetailsCard(props: { transfer = SystemInstruction.decodeTransfer(ix); } catch (err) { console.error(err); - return ; + return ; } const from = transfer.fromPubkey.toBase58(); const to = transfer.toPubkey.toBase58(); const [fromMeta, toMeta] = ix.keys; return ( - + Program diff --git a/explorer/src/components/instruction/UnknownDetailsCard.tsx b/explorer/src/components/instruction/UnknownDetailsCard.tsx new file mode 100644 index 0000000000..33a3cacaf6 --- /dev/null +++ b/explorer/src/components/instruction/UnknownDetailsCard.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { TransactionInstruction, SignatureResult } from "@solana/web3.js"; +import { InstructionCard } from "./InstructionCard"; + +export function UnknownDetailsCard({ + ix, + index, + result +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; +}) { + return ( + + ); +}