solace 0.0.10 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +104 -23
- data/README.md +10 -8
- data/lib/solace/composers/base.rb +35 -0
- data/lib/solace/composers/spl_token_program_initialize_mint_composer.rb +95 -0
- data/lib/solace/composers/spl_token_program_mint_to_composer.rb +86 -0
- data/lib/solace/composers/spl_token_program_transfer_composer.rb +90 -0
- data/lib/solace/composers/system_program_create_account_composer.rb +98 -0
- data/lib/solace/connection.rb +88 -13
- data/lib/solace/errors/confirmation_timeout.rb +18 -4
- data/lib/solace/errors/http_error.rb +16 -1
- data/lib/solace/errors/parse_error.rb +15 -1
- data/lib/solace/errors/rpc_error.rb +17 -1
- data/lib/solace/errors.rb +8 -3
- data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +9 -2
- data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +0 -1
- data/lib/solace/instructions/spl_token/transfer_instruction.rb +21 -0
- data/lib/solace/instructions/system_program/create_account_instruction.rb +30 -0
- data/lib/solace/instructions/system_program/transfer_instruction.rb +11 -0
- data/lib/solace/programs/associated_token_account.rb +57 -30
- data/lib/solace/programs/base.rb +23 -0
- data/lib/solace/programs/spl_token.rb +197 -125
- data/lib/solace/serializers/base_serializer.rb +29 -1
- data/lib/solace/tokens/token.rb +53 -0
- data/lib/solace/tokens.rb +86 -0
- data/lib/solace/transaction.rb +24 -21
- data/lib/solace/transaction_composer.rb +77 -3
- data/lib/solace/utils/account_context.rb +1 -1
- data/lib/solace/utils/codecs.rb +17 -0
- data/lib/solace/utils/pda.rb +13 -5
- data/lib/solace/version.rb +3 -2
- data/lib/solace.rb +38 -11
- metadata +21 -1
@@ -15,6 +15,7 @@ module Solace
|
|
15
15
|
# # Create an SPL Token mint
|
16
16
|
# result = program.create_mint(
|
17
17
|
# payer: payer,
|
18
|
+
# funder: funder,
|
18
19
|
# decimals: 6,
|
19
20
|
# mint_keypair: mint_keypair,
|
20
21
|
# mint_authority: mint_authority,
|
@@ -22,11 +23,9 @@ module Solace
|
|
22
23
|
# )
|
23
24
|
#
|
24
25
|
# # Wait for the transaction to be finalized
|
25
|
-
#
|
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
|
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(
|
43
|
-
|
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
|
-
|
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
|
71
|
+
# Prepares a new SPL Token mint transaction.
|
49
72
|
#
|
50
|
-
# @param
|
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 [
|
53
|
-
# @param freeze_authority [
|
54
|
-
# @param
|
55
|
-
# @return [
|
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
|
59
|
-
|
80
|
+
def compose_create_mint(
|
81
|
+
funder:,
|
60
82
|
decimals:,
|
61
83
|
mint_authority:,
|
62
|
-
freeze_authority
|
63
|
-
|
84
|
+
freeze_authority: nil,
|
85
|
+
mint_account: Solace::Keypair.generate
|
64
86
|
)
|
65
|
-
accounts
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
102
|
+
mint_account: mint_account,
|
103
|
+
mint_authority: mint_authority,
|
92
104
|
freeze_authority: freeze_authority
|
93
105
|
)
|
94
106
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
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(
|
114
|
-
|
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
|
-
|
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 [
|
123
|
-
# @param [
|
124
|
-
# @param [
|
125
|
-
# @
|
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
|
-
#
|
129
|
-
def
|
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
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
program_index: 4
|
164
|
+
mint: mint,
|
165
|
+
destination: destination,
|
166
|
+
mint_authority: mint_authority
|
150
167
|
)
|
151
168
|
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
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(
|
171
|
-
|
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
|
-
|
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
|
179
|
-
# @param
|
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 [
|
183
|
-
# @return [
|
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
|
-
|
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
|
-
|
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
|
-
|
204
|
-
|
205
|
-
|
206
|
-
program_index: 4
|
222
|
+
owner: owner,
|
223
|
+
source: source,
|
224
|
+
destination: destination
|
207
225
|
)
|
208
226
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
217
|
-
|
259
|
+
connection.send_transaction(tx.serialize) if execute
|
260
|
+
end
|
218
261
|
|
219
262
|
tx
|
220
263
|
end
|
221
|
-
|
264
|
+
|
265
|
+
# Prepares a transfer checked instruction and returns the signed transaction.
|
266
|
+
#
|
267
|
+
# @param amount [Integer] The number of tokens to transfer.
|
268
|
+
# @param decimals [Integer] The number of decimals for the token.
|
269
|
+
# @param from [#to_s, PublicKey] The source token account address.
|
270
|
+
# @param to [#to_s, PublicKey] The destination token account address.
|
271
|
+
# @param mint [#to_s, PublicKey] The mint address
|
272
|
+
# @param authority [#to_s, PublicKey] The keypair of the owner of the source account.
|
273
|
+
# @return [TransactionComposer] A composer with required instructions.
|
274
|
+
def compose_transfer_checked(
|
275
|
+
to:,
|
276
|
+
from:,
|
277
|
+
mint:,
|
278
|
+
authority:,
|
279
|
+
amount:,
|
280
|
+
decimals:
|
281
|
+
)
|
282
|
+
ix = Composers::SplTokenProgramTransferCheckedComposer.new(
|
283
|
+
to: to,
|
284
|
+
from: from,
|
285
|
+
mint: mint,
|
286
|
+
authority: authority,
|
287
|
+
amount: amount,
|
288
|
+
decimals: decimals
|
289
|
+
)
|
290
|
+
|
291
|
+
TransactionComposer
|
292
|
+
.new(connection: connection)
|
293
|
+
.add_instruction(ix)
|
294
|
+
end
|
222
295
|
end
|
223
|
-
# rubocop:enable Metrics/ClassLength
|
224
296
|
end
|
225
297
|
end
|
@@ -1,7 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Solace
|
4
|
-
# Serializers module
|
4
|
+
# The Serializers module contains classes for converting data structures to and
|
5
|
+
# from the binary format required by the Solana runtime.
|
6
|
+
#
|
7
|
+
# Solana transactions, messages, and instructions use a compact binary encoding
|
8
|
+
# for efficiency. The serializers in this module handle the conversion between
|
9
|
+
# Ruby objects and this binary format, including proper handling of:
|
10
|
+
# - Compact array encoding
|
11
|
+
# - Account key serialization
|
12
|
+
# - Instruction data packing
|
13
|
+
# - Message header construction
|
14
|
+
#
|
15
|
+
# Each serializer corresponds to a specific data structure:
|
16
|
+
# - {Solace::Serializers::TransactionSerializer} - Complete transactions
|
17
|
+
# - {Solace::Serializers::MessageSerializer} - Transaction messages
|
18
|
+
# - {Solace::Serializers::InstructionSerializer} - Individual instructions
|
19
|
+
# - {Solace::Serializers::AddressLookupTableSerializer} - Address lookup tables
|
20
|
+
#
|
21
|
+
# Each deserializer handles the inverse operation, converting binary data:
|
22
|
+
# - {Solace::Serializers::TransactionDeserializer}
|
23
|
+
# - {Solace::Serializers::MessageDeserializer}
|
24
|
+
# - {Solace::Serializers::InstructionDeserializer}
|
25
|
+
# - {Solace::Serializers::AddressLookupTableDeserializer}
|
26
|
+
#
|
27
|
+
# These utilities are primarily used internally by other parts of the gem, but
|
28
|
+
# can also be used directly for advanced use cases.
|
29
|
+
#
|
30
|
+
# @see Solace::Serializers::BaseSerializer
|
31
|
+
# @see Solace::Serializers::BaseDeserializer
|
32
|
+
# @since 0.0.1
|
5
33
|
module Serializers
|
6
34
|
# Autoload serializers
|
7
35
|
autoload :TransactionSerializer, 'solace/serializers/transaction_serializer'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solace
|
4
|
+
module Tokens
|
5
|
+
# Represents a Solana token with its metadata.
|
6
|
+
#
|
7
|
+
# A token object encapsulates the symbol and associated metadata for a Solana token. It provides
|
8
|
+
# dynamic access to metadata attributes via method calls. This class is used within the Solace::Tokens
|
9
|
+
# module to represent individual tokens loaded from a YAML configuration file.
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
class Token
|
13
|
+
attr_reader :symbol, :metadata
|
14
|
+
|
15
|
+
# Initializes a new Token instance.
|
16
|
+
#
|
17
|
+
# @param symbol [String] The symbol of the token (e.g., 'USDC')
|
18
|
+
# @param metadata [Hash] A hash containing the token's metadata attributes
|
19
|
+
# @return [self] The initialized Token object
|
20
|
+
def initialize(symbol, metadata)
|
21
|
+
@symbol = symbol
|
22
|
+
@metadata = metadata.transform_keys(&:to_sym)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Dynamically access metadata attributes.
|
26
|
+
#
|
27
|
+
# @param name [Symbol] The name of the metadata attribute
|
28
|
+
# @return [Object] The value of the metadata attribute if it exists
|
29
|
+
# @raise [NoMethodError] If the attribute does not exist
|
30
|
+
def method_missing(name, *)
|
31
|
+
return metadata[name] if metadata.key?(name)
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if a metadata attribute exists.
|
37
|
+
#
|
38
|
+
# @param name [Symbol] The name of the metadata attribute
|
39
|
+
# @param _include_private [Boolean] Whether to include private methods (not used)
|
40
|
+
# @return [Boolean] True if the attribute exists, false otherwise
|
41
|
+
def respond_to_missing?(name, _include_private = false)
|
42
|
+
metadata.key?(name) || super
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a string representation of the Token object.
|
46
|
+
#
|
47
|
+
# @return [String] The string representation of the Token
|
48
|
+
def inspect
|
49
|
+
"#<Solace::Token #{symbol} #{metadata.inspect}>"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Solace
|
6
|
+
# Represents a Solana token with its metadata.
|
7
|
+
#
|
8
|
+
# A tokens object encapsulates the symbol and associated metadata for a Solana token. It provides
|
9
|
+
# dynamic access to metadata attributes via method calls. This class is used within the Solace::Tokens
|
10
|
+
# module to represent individual tokens loaded from a YAML configuration file.
|
11
|
+
#
|
12
|
+
# @example Load tokens for the 'mainnet' network
|
13
|
+
# Solace::Tokens.load(path: 'path/to/tokens.yml', network: 'mainnet')
|
14
|
+
#
|
15
|
+
# @example Access a specific token
|
16
|
+
# usdc = Solace::Tokens.fetch('USDC')
|
17
|
+
# puts usdc.address
|
18
|
+
#
|
19
|
+
# @example Query tokens by criteria
|
20
|
+
# stablecoins = Solace::Tokens.where(type: 'stablecoin')
|
21
|
+
#
|
22
|
+
# @since 0.1.0
|
23
|
+
module Tokens
|
24
|
+
autoload :Token, File.expand_path('tokens/token', __dir__)
|
25
|
+
|
26
|
+
# Load tokens from a YAML file for a specific network
|
27
|
+
#
|
28
|
+
# @param path [String] Path to the YAML file
|
29
|
+
# @param network [String, Symbol] Network name (e.g., 'mainnet', 'testnet')
|
30
|
+
# @return [void]
|
31
|
+
def self.load(path:, network:)
|
32
|
+
data = YAML.load_file(path)
|
33
|
+
tokens = data.fetch(network.to_s) do
|
34
|
+
raise ArgumentError, "Network '#{network}' not found in config"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Clear previous constants and registry
|
38
|
+
clear!
|
39
|
+
|
40
|
+
@registry = {}
|
41
|
+
|
42
|
+
tokens.each do |symbol, attrs|
|
43
|
+
token = Solace::Tokens::Token.new(symbol, attrs)
|
44
|
+
const_set(symbol, token)
|
45
|
+
@registry[symbol.to_s] = token
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Clear loaded tokens
|
50
|
+
#
|
51
|
+
# @return [void]
|
52
|
+
def self.clear!
|
53
|
+
(constants - [:Token]).each { |c| remove_const(c) }
|
54
|
+
@registry = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get all loaded tokens
|
58
|
+
#
|
59
|
+
# @return [Array<Solace::Tokens::Token>]
|
60
|
+
def self.all
|
61
|
+
@registry.values
|
62
|
+
end
|
63
|
+
|
64
|
+
# Fetch a token by its symbol
|
65
|
+
#
|
66
|
+
# @param symbol [String] The symbol of the token
|
67
|
+
# @return [Solace::Tokens::Token, nil] The token object or nil if not found
|
68
|
+
def self.fetch(symbol)
|
69
|
+
@registry[symbol.to_s]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Query tokens based on criteria
|
73
|
+
#
|
74
|
+
# @param criteria [Hash{Symbol => Object}] Key-value pairs to filter tokens
|
75
|
+
# @return [Array<Solace::Tokens::Token>] Array of tokens matching the criteria
|
76
|
+
def self.where(criteria = {})
|
77
|
+
return all if criteria.empty?
|
78
|
+
|
79
|
+
normalized = criteria.transform_keys(&:to_sym)
|
80
|
+
|
81
|
+
all.select do |token|
|
82
|
+
normalized.all? { |k, v| token.metadata[k] == v }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/solace/transaction.rb
CHANGED
@@ -6,17 +6,17 @@ module Solace
|
|
6
6
|
#
|
7
7
|
# Transactions are the basic building blocks of Solana. They contain a message and an array of signatures. The
|
8
8
|
# message contains the instructions to be executed and the accounts that are used by the instructions. The signatures
|
9
|
-
# are the signatures of the accounts that are used by the instructions. This class provides methods for
|
10
|
-
# serializing, and deserializing transactions.
|
9
|
+
# are the required signatures of the accounts that are used by the instructions. This class provides methods for
|
10
|
+
# signing, serializing, and deserializing transactions.
|
11
11
|
#
|
12
12
|
# The BufferLayout is:
|
13
|
-
# -
|
14
|
-
# -
|
15
|
-
# -
|
16
|
-
# -
|
17
|
-
# -
|
18
|
-
# -
|
19
|
-
# -
|
13
|
+
# - Signatures (variable length)
|
14
|
+
# - Version (1 byte if versioned)
|
15
|
+
# - Message header (3 bytes)
|
16
|
+
# - Account keys (variable length)
|
17
|
+
# - Recent blockhash (32 bytes)
|
18
|
+
# - Instructions (variable length)
|
19
|
+
# - Address lookup table (variable length if versioned)
|
20
20
|
#
|
21
21
|
# @example
|
22
22
|
# # Create a new transaction
|
@@ -32,31 +32,26 @@ module Solace
|
|
32
32
|
class Transaction
|
33
33
|
include Solace::Concerns::BinarySerializable
|
34
34
|
|
35
|
-
#
|
36
|
-
# @return [Solace::Serializers::TransactionSerializer] The serializer for the transaction
|
35
|
+
# The serializer for the transaction
|
37
36
|
SERIALIZER = Solace::Serializers::TransactionSerializer
|
38
37
|
|
39
|
-
#
|
40
|
-
# @return [Solace::Serializers::TransactionDeserializer] The deserializer for the transaction
|
38
|
+
# The deserializer for the transaction
|
41
39
|
DESERIALIZER = Solace::Serializers::TransactionDeserializer
|
42
40
|
|
43
|
-
#
|
44
|
-
# @return [String] Placeholder for a signature in the transaction
|
41
|
+
# Placeholder for a signature in the transaction
|
45
42
|
SIGNATURE_PLACEHOLDER = Solace::Utils::Codecs.base58_to_binary('1' * 64)
|
46
43
|
|
47
|
-
#
|
48
|
-
# @return [Array<String>] Signatures of the transaction (binary)
|
44
|
+
# @return [Array<String>] Signatures of the transaction (binary)
|
49
45
|
attr_accessor :signatures
|
50
46
|
|
51
|
-
#
|
52
|
-
# @return [Solace::Message] Message of the transaction
|
47
|
+
# @return [Solace::Message] Message of the transaction
|
53
48
|
attr_accessor :message
|
54
49
|
|
55
50
|
class << self
|
56
51
|
# Deserialize a base64 encoded transaction into a Solace::Transaction object
|
57
52
|
#
|
58
53
|
# @param base64_tx [String] The base64 encoded transaction
|
59
|
-
# @return [
|
54
|
+
# @return [Transaction] The deserialized transaction
|
60
55
|
def from(base64_tx)
|
61
56
|
DESERIALIZER.new(Solace::Utils::Codecs.base64_to_bytestream(base64_tx)).call
|
62
57
|
end
|
@@ -64,7 +59,7 @@ module Solace
|
|
64
59
|
|
65
60
|
# Initialize a new transaction
|
66
61
|
#
|
67
|
-
# @return [
|
62
|
+
# @return [Transaction] The new transaction object
|
68
63
|
def initialize(
|
69
64
|
signatures: [],
|
70
65
|
message: Solace::Message.new
|
@@ -74,6 +69,14 @@ module Solace
|
|
74
69
|
@message = message
|
75
70
|
end
|
76
71
|
|
72
|
+
# Returns the first signature of the transaction (signature of the transaction fee payer)
|
73
|
+
#
|
74
|
+
# @return [String, nil] The first signature of the transaction or nil if there are no signatures
|
75
|
+
# @since 0.1.0
|
76
|
+
def signature
|
77
|
+
Utils::Codecs.binary_to_base58(signatures.first) unless signatures.empty?
|
78
|
+
end
|
79
|
+
|
77
80
|
# Sign the transaction
|
78
81
|
#
|
79
82
|
# Calls sign_and_update_signatures for each keypair passed in.
|