Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import {
PublicKey,
TransactionInstruction,
SystemProgram,
} from "@solana/web3.js";
import { LazerMultisigInstruction } from "../multisig_transaction/LazerMultisigInstruction";
import {
MultisigInstructionProgram,
UNRECOGNIZED_INSTRUCTION,
} from "../multisig_transaction";

describe("LazerMultisigInstruction", () => {
const mockProgramId = new PublicKey(
"pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt"
);
const systemProgram = SystemProgram.programId;

// Generate reusable keypairs for tests
const topAuthority = PublicKey.unique();
const storage = PublicKey.unique();
const payer = PublicKey.unique();

// Test recognized instruction
test("fromInstruction should decode update instruction", () => {
const instructionData = Buffer.from([
// Anchor discriminator for update (from IDL)
219,
200,
88,
176,
158,
63,
253,
127,
// trusted_signer (pubkey - 32 bytes)
...Array(32).fill(1),
// expires_at (i64 - 8 bytes)
42,
0,
0,
0,
0,
0,
0,
0,
]);

const keys = [
{
pubkey: topAuthority,
isSigner: true,
isWritable: false,
},
{
pubkey: storage,
isSigner: false,
isWritable: true,
},
];

const instruction = new TransactionInstruction({
programId: mockProgramId,
keys,
data: instructionData,
});

const lazerInstruction =
LazerMultisigInstruction.fromInstruction(instruction);

expect(lazerInstruction.name).toBe("update");
expect(lazerInstruction.args).toBeDefined();
expect(lazerInstruction.args.trustedSigner).toBeDefined();
expect(lazerInstruction.args.expiresAt).toBeDefined();
expect(lazerInstruction.accounts).toBeDefined();
expect(lazerInstruction.accounts.named.topAuthority).toBeDefined();
expect(lazerInstruction.accounts.named.storage).toBeDefined();
});

// Test unrecognized instruction
test("fromInstruction should handle unrecognized instruction", () => {
const unrecognizedData = Buffer.from([1, 2, 3, 4]);
const keys = [
{
pubkey: topAuthority,
isSigner: false,
isWritable: true,
},
];

const instruction = new TransactionInstruction({
programId: mockProgramId,
keys,
data: unrecognizedData,
});

const lazerInstruction =
LazerMultisigInstruction.fromInstruction(instruction);

expect(lazerInstruction.name).toBe(UNRECOGNIZED_INSTRUCTION);
expect(lazerInstruction.args).toEqual({ data: unrecognizedData });
expect(lazerInstruction.accounts.remaining).toEqual(keys);
});

// Test initialize instruction
test("fromInstruction should decode initialize instruction", () => {
const instructionData = Buffer.from([
// Anchor discriminator for initialize (from IDL)
175,
175,
109,
31,
13,
152,
155,
237,
// top_authority (pubkey - 32 bytes)
...Array(32).fill(2),
// treasury (pubkey - 32 bytes)
...Array(32).fill(3),
]);

const keys = [
{
pubkey: payer,
isSigner: true,
isWritable: true,
},
{
pubkey: storage,
isSigner: false,
isWritable: true,
},
{
pubkey: systemProgram,
isSigner: false,
isWritable: false,
},
];

const instruction = new TransactionInstruction({
programId: mockProgramId,
keys,
data: instructionData,
});

const lazerInstruction =
LazerMultisigInstruction.fromInstruction(instruction);

expect(lazerInstruction.name).toBe("initialize");
expect(lazerInstruction.args).toBeDefined();
expect(lazerInstruction.args.topAuthority).toBeDefined();
expect(lazerInstruction.args.treasury).toBeDefined();
expect(lazerInstruction.accounts).toBeDefined();
expect(lazerInstruction.accounts.named.payer).toBeDefined();
expect(lazerInstruction.accounts.named.storage).toBeDefined();
expect(lazerInstruction.accounts.named.systemProgram).toBeDefined();
});

// Test program field
test("should have correct program type", () => {
const instruction = new TransactionInstruction({
programId: mockProgramId,
keys: [],
data: Buffer.from([]),
});

const lazerInstruction =
LazerMultisigInstruction.fromInstruction(instruction);
expect(lazerInstruction.program).toBe(MultisigInstructionProgram.Lazer);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
MultisigInstruction,
MultisigInstructionProgram,
UNRECOGNIZED_INSTRUCTION,
} from "./index";
import { AnchorAccounts, resolveAccountNames } from "./anchor";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { Idl, BorshInstructionCoder } from "@coral-xyz/anchor";
import lazerIdl from "./idl/lazer.json";

export const LAZER_PROGRAM_ID = new PublicKey(
"pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt"
);

export class LazerMultisigInstruction implements MultisigInstruction {
readonly program = MultisigInstructionProgram.Lazer;
readonly name: string;
readonly args: { [key: string]: any };
readonly accounts: AnchorAccounts;

constructor(
name: string,
args: { [key: string]: any },
accounts: AnchorAccounts
) {
this.name = name;
this.args = args;
this.accounts = accounts;
}

static fromInstruction(
instruction: TransactionInstruction
): LazerMultisigInstruction {
// TODO: This is a hack to transform the IDL to be compatible with the anchor version we are using, we can't upgrade anchor to 0.30.1 because then the existing idls will break
const idl = lazerIdl as Idl;

const coder = new BorshInstructionCoder(idl);

const deserializedData = coder.decode(instruction.data);

if (deserializedData) {
return new LazerMultisigInstruction(
deserializedData.name,
deserializedData.data,
resolveAccountNames(idl, deserializedData.name, instruction)
);
} else {
return new LazerMultisigInstruction(
UNRECOGNIZED_INSTRUCTION,
{ data: instruction.data },
{ named: {}, remaining: instruction.keys }
);
}
}
}
Loading
Loading