Skip to content

Commit afcb132

Browse files
authored
feat(xc_admin): implement set trusted signer instruction proposal for lazer (#2253)
* export lazer program id * bump * export storage id * add set-trusted-signer proposal instruction * add script to check trusted signer * fix constants * use anchor * precommit
1 parent 551c66a commit afcb132

File tree

9 files changed

+130
-11
lines changed

9 files changed

+130
-11
lines changed

governance/xc_admin/packages/xc_admin_cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@pythnetwork/pyth-solana-receiver": "workspace:*",
2828
"@pythnetwork/solana-utils": "workspace:^",
2929
"@pythnetwork/xc-admin-common": "workspace:*",
30+
"@pythnetwork/pyth-lazer-sdk": "workspace:*",
3031
"@solana/spl-token": "^0.3.7",
3132
"@solana/web3.js": "^1.73.0",
3233
"@sqds/mesh": "^1.0.6",

governance/xc_admin/packages/xc_admin_cli/src/index.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Program } from "@coral-xyz/anchor";
1+
import { Program, BN, Idl } from "@coral-xyz/anchor";
22
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
33
import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
44
import { TOKEN_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token";
@@ -47,13 +47,18 @@ import {
4747
getProposalInstructions,
4848
idlSetBuffer,
4949
isPriceStorePublisherInitialized,
50+
lazerIdl,
5051
} from "@pythnetwork/xc-admin-common";
5152

5253
import {
5354
pythSolanaReceiverIdl,
5455
getConfigPda,
5556
DEFAULT_RECEIVER_PROGRAM_ID,
5657
} from "@pythnetwork/pyth-solana-receiver";
58+
import {
59+
SOLANA_LAZER_PROGRAM_ID,
60+
SOLANA_STORAGE_ID,
61+
} from "@pythnetwork/pyth-lazer-sdk";
5762

5863
import { LedgerNodeWallet } from "./ledger";
5964
import {
@@ -924,4 +929,46 @@ multisigCommand("execute-add-and-delete", "Execute a roster change proposal")
924929
await vault.squad.executeTransaction(proposal);
925930
});
926931

932+
multisigCommand(
933+
"set-trusted-signer",
934+
"Set a trusted signer for the Lazer program"
935+
)
936+
.requiredOption(
937+
"-s, --signer <pubkey>",
938+
"public key of the trusted signer to add/update"
939+
)
940+
.requiredOption(
941+
"-e, --expiry-time <seconds>",
942+
"expiry time in seconds since Unix epoch. Set to 0 to remove the signer."
943+
)
944+
.action(async (options: any) => {
945+
const vault = await loadVaultFromOptions(options);
946+
const targetCluster: PythCluster = options.cluster;
947+
948+
const trustedSigner = new PublicKey(options.signer);
949+
const expiryTime = new BN(options.expiryTime);
950+
951+
// Create Anchor program instance
952+
const lazerProgram = new Program(
953+
lazerIdl as Idl,
954+
SOLANA_LAZER_PROGRAM_ID,
955+
vault.getAnchorProvider()
956+
);
957+
958+
// Use Anchor to create the instruction
959+
const updateInstruction = await lazerProgram.methods
960+
.update(trustedSigner, expiryTime)
961+
.accounts({
962+
authority: await vault.getVaultAuthorityPDA(targetCluster),
963+
storage: SOLANA_STORAGE_ID,
964+
})
965+
.instruction();
966+
967+
await vault.proposeInstructions(
968+
[updateInstruction],
969+
targetCluster,
970+
DEFAULT_PRIORITY_FEE_CONFIG
971+
);
972+
});
973+
927974
program.parse();

