solace 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +0 -0
  3. data/LICENSE +0 -0
  4. data/README.md +661 -0
  5. data/lib/solace/address_lookup_table.rb +50 -0
  6. data/lib/solace/concerns/binary_serializable.rb +30 -0
  7. data/lib/solace/connection.rb +187 -0
  8. data/lib/solace/constants.rb +52 -0
  9. data/lib/solace/instruction.rb +38 -0
  10. data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +68 -0
  11. data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +46 -0
  12. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +68 -0
  13. data/lib/solace/instructions/spl_token/mint_to_instruction.rb +48 -0
  14. data/lib/solace/instructions/spl_token/transfer_instruction.rb +48 -0
  15. data/lib/solace/instructions/system_program/create_account_instruction.rb +58 -0
  16. data/lib/solace/instructions/transfer_checked_instruction.rb +58 -0
  17. data/lib/solace/instructions/transfer_instruction.rb +48 -0
  18. data/lib/solace/keypair.rb +121 -0
  19. data/lib/solace/message.rb +95 -0
  20. data/lib/solace/programs/associated_token_account.rb +96 -0
  21. data/lib/solace/programs/base.rb +22 -0
  22. data/lib/solace/programs/spl_token.rb +187 -0
  23. data/lib/solace/public_key.rb +74 -0
  24. data/lib/solace/serializable_record.rb +26 -0
  25. data/lib/solace/serializers/address_lookup_table_deserializer.rb +62 -0
  26. data/lib/solace/serializers/address_lookup_table_serializer.rb +54 -0
  27. data/lib/solace/serializers/base.rb +31 -0
  28. data/lib/solace/serializers/base_deserializer.rb +56 -0
  29. data/lib/solace/serializers/base_serializer.rb +52 -0
  30. data/lib/solace/serializers/instruction_deserializer.rb +62 -0
  31. data/lib/solace/serializers/instruction_serializer.rb +54 -0
  32. data/lib/solace/serializers/message_deserializer.rb +116 -0
  33. data/lib/solace/serializers/message_serializer.rb +95 -0
  34. data/lib/solace/serializers/transaction_deserializer.rb +49 -0
  35. data/lib/solace/serializers/transaction_serializer.rb +60 -0
  36. data/lib/solace/transaction.rb +98 -0
  37. data/lib/solace/utils/codecs.rb +220 -0
  38. data/lib/solace/utils/curve25519_dalek.rb +59 -0
  39. data/lib/solace/utils/libcurve25519_dalek-linux/libcurve25519_dalek.so +0 -0
  40. data/lib/solace/utils/libcurve25519_dalek-macos/libcurve25519_dalek.dylib +0 -0
  41. data/lib/solace/utils/libcurve25519_dalek-windows/curve25519_dalek.dll +0 -0
  42. data/lib/solace/utils/pda.rb +100 -0
  43. data/lib/solace/version.rb +5 -0
  44. data/lib/solace.rb +39 -0
  45. metadata +165 -0
