solana-ruby-kit 7.0.0 → 7.0.0.1

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: 7b2766067cfcd434e5c657e1fde1dab4b44b23880e0f514f924d28a94ea0ca4a
4
- data.tar.gz: 32814612b342df38df079aaba08b8ea17c9128ac9f54279ab3abad622ba02492
3
+ metadata.gz: 950ad695ca0d9971314172207d974e747cf35c5dfec1210fb32882f0517306f8
4
+ data.tar.gz: ce0c4c171c78ca202a35bbacdd74630bb24afd3a438c7ebe7669b5cb56ee9a71
5
5
  SHA512:
6
- metadata.gz: 3bcfbd20e6f610a47053cb385511931004590646ff04e0edf4587e5699df3db8bb148af2b706cf025e8399f26e4736ae9db35ebfd04d68be287df8302bc9f4de
7
- data.tar.gz: d40b332b7cc427730b17b01a091c53815c596c0ac114bca93f52bed4d35775cd2b57ca5146893e2c94d46cebcbe0f51ccf98d2c6e2cad308cd51c2ff7b022d62
6
+ metadata.gz: 7d356ad614cfe6990a0df08121f6db68dcdf4e72b19979bd01a56233a1fccc699a16ee716c2e12d33b4de5c34792cc627fd0e273bfefce73a7144f70fff37ddf
7
+ data.tar.gz: b866ff957a09ba7dc4ab26708ea1311b2c8a283e992f972351c173187a47d905b6ba62483beefd0f5b2c9d46f9ecdd8574bba1f4491e96771abdc9c75417f7ee
@@ -139,6 +139,7 @@ module Solana::Ruby::Kit
139
139
 
140
140
  # ── Subscribable ──────────────────────────────────────────────────────────
141
141
  SUBSCRIBABLE__RETRY_NOT_SUPPORTED = :SOLANA_ERROR__SUBSCRIBABLE__RETRY_NOT_SUPPORTED
142
+ SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERROR = :SOLANA_ERROR__SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERROR
142
143
 
143
144
  # ── Fixed-points ──────────────────────────────────────────────────────────
144
145
  FIXED_POINTS__STRICT_MODE_PRECISION_LOSS = :SOLANA_ERROR__FIXED_POINTS__STRICT_MODE_PRECISION_LOSS
@@ -271,6 +272,7 @@ module Solana::Ruby::Kit
271
272
 
272
273
  # Subscribable
273
274
  SUBSCRIBABLE__RETRY_NOT_SUPPORTED => 'This reactive store does not support retry(); use create_reactive_store_from_data_publisher_factory for a retryable store',
275
+ SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERROR => 'The stream store closed in an error state but did not report an error.',
274
276
 
275
277
  # Fixed-points
276
278
  FIXED_POINTS__STRICT_MODE_PRECISION_LOSS => 'Value has more than 9 fractional digits and cannot be represented exactly as a Sol amount',
