diff --git a/governance/xc_admin/packages/xc_admin_cli/package.json b/governance/xc_admin/packages/xc_admin_cli/package.json index 11b1dd1259..f6df93ad8b 100644 --- a/governance/xc_admin/packages/xc_admin_cli/package.json +++ b/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", diff --git a/governance/xc_admin/packages/xc_admin_cli/src/index.ts b/governance/xc_admin/packages/xc_admin_cli/src/index.ts index cc1efbd2fe..69a4768e0e 100644 --- a/governance/xc_admin/packages/xc_admin_cli/src/index.ts +++ b/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 ", + "public key of the trusted signer to add/update" + ) + .requiredOption( + "-e, --expiry-time ", + "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(); diff --git a/governance/xc_admin/packages/xc_admin_common/package.json b/governance/xc_admin/packages/xc_admin_common/package.json index faf5458fd0..ae53311baa 100644 --- a/governance/xc_admin/packages/xc_admin_common/package.json +++ b/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": { diff --git a/governance/xc_admin/packages/xc_admin_common/src/index.ts b/governance/xc_admin/packages/xc_admin_common/src/index.ts index 3ffc5923f3..24c55fbe2c 100644 --- a/governance/xc_admin/packages/xc_admin_common/src/index.ts +++ b/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"; diff --git a/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts b/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts index 29a2758d42..7787c3fd4a 100644 --- a/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/LazerMultisigInstruction.ts +++ b/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; diff --git a/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/index.ts b/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/index.ts index 52fc7287df..4b7159ea22 100644 --- a/governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/index.ts +++ b/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); diff --git a/lazer/contracts/solana/package.json b/lazer/contracts/solana/package.json index cb52148d7b..095e0619b1 100644 --- a/lazer/contracts/solana/package.json +++ b/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" diff --git a/lazer/contracts/solana/scripts/check_trusted_signer.ts b/lazer/contracts/solana/scripts/check_trusted_signer.ts new file mode 100644 index 0000000000..8dac4ed2c1 --- /dev/null +++ b/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 = 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); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83abc81069..33c7cd3375 100644 --- a/pnpm-lock.yaml +++ b/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