@@ -0,0 +1,50 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ # =============================
5
+ # Address Lookup Table
6
+ # =============================
7
+ #
8
+ # Class representing an address lookup table.
9
+ #
10
+ # The BufferLayout is:
11
+ # - [Account key (32 bytes)]
12
+ # - [Number of writable indexes (compact u16)]
13
+ # - [Writable indexes (variable length)]
14
+ # - [Number of readonly indexes (compact u16)]
15
+ # - [Readonly indexes (variable length)]
16
+ #
17
+ module Solace
18
+ class AddressLookupTable
19
+ include Solace::Concerns::BinarySerializable
20
+
21
+ # @!attribute [rw] account
22
+ # @return [String] The account key of the address lookup table
23
+ attr_accessor :account
24
+
25
+ # @!attribute [rw] writable_indexes
26
+ # @return [Array<Integer>] The writable indexes in the address lookup table
27
+ attr_accessor :writable_indexes
28
+
29
+ # @!attribute [rw] readonly_indexes
30
+ # @return [Array<Integer>] The readonly indexes in the address lookup table
31
+ attr_accessor :readonly_indexes
32
+
33
+ class << self
34
+ # Parse address lookup table from io stream
35
+ #
36
+ # @param io [IO or StringIO] The input to read bytes from.
37
+ # @return [Solace::AddressLookupTable] Parsed address lookup table object
38
+ def deserialize(io)
39
+ Solace::Serializers::AddressLookupTableDeserializer.call(io)
40
+ end
41
+ end
42
+
43
+ # Serialize the address lookup table
44
+ #
45
+ # @return [String] The serialized address lookup table (base64)
46
+ def serialize
47
+ Solace::Serializers::AddressLookupTableSerializer.call(self)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Concerns
5
+ module BinarySerializable
6
+ # Returns the binary decoded from the serialized string
7
+ #
8
+ # Expects the class to have a `serialize` method that returns a base64 string.
9
+ #
10
+ # @return [String] The binary decoded from the serialized string
11
+ def to_binary
12
+ Base64.decode64(serialize)
13
+ end
14
+
15
+ # Returns a StringIO stream of the binary data
16
+ #
17
+ # @return [IO] The StringIO stream of the binary data
18
+ def to_io
19
+ StringIO.new(to_binary)
20
+ end
21
+
22
+ # Returns the bytes of the binary data as an array of integers
23
+ #
24
+ # @return [Array] The bytes of the binary data as an array of integers
25
+ def to_bytes
26
+ to_binary.bytes
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ module Solace
8
+ class Connection
9
+ attr_reader :rpc_url
10
+
11
+ # !const default options
12
+ DEFAULT_OPTIONS = {
13
+ encoding: 'base64',
14
+ commitment: 'confirmed'
15
+ }.freeze
16
+
17
+ # Initialize the connection with a default or custom RPC URL
18
+ #
19
+ # @param rpc_url [String] The URL of the Solana RPC node
20
+ # @return [Solace::Connection] The connection object
21
+ def initialize(rpc_url = 'http://localhost:8899')
22
+ @rpc_url = rpc_url
23
+ @request_id = nil
24
+ end
25
+
26
+ # Make an RPC request to the Solana node
27
+ #
28
+ # @param method [String] The RPC method to call
29
+ # @param params [Array] Parameters for the RPC method
30
+ # @return [Object] Result of the RPC call
31
+ def rpc_request(method, params = [])
32
+ uri = URI(rpc_url)
33
+
34
+ req = Net::HTTP::Post.new(uri)
35
+ req['Accept'] = 'application/json'
36
+ req['Content-Type'] = 'application/json'
37
+
38
+ @request_id = SecureRandom.uuid
39
+
40
+ req.body = {
41
+ jsonrpc: '2.0',
42
+ id: @request_id,
43
+ method: method,
44
+ params: params
45
+ }.to_json
46
+
47
+ res = Net::HTTP.start(
48
+ uri.hostname,
49
+ uri.port,
50
+ use_ssl: uri.scheme == 'https'
51
+ ) do |http|
52
+ http.request(req)
53
+ end
54
+
55
+ raise "RPC error: #{res.body}" unless res.is_a?(Net::HTTPSuccess)
56
+
57
+ JSON.parse(res.body)
58
+ end
59
+
60
+ # Request an airdrop of lamports to a given address
61
+ #
62
+ # @param pubkey [String] The public key of the account to receive the airdrop
63
+ # @param lamports [Integer] Amount of lamports to airdrop
64
+ # @return [String] The transaction signature of the airdrop
65
+ def request_airdrop(pubkey, lamports, options = {})
66
+ rpc_request(
67
+ 'requestAirdrop',
68
+ [
69
+ pubkey,
70
+ lamports,
71
+ DEFAULT_OPTIONS.merge(options)
72
+ ]
73
+ )
74
+ end
75
+
76
+ # Get the latest blockhash from the Solana node
77
+ #
78
+ # @return [String] The latest blockhash
79
+ def get_latest_blockhash
80
+ rpc_request('getLatestBlockhash')['result']['value']['blockhash']
81
+ end
82
+
83
+ # Get the minimum required lamports for rent exemption
84
+ #
85
+ # @param space [Integer] Number of bytes to allocate for the account
86
+ # @return [Integer] The minimum required lamports
87
+ def get_minimum_lamports_for_rent_exemption(space)
88
+ rpc_request('getMinimumBalanceForRentExemption', [space])['result']
89
+ end
90
+
91
+ # Get the account information from the Solana node
92
+ #
93
+ # @param pubkey [String] The public key of the account
94
+ # @return [Object] The account information
95
+ def get_account_info(pubkey)
96
+ response = rpc_request(
97
+ 'getAccountInfo',
98
+ [
99
+ pubkey,
100
+ DEFAULT_OPTIONS
101
+ ]
102
+ )['result']
103
+
104
+ return if response.nil?
105
+
106
+ response['value']
107
+ end
108
+
109
+ # Get the balance of a specific account
110
+ #
111
+ # @param pubkey [String] The public key of the account
112
+ # @return [Integer] The balance of the account
113
+ def get_balance(pubkey)
114
+ rpc_request(
115
+ 'getBalance',
116
+ [
117
+ pubkey,
118
+ DEFAULT_OPTIONS
119
+ ]
120
+ )['result']['value']
121
+ end
122
+
123
+ # Get the transaction by signature
124
+ #
125
+ # @param signature [String] The signature of the transaction
126
+ # @return [Solace::Transaction] The transaction object
127
+ def get_transaction(signature, options = { maxSupportedTransactionVersion: 0 })
128
+ rpc_request(
129
+ 'getTransaction',
130
+ [
131
+ signature,
132
+ DEFAULT_OPTIONS.merge(options)
133
+ ]
134
+ )['result']
135
+ end
136
+
137
+ # Get the signature status
138
+ #
139
+ # @param signatures [Array] The signatures of the transactions
140
+ # @return [Object] The signature status
141
+ def get_signature_status(signatures)
142
+ rpc_request(
143
+ 'getSignatureStatuses',
144
+ [
145
+ signatures,
146
+ DEFAULT_OPTIONS.merge({ 'searchTransactionHistory' => true })
147
+ ]
148
+ )['result']
149
+ end
150
+
151
+ # Send a transaction to the Solana node
152
+ #
153
+ # @param transaction [Solace::Transaction] The transaction to send
154
+ # @return [String] The signature of the transaction
155
+ def send_transaction(transaction, options = {})
156
+ rpc_request(
157
+ 'sendTransaction',
158
+ [
159
+ transaction,
160
+ DEFAULT_OPTIONS.merge(options)
161
+ ]
162
+ )
163
+ end
164
+
165
+ # Wait for a confirmed signature from the transaction
166
+ #
167
+ # @param commitment [String] The commitment level to wait for
168
+ # @return [Boolean] True if the transaction was confirmed, false otherwise
169
+ def wait_for_confirmed_signature(commitment = 'confirmed')
170
+ raise ArgumentError, 'Block required' unless block_given?
171
+
172
+ # Get the signature from the block
173
+ signature = yield
174
+
175
+ # Wait for confirmation
176
+ loop do
177
+ status = get_signature_status([signature]).dig('value', 0)
178
+
179
+ break if status && status['confirmationStatus'] == commitment
180
+
181
+ sleep 0.5
182
+ end
183
+
184
+ signature
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Constants module
4
+ #
5
+ # Contains constants used across the library.
6
+ #
7
+ # @return [Module] Constants module
8
+ module Solace
9
+ module Constants
10
+ # @!const SYSTEM_PROGRAM_ID
11
+ # The public key of the System Program (native SOL transfers, account creation, etc)
12
+ # This is the same across all Solana clusters
13
+ # @return [String]
14
+ SYSTEM_PROGRAM_ID = '11111111111111111111111111111111'
15
+
16
+ # @!const SYSVAR_RENT_PROGRAM_ID
17
+ # The public key of the Rent Program
18
+ # This is the same across all Solana clusters
19
+ # @return [String]
20
+ SYSVAR_RENT_PROGRAM_ID = 'SysvarRent111111111111111111111111111111111'
21
+
22
+ # @!const COMPUTE_BUDGET_PROGRAM_ID
23
+ # The public key of the Compute Budget Program
24
+ # This is the same across all Solana clusters
25
+ # @return [String]
26
+ COMPUTE_BUDGET_PROGRAM_ID = 'ComputeBudget111111111111111111111111111111'
27
+
28
+ # @!const TOKEN_PROGRAM_ID
29
+ # The public key of the SPL Token Program
30
+ # This is the same across all Solana clusters
31
+ # @return [String]
32
+ TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
33
+
34
+ # @!const ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
35
+ # The public key of the Associated Token Account Program
36
+ # This is the same across all Solana clusters
37
+ # @return [String]
38
+ ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'
39
+
40
+ # @!const MEMO_PROGRAM_ID
41
+ # The public key of the Memo Program
42
+ # This is the same across all Solana clusters
43
+ # @return [String]
44
+ MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'
45
+
46
+ # @!const ADDRESS_LOOKUP_TABLE_PROGRAM_ID
47
+ # The public key of the Address Lookup Table Program
48
+ # This is the same across all Solana clusters
49
+ # @return [String]
50
+ ADDRESS_LOOKUP_TABLE_PROGRAM_ID = 'AddressLookupTab1e1111111111111111111111111'
51
+ end
52
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================
4
+ # Instruction
5
+ # =============================
6
+ #
7
+ # Class representing a Solana instruction.
8
+ #
9
+ # The BufferLayout is:
10
+ # - [Program index (1 byte)]
11
+ # - [Number of accounts (compact u16)]
12
+ # - [Accounts (variable length)]
13
+ # - [Data length (compact u16)]
14
+ # - [Data (variable length)]
15
+ #
16
+ module Solace
17
+ class Instruction < Solace::SerializableRecord
18
+ # @!const SERIALIZER
19
+ # @return [Solace::Serializers::InstructionSerializer] The serializer for the instruction
20
+ SERIALIZER = Solace::Serializers::InstructionSerializer
21
+
22
+ # @!const DESERIALIZER
23
+ # @return [Solace::Serializers::InstructionDeserializer] The deserializer for the instruction
24
+ DESERIALIZER = Solace::Serializers::InstructionDeserializer
25
+
26
+ # @!attribute [rw] program_index
27
+ # @return [Integer] The program index of the instruction
28
+ attr_accessor :program_index
29
+
30
+ # @!attribute [rw] accounts
31
+ # @return [Array<Integer>] The accounts of the instruction
32
+ attr_accessor :accounts
33
+
34
+ # @!attribute [rw] data
35
+ # @return [Array<Integer>] The instruction data
36
+ attr_accessor :data
37
+ end
38
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module AssociatedTokenAccount
6
+ # A class to build the CreateAssociatedTokenAccount instruction for the Associated Token Account Program.
7
+ # This is a special "all-in-one" instruction that creates and initializes the account.
8
+ class CreateAssociatedTokenAccountInstruction
9
+ # !@const INSTRUCTION_INDEX
10
+ # Instruction index for CreateAssociatedTokenAccount
11
+ #
12
+ # @return [Array<Integer>]
13
+ INSTRUCTION_INDEX = [0].freeze
14
+
15
+ # Builds a CreateAssociatedTokenAccount instruction.
16
+ #
17
+ # The on-chain program requires accounts in a specific order:
18
+ # 1. [writable, signer] Funder: The account paying for the rent.
19
+ # 2. [writable] ATA: The new Associated Token Account to be created.
20
+ # 3. [readonly] Owner: The wallet that will own the new ATA.
21
+ # 4. [readonly] Mint: The token mint for the new ATA.
22
+ # 5. [readonly] System Program: Required to create the account.
23
+ # 6. [readonly] SPL Token Program: Required to initialize the account.
24
+ #
25
+ # @param funder_index [Integer] Index of the funding account (payer).
26
+ # @param associated_token_account_index [Integer] Index of the Associated Token Account to be created.
27
+ # @param owner_index [Integer] Index of the wallet that will own the new ATA.
28
+ # @param mint_index [Integer] Index of the token mint.
29
+ # @param system_program_index [Integer] Index of the System Program.
30
+ # @param token_program_index [Integer] Index of the SPL Token Program.
31
+ # @param program_index [Integer] Index of the Associated Token Program itself.
32
+ # @return [Solace::Instruction]
33
+ def self.build(
34
+ funder_index:,
35
+ associated_token_account_index:,
36
+ owner_index:,
37
+ mint_index:,
38
+ system_program_index:,
39
+ token_program_index:,
40
+ program_index:
41
+ )
42
+ Solace::Instruction.new.tap do |ix|
43
+ ix.program_index = program_index
44
+ ix.accounts = [
45
+ funder_index,
46
+ associated_token_account_index,
47
+ owner_index,
48
+ mint_index,
49
+ system_program_index,
50
+ token_program_index
51
+ ]
52
+ ix.data = data
53
+ end
54
+ end
55
+
56
+ # Data for a CreateAssociatedTokenAccount instruction
57
+ #
58
+ # The BufferLayout is:
59
+ # - [Instruction Index (1 byte)]
60
+ #
61
+ # @return [Array] 1-byte instruction index
62
+ def self.data
63
+ INSTRUCTION_INDEX
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,46 @@
1
+ # lib/solace/instructions/spl_token/initialize_account_instruction.rb
2
+
3
+ module Solace
4
+ module Instructions
5
+ module SplToken
6
+ # A class to build the InitializeAccount instruction for the SPL Token Program.
7
+ class InitializeAccountInstruction
8
+ # @!const [Array<Integer>] INSTRUCTION_INDEX
9
+ # Instruction index for SPL Token Program's InitializeAccount instruction.
10
+ INSTRUCTION_INDEX = [1].freeze
11
+
12
+ # Builds a SPLToken::InitializeAccount instruction.
13
+ #
14
+ # @param account_index [Integer] Index of the new token account in the transaction's accounts.
15
+ # @param mint_index [Integer] Index of the mint account in the transaction's accounts.
16
+ # @param owner_index [Integer] Index of the owner of the new account in the transaction's accounts.
17
+ # @param rent_sysvar_index [Integer] Index of the Rent Sysvar in the transaction's accounts.
18
+ # @param program_index [Integer] Index of the SPL Token program in the transaction's accounts.
19
+ # @return [Solace::Instruction]
20
+ def self.build(
21
+ account_index:,
22
+ mint_index:,
23
+ owner_index:,
24
+ rent_sysvar_index:,
25
+ program_index:
26
+ )
27
+ Solace::Instruction.new.tap do |ix|
28
+ ix.program_index = program_index
29
+ ix.accounts = [account_index, mint_index, owner_index, rent_sysvar_index]
30
+ ix.data = data
31
+ end
32
+ end
33
+
34
+ # Builds the data for a SPLToken::InitializeAccount instruction.
35
+ #
36
+ # The BufferLayout is:
37
+ # - [Instruction Index (1 byte)]
38
+ #
39
+ # @return [Array] 1-byte instruction index
40
+ def self.data
41
+ INSTRUCTION_INDEX
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module SplToken
6
+ class InitializeMintInstruction
7
+ # Instruction index for Initialize Mint
8
+ INSTRUCTION_INDEX = [0].freeze
9
+
10
+ # Builds a Solace::Instruction for initializing an SPL Token Program mint
11
+ #
12
+ # The BufferLayout is:
13
+ # - [Instruction Index (1 byte)]
14
+ # - [Decimals (1 byte)]
15
+ # - [Mint authority (32 bytes)]
16
+ # - [Freeze authority option (1 byte)]
17
+ # - [Freeze authority (32 bytes)]
18
+ #
19
+ # @param decimals [Integer] Number of decimals for the token
20
+ # @param mint_authority [String] Public key of the mint authority
21
+ # @param freeze_authority [String, nil] Public key of the freeze authority
22
+ # @param rent_sysvar_index [Integer] Index of the rent sysvar in the transaction's accounts
23
+ # @param mint_account_index [Integer] Index of the mint account in the transaction's accounts
24
+ # @param program_index [Integer] Index of the SPL Token Program in the transaction's accounts (default: 3)
25
+ # @return [Solace::Instruction]
26
+ def self.build(
27
+ decimals:,
28
+ mint_authority:,
29
+ rent_sysvar_index:,
30
+ mint_account_index:,
31
+ freeze_authority: nil,
32
+ program_index: 2
33
+ )
34
+ Solace::Instruction.new.tap do |ix|
35
+ ix.program_index = program_index
36
+ ix.accounts = [mint_account_index, rent_sysvar_index]
37
+ ix.data = data(decimals, mint_authority, freeze_authority)
38
+ end
39
+ end
40
+
41
+ # Instruction data for an initialize mint instruction
42
+ #
43
+ # The BufferLayout is:
44
+ # - [Instruction Index (1 byte)]
45
+ # - [Decimals (1 byte)]
46
+ # - [Mint authority (32 bytes)]
47
+ # - [Freeze authority option (33 byte)]
48
+ #
49
+ # @param decimals [Integer] Number of decimals for the token
50
+ # @param mint_authority [String] Public key of the mint authority
51
+ # @param freeze_authority [String, nil] Public key of the freeze authority
52
+ # @return [Array] 1-byte instruction index + 1-byte decimals + 32-byte mint authority + 1-byte freeze authority option + 32-byte freeze authority
53
+ def self.data(decimals, mint_authority, freeze_authority)
54
+ INSTRUCTION_INDEX +
55
+ [decimals] +
56
+ Solace::Utils::Codecs.base58_to_bytes(mint_authority) +
57
+ (
58
+ if freeze_authority
59
+ [1] + Solace::Utils::Codecs.base58_to_bytes(freeze_authority)
60
+ else
61
+ [0]
62
+ end
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module SplToken
6
+ # A class to build the MintTo instruction for the SPL Token Program.
7
+ class MintToInstruction
8
+ # @!const [Array<Integer>] INSTRUCTION_INDEX
9
+ # Instruction index for SPL Token Program's MintTo instruction.
10
+ INSTRUCTION_INDEX = [7].freeze
11
+
12
+ # Builds a MintTo instruction.
13
+ #
14
+ # @param amount [Integer] The amount of tokens to mint.
15
+ # @param mint_index [Integer] The index of the mint account.
16
+ # @param destination_index [Integer] The index of the token account to mint to.
17
+ # @param mint_authority_index [Integer] The index of the mint authority account.
18
+ # @param program_index [Integer] The index of the SPL Token Program.
19
+ # @return [Solace::Instruction]
20
+ def self.build(
21
+ amount:,
22
+ mint_index:,
23
+ mint_authority_index:,
24
+ destination_index:,
25
+ program_index:
26
+ )
27
+ Solace::Instruction.new.tap do |ix|
28
+ ix.program_index = program_index
29
+ ix.accounts = [mint_index, destination_index, mint_authority_index]
30
+ ix.data = data(amount)
31
+ end
32
+ end
33
+
34
+ # Builds the data for a MintTo instruction.
35
+ #
36
+ # The BufferLayout is:
37
+ # - [Instruction Index (1 byte)]
38
+ # - [Amount (8 bytes)]
39
+ #
40
+ # @param amount [Integer] The amount of tokens to mint.
41
+ # @return [Array] 1-byte instruction index + 8-byte amount
42
+ def self.data(amount)
43
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module SplToken
6
+ # A class to build the Transfer instruction for the SPL Token Program.
7
+ class TransferInstruction
8
+ # @!const [Array<Integer>] INSTRUCTION_INDEX
9
+ # Instruction index for SPL Token Program's Transfer instruction.
10
+ INSTRUCTION_INDEX = [3].freeze
11
+
12
+ # Builds a Transfer instruction.
13
+ #
14
+ # @param amount [Integer] The amount of tokens to transfer.
15
+ # @param source_index [Integer] The index of the source token account.
16
+ # @param destination_index [Integer] The index of the destination token account.
17
+ # @param owner_index [Integer] The index of the source account's owner.
18
+ # @param program_index [Integer] The index of the SPL Token Program.
19
+ # @return [Solace::Instruction]
20
+ def self.build(
21
+ amount:,
22
+ owner_index:,
23
+ source_index:,
24
+ destination_index:,
25
+ program_index:
26
+ )
27
+ Solace::Instruction.new.tap do |ix|
28
+ ix.program_index = program_index
29
+ ix.accounts = [source_index, destination_index, owner_index]
30
+ ix.data = data(amount)
31
+ end
32
+ end
33
+
34
+ # Builds the data for a Transfer instruction.
35
+ #
36
+ # The BufferLayout is:
37
+ # - [Instruction Index (1 byte)]
38
+ # - [Amount (8 bytes)]
39
+ #
40
+ # @param amount [Integer] The amount of tokens to transfer.
41
+ # @return [Array<Integer>] 1-byte instruction index + 8-byte amount
42
+ def self.data(amount)
43
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end