Forráskód Böngészése

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
Daniel Chew 10 hónapja
szülő
commit
afcb132029

+ 1 - 0
governance/xc_admin/packages/xc_admin_cli/package.json

@@ -27,6 +27,7 @@
     "@pythnetwork/pyth-solana-receiver": "workspace:*",
     "@pythnetwork/solana-utils": "workspace:^",
     "@pythnetwork/xc-admin-common": "workspace:*",
+    "@pythnetwork/pyth-lazer-sdk": "workspace:*",
     "@solana/spl-token": "^0.3.7",
     "@solana/web3.js": "^1.73.0",
     "@sqds/mesh": "^1.0.6",

+ 48 - 1
governance/xc_admin/packages/xc_admin_cli/src/index.ts

@@ -1,4 +1,4 @@
-import { Program } from "@coral-xyz/anchor";
+import { Program, BN, Idl } from "@coral-xyz/anchor";
 import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
 import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
 import { TOKEN_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token";
@@ -47,6 +47,7 @@ import {
   getProposalInstructions,
   idlSetBuffer,
   isPriceStorePublisherInitialized,
+  lazerIdl,
 } from "@pythnetwork/xc-admin-common";
 
 import {
@@ -54,6 +55,10 @@ import {
   getConfigPda,
   DEFAULT_RECEIVER_PROGRAM_ID,
 } from "@pythnetwork/pyth-solana-receiver";
+import {
+  SOLANA_LAZER_PROGRAM_ID,
+  SOLANA_STORAGE_ID,
+} from "@pythnetwork/pyth-lazer-sdk";
 
 import { LedgerNodeWallet } from "./ledger";
 import {
@@ -924,4 +929,46 @@ multisigCommand("execute-add-and-delete", "Execute a roster change proposal")
     await vault.squad.executeTransaction(proposal);
   });
 
+multisigCommand(
+  "set-trusted-signer",
+  "Set a trusted signer for the Lazer program"
+)
+  .requiredOption(
+    "-s, --signer <pubkey>",
+    "public key of the trusted signer to add/update"
+  )
+  .requiredOption(
+    "-e, --expiry-time <seconds>",
+    "expiry time in seconds since Unix epoch. Set to 0 to remove the signer."
+  )
+  .action(async (options: any) => {
+    const vault = await loadVaultFromOptions(options);
+    const targetCluster: PythCluster = options.cluster;
+
+    const trustedSigner = new PublicKey(options.signer);
+    const expiryTime = new BN(options.expiryTime);
+
+    // Create Anchor program instance
+    const lazerProgram = new Program(
+      lazerIdl as Idl,
+      SOLANA_LAZER_PROGRAM_ID,
+      vault.getAnchorProvider()
+    );
+
+    // Use Anchor to create the instruction
+    const updateInstruction = await lazerProgram.methods
+      .update(trustedSigner, expiryTime)
+      .accounts({
+        authority: await vault.getVaultAuthorityPDA(targetCluster),
+        storage: SOLANA_STORAGE_ID,
+      })
+      .instruction();
+
+    await vault.proposeInstructions(
+      [updateInstruction],
+      targetCluster,
+      DEFAULT_PRIORITY_FEE_CONFIG
+    );
+  });
+
 program.parse();

+ 1 - 0
governance/xc_admin/packages/xc_admin_common/package.json

@@ -40,6 +40,7 @@
     "ethers": "^5.7.2",
     "lodash": "^4.17.21",
     "message_buffer": "workspace:^",
+    "@pythnetwork/pyth-lazer-sdk": "workspace:*",
     "typescript": "^4.9.4"
   },
   "devDependencies": {

+ 1 - 0
governance/xc_admin/packages/xc_admin_common/src/index.ts

@@ -13,3 +13,4 @@ export * from "./executor";
 export * from "./chains";
 export * from "./deterministic_stake_accounts";
 export * from "./price_store";
+export { default as lazerIdl } from "./multisig_transaction/idl/lazer.json";

+ 0 - 4
governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts

@@ -8,10 +8,6 @@ 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;

+ 3 - 5
governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/index.ts

@@ -27,10 +27,8 @@ import {
   PRICE_STORE_PROGRAM_ID,
   PriceStoreMultisigInstruction,
 } from "../price_store";
-import {
-  LazerMultisigInstruction,
-  LAZER_PROGRAM_ID,
-} from "./LazerMultisigInstruction";
+import { LazerMultisigInstruction } from "./LazerMultisigInstruction";
+import { SOLANA_LAZER_PROGRAM_ID } from "@pythnetwork/pyth-lazer-sdk";
 
 export const UNRECOGNIZED_INSTRUCTION = "unrecognizedInstruction";
 export enum MultisigInstructionProgram {
@@ -168,7 +166,7 @@ export class MultisigParser {
       return SolanaStakingMultisigInstruction.fromTransactionInstruction(
         instruction
       );
-    } else if (instruction.programId.equals(LAZER_PROGRAM_ID)) {
+    } else if (instruction.programId.equals(SOLANA_LAZER_PROGRAM_ID)) {
       return LazerMultisigInstruction.fromInstruction(instruction);
     } else {
       return UnrecognizedProgram.fromTransactionInstruction(instruction);

+ 2 - 1
lazer/contracts/solana/package.json

@@ -8,7 +8,8 @@
     "test:anchor": "CARGO_TARGET_DIR=\"$PWD/target\" anchor test",
     "test": "pnpm run test:format && pnpm run test:anchor",
     "setup": "anchor build && pnpm ts-node scripts/setup.ts",
-    "migrate_from_0_1_0": "pnpm ts-node scripts/migrate_from_0_1_0.ts"
+    "migrate_from_0_1_0": "pnpm ts-node scripts/migrate_from_0_1_0.ts",
+    "check_trusted_signer": "pnpm ts-node scripts/check_trusted_signer.ts"
   },
   "dependencies": {
     "@coral-xyz/anchor": "^0.30.1"

+ 68 - 0
lazer/contracts/solana/scripts/check_trusted_signer.ts

@@ -0,0 +1,68 @@
+import * as anchor from "@coral-xyz/anchor";
+import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
+import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
+import yargs from "yargs/yargs";
+import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
+
+const parser = yargs(process.argv.slice(2)).options({
+  url: {
+    type: "string",
+    demandOption: true,
+    desc: "RPC URL to use",
+  },
+  "storage-id": {
+    type: "string",
+    demandOption: true,
+    desc: "Storage account ID to check",
+  },
+});
+
+async function main() {
+  const argv = await parser.argv;
+
+  // Setup anchor provider
+  const connection = new anchor.web3.Connection(argv.url);
+  const provider = new anchor.AnchorProvider(
+    connection,
+    new NodeWallet(anchor.web3.Keypair.generate()), // Dummy wallet since we're only reading
+    { commitment: "confirmed" }
+  );
+  anchor.setProvider(provider);
+
+  const program: anchor.Program<PythLazerSolanaContract> = new anchor.Program(
+    pythLazerSolanaContractIdl as PythLazerSolanaContract,
+    provider
+  );
+
+  // Fetch and decode storage account
+  const storageId = new anchor.web3.PublicKey(argv["storage-id"]);
+  const storage = await program.account.storage.fetch(storageId);
+
+  // Print storage info
+  console.log("Storage Account Info:");
+  console.log("--------------------");
+  console.log("Top Authority:", storage.topAuthority.toBase58());
+  console.log("Treasury:", storage.treasury.toBase58());
+  console.log("\nTrusted Signers:");
+  console.log("----------------");
+
+  for (const signer of storage.trustedSigners) {
+    if (signer.pubkey.equals(anchor.web3.PublicKey.default)) continue;
+    console.log(`\nPublic Key: ${signer.pubkey.toBase58()}`);
+    console.log(
+      `Expires At: ${new Date(
+        signer.expiresAt.toNumber() * 1000
+      ).toISOString()}`
+    );
+    console.log(
+      `Active: ${
+        signer.expiresAt.toNumber() > Date.now() / 1000 ? "Yes" : "No"
+      }`
+    );
+  }
+}
+
+main().catch((err) => {
+  console.error(err);
+  process.exit(1);
+});

+ 6 - 0
pnpm-lock.yaml

@@ -1249,6 +1249,9 @@ importers:
       '@pythnetwork/client':
         specifier: ^2.22.0
         version: 2.22.0(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
+      '@pythnetwork/pyth-lazer-sdk':
+        specifier: workspace:*
+        version: link:../../../../lazer/sdk/js
       '@pythnetwork/pyth-solana-receiver':
         specifier: workspace:*
         version: link:../../../../target_chains/solana/sdk/js/pyth_solana_receiver
@@ -1291,6 +1294,9 @@ importers:
       '@pythnetwork/client':
         specifier: ^2.22.0
         version: 2.22.0(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)
+      '@pythnetwork/pyth-lazer-sdk':
+        specifier: workspace:*
+        version: link:../../../../lazer/sdk/js
       '@pythnetwork/pyth-solana-receiver':
         specifier: workspace:*
         version: link:../../../../target_chains/solana/sdk/js/pyth_solana_receiver