solace 0.0.10 → 0.1.0

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +104 -23
  3. data/README.md +10 -8
  4. data/lib/solace/composers/base.rb +35 -0
  5. data/lib/solace/composers/spl_token_program_initialize_mint_composer.rb +95 -0
  6. data/lib/solace/composers/spl_token_program_mint_to_composer.rb +86 -0
  7. data/lib/solace/composers/spl_token_program_transfer_composer.rb +90 -0
  8. data/lib/solace/composers/system_program_create_account_composer.rb +98 -0
  9. data/lib/solace/connection.rb +88 -13
  10. data/lib/solace/errors/confirmation_timeout.rb +18 -4
  11. data/lib/solace/errors/http_error.rb +16 -1
  12. data/lib/solace/errors/parse_error.rb +15 -1
  13. data/lib/solace/errors/rpc_error.rb +17 -1
  14. data/lib/solace/errors.rb +8 -3
  15. data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +9 -2
  16. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +0 -1
  17. data/lib/solace/instructions/spl_token/transfer_instruction.rb +21 -0
  18. data/lib/solace/instructions/system_program/create_account_instruction.rb +30 -0
  19. data/lib/solace/instructions/system_program/transfer_instruction.rb +11 -0
  20. data/lib/solace/programs/associated_token_account.rb +57 -30
  21. data/lib/solace/programs/base.rb +23 -0
  22. data/lib/solace/programs/spl_token.rb +197 -125
  23. data/lib/solace/serializers/base_serializer.rb +29 -1
  24. data/lib/solace/tokens/token.rb +53 -0
  25. data/lib/solace/tokens.rb +86 -0
  26. data/lib/solace/transaction.rb +24 -21
  27. data/lib/solace/transaction_composer.rb +77 -3
  28. data/lib/solace/utils/account_context.rb +1 -1
  29. data/lib/solace/utils/codecs.rb +17 -0
  30. data/lib/solace/utils/pda.rb +13 -5
  31. data/lib/solace/version.rb +3 -2
  32. data/lib/solace.rb +38 -11
  33. metadata +21 -1
@@ -15,6 +15,7 @@ module Solace
15
15
  # # Create an SPL Token mint
16
16
  # result = program.create_mint(
17
17
  # payer: payer,
18
+ # funder: funder,
18
19
  # decimals: 6,
19
20
  # mint_keypair: mint_keypair,
20
21
  # mint_authority: mint_authority,
@@ -22,11 +23,9 @@ module Solace
22
23
  # )
23
24
  #
24
25
  # # Wait for the transaction to be finalized
25
- # @connection.wait_for_confirmed_signature('finalized') { result['result'] }
26
+ # connection.wait_for_confirmed_signature('finalized') { result['result'] }
26
27
  #
27
28
  # @since 0.0.2
28
- #
29
- # rubocop:disable Metrics/ClassLength
30
29
  class SplToken < Base
31
30
  # Initializes a new SPL Token client.
32
31
  #
@@ -37,189 +36,262 @@ module Solace
37
36
 
38
37
  # Creates a new SPL Token mint.
39
38
  #
40
- # @param options [Hash] Options for calling the prepare_create_mint method.
39
+ # @param payer [#to_s, PublicKey] The keypair that will pay for fees and rent.
40
+ # @param sign [Boolean] Whether to sign the transaction.
41
+ # @param execute [Boolean] Whether to execute the transaction.
42
+ # @param composer_opts [Hash] Options for calling the compose_create_mint method.
41
43
  # @return [String] The signature of the transaction.
42
- def create_mint(**options)
43
- tx = prepare_create_mint(**options)
44
+ def create_mint(
45
+ payer:,
46
+ sign: true,
47
+ execute: true,
48
+ **composer_opts
49
+ )
50
+ composer = compose_create_mint(**composer_opts)
51
+
52
+ yield composer if block_given?
44
53
 
45
- @connection.send_transaction(tx.serialize)
54
+ tx = composer
55
+ .set_fee_payer(payer)
56
+ .compose_transaction
57
+
58
+ if sign
59
+ tx.sign(
60
+ payer,
61
+ composer_opts[:funder],
62
+ composer_opts[:mint_account]
63
+ )
64
+
65
+ connection.send_transaction(tx.serialize) if execute
66
+ end
67
+
68
+ tx
46
69
  end
