-
Notifications
You must be signed in to change notification settings - Fork 222
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(xc-admin): add support for deserializing lazer instructions in x…
…c-admin (#2246) * feat: add support for deserializing lazer instructions in xc-admin * fix error * add test
- Loading branch information
Showing
4 changed files
with
542 additions
and
0 deletions.
There are no files selected for viewing
171 changes: 171 additions & 0 deletions
171
governance/xc_admin/packages/xc_admin_common/src/__tests__/LazerMultisigInstruction.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); |
55 changes: 55 additions & 0 deletions
55
...ce/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.