erpc-sdk 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSES/Apache-2.0.txt +201 -0
- data/LICENSES/BSD-3-Clause-Google-Protobuf.txt +29 -0
- data/README.md +50 -7
- data/THIRD_PARTY_NOTICES.md +41 -0
- data/lib/erpc_sdk/arbitrum.rb +62 -0
- data/lib/erpc_sdk/arc.rb +33 -0
- data/lib/erpc_sdk/base.rb +333 -0
- data/lib/erpc_sdk/bitcoin.rb +126 -0
- data/lib/erpc_sdk/bitcoin_codec.rb +574 -0
- data/lib/erpc_sdk/bitcoin_transport.rb +369 -0
- data/lib/erpc_sdk/bsc.rb +33 -0
- data/lib/erpc_sdk/client.rb +109 -1
- data/lib/erpc_sdk/config.rb +168 -5
- data/lib/erpc_sdk/errors.rb +31 -1
- data/lib/erpc_sdk/generated/rpc_network_contract.rb +3196 -0
- data/lib/erpc_sdk/hyperliquid.rb +33 -0
- data/lib/erpc_sdk/kaia.rb +33 -0
- data/lib/erpc_sdk/monad.rb +33 -0
- data/lib/erpc_sdk/near.rb +276 -0
- data/lib/erpc_sdk/near_transport.rb +345 -0
- data/lib/erpc_sdk/optimism.rb +33 -0
- data/lib/erpc_sdk/polygon.rb +33 -0
- data/lib/erpc_sdk/robinhood.rb +33 -0
- data/lib/erpc_sdk/soneium.rb +33 -0
- data/lib/erpc_sdk/sui.rb +5 -0
- data/lib/erpc_sdk/sui_grpc_web.rb +226 -0
- data/lib/erpc_sdk/sui_proto.rb +370 -0
- data/lib/erpc_sdk/transport.rb +28 -0
- data/lib/erpc_sdk/tron.rb +33 -0
- data/lib/erpc_sdk/version.rb +1 -1
- data/lib/erpc_sdk.rb +21 -0
- metadata +57 -7
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "timeout"
|
|
6
|
+
require "uri"
|
|
7
|
+
|
|
8
|
+
require_relative "bitcoin_codec"
|
|
9
|
+
require_relative "config"
|
|
10
|
+
require_relative "errors"
|
|
11
|
+
require_relative "transport"
|
|
12
|
+
|
|
13
|
+
module ERPC
|
|
14
|
+
BitcoinTransportFacts = Struct.new(
|
|
15
|
+
:hosted_target,
|
|
16
|
+
:method_names,
|
|
17
|
+
:public_methods,
|
|
18
|
+
keyword_init: true
|
|
19
|
+
) do
|
|
20
|
+
def initialize(hosted_target:, method_names:, public_methods:)
|
|
21
|
+
super(
|
|
22
|
+
hosted_target: hosted_target.to_s.freeze,
|
|
23
|
+
method_names: method_names.map { |name| name.to_s.dup.freeze }.freeze,
|
|
24
|
+
public_methods: public_methods.to_h.each_with_object({}) do |(name, wire), result|
|
|
25
|
+
result[name.to_s.dup.freeze] = wire.to_s.dup.freeze
|
|
26
|
+
end.freeze
|
|
27
|
+
)
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wire_method(public_name)
|
|
32
|
+
public_methods.fetch(public_name.to_s) do
|
|
33
|
+
raise ConfigError, "Bitcoin public method is absent from the accepted contract", cause: nil
|
|
34
|
+
end
|
|
35
|
+
rescue KeyError
|
|
36
|
+
raise ConfigError, "Bitcoin public method is absent from the accepted contract", cause: nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module BitcoinTransportContract
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def facts_from_contract(contract)
|
|
44
|
+
clients = contract.fetch("clientContracts")
|
|
45
|
+
method_sets = contract.fetch("methodSets")
|
|
46
|
+
client = clients.find { |value| value.is_a?(Hash) && value["clientContractId"] == "client-bitcoin-rpc" }
|
|
47
|
+
raise ConfigError, "Bitcoin contract facts are unavailable", cause: nil unless client
|
|
48
|
+
|
|
49
|
+
method_set_id = client.fetch("methodSetId")
|
|
50
|
+
hosted = client.fetch("endpointModes").find { |value| value.is_a?(Hash) && value["mode"] == "hosted" }
|
|
51
|
+
method_set = method_sets.find { |value| value.is_a?(Hash) && value["methodSetId"] == method_set_id }
|
|
52
|
+
capabilities = method_set&.fetch("capabilities")
|
|
53
|
+
target = hosted&.fetch("defaultTarget")
|
|
54
|
+
unless target.is_a?(String) && capabilities.is_a?(Array) && !capabilities.empty?
|
|
55
|
+
raise ConfigError, "Bitcoin contract facts are unavailable", cause: nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
public_methods = {}
|
|
59
|
+
method_names = []
|
|
60
|
+
capabilities.each do |capability|
|
|
61
|
+
wire = capability.fetch("wireOperation")
|
|
62
|
+
names = capability.fetch("publicNames")
|
|
63
|
+
public_name = names.fetch("ruby")
|
|
64
|
+
unless wire.is_a?(String) && public_name.is_a?(String) && !wire.empty? && !public_name.empty?
|
|
65
|
+
raise ConfigError, "Bitcoin contract facts are unavailable", cause: nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
method_names << wire
|
|
69
|
+
public_methods[public_name] = wire
|
|
70
|
+
end
|
|
71
|
+
BitcoinTransportFacts.new(
|
|
72
|
+
hosted_target: target,
|
|
73
|
+
method_names: method_names,
|
|
74
|
+
public_methods: public_methods
|
|
75
|
+
)
|
|
76
|
+
rescue KeyError, NoMethodError, TypeError
|
|
77
|
+
raise ConfigError, "Bitcoin contract facts are unavailable", cause: nil
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class BitcoinHttpTransport
|
|
82
|
+
attr_reader :method_names
|
|
83
|
+
|
|
84
|
+
def initialize(facts:, api_key: nil, endpoint: nil, direct_endpoint: nil, headers: {}, timeout: DEFAULT_TIMEOUT,
|
|
85
|
+
adapter: NetHttpAdapter.new, namespace: "bitcoin.rpc")
|
|
86
|
+
unless facts.is_a?(BitcoinTransportFacts)
|
|
87
|
+
raise ConfigError, "Bitcoin transport facts are unavailable", cause: nil
|
|
88
|
+
end
|
|
89
|
+
raise ConfigError, "timeout must be positive", cause: nil unless timeout.is_a?(Numeric) && timeout.finite? && timeout.positive?
|
|
90
|
+
|
|
91
|
+
@facts = facts
|
|
92
|
+
@method_names = facts.method_names
|
|
93
|
+
@api_key = api_key.is_a?(String) && !api_key.strip.empty? ? api_key.strip : nil
|
|
94
|
+
@timeout = timeout.to_f
|
|
95
|
+
@adapter = adapter
|
|
96
|
+
@namespace = namespace.to_s.freeze
|
|
97
|
+
@id_mutex = Mutex.new
|
|
98
|
+
@next_id = 0
|
|
99
|
+
@direct = !direct_endpoint.nil?
|
|
100
|
+
|
|
101
|
+
if direct_endpoint
|
|
102
|
+
unless direct_endpoint.is_a?(RpcEndpointConfig)
|
|
103
|
+
raise ConfigError, "bitcoin_rpc must use RpcEndpointConfig", cause: nil
|
|
104
|
+
end
|
|
105
|
+
if direct_endpoint.websocket_url
|
|
106
|
+
raise ConfigError, "bitcoin_rpc does not support WebSocket URLs", cause: nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
@target = validate_http_url(direct_endpoint.http_url)
|
|
110
|
+
@headers = direct_endpoint.headers.dup
|
|
111
|
+
@redactions = Redaction.send(:direct_variants_with_path,
|
|
112
|
+
http_url: direct_endpoint.http_url,
|
|
113
|
+
websocket_url: direct_endpoint.websocket_url,
|
|
114
|
+
headers: direct_endpoint.headers
|
|
115
|
+
)
|
|
116
|
+
@available = true
|
|
117
|
+
elsif @api_key
|
|
118
|
+
root = endpoint || default_hosted_target
|
|
119
|
+
@target = hosted_target(root)
|
|
120
|
+
@headers = headers.to_h.dup
|
|
121
|
+
@headers.delete_if { |name, _| name.to_s.casecmp?("authorization") }
|
|
122
|
+
@headers["authorization"] = "Bearer #{@api_key}"
|
|
123
|
+
@redactions = [@api_key]
|
|
124
|
+
@available = true
|
|
125
|
+
else
|
|
126
|
+
@target = nil
|
|
127
|
+
@headers = {}
|
|
128
|
+
@redactions = []
|
|
129
|
+
@available = false
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def endpoint
|
|
134
|
+
return "" unless @available
|
|
135
|
+
|
|
136
|
+
uri = URI.parse(@target)
|
|
137
|
+
uri.path = "/"
|
|
138
|
+
uri.query = nil
|
|
139
|
+
uri.fragment = nil
|
|
140
|
+
uri.user = nil if uri.respond_to?(:user=)
|
|
141
|
+
uri.to_s
|
|
142
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
143
|
+
""
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def inspect
|
|
147
|
+
"#<#{self.class} endpoint=#{endpoint.inspect} header_names=#{safe_header_names.inspect}>"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def wire_method(public_name)
|
|
151
|
+
@facts.wire_method(public_name)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def request(method, params = nil, projector: nil)
|
|
155
|
+
unless @method_names.include?(method.to_s)
|
|
156
|
+
raise ConfigError, "Bitcoin method is not in the accepted named-read contract", cause: nil
|
|
157
|
+
end
|
|
158
|
+
raise NotConfiguredError, @namespace unless @available
|
|
159
|
+
unless @adapter.respond_to?(:request_stream)
|
|
160
|
+
raise ConfigError, "Bitcoin transport requires a streaming HTTP adapter", cause: nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
request_id = next_id
|
|
164
|
+
body = {
|
|
165
|
+
"jsonrpc" => "2.0",
|
|
166
|
+
"id" => request_id,
|
|
167
|
+
"method" => method.to_s,
|
|
168
|
+
"params" => params.nil? ? [] : params
|
|
169
|
+
}
|
|
170
|
+
raw_body = JSON.generate(body)
|
|
171
|
+
text = stream_body(raw_body)
|
|
172
|
+
BitcoinCodec.decode(
|
|
173
|
+
text,
|
|
174
|
+
request_id,
|
|
175
|
+
redactions: @redactions,
|
|
176
|
+
projector: projector
|
|
177
|
+
)
|
|
178
|
+
rescue InvalidResponseError, JsonRpcError, HttpError, NotConfiguredError, ConfigError, TimeoutError, TransportError
|
|
179
|
+
raise
|
|
180
|
+
rescue JSON::GeneratorError, TypeError, ArgumentError
|
|
181
|
+
raise InvalidResponseError.new("ERPC returned an invalid Bitcoin response"), cause: nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
def default_hosted_target
|
|
187
|
+
# The generated projection supplies the accepted target after its
|
|
188
|
+
# visibility gate. Until then, callers pass the immutable facts' target
|
|
189
|
+
# explicitly through the private constructor path.
|
|
190
|
+
@facts.hosted_target
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def validate_http_url(value)
|
|
194
|
+
input = value.to_s
|
|
195
|
+
uri = URI.parse(input)
|
|
196
|
+
valid = uri.absolute? && uri.host && %w[http https].include?(uri.scheme) && !input.include?("#")
|
|
197
|
+
valid &&= !input.match?(%r{://[^/?#]*@})
|
|
198
|
+
raise ConfigError, "HTTP endpoint must be an absolute HTTP(S) URL", cause: nil unless valid
|
|
199
|
+
|
|
200
|
+
input.freeze
|
|
201
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
202
|
+
raise ConfigError, "HTTP endpoint must be an absolute HTTP(S) URL", cause: nil
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def hosted_target(root)
|
|
206
|
+
target = validate_http_url(root)
|
|
207
|
+
uri = URI.parse(target)
|
|
208
|
+
hosted_path = URI.parse(@facts.hosted_target).path.to_s.sub(%r{\A/+}, "").sub(%r{/+\z}, "")
|
|
209
|
+
base_path = uri.path.to_s.sub(%r{/+\z}, "")
|
|
210
|
+
unless hosted_path.empty? || base_path.split("/").last == hosted_path
|
|
211
|
+
base_path = "#{base_path}/#{hosted_path}"
|
|
212
|
+
end
|
|
213
|
+
uri.path = base_path.empty? ? "/" : base_path
|
|
214
|
+
pairs = URI.decode_www_form(uri.query.to_s).reject { |name, _| name.casecmp?("api-key") }
|
|
215
|
+
uri.query = pairs.empty? ? nil : URI.encode_www_form(pairs)
|
|
216
|
+
uri.fragment = nil
|
|
217
|
+
uri.to_s
|
|
218
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
219
|
+
raise ConfigError, "HTTP endpoint must be an absolute HTTP(S) URL", cause: nil
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def request_headers
|
|
223
|
+
values = @headers.dup
|
|
224
|
+
values.delete_if { |name, _| %w[accept content-type].include?(name.to_s.downcase) }
|
|
225
|
+
values["accept"] = "application/json"
|
|
226
|
+
values["content-type"] = "application/json"
|
|
227
|
+
values
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def next_id
|
|
231
|
+
@id_mutex.synchronize do
|
|
232
|
+
@next_id += 1
|
|
233
|
+
@next_id = 1 if @next_id > 2_147_483_647
|
|
234
|
+
@next_id
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def stream_body(raw_body)
|
|
239
|
+
deadline = monotonic_now + @timeout
|
|
240
|
+
consumed = false
|
|
241
|
+
text = nil
|
|
242
|
+
close_resource = nil
|
|
243
|
+
timeout_value = [remaining(deadline), 0.001].max
|
|
244
|
+
|
|
245
|
+
Timeout.timeout(timeout_value) do
|
|
246
|
+
result = @adapter.request_stream(
|
|
247
|
+
method: :post,
|
|
248
|
+
url: @target,
|
|
249
|
+
headers: request_headers,
|
|
250
|
+
body: raw_body,
|
|
251
|
+
timeout: remaining(deadline)
|
|
252
|
+
) do |status, chunks, response_headers|
|
|
253
|
+
consumed = true
|
|
254
|
+
raise HttpError, status unless status.to_i.between?(200, 299)
|
|
255
|
+
|
|
256
|
+
encoded = non_identity_content_encoding?(response_headers)
|
|
257
|
+
text, close_resource = read_chunks(
|
|
258
|
+
chunks, deadline,
|
|
259
|
+
expected_bytes: encoded ? nil : response_length(response_headers)
|
|
260
|
+
)
|
|
261
|
+
text
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
unless consumed
|
|
265
|
+
status, chunks, close_resource = unpack_stream_response(result)
|
|
266
|
+
raise HttpError, status unless status.to_i.between?(200, 299)
|
|
267
|
+
|
|
268
|
+
text = read_chunks(chunks, deadline).first
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
text
|
|
272
|
+
rescue ::Timeout::Error
|
|
273
|
+
raise TimeoutError, @timeout, cause: nil
|
|
274
|
+
rescue HttpError, InvalidResponseError
|
|
275
|
+
raise
|
|
276
|
+
rescue ConfigError, TimeoutError, TransportError
|
|
277
|
+
raise
|
|
278
|
+
rescue IOError, EOFError, SocketError, SystemCallError, Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout
|
|
279
|
+
raise TransportError, "Unable to reach ERPC", cause: nil
|
|
280
|
+
ensure
|
|
281
|
+
close_stream(close_resource)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def read_chunks(chunks, deadline, expected_bytes: nil)
|
|
285
|
+
total = 0
|
|
286
|
+
buffer = +"".b
|
|
287
|
+
close_resource = chunks if chunks.respond_to?(:close)
|
|
288
|
+
begin
|
|
289
|
+
enumerable = if chunks.respond_to?(:each)
|
|
290
|
+
chunks
|
|
291
|
+
else
|
|
292
|
+
raise ConfigError, "streaming HTTP adapter returned no body", cause: nil
|
|
293
|
+
end
|
|
294
|
+
enumerable.each do |chunk|
|
|
295
|
+
raise TimeoutError, @timeout, cause: nil if remaining(deadline) <= 0
|
|
296
|
+
bytes = chunk.to_s.b
|
|
297
|
+
if total + bytes.bytesize > BITCOIN_RESPONSE_LIMITS.decoded_entity_bytes
|
|
298
|
+
raise InvalidResponseError.new("ERPC returned an invalid Bitcoin response"), cause: nil
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
buffer << bytes
|
|
302
|
+
total += bytes.bytesize
|
|
303
|
+
end
|
|
304
|
+
if expected_bytes && total != expected_bytes
|
|
305
|
+
raise TransportError, "Unable to reach ERPC", cause: nil
|
|
306
|
+
end
|
|
307
|
+
buffer.force_encoding(Encoding::UTF_8)
|
|
308
|
+
unless buffer.valid_encoding?
|
|
309
|
+
raise InvalidResponseError.new("ERPC returned an invalid Bitcoin response"), cause: nil
|
|
310
|
+
end
|
|
311
|
+
[buffer, close_resource]
|
|
312
|
+
ensure
|
|
313
|
+
close_stream(close_resource)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def unpack_stream_response(result)
|
|
318
|
+
if result.is_a?(HttpStreamResponse)
|
|
319
|
+
[result.status, result.body, result.close]
|
|
320
|
+
elsif result.respond_to?(:status) && result.respond_to?(:body)
|
|
321
|
+
[result.status, result.body, result.respond_to?(:close) ? result.close : nil]
|
|
322
|
+
elsif result.is_a?(Array) && result.length >= 2
|
|
323
|
+
[result[0], result[1], result[2]]
|
|
324
|
+
else
|
|
325
|
+
raise ConfigError, "streaming HTTP adapter returned no response", cause: nil
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def close_stream(resource)
|
|
330
|
+
resource.close if resource.respond_to?(:close)
|
|
331
|
+
rescue StandardError
|
|
332
|
+
nil
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def monotonic_now
|
|
336
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def remaining(deadline)
|
|
340
|
+
deadline - monotonic_now
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def response_length(headers)
|
|
344
|
+
value = Array(headers && (headers["content-length"] || headers["Content-Length"])).first
|
|
345
|
+
return nil if value.nil?
|
|
346
|
+
|
|
347
|
+
Integer(value.to_s, 10)
|
|
348
|
+
rescue ArgumentError, TypeError
|
|
349
|
+
nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def non_identity_content_encoding?(headers)
|
|
353
|
+
values = (headers.respond_to?(:to_h) ? headers.to_h : {}).each_with_object([]) do |(name, value), result|
|
|
354
|
+
next unless name.to_s.casecmp?("content-encoding")
|
|
355
|
+
|
|
356
|
+
result.concat(Array(value))
|
|
357
|
+
end
|
|
358
|
+
values.flat_map { |value| value.to_s.split(",") }
|
|
359
|
+
.map { |value| value.strip.downcase }
|
|
360
|
+
.any? { |coding| !coding.empty? && coding != "identity" }
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def safe_header_names
|
|
364
|
+
@headers.keys.map(&:to_s).sort
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
private_constant :BitcoinTransportFacts, :BitcoinTransportContract, :BitcoinHttpTransport
|
|
369
|
+
end
|
data/lib/erpc_sdk/bsc.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ERPC
|
|
6
|
+
BSC_TRANSPORT_FACTS = SealedEvmContract.facts_from_contract(RPC_NETWORK_CONTRACT, "client-bsc-rpc")
|
|
7
|
+
BSC_RPC_METHODS = BSC_TRANSPORT_FACTS.method_names
|
|
8
|
+
private_constant :BSC_TRANSPORT_FACTS
|
|
9
|
+
|
|
10
|
+
class BscRpc < BaseRpc
|
|
11
|
+
alias_method :endpoint, :endpoint
|
|
12
|
+
alias_method :eth_chain_id, :eth_chain_id
|
|
13
|
+
alias_method :eth_get_balance, :eth_get_balance
|
|
14
|
+
alias_method :eth_call, :eth_call
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class BscClient
|
|
18
|
+
attr_reader :rpc
|
|
19
|
+
|
|
20
|
+
def initialize(transport)
|
|
21
|
+
@rpc = BscRpc.new(transport)
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Bsc
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def create_client(transport)
|
|
30
|
+
BscClient.new(transport)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/erpc_sdk/client.rb
CHANGED
|
@@ -23,7 +23,9 @@ module ERPC
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
class Client
|
|
26
|
-
attr_reader :solana, :ethereum, :avalanche, :price, :account, :usage, :swap
|
|
26
|
+
attr_reader :solana, :ethereum, :avalanche, :price, :account, :usage, :swap,
|
|
27
|
+
:bitcoin, :base, :arbitrum, :polygon, :bsc, :optimism, :kaia,
|
|
28
|
+
:soneium, :monad, :hyperliquid, :arc, :robinhood, :tron, :near, :sui
|
|
27
29
|
|
|
28
30
|
def initialize(config, http_adapter: nil, websocket_factory: nil)
|
|
29
31
|
adapter = http_adapter || NetHttpAdapter.new
|
|
@@ -303,6 +305,97 @@ module ERPC
|
|
|
303
305
|
)
|
|
304
306
|
end
|
|
305
307
|
)
|
|
308
|
+
if defined?(BITCOIN_TRANSPORT_FACTS)
|
|
309
|
+
@bitcoin = BitcoinClient.new(
|
|
310
|
+
BitcoinHttpTransport.new(
|
|
311
|
+
facts: BITCOIN_TRANSPORT_FACTS,
|
|
312
|
+
api_key: config.api_key,
|
|
313
|
+
endpoint: config.endpoint,
|
|
314
|
+
direct_endpoint: config.bitcoin_rpc,
|
|
315
|
+
headers: config.headers,
|
|
316
|
+
timeout: config.timeout,
|
|
317
|
+
adapter: adapter,
|
|
318
|
+
namespace: "bitcoin.rpc"
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
@base = BaseClient.new(
|
|
322
|
+
SealedEvmHttpTransport.new(
|
|
323
|
+
facts: BASE_TRANSPORT_FACTS,
|
|
324
|
+
api_key: config.api_key,
|
|
325
|
+
endpoint: config.base_endpoint,
|
|
326
|
+
direct_endpoint: config.base_rpc,
|
|
327
|
+
headers: config.headers,
|
|
328
|
+
timeout: config.timeout,
|
|
329
|
+
adapter: adapter,
|
|
330
|
+
namespace: "base.rpc"
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
@arbitrum = ArbitrumClient.new(
|
|
334
|
+
SealedEvmHttpTransport.new(
|
|
335
|
+
facts: ARBITRUM_TRANSPORT_FACTS,
|
|
336
|
+
api_key: config.api_key,
|
|
337
|
+
endpoint: config.arbitrum_endpoint,
|
|
338
|
+
direct_endpoint: config.arbitrum_rpc,
|
|
339
|
+
headers: config.headers,
|
|
340
|
+
timeout: config.timeout,
|
|
341
|
+
adapter: adapter,
|
|
342
|
+
namespace: "arbitrum.rpc"
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
@polygon = sealed_client(config, POLYGON_TRANSPORT_FACTS, config.polygon_rpc,
|
|
346
|
+
config.polygon_endpoint, "polygon.rpc", PolygonClient, adapter)
|
|
347
|
+
@bsc = sealed_client(config, BSC_TRANSPORT_FACTS, config.bsc_rpc,
|
|
348
|
+
config.bsc_endpoint, "bsc.rpc", BscClient, adapter)
|
|
349
|
+
@optimism = sealed_client(config, OPTIMISM_TRANSPORT_FACTS, config.optimism_rpc,
|
|
350
|
+
config.optimism_endpoint, "optimism.rpc", OptimismClient, adapter)
|
|
351
|
+
@kaia = sealed_client(config, KAIA_TRANSPORT_FACTS, config.kaia_rpc,
|
|
352
|
+
config.kaia_endpoint, "kaia.rpc", KaiaClient, adapter)
|
|
353
|
+
@soneium = sealed_client(config, SONEIUM_TRANSPORT_FACTS, config.soneium_rpc,
|
|
354
|
+
config.soneium_endpoint, "soneium.rpc", SoneiumClient, adapter)
|
|
355
|
+
@monad = sealed_client(config, MONAD_TRANSPORT_FACTS, config.monad_rpc,
|
|
356
|
+
config.monad_endpoint, "monad.rpc", MonadClient, adapter)
|
|
357
|
+
@hyperliquid = sealed_client(config, HYPERLIQUID_TRANSPORT_FACTS, config.hyperliquid_rpc,
|
|
358
|
+
config.hyperliquid_endpoint, "hyperliquid.rpc", HyperliquidClient, adapter)
|
|
359
|
+
@arc = sealed_client(config, ARC_TRANSPORT_FACTS, config.arc_rpc,
|
|
360
|
+
config.arc_endpoint, "arc.rpc", ArcClient, adapter)
|
|
361
|
+
@robinhood = sealed_client(config, ROBINHOOD_TRANSPORT_FACTS, config.robinhood_rpc,
|
|
362
|
+
config.robinhood_endpoint, "robinhood.rpc", RobinhoodClient, adapter)
|
|
363
|
+
@tron = sealed_client(config, TRON_TRANSPORT_FACTS, config.tron_rpc,
|
|
364
|
+
config.tron_endpoint, "tron.rpc", TronClient, adapter)
|
|
365
|
+
@near = NearClient.new(
|
|
366
|
+
NearHttpTransport.new(
|
|
367
|
+
facts: NEAR_TRANSPORT_FACTS,
|
|
368
|
+
api_key: config.api_key,
|
|
369
|
+
endpoint: config.near_endpoint,
|
|
370
|
+
direct_endpoint: config.near_rpc,
|
|
371
|
+
headers: config.headers,
|
|
372
|
+
timeout: config.timeout,
|
|
373
|
+
adapter: adapter,
|
|
374
|
+
namespace: "near.rpc"
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
@sui = SuiClient.new(
|
|
378
|
+
SuiGrpcWebTransport.new(
|
|
379
|
+
facts: SUI_TRANSPORT_FACTS,
|
|
380
|
+
api_key: config.api_key,
|
|
381
|
+
endpoint: config.sui_endpoint,
|
|
382
|
+
direct_endpoint: config.sui_grpc_web,
|
|
383
|
+
headers: config.headers,
|
|
384
|
+
timeout: config.timeout,
|
|
385
|
+
adapter: adapter,
|
|
386
|
+
namespace: "sui.grpc"
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
else
|
|
390
|
+
# The bootstrap tree may run before the repaired generated projection
|
|
391
|
+
# is present. The released namespaces remain fully constructible in
|
|
392
|
+
# that phase; the new surfaces activate when their private projection
|
|
393
|
+
# is loaded.
|
|
394
|
+
@bitcoin = @base = @arbitrum = @polygon = @bsc = @optimism = @kaia = @soneium = @monad = nil
|
|
395
|
+
@hyperliquid = @arc = @robinhood = @tron = nil
|
|
396
|
+
@near = nil
|
|
397
|
+
@sui = nil
|
|
398
|
+
end
|
|
306
399
|
@closed = false
|
|
307
400
|
end
|
|
308
401
|
|
|
@@ -318,6 +411,21 @@ module ERPC
|
|
|
318
411
|
|
|
319
412
|
private
|
|
320
413
|
|
|
414
|
+
def sealed_client(config, facts, override, endpoint, namespace, client_class, adapter)
|
|
415
|
+
client_class.new(
|
|
416
|
+
SealedEvmHttpTransport.new(
|
|
417
|
+
facts: facts,
|
|
418
|
+
api_key: config.api_key,
|
|
419
|
+
endpoint: endpoint,
|
|
420
|
+
direct_endpoint: override,
|
|
421
|
+
headers: config.headers,
|
|
422
|
+
timeout: config.timeout,
|
|
423
|
+
adapter: adapter,
|
|
424
|
+
namespace: namespace
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
end
|
|
428
|
+
|
|
321
429
|
def avalanche_namespace(transport, methods, prefix)
|
|
322
430
|
RpcNamespace.new(
|
|
323
431
|
transport,
|