diff --git a/explorer/src/components/ProgramLogsCardBody.tsx b/explorer/src/components/ProgramLogsCardBody.tsx new file mode 100644 index 000000000..fd4e0de91 --- /dev/null +++ b/explorer/src/components/ProgramLogsCardBody.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { Message, ParsedMessage } from "@solana/web3.js"; +import { Cluster } from "providers/cluster"; +import { TableCardBody } from "components/common/TableCardBody"; +import { programLabel } from "utils/tx"; +import { InstructionLogs } from "utils/program-logs"; + +export function ProgramLogsCardBody({ + message, + logs, + cluster, +}: { + message: Message | ParsedMessage; + logs: InstructionLogs[]; + cluster: Cluster; +}) { + return ( + + {message.instructions.map((ix, index) => { + let programId; + if ("programIdIndex" in ix) { + const programAccount = message.accountKeys[ix.programIdIndex]; + if ("pubkey" in programAccount) { + programId = programAccount.pubkey; + } else { + programId = programAccount; + } + } else { + programId = ix.programId; + } + + const programName = + programLabel(programId.toBase58(), cluster) || "Unknown Program"; + const programLogs: InstructionLogs | undefined = logs[index]; + + let badgeColor = "white"; + if (programLogs) { + badgeColor = programLogs.failed ? "warning" : "success"; + } + + return ( + + +
+ + #{index + 1} + + {programName} Instruction +
+ {programLogs && ( +
+ {programLogs.logs.map((log, key) => { + return ( + + {log.prefix} + {log.text} + + ); + })} +
+ )} + + + ); + })} +
+ ); +} diff --git a/explorer/src/components/instruction/MangoDetails.tsx b/explorer/src/components/instruction/MangoDetails.tsx index 0e0c76a4d..0d6b7c9b7 100644 --- a/explorer/src/components/instruction/MangoDetails.tsx +++ b/explorer/src/components/instruction/MangoDetails.tsx @@ -152,7 +152,7 @@ export function MangoDetailsCard(props: { ix={ix} index={index} result={result} - title={`Mango: ${title || "Unknown"}`} + title={`Mango Program: ${title || "Unknown"}`} innerCards={innerCards} childIndex={childIndex} defaultRaw diff --git a/explorer/src/components/instruction/MemoDetailsCard.tsx b/explorer/src/components/instruction/MemoDetailsCard.tsx index 4be241380..085820a38 100644 --- a/explorer/src/components/instruction/MemoDetailsCard.tsx +++ b/explorer/src/components/instruction/MemoDetailsCard.tsx @@ -23,7 +23,7 @@ export function MemoDetailsCard({ ix={ix} index={index} result={result} - title="Memo" + title="Memo Program: Memo" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/SerumDetailsCard.tsx b/explorer/src/components/instruction/SerumDetailsCard.tsx index bc112c8bb..7f4964316 100644 --- a/explorer/src/components/instruction/SerumDetailsCard.tsx +++ b/explorer/src/components/instruction/SerumDetailsCard.tsx @@ -92,7 +92,7 @@ export function SerumDetailsCard(props: { ix={ix} index={index} result={result} - title={`Serum: ${title || "Unknown"}`} + title={`Serum Program: ${title || "Unknown"}`} innerCards={innerCards} childIndex={childIndex} defaultRaw diff --git a/explorer/src/components/instruction/UnknownDetailsCard.tsx b/explorer/src/components/instruction/UnknownDetailsCard.tsx index 1e37f47de..c86e59e66 100644 --- a/explorer/src/components/instruction/UnknownDetailsCard.tsx +++ b/explorer/src/components/instruction/UnknownDetailsCard.tsx @@ -5,6 +5,8 @@ import { ParsedInstruction, } from "@solana/web3.js"; import { InstructionCard } from "./InstructionCard"; +import { programLabel } from "utils/tx"; +import { useCluster } from "providers/cluster"; export function UnknownDetailsCard({ ix, @@ -19,12 +21,15 @@ export function UnknownDetailsCard({ innerCards?: JSX.Element[]; childIndex?: number; }) { + const { cluster } = useCluster(); + const programName = + programLabel(ix.programId.toBase58(), cluster) || "Unknown Program"; return ( diff --git a/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx b/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx index f3d90ab0a..83a4c08a8 100644 --- a/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx @@ -15,7 +15,7 @@ export function AddOracleDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: AddOracle" + title="Mango Program: AddOracle" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx b/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx index 2aebf7de0..753e1a394 100644 --- a/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx @@ -18,7 +18,7 @@ export function AddPerpMarketDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: AddPerpMarket" + title="Mango Program: AddPerpMarket" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx b/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx index 7bc7f6f2a..cc38ccca8 100644 --- a/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx @@ -17,7 +17,7 @@ export function AddSpotMarketDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: AddSpotMarket" + title="Mango Program: AddSpotMarket" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx index b1d10c55e..5b7877f9d 100644 --- a/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx @@ -24,7 +24,7 @@ export function CancelPerpOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: CancelPerpOrder" + title="Mango Program: CancelPerpOrder" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx index 8688b77c8..a09b92134 100644 --- a/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx @@ -24,7 +24,7 @@ export function CancelSpotOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: CancelSpotOrder" + title="Mango Program: CancelSpotOrder" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx b/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx index 6ea4e7d86..98f991e19 100644 --- a/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx @@ -52,7 +52,7 @@ export function ChangePerpMarketParamsDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: ChangePerpMarketParams" + title="Mango Program: ChangePerpMarketParams" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx b/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx index a97c2ba8c..d74a6ed85 100644 --- a/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx @@ -23,7 +23,7 @@ export function ConsumeEventsDetailsCard(props: { ix={ix} index={index} result={result} - title={"Mango: ConsumeEvents"} + title={"Mango Program: ConsumeEvents"} innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx index f291b72bf..c5382768b 100644 --- a/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx @@ -27,7 +27,7 @@ export function GenericMngoAccountDetailsCard(props: { ix={ix} index={index} result={result} - title={"Mango: " + title} + title={"Mango Program: " + title} innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx index 5d4dc6eda..e42817d01 100644 --- a/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx @@ -35,7 +35,7 @@ export function GenericPerpMngoDetailsCard(props: { ix={ix} index={index} result={result} - title={"Mango: " + title} + title={"Mango Program: " + title} innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx index df33a9b38..5a65caf21 100644 --- a/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx @@ -35,7 +35,7 @@ export function GenericSpotMngoDetailsCard(props: { ix={ix} index={index} result={result} - title={"Mango: " + title} + title={"Mango Program: " + title} innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx index ece6ceb66..b52d993b2 100644 --- a/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx @@ -58,7 +58,7 @@ export function PlacePerpOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: PlacePerpOrder" + title="Mango Program: PlacePerpOrder" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx index aeac88645..2d5b52f4a 100644 --- a/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx +++ b/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx @@ -68,7 +68,7 @@ export function PlaceSpotOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Mango: PlaceSpotOrder" + title="Mango Program: PlaceSpotOrder" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/CancelOrderByClientIdDetails.tsx b/explorer/src/components/instruction/serum/CancelOrderByClientIdDetails.tsx index 1bdce23f2..74cb066eb 100644 --- a/explorer/src/components/instruction/serum/CancelOrderByClientIdDetails.tsx +++ b/explorer/src/components/instruction/serum/CancelOrderByClientIdDetails.tsx @@ -19,7 +19,7 @@ export function CancelOrderByClientIdDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Cancel Order By Client Id" + title="Serum Program: Cancel Order By Client Id" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/CancelOrderDetails.tsx b/explorer/src/components/instruction/serum/CancelOrderDetails.tsx index 64dcaf10d..40d176a85 100644 --- a/explorer/src/components/instruction/serum/CancelOrderDetails.tsx +++ b/explorer/src/components/instruction/serum/CancelOrderDetails.tsx @@ -19,7 +19,7 @@ export function CancelOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Cancel Order" + title="Serum Program: Cancel Order" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/ConsumeEventsDetails.tsx b/explorer/src/components/instruction/serum/ConsumeEventsDetails.tsx index 4a278b0b8..705ab4c05 100644 --- a/explorer/src/components/instruction/serum/ConsumeEventsDetails.tsx +++ b/explorer/src/components/instruction/serum/ConsumeEventsDetails.tsx @@ -19,7 +19,7 @@ export function ConsumeEventsDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Consume Events" + title="Serum Program: Consume Events" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/InitializeMarketDetailsCard.tsx b/explorer/src/components/instruction/serum/InitializeMarketDetailsCard.tsx index 6c9c27782..50d28aa4e 100644 --- a/explorer/src/components/instruction/serum/InitializeMarketDetailsCard.tsx +++ b/explorer/src/components/instruction/serum/InitializeMarketDetailsCard.tsx @@ -19,7 +19,7 @@ export function InitializeMarketDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Initialize Market" + title="Serum Program: Initialize Market" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/MatchOrdersDetailsCard.tsx b/explorer/src/components/instruction/serum/MatchOrdersDetailsCard.tsx index 5457ccb1d..248442b65 100644 --- a/explorer/src/components/instruction/serum/MatchOrdersDetailsCard.tsx +++ b/explorer/src/components/instruction/serum/MatchOrdersDetailsCard.tsx @@ -19,7 +19,7 @@ export function MatchOrdersDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Match Orders" + title="Serum Program: Match Orders" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/NewOrderDetailsCard.tsx b/explorer/src/components/instruction/serum/NewOrderDetailsCard.tsx index 99bbe58a7..f5850d0ee 100644 --- a/explorer/src/components/instruction/serum/NewOrderDetailsCard.tsx +++ b/explorer/src/components/instruction/serum/NewOrderDetailsCard.tsx @@ -19,7 +19,7 @@ export function NewOrderDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: New Order" + title="Serum Program: New Order" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/serum/SettleFundsDetailsCard.tsx b/explorer/src/components/instruction/serum/SettleFundsDetailsCard.tsx index 3cc3aea09..cb8d812d8 100644 --- a/explorer/src/components/instruction/serum/SettleFundsDetailsCard.tsx +++ b/explorer/src/components/instruction/serum/SettleFundsDetailsCard.tsx @@ -19,7 +19,7 @@ export function SettleFundsDetailsCard(props: { ix={ix} index={index} result={result} - title="Serum: Settle Funds" + title="Serum Program: Settle Funds" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx index b390bb785..23b80de9b 100644 --- a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx @@ -23,7 +23,7 @@ export function AuthorizeDetailsCard(props: { ix={ix} index={index} result={result} - title="Stake Authorize" + title="Stake Program: Authorize" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx index 7577d4c03..d51e38957 100644 --- a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx @@ -23,7 +23,7 @@ export function DeactivateDetailsCard(props: { ix={ix} index={index} result={result} - title="Deactivate Stake" + title="Stake Program: Deactivate Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx index 502ea8d77..50daf63fb 100644 --- a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx @@ -23,7 +23,7 @@ export function DelegateDetailsCard(props: { ix={ix} index={index} result={result} - title="Delegate Stake" + title="Stake Program: Delegate Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx index da2b87dfa..316153e32 100644 --- a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx @@ -26,7 +26,7 @@ export function InitializeDetailsCard(props: { ix={ix} index={index} result={result} - title="Stake Initialize" + title="Stake Program: Initialize Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/MergeDetailsCard.tsx b/explorer/src/components/instruction/stake/MergeDetailsCard.tsx index ad7b0b8b1..2a4d3d246 100644 --- a/explorer/src/components/instruction/stake/MergeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/MergeDetailsCard.tsx @@ -23,7 +23,7 @@ export function MergeDetailsCard(props: { ix={ix} index={index} result={result} - title="Stake Merge" + title="Stake Program: Merge Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx index 2f6b50957..f99ce3064 100644 --- a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx @@ -24,7 +24,7 @@ export function SplitDetailsCard(props: { ix={ix} index={index} result={result} - title="Split Stake" + title="Stake Program: Split Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx index 58becc85d..a537b8374 100644 --- a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx @@ -24,7 +24,7 @@ export function WithdrawDetailsCard(props: { ix={ix} index={index} result={result} - title="Withdraw Stake" + title="System Program: Withdraw Stake" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx index 377aec1ba..a20b8a135 100644 --- a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx @@ -23,7 +23,7 @@ export function AllocateDetailsCard(props: { ix={ix} index={index} result={result} - title="Allocate Account" + title="System Program: Allocate Account" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx index 7d82671e3..dcecebf2e 100644 --- a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx @@ -24,7 +24,7 @@ export function AllocateWithSeedDetailsCard(props: { ix={ix} index={index} result={result} - title="Allocate Account w/ Seed" + title="System Program: Allocate Account w/ Seed" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/AssignDetailsCard.tsx b/explorer/src/components/instruction/system/AssignDetailsCard.tsx index 92a73ce59..75df5d3e1 100644 --- a/explorer/src/components/instruction/system/AssignDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignDetailsCard.tsx @@ -23,7 +23,7 @@ export function AssignDetailsCard(props: { ix={ix} index={index} result={result} - title="Assign Account" + title="System Program: Assign Account" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx index 1339c0b15..51a6c2fbb 100644 --- a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx @@ -24,7 +24,7 @@ export function AssignWithSeedDetailsCard(props: { ix={ix} index={index} result={result} - title="Assign Account w/ Seed" + title="System Program: Assign Account w/ Seed" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/CreateDetailsCard.tsx b/explorer/src/components/instruction/system/CreateDetailsCard.tsx index 71d8459ad..a0fd1b871 100644 --- a/explorer/src/components/instruction/system/CreateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateDetailsCard.tsx @@ -24,7 +24,7 @@ export function CreateDetailsCard(props: { ix={ix} index={index} result={result} - title="Create Account" + title="System Program: Create Account" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx index 0ef9a6168..9ed25fd68 100644 --- a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx @@ -25,7 +25,7 @@ export function CreateWithSeedDetailsCard(props: { ix={ix} index={index} result={result} - title="Create Account w/ Seed" + title="System Program: Create Account w/ Seed" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx index 833fec39a..c2b1d790f 100644 --- a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx @@ -23,7 +23,7 @@ export function NonceAdvanceDetailsCard(props: { ix={ix} index={index} result={result} - title="Advance Nonce" + title="System Program: Advance Nonce" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx index 39b83b5ca..d5fe01b52 100644 --- a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx @@ -23,7 +23,7 @@ export function NonceAuthorizeDetailsCard(props: { ix={ix} index={index} result={result} - title="Authorize Nonce" + title="System Program: Authorize Nonce" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx index c421b604d..03de00efe 100644 --- a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx @@ -23,7 +23,7 @@ export function NonceInitializeDetailsCard(props: { ix={ix} index={index} result={result} - title="Initialize Nonce" + title="System Program: Initialize Nonce" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx index a5638b7ed..616f3b714 100644 --- a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx @@ -24,7 +24,7 @@ export function NonceWithdrawDetailsCard(props: { ix={ix} index={index} result={result} - title="Withdraw Nonce" + title="System Program: Withdraw Nonce" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/TransferDetailsCard.tsx b/explorer/src/components/instruction/system/TransferDetailsCard.tsx index ba1d2ef75..8b8696442 100644 --- a/explorer/src/components/instruction/system/TransferDetailsCard.tsx +++ b/explorer/src/components/instruction/system/TransferDetailsCard.tsx @@ -24,7 +24,7 @@ export function TransferDetailsCard(props: { ix={ix} index={index} result={result} - title="Transfer" + title="System Program: Transfer" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/system/TransferWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/TransferWithSeedDetailsCard.tsx index f6c88d206..51353bd63 100644 --- a/explorer/src/components/instruction/system/TransferWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/TransferWithSeedDetailsCard.tsx @@ -25,7 +25,7 @@ export function TransferWithSeedDetailsCard(props: { ix={ix} index={index} result={result} - title="Transfer w/ Seed" + title="System Program: Transfer w/ Seed" innerCards={innerCards} childIndex={childIndex} > diff --git a/explorer/src/components/instruction/token/TokenDetailsCard.tsx b/explorer/src/components/instruction/token/TokenDetailsCard.tsx index 2fba9491f..fd18df1cd 100644 --- a/explorer/src/components/instruction/token/TokenDetailsCard.tsx +++ b/explorer/src/components/instruction/token/TokenDetailsCard.tsx @@ -40,7 +40,7 @@ export function TokenDetailsCard(props: DetailsProps) { const parsed = create(props.ix.parsed, ParsedInfo); const { type: rawType, info } = parsed; const type = create(rawType, TokenInstructionType); - const title = `Token: ${IX_TITLES[type]}`; + const title = `Token Program: ${IX_TITLES[type]}`; const created = create(info, IX_STRUCTS[type] as any); return ; } catch (err) { diff --git a/explorer/src/components/transaction/ProgramLogSection.tsx b/explorer/src/components/transaction/ProgramLogSection.tsx index 29b9aeb06..1ded8fcbd 100644 --- a/explorer/src/components/transaction/ProgramLogSection.tsx +++ b/explorer/src/components/transaction/ProgramLogSection.tsx @@ -1,227 +1,33 @@ import React from "react"; import { SignatureProps } from "pages/TransactionDetailsPage"; import { useTransactionDetails } from "providers/transactions"; -import { TransactionError } from "@solana/web3.js"; - -const transactionErrorMessage: Map = new Map([ - ["AccountInUse", "Account in use"], - ["AccountLoadedTwice", "Account loaded twice"], - [ - "AccountNotFound", - "Attempt to debit an account but found no record of a prior credit.", - ], - ["ProgramAccountNotFound", "Attempt to load a program that does not exist"], - ["InsufficientFundsForFee", "Insufficient funds for fee"], - [ - "InvalidAccountForFee", - "This account may not be used to pay transaction fees", - ], - ["AlreadyProcessed", "This transaction has already been processed"], - ["BlockhashNotFound", "Blockhash not found"], - ["CallChainTooDeep", "Loader call chain is too deep"], - [ - "MissingSignatureForFee", - "Transaction requires a fee but has no signature present", - ], - ["InvalidAccountIndex", "Transaction contains an invalid account reference"], - ["SignatureFailure", "Transaction did not pass signature verification"], - [ - "InvalidProgramForExecution", - "This program may not be used for executing instructions", - ], - [ - "SanitizeFailure", - "Transaction failed to sanitize accounts offsets correctly", - ], - [ - "ClusterMaintenance", - "Transactions are currently disabled due to cluster maintenance", - ], - [ - "AccountBorrowOutstanding", - "Transaction processing left an account with an outstanding borrowed reference", - ], - ["InstructionError", "Error processing Instruction {0}: {1}"], -]); - -const instructionErrorMessage: Map = new Map([ - ["GenericError", "generic instruction error"], - ["InvalidArgument", "invalid program argument"], - ["InvalidInstructionData", "invalid instruction data"], - ["InvalidAccountData", "invalid account data for instruction"], - ["AccountDataTooSmall", "account data too small for instruction"], - ["InsufficientFunds", "insufficient funds for instruction"], - ["IncorrectProgramId", "incorrect program id for instruction"], - ["MissingRequiredSignature", "missing required signature for instruction"], - [ - "AccountAlreadyInitialized", - "instruction requires an uninitialized account", - ], - ["UninitializedAccount", "instruction requires an initialized account"], - [ - "UnbalancedInstruction", - "sum of account balances before and after instruction do not match", - ], - ["ModifiedProgramId", "instruction modified the program id of an account"], - [ - "ExternalAccountLamportSpend", - "instruction spent from the balance of an account it does not own", - ], - [ - "ExternalAccountDataModified", - "instruction modified data of an account it does not own", - ], - [ - "ReadonlyLamportChange", - "instruction changed the balance of a read-only account", - ], - ["ReadonlyDataModified", "instruction modified data of a read-only account"], - ["DuplicateAccountIndex", "instruction contains duplicate accounts"], - ["ExecutableModified", "instruction changed executable bit of an account"], - ["RentEpochModified", "instruction modified rent epoch of an account"], - ["NotEnoughAccountKeys", "insufficient account keys for instruction"], - ["AccountDataSizeChanged", "non-system instruction changed account size"], - ["AccountNotExecutable", "instruction expected an executable account"], - [ - "AccountBorrowFailed", - "instruction tries to borrow reference for an account which is already borrowed", - ], - [ - "AccountBorrowOutstanding", - "instruction left account with an outstanding borrowed reference", - ], - [ - "DuplicateAccountOutOfSync", - "instruction modifications of multiply-passed account differ", - ], - ["Custom", "custom program error: {0}"], - ["InvalidError", "program returned invalid error code"], - ["ExecutableDataModified", "instruction changed executable accounts data"], - [ - "ExecutableLamportChange", - "instruction changed the balance of a executable account", - ], - ["ExecutableAccountNotRentExempt", "executable accounts must be rent exempt"], - ["UnsupportedProgramId", "Unsupported program id"], - ["CallDepth", "Cross-program invocation call depth too deep"], - ["MissingAccount", "An account required by the instruction is missing"], - [ - "ReentrancyNotAllowed", - "Cross-program invocation reentrancy not allowed for this instruction", - ], - [ - "MaxSeedLengthExceeded", - "Length of the seed is too long for address generation", - ], - ["InvalidSeeds", "Provided seeds do not result in a valid address"], - ["InvalidRealloc", "Failed to reallocate account data"], - ["ComputationalBudgetExceeded", "Computational budget exceeded"], - [ - "PrivilegeEscalation", - "Cross-program invocation with unauthorized signer or writable account", - ], - [ - "ProgramEnvironmentSetupFailure", - "Failed to create program execution environment", - ], - ["ProgramFailedToComplete", "Program failed to complete"], - ["ProgramFailedToCompile", "Program failed to compile"], - ["Immutable", "Account is immutable"], - ["IncorrectAuthority", "Incorrect authority provided"], - ["BorshIoError", "Failed to serialize or deserialize account data: {0}"], - [ - "AccountNotRentExempt", - "An account does not have enough lamports to be rent-exempt", - ], - ["InvalidAccountOwner", "Invalid account owner"], - ["ArithmeticOverflow", "Program arithmetic overflowed"], - ["UnsupportedSysvar", "Unsupported sysvar"], - ["IllegalOwner", "Provided owner is not allowed"], -]); - -function getTransactionError( - error?: TransactionError | null -): string | undefined { - if (!error) { - return; - } - - if (typeof error === "string") { - const message = transactionErrorMessage.get(error); - if (message) { - return message; - } - } else if ("InstructionError" in error) { - const out = transactionErrorMessage.get("InstructionError"); - const innerError = error["InstructionError"]; - const index = innerError[0]; - const instructionError = innerError[1]; - - if (out) { - return out - .replace("{0}", index) - .replace("{1}", getInstructionError(instructionError)); - } - } - - return "Unknown transaction error"; -} - -function getInstructionError(error: TransactionError): string { - let out; - let value; - - if (typeof error === "string") { - const message = instructionErrorMessage.get(error); - if (message) { - return message; - } - } else if ("Custom" in error) { - out = instructionErrorMessage.get("Custom"); - value = error["Custom"][0]; - } else if ("BorshIoError" in error) { - out = instructionErrorMessage.get("BorshIoError"); - value = error["BorshIoError"][0]; - } - - if (out && value) { - return out.replace("{0}", value); - } - - return "Unknown instruction error"; -} +import { ProgramLogsCardBody } from "components/ProgramLogsCardBody"; +import { prettyProgramLogs } from "utils/program-logs"; +import { useCluster } from "providers/cluster"; export function ProgramLogSection({ signature }: SignatureProps) { + const { cluster } = useCluster(); const details = useTransactionDetails(signature); - const logMessages = details?.data?.transaction?.meta?.logMessages; - const transactionError = getTransactionError( - details?.data?.transaction?.meta?.err - ); + const transaction = details?.data?.transaction; + if (!transaction) return null; + const message = transaction.transaction.message; - if ((!logMessages || logMessages.length < 1) && !transactionError) { - return null; - } + const logMessages = transaction.meta?.logMessages || null; + const err = transaction.meta?.err || null; + const prettyLogs = prettyProgramLogs(logMessages, err, cluster); return ( <> -
-
-
-

Program Log

-
-
-
-
    - {logMessages && - logMessages.map((message, key) => ( -
  • {message.replace(/^Program log: /, "")}
  • - ))} - {transactionError && ( -
  • Transaction failed: {transactionError}
  • - )} -
+
+

Program Logs

+
+
); diff --git a/explorer/src/pages/inspector/SimulatorCard.tsx b/explorer/src/pages/inspector/SimulatorCard.tsx index 134632fd7..ddeb5666d 100644 --- a/explorer/src/pages/inspector/SimulatorCard.tsx +++ b/explorer/src/pages/inspector/SimulatorCard.tsx @@ -2,19 +2,8 @@ import React from "react"; import bs58 from "bs58"; import { Connection, Message, Transaction } from "@solana/web3.js"; import { useCluster } from "providers/cluster"; -import { TableCardBody } from "components/common/TableCardBody"; -import { programLabel } from "utils/tx"; - -type LogMessage = { - text: string; - prefix: string; - style: "muted" | "info" | "success" | "warning"; -}; - -type InstructionLogs = { - logs: LogMessage[]; - failed: boolean; -}; +import { InstructionLogs, prettyProgramLogs } from "utils/program-logs"; +import { ProgramLogsCardBody } from "components/ProgramLogsCardBody"; const DEFAULT_SIGNATURE = bs58.encode(Buffer.alloc(64).fill(0)); @@ -78,46 +67,7 @@ export function SimulatorCard({ message }: { message: Message }) { Retry - - {message.instructions.map((ix, index) => { - const programId = message.accountKeys[ix.programIdIndex]; - const programName = - programLabel(programId.toBase58(), cluster) || "Unknown"; - const programLogs: InstructionLogs | undefined = logs[index]; - - let badgeColor = "white"; - if (programLogs) { - badgeColor = programLogs.failed ? "warning" : "success"; - } - - return ( - - -
- - #{index + 1} - - {programName} Instruction -
- {programLogs && ( -
- {programLogs.logs.map((log, key) => { - return ( - - {log.prefix} - - {log.text} - - - ); - })} -
- )} - - - ); - })} -
+ ); } @@ -152,124 +102,8 @@ function useSimulator(message: Message) { // Simulate without signers to skip signer verification const resp = await connection.simulateTransaction(tx); - let depth = 0; - let instructionLogs: InstructionLogs[] = []; - const prefixBuilder = (depth: number) => { - const prefix = new Array(depth - 1).fill("\u00A0\u00A0").join(""); - return prefix + "> "; - }; - - let instructionError; - const responseLogs = resp.value.logs; - const responseErr = resp.value.err; - if (!responseLogs) { - if (resp.value.err) throw new Error(JSON.stringify(responseErr)); - throw new Error("No logs detected"); - } else if (responseErr) { - if (typeof responseErr !== "string") { - let ixError = (responseErr as any)["InstructionError"]; - const [index, message] = ixError; - if (typeof message === "string") { - instructionError = { index, message }; - } - } else { - throw new Error(responseErr); - } - } - - responseLogs.forEach((log) => { - if (log.startsWith("Program log:")) { - instructionLogs[instructionLogs.length - 1].logs.push({ - prefix: prefixBuilder(depth), - text: log, - style: "muted", - }); - } else { - const regex = /Program (\w*) invoke \[(\d)\]/g; - const matches = [...log.matchAll(regex)]; - - if (matches.length > 0) { - const programAddress = matches[0][1]; - const programName = - programLabel(programAddress, cluster) || - `Unknown (${programAddress}) Program`; - - if (depth === 0) { - instructionLogs.push({ - logs: [], - failed: false, - }); - } else { - instructionLogs[instructionLogs.length - 1].logs.push({ - prefix: prefixBuilder(depth), - style: "info", - text: `Invoking ${programName}`, - }); - } - - depth++; - } else if (log.includes("success")) { - instructionLogs[instructionLogs.length - 1].logs.push({ - prefix: prefixBuilder(depth), - style: "success", - text: `Program returned success`, - }); - depth--; - } else if (log.includes("failed")) { - const instructionLog = - instructionLogs[instructionLogs.length - 1]; - if (!instructionLog.failed) { - instructionLog.failed = true; - instructionLog.logs.push({ - prefix: prefixBuilder(depth), - style: "warning", - text: `Program returned error: ${log.slice( - log.indexOf(": ") + 2 - )}`, - }); - } - depth--; - } else { - if (depth === 0) { - instructionLogs.push({ - logs: [], - failed: false, - }); - depth++; - } - // system transactions don't start with "Program log:" - instructionLogs[instructionLogs.length - 1].logs.push({ - prefix: prefixBuilder(depth), - text: log, - style: "muted", - }); - } - } - }); - - // If the instruction's simulation returned an error without any logs then add an empty log entry for Runtime error - // For example BpfUpgradableLoader fails without returning any logs for Upgrade instruction with buffer that doesn't exist - if (instructionError && instructionLogs.length === 0) { - instructionLogs.push({ - logs: [], - failed: true, - }); - } - - if ( - instructionError && - instructionError.index === instructionLogs.length - 1 - ) { - const failedIx = instructionLogs[instructionError.index]; - failedIx.failed = true; - failedIx.logs.push({ - prefix: prefixBuilder(1), - text: `Runtime error: ${instructionError.message}`, - style: "warning", - }); - } - - setLogs(instructionLogs); + // Prettify logs + setLogs(prettyProgramLogs(resp.value.logs, resp.value.err, cluster)); } catch (err) { console.error(err); setLogs(null); diff --git a/explorer/src/utils/program-err.ts b/explorer/src/utils/program-err.ts new file mode 100644 index 000000000..0bef22c7f --- /dev/null +++ b/explorer/src/utils/program-err.ts @@ -0,0 +1,144 @@ +import { TransactionError } from "@solana/web3.js"; + +const instructionErrorMessage: Map = new Map([ + ["GenericError", "generic instruction error"], + ["InvalidArgument", "invalid program argument"], + ["InvalidInstructionData", "invalid instruction data"], + ["InvalidAccountData", "invalid account data for instruction"], + ["AccountDataTooSmall", "account data too small for instruction"], + ["InsufficientFunds", "insufficient funds for instruction"], + ["IncorrectProgramId", "incorrect program id for instruction"], + ["MissingRequiredSignature", "missing required signature for instruction"], + [ + "AccountAlreadyInitialized", + "instruction requires an uninitialized account", + ], + ["UninitializedAccount", "instruction requires an initialized account"], + [ + "UnbalancedInstruction", + "sum of account balances before and after instruction do not match", + ], + ["ModifiedProgramId", "instruction modified the program id of an account"], + [ + "ExternalAccountLamportSpend", + "instruction spent from the balance of an account it does not own", + ], + [ + "ExternalAccountDataModified", + "instruction modified data of an account it does not own", + ], + [ + "ReadonlyLamportChange", + "instruction changed the balance of a read-only account", + ], + ["ReadonlyDataModified", "instruction modified data of a read-only account"], + ["DuplicateAccountIndex", "instruction contains duplicate accounts"], + ["ExecutableModified", "instruction changed executable bit of an account"], + ["RentEpochModified", "instruction modified rent epoch of an account"], + ["NotEnoughAccountKeys", "insufficient account keys for instruction"], + ["AccountDataSizeChanged", "non-system instruction changed account size"], + ["AccountNotExecutable", "instruction expected an executable account"], + [ + "AccountBorrowFailed", + "instruction tries to borrow reference for an account which is already borrowed", + ], + [ + "AccountBorrowOutstanding", + "instruction left account with an outstanding borrowed reference", + ], + [ + "DuplicateAccountOutOfSync", + "instruction modifications of multiply-passed account differ", + ], + ["Custom", "custom program error: {0}"], + ["InvalidError", "program returned invalid error code"], + ["ExecutableDataModified", "instruction changed executable accounts data"], + [ + "ExecutableLamportChange", + "instruction changed the balance of a executable account", + ], + ["ExecutableAccountNotRentExempt", "executable accounts must be rent exempt"], + ["UnsupportedProgramId", "Unsupported program id"], + ["CallDepth", "Cross-program invocation call depth too deep"], + ["MissingAccount", "An account required by the instruction is missing"], + [ + "ReentrancyNotAllowed", + "Cross-program invocation reentrancy not allowed for this instruction", + ], + [ + "MaxSeedLengthExceeded", + "Length of the seed is too long for address generation", + ], + ["InvalidSeeds", "Provided seeds do not result in a valid address"], + ["InvalidRealloc", "Failed to reallocate account data"], + ["ComputationalBudgetExceeded", "Computational budget exceeded"], + [ + "PrivilegeEscalation", + "Cross-program invocation with unauthorized signer or writable account", + ], + [ + "ProgramEnvironmentSetupFailure", + "Failed to create program execution environment", + ], + ["ProgramFailedToComplete", "Program failed to complete"], + ["ProgramFailedToCompile", "Program failed to compile"], + ["Immutable", "Account is immutable"], + ["IncorrectAuthority", "Incorrect authority provided"], + ["BorshIoError", "Failed to serialize or deserialize account data: {0}"], + [ + "AccountNotRentExempt", + "An account does not have enough lamports to be rent-exempt", + ], + ["InvalidAccountOwner", "Invalid account owner"], + ["ArithmeticOverflow", "Program arithmetic overflowed"], + ["UnsupportedSysvar", "Unsupported sysvar"], + ["IllegalOwner", "Provided owner is not allowed"], +]); + +export type ProgramError = { + index: number; + message: string; +}; + +export function getTransactionInstructionError( + error?: TransactionError | null +): ProgramError | undefined { + if (!error) { + return; + } + + if (typeof error === "object" && "InstructionError" in error) { + const innerError = error["InstructionError"]; + const index = innerError[0] as number; + const instructionError = innerError[1]; + + return { + index, + message: getInstructionError(instructionError), + }; + } +} + +function getInstructionError(error: any): string { + let out; + let value; + + if (typeof error === "string") { + const message = instructionErrorMessage.get(error); + if (message) { + return message; + } + } else if ("Custom" in error) { + out = instructionErrorMessage.get("Custom"); + value = error["Custom"]; + } else if ("BorshIoError" in error) { + out = instructionErrorMessage.get("BorshIoError"); + value = error["BorshIoError"]; + } + + if (out && value) { + return out.replace("{0}", value); + } + + return "Unknown instruction error"; +} diff --git a/explorer/src/utils/program-logs.ts b/explorer/src/utils/program-logs.ts new file mode 100644 index 000000000..fb5935b9e --- /dev/null +++ b/explorer/src/utils/program-logs.ts @@ -0,0 +1,124 @@ +import { TransactionError } from "@solana/web3.js"; +import { Cluster } from "providers/cluster"; +import { programLabel } from "utils/tx"; +import { getTransactionInstructionError } from "utils/program-err"; + +export type LogMessage = { + text: string; + prefix: string; + style: "muted" | "info" | "success" | "warning"; +}; + +export type InstructionLogs = { + logs: LogMessage[]; + failed: boolean; +}; + +export function prettyProgramLogs( + logs: string[] | null, + error: TransactionError | null, + cluster: Cluster +): InstructionLogs[] { + let depth = 0; + let prettyLogs: InstructionLogs[] = []; + const prefixBuilder = (depth: number) => { + const prefix = new Array(depth - 1).fill("\u00A0\u00A0").join(""); + return prefix + "> "; + }; + + let prettyError; + if (!logs) { + if (error) throw new Error(JSON.stringify(error)); + throw new Error("No logs detected"); + } else if (error) { + prettyError = getTransactionInstructionError(error); + } + + logs.forEach((log) => { + if (log.startsWith("Program log:")) { + prettyLogs[prettyLogs.length - 1].logs.push({ + prefix: prefixBuilder(depth), + text: log, + style: "muted", + }); + } else { + const regex = /Program (\w*) invoke \[(\d)\]/g; + const matches = [...log.matchAll(regex)]; + + if (matches.length > 0) { + const programAddress = matches[0][1]; + const programName = + programLabel(programAddress, cluster) || + `Unknown (${programAddress}) Program`; + + if (depth === 0) { + prettyLogs.push({ + logs: [], + failed: false, + }); + } else { + prettyLogs[prettyLogs.length - 1].logs.push({ + prefix: prefixBuilder(depth), + style: "info", + text: `Invoking ${programName}`, + }); + } + + depth++; + } else if (log.includes("success")) { + prettyLogs[prettyLogs.length - 1].logs.push({ + prefix: prefixBuilder(depth), + style: "success", + text: `Program returned success`, + }); + depth--; + } else if (log.includes("failed")) { + const instructionLog = prettyLogs[prettyLogs.length - 1]; + if (!instructionLog.failed) { + instructionLog.failed = true; + instructionLog.logs.push({ + prefix: prefixBuilder(depth), + style: "warning", + text: `Program returned error: ${log.slice(log.indexOf(": ") + 2)}`, + }); + } + depth--; + } else { + if (depth === 0) { + prettyLogs.push({ + logs: [], + failed: false, + }); + depth++; + } + // system transactions don't start with "Program log:" + prettyLogs[prettyLogs.length - 1].logs.push({ + prefix: prefixBuilder(depth), + text: log, + style: "muted", + }); + } + } + }); + + // If the instruction's simulation returned an error without any logs then add an empty log entry for Runtime error + // For example BpfUpgradableLoader fails without returning any logs for Upgrade instruction with buffer that doesn't exist + if (prettyError && prettyLogs.length === 0) { + prettyLogs.push({ + logs: [], + failed: true, + }); + } + + if (prettyError && prettyError.index === prettyLogs.length - 1) { + const failedIx = prettyLogs[prettyError.index]; + failedIx.failed = true; + failedIx.logs.push({ + prefix: prefixBuilder(1), + text: `Runtime error: ${prettyError.message}`, + style: "warning", + }); + } + + return prettyLogs; +}