governance/xc_admin/packages/xc_admin_common/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"ethers": "^5.7.2",
4141
"lodash": "^4.17.21",
4242
"message_buffer": "workspace:^",
43+
"@pythnetwork/pyth-lazer-sdk": "workspace:*",
4344
"typescript": "^4.9.4"
4445
},
4546
"devDependencies": {

governance/xc_admin/packages/xc_admin_common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from "./executor";
1313
export * from "./chains";
1414
export * from "./deterministic_stake_accounts";
1515
export * from "./price_store";
16+
export { default as lazerIdl } from "./multisig_transaction/idl/lazer.json";

governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
88
import { Idl, BorshInstructionCoder } from "@coral-xyz/anchor";
99
import lazerIdl from "./idl/lazer.json";
1010

11-
export const LAZER_PROGRAM_ID = new PublicKey(
12-
"pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt"
13-
);
14-
1511
export class LazerMultisigInstruction implements MultisigInstruction {
1612
readonly program = MultisigInstructionProgram.Lazer;
1713
readonly name: string;

governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/index.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@ import {
2727
PRICE_STORE_PROGRAM_ID,
2828
PriceStoreMultisigInstruction,
2929
} from "../price_store";
30-
import {
31-
LazerMultisigInstruction,
32-
LAZER_PROGRAM_ID,
33-
} from "./LazerMultisigInstruction";
30+
import { LazerMultisigInstruction } from "./LazerMultisigInstruction";
31+
import { SOLANA_LAZER_PROGRAM_ID } from "@pythnetwork/pyth-lazer-sdk";
3432

3533
export const UNRECOGNIZED_INSTRUCTION = "unrecognizedInstruction";
3634
export enum MultisigInstructionProgram {
@@ -168,7 +166,7 @@ export class MultisigParser {
168166
return SolanaStakingMultisigInstruction.fromTransactionInstruction(
169167
instruction
170168
);
171-
} else if (instruction.programId.equals(LAZER_PROGRAM_ID)) {
169+
} else if (instruction.programId.equals(SOLANA_LAZER_PROGRAM_ID)) {
172170
return LazerMultisigInstruction.fromInstruction(instruction);
173171
} else {
174172
return UnrecognizedProgram.fromTransactionInstruction(instruction);

lazer/contracts/solana/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"test:anchor": "CARGO_TARGET_DIR=\"$PWD/target\" anchor test",
99
"test": "pnpm run test:format && pnpm run test:anchor",
1010
"setup": "anchor build && pnpm ts-node scripts/setup.ts",
11-
"migrate_from_0_1_0": "pnpm ts-node scripts/migrate_from_0_1_0.ts"
11+
"migrate_from_0_1_0": "pnpm ts-node scripts/migrate_from_0_1_0.ts",
12+
"check_trusted_signer": "pnpm ts-node scripts/check_trusted_signer.ts"
1213
},
1314
"dependencies": {
1415
"@coral-xyz/anchor": "^0.30.1"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as anchor from "@coral-xyz/anchor";
2+
import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
3+
import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
4+
import yargs from "yargs/yargs";
5+
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
6+
7+
const parser = yargs(process.argv.slice(2)).options({
8+
url: {
9+
type: "string",
10+
demandOption: true,
11+
desc: "RPC URL to use",
12+
},
13+
"storage-id": {
14+
type: "string",
15+
demandOption: true,
16+
desc: "Storage account ID to check",
17+
},
18+
});
19+
20+
async function main() {
21+
const argv = await parser.argv;
22+
23+
// Setup anchor provider
24+
const connection = new anchor.web3.Connection(argv.url);
25+
const provider = new anchor.AnchorProvider(
26+
connection,
27+
new NodeWallet(anchor.web3.Keypair.generate()), // Dummy wallet since we're only reading
28+
{ commitment: "confirmed" }
29+
);
30+
anchor.setProvider(provider);
31+
32+
const program: anchor.Program<PythLazerSolanaContract> = new anchor.Program(
33+
pythLazerSolanaContractIdl as PythLazerSolanaContract,
34+
provider
35+
);
36+
37+
// Fetch and decode storage account
38+
const storageId = new anchor.web3.PublicKey(argv["storage-id"]);
39+
const storage = await program.account.storage.fetch(storageId);
40+
41+
// Print storage info
42+
console.log("Storage Account Info:");
43+
console.log("--------------------");
44+
console.log("Top Authority:", storage.topAuthority.toBase58());
45+
console.log("Treasury:", storage.treasury.toBase58());
46+
console.log("\nTrusted Signers:");
47+
console.log("----------------");
48+
49+
for (const signer of storage.trustedSigners) {
50+
if (signer.pubkey.equals(anchor.web3.PublicKey.default)) continue;
51+
console.log(`\nPublic Key: ${signer.pubkey.toBase58()}`);
52+
console.log(
53+
`Expires At: ${new Date(
54+
signer.expiresAt.toNumber() * 1000
55+
).toISOString()}`
56+
);
57+
console.log(
58+
`Active: ${
59+
signer.expiresAt.toNumber() > Date.now() / 1000 ? "Yes" : "No"
60+
}`
61+
);
62+
}
63+
}
64+
65+
main().catch((err) => {
66+
console.error(err);
67+
process.exit(1);
68+
});

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)