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
@@ -33,6 +33,7 @@ module Solace
33
33
  # Solace::Errors::ConfirmationTimeout
34
34
  # ]
35
35
  # @since 0.0.1
36
+ # rubocop:disable Metrics/ClassLength
36
37
  class Connection
37
38
  # @!attribute [r] rpc_url
38
39
  # The URL of the Solana RPC node
@@ -73,7 +74,7 @@ module Solace
73
74
  # Set default options for rpc requests
74
75
  @default_options = {
75
76
  commitment: commitment,
76
- encoding: encoding
77
+ encoding: encoding
77
78
  }
78
79
  end
79
80
 
@@ -196,15 +197,33 @@ module Solace
196
197
  @rpc_client.rpc_request('getTokenAccountBalance', [token_account, default_options]).dig('result', 'value')
197
198
  end
198
199
 
199
- # Gets the token accounts by owner
200
+ # Gets the token accounts by owner.
201
+ #
202
+ # Defaults to the legacy SPL Token program; pass
203
+ # +token_program_id: Solace::Constants::TOKEN_2022_PROGRAM_ID+ for Token-2022.
200
204
  #
201
205
  # @param owner [String] The public key of the owner
202
- # @return [Array<Hash>] The token accounts owned by the owner for the specified mint
203
- def get_token_accounts_by_owner(owner)
204
- params = [owner, { programId: Constants::TOKEN_PROGRAM_ID }, default_options]
206
+ # @param token_program_id [String] The token program to filter by (defaults to legacy SPL Token)
207
+ # @return [Array<Hash>] The token accounts owned by the owner under the given token program
208
+ def get_token_accounts_by_owner(owner, token_program_id: Constants::TOKEN_PROGRAM_ID)
209
+ params = [owner, { programId: token_program_id }, default_options]
205
210
  @rpc_client.rpc_request('getTokenAccountsByOwner', params).dig('result', 'value')
206
211
  end
207
212
 
213
+ # Discovery-only helper: returns the program ID that owns a given mint.
214
+ #
215
+ # Use this to dispatch between {Constants::TOKEN_PROGRAM_ID} and
216
+ # {Constants::TOKEN_2022_PROGRAM_ID} when the mint's program is not known
217
+ # ahead of time. If you also need the rest of the mint's account data
218
+ # (decimals, supply, ...), call {#get_account_info} directly and read
219
+ # +'owner'+ yourself — calling both would cost two RPC roundtrips.
220
+ #
221
+ # @param mint [String] The mint address
222
+ # @return [String, nil] The program ID that owns the mint, or +nil+ if the account does not exist
223
+ def get_mint_program_id(mint)
224
+ get_account_info(mint)&.dig('owner')
225
+ end
226
+
208
227
  # Get the transaction by signature
209
228
  #
210
229
  # @param signature [String] The signature of the transaction
@@ -247,9 +266,9 @@ module Solace
247
266
  # @return [Hash] The options for the send_transaction call
248
267
  def build_send_transaction_options(overrides)
249
268
  {
250
- skipPreflight: false,
251
- encoding: default_options[:encoding],
252
- commitment: default_options[:commitment],
269
+ skipPreflight: false,
270
+ encoding: default_options[:encoding],
271
+ commitment: default_options[:commitment],
253
272
  preflightCommitment: default_options[:commitment]
254
273
  }.merge(overrides)
255
274
  end
@@ -288,7 +307,7 @@ module Solace
288
307
  raise ArgumentError, 'Block required' unless block_given?
289
308
 
290
309
  signature = extract_signature_from(yield)
291
- deadline = monotonic_deadline(timeout)
310
+ deadline = monotonic_deadline(timeout)
292
311
 
293
312
  # Wait for confirmation
294
313
  until deadline_passed?(deadline)
@@ -335,4 +354,5 @@ module Solace
335
354
  Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
336
355
  end
337
356
  end
357
+ # rubocop:enable Metrics/ClassLength
338
358
  end
@@ -23,10 +23,16 @@ module Solace
23
23
  COMPUTE_BUDGET_PROGRAM_ID = 'ComputeBudget111111111111111111111111111111'
24
24
 
25
25
  # @!attribute TOKEN_PROGRAM_ID
