diff --git a/explorer/src/components/ProgramLogsCardBody.tsx b/explorer/src/components/ProgramLogsCardBody.tsx
index fff28b8b8ce112..b1b1cbd445acca 100644
--- a/explorer/src/components/ProgramLogsCardBody.tsx
+++ b/explorer/src/components/ProgramLogsCardBody.tsx
@@ -1,4 +1,4 @@
-import { Message, ParsedMessage } from "@solana/web3.js";
+import { ParsedMessage, PublicKey, VersionedMessage } from "@solana/web3.js";
import { Cluster } from "providers/cluster";
import { TableCardBody } from "components/common/TableCardBody";
import { InstructionLogs } from "utils/program-logs";
@@ -21,27 +21,24 @@ export function ProgramLogsCardBody({
cluster,
url,
}: {
- message: Message | ParsedMessage;
+ message: VersionedMessage | ParsedMessage;
logs: InstructionLogs[];
cluster: Cluster;
url: string;
}) {
let logIndex = 0;
+ let instructionProgramIds: PublicKey[];
+ if ("compiledInstructions" in message) {
+ instructionProgramIds = message.compiledInstructions.map((ix) => {
+ return message.staticAccountKeys[ix.programIdIndex];
+ });
+ } else {
+ instructionProgramIds = message.instructions.map((ix) => ix.programId);
+ }
+
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;
- }
-
+ {instructionProgramIds.map((programId, index) => {
const programAddress = programId.toBase58();
let programLogs: InstructionLogs | undefined = logs[logIndex];
if (programLogs?.invokedProgram === programAddress) {
diff --git a/explorer/src/components/block/BlockAccountsCard.tsx b/explorer/src/components/block/BlockAccountsCard.tsx
index 2d1c0a7ab635a8..823000d7bce889 100644
--- a/explorer/src/components/block/BlockAccountsCard.tsx
+++ b/explorer/src/components/block/BlockAccountsCard.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { BlockResponse, PublicKey } from "@solana/web3.js";
+import { PublicKey, VersionedBlockResponse } from "@solana/web3.js";
import { Address } from "components/common/Address";
import { Link } from "react-router-dom";
import { clusterPath } from "utils/url";
@@ -15,7 +15,7 @@ export function BlockAccountsCard({
block,
blockSlot,
}: {
- block: BlockResponse;
+ block: VersionedBlockResponse;
blockSlot: number;
}) {
const [numDisplayed, setNumDisplayed] = React.useState(10);
@@ -26,9 +26,9 @@ export function BlockAccountsCard({
block.transactions.forEach((tx) => {
const message = tx.transaction.message;
const txSet = new Map();
- message.instructions.forEach((ix) => {
- ix.accounts.forEach((index) => {
- const address = message.accountKeys[index].toBase58();
+ message.compiledInstructions.forEach((ix) => {
+ ix.accountKeyIndexes.forEach((index) => {
+ const address = message.getAccountKeys().get(index)!.toBase58();
txSet.set(address, message.isAccountWritable(index));
});
});
diff --git a/explorer/src/components/block/BlockHistoryCard.tsx b/explorer/src/components/block/BlockHistoryCard.tsx
index e516e960afa444..171345bac16715 100644
--- a/explorer/src/components/block/BlockHistoryCard.tsx
+++ b/explorer/src/components/block/BlockHistoryCard.tsx
@@ -2,11 +2,11 @@ import React from "react";
import { Link, useHistory, useLocation } from "react-router-dom";
import { Location } from "history";
import {
- BlockResponse,
ConfirmedTransactionMeta,
TransactionSignature,
PublicKey,
VOTE_PROGRAM_ID,
+ VersionedBlockResponse,
} from "@solana/web3.js";
import { ErrorCard } from "components/common/ErrorCard";
import { Signature } from "components/common/Signature";
@@ -51,7 +51,7 @@ type TransactionWithInvocations = {
logTruncated: boolean;
};
-export function BlockHistoryCard({ block }: { block: BlockResponse }) {
+export function BlockHistoryCard({ block }: { block: VersionedBlockResponse }) {
const [numDisplayed, setNumDisplayed] = React.useState(PAGE_SIZE);
const [showDropdown, setDropdown] = React.useState(false);
const query = useQuery();
@@ -72,7 +72,7 @@ export function BlockHistoryCard({ block }: { block: BlockResponse }) {
signature = tx.transaction.signatures[0];
}
- let programIndexes = tx.transaction.message.instructions
+ let programIndexes = tx.transaction.message.compiledInstructions
.map((ix) => ix.programIdIndex)
.concat(
tx.meta?.innerInstructions?.flatMap((ix) => {
@@ -87,8 +87,14 @@ export function BlockHistoryCard({ block }: { block: BlockResponse }) {
});
const invocations = new Map();
+ const accountKeysFromLookups = tx.meta?.loadedAddresses;
+ const accountKeys = tx.transaction.message.getAccountKeys(
+ accountKeysFromLookups && {
+ accountKeysFromLookups,
+ }
+ );
for (const [i, count] of indexMap.entries()) {
- const programId = tx.transaction.message.accountKeys[i].toBase58();
+ const programId = accountKeys.get(i)!.toBase58();
invocations.set(programId, count);
const programTransactionCount = invokedPrograms.get(programId) || 0;
invokedPrograms.set(programId, programTransactionCount + 1);
@@ -143,8 +149,18 @@ export function BlockHistoryCard({ block }: { block: BlockResponse }) {
if (accountFilter === null) {
return true;
}
- const tx = block.transactions[index].transaction;
- return tx.message.accountKeys.find((key) => key.equals(accountFilter));
+
+ const tx = block.transactions[index];
+ const accountKeysFromLookups = tx.meta?.loadedAddresses;
+ const accountKeys = tx.transaction.message.getAccountKeys(
+ accountKeysFromLookups && {
+ accountKeysFromLookups,
+ }
+ );
+ return accountKeys
+ .keySegments()
+ .flat()
+ .find((key) => key.equals(accountFilter));
});
const showComputeUnits = filteredTxs.every(
diff --git a/explorer/src/components/block/BlockOverviewCard.tsx b/explorer/src/components/block/BlockOverviewCard.tsx
index 15279382c72c94..1675e4c518493e 100644
--- a/explorer/src/components/block/BlockOverviewCard.tsx
+++ b/explorer/src/components/block/BlockOverviewCard.tsx
@@ -7,7 +7,7 @@ import { Slot } from "components/common/Slot";
import { ClusterStatus, useCluster } from "providers/cluster";
import { BlockHistoryCard } from "./BlockHistoryCard";
import { BlockRewardsCard } from "./BlockRewardsCard";
-import { BlockResponse } from "@solana/web3.js";
+import { VersionedBlockResponse } from "@solana/web3.js";
import { NavLink } from "react-router-dom";
import { clusterPath } from "utils/url";
import { BlockProgramsCard } from "./BlockProgramsCard";
@@ -211,7 +211,7 @@ function MoreSection({
tab,
}: {
slot: number;
- block: BlockResponse;
+ block: VersionedBlockResponse;
tab?: string;
}) {
return (
diff --git a/explorer/src/components/block/BlockProgramsCard.tsx b/explorer/src/components/block/BlockProgramsCard.tsx
index b1a70006ab9b1c..0b9d986c47fccb 100644
--- a/explorer/src/components/block/BlockProgramsCard.tsx
+++ b/explorer/src/components/block/BlockProgramsCard.tsx
@@ -1,9 +1,13 @@
import React from "react";
-import { BlockResponse, PublicKey } from "@solana/web3.js";
+import { PublicKey, VersionedBlockResponse } from "@solana/web3.js";
import { Address } from "components/common/Address";
import { TableCardBody } from "components/common/TableCardBody";
-export function BlockProgramsCard({ block }: { block: BlockResponse }) {
+export function BlockProgramsCard({
+ block,
+}: {
+ block: VersionedBlockResponse;
+}) {
const totalTransactions = block.transactions.length;
const txSuccesses = new Map();
const txFrequency = new Map();
@@ -12,18 +16,26 @@ export function BlockProgramsCard({ block }: { block: BlockResponse }) {
let totalInstructions = 0;
block.transactions.forEach((tx) => {
const message = tx.transaction.message;
- totalInstructions += message.instructions.length;
+ totalInstructions += message.compiledInstructions.length;
const programUsed = new Set();
+ const accountKeysFromLookups = tx.meta?.loadedAddresses;
+ const accountKeys = tx.transaction.message.getAccountKeys(
+ accountKeysFromLookups && {
+ accountKeysFromLookups,
+ }
+ );
const trackProgram = (index: number) => {
- if (index >= message.accountKeys.length) return;
- const programId = message.accountKeys[index];
+ if (index >= accountKeys.length) return;
+ const programId = accountKeys.get(index)!;
const programAddress = programId.toBase58();
programUsed.add(programAddress);
const frequency = ixFrequency.get(programAddress);
ixFrequency.set(programAddress, frequency ? frequency + 1 : 1);
};
- message.instructions.forEach((ix) => trackProgram(ix.programIdIndex));
+ message.compiledInstructions.forEach((ix) =>
+ trackProgram(ix.programIdIndex)
+ );
tx.meta?.innerInstructions?.forEach((inner) => {
totalInstructions += inner.instructions.length;
inner.instructions.forEach((innerIx) =>
diff --git a/explorer/src/components/block/BlockRewardsCard.tsx b/explorer/src/components/block/BlockRewardsCard.tsx
index c1085b14005e04..8d4d41fc261e19 100644
--- a/explorer/src/components/block/BlockRewardsCard.tsx
+++ b/explorer/src/components/block/BlockRewardsCard.tsx
@@ -1,11 +1,11 @@
import React from "react";
import { SolBalance } from "utils";
-import { BlockResponse, PublicKey } from "@solana/web3.js";
+import { PublicKey, VersionedBlockResponse } from "@solana/web3.js";
import { Address } from "components/common/Address";
const PAGE_SIZE = 10;
-export function BlockRewardsCard({ block }: { block: BlockResponse }) {
+export function BlockRewardsCard({ block }: { block: VersionedBlockResponse }) {
const [rewardsDisplayed, setRewardsDisplayed] = React.useState(PAGE_SIZE);
if (!block.rewards || block.rewards.length < 1) {
diff --git a/explorer/src/pages/TransactionDetailsPage.tsx b/explorer/src/pages/TransactionDetailsPage.tsx
index 044c46d5028f49..6ebe5478b82190 100644
--- a/explorer/src/pages/TransactionDetailsPage.tsx
+++ b/explorer/src/pages/TransactionDetailsPage.tsx
@@ -197,6 +197,7 @@ function StatusCard({
const fee = transactionWithMeta?.meta?.fee;
const transaction = transactionWithMeta?.transaction;
const blockhash = transaction?.message.recentBlockhash;
+ const version = transactionWithMeta?.version;
const isNonce = (() => {
if (!transaction || transaction.message.instructions.length < 1) {
return false;
@@ -330,6 +331,13 @@ function StatusCard({
)}
+
+ {version !== undefined && (
+
+ Transaction Version |
+ {version} |
+
+ )}
);
diff --git a/explorer/src/pages/inspector/AccountsCard.tsx b/explorer/src/pages/inspector/AccountsCard.tsx
index 53702ea3fb3405..b784df5fc62e7a 100644
--- a/explorer/src/pages/inspector/AccountsCard.tsx
+++ b/explorer/src/pages/inspector/AccountsCard.tsx
@@ -1,10 +1,10 @@
import React from "react";
-import { Message, PublicKey } from "@solana/web3.js";
+import { PublicKey, VersionedMessage } from "@solana/web3.js";
import { TableCardBody } from "components/common/TableCardBody";
-import { AddressWithContext } from "./AddressWithContext";
+import { AddressFromLookupTableWithContext, AddressWithContext } from "./AddressWithContext";
import { ErrorCard } from "components/common/ErrorCard";
-export function AccountsCard({ message }: { message: Message }) {
+export function AccountsCard({ message }: { message: VersionedMessage }) {
const [expanded, setExpanded] = React.useState(true);
const { validMessage, error } = React.useMemo(() => {
@@ -16,9 +16,11 @@ export function AccountsCard({ message }: { message: Message }) {
if (numReadonlySignedAccounts >= numRequiredSignatures) {
return { validMessage: undefined, error: "Invalid header" };
- } else if (numReadonlyUnsignedAccounts >= message.accountKeys.length) {
+ } else if (
+ numReadonlyUnsignedAccounts >= message.staticAccountKeys.length
+ ) {
return { validMessage: undefined, error: "Invalid header" };
- } else if (message.accountKeys.length === 0) {
+ } else if (message.staticAccountKeys.length === 0) {
return { validMessage: undefined, error: "Message has no accounts" };
}
@@ -28,10 +30,10 @@ export function AccountsCard({ message }: { message: Message }) {
};
}, [message]);
- const accountRows = React.useMemo(() => {
+ const {accountRows, numAccounts} = React.useMemo(() => {
const message = validMessage;
- if (!message) return;
- return message.accountKeys.map((publicKey, accountIndex) => {
+ if (!message) return {accountRows: undefined, numAccounts: 0};
+ const staticAccountRows = message.staticAccountKeys.map((publicKey, accountIndex) => {
const {
numRequiredSignatures,
numReadonlySignedAccounts,
@@ -47,7 +49,7 @@ export function AccountsCard({ message }: { message: Message }) {
}
} else if (
accountIndex >=
- message.accountKeys.length - numReadonlyUnsignedAccounts
+ message.staticAccountKeys.length - numReadonlyUnsignedAccounts
) {
readOnly = true;
}
@@ -61,6 +63,40 @@ export function AccountsCard({ message }: { message: Message }) {
return ;
});
+
+ let accountIndex = message.staticAccountKeys.length;
+ const writableLookupTableRows = message.addressTableLookups.flatMap((lookup) => {
+ return lookup.writableIndexes.map(lookupTableIndex => {
+ const props = {
+ accountIndex,
+ lookupTableKey: lookup.accountKey,
+ lookupTableIndex,
+ readOnly: false,
+ };
+
+ accountIndex += 1;
+ return ;
+ });
+ });
+
+ const readonlyLookupTableRows = message.addressTableLookups.flatMap((lookup) => {
+ return lookup.readonlyIndexes.map(lookupTableIndex => {
+ const props = {
+ accountIndex,
+ lookupTableKey: lookup.accountKey,
+ lookupTableIndex,
+ readOnly: true,
+ };
+
+ accountIndex += 1;
+ return ;
+ });
+ });
+
+ return {
+ accountRows: [...staticAccountRows, ...writableLookupTableRows, ...readonlyLookupTableRows],
+ numAccounts: accountIndex,
+ };
}, [validMessage]);
if (error) {
@@ -71,7 +107,7 @@ export function AccountsCard({ message }: { message: Message }) {
- {`Account List (${message.accountKeys.length})`}
+ {`Account List (${numAccounts})`}
- {message.accountKeys.length === 0 ? (
+ {message.staticAccountKeys.length === 0 ? (
"No Fee Payer"
) : (
)}
diff --git a/explorer/src/pages/inspector/InstructionsSection.tsx b/explorer/src/pages/inspector/InstructionsSection.tsx
index baba3a012bf28f..03618fd5ce6391 100644
--- a/explorer/src/pages/inspector/InstructionsSection.tsx
+++ b/explorer/src/pages/inspector/InstructionsSection.tsx
@@ -1,6 +1,5 @@
import React from "react";
-import bs58 from "bs58";
-import { CompiledInstruction, Message } from "@solana/web3.js";
+import { MessageCompiledInstruction, VersionedMessage } from "@solana/web3.js";
import { TableCardBody } from "components/common/TableCardBody";
import { AddressWithContext, programValidator } from "./AddressWithContext";
import { useCluster } from "providers/cluster";
@@ -9,10 +8,14 @@ import { HexData } from "components/common/HexData";
import getInstructionCardScrollAnchorId from "utils/get-instruction-card-scroll-anchor-id";
import { useScrollAnchor } from "providers/scroll-anchor";
-export function InstructionsSection({ message }: { message: Message }) {
+export function InstructionsSection({
+ message,
+}: {
+ message: VersionedMessage;
+}) {
return (
<>
- {message.instructions.map((ix, index) => {
+ {message.compiledInstructions.map((ix, index) => {
return ;
})}
>
@@ -24,13 +27,13 @@ function InstructionCard({
ix,
index,
}: {
- message: Message;
- ix: CompiledInstruction;
+ message: VersionedMessage;
+ ix: MessageCompiledInstruction;
index: number;
}) {
const [expanded, setExpanded] = React.useState(false);
const { cluster } = useCluster();
- const programId = message.accountKeys[ix.programIdIndex];
+ const programId = message.staticAccountKeys[ix.programIdIndex];
const programName = getProgramName(programId.toBase58(), cluster);
const scrollAnchorRef = useScrollAnchor(
getInstructionCardScrollAnchorId([index + 1])
@@ -58,12 +61,12 @@ function InstructionCard({
| Program |
|
- {ix.accounts.map((accountIndex, index) => {
+ {ix.accountKeyIndexes.map((accountIndex, index) => {
return (
@@ -82,9 +85,13 @@ function InstructionCard({
|
-
+ {accountIndex < message.staticAccountKeys.length ? (
+
+ ) : (
+ "Context not yet available for address table lookups"
+ )}
|
);
@@ -94,7 +101,7 @@ function InstructionCard({
Instruction Data
(Hex)
-
+
|
diff --git a/explorer/src/pages/inspector/RawInputCard.tsx b/explorer/src/pages/inspector/RawInputCard.tsx
index 6e725f87a2b71e..6b9f150cde8927 100644
--- a/explorer/src/pages/inspector/RawInputCard.tsx
+++ b/explorer/src/pages/inspector/RawInputCard.tsx
@@ -1,12 +1,12 @@
import React from "react";
-import { Message } from "@solana/web3.js";
+import { VersionedMessage } from "@solana/web3.js";
import type { TransactionData } from "./InspectorPage";
import { useQuery } from "utils/url";
import { useHistory, useLocation } from "react-router";
import base58 from "bs58";
function deserializeTransaction(bytes: Uint8Array): {
- message: Message;
+ message: VersionedMessage;
signatures: string[];
} | null {
const SIGNATURE_LENGTH = 64;
@@ -19,17 +19,12 @@ function deserializeTransaction(bytes: Uint8Array): {
bytes = bytes.slice(SIGNATURE_LENGTH);
signatures.push(base58.encode(rawSignature));
}
-
- const requiredSignatures = bytes[0];
- if (requiredSignatures !== signaturesLen) {
- throw new Error("Signature length mismatch");
- }
} catch (err) {
// Errors above indicate that the bytes do not encode a transaction.
return null;
}
- const message = Message.from(bytes);
+ const message = VersionedMessage.deserialize(bytes);
return { message, signatures };
}
@@ -103,7 +98,7 @@ export function RawInput({
signatures: tx.signatures,
});
} else {
- const message = Message.from(buffer);
+ const message = VersionedMessage.deserialize(buffer);
setTransactionData({
rawMessage: buffer,
message,
diff --git a/explorer/src/pages/inspector/SignaturesCard.tsx b/explorer/src/pages/inspector/SignaturesCard.tsx
index 6ba22cdef6c89b..3ae61faa23c5eb 100644
--- a/explorer/src/pages/inspector/SignaturesCard.tsx
+++ b/explorer/src/pages/inspector/SignaturesCard.tsx
@@ -1,7 +1,7 @@
import React from "react";
import bs58 from "bs58";
import * as nacl from "tweetnacl";
-import { Message, PublicKey } from "@solana/web3.js";
+import { PublicKey, VersionedMessage } from "@solana/web3.js";
import { Signature } from "components/common/Signature";
import { Address } from "components/common/Address";
@@ -11,12 +11,12 @@ export function TransactionSignatures({
rawMessage,
}: {
signatures: (string | null)[];
- message: Message;
+ message: VersionedMessage;
rawMessage: Uint8Array;
}) {
const signatureRows = React.useMemo(() => {
return signatures.map((signature, index) => {
- const publicKey = message.accountKeys[index];
+ const publicKey = message.staticAccountKeys[index];
let verified;
if (signature) {
diff --git a/explorer/src/pages/inspector/SimulatorCard.tsx b/explorer/src/pages/inspector/SimulatorCard.tsx
index 4ca2fbf66b6451..ef89e605969be0 100644
--- a/explorer/src/pages/inspector/SimulatorCard.tsx
+++ b/explorer/src/pages/inspector/SimulatorCard.tsx
@@ -1,13 +1,14 @@
import React from "react";
-import bs58 from "bs58";
-import { Connection, Message, Transaction } from "@solana/web3.js";
+import {
+ Connection,
+ VersionedMessage,
+ VersionedTransaction,
+} from "@solana/web3.js";
import { useCluster } from "providers/cluster";
import { InstructionLogs, parseProgramLogs } from "utils/program-logs";
import { ProgramLogsCardBody } from "components/ProgramLogsCardBody";
-const DEFAULT_SIGNATURE = bs58.encode(Buffer.alloc(64).fill(0));
-
-export function SimulatorCard({ message }: { message: Message }) {
+export function SimulatorCard({ message }: { message: VersionedMessage }) {
const { cluster, url } = useCluster();
const {
simulate,
@@ -77,7 +78,7 @@ export function SimulatorCard({ message }: { message: Message }) {
);
}
-function useSimulator(message: Message) {
+function useSimulator(message: VersionedMessage) {
const { cluster, url } = useCluster();
const [simulating, setSimulating] = React.useState(false);
const [logs, setLogs] = React.useState
| null>(null);
@@ -97,15 +98,10 @@ function useSimulator(message: Message) {
const connection = new Connection(url, "confirmed");
(async () => {
try {
- const tx = Transaction.populate(
- message,
- new Array(message.header.numRequiredSignatures).fill(
- DEFAULT_SIGNATURE
- )
- );
-
// Simulate without signers to skip signer verification
- const resp = await connection.simulateTransaction(tx);
+ const resp = await connection.simulateTransaction(
+ new VersionedTransaction(message)
+ );
if (resp.value.logs === null) {
throw new Error("Expected to receive logs from simulation");
}
diff --git a/explorer/src/providers/accounts/history.tsx b/explorer/src/providers/accounts/history.tsx
index 618a426a15e325..5df235889d53dc 100644
--- a/explorer/src/providers/accounts/history.tsx
+++ b/explorer/src/providers/accounts/history.tsx
@@ -109,7 +109,9 @@ async function fetchParsedTransactions(
0,
MAX_TRANSACTION_BATCH_SIZE
);
- const fetched = await connection.getParsedTransactions(signatures);
+ const fetched = await connection.getParsedTransactions(signatures, {
+ maxSupportedTransactionVersion: 0,
+ });
fetched.forEach(
(
transactionWithMeta: ParsedTransactionWithMeta | null,
diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx
index 1618ccde7a96f1..a7b5d663eb09dc 100644
--- a/explorer/src/providers/accounts/index.tsx
+++ b/explorer/src/providers/accounts/index.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { pubkeyToString } from "utils";
-import { PublicKey, Connection, StakeActivationData } from "@solana/web3.js";
+import { PublicKey, Connection, StakeActivationData, AddressLookupTableAccount } from "@solana/web3.js";
import { useCluster, Cluster } from "../cluster";
import { HistoryProvider } from "./history";
import { TokensProvider } from "./tokens";
@@ -19,6 +19,7 @@ import { VoteAccount } from "validators/accounts/vote";
import { NonceAccount } from "validators/accounts/nonce";
import { SysvarAccount } from "validators/accounts/sysvar";
import { ConfigAccount } from "validators/accounts/config";
+import { ParsedAddressLookupTableAccount } from "validators/accounts/address-lookup-table";
import { FlaggedAccountsProvider } from "./flagged-accounts";
import {
ProgramDataAccount,
@@ -76,6 +77,11 @@ export type ConfigProgramData = {
parsed: ConfigAccount;
};
+export type AddressLookupTableProgramData = {
+ program: "address-lookup-table";
+ parsed: ParsedAddressLookupTableAccount;
+};
+
export type ProgramData =
| UpgradeableLoaderAccountData
| StakeProgramData
@@ -83,7 +89,8 @@ export type ProgramData =
| VoteProgramData
| NonceProgramData
| SysvarProgramData
- | ConfigProgramData;
+ | ConfigProgramData
+ | AddressLookupTableProgramData;
export interface Details {
executable: boolean;
@@ -238,6 +245,17 @@ async function fetchAccountInfo(
};
break;
+ case "address-lookup-table": {
+ const parsed = create(info, ParsedAddressLookupTableAccount);
+
+ data = {
+ program: result.data.program,
+ parsed,
+ };
+
+ break;
+ }
+
case "spl-token":
const parsed = create(info, TokenAccount);
let nftData;
@@ -428,6 +446,30 @@ export function useTokenAccountInfo(
}
}
+export function useAddressLookupTable(
+ address: string | undefined
+): AddressLookupTableAccount | undefined {
+ const accountInfo = useAccountInfo(address);
+ if (address === undefined) return;
+ if (accountInfo?.data?.details === undefined) return;
+ const {data, rawData} = accountInfo.data.details;
+
+ const key = new PublicKey(address);
+ if (data && data.program === "address-lookup-table") {
+ if (data.parsed.type === "lookupTable") {
+ return new AddressLookupTableAccount({
+ key,
+ state: data.parsed.info,
+ });
+ }
+ } else if (rawData) {
+ return new AddressLookupTableAccount({
+ key,
+ state: AddressLookupTableAccount.deserialize(rawData),
+ });
+ }
+}
+
export function useFetchAccountInfo() {
const dispatch = React.useContext(DispatchContext);
if (!dispatch) {
diff --git a/explorer/src/providers/block.tsx b/explorer/src/providers/block.tsx
index fa1b564950ddfc..8414413769313a 100644
--- a/explorer/src/providers/block.tsx
+++ b/explorer/src/providers/block.tsx
@@ -1,7 +1,7 @@
import React from "react";
import * as Sentry from "@sentry/react";
import * as Cache from "providers/cache";
-import { Connection, BlockResponse, PublicKey } from "@solana/web3.js";
+import { Connection, PublicKey, VersionedBlockResponse } from "@solana/web3.js";
import { useCluster, Cluster } from "./cluster";
export enum FetchStatus {
@@ -16,7 +16,7 @@ export enum ActionType {
}
type Block = {
- block?: BlockResponse;
+ block?: VersionedBlockResponse;
blockLeader?: PublicKey;
childSlot?: number;
childLeader?: PublicKey;
@@ -76,7 +76,9 @@ export async function fetchBlock(
try {
const connection = new Connection(url, "confirmed");
- const block = await connection.getBlock(slot);
+ const block = await connection.getBlock(slot, {
+ maxSupportedTransactionVersion: 0,
+ });
if (block === null) {
data = {};
status = FetchStatus.Fetched;
diff --git a/explorer/src/providers/transactions/parsed.tsx b/explorer/src/providers/transactions/parsed.tsx
index 4ec8b2aaa03cac..1712f8d5691d77 100644
--- a/explorer/src/providers/transactions/parsed.tsx
+++ b/explorer/src/providers/transactions/parsed.tsx
@@ -57,7 +57,7 @@ async function fetchDetails(
try {
transactionWithMeta = await new Connection(url).getParsedTransaction(
signature,
- "confirmed"
+ { commitment: "confirmed", maxSupportedTransactionVersion: 0 }
);
fetchStatus = FetchStatus.Fetched;
} catch (error) {
diff --git a/explorer/src/providers/transactions/raw.tsx b/explorer/src/providers/transactions/raw.tsx
index 3e928b20e523a6..cdf29de7fe77a2 100644
--- a/explorer/src/providers/transactions/raw.tsx
+++ b/explorer/src/providers/transactions/raw.tsx
@@ -2,8 +2,9 @@ import React from "react";
import {
Connection,
TransactionSignature,
- Transaction,
- Message,
+ TransactionMessage,
+ DecompileArgs,
+ VersionedMessage,
} from "@solana/web3.js";
import { useCluster, Cluster } from "../cluster";
import * as Cache from "providers/cache";
@@ -12,8 +13,8 @@ import { reportError } from "utils/sentry";
export interface Details {
raw?: {
- transaction: Transaction;
- message: Message;
+ transaction: TransactionMessage;
+ message: VersionedMessage;
signatures: string[];
} | null;
}
@@ -66,17 +67,22 @@ async function fetchRawTransaction(
) {
let fetchStatus;
try {
- const response = await new Connection(url).getTransaction(signature);
+ const response = await new Connection(url).getTransaction(signature, {
+ maxSupportedTransactionVersion: 0,
+ });
fetchStatus = FetchStatus.Fetched;
let data: Details = { raw: null };
if (response !== null) {
const { message, signatures } = response.transaction;
+ const accountKeysFromLookups = response.meta?.loadedAddresses;
+ const decompileArgs: DecompileArgs | undefined =
+ accountKeysFromLookups && { accountKeysFromLookups };
data = {
raw: {
message,
signatures,
- transaction: Transaction.populate(message, signatures),
+ transaction: TransactionMessage.decompile(message, decompileArgs),
},
};
}
diff --git a/explorer/src/validators/accounts/address-lookup-table.ts b/explorer/src/validators/accounts/address-lookup-table.ts
new file mode 100644
index 00000000000000..da8f9e46421688
--- /dev/null
+++ b/explorer/src/validators/accounts/address-lookup-table.ts
@@ -0,0 +1,32 @@
+/* eslint-disable @typescript-eslint/no-redeclare */
+
+import { Infer, number, enums, type, array, optional } from "superstruct";
+import { PublicKeyFromString } from "validators/pubkey";
+import { BigIntFromString, NumberFromString } from "validators/number";
+
+export type AddressLookupTableAccountType = Infer<
+ typeof AddressLookupTableAccountType
+>;
+export const AddressLookupTableAccountType = enums([
+ "uninitialized",
+ "lookupTable",
+]);
+
+export type AddressLookupTableAccountInfo = Infer<
+ typeof AddressLookupTableAccountInfo
+>;
+export const AddressLookupTableAccountInfo = type({
+ deactivationSlot: BigIntFromString,
+ lastExtendedSlot: NumberFromString,
+ lastExtendedSlotStartIndex: number(),
+ authority: optional(PublicKeyFromString),
+ addresses: array(PublicKeyFromString),
+});
+
+export type ParsedAddressLookupTableAccount = Infer<
+ typeof ParsedAddressLookupTableAccount
+>;
+export const ParsedAddressLookupTableAccount = type({
+ type: AddressLookupTableAccountType,
+ info: AddressLookupTableAccountInfo,
+});