Ver Fonte

add support to execute tx through squads ui (#322)

Daniel Chew há 3 anos atrás
pai
commit
29f53df71c

+ 7 - 7
third_party/pyth/multisig-wh-message-builder/package-lock.json

@@ -14,7 +14,7 @@
         "@ledgerhq/hw-transport-node-hid": "^6.27.2",
         "@project-serum/anchor": "^0.25.0",
         "@solana/web3.js": "^1.53.0",
-        "@sqds/mesh": "^1.0.4",
+        "@sqds/mesh": "^1.0.6",
         "bs58": "^5.0.0",
         "commander": "^9.4.0",
         "ethers": "^5.7.0"
@@ -2271,9 +2271,9 @@
       "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="
     },
     "node_modules/@sqds/mesh": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@sqds/mesh/-/mesh-1.0.4.tgz",
-      "integrity": "sha512-Mj2rMEkKwkq4a9PlHAWVlCOXczflwcnwlit5W97llVVitC0zT5exIHtgmGSPbYgXlx78tEkq+IvQrnrtGdQRqQ==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@sqds/mesh/-/mesh-1.0.6.tgz",
+      "integrity": "sha512-z+x1GjixJm8K3uPwaDebTsssU3B71zJzRCkywmtz2ZZoMvoz9w/C4nY+v7v6Wg/9OTbfSDgcX/Hoo/FlphkWvg==",
       "dependencies": {
         "@project-serum/anchor": "^0.25.0",
         "@solana/web3.js": "^1.53.0",
@@ -9427,9 +9427,9 @@
       }
     },
     "@sqds/mesh": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@sqds/mesh/-/mesh-1.0.4.tgz",
-      "integrity": "sha512-Mj2rMEkKwkq4a9PlHAWVlCOXczflwcnwlit5W97llVVitC0zT5exIHtgmGSPbYgXlx78tEkq+IvQrnrtGdQRqQ==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@sqds/mesh/-/mesh-1.0.6.tgz",
+      "integrity": "sha512-z+x1GjixJm8K3uPwaDebTsssU3B71zJzRCkywmtz2ZZoMvoz9w/C4nY+v7v6Wg/9OTbfSDgcX/Hoo/FlphkWvg==",
       "requires": {
         "@project-serum/anchor": "^0.25.0",
         "@solana/web3.js": "^1.53.0",

+ 1 - 1
third_party/pyth/multisig-wh-message-builder/package.json

@@ -43,7 +43,7 @@
     "@ledgerhq/hw-transport-node-hid": "^6.27.2",
     "@project-serum/anchor": "^0.25.0",
     "@solana/web3.js": "^1.53.0",
-    "@sqds/mesh": "^1.0.4",
+    "@sqds/mesh": "^1.0.6",
     "bs58": "^5.0.0",
     "commander": "^9.4.0",
     "ethers": "^5.7.0"

+ 89 - 46
third_party/pyth/multisig-wh-message-builder/src/index.ts

@@ -110,15 +110,24 @@ program
       options.ledger,
       new PublicKey(options.vaultAddress)
     );
-    const instructions = [
-      await setIsActiveIx(
-        vaultAuthority,
-        vaultAuthority,
-        attesterProgramId,
-        options.active
-      ),
+    const squadIxs: SquadInstruction[] = [
+      {
+        instruction: await setIsActiveIx(
+          vaultAuthority,
+          vaultAuthority,
+          attesterProgramId,
+          options.active
+        ),
+      },
     ];
-    await addInstructionsToTx(squad, options.ledger, txKey, instructions);
+    await addInstructionsToTx(
+      options.cluster,
+      squad,
+      options.ledger,
+      msAccount.publicKey,
+      txKey,
+      squadIxs
+    );
   });
 
 program
@@ -140,11 +149,6 @@ program
     "multisig wallet secret key filepath",
     "keys/key.json"
   )
