Skip to content

Commit cbfc885

Browse files
authored
feat(xc-admin): add support for deserializing lazer instructions in xc-admin (#2246)
* feat: add support for deserializing lazer instructions in xc-admin * fix error * add test
1 parent 2faddf9 commit cbfc885

File tree

4 files changed

+542
-0
lines changed

4 files changed

+542
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import {
2+
PublicKey,
3+
TransactionInstruction,
4+
SystemProgram,
5+
} from "@solana/web3.js";
6+
import { LazerMultisigInstruction } from "../multisig_transaction/LazerMultisigInstruction";
7+
import {
8+
MultisigInstructionProgram,
9+
UNRECOGNIZED_INSTRUCTION,
10+
} from "../multisig_transaction";
11+
12+
describe("LazerMultisigInstruction", () => {
13+
const mockProgramId = new PublicKey(
14+
"pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt"
15+
);
16+
const systemProgram = SystemProgram.programId;
17+
18+
// Generate reusable keypairs for tests
19+
const topAuthority = PublicKey.unique();
20+
const storage = PublicKey.unique();
21+
const payer = PublicKey.unique();
22+
23+
// Test recognized instruction
24+
test("fromInstruction should decode update instruction", () => {
25+
const instructionData = Buffer.from([
26+
// Anchor discriminator for update (from IDL)
27+
219,
28+
200,
29+
88,
30+
176,
31+
158,
32+
63,
33+
253,
34+
127,
35+
// trusted_signer (pubkey - 32 bytes)
36+
...Array(32).fill(1),
37+
// expires_at (i64 - 8 bytes)
38+
42,
39+
0,
40+
0,
41+
0,
42+
0,
43+
0,
44+
0,
45+
0,
46+
]);
47+
48+
const keys = [
49+
{
50+
pubkey: topAuthority,
51+
isSigner: true,
52+
isWritable: false,
53+
},
54+
{
55+
pubkey: storage,
56+
isSigner: false,
57+
isWritable: true,
58+
},
59+
];
60+
61+
const instruction = new TransactionInstruction({
62+
programId: mockProgramId,
63+
keys,
64+
data: instructionData,
65+
});
66+
67+
const lazerInstruction =
68+
LazerMultisigInstruction.fromInstruction(instruction);
69+
70+
expect(lazerInstruction.name).toBe("update");
71+
expect(lazerInstruction.args).toBeDefined();
72+
expect(lazerInstruction.args.trustedSigner).toBeDefined();
73+
expect(lazerInstruction.args.expiresAt).toBeDefined();
74+
expect(lazerInstruction.accounts).toBeDefined();
75+
expect(lazerInstruction.accounts.named.topAuthority).toBeDefined();
76+
expect(lazerInstruction.accounts.named.storage).toBeDefined();
77+
});
78+
79+
// Test unrecognized instruction
80+
test("fromInstruction should handle unrecognized instruction", () => {
81+
const unrecognizedData = Buffer.from([1, 2, 3, 4]);
82+
const keys = [
83+
{
84+
pubkey: topAuthority,
85+
isSigner: false,
86+
isWritable: true,
87+
},
88+
];
89+
90+
const instruction = new TransactionInstruction({
91+
programId: mockProgramId,
92+
keys,
93+
data: unrecognizedData,
94+
});
95+
96+
const lazerInstruction =
97+
LazerMultisigInstruction.fromInstruction(instruction);
98+
99+
expect(lazerInstruction.name).toBe(UNRECOGNIZED_INSTRUCTION);
100+
expect(lazerInstruction.args).toEqual({ data: unrecognizedData });
101+
expect(lazerInstruction.accounts.remaining).toEqual(keys);
102+
});
103+
104+
// Test initialize instruction
105+
test("fromInstruction should decode initialize instruction", () => {
106+
const instructionData = Buffer.from([
107+
// Anchor discriminator for initialize (from IDL)
108+
175,
109+
175,
110+
109,
111+
31,
112+
13,
113+
152,
114+
155,
115+
237,
116+
// top_authority (pubkey - 32 bytes)
117+
...Array(32).fill(2),
118+
// treasury (pubkey - 32 bytes)
119+
...Array(32).fill(3),
120+
]);
121+
122+
const keys = [
123+
{
124+
pubkey: payer,
125+
isSigner: true,
126+
isWritable: true,
127+
},
128+
{
129+
pubkey: storage,
130+
isSigner: false,
131+
isWritable: true,
132+
},
133+
{
134+
pubkey: systemProgram,
135+
isSigner: false,
136+
isWritable: false,
137+
},
138+
];
139+
140+
const instruction = new TransactionInstruction({
141+
programId: mockProgramId,
142+
keys,
143+
data: instructionData,
144+
});
145+
146+
const lazerInstruction =
147+
LazerMultisigInstruction.fromInstruction(instruction);
148+
149+
expect(lazerInstruction.name).toBe("initialize");
150+
expect(lazerInstruction.args).toBeDefined();
151+
expect(lazerInstruction.args.topAuthority).toBeDefined();
152+
expect(lazerInstruction.args.treasury).toBeDefined();
153+
expect(lazerInstruction.accounts).toBeDefined();
154+
expect(lazerInstruction.accounts.named.payer).toBeDefined();
155+
expect(lazerInstruction.accounts.named.storage).toBeDefined();
156+
expect(lazerInstruction.accounts.named.systemProgram).toBeDefined();
157+
});
158+
159+
// Test program field
160+
test("should have correct program type", () => {
161+
const instruction = new TransactionInstruction({
162+
programId: mockProgramId,
163+
keys: [],
164+
data: Buffer.from([]),
165+
});
166+
167+
const lazerInstruction =
168+
LazerMultisigInstruction.fromInstruction(instruction);
169+
expect(lazerInstruction.program).toBe(MultisigInstructionProgram.Lazer);
170+
});
171+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
MultisigInstruction,
3+
MultisigInstructionProgram,
4+
UNRECOGNIZED_INSTRUCTION,
5+
} from "./index";
6+
import { AnchorAccounts, resolveAccountNames } from "./anchor";
7+
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
8+
import { Idl, BorshInstructionCoder } from "@coral-xyz/anchor";
9+
import lazerIdl from "./idl/lazer.json";
10+
11+
export const LAZER_PROGRAM_ID = new PublicKey(
12+
"pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt"
13+
);
14+
15+
export class LazerMultisigInstruction implements MultisigInstruction {
16+
readonly program = MultisigInstructionProgram.Lazer;
17+
readonly name: string;
18+
readonly args: { [key: string]: any };
19+
readonly accounts: AnchorAccounts;
20+
21+
constructor(
22+
name: string,
23+
args: { [key: string]: any },
24+
accounts: AnchorAccounts
25+
) {
26+
this.name = name;
27+
this.args = args;
28+
this.accounts = accounts;
29+
}
30+
31+
static fromInstruction(
32+
instruction: TransactionInstruction
33+
): LazerMultisigInstruction {
34+
// 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
35+
const idl = lazerIdl as Idl;
36+
37+
const coder = new BorshInstructionCoder(idl);
38+
39+
const deserializedData = coder.decode(instruction.data);
40+
41+
if (deserializedData) {
42+
return new LazerMultisigInstruction(
43+
deserializedData.name,
44+
deserializedData.data,
45+
resolveAccountNames(idl, deserializedData.name, instruction)
46+
);
47+
} else {
48+
return new LazerMultisigInstruction(
49+
UNRECOGNIZED_INSTRUCTION,
50+
{ data: instruction.data },
51+
{ named: {}, remaining: instruction.keys }
52+
);
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)