Xc admin/propose instructions (#463)

* Add encoder

* Checkpoint

* Refactor

* Cleanup

* Add comment

* Cleanup
This commit is contained in:
guibescos 2023-01-09 21:46:03 -06:00 committed by GitHub
parent 097943f657
commit 2a961d5853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 177 additions and 0 deletions

View File

@ -1,2 +1,3 @@
export * from "./multisig";
export * from "./propose";
export * from "./governance_payload";

View File

@ -0,0 +1,176 @@
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 { encodeExecutePostedVaa } 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,
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
)
)
);
}
}
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 = encodeExecutePostedVaa({
targetChainId: "pythnet",
instructions: [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",
};
}