47
70
 
48
- # Prepares a new SPL Token mint and returns the signed transaction.
71
+ # Prepares a new SPL Token mint transaction.
49
72
  #
50
- # @param payer [Solace::Keypair] The keypair that will pay for fees and rent.
73
+ # @param funder [#to_s, PublicKey] The keypair that will pay for rent of the new mint account.
51
74
  # @param decimals [Integer] The number of decimal places for the token.
52
- # @param mint_authority [String] The base58 public key for the mint authority.
53
- # @param freeze_authority [String] (Optional) The base58 public key for the freeze authority.
54
- # @param mint_keypair [Solace::Keypair] (Optional) The keypair for the new mint.
55
- # @return [Solace::Transaction] The signed transaction.
56
- #
75
+ # @param mint_authority [#to_s, PublicKey] The base58 public key for the mint authority.
76
+ # @param freeze_authority [#to_s, PublicKey] (Optional) The base58 public key for the freeze authority.
77
+ # @param mint_account [#to_s, PublicKey] (Optional) The keypair for the new mint.
78
+ # @return [TransactionComposer] A composer with required instructions.
57
79
  # rubocop:disable Metrics/MethodLength
58
- def prepare_create_mint(
59
- payer:,
80
+ def compose_create_mint(
81
+ funder:,
60
82
  decimals:,
61
83
  mint_authority:,
62
- freeze_authority:,
63
- mint_keypair: Solace::Keypair.generate
84
+ freeze_authority: nil,
85
+ mint_account: Solace::Keypair.generate
64
86
  )
65
- accounts = [
66
- payer.to_s,
67
- mint_keypair.to_s,
68
- Solace::Constants::SYSVAR_RENT_PROGRAM_ID,
69
- Solace::Constants::TOKEN_PROGRAM_ID,
70
- Solace::Constants::SYSTEM_PROGRAM_ID
71
- ]
72
-
73
- rent_lamports = @connection.get_minimum_lamports_for_rent_exemption(82)
74
-
75
- create_account_ix = Solace::Instructions::SystemProgram::CreateAccountInstruction.build(
76
- from_index: 0,
77
- new_account_index: 1,
78
- system_program_index: 4,
87
+ # Mint accounts need 82 bytes of space, and we need to fund it with enough lamports to be rent-exempt
88
+ rent_lamports = connection.get_minimum_lamports_for_rent_exemption(82)
89
+
90
+ # Build the account for the mint
91
+ create_account_ix = Composers::SystemProgramCreateAccountComposer.new(
92
+ from: funder,
93
+ new_account: mint_account,
94
+ owner: program_id,
79
95
  lamports: rent_lamports,
80
- space: 82,
81
- owner: program_id
96
+ space: 82
82
97
  )
83
98
 
84
- freeze_authority = freeze_authority.to_s unless freeze_authority.nil?
85
-
86
- initialize_mint_ix = Solace::Instructions::SplToken::InitializeMintInstruction.build(
87
- mint_account_index: 1,
88
- rent_sysvar_index: 2,
89
- program_index: 3,
99
+ # Build the initialize mint composer
100
+ initialize_mint_ix = Composers::SplTokenProgramInitializeMintComposer.new(
90
101
  decimals: decimals,
91
- mint_authority: mint_authority.to_s,
102
+ mint_account: mint_account,
103
+ mint_authority: mint_authority,
92
104
  freeze_authority: freeze_authority
93
105
  )
94
106
 
95
- message = Message.new(
96
- header: [2, 0, 3],
97
- accounts: accounts,
98
- recent_blockhash: @connection.get_latest_blockhash[0],
99
- instructions: [create_account_ix, initialize_mint_ix]
100
- )
101
-
102
- tx = Transaction.new(message: message)
103
- tx.sign(payer, mint_keypair)
104
-
105
- tx
107
+ TransactionComposer
108
+ .new(connection: @connection)
109
+ .add_instruction(create_account_ix)
110
+ .add_instruction(initialize_mint_ix)
106
111
  end
107
112
  # rubocop:enable Metrics/MethodLength
108
113
 
109
114
  # Mint tokens to a token account
110
115
  #
111
- # @param options [Hash] Options for calling the prepare_mint_to method.
116
+ # @param payer [#to_s, PublicKey] The keypair that will pay for fees and rent.
117
+ # @param sign [Boolean] Whether to sign the transaction.
118
+ # @param execute [Boolean] Whether to execute the transaction.
119
+ # @param composer_opts [Hash] Options for calling the compose_mint_to method.
112
120
  # @return [String] The signature of the transaction.
113
- def mint_to(**options)
114
- tx = prepare_mint_to(**options)
121
+ def mint_to(
122
+ payer:,
123
+ sign: true,
124
+ execute: true,
125
+ **composer_opts
126
+ )
127
+ composer = compose_mint_to(**composer_opts)
128
+
129
+ yield composer if block_given?
130
+
131
+ tx = composer
132
+ .set_fee_payer(payer)
133
+ .compose_transaction
115
134
 
116
- @connection.send_transaction(tx.serialize)
135
+ if sign
136
+ tx.sign(
137
+ payer,
138
+ composer_opts[:mint_authority]
139
+ )
140
+
141
+ connection.send_transaction(tx.serialize) if execute
142
+ end
143
+
144
+ tx
117
145
  end
118
146
 
119
147
  # Prepares a mint to instruction and returns the signed transaction.
120
148
  #
121
149
  # @param [Integer] amount The amount of tokens to mint.
122
- # @param [PublicKey, Keypair, String] payer The payer of the transaction.
123
- # @param [PublicKey, Keypair, String] mint The mint of the token.
124
- # @param [PublicKey, Keypair, String] destination The destination of the token.
125
- # @param [PublicKey, Keypair, String] mint_authority The mint authority of the token.
126
- # @return [Solace::Transaction] The signed transaction.
150
+ # @param [#to_s, PublicKey] mint The mint of the token.
151
+ # @param [#to_s, PublicKey] destination The destination of the token.
152
+ # @param [#to_s, PublicKey] mint_authority The mint authority of the token.
153
+ # @return [TransactionComposer] A composer with required instructions.
127
154
  #
128
- # rubocop:disable Metrics/MethodLength
129
- def prepare_mint_to(
130
- payer:,
155
+ # @param [Boolean] ensure_account
156
+ def compose_mint_to(
131
157
  mint:,
132
- destination:,
133
158
  amount:,
159
+ destination:,
134
160
  mint_authority:
135
161
  )
136
- accounts = [
137
- payer.to_s,
138
- mint_authority.to_s,
139
- mint.to_s,
140
- destination.to_s,
141
- Solace::Constants::TOKEN_PROGRAM_ID.to_s
142
- ]
143
-
144
- ix = Solace::Instructions::SplToken::MintToInstruction.build(
162
+ ix = Composers::SplTokenProgramMintToComposer.new(
145
163
  amount: amount,
146
- mint_authority_index: 1,
147
- mint_index: 2,
148
- destination_index: 3,
149
- program_index: 4
164
+ mint: mint,
165
+ destination: destination,
166
+ mint_authority: mint_authority
150
167
  )
151
168
 
152
- message = Solace::Message.new(
153
- header: [2, 0, 1],
154
- accounts: accounts,
155
- instructions: [ix],
156
- recent_blockhash: connection.get_latest_blockhash[0]
157
- )
158
-
159
- tx = Solace::Transaction.new(message: message)
160
- tx.sign(payer, mint_authority)
161
-
162
- tx
169
+ TransactionComposer
170
+ .new(connection: connection)
171
+ .add_instruction(ix)
163
172
  end
164
- # rubocop:enable Metrics/MethodLength
165
173
 
166
174
  # Transfers tokens from one account to another
167
175
  #
168
- # @param options [Hash] Options for calling the prepare_transfer method.
176
+ # @param payer [#to_s, PublicKey] The keypair that will pay for fees and rent.
177
+ # @param sign [Boolean] Whether to sign the transaction.
178
+ # @param execute [Boolean] Whether to execute the transaction.
179
+ # @param composer_opts [Hash] Options for calling the compose_transfer method.
169
180
  # @return [String] The signature of the transaction.
170
- def transfer(**options)
171
- tx = prepare_transfer(**options)
181
+ def transfer(
182
+ payer:,
183
+ sign: true,
184
+ execute: true,
185
+ **composer_opts
186
+ )
187
+ composer = compose_transfer(**composer_opts)
188
+
189
+ yield composer if block_given?
172
190
 
173
- @connection.send_transaction(tx.serialize)
191
+ tx = composer
192
+ .set_fee_payer(payer)
193
+ .compose_transaction
194
+
195
+ if sign
196
+ tx.sign(
197
+ payer,
198
+ composer_opts[:owner]
199
+ )
200
+
201
+ connection.send_transaction(tx.serialize) if execute
202
+ end
203
+ tx
174
204
  end
175
205
 
176
206
  # Prepares a transfer instruction and returns the signed transaction.
177
207
  #
178
- # @param payer [Solace::Keypair] The keypair that will pay for fees and rent.
179
- # @param source [String] The source token account address.
180
- # @param destination [String] The destination token account address.
208
+ # @param source [#to_s, PublicKey] The source token account address.
209
+ # @param destination [#to_s, PublicKey] The destination token account address.
181
210
  # @param amount [Integer] The number of tokens to transfer.
182
- # @param owner [Solace::Keypair] The keypair of the owner of the source account.
183
- # @return [Solace::Transaction] The signed transaction.
211
+ # @param owner [#to_s, PublicKey] The keypair of the owner of the source account.
212
+ # @return [TransactionComposer] A composer with required instructions.
184
213
  #
185
- # rubocop:disable Metrics/MethodLength
186
- def prepare_transfer(
214
+ def compose_transfer(
187
215
  amount:,
188
- payer:,
189
216
  source:,
190
217
  destination:,
191
218
  owner:
192
219
  )
193
- accounts = [
194
- payer.to_s,
195
- owner.to_s,
196
- source.to_s,
197
- destination.to_s,
198
- Solace::Constants::TOKEN_PROGRAM_ID.to_s
199
- ]
200
-
201
- ix = Solace::Instructions::SplToken::TransferInstruction.build(
220
+ ix = Composers::SplTokenProgramTransferComposer.new(
202
221
  amount: amount,
203
- owner_index: 1,
204
- source_index: 2,
205
- destination_index: 3,
206
- program_index: 4
222
+ owner: owner,
223
+ source: source,
224
+ destination: destination
207
225
  )
208
226
 
209
- message = Solace::Message.new(
210
- header: [2, 0, 1],
211
- accounts: accounts,
212
- instructions: [ix],
213
- recent_blockhash: connection.get_latest_blockhash[0]
214
- )
227
+ TransactionComposer
228
+ .new(connection: connection)
229
+ .add_instruction(ix)
230
+ end
231
+
232
+ # Transfers tokens with decimal precision and validation checks
233
+ #
234
+ # @param payer [#to_s, PublicKey] The keypair that will pay for fees and rent.
235
+ # @param sign [Boolean] Whether to sign the transaction.
236
+ # @param execute [Boolean] Whether to execute the transaction.
237
+ # @param composer_opts [Hash] Options for calling the compose_transfer_checked method.
238
+ # @return [String] The signature of the transaction.
239
+ def transfer_checked(
240
+ payer:,
241
+ sign: true,
242
+ execute: true,
243
+ **composer_opts
244
+ )
245
+ composer = compose_transfer_checked(**composer_opts)
246
+
247
+ yield composer if block_given?
248
+
249
+ tx = composer
250
+ .set_fee_payer(payer)
251
+ .compose_transaction
252
+
253
+ if sign
254
+ tx.sign(
255
+ payer,
256
+ composer_opts[:authority]
257
+ )
215
258
 
216
- tx = Solace::Transaction.new(message: message)
217
- tx.sign(payer, owner)
259
+ connection.send_transaction(tx.serialize) if execute
260
+ end
218
261
 
219
262
  tx
220
263
  end
221
- # rubocop:enable Metrics/MethodLength
264
+
265
+ # Prepares a transfer checked instruction and returns the signed transaction.
266
+ #
267
+ # @param amount [Integer] The number of tokens to transfer.
268
+ # @param decimals [Integer] The number of decimals for the token.
269
+ # @param from [#to_s, PublicKey] The source token account address.
270
+ # @param to [#to_s, PublicKey] The destination token account address.
271
+ # @param mint [#to_s, PublicKey] The mint address
272
+ # @param authority [#to_s, PublicKey] The keypair of the owner of the source account.
273
+ # @return [TransactionComposer] A composer with required instructions.
274
+ def compose_transfer_checked(
275
+ to:,
276
+ from:,
277
+ mint:,
278
+ authority:,
279
+ amount:,
280
+ decimals:
281
+ )
282
+ ix = Composers::SplTokenProgramTransferCheckedComposer.new(
283
+ to: to,
284
+ from: from,
285
+ mint: mint,
286
+ authority: authority,
287
+ amount: amount,
288
+ decimals: decimals
289
+ )
290
+
291
+ TransactionComposer
292
+ .new(connection: connection)
293
+ .add_instruction(ix)
294
+ end
222
295
  end
223
- # rubocop:enable Metrics/ClassLength
224
296
  end
225
297
  end
@@ -1,7 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solace
4
- # Serializers module
4
+ # The Serializers module contains classes for converting data structures to and
5
+ # from the binary format required by the Solana runtime.
6
+ #
7
+ # Solana transactions, messages, and instructions use a compact binary encoding
8
+ # for efficiency. The serializers in this module handle the conversion between
9
+ # Ruby objects and this binary format, including proper handling of:
10
+ # - Compact array encoding
11
+ # - Account key serialization
12
+ # - Instruction data packing
13
+ # - Message header construction
14
+ #
15
+ # Each serializer corresponds to a specific data structure:
16
+ # - {Solace::Serializers::TransactionSerializer} - Complete transactions
17
+ # - {Solace::Serializers::MessageSerializer} - Transaction messages
18
+ # - {Solace::Serializers::InstructionSerializer} - Individual instructions
19
+ # - {Solace::Serializers::AddressLookupTableSerializer} - Address lookup tables
20
+ #
21
+ # Each deserializer handles the inverse operation, converting binary data:
22
+ # - {Solace::Serializers::TransactionDeserializer}
23
+ # - {Solace::Serializers::MessageDeserializer}
24
+ # - {Solace::Serializers::InstructionDeserializer}
25
+ # - {Solace::Serializers::AddressLookupTableDeserializer}
26
+ #
27
+ # These utilities are primarily used internally by other parts of the gem, but
28
+ # can also be used directly for advanced use cases.
29
+ #
30
+ # @see Solace::Serializers::BaseSerializer
31
+ # @see Solace::Serializers::BaseDeserializer
32
+ # @since 0.0.1
5
33
  module Serializers
6
34
  # Autoload serializers
7
35
  autoload :TransactionSerializer, 'solace/serializers/transaction_serializer'
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Tokens
5
+ # Represents a Solana token with its metadata.
6
+ #
7
+ # A token object encapsulates the symbol and associated metadata for a Solana token. It provides
8
+ # dynamic access to metadata attributes via method calls. This class is used within the Solace::Tokens
9
+ # module to represent individual tokens loaded from a YAML configuration file.
10
+ #
11
+ # @since 0.1.0
12
+ class Token
13
+ attr_reader :symbol, :metadata
14
+
15
+ # Initializes a new Token instance.
16
+ #
17
+ # @param symbol [String] The symbol of the token (e.g., 'USDC')
18
+ # @param metadata [Hash] A hash containing the token's metadata attributes
19
+ # @return [self] The initialized Token object
20
+ def initialize(symbol, metadata)
21
+ @symbol = symbol
22
+ @metadata = metadata.transform_keys(&:to_sym)
23
+ end
24
+
25
+ # Dynamically access metadata attributes.
26
+ #
27
+ # @param name [Symbol] The name of the metadata attribute
28
+ # @return [Object] The value of the metadata attribute if it exists
29
+ # @raise [NoMethodError] If the attribute does not exist
30
+ def method_missing(name, *)
31
+ return metadata[name] if metadata.key?(name)
32
+
33
+ super
34
+ end
35
+
36
+ # Check if a metadata attribute exists.
37
+ #
38
+ # @param name [Symbol] The name of the metadata attribute
39
+ # @param _include_private [Boolean] Whether to include private methods (not used)
40
+ # @return [Boolean] True if the attribute exists, false otherwise
41
+ def respond_to_missing?(name, _include_private = false)
42
+ metadata.key?(name) || super
43
+ end
44
+
45
+ # Returns a string representation of the Token object.
46
+ #
47
+ # @return [String] The string representation of the Token
48
+ def inspect
49
+ "#<Solace::Token #{symbol} #{metadata.inspect}>"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Solace
6
+ # Represents a Solana token with its metadata.
7
+ #
8
+ # A tokens object encapsulates the symbol and associated metadata for a Solana token. It provides
9
+ # dynamic access to metadata attributes via method calls. This class is used within the Solace::Tokens
10
+ # module to represent individual tokens loaded from a YAML configuration file.
11
+ #
12
+ # @example Load tokens for the 'mainnet' network
13
+ # Solace::Tokens.load(path: 'path/to/tokens.yml', network: 'mainnet')
14
+ #
15
+ # @example Access a specific token
16
+ # usdc = Solace::Tokens.fetch('USDC')
17
+ # puts usdc.address
18
+ #
19
+ # @example Query tokens by criteria
20
+ # stablecoins = Solace::Tokens.where(type: 'stablecoin')
21
+ #
22
+ # @since 0.1.0
23
+ module Tokens
24
+ autoload :Token, File.expand_path('tokens/token', __dir__)
25
+
26
+ # Load tokens from a YAML file for a specific network
27
+ #
28
+ # @param path [String] Path to the YAML file
29
+ # @param network [String, Symbol] Network name (e.g., 'mainnet', 'testnet')
30
+ # @return [void]
31
+ def self.load(path:, network:)
32
+ data = YAML.load_file(path)
33
+ tokens = data.fetch(network.to_s) do
34
+ raise ArgumentError, "Network '#{network}' not found in config"
35
+ end
36
+
37
+ # Clear previous constants and registry
38
+ clear!
39
+
40
+ @registry = {}
41
+
42
+ tokens.each do |symbol, attrs|
43
+ token = Solace::Tokens::Token.new(symbol, attrs)
44
+ const_set(symbol, token)
45
+ @registry[symbol.to_s] = token
46
+ end
47
+ end
48
+
49
+ # Clear loaded tokens
50
+ #
51
+ # @return [void]
52
+ def self.clear!
53
+ (constants - [:Token]).each { |c| remove_const(c) }
54
+ @registry = {}
55
+ end
56
+
57
+ # Get all loaded tokens
58
+ #
59
+ # @return [Array<Solace::Tokens::Token>]
60
+ def self.all
61
+ @registry.values
62
+ end
63
+
64
+ # Fetch a token by its symbol
65
+ #
66
+ # @param symbol [String] The symbol of the token
67
+ # @return [Solace::Tokens::Token, nil] The token object or nil if not found
68
+ def self.fetch(symbol)
69
+ @registry[symbol.to_s]
70
+ end
71
+
72
+ # Query tokens based on criteria
73
+ #
74
+ # @param criteria [Hash{Symbol => Object}] Key-value pairs to filter tokens
75
+ # @return [Array<Solace::Tokens::Token>] Array of tokens matching the criteria
76
+ def self.where(criteria = {})
77
+ return all if criteria.empty?
78
+
79
+ normalized = criteria.transform_keys(&:to_sym)
80
+
81
+ all.select do |token|
82
+ normalized.all? { |k, v| token.metadata[k] == v }
83
+ end
84
+ end
85
+ end
86
+ end
@@ -6,17 +6,17 @@ module Solace
6
6
  #
7
7
  # Transactions are the basic building blocks of Solana. They contain a message and an array of signatures. The
8
8
  # message contains the instructions to be executed and the accounts that are used by the instructions. The signatures
9
- # are the signatures of the accounts that are used by the instructions. This class provides methods for signing,
10
- # serializing, and deserializing transactions.
9
+ # are the required signatures of the accounts that are used by the instructions. This class provides methods for
10
+ # signing, serializing, and deserializing transactions.
11
11
  #
12
12
  # The BufferLayout is:
13
- # - [Signatures (variable length)]
14
- # - [Version (1 byte)] (if versioned)
15
- # - [Message header (3 bytes)]
16
- # - [Account keys (variable length)]
17
- # - [Recent blockhash (32 bytes)]
18
- # - [Instructions (variable length)]
19
- # - [Address lookup table (variable length)] (if versioned)
13
+ # - Signatures (variable length)
14
+ # - Version (1 byte if versioned)
15
+ # - Message header (3 bytes)
16
+ # - Account keys (variable length)
17
+ # - Recent blockhash (32 bytes)
18
+ # - Instructions (variable length)
19
+ # - Address lookup table (variable length if versioned)
20
20
  #
21
21
  # @example
22
22
  # # Create a new transaction
@@ -32,31 +32,26 @@ module Solace
32
32
  class Transaction
33
33
  include Solace::Concerns::BinarySerializable
34
34
 
35
- # @!attribute SERIALIZER
36
- # @return [Solace::Serializers::TransactionSerializer] The serializer for the transaction
35
+ # The serializer for the transaction
37
36
  SERIALIZER = Solace::Serializers::TransactionSerializer
38
37
 
39
- # @!attribute DESERIALIZER
40
- # @return [Solace::Serializers::TransactionDeserializer] The deserializer for the transaction
38
+ # The deserializer for the transaction
41
39
  DESERIALIZER = Solace::Serializers::TransactionDeserializer
42
40
 
43
- # @!attribute SIGNATURE_PLACEHOLDER
44
- # @return [String] Placeholder for a signature in the transaction
41
+ # Placeholder for a signature in the transaction
45
42
  SIGNATURE_PLACEHOLDER = Solace::Utils::Codecs.base58_to_binary('1' * 64)
46
43
 
47
- # @!attribute [rw] signatures
48
- # @return [Array<String>] Signatures of the transaction (binary)
44
+ # @return [Array<String>] Signatures of the transaction (binary)
49
45
  attr_accessor :signatures
50
46
 
51
- # @!attribute [rw] message
52
- # @return [Solace::Message] Message of the transaction
47
+ # @return [Solace::Message] Message of the transaction
53
48
  attr_accessor :message
54
49
 
55
50
  class << self
56
51
  # Deserialize a base64 encoded transaction into a Solace::Transaction object
57
52
  #
58
53
  # @param base64_tx [String] The base64 encoded transaction
59
- # @return [Solace::Transaction] The deserialized transaction
54
+ # @return [Transaction] The deserialized transaction
60
55
  def from(base64_tx)
61
56
  DESERIALIZER.new(Solace::Utils::Codecs.base64_to_bytestream(base64_tx)).call
62
57
  end
@@ -64,7 +59,7 @@ module Solace
64
59
 
65
60
  # Initialize a new transaction
66
61
  #
67
- # @return [Solace::Transaction] The new transaction object
62
+ # @return [Transaction] The new transaction object
68
63
  def initialize(
69
64
  signatures: [],
70
65
  message: Solace::Message.new
@@ -74,6 +69,14 @@ module Solace
74
69
  @message = message
75
70
  end
76
71
 
72
+ # Returns the first signature of the transaction (signature of the transaction fee payer)
73
+ #
74
+ # @return [String, nil] The first signature of the transaction or nil if there are no signatures
75
+ # @since 0.1.0
76
+ def signature
77
+ Utils::Codecs.binary_to_base58(signatures.first) unless signatures.empty?
78
+ end
79
+
77
80
  # Sign the transaction
78
81
  #
79
82
  # Calls sign_and_update_signatures for each keypair passed in.