Skip to content

Commit aa15b4f

Browse files
committed
feat(governance, lazer): add scripts for setting ecdsa signer for lazer solana program
1 parent df8e20a commit aa15b4f

File tree

4 files changed

+196
-3
lines changed

4 files changed

+196
-3
lines changed

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,4 +972,74 @@ multisigCommand(
972972
);
973973
});
974974

975+
multisigCommand(
976+
"upgrade-program-and-set-trusted-ecdsa-signer",
977+
"Upgrade the Lazer program and set a trusted ECDSA signer",
978+
)
979+
.requiredOption("-b, --buffer <pubkey>", "buffer account for the upgrade")
980+
.requiredOption(
981+
"-s, --signer <address>",
982+
"public address (hex) of the trusted ECDSA signer to add/update",
983+
)
984+
.requiredOption(
985+
"-e, --expiry-time <seconds>",
986+
"expiry time in seconds since Unix epoch. Set to 0 to remove the signer.",
987+
)
988+
.action(async (options: any) => {
989+
const vault = await loadVaultFromOptions(options);
990+
const targetCluster: PythCluster = options.cluster;
991+
992+
const buffer: PublicKey = new PublicKey(options.buffer);
993+
const trustedSigner = Buffer.from(options.signer, "hex");
994+
const expiryTime = new BN(options.expiryTime);
995+
996+
const programId = SOLANA_LAZER_PROGRAM_ID;
997+
const programDataAccount = PublicKey.findProgramAddressSync(
998+
[programId.toBuffer()],
999+
BPF_UPGRADABLE_LOADER,
1000+
)[0];
1001+
1002+
// This is intruction is not in @solana/web3.js, source : https://docs.rs/solana-program/latest/src/solana_program/bpf_loader_upgradeable.rs.html#200
1003+
const upgradeInstruction: TransactionInstruction = {
1004+
programId: BPF_UPGRADABLE_LOADER,
1005+
// 4-bytes instruction discriminator, got it from https://docs.rs/solana-program/latest/src/solana_program/loader_upgradeable_instruction.rs.html#104
1006+
data: Buffer.from([3, 0, 0, 0]),
1007+
keys: [
1008+
{ pubkey: programDataAccount, isSigner: false, isWritable: true },
1009+
{ pubkey: programId, isSigner: false, isWritable: true },
1010+
{ pubkey: buffer, isSigner: false, isWritable: true },
1011+
{ pubkey: vault.wallet.publicKey, isSigner: false, isWritable: true },
1012+
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1013+
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1014+
{
1015+
pubkey: await vault.getVaultAuthorityPDA(targetCluster),
1016+
isSigner: true,
1017+
isWritable: false,
1018+
},
1019+
],
1020+
};
1021+
1022+
// Create Anchor program instance
1023+
const lazerProgram = new Program(
1024+
lazerIdl as Idl,
1025+
SOLANA_LAZER_PROGRAM_ID,
1026+
vault.getAnchorProvider(),
1027+
);
1028+
1029+
// Use Anchor to create the instruction
1030+
const updateSignerInstruction = await lazerProgram.methods
1031+
.updateEcdsaSigner(trustedSigner, expiryTime)
1032+
.accounts({
1033+
topAuthority: await vault.getVaultAuthorityPDA(targetCluster),
1034+
storage: SOLANA_STORAGE_ID,
1035+
})
1036+
.instruction();
1037+
1038+
await vault.proposeInstructions(
1039+
[upgradeInstruction, updateSignerInstruction],
1040+
targetCluster,
1041+
DEFAULT_PRIORITY_FEE_CONFIG,
1042+
);
1043+
});
1044+
9751045
program.parse();
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as anchor from "@coral-xyz/anchor";
2+
import { Program } from "@coral-xyz/anchor";
3+
import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
4+
import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
5+
import yargs from "yargs/yargs";
6+
import { readFileSync } from "fs";
7+
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
8+
9+
// Add a trusted signer or change its expiry time.
10+
//
11+
// Example:
12+
// pnpm ts-node scripts/add_ecdsa_signer.ts --url 'https://api.testnet.solana.com' \
13+
// --keypair-path .../key.json --trusted-signer b8d50f0bae75bf6e03c104903d7c3afc4a6596da \
14+
// --expiry-time-seconds 2057930841
15+
async function main() {
16+
let argv = await yargs(process.argv.slice(2))
17+
.options({
18+
url: { type: "string", demandOption: true },
19+
"keypair-path": { type: "string", demandOption: true },
20+
"trusted-signer": { type: "string", demandOption: true },
21+
"expiry-time-seconds": { type: "number", demandOption: true },
22+
})
23+
.parse();
24+
25+
const keypair = anchor.web3.Keypair.fromSecretKey(
26+
new Uint8Array(JSON.parse(readFileSync(argv.keypairPath, "ascii"))),
27+
);
28+
29+
const wallet = new NodeWallet(keypair);
30+
const connection = new anchor.web3.Connection(argv.url, {
31+
commitment: "confirmed",
32+
});
33+
const provider = new anchor.AnchorProvider(connection, wallet);
34+
35+
const program: Program<PythLazerSolanaContract> = new Program(
36+
pythLazerSolanaContractIdl as PythLazerSolanaContract,
37+
provider,
38+
);
39+
40+
await program.methods
41+
.updateEcdsaSigner(
42+
[...Buffer.from(argv.trustedSigner, "hex")],
43+
new anchor.BN(argv.expiryTimeSeconds),
44+
)
45+
.accounts({
46+
payer: wallet.publicKey,
47+
})
48+
.rpc();
49+
console.log("signer updated");
50+
}
51+
52+
main();

