From 843925b8943392a5d304310ee21ea39b13182afa Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Alapont Date: Tue, 17 Jan 2023 15:35:08 -0600 Subject: [PATCH 1/5] Add wormhole and fix some bugs --- governance/xc-admin/package-lock.json | 9 ++-- .../packages/crank-executor/package.json | 1 + .../packages/crank-executor/src/index.ts | 52 +++++++++++++++++-- .../packages/xc-admin-cli/src/index.ts | 31 ++++++++++- .../packages/xc-admin-common/src/propose.ts | 38 ++++++++++---- 5 files changed, 113 insertions(+), 18 deletions(-) diff --git a/governance/xc-admin/package-lock.json b/governance/xc-admin/package-lock.json index 33a6a1dc75..0278a78dac 100644 --- a/governance/xc-admin/package-lock.json +++ b/governance/xc-admin/package-lock.json @@ -1948,7 +1948,8 @@ }, "node_modules/@certusone/wormhole-sdk": { "version": "0.9.9", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.9.9.tgz", + "integrity": "sha512-seausUXqUIvUN19u4ef0VgMXNvyftQHrq5+A8AHHbsk14oBGRbvQ5JqeI+vgtKUMggK8jCaa/ICR1TnD7MW67Q==", "dependencies": { "@certusone/wormhole-sdk-proto-web": "0.0.6", "@certusone/wormhole-sdk-wasm": "^0.0.1", @@ -25188,6 +25189,7 @@ "version": "0.0.0", "license": "ISC", "dependencies": { + "@certusone/wormhole-sdk": "^0.9.9", "@coral-xyz/anchor": "^0.26.0", "@pythnetwork/client": "^2.9.0", "@solana/web3.js": "^1.73.0", @@ -25244,7 +25246,6 @@ "@headlessui/react": "^1.7.7", "@pythnetwork/client": "^2.9.0", "@solana/wallet-adapter-base": "^0.9.20", - "@solana/wallet-adapter-react": "^0.15.28", "@solana/wallet-adapter-react-ui": "^0.9.27", "@solana/wallet-adapter-wallets": "^0.19.10", "@solana/web3.js": "^1.73.0", @@ -26402,6 +26403,8 @@ }, "@certusone/wormhole-sdk": { "version": "0.9.9", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.9.9.tgz", + "integrity": "sha512-seausUXqUIvUN19u4ef0VgMXNvyftQHrq5+A8AHHbsk14oBGRbvQ5JqeI+vgtKUMggK8jCaa/ICR1TnD7MW67Q==", "requires": { "@certusone/wormhole-sdk-proto-web": "0.0.6", "@certusone/wormhole-sdk-wasm": "^0.0.1", @@ -33203,6 +33206,7 @@ "crank-executor": { "version": "file:packages/crank-executor", "requires": { + "@certusone/wormhole-sdk": "^0.9.9", "@coral-xyz/anchor": "^0.26.0", "@pythnetwork/client": "^2.9.0", "@solana/web3.js": "^1.73.0", @@ -41995,7 +41999,6 @@ "@headlessui/react": "^1.7.7", "@pythnetwork/client": "^2.9.0", "@solana/wallet-adapter-base": "^0.9.20", - "@solana/wallet-adapter-react": "*", "@solana/wallet-adapter-react-ui": "^0.9.27", "@solana/wallet-adapter-wallets": "^0.19.10", "@solana/web3.js": "^1.73.0", diff --git a/governance/xc-admin/packages/crank-executor/package.json b/governance/xc-admin/packages/crank-executor/package.json index e303ac489b..78575bef12 100644 --- a/governance/xc-admin/packages/crank-executor/package.json +++ b/governance/xc-admin/packages/crank-executor/package.json @@ -18,6 +18,7 @@ "format": "prettier --write \"src/**/*.ts\"" }, "dependencies": { + "@certusone/wormhole-sdk": "^0.9.9", "@coral-xyz/anchor": "^0.26.0", "@pythnetwork/client": "^2.9.0", "@solana/web3.js": "^1.73.0", diff --git a/governance/xc-admin/packages/crank-executor/src/index.ts b/governance/xc-admin/packages/crank-executor/src/index.ts index 4f767c6387..fa21cf6a72 100644 --- a/governance/xc-admin/packages/crank-executor/src/index.ts +++ b/governance/xc-admin/packages/crank-executor/src/index.ts @@ -1,21 +1,28 @@ import { + AccountMeta, Commitment, Connection, Keypair, PublicKey, SendTransactionError, + SystemProgram, Transaction, } from "@solana/web3.js"; import SquadsMesh, { DEFAULT_MULTISIG_PROGRAM_ID, getIxPDA } from "@sqds/mesh"; import * as fs from "fs"; import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; -import { getProposals } from "xc-admin-common"; +import { getProposals, MultisigParser } from "xc-admin-common"; import BN from "bn.js"; import { AnchorProvider } from "@project-serum/anchor"; import { getPythClusterApiUrl, PythCluster, } from "@pythnetwork/client/lib/cluster"; +import { WormholeMultisigInstruction } from "xc-admin-common/src/multisig_transaction/WormholeMultisigInstruction"; +import { + deriveFeeCollectorKey, + getWormholeBridgeData, +} from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; export function envOrErr(env: string): string { const val = process.env[env]; @@ -42,6 +49,15 @@ async function run() { wallet: new NodeWallet(KEYPAIR), multisigProgramId: DEFAULT_MULTISIG_PROGRAM_ID, }); + const multisigParser = MultisigParser.fromCluster(CLUSTER as PythCluster); + const wormholeFee = ( + await getWormholeBridgeData( + squad.connection, + multisigParser.wormholeBridgeAddress!, + COMMITMENT + ) + ).config.fee; + const proposals = await getProposals(squad, VAULT, undefined, "executeReady"); for (const proposal of proposals) { // If we have previously cancelled because the proposal was failing, don't attempt @@ -51,7 +67,35 @@ async function run() { i <= proposal.instructionIndex; i++ ) { - const transaction = new Transaction().add( + const instructionPda = getIxPDA( + proposal.publicKey, + new BN(i), + squad.multisigProgramId + )[0]; + const instruction = await squad.getInstruction(instructionPda); + const parsedInstruction = multisigParser.parseInstruction({ + programId: instruction.programId, + data: instruction.data as Buffer, + keys: instruction.keys as AccountMeta[], + }); + const transaction = new Transaction(); + + if ( + parsedInstruction instanceof WormholeMultisigInstruction && + parsedInstruction.name == "postMessage" + ) { + transaction.add( + SystemProgram.transfer({ + lamports: wormholeFee, + toPubkey: deriveFeeCollectorKey( + multisigParser.wormholeBridgeAddress! + ), + fromPubkey: squad.wallet.publicKey, + }) + ); + } + + transaction.add( await squad.buildExecuteInstruction( proposal.publicKey, getIxPDA(proposal.publicKey, new BN(i), squad.multisigProgramId)[0] @@ -59,12 +103,14 @@ async function run() { ); try { + console.log("Sending: ", transaction.instructions.length); await new AnchorProvider(squad.connection, squad.wallet, { commitment: COMMITMENT, preflightCommitment: COMMITMENT, - }).sendAndConfirm(transaction, []); + }).sendAndConfirm(transaction, [], { skipPreflight: true }); } catch (error) { // Mark the transaction as cancelled if we failed to run it + console.log(error); if (error instanceof SendTransactionError) { await squad.cancelTransaction(proposal.publicKey); } diff --git a/governance/xc-admin/packages/xc-admin-cli/src/index.ts b/governance/xc-admin/packages/xc-admin-cli/src/index.ts index e2c5091fc9..07aed31895 100644 --- a/governance/xc-admin/packages/xc-admin-cli/src/index.ts +++ b/governance/xc-admin/packages/xc-admin-cli/src/index.ts @@ -1,4 +1,4 @@ -import { Keypair, PublicKey } from "@solana/web3.js"; +import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; import { program } from "commander"; import { PythCluster } from "@pythnetwork/client/lib/cluster"; import { getPythClusterApiUrl } from "@pythnetwork/client/lib/cluster"; @@ -95,4 +95,33 @@ mutlisigCommand( await proposeInstructions(squad, vault, [proposalInstruction], false); }); +mutlisigCommand("propose-test", "Propose test").action(async (options: any) => { + const wallet = new NodeWallet( + Keypair.fromSecretKey( + Uint8Array.from(JSON.parse(fs.readFileSync(options.wallet, "ascii"))) + ) + ); + const cluster: PythCluster = options.cluster; + const vault: PublicKey = new PublicKey(options.vault); + + const squad = SquadsMesh.endpoint(getPythClusterApiUrl(cluster), wallet); + await proposeInstructions( + squad, + vault, + [ + SystemProgram.transfer({ + lamports: 1, + toPubkey: PublicKey.unique(), + fromPubkey: PublicKey.unique(), + }), + SystemProgram.transfer({ + lamports: 1, + toPubkey: PublicKey.unique(), + fromPubkey: PublicKey.unique(), + }), + ], + true, + new PublicKey("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o") + ); +}); program.parse(); diff --git a/governance/xc-admin/packages/xc-admin-common/src/propose.ts b/governance/xc-admin/packages/xc-admin-common/src/propose.ts index 91e8976827..0210208cdf 100644 --- a/governance/xc-admin/packages/xc-admin-common/src/propose.ts +++ b/governance/xc-admin/packages/xc-admin-common/src/propose.ts @@ -3,12 +3,17 @@ import { PublicKey, Transaction, TransactionInstruction, + SYSVAR_RENT_PUBKEY, + SYSVAR_CLOCK_PUBKEY, + SystemProgram, } from "@solana/web3.js"; import { BN } from "bn.js"; import { AnchorProvider } from "@project-serum/anchor"; import { createWormholeProgramInterface, - getPostMessageAccounts, + deriveWormholeBridgeDataKey, + deriveEmitterSequenceKey, + deriveFeeCollectorKey, } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; import { ExecutePostedVaa } from "./governance_payload/ExecutePostedVaa"; @@ -36,7 +41,6 @@ export async function proposeInstructions( ): Promise { const msAccount = await squad.getMultisig(vault); let txToSend: Transaction[] = []; - const createProposal = new Transaction().add( await squad.buildCreateTransaction( msAccount.publicKey, @@ -134,11 +138,11 @@ export async function wrapAsRemoteInstruction( instructionIndex: number, wormholeAddress: PublicKey ): Promise { - const emitter = squad.getAuthorityPDA(vault, 0); + const emitter = squad.getAuthorityPDA(vault, 1); const [messagePDA, messagePdaBump] = getIxAuthorityPDA( proposalAddress, - new BN(instructionIndex), + new BN(instructionIndex + 1), squad.multisigProgramId ); @@ -156,20 +160,32 @@ export async function wrapAsRemoteInstruction( instruction, ]).encode(); - const accounts = getPostMessageAccounts( - wormholeAddress, - emitter, - emitter, - messagePDA - ); + const accounts = getPostMessageAccounts(wormholeAddress, emitter, messagePDA); return { instruction: await wormholeProgram.methods .postMessage(0, buffer, 0) .accounts(accounts) .instruction(), - authorityIndex: instructionIndex, + authorityIndex: instructionIndex + 1, authorityBump: messagePdaBump, authorityType: "custom", }; } +function getPostMessageAccounts( + wormholeAddress: PublicKey, + emitter: PublicKey, + message: PublicKey +) { + return { + bridge: deriveWormholeBridgeDataKey(wormholeAddress), + message, + emitter, + sequence: deriveEmitterSequenceKey(emitter, wormholeAddress), + payer: emitter, + feeCollector: deriveFeeCollectorKey(wormholeAddress), + clock: SYSVAR_CLOCK_PUBKEY, + rent: SYSVAR_RENT_PUBKEY, + systemProgram: SystemProgram.programId, + }; +} From 0c97035a197053d0b325258d860b616a946ba04f Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Alapont Date: Tue, 17 Jan 2023 15:38:02 -0600 Subject: [PATCH 2/5] Export WormholeMultisigInstruction --- governance/xc-admin/packages/crank-executor/src/index.ts | 7 +++++-- .../xc-admin-common/src/multisig_transaction/index.ts | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/governance/xc-admin/packages/crank-executor/src/index.ts b/governance/xc-admin/packages/crank-executor/src/index.ts index fa21cf6a72..fc461f7a4a 100644 --- a/governance/xc-admin/packages/crank-executor/src/index.ts +++ b/governance/xc-admin/packages/crank-executor/src/index.ts @@ -11,14 +11,17 @@ import { import SquadsMesh, { DEFAULT_MULTISIG_PROGRAM_ID, getIxPDA } from "@sqds/mesh"; import * as fs from "fs"; import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; -import { getProposals, MultisigParser } from "xc-admin-common"; +import { + getProposals, + MultisigParser, + WormholeMultisigInstruction, +} from "xc-admin-common"; import BN from "bn.js"; import { AnchorProvider } from "@project-serum/anchor"; import { getPythClusterApiUrl, PythCluster, } from "@pythnetwork/client/lib/cluster"; -import { WormholeMultisigInstruction } from "xc-admin-common/src/multisig_transaction/WormholeMultisigInstruction"; import { deriveFeeCollectorKey, getWormholeBridgeData, diff --git a/governance/xc-admin/packages/xc-admin-common/src/multisig_transaction/index.ts b/governance/xc-admin/packages/xc-admin-common/src/multisig_transaction/index.ts index 2e19da60ea..5dcbbc305c 100644 --- a/governance/xc-admin/packages/xc-admin-common/src/multisig_transaction/index.ts +++ b/governance/xc-admin/packages/xc-admin-common/src/multisig_transaction/index.ts @@ -65,3 +65,6 @@ export class MultisigParser { } } } + +export { WormholeMultisigInstruction } from "./WormholeMultisigInstruction"; +export { PythMultisigInstruction } from "./PythMultisigInstruction"; From 39f9fddbe773e0b9951bb4416e50c0f8d79bcff2 Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Alapont Date: Tue, 17 Jan 2023 15:39:24 -0600 Subject: [PATCH 3/5] Restore some unrelated parts of the code --- governance/xc-admin/packages/crank-executor/src/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/governance/xc-admin/packages/crank-executor/src/index.ts b/governance/xc-admin/packages/crank-executor/src/index.ts index fc461f7a4a..9956dbc847 100644 --- a/governance/xc-admin/packages/crank-executor/src/index.ts +++ b/governance/xc-admin/packages/crank-executor/src/index.ts @@ -106,14 +106,12 @@ async function run() { ); try { - console.log("Sending: ", transaction.instructions.length); await new AnchorProvider(squad.connection, squad.wallet, { commitment: COMMITMENT, preflightCommitment: COMMITMENT, - }).sendAndConfirm(transaction, [], { skipPreflight: true }); + }).sendAndConfirm(transaction, []); } catch (error) { // Mark the transaction as cancelled if we failed to run it - console.log(error); if (error instanceof SendTransactionError) { await squad.cancelTransaction(proposal.publicKey); } From e1d4675b69b2531d64b55ee640a8718190bf6af9 Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Alapont Date: Tue, 17 Jan 2023 15:40:35 -0600 Subject: [PATCH 4/5] Cleanup --- governance/xc-admin/packages/xc-admin-common/src/propose.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/governance/xc-admin/packages/xc-admin-common/src/propose.ts b/governance/xc-admin/packages/xc-admin-common/src/propose.ts index 0210208cdf..a1404e67aa 100644 --- a/governance/xc-admin/packages/xc-admin-common/src/propose.ts +++ b/governance/xc-admin/packages/xc-admin-common/src/propose.ts @@ -65,7 +65,7 @@ export async function proposeInstructions( vault, newProposalAddress, instructions[i], - i, + i + 1, wormholeAddress ); txToSend.push( @@ -142,7 +142,7 @@ export async function wrapAsRemoteInstruction( const [messagePDA, messagePdaBump] = getIxAuthorityPDA( proposalAddress, - new BN(instructionIndex + 1), + new BN(instructionIndex), squad.multisigProgramId ); @@ -167,7 +167,7 @@ export async function wrapAsRemoteInstruction( .postMessage(0, buffer, 0) .accounts(accounts) .instruction(), - authorityIndex: instructionIndex + 1, + authorityIndex: instructionIndex, authorityBump: messagePdaBump, authorityType: "custom", }; From 25a42ad3d85e2cc8883750e9807f9dfff4eae2b6 Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Alapont Date: Tue, 17 Jan 2023 15:43:01 -0600 Subject: [PATCH 5/5] Don't change this file --- .../packages/xc-admin-cli/src/index.ts | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/governance/xc-admin/packages/xc-admin-cli/src/index.ts b/governance/xc-admin/packages/xc-admin-cli/src/index.ts index 07aed31895..e2c5091fc9 100644 --- a/governance/xc-admin/packages/xc-admin-cli/src/index.ts +++ b/governance/xc-admin/packages/xc-admin-cli/src/index.ts @@ -1,4 +1,4 @@ -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; +import { Keypair, PublicKey } from "@solana/web3.js"; import { program } from "commander"; import { PythCluster } from "@pythnetwork/client/lib/cluster"; import { getPythClusterApiUrl } from "@pythnetwork/client/lib/cluster"; @@ -95,33 +95,4 @@ mutlisigCommand( await proposeInstructions(squad, vault, [proposalInstruction], false); }); -mutlisigCommand("propose-test", "Propose test").action(async (options: any) => { - const wallet = new NodeWallet( - Keypair.fromSecretKey( - Uint8Array.from(JSON.parse(fs.readFileSync(options.wallet, "ascii"))) - ) - ); - const cluster: PythCluster = options.cluster; - const vault: PublicKey = new PublicKey(options.vault); - - const squad = SquadsMesh.endpoint(getPythClusterApiUrl(cluster), wallet); - await proposeInstructions( - squad, - vault, - [ - SystemProgram.transfer({ - lamports: 1, - toPubkey: PublicKey.unique(), - fromPubkey: PublicKey.unique(), - }), - SystemProgram.transfer({ - lamports: 1, - toPubkey: PublicKey.unique(), - fromPubkey: PublicKey.unique(), - }), - ], - true, - new PublicKey("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o") - ); -}); program.parse();