@@ -9,7 +9,9 @@ module Solana::Ruby::Kit
9
9
  InflationReward = T.let(
10
10
  Struct.new(
11
11
  :amount, # Integer (Lamports) — reward credited
12
- :commission, # Integer — vote account commission at reward time
12
+ :commission, # Integer, nilable — vote account commission at reward time;
13
+ # null once the vote account reports commission through
14
+ # getVoteAccounts' inflationRewardsCommissionBps instead
13
15
  :effective_slot, # Integer — slot in which rewards were delivered
14
16
  :epoch, # Integer — epoch for which reward occurred
15
17
  :post_balance, # Integer (Lamports) — post-reward account balance
@@ -46,7 +48,7 @@ module Solana::Ruby::Kit
46
48
 
47
49
  InflationReward.new(
48
50
  amount: Kernel.Integer(entry['amount']),
49
- commission: Kernel.Integer(entry['commission']),
51
+ commission: entry['commission'] ? Kernel.Integer(entry['commission']) : nil,
50
52
  effective_slot: Kernel.Integer(entry['effectiveSlot']),
51
53
  epoch: Kernel.Integer(entry['epoch']),
52
54
  post_balance: Kernel.Integer(entry['postBalance'])
@@ -0,0 +1,208 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Solana::Ruby::Kit
5
+ module Rpc
6
+ module Api
7
+ # One signature-level entry returned by getTransactionsForAddress when
8
+ # `transaction_details` is `:signatures`.
9
+ # Mirrors TypeScript's GetTransactionsForAddressSignature.
10
+ TransactionsForAddressSignature = T.let(
11
+ Struct.new(
12
+ :block_time, # Integer | nil — Unix timestamp of block production, nil if unavailable
13
+ :confirmation_status, # Symbol | nil — :processed, :confirmed, or :finalized
14
+ :err, # Hash | nil — transaction error, nil if succeeded
15
+ :memo, # String | nil — memo associated with the transaction
16
+ :signature, # String — base-58 encoded transaction signature
17
+ :slot, # Integer — slot containing the transaction
18
+ :transaction_index, # Integer — 0-based index within the block
19
+ keyword_init: true
20
+ ),
21
+ T.untyped
22
+ )
23
+
24
+ # The result envelope shared by every mode of getTransactionsForAddress.
25
+ #
26
+ # `data` holds the matching transactions in the requested sort order. Its
27
+ # element type depends on `transaction_details`: TransactionsForAddressSignature
28
+ # structs for `:signatures`, and raw (String-keyed) response hashes for
29
+ # `:full` — the same shape `Rpc::Api::GetTransaction#get_transaction`
30
+ # returns, so each element can be handed straight to
31
+ # `TransactionIntrospection.decode_transaction_from_rpc_response`.
32
+ #
33
+ # `pagination_token` is the cursor to pass back as `pagination_token` on a
34
+ # subsequent call, or nil when there are no more results.
35
+ #
36
+ # Mirrors TypeScript's GetTransactionsForAddressApiResponse.
37
+ TransactionsForAddressPage = T.let(
38
+ Struct.new(
39
+ :data, # Array<TransactionsForAddressSignature> | Array<Hash>
40
+ :pagination_token, # String | nil — cursor for the next page
41
+ keyword_init: true
42
+ ),
43
+ T.untyped
44
+ )
45
+
46
+ # Returns confirmed transactions that load the given address, with
47
+ # server-side filtering, sorting, and cursor-based pagination.
48
+ #
49
+ # This combines the discovery step of getSignaturesForAddress with
50
+ # filtering and pagination the server applies itself. Note: check your RPC
51
+ # provider for support for this method.
52
+ #
53
+ # Mirrors TypeScript's GetTransactionsForAddressApi.getTransactionsForAddress.
54
+ # See https://solana.com/docs/rpc/http/gettransactionsforaddress
55
+ module GetTransactionsForAddress
56
+ extend T::Sig
57
+
58
+ # Maps the snake_case keys of a Ruby +filters+ hash onto the wire names.
59
+ # Comparison operators (:eq, :gt, :gte, :lt, :lte) pass through as-is.
60
+ FILTER_KEYS = T.let(
61
+ {
62
+ block_time: 'blockTime',
63
+ signature: 'signature',
64
+ slot: 'slot',
65
+ status: 'status',
66
+ token_accounts: 'tokenAccounts'
67
+ }.freeze,
68
+ T::Hash[Symbol, String]
69
+ )
70
+
71
+ COMPARISON_KEYS = T.let(%i[eq gt gte lt lte].freeze, T::Array[Symbol])
72
+
73
+ # @param address [String] base-58 address to search for.
74
+ # @param transaction_details [Symbol] :signatures (default) or :full.
75
+ # @param encoding [String, nil] 'base58', 'base64', 'json' (server default),
76
+ # or 'jsonParsed'. Only valid with `transaction_details: :full`.
77
+ # @param max_supported_transaction_version [Integer, nil] newest transaction
78
+ # version to accept. Omit to receive only legacy transactions (in which
79
+ # case no `version` is returned). Only valid with `transaction_details: :full`.
80
+ # @param commitment [Symbol, nil] :confirmed or :finalized. This method does
81
+ # not support :processed.
82
+ # @param filters [Hash, nil] server-side filters, combined with AND. Supports
83
+ # `block_time:` ({eq/gt/gte/lt/lte}), `signature:` ({gt/gte/lt/lte}),
84
+ # `slot:` ({gt/gte/lt/lte}), `status:` (:any, :succeeded, :failed), and
85
+ # `token_accounts:` (:none, :balance_changed, :all).
86
+ # @param limit [Integer, nil] max results. The server caps this at 1000 for
87
+ # `:signatures` and 100 for `:full`.
88
+ # @param min_context_slot [Integer, nil] reject data older than this slot.
89
+ # @param pagination_token [String, nil] cursor from a previous response's
90
+ # `pagination_token`, formatted "<slot>:<position>".
91
+ # @param sort_order [Symbol, nil] :desc (newest first, server default) or :asc.
92
+ # @return [TransactionsForAddressPage]
93
+ sig do
94
+ params(
95
+ address: String,
96
+ transaction_details: Symbol,
97
+ encoding: T.nilable(String),
98
+ max_supported_transaction_version: T.nilable(Integer),
99
+ commitment: T.nilable(Symbol),
100
+ filters: T.nilable(T::Hash[Symbol, T.untyped]),
101
+ limit: T.nilable(Integer),
102
+ min_context_slot: T.nilable(Integer),
103
+ pagination_token: T.nilable(String),
104
+ sort_order: T.nilable(Symbol)
105
+ ).returns(T.untyped)
106
+ end
107
+ def get_transactions_for_address(
108
+ address,
109
+ transaction_details: :signatures,
110
+ encoding: nil,
111
+ max_supported_transaction_version: nil,
112
+ commitment: nil,
113
+ filters: nil,
114
+ limit: nil,
115
+ min_context_slot: nil,
116
+ pagination_token: nil,
117
+ sort_order: nil
118
+ )
119
+ unless %i[signatures full].include?(transaction_details)
120
+ Kernel.raise ArgumentError, "transaction_details must be :signatures or :full, got #{transaction_details.inspect}"
121
+ end
122
+
123
+ # The RPC rejects `processed` for this method; TypeScript encodes that
124
+ # as `Exclude<Commitment, 'processed'>`, so fail fast here too.
125
+ if commitment == :processed
126
+ Kernel.raise ArgumentError, 'getTransactionsForAddress does not support the :processed commitment'
127
+ end
128
+
129
+ if transaction_details == :signatures
130
+ if encoding
131
+ Kernel.raise ArgumentError, 'encoding is only valid with transaction_details: :full'
132
+ end
133
+ if max_supported_transaction_version
134
+ Kernel.raise ArgumentError, 'max_supported_transaction_version is only valid with transaction_details: :full'
135
+ end
136
+ end
137
+
138
+ config = { 'transactionDetails' => transaction_details.to_s }
139
+ config['encoding'] = encoding if encoding
140
+ config['maxSupportedTransactionVersion'] = max_supported_transaction_version if max_supported_transaction_version
141
+ config['commitment'] = commitment.to_s if commitment
142
+ config['limit'] = limit if limit
143
+ config['minContextSlot'] = min_context_slot if min_context_slot
144
+ config['paginationToken'] = pagination_token if pagination_token
145
+ config['sortOrder'] = sort_order.to_s if sort_order
146
+ config['filters'] = build_filters(filters) if filters && !filters.empty?
147
+
148
+ raw = transport.request('getTransactionsForAddress', [address, config])
149
+ rows = raw['data'] || []
150
+
151
+ data = if transaction_details == :full
152
+ # Returned verbatim so each element can be fed to
153
+ # TransactionIntrospection.decode_transaction_from_rpc_response.
154
+ rows
155
+ else
156
+ rows.map do |tx|
157
+ TransactionsForAddressSignature.new(
158
+ block_time: tx['blockTime'],
159
+ confirmation_status: tx['confirmationStatus']&.to_sym,
160
+ err: tx['err'],
161
+ memo: tx['memo'],
162
+ signature: tx['signature'],
163
+ slot: Kernel.Integer(tx['slot']),
164
+ transaction_index: tx['transactionIndex'] && Kernel.Integer(tx['transactionIndex'])
165
+ )
166
+ end
167
+ end
168
+
169
+ TransactionsForAddressPage.new(data: data, pagination_token: raw['paginationToken'])
170
+ end
171
+
172
+ # ── Private helpers ────────────────────────────────────────────────────
173
+
174
+ # Converts the snake_case +filters+ hash into its wire form. Symbol values
175
+ # (`status`, `token_accounts`) are lowerCamelCased so `:balance_changed`
176
+ # reaches the server as `"balanceChanged"`.
177
+ sig { params(filters: T::Hash[Symbol, T.untyped]).returns(T::Hash[String, T.untyped]) }
178
+ def build_filters(filters)
179
+ filters.each_with_object({}) do |(key, value), out|
180
+ wire_key = FILTER_KEYS[key]
181
+ Kernel.raise ArgumentError, "unknown filter #{key.inspect}" if wire_key.nil?
182
+
183
+ out[wire_key] = if value.is_a?(Hash)
184
+ value.each_with_object({}) do |(op, operand), cmp|
185
+ unless COMPARISON_KEYS.include?(op)
186
+ Kernel.raise ArgumentError, "unknown comparison #{op.inspect} in filter #{key.inspect}"
187
+ end
188
+ cmp[op.to_s] = operand
189
+ end
190
+ elsif value.is_a?(Symbol)
191
+ camelize(value)
192
+ else
193
+ value
194
+ end
195
+ end
196
+ end
197
+ private :build_filters
198
+
199
+ sig { params(value: Symbol).returns(String) }
200
+ def camelize(value)
201
+ head, *rest = value.to_s.split('_')
202
+ ([head] + rest.map(&:capitalize)).join
203
+ end
204
+ private :camelize
205
+ end
206
+ end
207
+ end
208
+ end
@@ -24,6 +24,7 @@ require_relative 'api/get_block_time'
24
24
  require_relative 'api/get_epoch_schedule'
25
25
  require_relative 'api/get_inflation_reward'
26
26
  require_relative 'api/get_signatures_for_address'
27
+ require_relative 'api/get_transactions_for_address'
27
28
 
28
29
  module Solana::Ruby::Kit
29
30
  module Rpc
@@ -64,6 +65,7 @@ module Solana::Ruby::Kit
64
65
  include Api::GetEpochSchedule
65
66
  include Api::GetInflationReward
66
67
  include Api::GetSignaturesForAddress
68
+ include Api::GetTransactionsForAddress
67
69
 
68
70
  sig { returns(Transport) }
69
71
  attr_reader :transport
@@ -0,0 +1,172 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../errors'
5
+ require_relative 'reactive_stream_store'
6
+
7
+ module Solana::Ruby::Kit
8
+ module Subscribable
9
+ extend T::Sig
10
+
11
+ module_function
12
+
13
+ # Adapts a ReactiveStreamStore into an Enumerator, so a *push*-based
14
+ # reactive store can be driven by *pull*-based code that consumes it with
15
+ # `#each`, `#next`, `#take`, or any other Enumerable method.
16
+ #
17
+ # The bridge only *observes* the store; it does not open or tear down the
18
+ # connection. The caller owns the store's lifecycle. The bridge subscribes,
19
+ # yields the store's current and subsequent values, and unsubscribes when
20
+ # iteration ends — however it ends (exhaustion, error, `break`, or abort).
21
+ # It does not reset the store; that is the caller's decision.
22
+ #
23
+ # This is the store-backed counterpart to AsyncIterable.from_publisher.
24
+ # That helper turns a raw DataPublisher into an Enumerator and queues every
25
+ # message so none are dropped. `bridge_store_to_async_iterable` instead sits
26
+ # on top of a ReactiveStreamStore, so it reflects the store's
27
+ # loading/loaded/error/retrying lifecycle — and, because a store only ever
28
+ # holds the *latest* snapshot, it is latest-wins rather than fully buffered.
29
+ #
30
+ # On iteration it seeds from the store's current snapshot, then yields its
31
+ # lifecycle:
32
+ # - 'loaded' → yields the value (the one already present when iteration
33
+ # begins, then each subsequent update), unless the optional
34
+ # +should_yield+ predicate rejects it. Latest-wins: if several
35
+ # notifications land between pulls, only the most recent
36
+ # unconsumed value is yielded.
37
+ # - 'error' → raises, so the consuming loop propagates it. Substitutes a
38
+ # SolanaError::SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERROR
39
+ # sentinel when the store reports an error with a nil payload.
40
+ # An error takes precedence over a buffered value: if a loaded
41
+ # value is still pending when an error arrives, that value is
42
+ # dropped and the error propagates.
43
+ # - 'loading' / 'retrying' → carry no new value and no error; nothing to yield.
44
+ #
45
+ # +signal+ follows the same convention as the rest of Subscribable: a lambda
46
+ # called to test for abort, which *raises* once aborted. When it raises,
47
+ # iteration ends cleanly (no error) — an abort is teardown, so the store's
48
+ # incidental abort-driven error state is not surfaced. Because a Ruby
49
+ # consumer parks in a blocking wait rather than on an event loop, the signal
50
+ # is re-tested every +poll_interval+ seconds while parked; it is checked
51
+ # immediately on every store change regardless. With no +signal+, the
52
+ # enumerator parks indefinitely between values — a subscription never
53
+ # completes on its own, so `break` out of the loop to stop.
54
+ #
55
+ # @param store [ReactiveStreamStore] a store to observe. Connect it yourself.
56
+ # @param signal [Proc, nil] raises once aborted; ends iteration cleanly.
57
+ # @param should_yield [Proc, nil] gate run against each loaded value before
58
+ # it is yielded. Return false to drop the value.
59
+ # @param poll_interval [Float] seconds between signal re-tests while parked.
60
+ # @return [Enumerator]
61
+ #
62
+ # @example
63
+ # store = Subscribable.create_reactive_store_from_data_publisher(
64
+ # publisher, data_channel: :slotNotification, error_channel: :error
65
+ # )
66
+ # Subscribable.bridge_store_to_async_iterable(store).each do |notification|
67
+ # puts "Latest slot: #{notification['slot']}"
68
+ # end
69
+ #
70
+ # Mirrors bridgeStoreToAsyncIterable from @solana/subscribable.
71
+ sig do
72
+ params(
73
+ store: ReactiveStreamStore,
74
+ signal: T.nilable(T.proc.void),
75
+ should_yield: T.nilable(T.proc.params(value: T.untyped).returns(T::Boolean)),
76
+ poll_interval: Float
77
+ ).returns(T::Enumerator[T.untyped])
78
+ end
79
+ def bridge_store_to_async_iterable(store, signal: nil, should_yield: nil, poll_interval: 0.05)
80
+ Enumerator.new do |yielder|
81
+ mutex = Mutex.new
82
+ cond = ConditionVariable.new
83
+ # Latest-wins single-slot buffer. Both are one-element arrays used as
84
+ # "present or nil" boxes so a legitimately-nil value is distinguishable
85
+ # from "nothing buffered".
86
+ latest = T.let(nil, T.nilable(T::Array[T.untyped]))
87
+ failure = T.let(nil, T.nilable(T::Array[T.untyped]))
88
+
89
+ on_change = Kernel.lambda do
90
+ state = store.get_unified_state
91
+ case state.status
92
+ when 'loaded'
93
+ # Drop a value the gate rejects; park again rather than yielding it.
94
+ next if should_yield && !should_yield.call(state.data)
95
+ mutex.synchronize do
96
+ latest = [state.data]
97
+ cond.broadcast
98
+ end
99
+ when 'error'
100
+ # A nil error would otherwise surface as a value-less success;
101
+ # substitute a sentinel so the failure propagates.
102
+ err = state.error || SolanaError.new(SolanaError::SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERROR)
103
+ mutex.synchronize do
104
+ failure = [err]
105
+ cond.broadcast
106
+ end
107
+ end
108
+ end
109
+
110
+ aborted = Kernel.lambda do
111
+ next false if signal.nil?
112
+
113
+ begin
114
+ signal.call
115
+ false
116
+ rescue StandardError
117
+ true
118
+ end
119
+ end
120
+
121
+ unsubscribe = store.subscribe(&on_change)
122
+ # Seed from the store's current snapshot: the caller may already have
123
+ # connected (and a value or error may already be present) before
124
+ # iteration began. The bridge never connects the store itself.
125
+ on_change.call
126
+
127
+ begin
128
+ Kernel.loop do
129
+ # Abort wins over everything: end cleanly without raising.
130
+ break if aborted.call
131
+
132
+ action = T.let(:park, Symbol)
133
+ payload = T.let(nil, T.untyped)
134
+
135
+ mutex.synchronize do
136
+ if failure
137
+ action = :raise
138
+ payload = failure[0]
139
+ elsif latest
140
+ action = :yield
141
+ payload = latest[0]
142
+ latest = nil
143
+ else
144
+ cond.wait(mutex, signal ? poll_interval : nil)
145
+ end
146
+ end
147
+
148
+ case action
149
+ when :yield then yielder.yield(payload)
150
+ when :raise then Kernel.raise(coerce_error(payload))
151
+ end
152
+ end
153
+ ensure
154
+ unsubscribe.call
155
+ end
156
+ end
157
+ end
158
+
159
+ # The store's error channel carries whatever the publisher emitted, which
160
+ # need not be an Exception. Ruby can only raise exceptions, so wrap anything
161
+ # else rather than losing the failure.
162
+ sig { params(error: T.untyped).returns(Exception) }
163
+ def coerce_error(error)
164
+ case error
165
+ when Exception then error
166
+ when String then RuntimeError.new(error)
167
+ else RuntimeError.new(error.inspect)
168
+ end
169
+ end
170
+ private_class_method :coerce_error
171
+ end
172
+ end
@@ -8,6 +8,7 @@ require_relative 'subscribable/data_publisher'
8
8
  require_relative 'subscribable/async_iterable'
9
9
  require_relative 'subscribable/reactive_stream_store'
10
10
  require_relative 'subscribable/reactive_action_store'
11
+ require_relative 'subscribable/bridge_store_to_async_iterable'
11
12
 
12
13
  module Solana::Ruby::Kit
13
14
  module Subscribable
@@ -14,7 +14,7 @@ module Solana::Ruby::Kit
14
14
  module TransactionIntrospection
15
15
  extend T::Sig
16
16
 
17
- # The result of decoding a `getTransaction` response: the
17
+ # The result of decoding a confirmed-transaction RPC response: the
18
18
  # CompiledTransactionMessage, the loaded ALT addresses pulled from `meta`
19
19
  # (if any), and — for `'base64'` and `'base58'` responses — the
20
20
  # re-encodable wire-format Transactions::Transaction.
@@ -33,14 +33,28 @@ module Solana::Ruby::Kit
33
33
 
34
34
  module_function
35
35
 
36
- # Decodes a `getTransaction` response (any of `encoding: 'base64'`,
36
+ # Decodes a confirmed-transaction RPC response (any of `encoding: 'base64'`,
37
37
  # `'base58'`, or `'json'`) into a CompiledTransactionMessage plus, for
38
38
  # `'base64'` and `'base58'`, a re-encodable Transactions::Transaction.
39
39
  # The JSON path does not produce a Transaction: the server has already
40
40
  # decompiled the wire format, so there are no message bytes to carry.
41
41
  #
42
- # +rpc_tx+ is the raw (String-keyed) JSON hash returned by
43
- # `Rpc::Api::GetTransaction#get_transaction`.
42
+ # +rpc_tx+ is a raw (String-keyed) JSON hash. Only the `transaction` /
43
+ # `meta` / `version` envelope is read — not a method-specific response
44
+ # shape — so results from any method that returns confirmed transactions in
45
+ # these encodings work, and extra fields (`slot`, `blockTime`,
46
+ # `transactionIndex`, …) are ignored:
47
+ #
48
+ # # getTransaction — a single response hash
49
+ # rpc_tx = rpc.get_transaction(signature, encoding: 'base64')
50
+ # TransactionIntrospection.decode_transaction_from_rpc_response(rpc_tx)
51
+ #
52
+ # # getTransactionsForAddress — one hash per element of `data`
53
+ # page = rpc.get_transactions_for_address(
54
+ # address, transaction_details: :full, encoding: 'base64',
55
+ # max_supported_transaction_version: 0
56
+ # )
57
+ # page.data.map { |tx| TransactionIntrospection.decode_transaction_from_rpc_response(tx) }
44
58
  #
45
59
  # `'jsonParsed'` is not supported — its instructions arrive pre-parsed by
46
60
  # the server and lack raw bytes, so they cannot be round-tripped. Passing
@@ -135,7 +149,10 @@ module Solana::Ruby::Kit
135
149
 
136
150
  # The envelope only carries `version` when `maxSupportedTransactionVersion`
137
151
  # was set on the request; otherwise the response is necessarily legacy.
138
- version = rpc_tx.key?('version') ? rpc_tx['version'] : :legacy
152
+ # A `version` key that is present but null counts as absent — some methods
153
+ # include the key unconditionally. Ruby's `||` is safe here where
154
+ # TypeScript needs `??`: 0 (v0) is truthy in Ruby, so a real v0 survives.
155
+ version = rpc_tx['version'] || :legacy
139
156
  version = :legacy if version == 'legacy'
140
157
 
141
158
  compiled_message = CompiledTransactionMessage.new(
@@ -12,7 +12,7 @@ module Solana::Ruby::Kit
12
12
 
13
13
  module_function
14
14
 
15
- # Returns the inner instructions in a `getTransaction` response as
15
+ # Returns the inner instructions in a transaction `meta` as
16
16
  # TracedInstructions.
17
17
  #
18
18
  # The RPC returns inner instructions in a different shape from the wire
@@ -21,8 +21,10 @@ module Solana::Ruby::Kit
21
21
  # data, resolves the indices against the supplied AccountMeta list, and
22
22
  # tags each instruction with an `inner` trace.
23
23
  #
24
- # +meta+ is the raw (String-keyed) `meta` hash from a `getTransaction`
25
- # response. Returns an empty array if it carries no `innerInstructions`.
24
+ # +meta+ is the raw (String-keyed) `meta` hash from any method that returns
25
+ # transaction metadata without `jsonParsed` encoding `getTransaction`,
26
+ # `getTransactionsForAddress`, or `getBlock`. Returns an empty array if it
27
+ # carries no `innerInstructions`.
26
28
  #
27
29
  # Raises if any `programIdIndex` or account index falls outside the
28
30
  # supplied `account_metas` list.
@@ -4,7 +4,7 @@
4
4
  module Solana
5
5
  module Ruby
6
6
  module Kit
7
- VERSION = '7.0.0'
7
+ VERSION = '7.0.0.1'
8
8
  end
9
9
  end
10
10
  end
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: 7.0.0
4
+ version: 7.0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Zupan, Idhra Inc.
@@ -255,6 +255,7 @@ files:
255
255
  - lib/solana/ruby/kit/rpc/api/get_token_account_balance.rb
256
256
  - lib/solana/ruby/kit/rpc/api/get_token_accounts_by_owner.rb
257
257
  - lib/solana/ruby/kit/rpc/api/get_transaction.rb
258
+ - lib/solana/ruby/kit/rpc/api/get_transactions_for_address.rb
258
259
  - lib/solana/ruby/kit/rpc/api/get_vote_accounts.rb
259
260
  - lib/solana/ruby/kit/rpc/api/is_blockhash_valid.rb
260
261
  - lib/solana/ruby/kit/rpc/api/request_airdrop.rb
@@ -289,6 +290,7 @@ files:
289
290
  - lib/solana/ruby/kit/signers/keypair_signer.rb
290
291
  - lib/solana/ruby/kit/subscribable.rb
291
292
  - lib/solana/ruby/kit/subscribable/async_iterable.rb
293
+ - lib/solana/ruby/kit/subscribable/bridge_store_to_async_iterable.rb
292
294
  - lib/solana/ruby/kit/subscribable/data_publisher.rb
293
295
  - lib/solana/ruby/kit/subscribable/reactive_action_store.rb
294
296
  - lib/solana/ruby/kit/subscribable/reactive_stream_store.rb