@@ -19,6 +19,8 @@ import {
1919import { ExecutePostedVaa } from "./governance_payload/ExecutePostedVaa" ;
2020import { OPS_KEY } from "./multisig" ;
2121
22+ export const MAX_EXECUTOR_PAYLOAD_SIZE = PACKET_DATA_SIZE - 687 ; // Bigger payloads won't fit in one addInstruction call when adding to the proposal
23+
2224type SquadInstruction = {
2325 instruction : TransactionInstruction ;
2426 authorityIndex ?: number ;
@@ -43,7 +45,7 @@ export async function proposeInstructions(
4345) : Promise < PublicKey > {
4446 const msAccount = await squad . getMultisig ( vault ) ;
4547 let ixToSend : TransactionInstruction [ ] = [ ] ;
46- const createProposal = ixToSend . push (
48+ ixToSend . push (
4749 await squad . buildCreateTransaction (
4850 msAccount . publicKey ,
4951 msAccount . authorityIndex ,
@@ -60,12 +62,14 @@ export async function proposeInstructions(
6062 if ( ! wormholeAddress ) {
6163 throw new Error ( "Need wormhole address" ) ;
6264 }
63- for ( let i = 0 ; i < instructions . length ; i ++ ) {
65+
66+ const batches = batchIntoExecutorPayload ( instructions ) ;
67+ for ( const [ i , batch ] of batches . entries ( ) ) {
6468 const squadIx = await wrapAsRemoteInstruction (
6569 squad ,
6670 vault ,
6771 newProposalAddress ,
68- instructions [ i ] ,
72+ batch ,
6973 i + 1 ,
7074 wormholeAddress
7175 ) ;
@@ -100,7 +104,7 @@ export async function proposeInstructions(
100104
101105 ixToSend . push ( await squad . buildApproveTransaction ( vault , newProposalAddress ) ) ;
102106
103- const txToSend = batchIntoTransactions ( ixToSend , squad . wallet . publicKey ) ;
107+ const txToSend = batchIntoTransactions ( ixToSend ) ;
104108 await new AnchorProvider (
105109 squad . connection ,
106110 squad . wallet ,
@@ -113,12 +117,38 @@ export async function proposeInstructions(
113117 return newProposalAddress ;
114118}
115119
120+ /**
121+ * Batch instructions into batches for inclusion in a remote executor payload
122+ */
123+ export function batchIntoExecutorPayload (
124+ instructions : TransactionInstruction [ ]
125+ ) : TransactionInstruction [ ] [ ] {
126+ let i = 0 ;
127+ const batches : TransactionInstruction [ ] [ ] = [ ] ;
128+ while ( i < instructions . length ) {
129+ let j = i + 2 ;
130+ while (
131+ j < instructions . length &&
132+ getSizeOfExecutorInstructions ( instructions . slice ( i , j ) ) <=
133+ MAX_EXECUTOR_PAYLOAD_SIZE
134+ ) {
135+ j += 1 ;
136+ }
137+ const batch : TransactionInstruction [ ] = [ ] ;
138+ for ( let k = i ; k < j - 1 ; k += 1 ) {
139+ batch . push ( instructions [ k ] ) ;
140+ }
141+ i = j - 1 ;
142+ batches . push ( batch ) ;
143+ }
144+ return batches ;
145+ }
146+
116147/**
117148 * Batch instructions into transactions
118149 */
119150export function batchIntoTransactions (
120- instructions : TransactionInstruction [ ] ,
121- feePayer : PublicKey
151+ instructions : TransactionInstruction [ ]
122152) : Transaction [ ] {
123153 let i = 0 ;
124154 const txToSend : Transaction [ ] = [ ] ;
@@ -131,7 +161,6 @@ export function batchIntoTransactions(
131161 j += 1 ;
132162 }
133163 const tx = new Transaction ( ) ;
134- tx . feePayer = feePayer ;
135164 for ( let k = i ; k < j - 1 ; k += 1 ) {
136165 tx . add ( instructions [ k ] ) ;
137166 }
@@ -141,6 +170,16 @@ export function batchIntoTransactions(
141170 return txToSend ;
142171}
143172
173+ /** Get the size of instructions when serialized as in a remote executor payload */
174+ export function getSizeOfExecutorInstructions (
175+ instructions : TransactionInstruction [ ]
176+ ) {
177+ return instructions
178+ . map ( ( ix ) => {
179+ return 32 + 4 + ix . keys . length * 34 + 4 + ix . data . length ;
180+ } )
181+ . reduce ( ( a , b ) => a + b ) ;
182+ }
144183/**
145184 * Get the size of a transaction that would contain the provided array of instructions
146185 */
@@ -170,6 +209,7 @@ export function getSizeOfTransaction(
170209 ix . data . length
171210 )
172211 . reduce ( ( a , b ) => a + b , 0 ) ;
212+
173213 return (
174214 1 +
175215 signers . size * 64 +
@@ -194,7 +234,7 @@ export function getSizeOfCompressedU16(n: number) {
194234 * @param squad Squads client
195235 * @param vault vault public key (the id of the multisig where these instructions should be proposed)
196236 * @param proposalAddress address of the proposal
197- * @param instruction instruction to be wrapped in a Wormhole message
237+ * @param instructions instructions to be wrapped in a Wormhole message
198238 * @param instructionIndex index of the instruction within the proposal
199239 * @param wormholeAddress address of the Wormhole bridge
200240 * @returns an instruction to be proposed
@@ -203,7 +243,7 @@ export async function wrapAsRemoteInstruction(
203243 squad : Squads ,
204244 vault : PublicKey ,
205245 proposalAddress : PublicKey ,
206- instruction : TransactionInstruction ,
246+ instructions : TransactionInstruction [ ] ,
207247 instructionIndex : number ,
208248 wormholeAddress : PublicKey
209249) : Promise < SquadInstruction > {
@@ -225,9 +265,7 @@ export async function wrapAsRemoteInstruction(
225265 provider
226266 ) ;
227267
228- const buffer : Buffer = new ExecutePostedVaa ( "pythnet" , [
229- instruction ,
230- ] ) . encode ( ) ;
268+ const buffer : Buffer = new ExecutePostedVaa ( "pythnet" , instructions ) . encode ( ) ;
231269
232270 const accounts = getPostMessageAccounts ( wormholeAddress , emitter , messagePDA ) ;
233271
0 commit comments