solace 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +0 -0
  3. data/LICENSE +0 -0
  4. data/README.md +661 -0
  5. data/lib/solace/address_lookup_table.rb +50 -0
  6. data/lib/solace/concerns/binary_serializable.rb +30 -0
  7. data/lib/solace/connection.rb +187 -0
  8. data/lib/solace/constants.rb +52 -0
  9. data/lib/solace/instruction.rb +38 -0
  10. data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +68 -0
  11. data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +46 -0
  12. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +68 -0
  13. data/lib/solace/instructions/spl_token/mint_to_instruction.rb +48 -0
  14. data/lib/solace/instructions/spl_token/transfer_instruction.rb +48 -0
  15. data/lib/solace/instructions/system_program/create_account_instruction.rb +58 -0
  16. data/lib/solace/instructions/transfer_checked_instruction.rb +58 -0
  17. data/lib/solace/instructions/transfer_instruction.rb +48 -0
  18. data/lib/solace/keypair.rb +121 -0
  19. data/lib/solace/message.rb +95 -0
  20. data/lib/solace/programs/associated_token_account.rb +96 -0
  21. data/lib/solace/programs/base.rb +22 -0
  22. data/lib/solace/programs/spl_token.rb +187 -0
  23. data/lib/solace/public_key.rb +74 -0
  24. data/lib/solace/serializable_record.rb +26 -0
  25. data/lib/solace/serializers/address_lookup_table_deserializer.rb +62 -0
  26. data/lib/solace/serializers/address_lookup_table_serializer.rb +54 -0
  27. data/lib/solace/serializers/base.rb +31 -0
  28. data/lib/solace/serializers/base_deserializer.rb +56 -0
  29. data/lib/solace/serializers/base_serializer.rb +52 -0
  30. data/lib/solace/serializers/instruction_deserializer.rb +62 -0
  31. data/lib/solace/serializers/instruction_serializer.rb +54 -0
  32. data/lib/solace/serializers/message_deserializer.rb +116 -0
  33. data/lib/solace/serializers/message_serializer.rb +95 -0
  34. data/lib/solace/serializers/transaction_deserializer.rb +49 -0
  35. data/lib/solace/serializers/transaction_serializer.rb +60 -0
  36. data/lib/solace/transaction.rb +98 -0
  37. data/lib/solace/utils/codecs.rb +220 -0
  38. data/lib/solace/utils/curve25519_dalek.rb +59 -0
  39. data/lib/solace/utils/libcurve25519_dalek-linux/libcurve25519_dalek.so +0 -0
  40. data/lib/solace/utils/libcurve25519_dalek-macos/libcurve25519_dalek.dylib +0 -0
  41. data/lib/solace/utils/libcurve25519_dalek-windows/curve25519_dalek.dll +0 -0
  42. data/lib/solace/utils/pda.rb +100 -0
  43. data/lib/solace/version.rb +5 -0
  44. data/lib/solace.rb +39 -0
  45. metadata +165 -0
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # PublicKey
5
+ # =============================
6
+ #
7
+ # Represents a Solana Ed25519 Public Key
8
+ # Provides utility methods for encoding, decoding, and validation
9
+ module Solace
10
+ class PublicKey
11
+ include Solace::Utils::PDA
12
+
13
+ # !@const LENGTH
14
+ # The length of a Solana public key in bytes
15
+ #
16
+ # @return [Integer] The length of a public key
17
+ LENGTH = 32
18
+
19
+ # !@const MAX_BUMP_SEED
20
+ # The maximum seed value for a Program Derived Address
21
+ #
22
+ # @return [Integer] The maximum seed value
23
+ MAX_BUMP_SEED = 255
24
+
25
+ # !@const PDA_MARKER
26
+ # The marker for a Program Derived Address
27
+ #
28
+ # @return [String] The marker for a PDA
29
+ PDA_MARKER = 'ProgramDerivedAddress'
30
+
31
+ # !@attribute bytes
32
+ # @return [Array<Integer>] The bytes of the public key
33
+ attr_reader :bytes
34
+
35
+ # Initialize with a 32-byte array or string
36
+ #
37
+ # @param bytes [String, Array<Integer>] 32-byte array or string
38
+ # @return [PublicKey]
39
+ def initialize(bytes)
40
+ raise ArgumentError, "Public key must be #{LENGTH} bytes" unless bytes.length == LENGTH
41
+
42
+ @bytes = bytes.freeze
43
+ end
44
+
45
+ # Return the base58 representation of the public key
46
+ #
47
+ # @return [String]
48
+ def to_base58
49
+ Solace::Utils::Codecs.bytes_to_base58(@bytes)
50
+ end
51
+
52
+ # String representation (base58)
53
+ #
54
+ # @return [String]
55
+ def to_s
56
+ to_base58
57
+ end
58
+
59
+ # Compare two public keys for equality
60
+ #
61
+ # @param other [PublicKey]
62
+ # @return [Boolean]
63
+ def ==(other)
64
+ other.is_a?(Solace::PublicKey) && other.bytes == bytes
65
+ end
66
+
67
+ # Return the public key as a byte array
68
+ #
69
+ # @return [Array<Integer>]
70
+ def to_bytes
71
+ @bytes.dup
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ class SerializableRecord
5
+ include Solace::Concerns::BinarySerializable
6
+
7
+ # Parse instruction from io stream
8
+ #
9
+ # @param io [IO or StringIO] The input to read bytes from.
10
+ # @return [Solace::Instruction] Parsed instruction object
11
+ def self.deserialize(io)
12
+ self::DESERIALIZER.call(io)
13
+ rescue NameError => e
14
+ raise "DESERIALIZER must be defined: #{e.message}"
15
+ end
16
+
17
+ # Serializes the transaction to a binary format
18
+ #
19
+ # @return [String] The serialized transaction (binary)
20
+ def serialize
21
+ self.class::SERIALIZER.call(self)
22
+ rescue NameError => e
23
+ raise "SERIALIZER must be defined: #{e.message}"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Address Lookup Table Deserializer
5
+ # =============================
6
+ #
7
+ # Deserializes a Solana address lookup table from a binary format.
8
+ module Solace
9
+ module Serializers
10
+ class AddressLookupTableDeserializer < Solace::Serializers::BaseDeserializer
11
+ # @!attribute record_class
12
+ # The class of the record being deserialized
13
+ #
14
+ # @return [Class] The class of the record
15
+ self.record_class = Solace::AddressLookupTable
16
+
17
+ # @!attribute steps
18
+ # An ordered list of methods to deserialize the address lookup table
19
+ #
20
+ # @return [Array] The steps to deserialize the address lookup table
21
+ self.steps = %i[
22
+ next_extract_account
23
+ next_extract_writable_indexes
24
+ next_extract_readonly_indexes
25
+ ]
26
+
27
+ # Extract the account key from the transaction
28
+ #
29
+ # The BufferLayout is:
30
+ # - [Account key (32 bytes)]
31
+ #
32
+ # @return [String] The account key
33
+ def next_extract_account
34
+ record.account = Codecs.bytes_to_base58 io.read(32).bytes
35
+ end
36
+
37
+ # Extract the writable indexes from the transaction
38
+ #
39
+ # The BufferLayout is:
40
+ # - [Number of writable indexes (compact u16)]
41
+ # - [Writable indexes (variable length u8)]
42
+ #
43
+ # @return [Array<Integer>] The writable indexes
44
+ def next_extract_writable_indexes
45
+ length, = Codecs.decode_compact_u16(io)
46
+ record.writable_indexes = io.read(length).unpack('C*')
47
+ end
48
+
49
+ # Extract the readonly indexes from the transaction
50
+ #
51
+ # The BufferLayout is:
52
+ # - [Number of readonly indexes (compact u16)]
53
+ # - [Readonly indexes (variable length u8)]
54
+ #
55
+ # @return [Array<Integer>] The readonly indexes
56
+ def next_extract_readonly_indexes
57
+ length, = Codecs.decode_compact_u16(io)
58
+ record.readonly_indexes = io.read(length).unpack('C*')
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Address Lookup Table Serializer
5
+ # =============================
6
+ #
7
+ # Serializes a Solana address lookup table to a binary format.
8
+ module Solace
9
+ module Serializers
10
+ class AddressLookupTableSerializer < Solace::Serializers::BaseSerializer
11
+ # @!attribute steps
12
+ # An ordered list of methods to serialize the address lookup table
13
+ #
14
+ # @return [Array] The steps to serialize the address lookup table
15
+ self.steps = %i[
16
+ encode_account
17
+ encode_writable_indexes
18
+ encode_readonly_indexes
19
+ ]
20
+
21
+ # Encodes the account of the address lookup table
22
+ #
23
+ # The BufferLayout is:
24
+ # - [Account key (32 bytes)]
25
+ #
26
+ # @return [Array<Integer>] The bytes of the encoded account
27
+ def encode_account
28
+ Codecs.base58_to_bytes(record.account)
29
+ end
30
+
31
+ # Encodes the writable indexes of the address lookup table
32
+ #
33
+ # The BufferLayout is:
34
+ # - [Number of writable indexes (compact u16)]
35
+ # - [Writable indexes (variable length u8)]
36
+ #
37
+ # @return [Array<Integer>] The bytes of the encoded writable indexes
38
+ def encode_writable_indexes
39
+ Codecs.encode_compact_u16(record.writable_indexes.size).bytes + record.writable_indexes
40
+ end
41
+
42
+ # Encodes the readonly indexes of the address lookup table
43
+ #
44
+ # The BufferLayout is:
45
+ # - [Number of readonly indexes (compact u16)]
46
+ # - [Readonly indexes (variable length u8)]
47
+ #
48
+ # @return [Array<Integer>] The bytes of the encoded readonly indexes
49
+ def encode_readonly_indexes
50
+ Codecs.encode_compact_u16(record.readonly_indexes.size).bytes + record.readonly_indexes
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Serializers
5
+ class Base
6
+ include Solace::Utils
7
+
8
+ # Proxy method to call the serializer and create a new instance
9
+ #
10
+ # @return [String] The serialized record (base64)
11
+ def self.call(*args, **kwargs)
12
+ new(*args, **kwargs).call
13
+ end
14
+
15
+ # Serializes the record
16
+ #
17
+ # @return [String] The serialized record (base64)
18
+ def call
19
+ bin = self.class::STEPS
20
+ .map { |m| send(m) }
21
+ .flatten
22
+ .compact
23
+ .pack('C*')
24
+
25
+ Base64.strict_encode64(bin)
26
+ rescue NameError => e
27
+ raise "STEPS must be defined: #{e.message}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Serializers
5
+ # Autoload deserializers
6
+ autoload :TransactionDeserializer, 'solace/serializers/transaction_deserializer'
7
+ autoload :MessageDeserializer, 'solace/serializers/message_deserializer'
8
+ autoload :InstructionDeserializer, 'solace/serializers/instruction_deserializer'
9
+ autoload :AddressLookupTableDeserializer, 'solace/serializers/address_lookup_table_deserializer'
10
+
11
+ # Base deserializer class
12
+ class BaseDeserializer < Serializers::Base
13
+ class << self
14
+ # @!attribute STEPS
15
+ # An ordered list of methods to deserialize the record
16
+ #
17
+ # @return [Array] The steps to deserialize the record
18
+ attr_accessor :steps
19
+
20
+ # @!attribute RECORD_CLASS
21
+ # The class of the record being deserialized
22
+ #
23
+ # @return [Class] The class of the record
24
+ attr_accessor :record_class
25
+ end
26
+
27
+ # @!attribute io
28
+ # The input to read bytes from.
29
+ #
30
+ # @return [IO or StringIO] The input to read bytes from.
31
+ #
32
+ # @!attribute record
33
+ # The record instance being deserialized.
34
+ #
35
+ # @return [Record] The deserialized record.
36
+ attr_reader :io, :record
37
+
38
+ # Initialize a new deserializer
39
+ #
40
+ # @param io [IO or StringIO] The input to read bytes from.
41
+ # @return [BaseDeserializer] The new deserializer object
42
+ def initialize(io)
43
+ @io = io
44
+ @record = self.class.record_class.new
45
+ end
46
+
47
+ # Deserializes the record
48
+ #
49
+ # @return [Record] The deserialized record
50
+ def call
51
+ self.class.steps.each { send(_1) }
52
+ record
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Serializers
5
+ # Autoload serializers
6
+ autoload :TransactionSerializer, 'solace/serializers/transaction_serializer'
7
+ autoload :MessageSerializer, 'solace/serializers/message_serializer'
8
+ autoload :InstructionSerializer, 'solace/serializers/instruction_serializer'
9
+ autoload :AddressLookupTableSerializer, 'solace/serializers/address_lookup_table_serializer'
10
+
11
+ # Base serializer class
12
+ class BaseSerializer < Serializers::Base
13
+ class << self
14
+ # @!attribute STEPS
15
+ # An ordered list of methods to deserialize the record
16
+ #
17
+ # @return [Array] The steps to deserialize the record
18
+ attr_accessor :steps
19
+ end
20
+
21
+ # @!attribute record
22
+ # The record instance being serialized.
23
+ #
24
+ # @return [Record] The serialized record.
25
+ attr_reader :record
26
+
27
+ # Initialize a new serializer
28
+ #
29
+ # @param record [Record] The record to serialize
30
+ # @return [BaseSerializer] The new serializer object
31
+ def initialize(record)
32
+ @record = record
33
+ end
34
+
35
+ # Serializes the record
36
+ #
37
+ # @return [String] The serialized record (base64)
38
+ def call
39
+ bin = self.class
40
+ .steps
41
+ .map { |m| send(m) }
42
+ .flatten
43
+ .compact
44
+ .pack('C*')
45
+
46
+ Base64.strict_encode64(bin)
47
+ rescue NameError => e
48
+ raise "STEPS must be defined: #{e.message}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Instruction Deserializer
5
+ # =============================
6
+ #
7
+ # Deserializes a binary instruction into a Solace::Instruction object.
8
+ module Solace
9
+ module Serializers
10
+ class InstructionDeserializer < Solace::Serializers::BaseDeserializer
11
+ # @!attribute record_class
12
+ # The class of the record being deserialized
13
+ #
14
+ # @return [Class] The class of the record
15
+ self.record_class = Solace::Instruction
16
+
17
+ # @!attribute steps
18
+ # An ordered list of methods to deserialize the instruction
19
+ #
20
+ # @return [Array] The steps to deserialize the instruction
21
+ self.steps = %i[
22
+ next_extract_program_index
23
+ next_extract_accounts
24
+ next_extract_data
25
+ ]
26
+
27
+ # Extracts the program index from the instruction
28
+ #
29
+ # The BufferLayout is:
30
+ # - [Program index (1 byte)]
31
+ #
32
+ # @return [Integer] The program index
33
+ def next_extract_program_index
34
+ record.program_index = io.read(1).ord
35
+ end
36
+
37
+ # Extracts the accounts from the instruction
38
+ #
39
+ # The BufferLayout is:
40
+ # - [Number of accounts (compact u16)]
41
+ # - [Accounts (variable length u8)]
42
+ #
43
+ # @return [Array] The accounts
44
+ def next_extract_accounts
45
+ length, = Codecs.decode_compact_u16(io)
46
+ record.accounts = io.read(length).unpack('C*')
47
+ end
48
+
49
+ # Extracts the instruction data from the instruction
50
+ #
51
+ # The BufferLayout is:
52
+ # - [Number of data bytes (compact u16)]
53
+ # - [Data bytes (variable length u8)]
54
+ #
55
+ # @return [Array] The instruction data
56
+ def next_extract_data
57
+ length, = Codecs.decode_compact_u16(io)
58
+ record.data = io.read(length).unpack('C*')
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Instruction Serializer
5
+ # =============================
6
+ #
7
+ # Serializes a Solana instruction to a binary format.
8
+ module Solace
9
+ module Serializers
10
+ class InstructionSerializer < Solace::Serializers::BaseSerializer
11
+ # @!attribute steps
12
+ # An ordered list of methods to serialize the instruction
13
+ #
14
+ # @return [Array] The steps to serialize the instruction
15
+ self.steps = %i[
16
+ encode_program_index
17
+ encode_accounts
18
+ encode_data
19
+ ]
20
+
21
+ # Encodes the program index of the instruction
22
+ #
23
+ # The BufferLayout is:
24
+ # - [Program index (u8)]
25
+ #
26
+ # @return [Integer] The bytes of the encoded program index
27
+ def encode_program_index
28
+ record.program_index
29
+ end
30
+
31
+ # Encodes the accounts of the instruction
32
+ #
33
+ # The BufferLayout is:
34
+ # - [Number of accounts (compact u16)]
35
+ # - [Accounts (variable length u8)]
36
+ #
37
+ # @return [Array<Integer>] The bytes of the encoded accounts
38
+ def encode_accounts
39
+ Codecs.encode_compact_u16(record.accounts.size).bytes + record.accounts
40
+ end
41
+
42
+ # Encodes the data of the instruction
43
+ #
44
+ # The BufferLayout is:
45
+ # - [Number of data bytes (compact u16)]
46
+ # - [Data bytes (variable length u8)]
47
+ #
48
+ # @return [Array<Integer>] The bytes of the encoded data
49
+ def encode_data
50
+ Codecs.encode_compact_u16(record.data.size).bytes + record.data
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Message Deserializer
5
+ # =============================
6
+ #
7
+ # Deserializes a binary message into a Solace::Message object.
8
+ module Solace
9
+ module Serializers
10
+ class MessageDeserializer < Solace::Serializers::BaseDeserializer
11
+ # @!attribute record_class
12
+ # The class of the record being deserialized
13
+ #
14
+ # @return [Class] The class of the record
15
+ self.record_class = Solace::Message
16
+
17
+ # @!attribute steps
18
+ # An ordered list of methods to deserialize the message
19
+ #
20
+ # @return [Array] The steps to deserialize the message
21
+ self.steps = %i[
22
+ next_extract_version
23
+ next_extract_message_header
24
+ next_extract_accounts
25
+ next_extract_recent_blockhash
26
+ next_extract_instructions
27
+ next_extract_address_lookup_table
28
+ ]
29
+
30
+ # Extract version from the message
31
+ #
32
+ # Checks for version prefix and extracts version. If the prefix is not found, it
33
+ # assumes a legacy message and sets no version.
34
+ #
35
+ # The BufferLayout is:
36
+ # - [Version prefix (1 byte)]
37
+ # - [Version (variable length)]
38
+ #
39
+ # @return [Integer] The version of the message
40
+ def next_extract_version
41
+ next_byte = io.read(1).unpack1('C')
42
+
43
+ if next_byte & 0x80 == 0x80
44
+ record.version = next_byte & 0x7F
45
+ else
46
+ io.seek(-1, IO::SEEK_CUR)
47
+ record.version = nil
48
+ end
49
+ end
50
+
51
+ # Extract message header from the message
52
+ #
53
+ # The BufferLayout is:
54
+ # - [Message header (3 bytes)]
55
+ #
56
+ # @return [Array<Integer>] The message header of the message
57
+ def next_extract_message_header
58
+ record.header = io.read(3).bytes
59
+ end
60
+
61
+ # Extract account keys from the message
62
+ #
63
+ # The BufferLayout is:
64
+ # - [Number of accounts (compact u16)]
65
+ # - [Accounts (variable length u8)]
66
+ #
67
+ # @return [Array<String>] The account keys of the message
68
+ def next_extract_accounts
69
+ count, = Codecs.decode_compact_u16(io)
70
+ record.accounts = count.times.map do
71
+ Codecs.bytes_to_base58 io.read(32).bytes
72
+ end
73
+ end
74
+
75
+ # Extract recent blockhash from the message
76
+ #
77
+ # The BufferLayout is:
78
+ # - [Recent blockhash (32 bytes)]
79
+ #
80
+ # @return [String] The recent blockhash of the message
81
+ def next_extract_recent_blockhash
82
+ record.recent_blockhash = Codecs.bytes_to_base58 io.read(32).bytes
83
+ end
84
+
85
+ # Extract instructions from the message
86
+ #
87
+ # The BufferLayout is:
88
+ # - [Number of instructions (compact u16)]
89
+ # - [Instructions (variable length)]
90
+ #
91
+ # @return [Array<Solace::Instruction>] The instructions of the message
92
+ def next_extract_instructions
93
+ count, = Codecs.decode_compact_u16(io)
94
+ record.instructions = count.times.map do
95
+ Solace::Instruction.deserialize(io)
96
+ end
97
+ end
98
+
99
+ # Extract address lookup table from the message
100
+ #
101
+ # The BufferLayout is:
102
+ # - [Number of address lookup tables (compact u16)]
103
+ # - [Address lookup tables (variable length)]
104
+ #
105
+ # @return [Array<Solace::AddressLookupTable>] The address lookup table of the message
106
+ def next_extract_address_lookup_table
107
+ return unless record.versioned?
108
+
109
+ count, = Codecs.decode_compact_u16(io)
110
+ record.address_lookup_tables = count.times.map do
111
+ Solace::AddressLookupTable.deserialize(io)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Message Serializer
5
+ # =============================
6
+ #
7
+ # Serializes a Solana message to a binary format.
8
+ module Solace
9
+ module Serializers
10
+ class MessageSerializer < Solace::Serializers::BaseSerializer
11
+ # @!attribute steps
12
+ # An ordered list of methods to serialize the message
13
+ #
14
+ # @return [Array] The steps to serialize the message
15
+ self.steps = %i[
16
+ encode_version
17
+ encode_message_header
18
+ encode_accounts
19
+ encode_recent_blockhash
20
+ encode_instructions
21
+ encode_address_lookup_table
22
+ ]
23
+
24
+ # Encodes the version of the message
25
+ #
26
+ # The BufferLayout is:
27
+ # - [Version (1 byte)]
28
+ #
29
+ # @return [Array<Integer>] | nil The bytes of the encoded version
30
+ def encode_version
31
+ [0x80 | record.version] if record.versioned?
32
+ end
33
+
34
+ # Encodes the message header of the transaction
35
+ #
36
+ # The BufferLayout is:
37
+ # - [Message header (3 bytes)]
38
+ #
39
+ # @return [Array<Integer>] The bytes of the encoded message header
40
+ def encode_message_header
41
+ record.header
42
+ end
43
+
44
+ # Encodes the accounts of the transaction
45
+ #
46
+ # The BufferLayout is:
47
+ # - [Number of accounts (compact u16)]
48
+ # - [Accounts (variable length)]
49
+ #
50
+ # @return [Array<Integer>] The bytes of the encoded accounts
51
+ def encode_accounts
52
+ Codecs.encode_compact_u16(record.accounts.size).bytes +
53
+ record.accounts.map { Codecs.base58_to_bytes(_1) }
54
+ end
55
+
56
+ # Encodes the recent blockhash of the transaction
57
+ #
58
+ # The BufferLayout is:
59
+ # - [Recent blockhash (32 bytes)]
60
+ #
61
+ # @return [Array<Integer>] The bytes of the encoded recent blockhash
62
+ def encode_recent_blockhash
63
+ raise 'Failed to serialize message: recent blockhash is nil' if record.recent_blockhash.nil?
64
+
65
+ Codecs.base58_to_bytes(record.recent_blockhash)
66
+ end
67
+
68
+ # Encodes the instructions of the transaction
69
+ #
70
+ # The BufferLayout is:
71
+ # - [Number of instructions (compact u16)]
72
+ # - [Instructions (variable length)]
73
+ #
74
+ # @return [Array<Integer>] The bytes of the encoded instructions
75
+ def encode_instructions
76
+ Codecs.encode_compact_u16(record.instructions.size).bytes +
77
+ record.instructions.map(&:to_bytes)
78
+ end
79
+
80
+ # Encodes the address lookup table of the transaction
81
+ #
82
+ # The BufferLayout is:
83
+ # - [Number of address lookup tables (compact u16)]
84
+ # - [Address lookup tables (variable length)]
85
+ #
86
+ # @return [Array<Integer>] The bytes of the encoded address lookup table
87
+ def encode_address_lookup_table
88
+ return unless record.versioned?
89
+
90
+ Codecs.encode_compact_u16(record.address_lookup_tables.size).bytes +
91
+ record.address_lookup_tables.map(&:to_bytes)
92
+ end
93
+ end
94
+ end
95
+ end