-  .option(
-    "-m, --message <filepath>",
-    "multisig message account secret key filepath",
-    "keys/message.json"
-  )
   .requiredOption("-t, --tx-pda <address>", "transaction PDA")
   .requiredOption("-u, --rpc-url <url>", "wormhole RPC URL")
   .action((options) => {
@@ -155,7 +159,6 @@ program
       options.ledgerDerivationAccount,
       options.ledgerDerivationChange,
       options.wallet,
-      options.message,
       new PublicKey(options.txPda),
       options.rpcUrl
     );
@@ -166,14 +169,14 @@ program
 program.parse();
 
 // custom solana cluster type
-type Cluster = "devnet" | "mainnet-beta";
+type Cluster = "devnet" | "mainnet";
 type WormholeNetwork = "TESTNET" | "MAINNET";
 
 // solana cluster mapping to wormhole cluster
 const solanaClusterMappingToWormholeNetwork: Record<Cluster, WormholeNetwork> =
   {
     devnet: "TESTNET",
-    "mainnet-beta": "MAINNET",
+    mainnet: "MAINNET",
   };
 
 async function getSquadsClient(
@@ -224,21 +227,36 @@ async function createTx(
   return newTx.publicKey;
 }
 
+type SquadInstruction = {
+  instruction: anchor.web3.TransactionInstruction;
+  authorityIndex?: number;
+  authorityBump?: number;
+  authorityType?: string;
+};
+
 /** Adds the given instructions to the squads transaction at `txKey` and activates the transaction (makes it ready for signing). */
 async function addInstructionsToTx(
+  cluster: Cluster,
   squad: Squads,
   ledger: boolean,
+  vault: PublicKey,
   txKey: PublicKey,
-  instructions: TransactionInstruction[]
+  instructions: SquadInstruction[]
 ) {
   for (let i = 0; i < instructions.length; i++) {
     console.log(
-      `Adding instruction ${i}/${instructions.length} to transaction...`
+      `Adding instruction ${i + 1}/${instructions.length} to transaction...`
     );
     if (ledger) {
       console.log("Please approve the transaction on your ledger device...");
     }
-    await squad.addInstruction(txKey, instructions[i]);
+    await squad.addInstruction(
+      txKey,
+      instructions[i].instruction,
+      instructions[i].authorityIndex,
+      instructions[i].authorityBump,
+      instructions[i].authorityType
+    );
   }
 
   console.log("Activating transaction...");
@@ -246,6 +264,16 @@ async function addInstructionsToTx(
     console.log("Please approve the transaction on your ledger device...");
   await squad.activateTransaction(txKey);
   console.log("Transaction created.");
+  console.log("Approving transaction...");
+  if (ledger)
+    console.log("Please approve the transaction on your ledger device...");
+  await squad.approveTransaction(txKey);
+  console.log("Transaction approved.");
+  console.log(
+    `Tx URL: https://mesh${
+      cluster === "devnet" ? "-devnet" : ""
+    }.squads.so/transactions/${vault.toBase58()}/tx/${txKey.toBase58()}`
+  );
 }
 
 async function setIsActiveIx(
@@ -277,7 +305,7 @@ async function setIsActiveIx(
 
   const isActiveInt = isActive === true ? 1 : 0;
   // first byte is the isActive instruction, second byte is true/false
-  const data = new Buffer([4, isActiveInt]);
+  const data = Buffer.from([4, isActiveInt]);
 
   return {
     keys: [config, opsOwner, payer],
@@ -329,6 +357,22 @@ async function getWormholeMessageIx(
   ];
 }
 
+const getIxAuthority = async (
+  txPda: anchor.web3.PublicKey,
+  index: anchor.BN,
+  programId: anchor.web3.PublicKey
+) => {
+  return anchor.web3.PublicKey.findProgramAddress(
+    [
+      anchor.utils.bytes.utf8.encode("squad"),
+      txPda.toBuffer(),
+      index.toArrayLike(Buffer, "le", 4),
+      anchor.utils.bytes.utf8.encode("ix_authority"),
+    ],
+    programId
+  );
+};
+
 async function createWormholeMsgMultisigTx(
   cluster: Cluster,
   squad: Squads,
@@ -337,7 +381,6 @@ async function createWormholeMsgMultisigTx(
   payload: string
 ) {
   const msAccount = await squad.getMultisig(vault);
-
   const emitter = squad.getAuthorityPDA(
     msAccount.publicKey,
     msAccount.authorityIndex
@@ -346,28 +389,41 @@ async function createWormholeMsgMultisigTx(
 
   const txKey = await createTx(squad, ledger, vault);
 
-  const message = Keypair.generate();
-
-  fs.mkdirSync("keys", { recursive: true });
-  // save message to Uint8 array keypair file called mesage.json
-  fs.writeFileSync(
-    `keys/message-${txKey.toBase58()}.json`,
-    `[${message.secretKey.toString()}]`
+  const [messagePDA, messagePdaBump] = await getIxAuthority(
+    txKey,
+    new anchor.BN(1),
+    squad.multisigProgramId
   );
-  console.log(`Message Address: ${message.publicKey.toBase58()}`);
 
   console.log("Creating wormhole instructions...");
   const wormholeIxs = await getWormholeMessageIx(
     cluster,
     emitter,
     emitter,
-    message.publicKey,
+    messagePDA,
     squad.connection,
     payload
   );
   console.log("Wormhole instructions created.");
 
-  await addInstructionsToTx(squad, ledger, txKey, wormholeIxs);
+  const squadIxs: SquadInstruction[] = [
+    { instruction: wormholeIxs[0] },
+    {
+      instruction: wormholeIxs[1],
+      authorityIndex: 1,
+      authorityBump: messagePdaBump,
+      authorityType: "custom",
+    },
+  ];
+
+  await addInstructionsToTx(
+    cluster,
+    squad,
+    ledger,
+    msAccount.publicKey,
+    txKey,
+    squadIxs
+  );
 }
 
 async function executeMultisigTx(
@@ -377,7 +433,6 @@ async function executeMultisigTx(
   ledgerDerivationAccount: number | undefined,
   ledgerDerivationChange: number | undefined,
   walletPath: string,
-  messagePath: string,
   txPDA: PublicKey,
   rpcUrl: string
 ) {
@@ -398,13 +453,6 @@ async function executeMultisigTx(
     console.log(`Loaded wallet with address: ${wallet.publicKey.toBase58()}`);
   }
 
-  const message = Keypair.fromSecretKey(
-    Uint8Array.from(JSON.parse(fs.readFileSync(messagePath, "ascii")))
-  );
-  console.log(
-    `Loaded message account with address: ${message.publicKey.toBase58()}`
-  );
-
   const squad =
     cluster === "devnet" ? Squads.devnet(wallet) : Squads.mainnet(wallet);
   const msAccount = await squad.getMultisig(vault);
@@ -418,11 +466,6 @@ async function executeMultisigTx(
     txPDA,
     wallet.publicKey
   );
-  executeIx.keys.forEach((key) => {
-    if (key.pubkey.equals(message.publicKey)) {
-      key.isSigner = true;
-    }
-  });
 
   // airdrop 0.1 SOL to emitter if on devnet
   if (cluster === "devnet") {
@@ -457,7 +500,7 @@ async function executeMultisigTx(
   console.log("Sending transaction...");
   if (ledger)
     console.log("Please approve the transaction on your ledger device...");
-  const signature = await provider.sendAndConfirm(executeTx, [message]);
+  const signature = await provider.sendAndConfirm(executeTx);
 
   console.log(
     `Executed tx: https://explorer.solana.com/tx/${signature}${
@@ -497,8 +540,8 @@ async function executeMultisigTx(
   );
   const { vaaBytes } = await response.json();
   console.log(`VAA (Base64): ${vaaBytes}`);
-  const parsedVaa = await parse(vaaBytes);
   console.log(`VAA (Hex): ${Buffer.from(vaaBytes).toString("hex")}`);
+  const parsedVaa = await parse(vaaBytes);
   console.log(`Emitter chain: ${parsedVaa.emitter_chain}`);
   console.log(`Nonce: ${parsedVaa.nonce}`);
   console.log(`Payload: ${Buffer.from(parsedVaa.payload).toString("hex")}`);