瀏覽代碼

feat: support initializing stake accounts (#1819)

* feat: support initializing stake accounts

* cleanup

* fix

* fix

* remove console log

* cleanup

* fix

* simplify

* go

* fix: add initialize instruction parser
guibescos 1 年之前
父節點
當前提交
371286dea7

+ 63 - 0
governance/xc_admin/packages/xc_admin_cli/src/index.ts

@@ -35,6 +35,7 @@ import {
   PROGRAM_AUTHORITY_ESCROW,
   getMultisigCluster,
   getProposalInstructions,
+  findDetermisticStakeAccountAddress,
 } from "@pythnetwork/xc-admin-common";
 
 import {
@@ -408,6 +409,68 @@ multisigCommand(
     );
   });
 
+multisigCommand(
+  "initialize-stake-accounts",
+  "Initialize stake accounts and assign them to the given vote accounts"
+)
+  .requiredOption(
+    "-d, --vote-pubkeys <comma_separated_voter_pubkeys>",
+    "vote account to delegate to"
+  )
+  .action(async (options: any) => {
+    const vault = await loadVaultFromOptions(options);
+    const cluster: PythCluster = options.cluster;
+    const authorizedPubkey: PublicKey = await vault.getVaultAuthorityPDA(
+      cluster
+    );
+
+    const votePubkeys: PublicKey[] = options.votePubkeys
+      ? options.votePubkeys.split(",").map((m: string) => new PublicKey(m))
+      : [];
+
+    const instructions: TransactionInstruction[] = [];
+
+    for (const votePubkey of votePubkeys) {
+      const [stakePubkey, seed] = await findDetermisticStakeAccountAddress(
+        authorizedPubkey,
+        votePubkey
+      );
+      instructions.push(
+        SystemProgram.createAccountWithSeed({
+          basePubkey: authorizedPubkey,
+          seed: seed,
+          fromPubkey: authorizedPubkey,
+          newAccountPubkey: stakePubkey,
+          lamports: 100000 * LAMPORTS_PER_SOL,
+          space: StakeProgram.space,
+          programId: StakeProgram.programId,
+        })
+      );
+      instructions.push(
+        StakeProgram.initialize({
+          stakePubkey,
+          authorized: {
+            staker: authorizedPubkey,
+            withdrawer: authorizedPubkey,
+          },
+        })
+      );
+      instructions.push(
+        StakeProgram.delegate({
+          stakePubkey,
+          authorizedPubkey,
+          votePubkey,
+        }).instructions[0]
+      );
+    }
+
+    await vault.proposeInstructions(
+      instructions,
+      cluster,
+      DEFAULT_PRIORITY_FEE_CONFIG
+    );
+  });
+
 multisigCommand(
   "init-price",
   "Init price (useful for changing the exponent), only to be used on unused price feeds"

+ 14 - 0
governance/xc_admin/packages/xc_admin_common/src/deterministic_stake_accounts.ts

@@ -0,0 +1,14 @@
+import { PublicKey, StakeProgram } from "@solana/web3.js";
+
+export async function findDetermisticStakeAccountAddress(
+  basePubkey: PublicKey,
+  votePubkey: PublicKey
+): Promise<[PublicKey, string]> {
+  const seed: string = votePubkey.toBuffer().toString("hex").slice(0, 32);
+  const address: PublicKey = await PublicKey.createWithSeed(
+    basePubkey,
+    seed,
+    StakeProgram.programId
+  );
+  return [address, seed];
+}

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

@@ -11,3 +11,4 @@ export * from "./cranks";
 export * from "./message_buffer";
 export * from "./executor";
 export * from "./chains";
+export * from "./deterministic_stake_accounts";

+ 21 - 1
governance/xc_admin/packages/xc_admin_common/src/multisig_transaction/SolanaStakingMultisigInstruction.ts

@@ -77,9 +77,29 @@ export class SolanaStakingMultisigInstruction implements MultisigInstruction {
               remaining: [],
             }
           );
+        case "Initialize":
+          const decodedInitialize =
+            StakeInstruction.decodeInitialize(instruction);
+          return new SolanaStakingMultisigInstruction(
+            "Initialize",
+            {
+              authorized: decodedInitialize.authorized,
+              lockup: decodedInitialize.lockup,
+            },
+            {
+              named: {
+                stakePubkey: {
+                  pubkey: decodedInitialize.stakePubkey,
+                  isSigner: false,
+                  isWritable: true,
+                },
+              },
+              remaining: [],
+            }
+          );
         case "Authorize":
         case "AuthorizeWithSeed":
-        case "Initialize":
+
         case "Merge":
         case "Split":
         case "Withdraw":