solace 0.1.4 → 0.1.6

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solace/composers/associated_token_account_program_create_account_composer.rb +11 -10
  3. data/lib/solace/composers/associated_token_account_program_create_idempotent_account_composer.rb +2 -1
  4. data/lib/solace/composers/base.rb +1 -1
  5. data/lib/solace/composers/spl_token_program_close_account_composer.rb +3 -3
  6. data/lib/solace/composers/spl_token_program_initialize_mint_composer.rb +5 -5
  7. data/lib/solace/composers/spl_token_program_mint_to_composer.rb +4 -4
  8. data/lib/solace/composers/spl_token_program_transfer_checked_composer.rb +6 -6
  9. data/lib/solace/composers/spl_token_program_transfer_composer.rb +4 -4
  10. data/lib/solace/composers/system_program_create_account_composer.rb +5 -5
  11. data/lib/solace/composers/system_program_transfer_composer.rb +3 -3
  12. data/lib/solace/composers/token_2022_program_close_account_composer.rb +75 -0
  13. data/lib/solace/composers/token_2022_program_initialize_mint_composer.rb +93 -0
  14. data/lib/solace/composers/token_2022_program_mint_to_composer.rb +84 -0
  15. data/lib/solace/composers/token_2022_program_transfer_checked_composer.rb +109 -0
  16. data/lib/solace/composers/token_2022_program_transfer_composer.rb +86 -0
  17. data/lib/solace/connection.rb +29 -9
  18. data/lib/solace/constants.rb +8 -2
  19. data/lib/solace/errors/confirmation_timeout.rb +4 -4
  20. data/lib/solace/errors/rpc_error.rb +4 -4
  21. data/lib/solace/instructions/associated_token_account/create_account_instruction.rb +2 -1
  22. data/lib/solace/instructions/spl_token/close_account_instruction.rb +2 -2
  23. data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +2 -2
  24. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +2 -2
  25. data/lib/solace/instructions/spl_token/mint_to_instruction.rb +2 -2
  26. data/lib/solace/instructions/spl_token/transfer_checked_instruction.rb +3 -3
  27. data/lib/solace/instructions/spl_token/transfer_instruction.rb +2 -2
  28. data/lib/solace/instructions/system_program/create_account_instruction.rb +2 -2
  29. data/lib/solace/instructions/system_program/transfer_instruction.rb +2 -2
  30. data/lib/solace/instructions/token_2022/close_account_instruction.rb +50 -0
  31. data/lib/solace/instructions/token_2022/initialize_account_instruction.rb +62 -0
  32. data/lib/solace/instructions/token_2022/initialize_mint_instruction.rb +75 -0
  33. data/lib/solace/instructions/token_2022/mint_to_instruction.rb +59 -0
  34. data/lib/solace/instructions/token_2022/transfer_checked_instruction.rb +68 -0
  35. data/lib/solace/instructions/token_2022/transfer_instruction.rb +71 -0
  36. data/lib/solace/message.rb +5 -5
  37. data/lib/solace/programs/associated_token_account.rb +35 -17
  38. data/lib/solace/programs/spl_token.rb +18 -254
  39. data/lib/solace/programs/token_2022.rb +70 -0
  40. data/lib/solace/programs/token_program_interface.rb +312 -0
  41. data/lib/solace/serializers/address_lookup_table_deserializer.rb +2 -2
  42. data/lib/solace/serializers/base_deserializer.rb +1 -1
  43. data/lib/solace/serializers/instruction_deserializer.rb +2 -2
  44. data/lib/solace/serializers/message_deserializer.rb +3 -3
  45. data/lib/solace/serializers/transaction_deserializer.rb +1 -1
  46. data/lib/solace/tokens/token.rb +1 -1
  47. data/lib/solace/tokens.rb +2 -2
  48. data/lib/solace/transaction.rb +1 -1
  49. data/lib/solace/transaction_composer.rb +5 -5
  50. data/lib/solace/utils/account_context.rb +9 -9
  51. data/lib/solace/utils/codecs.rb +309 -49
  52. data/lib/solace/utils/rpc_client.rb +9 -9
  53. data/lib/solace/version.rb +1 -1
  54. data/lib/solace.rb +2 -0
  55. metadata +124 -9
  56. data/CHANGELOG +0 -226
  57. data/LICENSE +0 -21
  58. data/README.md +0 -518
@@ -68,11 +68,11 @@ module Solace
68
68
  )