26
- # The public key of the SPL Token Program
27
- # This is the same across all Solana clusters
26
+ # The public key of the SPL Token Program (legacy).
27
+ # This is the same across all Solana clusters.
28
28
  TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
29
29
 
30
+ # @!attribute TOKEN_2022_PROGRAM_ID
31
+ # The public key of the Token-2022 Program (formerly Token Extensions).
32
+ # This is the same across all Solana clusters. See {Programs::Token2022}
33
+ # for the full description of how it relates to the legacy program.
34
+ TOKEN_2022_PROGRAM_ID = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
35
+
30
36
  # @!attribute ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
31
37
  # The public key of the Associated Token Account Program
32
38
  # This is the same across all Solana clusters
@@ -26,9 +26,9 @@ module Solace
26
26
  # @param [Integer] timeout The time out reached
27
27
  def initialize(message, signature:, commitment:, timeout:)
28
28
  super(message)
29
- @signature = signature
29
+ @signature = signature
30
30
  @commitment = commitment
31
- @timeout = timeout
31
+ @timeout = timeout
32
32
  end
33
33
 
34
34
  # Formats a confirmation timeout error
@@ -40,9 +40,9 @@ module Solace
40
40
  def self.format(signature, commitment, timeout)
41
41
  new(
42
42
  "Timed out waiting for signature #{signature} at commitment=#{commitment} after #{timeout}s",
43
- signature: signature,
43
+ signature: signature,
44
44
  commitment: commitment,
45
- timeout: timeout
45
+ timeout: timeout
46
46
  )
47
47
  end
48
48
  end
@@ -28,9 +28,9 @@ module Solace
28
28
  # @param [Object] rpc_data The JSON-RPC error data
29
29
  def initialize(message, rpc_code:, rpc_message:, rpc_data: nil)
30
30
  super(message)
31
- @rpc_code = rpc_code
31
+ @rpc_code = rpc_code
32
32
  @rpc_message = rpc_message
33
- @rpc_data = rpc_data
33
+ @rpc_data = rpc_data
34
34
  end
35
35
 
36
36
  # Formats a response to an error
@@ -40,8 +40,8 @@ module Solace
40
40
  def self.format_response(response)
41
41
  new(
42
42
  "RPC error #{response['error']['code']}: #{response['error']['message']}",
43
- rpc_data: response['error']['data'],
44
- rpc_code: response['error']['code'],
43
+ rpc_data: response['error']['data'],
44
+ rpc_code: response['error']['code'],
45
45
  rpc_message: response['error']['message']
46
46
  )
47
47
  end
@@ -63,7 +63,7 @@ module Solace
63
63
  )
64
64
  Solace::Instruction.new.tap do |ix|
65
65
  ix.program_index = program_index
66
- ix.accounts = [
66
+ ix.accounts = [
67
67
  funder_index,
68
68
  associated_token_account_index,
69
69
  owner_index,
@@ -71,6 +71,7 @@ module Solace
71
71
  system_program_index,
72
72
  token_program_index
73
73
  ]
74
+
74
75
  ix.data = data
75
76
  end
76
77
  end
@@ -30,8 +30,8 @@ module Solace
30
30
  def self.build(account_index:, destination_index:, authority_index:, program_index:)
31
31
  Solace::Instruction.new.tap do |ix|
32
32
  ix.program_index = program_index
33
- ix.accounts = [account_index, destination_index, authority_index]
34
- ix.data = data
33
+ ix.accounts = [account_index, destination_index, authority_index]
34
+ ix.data = data
35
35
  end
36
36
  end
37
37
 
@@ -47,8 +47,8 @@ module Solace
47
47
  )
48
48
  Solace::Instruction.new.tap do |ix|
49
49
  ix.program_index = program_index
50
- ix.accounts = [account_index, mint_index, owner_index, rent_sysvar_index]
51
- ix.data = data
50
+ ix.accounts = [account_index, mint_index, owner_index, rent_sysvar_index]
51
+ ix.data = data
52
52
  end
53
53
  end
54
54
 
@@ -49,8 +49,8 @@ module Solace
49
49
  )
50
50
  Solace::Instruction.new.tap do |ix|
51
51
  ix.program_index = program_index
