| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- import Squads, { getIxAuthorityPDA, getTxPDA } from "@sqds/mesh";
- import {
- PublicKey,
- Transaction,
- TransactionInstruction,
- } from "@solana/web3.js";
- import { BN } from "bn.js";
- import { AnchorProvider } from "@project-serum/anchor";
- import {
- createWormholeProgramInterface,
- getPostMessageAccounts,
- } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole";
- import { ExecutePostedVaa } from "./governance_payload/ExecutePostedVaa";
- type SquadInstruction = {
- instruction: TransactionInstruction;
- authorityIndex?: number;
- authorityBump?: number;
- authorityType?: string;
- };
- /**
- * Propose an array of `TransactionInstructions` as a proposal
- * @param squad Squads client
- * @param vault vault public key (the id of the multisig where these instructions should be proposed)
- * @param instructions instructions that will be proposed
- * @param remote whether the instructions should be executed in the chain of the multisig or remotely on Pythnet
- * @returns the newly created proposal's pubkey
- */
- export async function proposeInstructions(
- squad: Squads,
- vault: PublicKey,
- instructions: TransactionInstruction[],
- remote: boolean,
- wormholeAddress?: PublicKey
- ): Promise<PublicKey> {
- const msAccount = await squad.getMultisig(vault);
- let txToSend: Transaction[] = [];
- const createProposal = new Transaction().add(
- await squad.buildCreateTransaction(
- msAccount.publicKey,
- msAccount.authorityIndex,
- msAccount.transactionIndex + 1
- )
- );
- const newProposalAddress = getTxPDA(
- vault,
- new BN(msAccount.transactionIndex + 1),
- squad.multisigProgramId
- )[0];
- txToSend.push(createProposal);
- if (remote) {
- if (!wormholeAddress) {
- throw new Error("Need wormhole address");
- }
- for (let i = 0; i < instructions.length; i++) {
- const squadIx = await wrapAsRemoteInstruction(
- squad,
- vault,
- newProposalAddress,
- instructions[i],
- i,
- wormholeAddress
- );
- txToSend.push(
- new Transaction().add(
- await squad.buildAddInstruction(
- vault,
- newProposalAddress,
- squadIx.instruction,
- i + 1,
- squadIx.authorityIndex,
- squadIx.authorityBump,
- squadIx.authorityType
- )
- )
- );
- }
- } else {
- for (let i = 0; i < instructions.length; i++) {
- txToSend.push(
- new Transaction().add(
- await squad.buildAddInstruction(
- vault,
- newProposalAddress,
- instructions[i],
- i + 1
- )
- )
- );
- }
- }
- txToSend.push(
- new Transaction().add(
- await squad.buildActivateTransaction(vault, newProposalAddress)
- )
- );
- txToSend.push(
- new Transaction().add(
- await squad.buildApproveTransaction(vault, newProposalAddress)
- )
- );
- await new AnchorProvider(
- squad.connection,
- squad.wallet,
- AnchorProvider.defaultOptions()
- ).sendAll(
- txToSend.map((tx) => {
- return { tx, signers: [] };
- })
- );
- return newProposalAddress;
- }
- /**
- * Wrap `instruction` in a Wormhole message for remote execution
- * @param squad Squads client
- * @param vault vault public key (the id of the multisig where these instructions should be proposed)
- * @param proposalAddress address of the proposal
- * @param instruction instruction to be wrapped in a Wormhole message
- * @param instructionIndex index of the instruction within the proposal
- * @param wormholeAddress address of the Wormhole bridge
- * @returns an instruction to be proposed
- */
- export async function wrapAsRemoteInstruction(
- squad: Squads,
- vault: PublicKey,
- proposalAddress: PublicKey,
- instruction: TransactionInstruction,
- instructionIndex: number,
- wormholeAddress: PublicKey
- ): Promise<SquadInstruction> {
- const emitter = squad.getAuthorityPDA(vault, 0);
- const [messagePDA, messagePdaBump] = getIxAuthorityPDA(
- proposalAddress,
- new BN(instructionIndex),
- squad.multisigProgramId
- );
- const provider = new AnchorProvider(
- squad.connection,
- squad.wallet,
- AnchorProvider.defaultOptions()
- );
- const wormholeProgram = createWormholeProgramInterface(
- wormholeAddress,
- provider
- );
- const buffer = new ExecutePostedVaa("pythnet", [instruction]);
- const accounts = getPostMessageAccounts(
- wormholeAddress,
- emitter,
- emitter,
- messagePDA
- );
- return {
- instruction: await wormholeProgram.methods
- .postMessage(0, buffer, 0)
- .accounts(accounts)
- .instruction(),
- authorityIndex: instructionIndex,
- authorityBump: messagePdaBump,
- authorityType: "custom",
- };
- }
|