solana-ruby-kit 8.5.0 → 8.5.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 +4 -4
- data/README.md +46 -0
- data/lib/solana/ruby/kit/rpc/client.rb +21 -3
- data/lib/solana/ruby/kit/rpc/retry_policy.rb +149 -0
- data/lib/solana/ruby/kit/rpc/transport.rb +55 -6
- data/lib/solana/ruby/kit/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 727cac096a118ea677195240a8ea3440f593b1f7930b0f0f603ae8156b66862a
|
|
4
|
+
data.tar.gz: e8a03418fa257b58b243933a9fe6c5918c7882f1bdfe47ad49f87bbd63a95a28
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7fdcd2a35bde1be1eba49bf48f373d5bf701cd399c23f54f1bc53c4a7fdf8a21c4b3f478079be2a57095bd91aff7eb736a31fa047d357231bbcb3410bbaa6ad6
|
|
7
|
+
data.tar.gz: 890775283c250e98d91ab31263afd4b5c6f3fd093ae1c96422623d9c63716deb69a5376537b11811fd9deeb0961445d319f69086a985e41ee5d800d7f7bdb008
|
data/README.md
CHANGED
|
@@ -299,8 +299,54 @@ rescue Solana::Ruby::Kit::Rpc::RpcError => e
|
|
|
299
299
|
puts e.message # JSON-RPC error message
|
|
300
300
|
rescue Solana::Ruby::Kit::Rpc::HttpTransportError => e
|
|
301
301
|
puts e.status_code # HTTP status code
|
|
302
|
+
puts e.retry_after # raw Retry-After header, or nil
|
|
302
303
|
```
|
|
303
304
|
|
|
305
|
+
#### Retries
|
|
306
|
+
|
|
307
|
+
Transient failures are retried automatically, for every RPC method, with
|
|
308
|
+
full-jitter exponential backoff. Defaults: `max_attempts: 5`, `base_delay: 0.25`,
|
|
309
|
+
`max_delay: 8.0` (seconds). The wait before retry *n* is
|
|
310
|
+
`rand * min(base_delay * 2**(n - 1), max_delay)`; a `Retry-After` header
|
|
311
|
+
overrides it (still capped at `max_delay`).
|
|
312
|
+
|
|
313
|
+
What gets retried depends on whether the request could have reached the node:
|
|
314
|
+
|
|
315
|
+
| Failure | Retried for |
|
|
316
|
+
|---|---|
|
|
317
|
+
| Connect-phase: `Errno::ENETUNREACH`, `EHOSTUNREACH`, `ECONNREFUSED`, `Net::OpenTimeout`, `SocketError` | every method — the bytes never left the machine |
|
|
318
|
+
| In-flight: `Errno::ECONNRESET`, `EPIPE`, `EOFError`, `Net::ReadTimeout`, `Net::WriteTimeout`, `OpenSSL::SSL::SSLError` | idempotent methods only |
|
|
319
|
+
| HTTP 429 / 502 / 503 / 504 | idempotent methods only |
|
|
320
|
+
| JSON-RPC error in a 200 response (`RpcError`) | never — the node answered |
|
|
321
|
+
|
|
322
|
+
Every attempt resends the same bytes with the same JSON-RPC `id`; nothing is
|
|
323
|
+
re-signed and no blockhash is refetched. Resending an identical signed
|
|
324
|
+
`sendTransaction` is safe because the cluster dedupes by signature. When
|
|
325
|
+
retries run out, the original error is re-raised.
|
|
326
|
+
|
|
327
|
+
**`requestAirdrop` is not idempotent**: each accepted request is a new airdrop,
|
|
328
|
+
so it is retried only on connect-phase errors. Add any of your own
|
|
329
|
+
non-idempotent methods to `non_idempotent:`.
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
# Customize, with logging
|
|
333
|
+
policy = Solana::Ruby::Kit::Rpc::RetryPolicy.new(
|
|
334
|
+
max_attempts: 8,
|
|
335
|
+
max_delay: 15.0,
|
|
336
|
+
on_retry: ->(method, attempt, error, wait) {
|
|
337
|
+
logger.warn("#{method} attempt #{attempt} failed (#{error.class}: #{error.message}); retrying in #{wait.round(2)}s")
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
rpc = Solana::Ruby::Kit::Rpc::Client.new(Solana::Ruby::Kit::RpcTypes.devnet, retry_policy: policy)
|
|
341
|
+
|
|
342
|
+
# Disable
|
|
343
|
+
rpc = Solana::Ruby::Kit::Rpc::Client.new(url, retry_policy: Solana::Ruby::Kit::Rpc::RetryPolicy.none)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
`Rpc::Transport.new` takes the same `retry_policy:`, plus `sleeper:` (a
|
|
347
|
+
callable receiving the wait in seconds, `Kernel.method(:sleep)` by default) so
|
|
348
|
+
tests need not actually wait. The WebSocket subscriptions client is not covered.
|
|
349
|
+
|
|
304
350
|
### `Solana::Ruby::Kit::RpcTypes` — `@solana/rpc-types`
|
|
305
351
|
|
|
306
352
|
Cluster URL helpers and typed wrappers.
|
|
@@ -80,18 +80,36 @@ module Solana::Ruby::Kit
|
|
|
80
80
|
# @param cluster_url [RpcTypes::ClusterUrl, String] endpoint to connect to.
|
|
81
81
|
# @param headers [Hash<String, String>] additional HTTP headers.
|
|
82
82
|
# @param timeout [Integer] read timeout in seconds.
|
|
83
|
+
# @param retry_policy [RetryPolicy] transient-failure retries; `RetryPolicy.none` disables.
|
|
84
|
+
# @param sleeper [#call] called with the wait in seconds between retries.
|
|
83
85
|
sig do
|
|
84
86
|
params(
|
|
85
87
|
cluster_url: T.untyped, # RpcTypes::ClusterUrl or String
|
|
86
88
|
headers: T::Hash[String, String],
|
|
87
89
|
timeout: Integer,
|
|
88
|
-
open_timeout: Integer
|
|
90
|
+
open_timeout: Integer,
|
|
91
|
+
retry_policy: RetryPolicy,
|
|
92
|
+
sleeper: T.any(Proc, Method)
|
|
89
93
|
).void
|
|
90
94
|
end
|
|
91
|
-
def initialize(
|
|
95
|
+
def initialize(
|
|
96
|
+
cluster_url,
|
|
97
|
+
headers: {},
|
|
98
|
+
timeout: Transport::DEFAULT_TIMEOUT,
|
|
99
|
+
open_timeout: Transport::DEFAULT_OPEN_TIMEOUT,
|
|
100
|
+
retry_policy: RetryPolicy.new,
|
|
101
|
+
sleeper: Kernel.method(:sleep)
|
|
102
|
+
)
|
|
92
103
|
url = cluster_url.respond_to?(:url) ? cluster_url.url : cluster_url.to_s
|
|
93
104
|
@transport = T.let(
|
|
94
|
-
Transport.new(
|
|
105
|
+
Transport.new(
|
|
106
|
+
url: url,
|
|
107
|
+
headers: headers,
|
|
108
|
+
timeout: timeout,
|
|
109
|
+
open_timeout: open_timeout,
|
|
110
|
+
retry_policy: retry_policy,
|
|
111
|
+
sleeper: sleeper
|
|
112
|
+
),
|
|
95
113
|
Transport
|
|
96
114
|
)
|
|
97
115
|
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
module Solana::Ruby::Kit
|
|
10
|
+
module Rpc
|
|
11
|
+
# Decides whether a failed JSON-RPC POST is retried, and how long to wait first.
|
|
12
|
+
# Ruby-only: upstream's `createHttpTransport` has no retry.
|
|
13
|
+
#
|
|
14
|
+
# Errors are classified by whether the request could have reached the node:
|
|
15
|
+
#
|
|
16
|
+
# - Connect-phase errors (`CONNECT_ERRORS`) are retried for every method — the
|
|
17
|
+
# request bytes never left the machine, so nothing can have been executed twice.
|
|
18
|
+
# - In-flight errors (`IN_FLIGHT_ERRORS`) and HTTP 429/502/503/504 are retried
|
|
19
|
+
# only for methods not listed in `non_idempotent`: the node may already have
|
|
20
|
+
# acted on the request.
|
|
21
|
+
# - JSON-RPC error objects (`RpcError`) are never retried — the node answered.
|
|
22
|
+
#
|
|
23
|
+
# Backoff is full-jitter exponential: before retry n (1-based) the transport
|
|
24
|
+
# waits `rand * min(base_delay * 2**(n - 1), max_delay)`. A `Retry-After` header
|
|
25
|
+
# overrides it, capped at `max_delay`.
|
|
26
|
+
class RetryPolicy
|
|
27
|
+
extend T::Sig
|
|
28
|
+
|
|
29
|
+
CONNECT_ERRORS = T.let(
|
|
30
|
+
[Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::OpenTimeout, SocketError].freeze,
|
|
31
|
+
T::Array[T.class_of(Exception)]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
IN_FLIGHT_ERRORS = T.let(
|
|
35
|
+
[Errno::ECONNRESET, Errno::EPIPE, EOFError, Net::ReadTimeout, Net::WriteTimeout, OpenSSL::SSL::SSLError].freeze,
|
|
36
|
+
T::Array[T.class_of(Exception)]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
RETRYABLE_HTTP_STATUSES = T.let([429, 502, 503, 504].freeze, T::Array[Integer])
|
|
40
|
+
|
|
41
|
+
DEFAULT_NON_IDEMPOTENT = T.let(['requestAirdrop'].freeze, T::Array[String])
|
|
42
|
+
|
|
43
|
+
sig { returns(Integer) }
|
|
44
|
+
attr_reader :max_attempts
|
|
45
|
+
|
|
46
|
+
sig { returns(Float) }
|
|
47
|
+
attr_reader :base_delay
|
|
48
|
+
|
|
49
|
+
sig { returns(Float) }
|
|
50
|
+
attr_reader :max_delay
|
|
51
|
+
|
|
52
|
+
sig { returns(T::Array[String]) }
|
|
53
|
+
attr_reader :non_idempotent
|
|
54
|
+
|
|
55
|
+
# Called before each retry's sleep as `on_retry.call(method, attempt, error, wait)`,
|
|
56
|
+
# where `attempt` is the 1-based number of the attempt that just failed.
|
|
57
|
+
sig { returns(T.nilable(T.any(Proc, Method))) }
|
|
58
|
+
attr_reader :on_retry
|
|
59
|
+
|
|
60
|
+
# A policy that never retries.
|
|
61
|
+
sig { returns(RetryPolicy) }
|
|
62
|
+
def self.none
|
|
63
|
+
new(max_attempts: 1)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
sig do
|
|
67
|
+
params(
|
|
68
|
+
max_attempts: Integer,
|
|
69
|
+
base_delay: Numeric,
|
|
70
|
+
max_delay: Numeric,
|
|
71
|
+
non_idempotent: T::Array[String],
|
|
72
|
+
on_retry: T.nilable(T.any(Proc, Method)),
|
|
73
|
+
random: Random
|
|
74
|
+
).void
|
|
75
|
+
end
|
|
76
|
+
def initialize(
|
|
77
|
+
max_attempts: 5,
|
|
78
|
+
base_delay: 0.25,
|
|
79
|
+
max_delay: 8.0,
|
|
80
|
+
non_idempotent: DEFAULT_NON_IDEMPOTENT,
|
|
81
|
+
on_retry: nil,
|
|
82
|
+
random: Random.new
|
|
83
|
+
)
|
|
84
|
+
raise ArgumentError, 'max_attempts must be at least 1' if max_attempts < 1
|
|
85
|
+
|
|
86
|
+
@max_attempts = T.let(max_attempts, Integer)
|
|
87
|
+
@base_delay = T.let(base_delay.to_f, Float)
|
|
88
|
+
@max_delay = T.let(max_delay.to_f, Float)
|
|
89
|
+
@non_idempotent = T.let(non_idempotent.dup.freeze, T::Array[String])
|
|
90
|
+
@on_retry = T.let(on_retry, T.nilable(T.any(Proc, Method)))
|
|
91
|
+
@random = T.let(random, Random)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Whether attempt number `attempt` (1-based) of `method`, having failed with
|
|
95
|
+
# `error`, should be followed by another.
|
|
96
|
+
sig { params(method: String, error: Exception, attempt: Integer).returns(T::Boolean) }
|
|
97
|
+
def retry?(method, error, attempt)
|
|
98
|
+
attempt < @max_attempts && retryable?(method, error)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Whether `error` is retryable for `method` at all, ignoring the attempt budget.
|
|
102
|
+
sig { params(method: String, error: Exception).returns(T::Boolean) }
|
|
103
|
+
def retryable?(method, error)
|
|
104
|
+
return true if connect_error?(error)
|
|
105
|
+
return false unless idempotent?(method)
|
|
106
|
+
|
|
107
|
+
case error
|
|
108
|
+
when HttpTransportError then RETRYABLE_HTTP_STATUSES.include?(error.status_code)
|
|
109
|
+
else IN_FLIGHT_ERRORS.any? { |klass| error.is_a?(klass) }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
sig { params(method: String).returns(T::Boolean) }
|
|
114
|
+
def idempotent?(method)
|
|
115
|
+
!@non_idempotent.include?(method)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Seconds to wait after attempt number `attempt` (1-based) failed.
|
|
119
|
+
# `retry_after` is the raw `Retry-After` header value, if any: delta-seconds
|
|
120
|
+
# or an HTTP-date. An unparseable value falls back to the backoff.
|
|
121
|
+
sig { params(attempt: Integer, retry_after: T.nilable(String)).returns(Float) }
|
|
122
|
+
def delay(attempt, retry_after: nil)
|
|
123
|
+
server_wait = parse_retry_after(retry_after)
|
|
124
|
+
return [server_wait, @max_delay].min if server_wait
|
|
125
|
+
|
|
126
|
+
@random.rand(1.0) * [@base_delay * (2.0**(attempt - 1)), @max_delay].min
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
sig { params(error: Exception).returns(T::Boolean) }
|
|
132
|
+
def connect_error?(error)
|
|
133
|
+
CONNECT_ERRORS.any? { |klass| error.is_a?(klass) }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
sig { params(value: T.nilable(String)).returns(T.nilable(Float)) }
|
|
137
|
+
def parse_retry_after(value)
|
|
138
|
+
return nil if value.nil?
|
|
139
|
+
|
|
140
|
+
stripped = value.strip
|
|
141
|
+
return [stripped.to_f, 0.0].max if stripped.match?(/\A\d+(\.\d+)?\z/)
|
|
142
|
+
|
|
143
|
+
[Time.httpdate(stripped) - Time.now, 0.0].max
|
|
144
|
+
rescue ArgumentError
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -5,6 +5,7 @@ require 'net/http'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'uri'
|
|
7
7
|
require_relative '../errors'
|
|
8
|
+
require_relative 'retry_policy'
|
|
8
9
|
|
|
9
10
|
module Solana::Ruby::Kit
|
|
10
11
|
module Rpc
|
|
@@ -33,9 +34,14 @@ module Solana::Ruby::Kit
|
|
|
33
34
|
sig { returns(Integer) }
|
|
34
35
|
attr_reader :status_code
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
# Raw `Retry-After` header value, when the response carried one.
|
|
38
|
+
sig { returns(T.nilable(String)) }
|
|
39
|
+
attr_reader :retry_after
|
|
40
|
+
|
|
41
|
+
sig { params(status_code: Integer, message: String, retry_after: T.nilable(String)).void }
|
|
42
|
+
def initialize(status_code, message, retry_after: nil)
|
|
38
43
|
@status_code = T.let(status_code, Integer)
|
|
44
|
+
@retry_after = T.let(retry_after, T.nilable(String))
|
|
39
45
|
super("HTTP #{status_code}: #{message}")
|
|
40
46
|
end
|
|
41
47
|
end
|
|
@@ -45,6 +51,14 @@ module Solana::Ruby::Kit
|
|
|
45
51
|
#
|
|
46
52
|
# Makes synchronous POST requests — TypeScript's async `fetch` maps to
|
|
47
53
|
# Ruby's blocking `Net::HTTP`. Use threads or Fibers for concurrency.
|
|
54
|
+
#
|
|
55
|
+
# Transient failures are retried according to `retry_policy` (see `RetryPolicy`;
|
|
56
|
+
# on by default, `RetryPolicy.none` disables it). The payload is built once, so
|
|
57
|
+
# every attempt sends the same bytes with the same JSON-RPC `id` — the transport
|
|
58
|
+
# never re-signs a transaction or refetches a blockhash.
|
|
59
|
+
#
|
|
60
|
+
# Each attempt opens its own connection (no keep-alive), so a Transport can be
|
|
61
|
+
# shared across threads and a retry never reuses a dead socket.
|
|
48
62
|
class Transport
|
|
49
63
|
extend T::Sig
|
|
50
64
|
|
|
@@ -61,7 +75,9 @@ module Solana::Ruby::Kit
|
|
|
61
75
|
timeout: Integer,
|
|
62
76
|
open_timeout: Integer,
|
|
63
77
|
to_json: T.nilable(T.proc.params(arg0: T.untyped).returns(String)),
|
|
64
|
-
from_json: T.nilable(T.proc.params(arg0: String, arg1: T.untyped).returns(T.untyped))
|
|
78
|
+
from_json: T.nilable(T.proc.params(arg0: String, arg1: T.untyped).returns(T.untyped)),
|
|
79
|
+
retry_policy: RetryPolicy,
|
|
80
|
+
sleeper: T.any(Proc, Method)
|
|
65
81
|
).void
|
|
66
82
|
end
|
|
67
83
|
def initialize(
|
|
@@ -70,7 +86,9 @@ module Solana::Ruby::Kit
|
|
|
70
86
|
timeout: DEFAULT_TIMEOUT,
|
|
71
87
|
open_timeout: DEFAULT_OPEN_TIMEOUT,
|
|
72
88
|
to_json: nil,
|
|
73
|
-
from_json: nil
|
|
89
|
+
from_json: nil,
|
|
90
|
+
retry_policy: RetryPolicy.new,
|
|
91
|
+
sleeper: Kernel.method(:sleep)
|
|
74
92
|
)
|
|
75
93
|
@url = T.let(url, String)
|
|
76
94
|
@headers = T.let(headers, T::Hash[String, String])
|
|
@@ -80,8 +98,13 @@ module Solana::Ruby::Kit
|
|
|
80
98
|
@from_json = T.let(from_json, T.nilable(T.proc.params(arg0: String, arg1: T.untyped).returns(T.untyped)))
|
|
81
99
|
@request_id = T.let(0, Integer)
|
|
82
100
|
@uri = T.let(URI.parse(url), URI::Generic)
|
|
101
|
+
@retry_policy = T.let(retry_policy, RetryPolicy)
|
|
102
|
+
@sleeper = T.let(sleeper, T.any(Proc, Method))
|
|
83
103
|
end
|
|
84
104
|
|
|
105
|
+
sig { returns(RetryPolicy) }
|
|
106
|
+
attr_reader :retry_policy
|
|
107
|
+
|
|
85
108
|
# Sends a single JSON-RPC request and returns the parsed `result`.
|
|
86
109
|
# Raises `RpcError` on a JSON-RPC error, `HttpTransportError` on HTTP failure.
|
|
87
110
|
sig { params(method: String, params: T::Array[T.untyped]).returns(T.untyped) }
|
|
@@ -89,7 +112,7 @@ module Solana::Ruby::Kit
|
|
|
89
112
|
payload = build_payload(method, params)
|
|
90
113
|
body = @to_json ? @to_json.call(payload) : JSON.generate(payload)
|
|
91
114
|
|
|
92
|
-
response =
|
|
115
|
+
response = post_with_retry(method, body)
|
|
93
116
|
|
|
94
117
|
parsed = @from_json ? @from_json.call(T.must(response.body), payload) : JSON.parse(T.must(response.body))
|
|
95
118
|
|
|
@@ -109,6 +132,32 @@ module Solana::Ruby::Kit
|
|
|
109
132
|
{ 'jsonrpc' => '2.0', 'id' => @request_id, 'method' => method, 'params' => params }
|
|
110
133
|
end
|
|
111
134
|
|
|
135
|
+
# Posts `body` until it succeeds, the error is not retryable for `method`, or
|
|
136
|
+
# the policy's attempts run out — then re-raises the last error unchanged.
|
|
137
|
+
sig { params(method: String, body: String).returns(Net::HTTPResponse) }
|
|
138
|
+
def post_with_retry(method, body)
|
|
139
|
+
attempt = 0
|
|
140
|
+
begin
|
|
141
|
+
attempt += 1
|
|
142
|
+
post(body)
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
reset_connection unless e.is_a?(HttpTransportError)
|
|
145
|
+
raise unless @retry_policy.retry?(method, e, attempt)
|
|
146
|
+
|
|
147
|
+
retry_after = e.is_a?(HttpTransportError) ? e.retry_after : nil
|
|
148
|
+
wait = @retry_policy.delay(attempt, retry_after: retry_after)
|
|
149
|
+
@retry_policy.on_retry&.call(method, attempt, e, wait)
|
|
150
|
+
@sleeper.call(wait)
|
|
151
|
+
retry
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Called after every socket-level error, before any retry. Nothing to do
|
|
156
|
+
# while each attempt opens a fresh `Net::HTTP`; a keep-alive connection
|
|
157
|
+
# would be closed here so the retry could not reuse a dead socket.
|
|
158
|
+
sig { void }
|
|
159
|
+
def reset_connection; end
|
|
160
|
+
|
|
112
161
|
sig { params(body: String).returns(Net::HTTPResponse) }
|
|
113
162
|
def post(body)
|
|
114
163
|
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
@@ -126,7 +175,7 @@ module Solana::Ruby::Kit
|
|
|
126
175
|
response = http.request(req)
|
|
127
176
|
|
|
128
177
|
unless response.is_a?(Net::HTTPSuccess)
|
|
129
|
-
raise HttpTransportError.new(response.code.to_i, response.message)
|
|
178
|
+
raise HttpTransportError.new(response.code.to_i, response.message, retry_after: response['Retry-After'])
|
|
130
179
|
end
|
|
131
180
|
|
|
132
181
|
response
|
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: 8.5.
|
|
4
|
+
version: 8.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Zupan, Idhra Inc.
|
|
@@ -289,6 +289,7 @@ files:
|
|
|
289
289
|
- lib/solana/ruby/kit/rpc/api/send_transaction.rb
|
|
290
290
|
- lib/solana/ruby/kit/rpc/api/simulate_transaction.rb
|
|
291
291
|
- lib/solana/ruby/kit/rpc/client.rb
|
|
292
|
+
- lib/solana/ruby/kit/rpc/retry_policy.rb
|
|
292
293
|
- lib/solana/ruby/kit/rpc/transport.rb
|
|
293
294
|
- lib/solana/ruby/kit/rpc_parsed_types.rb
|
|
294
295
|
- lib/solana/ruby/kit/rpc_parsed_types/address_lookup_table.rb
|