@@ -257,7 +257,11 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
257257 }
258258 }
259259
260- addressLookupKeysMap := make (map [PublicKey ]addressTablePubkeyWithIndex ) // all accounts from tables as map
260+ totalTableEntries := 0
261+ for _ , t := range options .addressTables {
262+ totalTableEntries += len (t )
263+ }
264+ addressLookupKeysMap := make (map [PublicKey ]addressTablePubkeyWithIndex , totalTableEntries ) // all accounts from tables as map
261265 sortedTableKeys := make (PublicKeySlice , 0 , len (options .addressTables ))
262266 for k := range options .addressTables {
263267 sortedTableKeys = append (sortedTableKeys , k )
@@ -284,8 +288,12 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
284288 }
285289 }
286290
287- programIDs := make (PublicKeySlice , 0 )
288- accounts := []* AccountMeta {}
291+ totalAccounts := 0
292+ for _ , instruction := range instructions {
293+ totalAccounts += len (instruction .Accounts ())
294+ }
295+ programIDs := make (PublicKeySlice , 0 , len (instructions ))
296+ accounts := make ([]* AccountMeta , 0 , totalAccounts + len (instructions ))
289297 for _ , instruction := range instructions {
290298 accounts = append (accounts , instruction .Accounts ()... )
291299 programIDs .UniqueAppend (instruction .ProgramID ())
@@ -315,8 +323,16 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
315323 return 0
316324 })
317325
318- uniqAccountsMap := map [PublicKey ]uint64 {}
319- uniqAccounts := []* AccountMeta {}
326+ // Hint the map only above a threshold: for small txs, an empty map is
327+ // cheaper than a single pre-allocated bucket (~640B for PublicKey keys).
328+ // For larger txs, the hint avoids several bucket-grow operations.
329+ var uniqAccountsMap map [PublicKey ]uint64
330+ if len (accounts ) > 16 {
331+ uniqAccountsMap = make (map [PublicKey ]uint64 , len (accounts ))
332+ } else {
333+ uniqAccountsMap = map [PublicKey ]uint64 {}
334+ }
335+ uniqAccounts := make ([]* AccountMeta , 0 , len (accounts ))
320336 for _ , acc := range accounts {
321337 if index , found := uniqAccountsMap [acc .PublicKey ]; found {
322338 uniqAccounts [index ].IsWritable = uniqAccounts [index ].IsWritable || acc .IsWritable
@@ -426,9 +442,14 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
426442 lookups := make ([]MessageAddressTableLookup , 0 , len (lookupsMap ))
427443
428444 sortedLookupKeys := make (PublicKeySlice , 0 , len (lookupsMap ))
429- for k := range lookupsMap {
445+ var totalWritable , totalReadonly int
446+ for k , l := range lookupsMap {
430447 sortedLookupKeys = append (sortedLookupKeys , k )
448+ totalWritable += len (l .Writable )
449+ totalReadonly += len (l .Readonly )
431450 }
451+ lookupsWritableKeys = make ([]PublicKey , 0 , totalWritable )
452+ lookupsReadOnlyKeys = make ([]PublicKey , 0 , totalReadonly )
432453 slices .SortFunc (sortedLookupKeys , func (a , b PublicKey ) int {
433454 return bytes .Compare (a [:], b [:])
434455 })
@@ -475,6 +496,7 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
475496 )
476497 }
477498
499+ message .Instructions = make ([]CompiledInstruction , 0 , len (instructions ))
478500 for txIdx , instruction := range instructions {
479501 accounts = instruction .Accounts ()
480502 accountIndex := make ([]uint16 , len (accounts ))
@@ -506,10 +528,11 @@ func (tx *Transaction) MarshalBinary() ([]byte, error) {
506528 }
507529
508530 signatures := tx .Signatures
509- for i := len (signatures ); i < int (tx .Message .Header .NumRequiredSignatures ); i ++ {
510- // append dummy signatures to the transaction, without it serialized transaction will be invalid
531+ if missing := int (tx .Message .Header .NumRequiredSignatures ) - len (signatures ); missing > 0 {
532+ // append zero-valued dummy signatures to the transaction, without them
533+ // the serialized transaction will be invalid.
511534 // reference: https://github.com/solana-labs/solana-web3.js/blob/4e9988cfc561f3ed11f4c5016a29090a61d129a8/src/transaction/versioned.ts#L36
512- signatures = append (signatures , SignatureFromBytes ( make ([]byte , SignatureLength )) )
535+ signatures = append (signatures , make ([]Signature , missing ) ... )
513536 }
514537
515538 var signaturesCountBytes []byte
@@ -793,42 +816,18 @@ func countWriteableAccounts(tx *Transaction) (count int) {
793816 return count
794817 }
795818 numStaticKeys := len (tx .Message .AccountKeys )
796- staticKeys := tx .Message .AccountKeys
797819 h := tx .Message .Header
798- for _ , key := range staticKeys {
799- accIndex , ok := getStaticAccountIndex (tx , key )
800- if ! ok {
801- continue
802- }
803- index := int (accIndex )
804- is := false
805- if index >= int (h .NumRequiredSignatures ) {
806- // Use int arithmetic to avoid underflow (Rust uses saturating_sub here).
807- numWritableUnsigned := max (numStaticKeys - int (h .NumRequiredSignatures )- int (h .NumReadonlyUnsignedAccounts ), 0 )
808- is = index - int (h .NumRequiredSignatures ) < numWritableUnsigned
809- } else {
810- is = index < max (int (h .NumRequiredSignatures )- int (h .NumReadonlySignedAccounts ), 0 )
811- }
812- if is {
813- count ++
814- }
815- }
820+ numSig := int (h .NumRequiredSignatures )
821+ numWritableSigned := max (numSig - int (h .NumReadonlySignedAccounts ), 0 )
822+ numWritableUnsigned := max (numStaticKeys - numSig - int (h .NumReadonlyUnsignedAccounts ), 0 )
823+ count += numWritableSigned + numWritableUnsigned
816824 if tx .Message .IsResolved () {
817825 return count
818826 }
819827 count += tx .Message .NumWritableLookups ()
820828 return count
821829}
822830
823- func getStaticAccountIndex (tx * Transaction , key PublicKey ) (int , bool ) {
824- for idx , a := range tx .Message .AccountKeys {
825- if a .Equals (key ) {
826- return (idx ), true
827- }
828- }
829- return - 1 , false
830- }
831-
832831func (tx * Transaction ) IsVote () bool {
833832 // is vote if any of the instructions are of the vote program
834833 for _ , inst := range tx .Message .Instructions {
0 commit comments