From f0db85486d47ad0458af59fd0d017022a2e2ae9a Mon Sep 17 00:00:00 2001
From: Joey Meere <100378695+joeymeere@users.noreply.github.com>
Date: Tue, 5 Nov 2024 13:18:31 -0500
Subject: [PATCH 1/5] fix(tx): handle generic messages as input
---
components/CreateTransactionButton.tsx | 5 +++--
lib/transaction/decodeAndDeserialize.ts | 12 ++++++++++--
lib/transaction/getAccountsForSimulation.ts | 4 +++-
lib/transaction/simulateEncodedTransaction.ts | 2 +-
4 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/components/CreateTransactionButton.tsx b/components/CreateTransactionButton.tsx
index 2991a03..b31f147 100644
--- a/components/CreateTransactionButton.tsx
+++ b/components/CreateTransactionButton.tsx
@@ -14,6 +14,7 @@ import * as multisig from "@sqds/multisig";
import { useWallet } from "@solana/wallet-adapter-react";
import {
Connection,
+ Message,
PublicKey,
TransactionInstruction,
TransactionMessage,
@@ -54,7 +55,7 @@ const CreateTransaction = ({
programId: programId ? new PublicKey(programId) : multisig.PROGRAM_ID,
})[0];
- const dummyMessage = new TransactionMessage({
+ const dummyMessage = Message.compile({
instructions: [
new TransactionInstruction({
keys: [
@@ -72,7 +73,7 @@ const CreateTransaction = ({
],
payerKey: vaultAddress,
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
- }).compileToLegacyMessage();
+ });
const encoded = bs58.default.encode(dummyMessage.serialize());
diff --git a/lib/transaction/decodeAndDeserialize.ts b/lib/transaction/decodeAndDeserialize.ts
index 31cd53d..89f3e5a 100644
--- a/lib/transaction/decodeAndDeserialize.ts
+++ b/lib/transaction/decodeAndDeserialize.ts
@@ -1,5 +1,5 @@
import * as bs58 from "bs58";
-import { VersionedMessage } from "@solana/web3.js";
+import { Message, TransactionMessage, VersionedMessage } from "@solana/web3.js";
export function decodeAndDeserialize(tx: string): {
message: VersionedMessage;
@@ -8,7 +8,15 @@ export function decodeAndDeserialize(tx: string): {
try {
const messageBytes = bs58.default.decode(tx);
const version = VersionedMessage.deserializeMessageVersion(messageBytes);
- const message = VersionedMessage.deserialize(messageBytes);
+
+ let message;
+ if (version === "legacy") {
+ let legMsg = Message.from(messageBytes);
+ let converted = TransactionMessage.decompile(legMsg).compileToV0Message();
+ message = VersionedMessage.deserialize(converted.serialize());
+ } else {
+ message = VersionedMessage.deserialize(messageBytes);
+ }
return { version, message };
} catch (error) {
diff --git a/lib/transaction/getAccountsForSimulation.ts b/lib/transaction/getAccountsForSimulation.ts
index 4d7270b..a493ba6 100644
--- a/lib/transaction/getAccountsForSimulation.ts
+++ b/lib/transaction/getAccountsForSimulation.ts
@@ -26,7 +26,9 @@ export async function getAccountsForSimulation(
const { staticAccountKeys, accountKeysFromLookups } =
tx.message.getAccountKeys({ addressLookupTableAccounts });
- const staticAddresses = staticAccountKeys.map((k) => k.toString());
+ const staticAddresses = staticAccountKeys
+ .filter((k) => !k.equals(SystemProgram.programId))
+ .map((k) => k.toString());
const addressesFromLookups = accountKeysFromLookups
? accountKeysFromLookups.writable.map((k) => k.toString())
diff --git a/lib/transaction/simulateEncodedTransaction.ts b/lib/transaction/simulateEncodedTransaction.ts
index 195fdb6..503d230 100644
--- a/lib/transaction/simulateEncodedTransaction.ts
+++ b/lib/transaction/simulateEncodedTransaction.ts
@@ -20,7 +20,7 @@ export const simulateEncodedTransaction = async (
const keys = await getAccountsForSimulation(
connection,
transaction,
- version === "legacy"
+ version === 0
);
toast.loading("Simulating...", {
From ac98aff7738046dd1af1697fb8e374d862e67eab Mon Sep 17 00:00:00 2001
From: Joey Meere <100378695+joeymeere@users.noreply.github.com>
Date: Wed, 6 Nov 2024 09:35:38 -0500
Subject: [PATCH 2/5] remove decompile
---
lib/transaction/decodeAndDeserialize.ts | 64 ++++++++++++++++++++-----
lib/transaction/importTransaction.ts | 3 +-
2 files changed, 54 insertions(+), 13 deletions(-)
diff --git a/lib/transaction/decodeAndDeserialize.ts b/lib/transaction/decodeAndDeserialize.ts
index 89f3e5a..c31b13b 100644
--- a/lib/transaction/decodeAndDeserialize.ts
+++ b/lib/transaction/decodeAndDeserialize.ts
@@ -1,26 +1,66 @@
import * as bs58 from "bs58";
-import { Message, TransactionMessage, VersionedMessage } from "@solana/web3.js";
+import {
+ Message,
+ MessageAccountKeys,
+ MessageV0,
+ PublicKey,
+ Transaction,
+ TransactionMessage,
+ VersionedMessage,
+ VersionedTransaction,
+} from "@solana/web3.js";
-export function decodeAndDeserialize(tx: string): {
- message: VersionedMessage;
+interface DeserializedTransaction {
+ message: TransactionMessage;
version: number | "legacy";
-} {
+ accountKeys: PublicKey[];
+}
+
+/**
+ * Decodes a base58 encoded transaction and deserializes it into a TransactionMessage
+ * @param tx - Base58 encoded transaction string
+ * @returns Object containing the deserialized message, version, and account keys
+ * @throws Error if deserialization fails
+ */
+export function decodeAndDeserialize(tx: string): DeserializedTransaction {
+ if (!tx) {
+ throw new Error("Transaction string is required");
+ }
+
try {
const messageBytes = bs58.default.decode(tx);
const version = VersionedMessage.deserializeMessageVersion(messageBytes);
+ let message: TransactionMessage;
+ let accountKeys: PublicKey[];
- let message;
if (version === "legacy") {
- let legMsg = Message.from(messageBytes);
- let converted = TransactionMessage.decompile(legMsg).compileToV0Message();
- message = VersionedMessage.deserialize(converted.serialize());
+ const legacyMessage = Message.from(messageBytes);
+ accountKeys = legacyMessage.accountKeys;
+
+ const intermediate = VersionedMessage.deserialize(
+ new MessageV0(legacyMessage).serialize()
+ );
+ message = TransactionMessage.decompile(intermediate, {
+ addressLookupTableAccounts: [],
+ });
} else {
- message = VersionedMessage.deserialize(messageBytes);
+ const versionedMessage = VersionedMessage.deserialize(messageBytes);
+ accountKeys = versionedMessage.staticAccountKeys;
+
+ message = TransactionMessage.decompile(versionedMessage, {
+ addressLookupTableAccounts: [],
+ });
}
- return { version, message };
+ return {
+ version,
+ message,
+ accountKeys,
+ };
} catch (error) {
- console.error(error);
- throw new Error("Failed to decode transaction.");
+ if (error instanceof Error) {
+ throw new Error(`Failed to decode transaction: ${error.message}`);
+ }
+ throw new Error("Failed to decode transaction: Unknown error");
}
}
diff --git a/lib/transaction/importTransaction.ts b/lib/transaction/importTransaction.ts
index 9eaa0ad..4a34356 100644
--- a/lib/transaction/importTransaction.ts
+++ b/lib/transaction/importTransaction.ts
@@ -3,6 +3,7 @@ import {
Connection,
PublicKey,
TransactionMessage,
+ VersionedMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { decodeAndDeserialize } from "./decodeAndDeserialize";
@@ -29,7 +30,7 @@ export const importTransaction = async (
new PublicKey(multisigPda)
);
- const transactionMessage = TransactionMessage.decompile(message);
+ const transactionMessage = new TransactionMessage(message);
const addressLookupTableAccounts =
version === 0
From 1b24e7e930b2f277046b72cc324364f876bff672 Mon Sep 17 00:00:00 2001
From: Joey Meere <100378695+joeymeere@users.noreply.github.com>
Date: Wed, 6 Nov 2024 09:40:28 -0500
Subject: [PATCH 3/5] handle message in simulation
---
lib/transaction/simulateEncodedTransaction.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/transaction/simulateEncodedTransaction.ts b/lib/transaction/simulateEncodedTransaction.ts
index 503d230..267ec3d 100644
--- a/lib/transaction/simulateEncodedTransaction.ts
+++ b/lib/transaction/simulateEncodedTransaction.ts
@@ -15,7 +15,7 @@ export const simulateEncodedTransaction = async (
try {
const { message, version } = decodeAndDeserialize(tx);
- const transaction = new VersionedTransaction(message);
+ const transaction = new VersionedTransaction(message.compileToV0Message());
const keys = await getAccountsForSimulation(
connection,
From 08144c3cac7de714d72986555586a706975e99b0 Mon Sep 17 00:00:00 2001
From: Joey Meere <100378695+joeymeere@users.noreply.github.com>
Date: Thu, 7 Nov 2024 10:12:33 -0500
Subject: [PATCH 4/5] add alt-svm sim warning
simulations may fail on alt-SVM environments due to some accounts not being found
---
app/(app)/layout.tsx | 2 ++
components/CreateTransactionButton.tsx | 14 +++++++++-----
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx
index 79d57bf..3a22355 100644
--- a/app/(app)/layout.tsx
+++ b/app/(app)/layout.tsx
@@ -121,6 +121,8 @@ const AppLayout = async ({ children }: { children: React.ReactNode }) => {