solace 0.0.10 → 0.1.1

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 +124 -24
  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 +92 -15
  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
@@ -24,20 +24,21 @@ module Solace
24
24
  # # Create an associated token account
25
25
  # result = program.create_associated_token_account(
26
26
  # payer: payer,
27
+ # funder: funder,
27
28
  # owner: owner,
28
29
  # mint: mint
29
30
  # )
30
31
  #
31
32
  # # Wait for the transaction to be finalized
32
- # @connection.wait_for_confirmed_signature('finalized') { result['result'] }
33
+ # connection.wait_for_confirmed_signature('finalized') { result['result'] }
33
34
  #
34
35
  # @since 0.0.2
35
36
  class AssociatedTokenAccount < Base
36
37
  class << self
37
38
  # Gets the address of an associated token account.
38
39
  #
39
- # @param owner [Solace::Keypair, Solace::PublicKey] The keypair of the owner.
40
- # @param mint [Solace::Keypair, Solace::PublicKey] The keypair of the mint.
40
+ # @param owner [Keypair, PublicKey] The keypair of the owner.
41
+ # @param mint [Keypair, PublicKey] The keypair of the mint.
41
42
  # @return [String] The address of the associated token account.
42
43
  def get_address(owner:, mint:)
43
44
  Solace::Utils::PDA.find_program_address(
@@ -68,48 +69,76 @@ module Solace
68
69
 
69
70
  # Gets the address of an associated token account, creating it if it doesn't exist.
70
71
  #
71
- # @param payer [Solace::Keypair] The keypair that will pay for fees and rent.
72
- # @param owner [Solace::Keypair, Solace::PublicKey] The keypair of the owner.
73
- # @param mint [Solace::Keypair, Solace::PublicKey] The keypair of the mint.
74
- # @param commitment [String] The commitment level for the get_account_info call.
72
+ # @param payer [Keypair] The keypair that will pay for fees and rent.
73
+ # @param funder [Keypair] The keypair that will pay for rent of the new associated token account.
74
+ # @param owner [Keypair, PublicKey] The keypair of the owner.
75
+ # @param mint [Keypair, PublicKey] The keypair of the mint.
75
76
  # @return [String] The address of the associated token account
76
- def get_or_create_address(payer:, owner:, mint:, commitment: 'confirmed')
77
- ata_address, _bump = get_address(owner: owner, mint: mint)
77
+ def get_or_create_address(
78
+ payer:,
79
+ funder:,
80
+ owner:,
81
+ mint:
82
+ )
83
+ ata_address, = get_address(owner: owner, mint: mint)
78
84
 
79
- account_balance = @connection.get_account_info(ata_address)
85
+ account_balance = connection.get_account_info(ata_address)
80
86
 
81
87
  return ata_address unless account_balance.nil?
82
88
 
83
- response = create_associated_token_account(payer: payer, owner: owner, mint: mint)
84
-
85
- raise 'Failed to create associated token account' unless response['result']
89
+ tx = create_associated_token_account(
90
+ payer: payer,
91
+ funder: funder,
92
+ owner: owner,
93
+ mint: mint
94
+ )
86
95
 
87
- @connection.wait_for_confirmed_signature(commitment) { response }
96
+ connection.wait_for_confirmed_signature { tx.signature }
88
97
 
89
98
  ata_address
90
99
  end
91
100
 
92
101
  # Creates a new associated token account.
93
102
  #
94
- # @param options [Hash] Options for calling the prepare_create_associated_token_account method.
95
- # @return [String] The signature of the transaction.
96
- def create_associated_token_account(**options)
97
- tx = prepare_create_associated_token_account(**options)
103
+ # @param payer [#to_s, Keypair] The keypair that will pay for fees and rent.
104
+ # @param sign [Boolean] Whether to sign the transaction before sending it.
105
+ # @param execute [Boolean] Whether to send the transaction to the cluster.
106
+ # @param composer_opts [Hash] Options for calling the compose_create_associated_token_account method.
107
+ # @return [Transaction] The created or sent transaction.
108
+ def create_associated_token_account(
109
+ payer:,
110
+ sign: true,
111
+ execute: true,
112
+ **composer_opts
113
+ )
114
+ composer = compose_create_associated_token_account(**composer_opts)
115
+
116
+ yield composer if block_given?
117
+
118
+ tx = composer
119
+ .set_fee_payer(payer)
120
+ .compose_transaction
121
+
122
+ if sign
123
+ tx.sign(
124
+ payer,
125
+ composer_opts[:funder]
126
+ )
98
127
 
99
- tx.sign(options[:payer])
128
+ connection.send_transaction(tx.serialize) if execute
129
+ end
100
130
 
101
- @connection.send_transaction(tx.serialize)
131
+ tx
102
132
  end
103
133
 
104
134
  # Prepares a new associated token account and returns the signed transaction.
105
135
  #
106
- # @param owner [Solace::Keypair, Solace::PublicKey] The keypair of the owner.
107
- # @param mint [Solace::Keypair, Solace::PublicKey] The keypair of the mint.
108
- # @param payer [Solace::Keypair] The keypair that will pay for fees and rent.
109
- # @return [Solace::Transaction] The signed transaction.
110
- #
111
- def prepare_create_associated_token_account(
112
- payer:,
136
+ # @param owner [#to_s, PublicKey] The keypair of the owner.
137
+ # @param mint [#to_s, PublicKey] The keypair of the mint.
138
+ # @param funder [#to_s, PublicKey] The keypair that will pay for rent of the new associated token account.
139
+ # @return [Transaction] The signed transaction.
140
+ def compose_create_associated_token_account(
141
+ funder:,
113
142
  owner:,
114
143
  mint:
115
144
  )
@@ -118,15 +147,13 @@ module Solace
118
147
  ix = Solace::Composers::AssociatedTokenAccountProgramCreateAccountComposer.new(
119
148
  mint: mint,
120
149
  owner: owner,
121
- funder: payer,
150
+ funder: funder,
122
151
  ata_address: ata_address
123
152
  )
124
153
 
125
154
  TransactionComposer
126
155
  .new(connection: connection)
127
- .set_fee_payer(payer)
128
156
  .add_instruction(ix)
129
- .compose_transaction
130
157
  end
131
158
  end
132
159
  end
@@ -3,6 +3,29 @@
3
3
  # lib/solace/programs/base.rb
4
4
 
5
5
  module Solace
6
+ # The Programs module contains high-level interfaces to Solana on-chain programs.
7
+ #
8
+ # Programs in this module provide convenient methods for interacting with
9
+ # on-chain programs without needing to manually construct instructions or
10
+ # manage account ordering. They serve as a bridge between the low-level
11
+ # instruction builders and high-level application code.
12
+ #
13
+ # Each program class corresponds to a specific on-chain program:
14
+ # - {Solace::Programs::SplToken} - SPL Token Program
15
+ # - {Solace::Programs::AssociatedTokenAccount} - Associated Token Account Program
16
+ #
17
+ # @example Using a program interface
18
+ # token_program = Solace::Programs::SplToken.new(connection)
19
+ # token_program.transfer(
20
+ # to:,
21
+ # from:,
22
+ # owner:,
23
+ # amount:
24
+ # )
25
+ #
26
+ # @see Solace::Programs::SplToken
27
+ # @see Solace::Programs::AssociatedTokenAccount
28
+ # @since 0.0.2
6
29
  module Programs
7
30
  # Base class for program-specific clients.
8
31
  #
@@ -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