69
69
  super()
70
70
 
71
- @version = version
72
- @header = header
73
- @accounts = accounts
74
- @recent_blockhash = recent_blockhash
75
- @instructions = instructions
71
+ @version = version
72
+ @header = header
73
+ @accounts = accounts
74
+ @recent_blockhash = recent_blockhash
75
+ @instructions = instructions
76
76
  @address_lookup_tables = address_lookup_tables
77
77
  end
78
78
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The AssociatedTokenAccount program is a Solana program that provides a standardized way to create and manage token accounts.
3
+ # The AssociatedTokenAccount program is a Solana program that provides a standardized
4
+ # way to create and manage token accounts.
4
5
  #
5
6
  # This class provides a Ruby interface to the Associated Token Account program, allowing you to easily
6
7
  # find or create associated token accounts for a given wallet and mint.
@@ -37,14 +38,25 @@ module Solace
37
38
  class << self
38
39
  # Gets the address of an associated token account.
39
40
  #
41
+ # The token program ID is part of the ATA derivation seed. For mints
42
+ # owned by the legacy SPL Token program this is {Constants::TOKEN_PROGRAM_ID}
43
+ # (the default). For mints owned by the Token-2022 program (e.g. PYUSD on
44
+ # Solana), pass +token_program_id: Solace::Constants::TOKEN_2022_PROGRAM_ID+
45
+ # to derive the correct address. Deriving with the wrong program ID
46
+ # returns an address that does not exist on chain.
47
+ #
48
+ # Use {Connection#get_mint_program_id} to discover which token program
49
+ # owns a given mint at runtime.
50
+ #
40
51
  # @param owner [Keypair, PublicKey] The keypair of the owner.
41
52
  # @param mint [Keypair, PublicKey] The keypair of the mint.
