import type { Cluster , ConfirmOptions , PublicKey , RpcResponseAndContext , Signer , SimulatedTransactionResponse , TransactionInstruction , } from "@solana/web3.js" ; import { PACKET_DATA_SIZE , Transaction } from "@solana/web3.js" ; import invariant from "tiny-invariant" ; import type { BroadcastOptions } from ".." ; import { EstimatedTXTooBigError , printTXTable , suppressConsoleError , TXSizeEstimationError , } from ".." ; import type { Provider } from "../interfaces" ; import type { PendingTransaction } from "./PendingTransaction" ; import type { TransactionReceipt } from "./TransactionReceipt" ; import { calculateTxSizeUnsafe } from "./txSizer" ; import type { SerializableInstruction } from "./utils" ; import { generateInspectLinkFromBase64 , RECENT_BLOCKHASH_STUB } from "./utils" ; param ixs returns const filterRequiredSigners = ( ixs , signers ) : Signer [ ] => { const requiredSigners = ixs . flatMap ( ( ix ) => ix . keys . filter ( ( k ) => k . isSigner ) . map ( ( k ) => k . pubkey ) ) ; return signers . filter ( ( s ) => requiredSigners . find ( ( rs ) => rs . equals ( s . publicKey ) ) ) ; } ; /**
 * Options for simulating a transaction.
 */ export interface TXEnvelopeSimulateOptions extends ConfirmOptions { /**
     * Verify that the signers of the TX enveloper are valid.
     */ verifySigners ? : boolean ; } /**
 * Contains a Transaction that is being built.
 */ export class TransactionEnvelope { constructor ( /**
     * Provider that will be sending the transaction as the fee payer.
     */ readonly provider , /**
     * Instructions associated with the transaction.
     */ readonly instructions , /**
     * Optional signers of the transaction.
     */ readonly signers = [ ] ) { } /**
     * Adds the given  TransactionInstruction s to the  TransactionEnvelope .
     *  param instructions returns addInstructions ( ... instructions ) { this . instructions . push ( ... instructions . filter ( ( ix ) : ix is TransactionInstruction => ! ! ix ) ) ; return this ; } /**
     * Adds the given  Signer s to the  TransactionEnvelope .
     *  param signers returns addSigners ( ... signers ) { this . signers . push ( ... signers ) ; return this ; } param feePayer build ( feePayer = this . provider . wallet . publicKey ) { const tx = new Transaction ( ) . add ( ... this . instructions ) ; tx . feePayer = feePayer ; return tx ; } returns estimateSizeUnsafe ( ) { const builtTx = this . build ( ) ; builtTx . recentBlockhash = "MaryHadALittLeLambZNdhAUTrsLE1ydg6rmtvFEpKT" ; return calculateTxSizeUnsafe ( builtTx ) ; } /**
     * Builds a transaction and estimates the size in bytes. This number is primrily
     * to be used for checking to see if a transaction is too big and instructions
     * need to be split. It may not be 100% accurate.
     *
     * This is used in expectTXTable and is useful for increasing efficiency in
     * dapps that build large transactions.
     *
     * The max transaction size of a v1 Transaction in Solana is 1232 bytes.
     * For info about Transaction v2: https://docs.solana.com/proposals/transactions-v2
     */ estimateSize ( ) { return suppressConsoleError ( ( ) => { try { const builtTx = this . build ( ) ; builtTx . recentBlockhash = "MaryHadALittLeLambZNdhAUTrsLE1ydg6rmtvFEpKT" ; const size = calculateTxSizeUnsafe ( builtTx ) ; if ( size > PACKET_DATA_SIZE ) { return { error : new EstimatedTXTooBigError ( builtTx , size ) } ; } return { size } ; } catch ( e ) { return { error : new TXSizeEstimationError ( e ) } ; } } ) ; } /**
     * Partition a large  TransactionEnvelope into smaller, valid  Transaction s.
     * This relies on this envelope already having the correct number of signers.
     *
     *  param feePayer returns A list of  Transaction s.
      buildPartition ( feePayer = this . provider . wallet . publicKey ) { const partition = this . partition ( ) ; return partition . map ( ( env ) => env . build ( feePayer ) ) ; } /**
     * Partition a large  TransactionEnvelope into smaller, valid transaction envelopes which can be built.
     * This relies on this envelope already having the correct number of signers.
     *
     *  returns partition ( ) { const estimation = this . estimateSize ( ) ; if ( "size" in estimation ) { return [ this ] ; } if ( this . instructions . length === 0 ) { return [ ] ; } let lastTXEnv = new TransactionEnvelope ( this . provider , this . instructions . slice ( 0 , 1 ) , this . _filterRequiredSigners ( this . instructions . slice ( 0 , 1 ) ) ) ; let lastEstimation = lastTXEnv . estimateSizeUnsafe ( ) ; const txs = [ ] ; this . instructions . slice ( 1 ) . forEach ( ( ix , i ) => { if ( lastEstimation > PACKET_DATA_SIZE ) { throw new Error ( `cannot construct a valid partition: instruction ${ i } is too large (${ lastEstimation } > ${ PACKET_DATA_SIZE })` ) ; } const nextIXs = [ ... lastTXEnv . instructions , ix ] ; const nextSigners = this . _filterRequiredSigners ( nextIXs ) ; const nextTXEnv = new TransactionEnvelope ( this . provider , nextIXs , nextSigners ) ; const nextEstimation = nextTXEnv . estimateSizeUnsafe ( ) ; if ( nextEstimation > PACKET_DATA_SIZE ) { txs . push ( lastTXEnv ) ; const nextIXs = [ ix ] ; lastTXEnv = new TransactionEnvelope ( this . provider , nextIXs , this . _filterRequiredSigners ( nextIXs ) ) ; lastEstimation = lastTXEnv . estimateSizeUnsafe ( ) ; } else { lastTXEnv = nextTXEnv ; lastEstimation = nextEstimation ; } } ) ; txs . push ( lastTXEnv ) ; return txs ; } param ixs returns private _filterRequiredSigners ( ixs ) { return filterRequiredSigners ( ixs , this . signers ) ; } /**
     * Generates a link for inspecting the contents of this  TransactionEnvelope .
     *
     *  returns generateInspectLink ( cluster = "mainnet-beta" ) { const t = this . build ( ) ; t . recentBlockhash = RECENT_BLOCKHASH_STUB ; const str = t . serializeMessage ( ) . toString ( "base64" ) ; return generateInspectLinkFromBase64 ( cluster , str ) ; } param opts returns simulate ( opts = { verifySigners : true , } ) { return this . provider . simulate ( this . build ( ) , opts . verifySigners ? this . signers : undefined , opts ) ; } deprecated Use  TXEnvelope instead.
     *  param opts returns simulateUnchecked ( opts ) { return this . simulate ( { ... opts , verifySigners : false } ) ; } param opts returns simulateTable ( opts ? ) { return this . simulate ( opts ) . then ( ( simulation ) => { if ( simulation ?. value ?. logs ) { printTXTable ( this , simulation . value . logs , "" ) ; } return simulation ; } ) ; } param opts returns async send ( opts ? ) { const signed = await this . provider . signer . sign ( this . build ( ) , this . signers , opts ) ; return this . provider . broadcaster . broadcast ( signed , opts ) ; } param opts async confirm ( opts ? ) { return ( await this . send ( opts ) ) . wait ( ) ; } /**
     * Combines the instructions/signers of the other envelope to create one large transaction.
     */ combine ( other ) { return new TransactionEnvelope ( this . provider , [ ... this . instructions , ... other . instructions ] , [ ... this . signers , ... other . signers ] ) ; } /**
     * Get a list of all writable accounts, deduped
     * All of these accounts likely need to be updated after the transaction is confirmed.
     */ get writableKeys ( ) : PublicKey [ ] { return [ ... new Set ( [ ... this . instructions . map ( ( inst ) => inst . keys . filter ( ( key ) => key . isWritable ) . map ( ( k ) => k . pubkey ) ) . reduce ( ( acc , el ) => acc . concat ( el ) ) , ] ) . values ( ) , ] ; } /**
     * Gets the instructions in a format that can be serialized easily to JSON.
     */ get instructionsJSON ( ) : SerializableInstruction [ ] { return this . instructions . map ( ( instruction ) => ( { programId : instruction . programId . toString ( ) , keys : instruction . keys . map ( ( m ) => ( { isSigner : m . isSigner , isWritable : m . isWritable , publicKey : m . pubkey . toString ( ) , } ) ) , data : instruction . data . toString ( "base64" ) , } ) ) ; } /**
     * Returns a string representation of the  TransactionEnvelope .
      get debugStr ( ) : string { return [ "=> Instructions" , this . instructions . map ( ( ser , i ) => { return [ `Instruction ${ i }: ${ ser . programId . toString ( ) }` , ... ser . keys . map ( ( k , i ) => `  [${ i }] ${ k . pubkey . toString ( ) } ${ k . isWritable ? "(mut)" : "" } ${ k . isSigner ? "(signer)" : "" }` ) , `  Data (base64): ${ ser . data . toString ( "base64" ) }` , ] . join ( "\n" ) ; } ) . join ( "\n" ) , "=> Signers" , this . signers . map ( ( sg ) => sg . publicKey . toString ( ) ) . join ( "\n" ) , ] . join ( "\n" ) ; } /**
     * Creates a new  TransactionEnvelope .
     *  param provider param instructions param signers returns static create ( provider , instructions , signers = [ ] ) { const ixs = instructions . filter ( ( ix ) : ix is TransactionInstruction => ! ! ix ) ; return new TransactionEnvelope ( provider , ixs , signers ) ; } /**
     * Combines multiple TransactionEnvelopes into one.
     */ static combineAll ( ... txs ) { return txs . reduce ( ( acc , tx ) => acc . combine ( tx ) ) ; } /**
     * Takes a list of  TransactionEnvelope s and combines them if they
     * are able to be combined under the maximum TX size limit.
     *
     *  param txs returns static pack ( ... txs ) { if ( txs . length === 0 ) { return [ ] ; } const [ first , ... rest ] = txs ; invariant ( first ) ; const { provider } = first ; let lastTXEnv = first ; let lastEstimation = lastTXEnv . estimateSizeUnsafe ( ) ; const partition = [ ] ; rest . forEach ( ( addedTX , i ) => { if ( lastEstimation > PACKET_DATA_SIZE ) { throw new Error ( `cannot construct a valid partition: instruction ${ i } is too large (${ lastEstimation } > ${ PACKET_DATA_SIZE })` ) ; } const nextIXs = [ ... lastTXEnv . instructions , ... addedTX . instructions ] ; const nextSigners = filterRequiredSigners ( nextIXs , [ ... lastTXEnv . signers , ... addedTX . signers , ] ) ; const nextTXEnv = new TransactionEnvelope ( provider , nextIXs , nextSigners ) ; const nextEstimation = nextTXEnv . estimateSizeUnsafe ( ) ; if ( nextEstimation > PACKET_DATA_SIZE ) { partition . push ( lastTXEnv ) ; lastTXEnv = addedTX ; lastEstimation = lastTXEnv . estimateSizeUnsafe ( ) ; } else { lastTXEnv = nextTXEnv ; lastEstimation = nextEstimation ; } } ) ; partition . push ( lastTXEnv ) ; return partition ; } /**
     * Combines multiple async TransactionEnvelopes into one, serially.
     */ static async combineAllAsync ( firstTX , ... txs ) { let acc = await firstTX ; for ( const tx of txs ) { acc = acc . combine ( await tx ) ; } return acc ; } returns static async sendAll ( txs , opts ? ) { const firstTX = txs [ 0 ] ; if ( ! firstTX ) { return [ ] ; } const provider = firstTX . provider ; return await provider . sendAll ( txs . map ( ( tx ) => ( { tx : tx . build ( ) , signers : tx . signers } ) ) , opts ) ; } }