77 SystemProgram ,
88 PACKET_DATA_SIZE ,
99 ConfirmOptions ,
10+ sendAndConfirmRawTransaction ,
1011} from "@solana/web3.js" ;
1112import { BN } from "bn.js" ;
1213import { AnchorProvider } from "@coral-xyz/anchor" ;
@@ -17,7 +18,7 @@ import {
1718 deriveFeeCollectorKey ,
1819} from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole" ;
1920import { ExecutePostedVaa } from "./governance_payload/ExecutePostedVaa" ;
20- import { getOpsKey , getProposalInstructions } from "./multisig" ;
21+ import { getOpsKey } from "./multisig" ;
2122import { PythCluster } from "@pythnetwork/client/lib/cluster" ;
2223import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider" ;
2324import SquadsMesh , { getIxAuthorityPDA , getTxPDA } from "@sqds/mesh" ;
@@ -26,8 +27,8 @@ import { mapKey } from "./remote_executor";
2627import { WORMHOLE_ADDRESS } from "./wormhole" ;
2728
2829export const MAX_EXECUTOR_PAYLOAD_SIZE = PACKET_DATA_SIZE - 687 ; // Bigger payloads won't fit in one addInstruction call when adding to the proposal
29- export const SIZE_OF_SIGNED_BATCH = 30 ;
3030export const MAX_INSTRUCTIONS_PER_PROPOSAL = 256 - 1 ;
31+ export const MAX_NUMBER_OF_RETRIES = 5 ;
3132
3233type SquadInstruction = {
3334 instruction : TransactionInstruction ;
@@ -256,13 +257,7 @@ export class MultisigVault {
256257 ixToSend . push ( await this . approveProposalIx ( proposalAddress ) ) ;
257258
258259 const txToSend = batchIntoTransactions ( ixToSend ) ;
259- for ( let i = 0 ; i < txToSend . length ; i += SIZE_OF_SIGNED_BATCH ) {
260- await this . getAnchorProvider ( ) . sendAll (
261- txToSend . slice ( i , i + SIZE_OF_SIGNED_BATCH ) . map ( ( tx ) => {
262- return { tx, signers : [ ] } ;
263- } )
264- ) ;
265- }
260+ await this . sendAllTransactions ( txToSend ) ;
266261 return proposalAddress ;
267262 }
268263
@@ -367,18 +362,58 @@ export class MultisigVault {
367362
368363 const txToSend = batchIntoTransactions ( ixToSend ) ;
369364
370- for ( let i = 0 ; i < txToSend . length ; i += SIZE_OF_SIGNED_BATCH ) {
371- await this . getAnchorProvider ( {
372- preflightCommitment : "processed" ,
373- commitment : "confirmed" ,
374- } ) . sendAll (
375- txToSend . slice ( i , i + SIZE_OF_SIGNED_BATCH ) . map ( ( tx ) => {
376- return { tx, signers : [ ] } ;
377- } )
378- ) ;
379- }
365+ await this . sendAllTransactions ( txToSend ) ;
380366 return newProposals ;
381367 }
368+
369+ async sendAllTransactions ( transactions : Transaction [ ] ) {
370+ const provider = this . getAnchorProvider ( {
371+ preflightCommitment : "processed" ,
372+ commitment : "processed" ,
373+ } ) ;
374+
375+ let needToFetchBlockhash = true ; // We don't fetch blockhash everytime to save time
376+ let blockhash : string = "" ;
377+ for ( let [ index , tx ] of transactions . entries ( ) ) {
378+ console . log ( "Trying to send transaction : " + index ) ;
379+ let numberOfRetries = 0 ;
380+ let txHasLanded = false ;
381+
382+ while ( ! txHasLanded ) {
383+ try {
384+ if ( needToFetchBlockhash ) {
385+ blockhash = ( await provider . connection . getLatestBlockhash ( ) )
386+ . blockhash ;
387+ needToFetchBlockhash = false ;
388+ }
389+ tx . feePayer = tx . feePayer || provider . wallet . publicKey ;
390+ tx . recentBlockhash = blockhash ;
391+ provider . wallet . signTransaction ( tx ) ;
392+ await sendAndConfirmRawTransaction (
393+ provider . connection ,
394+ tx . serialize ( ) ,
395+ provider . opts
396+ ) ;
397+ txHasLanded = true ;
398+ } catch ( e ) {
399+ if ( numberOfRetries >= MAX_NUMBER_OF_RETRIES ) {
400+ // Cap the number of retries
401+ throw Error ( "Maximum number of retries exceeded" ) ;
402+ }
403+ const message = ( e as any ) . toString ( ) . split ( "\n" ) [ 0 ] ;
404+ if (
405+ message ==
406+ "Error: failed to send transaction: Transaction simulation failed: Blockhash not found"
407+ ) {
408+ // If blockhash has expired, we need to fetch a new one
409+ needToFetchBlockhash = true ;
410+ }
411+ console . log ( e ) ;
412+ numberOfRetries += 1 ;
413+ }
414+ }
415+ }
416+ }
382417}
383418
384419/**
0 commit comments