42
- # @return [String] The address of the associated token account.
43
- def get_address(owner:, mint:)
53
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
54
+ # @return [Array<String, Integer>] The associated token account address and bump seed.
55
+ def get_address(owner:, mint:, token_program_id: Solace::Constants::TOKEN_PROGRAM_ID)
44
56
  Solace::Utils::PDA.find_program_address(
45
57
  [
46
58
  owner.to_s,
47
- Solace::Constants::TOKEN_PROGRAM_ID,
59
+ token_program_id.to_s,
48
60
  mint.to_s
49
61
  ],
50
62
  Solace::Constants::ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
@@ -73,24 +85,27 @@ module Solace
73
85
  # @param funder [Keypair] The keypair that will pay for rent of the new associated token account.
74
86
  # @param owner [Keypair, PublicKey] The keypair of the owner.
75
87
  # @param mint [Keypair, PublicKey] The keypair of the mint.
88
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
76
89
  # @return [String] The address of the associated token account
77
90
  def get_or_create_address(
78
91
  payer:,
79
92
  funder:,
80
93
  owner:,
81
- mint:
94
+ mint:,
95
+ token_program_id: Solace::Constants::TOKEN_PROGRAM_ID
82
96
  )
83
- ata_address, = get_address(owner: owner, mint: mint)
97
+ ata_address, = get_address(owner: owner, mint: mint, token_program_id: token_program_id)
84
98
 
85
99
  account_balance = connection.get_account_info(ata_address)
86
100
 
87
101
  return ata_address unless account_balance.nil?
88
102
 
89
103
  tx = create_associated_token_account(
90
- payer: payer,
91
- funder: funder,
92
- owner: owner,
93
- mint: mint
104
+ payer: payer,
105
+ funder: funder,
106
+ owner: owner,
107
+ mint: mint,
108
+ token_program_id: token_program_id
94
109
  )
95
110
 
96
111
  connection.wait_for_confirmed_signature { tx.signature }
@@ -136,19 +151,22 @@ module Solace
136
151
  # @param owner [#to_s, PublicKey] The keypair of the owner.
137
152
  # @param mint [#to_s, PublicKey] The keypair of the mint.
138
153
  # @param funder [#to_s, PublicKey] The keypair that will pay for rent of the new associated token account.
139
- # @return [Transaction] The signed transaction.
154
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
155
+ # @return [TransactionComposer] A composer with the create-ATA instruction.
140
156
  def compose_create_associated_token_account(
141
157
  funder:,
142
158
  owner:,
143
- mint:
159
+ mint:,
160
+ token_program_id: Solace::Constants::TOKEN_PROGRAM_ID
144
161
  )
145
- ata_address, = get_address(owner: owner, mint: mint)
162
+ ata_address, = get_address(owner: owner, mint: mint, token_program_id: token_program_id)
146
163
 
147
164
  ix = Solace::Composers::AssociatedTokenAccountProgramCreateAccountComposer.new(
148
- mint: mint,
149
- owner: owner,
150
- funder: funder,
151
- ata_address: ata_address
165
+ mint: mint,
166
+ owner: owner,
167
+ funder: funder,
168
+ ata_address: ata_address,
169
+ token_program_id: token_program_id
152
170
  )
153
171
 
154
172
  TransactionComposer
@@ -2,11 +2,13 @@
2
2
 
3
3
  module Solace
4
4
  module Programs
5
- # Client for interacting with the SPL Token Program.
5
+ # Client for interacting with the legacy SPL Token Program.
6
6
  #
7
- # This client provides methods for interacting with the SPL Token Program. It is a wrapper around
8
- # the SPL Token Program and provides a more convenient interface for creating and managing SPL
9
- # Token mints and accounts.
7
+ # This client provides methods for creating mints, minting tokens, and
8
+ # transferring tokens via the legacy SPL Token program
9
+ # (+TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA+). The shared method shape
10
+ # comes from {TokenProgramInterface}; this class supplies the SPL-bound
11
+ # composer classes.
10
12
  #
11
13
  # @example Create an SPL Token mint
12
14
  # # Initialize the program with a connection
@@ -25,8 +27,11 @@ module Solace
25
27
  # # Wait for the transaction to be finalized
26
28
  # connection.wait_for_confirmed_signature('finalized') { result['result'] }
27
29
  #
30
+ # @see Solace::Programs::Token2022 for the Token-2022 successor program.
28
31
  # @since 0.0.2
29
32
  class SplToken < Base
33
+ include TokenProgramInterface
34
+
30
35
  # Initializes a new SPL Token client.
31
36
  #
32
37
  # @param connection [Solace::Connection] The connection to the Solana cluster.
@@ -34,263 +39,22 @@ module Solace
34
39
  super(connection: connection, program_id: Solace::Constants::TOKEN_PROGRAM_ID)
35
40
  end
36
41
 
37
- # Creates a new SPL Token mint.
38
- #
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.
43
- # @return [String] The signature of the transaction.
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?
53
-
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
69
- end
70
-
71
- # Prepares a new SPL Token mint transaction.
72
- #
73
- # @param funder [#to_s, PublicKey] The keypair that will pay for rent of the new mint account.
74
- # @param decimals [Integer] The number of decimal places for the token.
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.
79
- # rubocop:disable Metrics/MethodLength
80
- def compose_create_mint(
81
- funder:,
82
- decimals:,
83
- mint_authority:,
84
- freeze_authority: nil,
85
- mint_account: Solace::Keypair.generate
86
- )
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,
95
- lamports: rent_lamports,
96
- space: 82
97
- )
42
+ private
98
43
 
99
- # Build the initialize mint composer
100
- initialize_mint_ix = Composers::SplTokenProgramInitializeMintComposer.new(
101
- decimals: decimals,
102
- mint_account: mint_account,
103
- mint_authority: mint_authority,
104
- freeze_authority: freeze_authority
105
- )
106
-
107
- TransactionComposer
108
- .new(connection: @connection)
109
- .add_instruction(create_account_ix)
110
- .add_instruction(initialize_mint_ix)
44
+ def initialize_mint_composer_class
45
+ Composers::SplTokenProgramInitializeMintComposer
111
46
  end
112
- # rubocop:enable Metrics/MethodLength
113
-
114
- # Mint tokens to a token account
115
- #
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.
120
- # @return [String] The signature of the transaction.
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
134
-
135
- if sign
136
- tx.sign(
137
- payer,
138
- composer_opts[:mint_authority]
139
- )
140
47
 
141
- connection.send_transaction(tx.serialize) if execute
142
- end
143
-
144
- tx
48
+ def mint_to_composer_class
49
+ Composers::SplTokenProgramMintToComposer
145
50
  end
146
51
 
