1+ using Solana . Unity . Rpc . Models ;
2+ using Solana . Unity . Rpc . Utilities ;
3+ using Solana . Unity . Wallet ;
4+ using Solana . Unity . Wallet . Utilities ;
5+ using System ;
6+ using System . Collections . Generic ;
7+ using System . IO ;
8+
9+ namespace Solana . Unity . Rpc . Builders
10+ {
11+ /// <summary>
12+ /// A compiled instruction within the message.
13+ /// </summary>
14+ public class VersionedMessageBuilder : MessageBuilder
15+ {
16+
17+ /// <summary>
18+ /// Address Table Lookups
19+ /// </summary>
20+ public List < MessageAddressTableLookup > AddressTableLookups { get ; set ; }
21+
22+ /// <summary>
23+ /// Builds the message into the wire format.
24+ /// </summary>
25+ /// <returns>The encoded message.</returns>
26+ internal override byte [ ] Build ( )
27+ {
28+ if ( RecentBlockHash == null && NonceInformation == null )
29+ throw new Exception ( "recent block hash or nonce information is required" ) ;
30+ if ( Instructions == null )
31+ throw new Exception ( "no instructions provided in the transaction" ) ;
32+
33+ // In case the user specified nonce information, we'll use it.
34+ if ( NonceInformation != null )
35+ {
36+ RecentBlockHash = NonceInformation . Nonce ;
37+ _accountKeysList . Add ( NonceInformation . Instruction . Keys ) ;
38+ _accountKeysList . Add ( AccountMeta . ReadOnly ( new PublicKey ( NonceInformation . Instruction . ProgramId ) ,
39+ false ) ) ;
40+ List < TransactionInstruction > newInstructions = new ( ) { NonceInformation . Instruction } ;
41+ newInstructions . AddRange ( Instructions ) ;
42+ Instructions = newInstructions ;
43+ }
44+
45+ _messageHeader = new MessageHeader ( ) ;
46+
47+ List < AccountMeta > keysList = GetAccountKeys ( ) ;
48+ byte [ ] accountAddressesLength = ShortVectorEncoding . EncodeLength ( keysList . Count ) ;
49+ int compiledInstructionsLength = 0 ;
50+ List < CompiledInstruction > compiledInstructions = new ( ) ;
51+
52+ foreach ( TransactionInstruction instruction in Instructions )
53+ {
54+ int keyCount = instruction . Keys . Count ;
55+ byte [ ] keyIndices = new byte [ keyCount ] ;
56+
57+ if ( instruction . GetType ( ) == typeof ( VersionedTransactionInstruction ) )
58+ {
59+ keyIndices = ( ( VersionedTransactionInstruction ) instruction ) . KeyIndices ;
60+ }
61+ else
62+ {
63+ for ( int i = 0 ; i < keyCount ; i ++ )
64+ {
65+ keyIndices [ i ] = FindAccountIndex ( keysList , instruction . Keys [ i ] . PublicKey ) ;
66+ }
67+ }
68+
69+ CompiledInstruction compiledInstruction = new ( )
70+ {
71+ ProgramIdIndex = FindAccountIndex ( keysList , instruction . ProgramId ) ,
72+ KeyIndicesCount = ShortVectorEncoding . EncodeLength ( keyIndices . Length ) ,
73+ KeyIndices = keyIndices ,
74+ DataLength = ShortVectorEncoding . EncodeLength ( instruction . Data . Length ) ,
75+ Data = instruction . Data
76+ } ;
77+ compiledInstructions . Add ( compiledInstruction ) ;
78+ compiledInstructionsLength += compiledInstruction . Length ( ) ;
79+ }
80+
81+ int accountKeysBufferSize = _accountKeysList . AccountList . Count * 32 ;
82+ MemoryStream accountKeysBuffer = new MemoryStream ( accountKeysBufferSize ) ;
83+ byte [ ] instructionsLength = ShortVectorEncoding . EncodeLength ( compiledInstructions . Count ) ;
84+
85+ foreach ( AccountMeta accountMeta in keysList )
86+ {
87+ accountKeysBuffer . Write ( accountMeta . PublicKeyBytes , 0 , accountMeta . PublicKeyBytes . Length ) ;
88+ if ( accountMeta . IsSigner )
89+ {
90+ _messageHeader . RequiredSignatures += 1 ;
91+ if ( ! accountMeta . IsWritable )
92+ _messageHeader . ReadOnlySignedAccounts += 1 ;
93+ }
94+ else
95+ {
96+ if ( ! accountMeta . IsWritable )
97+ _messageHeader . ReadOnlyUnsignedAccounts += 1 ;
98+ }
99+ }
100+
101+ #region Build Message Body
102+
103+ int messageBufferSize = MessageHeader . Layout . HeaderLength + BlockHashLength +
104+ accountAddressesLength . Length +
105+ + instructionsLength . Length + compiledInstructionsLength + accountKeysBufferSize ;
106+ MemoryStream buffer = new MemoryStream ( messageBufferSize ) ;
107+ byte [ ] messageHeaderBytes = _messageHeader . ToBytes ( ) ;
108+
109+ buffer . Write ( new byte [ ] { 128 } , 0 , 1 ) ;
110+ buffer . Write ( messageHeaderBytes , 0 , messageHeaderBytes . Length ) ;
111+ buffer . Write ( accountAddressesLength , 0 , accountAddressesLength . Length ) ;
112+ buffer . Write ( accountKeysBuffer . ToArray ( ) , 0 , accountKeysBuffer . ToArray ( ) . Length ) ;
113+ var encodedRecentBlockHash = Encoders . Base58 . DecodeData ( RecentBlockHash ) ;
114+ buffer . Write ( encodedRecentBlockHash , 0 , encodedRecentBlockHash . Length ) ;
115+ buffer . Write ( instructionsLength , 0 , instructionsLength . Length ) ;
116+
117+ foreach ( CompiledInstruction compiledInstruction in compiledInstructions )
118+ {
119+ buffer . WriteByte ( compiledInstruction . ProgramIdIndex ) ;
120+ buffer . Write ( compiledInstruction . KeyIndicesCount , 0 , compiledInstruction . KeyIndicesCount . Length ) ;
121+ buffer . Write ( compiledInstruction . KeyIndices , 0 , compiledInstruction . KeyIndices . Length ) ;
122+ buffer . Write ( compiledInstruction . DataLength , 0 , compiledInstruction . DataLength . Length ) ;
123+ buffer . Write ( compiledInstruction . Data , 0 , compiledInstruction . Data . Length ) ;
124+ }
125+
126+ #endregion
127+
128+ var serializeAddressTableLookups = AddressTableLookupUtils . SerializeAddressTableLookups ( AddressTableLookups ) ;
129+ buffer . Write ( serializeAddressTableLookups , 0 , serializeAddressTableLookups . Length ) ;
130+
131+ return buffer . ToArray ( ) ;
132+ }
133+ }
134+ }
0 commit comments