solana-ruby-kit 0.1.8 → 7.0.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/lib/solana/ruby/kit/errors.rb +37 -5
- data/lib/solana/ruby/kit/instruction_plans/instruction_plan.rb +34 -7
- data/lib/solana/ruby/kit/instruction_plans/max_instructions.rb +59 -0
- data/lib/solana/ruby/kit/instruction_plans/transaction_planner.rb +49 -9
- data/lib/solana/ruby/kit/instruction_plans.rb +1 -0
- data/lib/solana/ruby/kit/resource_limit_estimation.rb +156 -0
- data/lib/solana/ruby/kit/rpc/api/get_signatures_for_address.rb +72 -0
- data/lib/solana/ruby/kit/rpc/api/get_vote_accounts.rb +2 -0
- data/lib/solana/ruby/kit/rpc/client.rb +2 -0
- data/lib/solana/ruby/kit/rpc/transport.rb +3 -4
- data/lib/solana/ruby/kit/transaction_introspection/compiled_transaction_message.rb +168 -0
- data/lib/solana/ruby/kit/transaction_introspection/decode_rpc_transaction.rb +159 -0
- data/lib/solana/ruby/kit/transaction_introspection/get_inner_instructions.rb +86 -0
- data/lib/solana/ruby/kit/transaction_introspection/get_instructions.rb +134 -0
- data/lib/solana/ruby/kit/transaction_introspection/loaded_addresses.rb +19 -0
- data/lib/solana/ruby/kit/transaction_introspection/types.rb +29 -0
- data/lib/solana/ruby/kit/transaction_introspection/walk_instructions.rb +60 -0
- data/lib/solana/ruby/kit/transaction_introspection.rb +13 -0
- data/lib/solana/ruby/kit/transaction_messages/compute_budget.rb +145 -0
- data/lib/solana/ruby/kit/transaction_messages.rb +1 -0
- data/lib/solana/ruby/kit/version.rb +1 -1
- data/lib/solana/ruby/kit.rb +3 -0
- metadata +13 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b2766067cfcd434e5c657e1fde1dab4b44b23880e0f514f924d28a94ea0ca4a
|
|
4
|
+
data.tar.gz: 32814612b342df38df079aaba08b8ea17c9128ac9f54279ab3abad622ba02492
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3bcfbd20e6f610a47053cb385511931004590646ff04e0edf4587e5699df3db8bb148af2b706cf025e8399f26e4736ae9db35ebfd04d68be287df8302bc9f4de
|
|
7
|
+
data.tar.gz: d40b332b7cc427730b17b01a091c53815c596c0ac114bca93f52bed4d35775cd2b57ca5146893e2c94d46cebcbe0f51ccf98d2c6e2cad308cd51c2ff7b022d62
|
|
@@ -46,12 +46,20 @@ module Solana::Ruby::Kit
|
|
|
46
46
|
|
|
47
47
|
# ── Transactions ──────────────────────────────────────────────────────────
|
|
48
48
|
TRANSACTIONS__TRANSACTION_NOT_SIGNABLE = :SOLANA_ERROR__TRANSACTIONS__TRANSACTION_NOT_SIGNABLE
|
|
49
|
+
TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT = :SOLANA_ERROR__TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT
|
|
50
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_COMPUTE_LIMIT = :SOLANA_ERROR__TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_COMPUTE_LIMIT
|
|
51
|
+
TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT = :SOLANA_ERROR__TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT
|
|
52
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS = :SOLANA_ERROR__TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS
|
|
49
53
|
TRANSACTIONS__EXCEEDS_SIZE_LIMIT = :SOLANA_ERROR__TRANSACTION__EXCEEDS_SIZE_LIMIT
|
|
50
54
|
TRANSACTIONS__MISSING_SIGNER = :SOLANA_ERROR__TRANSACTIONS__MISSING_SIGNER
|
|
51
55
|
TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE = :SOLANA_ERROR__TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE
|
|
52
56
|
TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS
|
|
53
57
|
TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING
|
|
54
58
|
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND
|
|
59
|
+
# context: { index: }
|
|
60
|
+
TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE = :SOLANA_ERROR__TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE
|
|
61
|
+
# context: { unsupported_version: }
|
|
62
|
+
TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED = :SOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED
|
|
55
63
|
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE = :SOLANA_ERROR__TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE
|
|
56
64
|
TRANSACTIONS__BLOCKHASH_NOT_FOUND = :SOLANA_ERROR__TRANSACTIONS__BLOCKHASH_NOT_FOUND
|
|
57
65
|
TRANSACTIONS__FAILED_TRANSACTION_PLAN = :SOLANA_ERROR__TRANSACTIONS__FAILED_TRANSACTION_PLAN
|
|
@@ -88,13 +96,12 @@ module Solana::Ruby::Kit
|
|
|
88
96
|
RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER
|
|
89
97
|
RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER
|
|
90
98
|
RPC__TRANSPORT_HTTP_ERROR = :SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR
|
|
91
|
-
# JSON-RPC server errors (-
|
|
92
|
-
# EPOCH_REWARDS_PERIOD_ACTIVE: { slot:, current_block_height:, rewards_complete_block_height: }
|
|
93
|
-
# SLOT_NOT_EPOCH_BOUNDARY: { slot: }
|
|
94
|
-
# LONG_TERM_STORAGE_UNREACHABLE: (no context)
|
|
99
|
+
# JSON-RPC server errors (see https://github.com/anza-xyz/kit/blob/main/packages/errors/src/codes.ts)
|
|
95
100
|
JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE
|
|
96
101
|
JSON_RPC__SERVER_ERROR_SLOT_NOT_EPOCH_BOUNDARY = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_NOT_EPOCH_BOUNDARY
|
|
97
102
|
JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE
|
|
103
|
+
JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY
|
|
104
|
+
JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND
|
|
98
105
|
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST
|
|
99
106
|
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID
|
|
100
107
|
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED
|
|
@@ -115,6 +122,16 @@ module Solana::Ruby::Kit
|
|
|
115
122
|
INSTRUCTION_PLANS__MESSAGE_PACKER_ALREADY_COMPLETE = :SOLANA_ERROR__INSTRUCTION_PLANS__MESSAGE_PACKER_ALREADY_COMPLETE
|
|
116
123
|
INSTRUCTION_PLANS__EMPTY_INSTRUCTION_PLAN = :SOLANA_ERROR__INSTRUCTION_PLANS__EMPTY_INSTRUCTION_PLAN
|
|
117
124
|
INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN = :SOLANA_ERROR__INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN
|
|
125
|
+
# context: { max_instructions:, transaction_instruction_limit: }
|
|
126
|
+
INSTRUCTION_PLANS__INVALID_MAX_INSTRUCTIONS_PER_TRANSACTION = :SOLANA_ERROR__INSTRUCTION_PLANS__INVALID_MAX_INSTRUCTIONS_PER_TRANSACTION
|
|
127
|
+
# context: { max_instructions:, num_instructions: }
|
|
128
|
+
INSTRUCTION_PLANS__MAX_INSTRUCTIONS_PER_TRANSACTION_EXCEEDED = :SOLANA_ERROR__INSTRUCTION_PLANS__MAX_INSTRUCTIONS_PER_TRANSACTION_EXCEEDED
|
|
129
|
+
|
|
130
|
+
# ── Transaction introspection ────────────────────────────────────────────
|
|
131
|
+
TRANSACTION_INTROSPECTION__CANNOT_DECODE_JSON_PARSED_TRANSACTION = :SOLANA_ERROR__TRANSACTION_INTROSPECTION__CANNOT_DECODE_JSON_PARSED_TRANSACTION
|
|
132
|
+
TRANSACTION_INTROSPECTION__UNRECOGNIZED_GET_TRANSACTION_RESPONSE = :SOLANA_ERROR__TRANSACTION_INTROSPECTION__UNRECOGNIZED_GET_TRANSACTION_RESPONSE
|
|
133
|
+
# context: { needed_bytes: }
|
|
134
|
+
TRANSACTION_INTROSPECTION__MALFORMED_COMPILED_MESSAGE = :SOLANA_ERROR__TRANSACTION_INTROSPECTION__MALFORMED_COMPILED_MESSAGE
|
|
118
135
|
|
|
119
136
|
# ── Wallet Standard ───────────────────────────────────────────────────────
|
|
120
137
|
WALLET_STANDARD__INVALID_WIRE_FORMAT = :SOLANA_ERROR__WALLET_STANDARD__INVALID_WIRE_FORMAT
|
|
@@ -172,12 +189,18 @@ module Solana::Ruby::Kit
|
|
|
172
189
|
|
|
173
190
|
# Transactions
|
|
174
191
|
TRANSACTIONS__TRANSACTION_NOT_SIGNABLE => 'Transaction is not signable (missing fee payer or lifetime constraint)',
|
|
192
|
+
TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT => 'Failed to estimate the compute unit consumption for this transaction message. This is likely because simulating the transaction failed.',
|
|
193
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_COMPUTE_LIMIT => 'Transaction failed when it was simulated in order to estimate the compute unit consumption.',
|
|
194
|
+
TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT => 'Failed to estimate the loaded accounts data size for this transaction message. The RPC did not return a loadedAccountsDataSize value from simulation. This value is required for version 1 transactions.',
|
|
195
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS => 'Transaction failed when it was simulated in order to estimate its resource limits.',
|
|
175
196
|
TRANSACTIONS__EXCEEDS_SIZE_LIMIT => 'Transaction wire size (%{actual_size} bytes) exceeds the limit of %{limit} bytes',
|
|
176
197
|
TRANSACTIONS__MISSING_SIGNER => 'Transaction is missing a required signer: %{address}',
|
|
177
198
|
TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE => 'Transaction version %{version} is out of range',
|
|
178
199
|
TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS => 'Failed to decompile address lookup table contents',
|
|
179
200
|
TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING => 'Failed to decompile transaction: fee payer is missing',
|
|
180
|
-
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND => 'Failed to decompile instruction: program address not found',
|
|
201
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND => 'Failed to decompile instruction: program address not found at index %{index}',
|
|
202
|
+
TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE => 'Failed to decompile instruction: account index %{index} is out of range',
|
|
203
|
+
TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED => 'Transaction version %{unsupported_version} is not supported',
|
|
181
204
|
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE => 'Transaction simulation failed: %{message}',
|
|
182
205
|
TRANSACTIONS__BLOCKHASH_NOT_FOUND => 'Blockhash not found',
|
|
183
206
|
TRANSACTIONS__FAILED_TRANSACTION_PLAN => 'Failed to execute transaction plan',
|
|
@@ -217,6 +240,8 @@ module Solana::Ruby::Kit
|
|
|
217
240
|
JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE => 'Epoch rewards period still active at slot %{slot}',
|
|
218
241
|
JSON_RPC__SERVER_ERROR_SLOT_NOT_EPOCH_BOUNDARY => "Rewards cannot be found because slot %{slot} is not the epoch boundary. This may be due to gap in the queried node's local ledger or long-term storage",
|
|
219
242
|
JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE => 'Failed to query long-term storage; please try again',
|
|
243
|
+
JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY => 'No slot history',
|
|
244
|
+
JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND => 'Filter transaction not found',
|
|
220
245
|
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST => 'Cannot create subscription request',
|
|
221
246
|
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID => 'Expected server to return a subscription ID',
|
|
222
247
|
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED => 'WebSocket channel closed before message could be buffered',
|
|
@@ -236,6 +261,13 @@ module Solana::Ruby::Kit
|
|
|
236
261
|
INSTRUCTION_PLANS__MESSAGE_PACKER_ALREADY_COMPLETE => 'Message packer is already complete; no more instructions to pack',
|
|
237
262
|
INSTRUCTION_PLANS__EMPTY_INSTRUCTION_PLAN => 'Instruction plan is empty and produced no transaction messages',
|
|
238
263
|
INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN => 'Failed to execute transaction plan',
|
|
264
|
+
INSTRUCTION_PLANS__INVALID_MAX_INSTRUCTIONS_PER_TRANSACTION => 'The configured maximum of %{max_instructions} instructions per transaction is invalid. It must be a positive integer no greater than the transaction format limit of %{transaction_instruction_limit} instructions per transaction.',
|
|
265
|
+
INSTRUCTION_PLANS__MAX_INSTRUCTIONS_PER_TRANSACTION_EXCEEDED => 'Planning this transaction message would require %{num_instructions} instructions, which exceeds the configured maximum of %{max_instructions} instructions per transaction.',
|
|
266
|
+
|
|
267
|
+
# Transaction introspection
|
|
268
|
+
TRANSACTION_INTROSPECTION__CANNOT_DECODE_JSON_PARSED_TRANSACTION => "Cannot decode a 'jsonParsed'-encoded getTransaction response; its instructions are pre-parsed and lack raw bytes. Fetch with encoding: 'json' or 'base64' instead.",
|
|
269
|
+
TRANSACTION_INTROSPECTION__UNRECOGNIZED_GET_TRANSACTION_RESPONSE => 'Unrecognized getTransaction response shape; expected a base64, base58, or json-encoded transaction.',
|
|
270
|
+
TRANSACTION_INTROSPECTION__MALFORMED_COMPILED_MESSAGE => 'Malformed compiled transaction message: expected at least %{needed_bytes} byte(s)',
|
|
239
271
|
|
|
240
272
|
# Subscribable
|
|
241
273
|
SUBSCRIBABLE__RETRY_NOT_SUPPORTED => 'This reactive store does not support retry(); use create_reactive_store_from_data_publisher_factory for a retryable store',
|
|
@@ -5,6 +5,7 @@ require_relative '../errors'
|
|
|
5
5
|
require_relative '../instructions/instruction'
|
|
6
6
|
require_relative '../transaction_messages/transaction_message'
|
|
7
7
|
require_relative '../transactions/compiler'
|
|
8
|
+
require_relative 'max_instructions'
|
|
8
9
|
|
|
9
10
|
module Solana::Ruby::Kit
|
|
10
11
|
module InstructionPlans
|
|
@@ -68,9 +69,17 @@ module Solana::Ruby::Kit
|
|
|
68
69
|
# Packs as many instructions as possible into +message+ and returns the
|
|
69
70
|
# updated message. Raises SolanaError if the message is too small or the
|
|
70
71
|
# packer is already done.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
#
|
|
73
|
+
# +max_instructions+ caps the number of top-level instructions allowed in the
|
|
74
|
+
# returned message (defaults to 16; must be a positive integer no greater than 64).
|
|
75
|
+
sig do
|
|
76
|
+
params(
|
|
77
|
+
message: TransactionMessages::TransactionMessage,
|
|
78
|
+
max_instructions: T.nilable(Integer)
|
|
79
|
+
).returns(TransactionMessages::TransactionMessage)
|
|
80
|
+
end
|
|
81
|
+
def pack_message_to_capacity(message, max_instructions: nil)
|
|
82
|
+
@pack_proc.call(message, max_instructions)
|
|
74
83
|
end
|
|
75
84
|
end
|
|
76
85
|
|
|
@@ -122,11 +131,15 @@ module Solana::Ruby::Kit
|
|
|
122
131
|
offset = T.let(0, Integer)
|
|
123
132
|
MessagePacker.new(
|
|
124
133
|
done_proc: -> { offset >= total_length },
|
|
125
|
-
pack_proc: ->(message) {
|
|
134
|
+
pack_proc: ->(message, max_instructions) {
|
|
126
135
|
if offset >= total_length
|
|
127
136
|
Kernel.raise SolanaError.new(SolanaError::INSTRUCTION_PLANS__MESSAGE_PACKER_ALREADY_COMPLETE)
|
|
128
137
|
end
|
|
129
138
|
|
|
139
|
+
InstructionPlans.assert_valid_max_instructions_per_transaction(max_instructions)
|
|
140
|
+
resolved_max = InstructionPlans.resolve_max_instructions(max_instructions)
|
|
141
|
+
InstructionPlans.assert_max_instructions_per_transaction(message.instructions.length + 1, resolved_max)
|
|
142
|
+
|
|
130
143
|
base_ix = get_instruction.call(offset, 0)
|
|
131
144
|
with_base = TransactionMessages.append_instructions(message, [base_ix])
|
|
132
145
|
base_size = Transactions.get_transaction_message_size(with_base)
|
|
@@ -164,18 +177,30 @@ module Solana::Ruby::Kit
|
|
|
164
177
|
idx = T.let(0, Integer)
|
|
165
178
|
MessagePacker.new(
|
|
166
179
|
done_proc: -> { idx >= instructions.length },
|
|
167
|
-
pack_proc: ->(message) {
|
|
180
|
+
pack_proc: ->(message, max_instructions) {
|
|
168
181
|
if idx >= instructions.length
|
|
169
182
|
Kernel.raise SolanaError.new(SolanaError::INSTRUCTION_PLANS__MESSAGE_PACKER_ALREADY_COMPLETE)
|
|
170
183
|
end
|
|
171
184
|
|
|
185
|
+
InstructionPlans.assert_valid_max_instructions_per_transaction(max_instructions)
|
|
186
|
+
resolved_max = InstructionPlans.resolve_max_instructions(max_instructions)
|
|
187
|
+
InstructionPlans.assert_max_instructions_per_transaction(message.instructions.length + 1, resolved_max)
|
|
188
|
+
|
|
172
189
|
original_size = Transactions.get_transaction_message_size(message)
|
|
173
190
|
packed = T.let(message, TransactionMessages::TransactionMessage)
|
|
174
191
|
start_idx = idx
|
|
175
192
|
|
|
176
193
|
(idx...instructions.length).each do |i|
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
# Stop once the message is full, before compiling a message that would exceed
|
|
195
|
+
# the instruction limit (which would raise). The assertion above guarantees at
|
|
196
|
+
# least the first instruction fits, so reaching the limit here is a graceful stop.
|
|
197
|
+
if packed.instructions.length >= resolved_max
|
|
198
|
+
idx = i
|
|
199
|
+
return packed
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
next_packed = TransactionMessages.append_instructions(packed, [instructions[i]])
|
|
203
|
+
size = Transactions.get_transaction_message_size(next_packed)
|
|
179
204
|
|
|
180
205
|
if size > Transactions::TRANSACTION_SIZE_LIMIT
|
|
181
206
|
if i == start_idx
|
|
@@ -190,6 +215,8 @@ module Solana::Ruby::Kit
|
|
|
190
215
|
idx = i
|
|
191
216
|
return packed
|
|
192
217
|
end
|
|
218
|
+
|
|
219
|
+
packed = next_packed
|
|
193
220
|
end
|
|
194
221
|
|
|
195
222
|
idx = instructions.length
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../errors'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module InstructionPlans
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
# The default maximum number of top-level instructions per planned transaction message.
|
|
11
|
+
#
|
|
12
|
+
# Intentionally lower than the transaction format's instruction limit to leave headroom
|
|
13
|
+
# for inner instructions (CPIs), which are not visible at planning time.
|
|
14
|
+
DEFAULT_MAX_INSTRUCTIONS_PER_TRANSACTION = T.let(16, Integer)
|
|
15
|
+
|
|
16
|
+
# The hard maximum number of top-level instructions the transaction format can encode.
|
|
17
|
+
TRANSACTION_INSTRUCTION_LIMIT = T.let(64, Integer)
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Resolves the effective maximum number of instructions allowed in a transaction message,
|
|
22
|
+
# falling back to the default when no value is provided.
|
|
23
|
+
# Mirrors `resolveMaxInstructions(maxInstructions)`.
|
|
24
|
+
sig { params(max_instructions: T.nilable(Integer)).returns(Integer) }
|
|
25
|
+
def resolve_max_instructions(max_instructions)
|
|
26
|
+
max_instructions || DEFAULT_MAX_INSTRUCTIONS_PER_TRANSACTION
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Asserts that a configured maximum number of instructions per transaction is valid.
|
|
30
|
+
# +nil+ is allowed and falls back to the default.
|
|
31
|
+
# Mirrors `assertValidMaxInstructionsPerTransaction(maxInstructions)`.
|
|
32
|
+
sig { params(max_instructions: T.nilable(Integer)).void }
|
|
33
|
+
def assert_valid_max_instructions_per_transaction(max_instructions)
|
|
34
|
+
return if max_instructions.nil?
|
|
35
|
+
|
|
36
|
+
if max_instructions <= 0 || max_instructions > TRANSACTION_INSTRUCTION_LIMIT
|
|
37
|
+
Kernel.raise SolanaError.new(
|
|
38
|
+
SolanaError::INSTRUCTION_PLANS__INVALID_MAX_INSTRUCTIONS_PER_TRANSACTION,
|
|
39
|
+
{
|
|
40
|
+
max_instructions: max_instructions,
|
|
41
|
+
transaction_instruction_limit: TRANSACTION_INSTRUCTION_LIMIT
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Raises if +num_instructions+ exceeds +max_instructions+.
|
|
48
|
+
# Mirrors `assertMaxInstructionsPerTransaction(numInstructions, maxInstructions)`.
|
|
49
|
+
sig { params(num_instructions: Integer, max_instructions: Integer).void }
|
|
50
|
+
def assert_max_instructions_per_transaction(num_instructions, max_instructions)
|
|
51
|
+
return if num_instructions <= max_instructions
|
|
52
|
+
|
|
53
|
+
Kernel.raise SolanaError.new(
|
|
54
|
+
SolanaError::INSTRUCTION_PLANS__MAX_INSTRUCTIONS_PER_TRANSACTION_EXCEEDED,
|
|
55
|
+
{ max_instructions: max_instructions, num_instructions: num_instructions }
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -23,27 +23,45 @@ module Solana::Ruby::Kit
|
|
|
23
23
|
# Called after instructions are appended to a message. Must return the
|
|
24
24
|
# (possibly further updated) message. Defaults to identity.
|
|
25
25
|
#
|
|
26
|
-
#
|
|
26
|
+
# max_instructions_per_transaction: Integer (optional)
|
|
27
|
+
# Maximum number of top-level instructions allowed in each planned transaction.
|
|
28
|
+
# Must be a positive integer no greater than 64. Defaults to 16. Can be overridden
|
|
29
|
+
# per-call via the returned planner's `max_instructions_per_transaction:` kwarg.
|
|
30
|
+
#
|
|
31
|
+
# The returned planner is a lambda:
|
|
32
|
+
# planner.call(instruction_plan, max_instructions_per_transaction: nil) -> TransactionPlan
|
|
27
33
|
#
|
|
28
34
|
# Mirrors `createTransactionPlanner(config)` from @solana/instruction-plans.
|
|
29
35
|
# NOTE: Ruby translation is synchronous; abort-signal support is omitted.
|
|
30
36
|
sig do
|
|
31
37
|
params(
|
|
32
|
-
create_transaction_message:
|
|
33
|
-
on_transaction_message_updated:
|
|
38
|
+
create_transaction_message: T.untyped,
|
|
39
|
+
on_transaction_message_updated: T.untyped,
|
|
40
|
+
max_instructions_per_transaction: T.nilable(Integer)
|
|
34
41
|
).returns(T.untyped)
|
|
35
42
|
end
|
|
36
43
|
def create_transaction_planner(
|
|
37
44
|
create_transaction_message:,
|
|
38
|
-
on_transaction_message_updated: ->(msg) { msg }
|
|
45
|
+
on_transaction_message_updated: ->(msg) { msg },
|
|
46
|
+
max_instructions_per_transaction: nil
|
|
39
47
|
)
|
|
40
48
|
ctx = {
|
|
41
49
|
create_transaction_message: create_transaction_message,
|
|
42
50
|
on_transaction_message_updated: on_transaction_message_updated
|
|
43
51
|
}
|
|
52
|
+
config_max_instructions_per_transaction = max_instructions_per_transaction
|
|
53
|
+
|
|
54
|
+
->(instruction_plan, max_instructions_per_transaction: nil) {
|
|
55
|
+
resolved_max = max_instructions_per_transaction || config_max_instructions_per_transaction
|
|
56
|
+
|
|
57
|
+
# Reject up front any configured maximum the transaction format could never satisfy,
|
|
58
|
+
# rather than discovering it mid-plan when a message fails to compile.
|
|
59
|
+
InstructionPlans.assert_valid_max_instructions_per_transaction(resolved_max)
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
mutable = planner_traverse(
|
|
62
|
+
instruction_plan,
|
|
63
|
+
ctx.merge(parent: nil, parent_candidates: [], max_instructions_per_transaction: resolved_max)
|
|
64
|
+
)
|
|
47
65
|
Kernel.raise SolanaError.new(SolanaError::INSTRUCTION_PLANS__EMPTY_INSTRUCTION_PLAN) unless mutable
|
|
48
66
|
planner_freeze(mutable)
|
|
49
67
|
}
|
|
@@ -136,7 +154,7 @@ module Solana::Ruby::Kit
|
|
|
136
154
|
candidates = ctx[:parent_candidates].dup
|
|
137
155
|
|
|
138
156
|
until packer.done?
|
|
139
|
-
predicate = ->(msg) { packer.pack_message_to_capacity(msg) }
|
|
157
|
+
predicate = ->(msg) { packer.pack_message_to_capacity(msg, max_instructions: ctx[:max_instructions_per_transaction]) }
|
|
140
158
|
candidate = planner_select_and_mutate_candidate(ctx, candidates, &predicate)
|
|
141
159
|
unless candidate
|
|
142
160
|
msg = planner_create_new_message(ctx, &predicate)
|
|
@@ -162,11 +180,18 @@ module Solana::Ruby::Kit
|
|
|
162
180
|
begin
|
|
163
181
|
updated = ctx[:on_transaction_message_updated].call(predicate.call(candidate[:message]))
|
|
164
182
|
if Transactions.get_transaction_message_size(updated) <= Transactions::TRANSACTION_SIZE_LIMIT
|
|
183
|
+
InstructionPlans.assert_max_instructions_per_transaction(
|
|
184
|
+
updated.instructions.length,
|
|
185
|
+
InstructionPlans.resolve_max_instructions(ctx[:max_instructions_per_transaction])
|
|
186
|
+
)
|
|
165
187
|
candidate[:message] = updated
|
|
166
188
|
return candidate
|
|
167
189
|
end
|
|
168
190
|
rescue SolanaError => e
|
|
169
|
-
next if
|
|
191
|
+
next if [
|
|
192
|
+
SolanaError::INSTRUCTION_PLANS__MESSAGE_CANNOT_ACCOMMODATE_PLAN,
|
|
193
|
+
SolanaError::INSTRUCTION_PLANS__MAX_INSTRUCTIONS_PER_TRANSACTION_EXCEEDED
|
|
194
|
+
].include?(e.code)
|
|
170
195
|
Kernel.raise
|
|
171
196
|
end
|
|
172
197
|
end
|
|
@@ -187,6 +212,11 @@ module Solana::Ruby::Kit
|
|
|
187
212
|
)
|
|
188
213
|
end
|
|
189
214
|
|
|
215
|
+
InstructionPlans.assert_max_instructions_per_transaction(
|
|
216
|
+
updated_msg.instructions.length,
|
|
217
|
+
InstructionPlans.resolve_max_instructions(ctx[:max_instructions_per_transaction])
|
|
218
|
+
)
|
|
219
|
+
|
|
190
220
|
updated_msg
|
|
191
221
|
end
|
|
192
222
|
|
|
@@ -205,11 +235,21 @@ module Solana::Ruby::Kit
|
|
|
205
235
|
num_free_bytes: Transactions::TRANSACTION_SIZE_LIMIT - base_size }
|
|
206
236
|
)
|
|
207
237
|
end
|
|
238
|
+
InstructionPlans.assert_max_instructions_per_transaction(
|
|
239
|
+
updated.instructions.length,
|
|
240
|
+
InstructionPlans.resolve_max_instructions(ctx[:max_instructions_per_transaction])
|
|
241
|
+
)
|
|
208
242
|
updated
|
|
209
243
|
when :message_packer
|
|
210
244
|
packer = plan.get_message_packer.call
|
|
211
245
|
msg = message
|
|
212
|
-
|
|
246
|
+
until packer.done?
|
|
247
|
+
msg = packer.pack_message_to_capacity(msg, max_instructions: ctx[:max_instructions_per_transaction])
|
|
248
|
+
InstructionPlans.assert_max_instructions_per_transaction(
|
|
249
|
+
msg.instructions.length,
|
|
250
|
+
InstructionPlans.resolve_max_instructions(ctx[:max_instructions_per_transaction])
|
|
251
|
+
)
|
|
252
|
+
end
|
|
213
253
|
msg
|
|
214
254
|
else
|
|
215
255
|
Kernel.raise SolanaError.new(
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# Mirrors @solana/instruction-plans.
|
|
5
5
|
# An InstructionPlan describes operations that may span multiple transactions.
|
|
6
|
+
require_relative 'instruction_plans/max_instructions'
|
|
6
7
|
require_relative 'instruction_plans/instruction_plan'
|
|
7
8
|
require_relative 'instruction_plans/transaction_plan'
|
|
8
9
|
require_relative 'instruction_plans/transaction_plan_result'
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'base64'
|
|
5
|
+
require_relative 'errors'
|
|
6
|
+
require_relative 'functional'
|
|
7
|
+
require_relative 'transaction_messages'
|
|
8
|
+
require_relative 'transaction_messages/compute_budget'
|
|
9
|
+
require_relative 'transactions'
|
|
10
|
+
|
|
11
|
+
module Solana::Ruby::Kit
|
|
12
|
+
# Utilities for estimating and setting transaction resource limits (compute units and
|
|
13
|
+
# loaded accounts data size) via simulation.
|
|
14
|
+
#
|
|
15
|
+
# Mirrors `estimateResourceLimitsFactory`, `estimateAndSetResourceLimitsFactory`,
|
|
16
|
+
# and `fillTransactionMessageProvisoryResourceLimits` from @solana/kit.
|
|
17
|
+
module ResourceLimitEstimation
|
|
18
|
+
extend T::Sig
|
|
19
|
+
|
|
20
|
+
PROVISORY_LIMIT = T.let(0, Integer)
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# Returns a callable that estimates the resource limits required by a transaction message
|
|
25
|
+
# by simulating it with maximum limits set, then reading back the actual consumption.
|
|
26
|
+
#
|
|
27
|
+
# The returned callable accepts a TransactionMessage and optional keyword args:
|
|
28
|
+
# commitment: Symbol (nil uses RPC default)
|
|
29
|
+
# min_context_slot: Integer
|
|
30
|
+
#
|
|
31
|
+
# Returns a Hash with:
|
|
32
|
+
# :compute_unit_limit Integer (always present)
|
|
33
|
+
# :loaded_accounts_data_size_limit Integer (present when the RPC returns it)
|
|
34
|
+
#
|
|
35
|
+
# Raises SolanaError with TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT
|
|
36
|
+
# if the message is version 1 but the RPC does not return loadedAccountsDataSize.
|
|
37
|
+
# Raises SolanaError with TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS
|
|
38
|
+
# if the simulated transaction itself fails.
|
|
39
|
+
# Raises SolanaError with TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT for any other error.
|
|
40
|
+
#
|
|
41
|
+
# Mirrors `estimateResourceLimitsFactory({ rpc })` from @solana/kit.
|
|
42
|
+
sig { params(rpc: T.untyped).returns(T.untyped) }
|
|
43
|
+
def estimate_resource_limits_factory(rpc:)
|
|
44
|
+
->(transaction_message, commitment: nil, min_context_slot: nil) do
|
|
45
|
+
replace_recent_blockhash = !TransactionMessages.durable_nonce_lifetime?(transaction_message)
|
|
46
|
+
is_v1 = transaction_message.version == 1
|
|
47
|
+
|
|
48
|
+
tx_for_sim = Functional.pipe(
|
|
49
|
+
transaction_message,
|
|
50
|
+
->(m) { TransactionMessages.set_transaction_message_compute_unit_limit(TransactionMessages::MAX_COMPUTE_UNIT_LIMIT, m) },
|
|
51
|
+
->(m) { is_v1 ? TransactionMessages.set_transaction_message_loaded_accounts_data_size_limit(TransactionMessages::MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT, m) : m }
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
transaction = Transactions.compile_transaction_message(tx_for_sim)
|
|
55
|
+
wire_bytes = Transactions.wire_encode_transaction(transaction)
|
|
56
|
+
encoded = Base64.strict_encode64(wire_bytes)
|
|
57
|
+
|
|
58
|
+
sim_opts = { replace_recent_blockhash: replace_recent_blockhash }
|
|
59
|
+
sim_opts[:commitment] = commitment if commitment
|
|
60
|
+
sim_opts[:min_context_slot] = min_context_slot if min_context_slot
|
|
61
|
+
|
|
62
|
+
begin
|
|
63
|
+
sim_value = rpc.simulate_transaction(encoded, **sim_opts).value
|
|
64
|
+
|
|
65
|
+
transaction_error = sim_value['err']
|
|
66
|
+
units_consumed = sim_value['unitsConsumed']
|
|
67
|
+
loaded_accounts_data_size = sim_value['loadedAccountsDataSize']
|
|
68
|
+
|
|
69
|
+
if is_v1 && loaded_accounts_data_size.nil?
|
|
70
|
+
Kernel.raise SolanaError.new(SolanaError::TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if transaction_error
|
|
74
|
+
Kernel.raise SolanaError.new(
|
|
75
|
+
SolanaError::TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
compute_unit_limit = [units_consumed.to_i, 4_294_967_295].min
|
|
80
|
+
|
|
81
|
+
estimate = { compute_unit_limit: compute_unit_limit }
|
|
82
|
+
estimate[:loaded_accounts_data_size_limit] = loaded_accounts_data_size.to_i unless loaded_accounts_data_size.nil?
|
|
83
|
+
estimate
|
|
84
|
+
rescue SolanaError => e
|
|
85
|
+
known = [
|
|
86
|
+
SolanaError::TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS,
|
|
87
|
+
SolanaError::TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT
|
|
88
|
+
]
|
|
89
|
+
Kernel.raise if known.include?(e.code)
|
|
90
|
+
Kernel.raise SolanaError.new(SolanaError::TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT)
|
|
91
|
+
rescue StandardError
|
|
92
|
+
Kernel.raise SolanaError.new(SolanaError::TRANSACTION__FAILED_TO_ESTIMATE_COMPUTE_LIMIT)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Returns a callable that estimates resource limits for a transaction message and
|
|
98
|
+
# sets them on the message, unless they are already explicitly configured.
|
|
99
|
+
#
|
|
100
|
+
# A limit of 0 (the provisory value) or the maximum (1,400,000 for CU) is treated
|
|
101
|
+
# as non-explicit and will be replaced with the estimate.
|
|
102
|
+
#
|
|
103
|
+
# Mirrors `estimateAndSetResourceLimitsFactory(estimator)` from @solana/kit.
|
|
104
|
+
sig { params(estimate_resource_limits: T.untyped).returns(T.untyped) }
|
|
105
|
+
def estimate_and_set_resource_limits_factory(estimate_resource_limits)
|
|
106
|
+
->(transaction_message, **opts) do
|
|
107
|
+
existing_cu = TransactionMessages.get_transaction_message_compute_unit_limit(transaction_message)
|
|
108
|
+
cu_explicit = !existing_cu.nil? &&
|
|
109
|
+
existing_cu != PROVISORY_LIMIT &&
|
|
110
|
+
existing_cu != TransactionMessages::MAX_COMPUTE_UNIT_LIMIT
|
|
111
|
+
|
|
112
|
+
is_v1 = transaction_message.version == 1
|
|
113
|
+
loaded_explicit = true
|
|
114
|
+
if is_v1
|
|
115
|
+
existing_loaded = TransactionMessages.get_transaction_message_loaded_accounts_data_size_limit(transaction_message)
|
|
116
|
+
loaded_explicit = !existing_loaded.nil? && existing_loaded != PROVISORY_LIMIT
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
return transaction_message if cu_explicit && loaded_explicit
|
|
120
|
+
|
|
121
|
+
estimate = estimate_resource_limits.call(transaction_message, **opts)
|
|
122
|
+
|
|
123
|
+
result = transaction_message
|
|
124
|
+
unless cu_explicit
|
|
125
|
+
result = TransactionMessages.set_transaction_message_compute_unit_limit(estimate[:compute_unit_limit], result)
|
|
126
|
+
end
|
|
127
|
+
if is_v1 && !loaded_explicit && estimate.key?(:loaded_accounts_data_size_limit)
|
|
128
|
+
result = TransactionMessages.set_transaction_message_loaded_accounts_data_size_limit(estimate[:loaded_accounts_data_size_limit], result)
|
|
129
|
+
end
|
|
130
|
+
result
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Sets compute unit limit (and, for V1, loaded accounts data size limit) to the
|
|
135
|
+
# provisory value of 0 on the message, unless a limit is already present.
|
|
136
|
+
#
|
|
137
|
+
# Use this during message construction to reserve space for limits that will later
|
|
138
|
+
# be replaced with actual estimates via `estimate_and_set_resource_limits_factory`.
|
|
139
|
+
#
|
|
140
|
+
# Mirrors `fillTransactionMessageProvisoryResourceLimits` from @solana/kit.
|
|
141
|
+
sig do
|
|
142
|
+
params(message: TransactionMessages::TransactionMessage)
|
|
143
|
+
.returns(TransactionMessages::TransactionMessage)
|
|
144
|
+
end
|
|
145
|
+
def fill_transaction_message_provisory_resource_limits(message)
|
|
146
|
+
result = message
|
|
147
|
+
if TransactionMessages.get_transaction_message_compute_unit_limit(result).nil?
|
|
148
|
+
result = TransactionMessages.set_transaction_message_compute_unit_limit(PROVISORY_LIMIT, result)
|
|
149
|
+
end
|
|
150
|
+
if result.version == 1 && TransactionMessages.get_transaction_message_loaded_accounts_data_size_limit(result).nil?
|
|
151
|
+
result = TransactionMessages.set_transaction_message_loaded_accounts_data_size_limit(PROVISORY_LIMIT, result)
|
|
152
|
+
end
|
|
153
|
+
result
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Struct representing one transaction signature entry returned by getSignaturesForAddress.
|
|
8
|
+
# Mirrors TypeScript's GetSignaturesForAddressTransaction.
|
|
9
|
+
SignatureInfo = T.let(
|
|
10
|
+
Struct.new(
|
|
11
|
+
:block_time, # Integer | nil — Unix timestamp of block production, nil if unavailable
|
|
12
|
+
:confirmation_status, # Symbol | nil — :processed, :confirmed, or :finalized
|
|
13
|
+
:err, # Hash | nil — transaction error, nil if succeeded
|
|
14
|
+
:memo, # String | nil — memo associated with the transaction
|
|
15
|
+
:signature, # String — base-58 encoded transaction signature
|
|
16
|
+
:slot, # Integer — slot containing the transaction
|
|
17
|
+
:transaction_index, # Integer | nil — 0-based index within block (Agave 4.0+)
|
|
18
|
+
keyword_init: true
|
|
19
|
+
),
|
|
20
|
+
T.untyped
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Returns confirmed transaction signatures that reference the given address.
|
|
24
|
+
# Mirrors TypeScript's GetSignaturesForAddressApi.getSignaturesForAddress.
|
|
25
|
+
# See https://solana.com/docs/rpc/http/getsignaturesforaddress
|
|
26
|
+
module GetSignaturesForAddress
|
|
27
|
+
extend T::Sig
|
|
28
|
+
|
|
29
|
+
sig do
|
|
30
|
+
params(
|
|
31
|
+
address: String,
|
|
32
|
+
before: T.nilable(String),
|
|
33
|
+
until_sig: T.nilable(String),
|
|
34
|
+
limit: T.nilable(Integer),
|
|
35
|
+
commitment: T.nilable(Symbol),
|
|
36
|
+
min_context_slot: T.nilable(Integer)
|
|
37
|
+
).returns(T::Array[T.untyped])
|
|
38
|
+
end
|
|
39
|
+
def get_signatures_for_address(
|
|
40
|
+
address,
|
|
41
|
+
before: nil,
|
|
42
|
+
until_sig: nil,
|
|
43
|
+
limit: nil,
|
|
44
|
+
commitment: nil,
|
|
45
|
+
min_context_slot: nil
|
|
46
|
+
)
|
|
47
|
+
config = {}
|
|
48
|
+
config['before'] = before if before
|
|
49
|
+
config['until'] = until_sig if until_sig
|
|
50
|
+
config['limit'] = limit if limit
|
|
51
|
+
config['commitment'] = commitment.to_s if commitment
|
|
52
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
53
|
+
|
|
54
|
+
params = config.empty? ? [address] : [address, config]
|
|
55
|
+
result = transport.request('getSignaturesForAddress', params)
|
|
56
|
+
|
|
57
|
+
result.map do |tx|
|
|
58
|
+
SignatureInfo.new(
|
|
59
|
+
block_time: tx['blockTime'],
|
|
60
|
+
confirmation_status: tx['confirmationStatus']&.to_sym,
|
|
61
|
+
err: tx['err'],
|
|
62
|
+
memo: tx['memo'],
|
|
63
|
+
signature: tx['signature'],
|
|
64
|
+
slot: Kernel.Integer(tx['slot']),
|
|
65
|
+
transaction_index: tx['transactionIndex']
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -12,6 +12,7 @@ module Solana::Ruby::Kit
|
|
|
12
12
|
:activated_stake,
|
|
13
13
|
:epoch_vote_account,
|
|
14
14
|
:commission,
|
|
15
|
+
:inflation_rewards_commission_bps,
|
|
15
16
|
:last_vote,
|
|
16
17
|
:epoch_credits,
|
|
17
18
|
:root_slot,
|
|
@@ -45,6 +46,7 @@ module Solana::Ruby::Kit
|
|
|
45
46
|
activated_stake: Kernel.Integer(v['activatedStake']),
|
|
46
47
|
epoch_vote_account: v['epochVoteAccount'],
|
|
47
48
|
commission: Kernel.Integer(v['commission']),
|
|
49
|
+
inflation_rewards_commission_bps: v['inflationRewardsCommissionBps'] ? Kernel.Integer(v['inflationRewardsCommissionBps']) : nil,
|
|
48
50
|
last_vote: Kernel.Integer(v['lastVote']),
|
|
49
51
|
epoch_credits: v['epochCredits'],
|
|
50
52
|
root_slot: v['rootSlot'] ? Kernel.Integer(v['rootSlot']) : nil
|
|
@@ -23,6 +23,7 @@ require_relative 'api/simulate_transaction'
|
|
|
23
23
|
require_relative 'api/get_block_time'
|
|
24
24
|
require_relative 'api/get_epoch_schedule'
|
|
25
25
|
require_relative 'api/get_inflation_reward'
|
|
26
|
+
require_relative 'api/get_signatures_for_address'
|
|
26
27
|
|
|
27
28
|
module Solana::Ruby::Kit
|
|
28
29
|
module Rpc
|
|
@@ -62,6 +63,7 @@ module Solana::Ruby::Kit
|
|
|
62
63
|
include Api::GetBlockTime
|
|
63
64
|
include Api::GetEpochSchedule
|
|
64
65
|
include Api::GetInflationReward
|
|
66
|
+
include Api::GetSignaturesForAddress
|
|
65
67
|
|
|
66
68
|
sig { returns(Transport) }
|
|
67
69
|
attr_reader :transport
|