- Bytes (base 64)
+ Bytes (Base 64)
|
{bytes}
diff --git a/explorer/src/components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard.tsx b/explorer/src/components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard.tsx
new file mode 100644
index 0000000000..8d418c1dbd
--- /dev/null
+++ b/explorer/src/components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard.tsx
@@ -0,0 +1,112 @@
+import React from "react";
+import {
+ ParsedTransaction,
+ ParsedInstruction,
+ SignatureResult,
+ PublicKey,
+} from "@solana/web3.js";
+import { Address } from "components/common/Address";
+import { coerce, Struct } from "superstruct";
+import { camelToTitleCase } from "utils";
+import { reportError } from "utils/sentry";
+import { ParsedInfo } from "validators";
+import { InstructionCard } from "../InstructionCard";
+import { UnknownDetailsCard } from "../UnknownDetailsCard";
+import {
+ DeployWithMaxDataLenInfo,
+ InitializeBufferInfo,
+ SetAuthorityInfo,
+ UpgradeInfo,
+ WriteInfo,
+} from "./types";
+
+type DetailsProps = {
+ tx: ParsedTransaction;
+ ix: ParsedInstruction;
+ index: number;
+ result: SignatureResult;
+ innerCards?: JSX.Element[];
+ childIndex?: number;
+};
+
+export function UpgradeableBpfLoaderDetailsCard(props: DetailsProps) {
+ try {
+ const parsed = coerce(props.ix.parsed, ParsedInfo);
+ switch (parsed.type) {
+ case "write": {
+ return renderDetails(props, parsed, WriteInfo);
+ }
+ case "upgrade": {
+ return renderDetails(props, parsed, UpgradeInfo);
+ }
+ case "setAuthority": {
+ return renderDetails(props, parsed, SetAuthorityInfo);
+ }
+ case "deployWithMaxDataLen": {
+ return renderDetails(
+ props,
+ parsed,
+ DeployWithMaxDataLenInfo
+ );
+ }
+ case "initializeBuffer": {
+ return renderDetails(
+ props,
+ parsed,
+ InitializeBufferInfo
+ );
+ }
+ default:
+ return ;
+ }
+ } catch (error) {
+ reportError(error, {
+ signature: props.tx.signatures[0],
+ });
+ return ;
+ }
+}
+
+function renderDetails(
+ props: DetailsProps,
+ parsed: ParsedInfo,
+ struct: Struct
+) {
+ const info = coerce(parsed.info, struct);
+
+ const attributes: JSX.Element[] = [];
+ for (let [key, value] of Object.entries(info)) {
+ if (value instanceof PublicKey) {
+ value = ;
+ } else if (key === "bytes") {
+ value = (
+ {value}
+ );
+ }
+
+ attributes.push(
+
+
+ {camelToTitleCase(key)}{" "}
+ {key === "bytes" && (Base 64)}
+ |
+ {value} |
+
+ );
+ }
+
+ return (
+
+
+ Program |
+
+
+ |
+
+ {attributes}
+
+ );
+}
diff --git a/explorer/src/components/instruction/upgradeable-bpf-loader/types.ts b/explorer/src/components/instruction/upgradeable-bpf-loader/types.ts
new file mode 100644
index 0000000000..7f206dbc6f
--- /dev/null
+++ b/explorer/src/components/instruction/upgradeable-bpf-loader/types.ts
@@ -0,0 +1,62 @@
+/* eslint-disable @typescript-eslint/no-redeclare */
+
+import { enums, nullable, number, pick, string, StructType } from "superstruct";
+import { Pubkey } from "validators/pubkey";
+
+export type WriteInfo = StructType;
+export const WriteInfo = pick({
+ account: Pubkey,
+ authority: Pubkey,
+ bytes: string(),
+ offset: number(),
+});
+
+export type InitializeBufferInfo = StructType;
+export const InitializeBufferInfo = pick({
+ account: Pubkey,
+ authority: Pubkey,
+});
+
+export type UpgradeInfo = StructType;
+export const UpgradeInfo = pick({
+ programDataAccount: Pubkey,
+ programAccount: Pubkey,
+ bufferAccount: Pubkey,
+ spillAccount: Pubkey,
+ authority: Pubkey,
+ rentSysvar: Pubkey,
+ clockSysvar: Pubkey,
+});
+
+export type SetAuthorityInfo = StructType;
+export const SetAuthorityInfo = pick({
+ account: Pubkey,
+ authority: Pubkey,
+ newAuthority: nullable(Pubkey),
+});
+
+export type DeployWithMaxDataLenInfo = StructType<
+ typeof DeployWithMaxDataLenInfo
+>;
+export const DeployWithMaxDataLenInfo = pick({
+ programDataAccount: Pubkey,
+ programAccount: Pubkey,
+ payerAccount: Pubkey,
+ bufferAccount: Pubkey,
+ authority: Pubkey,
+ rentSysvar: Pubkey,
+ clockSysvar: Pubkey,
+ systemProgram: Pubkey,
+ maxDataLen: number(),
+});
+
+export type UpgradeableBpfLoaderInstructionType = StructType<
+ typeof UpgradeableBpfLoaderInstructionType
+>;
+export const UpgradeableBpfLoaderInstructionType = enums([
+ "initializeBuffer",
+ "deployWithMaxDataLen",
+ "setAuthority",
+ "write",
+ "finalize",
+]);
diff --git a/explorer/src/components/transaction/InstructionsSection.tsx b/explorer/src/components/transaction/InstructionsSection.tsx
index ed2331e52d..7957257d31 100644
--- a/explorer/src/components/transaction/InstructionsSection.tsx
+++ b/explorer/src/components/transaction/InstructionsSection.tsx
@@ -35,6 +35,7 @@ import {
} from "providers/transactions";
import { Cluster, useCluster } from "providers/cluster";
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
+import { UpgradeableBpfLoaderDetailsCard } from "components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard";
export function InstructionsSection({ signature }: SignatureProps) {
const status = useTransactionStatus(signature);
@@ -162,6 +163,8 @@ function renderInstructionCard({
return ;
case "bpf-loader":
return ;
+ case "bpf-upgradeable-loader":
+ return ;
case "system":
return ;
case "stake":
diff --git a/explorer/src/scss/_solana.scss b/explorer/src/scss/_solana.scss
index dabfe2f7f7..7d6e06a31c 100644
--- a/explorer/src/scss/_solana.scss
+++ b/explorer/src/scss/_solana.scss
@@ -341,7 +341,7 @@ div.inner-cards {
border: 1px solid red;
}
-pre.data-wrap {
+pre.data-wrap, pre.json-wrap {
max-width: 23rem;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
@@ -349,3 +349,7 @@ pre.data-wrap {
white-space: -o-pre-wrap;
word-wrap: break-word;
}
+
+pre.json-wrap {
+ max-width: 36rem;
+}
diff --git a/explorer/src/utils/index.tsx b/explorer/src/utils/index.tsx
index 5649d49586..29d45f525f 100644
--- a/explorer/src/utils/index.tsx
+++ b/explorer/src/utils/index.tsx
@@ -110,3 +110,8 @@ export function localStorageIsAvailable() {
return false;
}
}
+
+export function camelToTitleCase(str: string): string {
+ const result = str.replace(/([A-Z])/g, " $1");
+ return result.charAt(0).toUpperCase() + result.slice(1);
+}
|