solana-ruby-kit 0.1.8 → 0.1.9
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 +13 -4
- 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/client.rb +2 -0
- data/lib/solana/ruby/kit/rpc/transport.rb +3 -4
- 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 +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d26df33c1471059cde38ac46ec2604e0c26ffc8d64c580de4171396516530fd
|
|
4
|
+
data.tar.gz: dba1b05e818b10e8fa7692f6ae4af89c3946bcd19707be52cfdfd1c503e81b63
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56674f680f3059cd3247591fe2a14f303853b8806e251f4c147de90fb4ff7ebcea498394a2650aab40089d3cb5e52b10686cd3c5797e171c230a21ef05602eb3
|
|
7
|
+
data.tar.gz: b57dd91be86b034b1a207c809a04c38ca045700f608e4481ac125536d546f21f68e796f01930259a5054ea5ecf72f020f9260d4b63f6661dd177525c32ab924d
|
|
@@ -46,6 +46,10 @@ 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
|
|
@@ -88,13 +92,12 @@ module Solana::Ruby::Kit
|
|
|
88
92
|
RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER
|
|
89
93
|
RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER
|
|
90
94
|
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)
|
|
95
|
+
# JSON-RPC server errors (see https://github.com/anza-xyz/kit/blob/main/packages/errors/src/codes.ts)
|
|
95
96
|
JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE
|
|
96
97
|
JSON_RPC__SERVER_ERROR_SLOT_NOT_EPOCH_BOUNDARY = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_NOT_EPOCH_BOUNDARY
|
|
97
98
|
JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE
|
|
99
|
+
JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY
|
|
100
|
+
JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND = :SOLANA_ERROR__JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND
|
|
98
101
|
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST
|
|
99
102
|
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID
|
|
100
103
|
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED
|
|
@@ -172,6 +175,10 @@ module Solana::Ruby::Kit
|
|
|
172
175
|
|
|
173
176
|
# Transactions
|
|
174
177
|
TRANSACTIONS__TRANSACTION_NOT_SIGNABLE => 'Transaction is not signable (missing fee payer or lifetime constraint)',
|
|
178
|
+
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.',
|
|
179
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_COMPUTE_LIMIT => 'Transaction failed when it was simulated in order to estimate the compute unit consumption.',
|
|
180
|
+
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.',
|
|
181
|
+
TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS => 'Transaction failed when it was simulated in order to estimate its resource limits.',
|
|
175
182
|
TRANSACTIONS__EXCEEDS_SIZE_LIMIT => 'Transaction wire size (%{actual_size} bytes) exceeds the limit of %{limit} bytes',
|
|
176
183
|
TRANSACTIONS__MISSING_SIGNER => 'Transaction is missing a required signer: %{address}',
|
|
177
184
|
TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE => 'Transaction version %{version} is out of range',
|
|
@@ -217,6 +224,8 @@ module Solana::Ruby::Kit
|
|
|
217
224
|
JSON_RPC__SERVER_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE => 'Epoch rewards period still active at slot %{slot}',
|
|
218
225
|
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
226
|
JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_UNREACHABLE => 'Failed to query long-term storage; please try again',
|
|
227
|
+
JSON_RPC__SERVER_ERROR_NO_SLOT_HISTORY => 'No slot history',
|
|
228
|
+
JSON_RPC__SERVER_ERROR_FILTER_TRANSACTION_NOT_FOUND => 'Filter transaction not found',
|
|
220
229
|
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST => 'Cannot create subscription request',
|
|
221
230
|
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID => 'Expected server to return a subscription ID',
|
|
222
231
|
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED => 'WebSocket channel closed before message could be buffered',
|
|
@@ -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
|
|
@@ -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
|
|
@@ -117,10 +117,9 @@ module Solana::Ruby::Kit
|
|
|
117
117
|
http.open_timeout = @open_timeout
|
|
118
118
|
|
|
119
119
|
req = Net::HTTP::Post.new(T.cast(@uri, URI::HTTP).request_uri)
|
|
120
|
-
req['Content-Type']
|
|
121
|
-
req['Accept']
|
|
122
|
-
req['
|
|
123
|
-
req['solana-client'] = 'ruby-kit'
|
|
120
|
+
req['Content-Type'] = 'application/json; charset=utf-8'
|
|
121
|
+
req['Accept'] = 'application/json'
|
|
122
|
+
req['solana-client'] = 'ruby-kit'
|
|
124
123
|
@headers.each { |k, v| req[k] = v }
|
|
125
124
|
req.body = body
|
|
126
125
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../addresses/address'
|
|
5
|
+
require_relative '../instructions/instruction'
|
|
6
|
+
require_relative 'transaction_message'
|
|
7
|
+
|
|
8
|
+
module Solana::Ruby::Kit
|
|
9
|
+
module TransactionMessages
|
|
10
|
+
# Address of the Compute Budget program.
|
|
11
|
+
COMPUTE_BUDGET_PROGRAM_ADDRESS = T.let(
|
|
12
|
+
Addresses::Address.new('ComputeBudget111111111111111111111111111111'),
|
|
13
|
+
Addresses::Address
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Maximum compute unit limit per transaction (from Agave execution_budget.rs).
|
|
17
|
+
MAX_COMPUTE_UNIT_LIMIT = T.let(1_400_000, Integer)
|
|
18
|
+
|
|
19
|
+
# Maximum loaded accounts data size limit per transaction (64 MiB, from Agave).
|
|
20
|
+
MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT = T.let(64 * 1024 * 1024, Integer)
|
|
21
|
+
|
|
22
|
+
SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR = T.let(2, Integer)
|
|
23
|
+
SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR = T.let(4, Integer)
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# SetComputeUnitLimit instruction helpers
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
sig { params(units: Integer).returns(Instructions::Instruction) }
|
|
32
|
+
def get_set_compute_unit_limit_instruction(units)
|
|
33
|
+
data = ([SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR].pack('C') + [units].pack('V')).b
|
|
34
|
+
Instructions::Instruction.new(
|
|
35
|
+
program_address: COMPUTE_BUDGET_PROGRAM_ADDRESS,
|
|
36
|
+
accounts: nil,
|
|
37
|
+
data: data
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { params(instruction: Instructions::Instruction).returns(T::Boolean) }
|
|
42
|
+
def set_compute_unit_limit_instruction?(instruction)
|
|
43
|
+
instruction.program_address == COMPUTE_BUDGET_PROGRAM_ADDRESS &&
|
|
44
|
+
!instruction.data.nil? &&
|
|
45
|
+
T.must(instruction.data).bytesize == 5 &&
|
|
46
|
+
T.must(instruction.data).getbyte(0) == SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sig { params(data: String).returns(Integer) }
|
|
50
|
+
def compute_unit_limit_from_instruction_data(data)
|
|
51
|
+
T.must(data[1, 4]).unpack1('V')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the compute unit limit set on a transaction message, or nil if none is set.
|
|
55
|
+
# Mirrors `getTransactionMessageComputeUnitLimit` from @solana/transaction-messages.
|
|
56
|
+
sig { params(message: TransactionMessage).returns(T.nilable(Integer)) }
|
|
57
|
+
def get_transaction_message_compute_unit_limit(message)
|
|
58
|
+
ix = message.instructions.find { |i| set_compute_unit_limit_instruction?(i) }
|
|
59
|
+
ix ? compute_unit_limit_from_instruction_data(T.must(ix.data)) : nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Sets the compute unit limit on a transaction message, appending or replacing the
|
|
63
|
+
# SetComputeUnitLimit instruction. Mirrors `setTransactionMessageComputeUnitLimit`.
|
|
64
|
+
sig { params(limit: Integer, message: TransactionMessage).returns(TransactionMessage) }
|
|
65
|
+
def set_transaction_message_compute_unit_limit(limit, message)
|
|
66
|
+
existing_idx = message.instructions.index { |i| set_compute_unit_limit_instruction?(i) }
|
|
67
|
+
new_ix = get_set_compute_unit_limit_instruction(limit)
|
|
68
|
+
|
|
69
|
+
if existing_idx.nil?
|
|
70
|
+
append_instructions(message, [new_ix])
|
|
71
|
+
elsif compute_unit_limit_from_instruction_data(T.must(message.instructions[existing_idx].data)) == limit
|
|
72
|
+
message
|
|
73
|
+
else
|
|
74
|
+
new_instructions = message.instructions.dup
|
|
75
|
+
new_instructions[T.must(existing_idx)] = new_ix
|
|
76
|
+
TransactionMessage.new(
|
|
77
|
+
version: message.version,
|
|
78
|
+
instructions: new_instructions,
|
|
79
|
+
fee_payer: message.fee_payer,
|
|
80
|
+
lifetime_constraint: message.lifetime_constraint,
|
|
81
|
+
address_table_lookups: message.address_table_lookups
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# SetLoadedAccountsDataSizeLimit instruction helpers
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
sig { params(limit: Integer).returns(Instructions::Instruction) }
|
|
91
|
+
def get_set_loaded_accounts_data_size_limit_instruction(limit)
|
|
92
|
+
data = ([SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR].pack('C') + [limit].pack('V')).b
|
|
93
|
+
Instructions::Instruction.new(
|
|
94
|
+
program_address: COMPUTE_BUDGET_PROGRAM_ADDRESS,
|
|
95
|
+
accounts: nil,
|
|
96
|
+
data: data
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
sig { params(instruction: Instructions::Instruction).returns(T::Boolean) }
|
|
101
|
+
def set_loaded_accounts_data_size_limit_instruction?(instruction)
|
|
102
|
+
instruction.program_address == COMPUTE_BUDGET_PROGRAM_ADDRESS &&
|
|
103
|
+
!instruction.data.nil? &&
|
|
104
|
+
T.must(instruction.data).bytesize == 5 &&
|
|
105
|
+
T.must(instruction.data).getbyte(0) == SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
sig { params(data: String).returns(Integer) }
|
|
109
|
+
def loaded_accounts_data_size_limit_from_instruction_data(data)
|
|
110
|
+
T.must(data[1, 4]).unpack1('V')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Returns the loaded accounts data size limit set on a transaction message, or nil.
|
|
114
|
+
# Mirrors `getTransactionMessageLoadedAccountsDataSizeLimit`.
|
|
115
|
+
sig { params(message: TransactionMessage).returns(T.nilable(Integer)) }
|
|
116
|
+
def get_transaction_message_loaded_accounts_data_size_limit(message)
|
|
117
|
+
ix = message.instructions.find { |i| set_loaded_accounts_data_size_limit_instruction?(i) }
|
|
118
|
+
ix ? loaded_accounts_data_size_limit_from_instruction_data(T.must(ix.data)) : nil
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Sets the loaded accounts data size limit on a transaction message.
|
|
122
|
+
# Mirrors `setTransactionMessageLoadedAccountsDataSizeLimit`.
|
|
123
|
+
sig { params(limit: Integer, message: TransactionMessage).returns(TransactionMessage) }
|
|
124
|
+
def set_transaction_message_loaded_accounts_data_size_limit(limit, message)
|
|
125
|
+
existing_idx = message.instructions.index { |i| set_loaded_accounts_data_size_limit_instruction?(i) }
|
|
126
|
+
new_ix = get_set_loaded_accounts_data_size_limit_instruction(limit)
|
|
127
|
+
|
|
128
|
+
if existing_idx.nil?
|
|
129
|
+
append_instructions(message, [new_ix])
|
|
130
|
+
elsif loaded_accounts_data_size_limit_from_instruction_data(T.must(message.instructions[existing_idx].data)) == limit
|
|
131
|
+
message
|
|
132
|
+
else
|
|
133
|
+
new_instructions = message.instructions.dup
|
|
134
|
+
new_instructions[T.must(existing_idx)] = new_ix
|
|
135
|
+
TransactionMessage.new(
|
|
136
|
+
version: message.version,
|
|
137
|
+
instructions: new_instructions,
|
|
138
|
+
fee_payer: message.fee_payer,
|
|
139
|
+
lifetime_constraint: message.lifetime_constraint,
|
|
140
|
+
address_table_lookups: message.address_table_lookups
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
data/lib/solana/ruby/kit.rb
CHANGED
|
@@ -50,6 +50,7 @@ require_relative 'kit/programs'
|
|
|
50
50
|
require_relative 'kit/sysvars'
|
|
51
51
|
require_relative 'kit/transaction_confirmation'
|
|
52
52
|
require_relative 'kit/instruction_plans'
|
|
53
|
+
require_relative 'kit/resource_limit_estimation'
|
|
53
54
|
|
|
54
55
|
# Solana::Ruby::Kit is a Ruby translation of @anza-xyz/kit — the JavaScript SDK for
|
|
55
56
|
# building Solana apps — into idiomatic Ruby with Sorbet static types.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solana-ruby-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Zupan, Idhra Inc.
|
|
@@ -235,6 +235,7 @@ files:
|
|
|
235
235
|
- lib/solana/ruby/kit/programs/system_program.rb
|
|
236
236
|
- lib/solana/ruby/kit/promises.rb
|
|
237
237
|
- lib/solana/ruby/kit/railtie.rb
|
|
238
|
+
- lib/solana/ruby/kit/resource_limit_estimation.rb
|
|
238
239
|
- lib/solana/ruby/kit/rpc.rb
|
|
239
240
|
- lib/solana/ruby/kit/rpc/api/get_account_info.rb
|
|
240
241
|
- lib/solana/ruby/kit/rpc/api/get_balance.rb
|
|
@@ -248,6 +249,7 @@ files:
|
|
|
248
249
|
- lib/solana/ruby/kit/rpc/api/get_multiple_accounts.rb
|
|
249
250
|
- lib/solana/ruby/kit/rpc/api/get_program_accounts.rb
|
|
250
251
|
- lib/solana/ruby/kit/rpc/api/get_signature_statuses.rb
|
|
252
|
+
- lib/solana/ruby/kit/rpc/api/get_signatures_for_address.rb
|
|
251
253
|
- lib/solana/ruby/kit/rpc/api/get_slot.rb
|
|
252
254
|
- lib/solana/ruby/kit/rpc/api/get_token_account_balance.rb
|
|
253
255
|
- lib/solana/ruby/kit/rpc/api/get_token_accounts_by_owner.rb
|
|
@@ -297,6 +299,7 @@ files:
|
|
|
297
299
|
- lib/solana/ruby/kit/sysvars/rent.rb
|
|
298
300
|
- lib/solana/ruby/kit/transaction_confirmation.rb
|
|
299
301
|
- lib/solana/ruby/kit/transaction_messages.rb
|
|
302
|
+
- lib/solana/ruby/kit/transaction_messages/compute_budget.rb
|
|
300
303
|
- lib/solana/ruby/kit/transaction_messages/transaction_message.rb
|
|
301
304
|
- lib/solana/ruby/kit/transactions.rb
|
|
302
305
|
- lib/solana/ruby/kit/transactions/compiler.rb
|