52
- ix.accounts = [mint_account_index, rent_sysvar_index]
53
- ix.data = data(decimals, mint_authority, freeze_authority)
52
+ ix.accounts = [mint_account_index, rent_sysvar_index]
53
+ ix.data = data(decimals, mint_authority, freeze_authority)
54
54
  end
55
55
  end
56
56
 
@@ -39,8 +39,8 @@ module Solace
39
39
  )
40
40
  Solace::Instruction.new.tap do |ix|
41
41
  ix.program_index = program_index
42
- ix.accounts = [mint_index, destination_index, mint_authority_index]
43
- ix.data = data(amount)
42
+ ix.accounts = [mint_index, destination_index, mint_authority_index]
43
+ ix.data = data(amount)
44
44
  end
45
45
  end
46
46
 
@@ -29,7 +29,7 @@ module Solace
29
29
  # SPL Token Program transfer instruction layout:
30
30
  # - 1 byte: instruction index (12 for transfer checked)
31
31
  # - 8 bytes: amount (u64, little-endian)
32
- # - 8 bytes: decimals (u64, little-endian)
32
+ # - 1 byte: decimals (u8)
33
33
  #
34
34
  # @param amount [Integer] Amount to transfer (in tokens, according to mint's decimals)
35
35
  # @param decimals [Integer] Number of decimals for the token
@@ -50,8 +50,8 @@ module Solace
50
50
  )
51
51
  Solace::Instruction.new.tap do |ix|
52
52
  ix.program_index = program_index
53
- ix.accounts = [from_index, mint_index, to_index, authority_index]
54
- ix.data = data(amount, decimals)
53
+ ix.accounts = [from_index, mint_index, to_index, authority_index]
54
+ ix.data = data(amount, decimals)
55
55
  end
56
56
  end
57
57
 
@@ -60,8 +60,8 @@ module Solace
60
60
  )
61
61
  Solace::Instruction.new.tap do |ix|
62
62
  ix.program_index = program_index
63
- ix.accounts = [source_index, destination_index, owner_index]
64
- ix.data = data(amount)
63
+ ix.accounts = [source_index, destination_index, owner_index]
64
+ ix.data = data(amount)
65
65
  end
66
66
  end
67
67
 
@@ -73,8 +73,8 @@ module Solace
73
73
  )
74
74
  Solace::Instruction.new.tap do |ix|
75
75
  ix.program_index = system_program_index
76
- ix.accounts = [from_index, new_account_index]
77
- ix.data = data(lamports, space, owner)
76
+ ix.accounts = [from_index, new_account_index]
77
+ ix.data = data(lamports, space, owner)
78
78
  end
79
79
  end
80
80
  # rubocop:enable Metrics/ParameterLists
@@ -46,8 +46,8 @@ module Solace
46
46
  )
47
47
  Instruction.new.tap do |ix|
48
48
  ix.program_index = program_index
49
- ix.accounts = [from_index, to_index]
50
- ix.data = data(lamports)
49
+ ix.accounts = [from_index, to_index]
50
+ ix.data = data(lamports)
51
51
  end
52
52
  end
