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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f96f7c22b28fbf9bec4186e3bf92ec52483d532b7e398da89bf143473b267878
4
- data.tar.gz: 4a2e915aba2620726be4b5c89880c49fd7d54acf950618d4002ce19b54ac7884
3
+ metadata.gz: 1d26df33c1471059cde38ac46ec2604e0c26ffc8d64c580de4171396516530fd
4
+ data.tar.gz: dba1b05e818b10e8fa7692f6ae4af89c3946bcd19707be52cfdfd1c503e81b63
5
5
  SHA512:
6
- metadata.gz: 8a7fef4cd3632770e3e7d67f5b4722585f66f2ad45c601687d6664fc38433a85d39de7f4d582062e6396be7df29ea3cf089ca77184a7d8c28c3cf2b3d37015ae
7
- data.tar.gz: '0967fe5ea552384a656b22f5f03d08674e4066c373e0ee83cc31bd45598573aacade37f4295a0c24341eb0e31b7d543720be99cf347aba0ab626429f5c629085'
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 (-32017..-32019); context keys mirror TypeScript SolanaErrorContext:
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'] = 'application/json; charset=utf-8'
121
- req['Accept'] = 'application/json'
122
- req['Content-Length'] = body.bytesize.to_s
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
@@ -3,3 +3,4 @@
3
3
 
4
4
  # Transaction message construction utilities — mirrors @solana/transaction-messages.
5
5
  require_relative 'transaction_messages/transaction_message'
6
+ require_relative 'transaction_messages/compute_budget'
@@ -4,7 +4,7 @@
4
4
  module Solana
5
5
  module Ruby
6
6
  module Kit
7
- VERSION = '0.1.8'
7
+ VERSION = '0.1.9'
8
8
  end
9
9
  end
10
10
  end
@@ -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.8
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