147
- # Prepares a mint to instruction and returns the signed transaction.
148
- #
149
- # @param [Integer] amount The amount of tokens to mint.
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.
154
- #
155
- # @param [Boolean] ensure_account
156
- def compose_mint_to(
157
- mint:,
158
- amount:,
159
- destination:,
160
- mint_authority:
161
- )
162
- ix = Composers::SplTokenProgramMintToComposer.new(
163
- amount: amount,
164
- mint: mint,
165
- destination: destination,
166
- mint_authority: mint_authority
167
- )
168
-
169
- TransactionComposer
170
- .new(connection: connection)
171
- .add_instruction(ix)
52
+ def transfer_composer_class
53
+ Composers::SplTokenProgramTransferComposer
172
54
  end
173
55
 
174
- # Transfers tokens from one account to another
175
- #
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.
180
- # @return [String] The signature of the transaction.
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?
190
-
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
204
- end
205
-
206
- # Prepares a transfer instruction and returns the signed transaction.
207
- #
208
- # @param source [#to_s, PublicKey] The source token account address.
209
- # @param destination [#to_s, PublicKey] The destination token account address.
210
- # @param amount [Integer] The number of tokens to transfer.
211
- # @param owner [#to_s, PublicKey] The keypair of the owner of the source account.
212
- # @return [TransactionComposer] A composer with required instructions.
213
- #
214
- def compose_transfer(
215
- amount:,
216
- source:,
217
- destination:,
218
- owner:
219
- )
220
- ix = Composers::SplTokenProgramTransferComposer.new(
221
- amount: amount,
222
- owner: owner,
223
- source: source,
224
- destination: destination
225
- )
226
-
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
- )
258
-
259
- connection.send_transaction(tx.serialize) if execute
260
- end
261
-
262
- tx
263
- end
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)
56
+ def transfer_checked_composer_class
57
+ Composers::SplTokenProgramTransferCheckedComposer
294
58
  end
295
59
  end
296
60
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Programs
5
+ # Client for interacting with the Token-2022 Program (formerly Token Extensions).
6
+ #
7
+ # Token-2022 is the successor to the legacy SPL Token program
8
+ # (+TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb+). Its base instructions —
9
+ # Transfer, TransferChecked, CloseAccount, MintTo, InitializeMint — are
10
+ # wire-compatible with the legacy program; only the program account they
11
+ # target is different. The shared method shape comes from
12
+ # {TokenProgramInterface}; this class supplies the Token-2022-bound
13
+ # composer classes. Branching between SPL Token and Token-2022 belongs at
14
+ # the call site, where the developer already knows which mint they hold.
15
+ #
16
+ # Important: mints owned by Token-2022 (e.g. PYUSD on Solana) derive
17
+ # their Associated Token Accounts with this program ID in the seed.
18
+ # When working with such a mint, pass
19
+ # +token_program_id: Solace::Constants::TOKEN_2022_PROGRAM_ID+ to
20
+ # {Programs::AssociatedTokenAccount.get_address}. Use
21
+ # {Connection#get_mint_program_id} to discover which token program owns
22
+ # a given mint at runtime.
23
+ #
24
+ # This class does not yet expose the Token-2022 extension instructions
25
+ # (transfer hooks, transfer fees, confidential transfer, etc.); those
26
+ # are out of scope for the base instruction surface.
27
+ #
28
+ # @example Create a Token-2022 mint
29
+ # program = Solace::Programs::Token2022.new(connection: connection)
30
+ #
31
+ # tx = program.create_mint(
32
+ # payer: payer,
33
+ # funder: funder,
34
+ # decimals: 6,
35
+ # mint_account: mint_account,
36
+ # mint_authority: mint_authority
37
+ # )
38
+ #
39
+ # @see Solace::Programs::SplToken
40
+ # @since 0.1.5
41
+ class Token2022 < Base
42
+ include TokenProgramInterface
43
+
44
+ # Initializes a new Token-2022 client.
45
+ #
46
+ # @param connection [Solace::Connection] The connection to the Solana cluster.
47
+ def initialize(connection:)
48
+ super(connection: connection, program_id: Solace::Constants::TOKEN_2022_PROGRAM_ID)
49
+ end
50
+
51
+ private
52
+
53
+ def initialize_mint_composer_class
54
+ Composers::Token2022ProgramInitializeMintComposer
55
+ end
56
+
57
+ def mint_to_composer_class
58
+ Composers::Token2022ProgramMintToComposer
59
+ end
60
+
61
+ def transfer_composer_class
62
+ Composers::Token2022ProgramTransferComposer
63
+ end
64
+
65
+ def transfer_checked_composer_class
66
+ Composers::Token2022ProgramTransferCheckedComposer
67
+ end
68
+ end
69
+ end
70
+ end