53
53
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction builder for Token-2022 CloseAccount.
7
+ #
8
+ # Closes a Token-2022 token account and transfers all remaining lamports
9
+ # to a destination account. The token account must have a balance of zero.
10
+ #
11
+ # Instruction discriminator: 9
12
+ #
13
+ # Accounts:
14
+ # 1. [writable] Token account to close
15
+ # 2. [writable] Destination account to receive lamports
16
+ # 3. [signer] Account authority
17
+ #
18
+ # @since 0.1.5
19
+ class CloseAccountInstruction
20
+ # Instruction discriminator for CloseAccount
21
+ INSTRUCTION_DISCRIMINATOR = [9].freeze
22
+
23
+ # Builds a CloseAccount instruction.
24
+ #
25
+ # @param account_index [Integer] Index of the token account to close
26
+ # @param destination_index [Integer] Index of the destination account
27
+ # @param authority_index [Integer] Index of the account authority
28
+ # @param program_index [Integer] Index of the Token-2022 program
29
+ # @return [Solace::Instruction] The constructed instruction
30
+ def self.build(account_index:, destination_index:, authority_index:, program_index:)
31
+ Solace::Instruction.new.tap do |ix|
32
+ ix.program_index = program_index
33
+ ix.accounts = [account_index, destination_index, authority_index]
34
+ ix.data = data
35
+ end
36
+ end
37
+
38
+ # Builds the data for a CloseAccount instruction.
39
+ #
40
+ # The BufferLayout is:
41
+ # - [Instruction Index (1 byte)]
42
+ #
43
+ # @return [Array] 1-byte instruction index
44
+ def self.data
45
+ INSTRUCTION_DISCRIMINATOR
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for initializing a new Token-2022 token account.
7
+ #
8
+ # Used in conjunction with the SystemProgram::CreateAccount instruction
9
+ # to create and initialize a new token account. For the common case of
10
+ # an associated token account, prefer
11
+ # {AssociatedTokenAccount::CreateAccountInstruction}.
12
+ #
13
+ # @example Build an InitializeAccount instruction
14
+ # instruction = Solace::Instructions::Token2022::InitializeAccountInstruction.build(
15
+ # account_index: 0,
16
+ # mint_index: 1,
17
+ # owner_index: 2,
18
+ # rent_sysvar_index: 3,
19
+ # program_index: 4
20
+ # )
21
+ #
22
+ # @since 0.1.5
23
+ class InitializeAccountInstruction
24
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
25
+ # Instruction index for Token-2022's InitializeAccount instruction.
26
+ INSTRUCTION_INDEX = [1].freeze
27
+
28
+ # Builds a Token2022::InitializeAccount instruction.
29
+ #
30
+ # @param account_index [Integer] Index of the new token account in the transaction's accounts.
31
+ # @param mint_index [Integer] Index of the mint account in the transaction's accounts.
32
+ # @param owner_index [Integer] Index of the owner of the new account in the transaction's accounts.
33
+ # @param rent_sysvar_index [Integer] Index of the Rent Sysvar in the transaction's accounts.
34
+ # @param program_index [Integer] Index of the Token-2022 program in the transaction's accounts.
35
+ # @return [Solace::Instruction]
36
+ def self.build(
37
+ account_index:,
38
+ mint_index:,
39
+ owner_index:,
40
+ rent_sysvar_index:,
41
+ program_index:
42
+ )
43
+ Solace::Instruction.new.tap do |ix|
44
+ ix.program_index = program_index
45
+ ix.accounts = [account_index, mint_index, owner_index, rent_sysvar_index]
46
+ ix.data = data
47
+ end
48
+ end
49
+
50
+ # Builds the data for a Token2022::InitializeAccount instruction.
51
+ #
52
+ # The BufferLayout is:
53
+ # - [Instruction Index (1 byte)]
54
+ #
55
+ # @return [Array] 1-byte instruction index
56
+ def self.data
57
+ INSTRUCTION_INDEX
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for initializing a new Token-2022 mint.
7
+ #
8
+ # @example Build an InitializeMint instruction
9
+ # instruction = Solace::Instructions::Token2022::InitializeMintInstruction.build(
10
+ # decimals: 6,
11
+ # mint_authority: mint_authority.address,
12
+ # freeze_authority: freeze_authority.address,
13
+ # rent_sysvar_index: 2,
14
+ # mint_account_index: 1,
15
+ # program_index: 3
16
+ # )
17
+ #
18
+ # @since 0.1.5
19
+ class InitializeMintInstruction
20
+ # Instruction index for Initialize Mint
21
+ INSTRUCTION_INDEX = [0].freeze
22
+
23
+ # Builds a Solace::Instruction for initializing a Token-2022 mint.
24
+ #
25
+ # The BufferLayout is:
26
+ # - [Instruction Index (1 byte)]
27
+ # - [Decimals (1 byte)]
28
+ # - [Mint authority (32 bytes)]
29
+ # - [Freeze authority option (1 byte)]
30
+ # - [Freeze authority (32 bytes)]
31
+ #
32
+ # @param decimals [Integer] Number of decimals for the token
33
+ # @param mint_authority [String] Public key of the mint authority
34
+ # @param freeze_authority [String, nil] Public key of the freeze authority
35
+ # @param rent_sysvar_index [Integer] Index of the rent sysvar in the transaction's accounts
36
+ # @param mint_account_index [Integer] Index of the mint account in the transaction's accounts
37
+ # @param program_index [Integer] Index of the Token-2022 Program in the transaction's accounts
38
+ # @return [Solace::Instruction]
39
+ def self.build(
40
+ decimals:,
41
+ mint_authority:,
42
+ rent_sysvar_index:,
43
+ mint_account_index:,
44
+ freeze_authority: nil,
45
+ program_index: 2
46
+ )
47
+ Solace::Instruction.new.tap do |ix|
48
+ ix.program_index = program_index
49
+ ix.accounts = [mint_account_index, rent_sysvar_index]
50
+ ix.data = data(decimals, mint_authority, freeze_authority)
51
+ end
52
+ end
53
+
54
+ # Instruction data for an initialize mint instruction.
55
+ #
56
+ # @param decimals [Integer] Number of decimals for the token
57
+ # @param mint_authority [String] Public key of the mint authority
58
+ # @param freeze_authority [String, nil] Public key of the freeze authority
59
+ # @return [Array<u8>] The instruction data
60
+ def self.data(decimals, mint_authority, freeze_authority)
61
+ INSTRUCTION_INDEX +
62
+ [decimals] +
63
+ Solace::Utils::Codecs.base58_to_bytes(mint_authority) +
64
+ (
65
+ if freeze_authority
66
+ [1] + Solace::Utils::Codecs.base58_to_bytes(freeze_authority)
67
+ else
68
+ [0]
69
+ end
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for minting Token-2022 tokens to a token account.
7
+ #
8
+ # @example Build a MintTo instruction
9
+ # instruction = Solace::Instructions::Token2022::MintToInstruction.build(
10
+ # amount: 100,
11
+ # mint_index: 1,
12
+ # mint_authority_index: 2,
13
+ # destination_index: 3,
14
+ # program_index: 4
15
+ # )
16
+ #
17
+ # @since 0.1.5
18
+ class MintToInstruction
19
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
20
+ # Instruction index for Token-2022's MintTo instruction.
21
+ INSTRUCTION_INDEX = [7].freeze
22
+
23
+ # Builds a MintTo instruction.
24
+ #
25
+ # @param amount [Integer] The amount of tokens to mint.
26
+ # @param mint_index [Integer] The index of the mint account.
27
+ # @param destination_index [Integer] The index of the token account to mint to.
28
+ # @param mint_authority_index [Integer] The index of the mint authority account.
29
+ # @param program_index [Integer] The index of the Token-2022 Program.
30
+ # @return [Solace::Instruction]
31
+ def self.build(
32
+ amount:,
33
+ mint_index:,
34
+ mint_authority_index:,
35
+ destination_index:,
36
+ program_index:
37
+ )
38
+ Solace::Instruction.new.tap do |ix|
39
+ ix.program_index = program_index
40
+ ix.accounts = [mint_index, destination_index, mint_authority_index]
41
+ ix.data = data(amount)
42
+ end
43
+ end
44
+
45
+ # Builds the data for a MintTo instruction.
46
+ #
47
+ # The BufferLayout is:
48
+ # - [Instruction Index (1 byte)]
49
+ # - [Amount (8 bytes)]
50
+ #
51
+ # @param amount [Integer] The amount of tokens to mint.
52
+ # @return [Array] 1-byte instruction index + 8-byte amount
53
+ def self.data(amount)
54
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for transferring Token-2022 tokens with decimal validation.
7
+ #
8
+ # @example Build a TransferChecked instruction
9
+ # instruction = Solace::Instructions::Token2022::TransferCheckedInstruction.build(
10
+ # amount: 100,
11
+ # decimals: 6,
12
+ # to_index: 1,
13
+ # from_index: 2,
14
+ # mint_index: 3,
15
+ # authority_index: 4,
16
+ # program_index: 5
17
+ # )
18
+ #
19
+ # @since 0.1.5
20
+ class TransferCheckedInstruction
21
+ # Token-2022 instruction index for Transfer Checked
22
+ INSTRUCTION_INDEX = [12].freeze
23
+
24
+ # Builds a Solace::Instruction for transferring Token-2022 tokens.
25
+ #
26
+ # The BufferLayout is:
27
+ # - [Instruction Index (1 byte)]
28
+ # - [Amount (8 bytes little-endian u64)]
29
+ # - [Decimals (1 byte)]
30
+ #
31
+ # @param amount [Integer] Amount to transfer (in tokens, according to mint's decimals)
32
+ # @param decimals [Integer] Number of decimals for the token
33
+ # @param to_index [Integer] Index of the destination token account in the transaction's accounts
34
+ # @param from_index [Integer] Index of the source token account in the transaction's accounts
35
+ # @param mint_index [Integer] Index of the mint in the transaction's accounts
36
+ # @param authority_index [Integer] Index of the authority (owner) in the transaction's accounts
37
+ # @param program_index [Integer] Index of the Token-2022 Program in the transaction's accounts
38
+ # @return [Solace::Instruction]
39
+ def self.build(
40
+ amount:,
41
+ decimals:,
42
+ to_index:,
43
+ from_index:,
44
+ mint_index:,
45
+ authority_index:,
46
+ program_index: 3
47
+ )
48
+ Solace::Instruction.new.tap do |ix|
49
+ ix.program_index = program_index
50
+ ix.accounts = [from_index, mint_index, to_index, authority_index]
51
+ ix.data = data(amount, decimals)
52
+ end
53
+ end
54
+
55
+ # Instruction data for a TransferChecked instruction.
56
+ #
57
+ # @param amount [Integer] Amount to transfer
58
+ # @param decimals [Integer] Number of decimals for the token
59
+ # @return [Array] 1-byte instruction index + 8-byte amount + 1-byte decimals
60
+ def self.data(amount, decimals)
61
+ INSTRUCTION_INDEX +
62
+ Solace::Utils::Codecs.encode_le_u64(amount).bytes +
63
+ [decimals]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ # The Token2022 module contains instruction builders for the Token-2022 Program
6
+ # (formerly Token Extensions, +TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb+).
7
+ #
8
+ # Token-2022 is wire-compatible with the legacy SPL Token program for its
9
+ # base instructions (Transfer, TransferChecked, CloseAccount, MintTo,
10
+ # InitializeMint, InitializeAccount). The instruction discriminators and
11
+ # data layouts are identical; only the program the instruction targets
12
+ # differs. These builders are duplicated from the {SplToken} namespace so
13
+ # that each instruction is unambiguously bound to a single on-chain program.
14
+ #
15
+ # @see Solace::Instructions::SplToken
16
+ # @since 0.1.5
17
+ module Token2022
18
+ # Instruction for transferring tokens via the Token-2022 program.
19
+ #
20
+ # @example Build a Transfer instruction
21
+ # instruction = Solace::Instructions::Token2022::TransferInstruction.build(
22
+ # amount: 100,
23
+ # owner_index: 1,
24
+ # source_index: 2,
25
+ # destination_index: 3,
26
+ # program_index: 4
27
+ # )
28
+ #
29
+ # @since 0.1.5
30
+ class TransferInstruction
31
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
32
+ # Instruction index for Token-2022's Transfer instruction.
33
+ INSTRUCTION_INDEX = [3].freeze
34
+
35
+ # Builds a Transfer instruction.
36
+ #
37
+ # @param amount [Integer] The amount of tokens to transfer.
38
+ # @param source_index [Integer] The index of the source token account.
39
+ # @param destination_index [Integer] The index of the destination token account.
40
+ # @param owner_index [Integer] The index of the source account's owner.
41
+ # @param program_index [Integer] The index of the Token-2022 Program.
42
+ # @return [Solace::Instruction]
43
+ def self.build(
44
+ amount:,
45
+ owner_index:,
46
+ source_index:,
47
+ destination_index:,
48
+ program_index:
49
+ )
50
+ Solace::Instruction.new.tap do |ix|
51
+ ix.program_index = program_index
52
+ ix.accounts = [source_index, destination_index, owner_index]
53
+ ix.data = data(amount)
54
+ end
55
+ end
56
+
57
+ # Builds the data for a Transfer instruction.
58
+ #
59
+ # The BufferLayout is:
60
+ # - [Instruction Index (1 byte)]
61
+ # - [Amount (8 bytes)]
62
+ #
63
+ # @param amount [Integer] The amount of tokens to transfer.
64
+ # @return [Array<Integer>] 1-byte instruction index + 8-byte amount
65
+ def self.data(amount)
66
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end