lazer/contracts/solana/scripts/check_trusted_signer.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,37 @@ async function main() {
4646
console.log("\nTrusted Signers:");
4747
console.log("----------------");
4848

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()}`);
49+
const trustedSigners = storage.trustedSigners.slice(
50+
0,
51+
storage.numTrustedSigners,
52+
);
53+
for (const signer of trustedSigners) {
54+
console.log(
55+
`\nPublic Key: ${(signer.pubkey as anchor.web3.PublicKey).toBase58()}`,
56+
);
57+
console.log(
58+
`Expires At: ${new Date(
59+
signer.expiresAt.toNumber() * 1000,
60+
).toISOString()}`,
61+
);
62+
console.log(
63+
`Active: ${
64+
signer.expiresAt.toNumber() > Date.now() / 1000 ? "Yes" : "No"
65+
}`,
66+
);
67+
}
68+
69+
console.log("\nTrusted ECDSA Signers:");
70+
console.log("----------------");
71+
72+
const trustedEcdsaSigners = storage.trustedEcdsaSigners.slice(
73+
0,
74+
storage.numTrustedEcdsaSigners,
75+
);
76+
for (const signer of trustedEcdsaSigners) {
77+
console.log(
78+
`\nPublic Address: ${Buffer.from(signer.pubkey as number[]).toString("hex")}`,
79+
);
5280
console.log(
5381
`Expires At: ${new Date(
5482
signer.expiresAt.toNumber() * 1000,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as anchor from "@coral-xyz/anchor";
2+
import { Program } from "@coral-xyz/anchor";
3+
import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
4+
import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
5+
import yargs from "yargs/yargs";
6+
import { readFileSync } from "fs";
7+
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
8+
9+
async function main() {
10+
let argv = await yargs(process.argv.slice(2))
11+
.options({
12+
url: { type: "string", demandOption: true },
13+
"keypair-path": { type: "string", demandOption: true },
14+
message: { type: "string", demandOption: true },
15+
})
16+
.parse();
17+
18+
const keypair = anchor.web3.Keypair.fromSecretKey(
19+
new Uint8Array(JSON.parse(readFileSync(argv.keypairPath, "ascii"))),
20+
);
21+
22+
const wallet = new NodeWallet(keypair);
23+
const connection = new anchor.web3.Connection(argv.url, {
24+
commitment: "confirmed",
25+
});
26+
const provider = new anchor.AnchorProvider(connection, wallet);
27+
28+
const program: Program<PythLazerSolanaContract> = new Program(
29+
pythLazerSolanaContractIdl as PythLazerSolanaContract,
30+
provider,
31+
);
32+
33+
await program.methods
34+
.verifyEcdsaMessage(Buffer.from(argv.message, "hex"))
35+
.accounts({
36+
payer: wallet.publicKey,
37+
})
38+
.rpc();
39+
40+
console.log("message is valid");
41+
}
42+
43+
main();

0 commit comments

Comments
 (0)