erpc-sdk 0.6.0 → 0.8.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/README.md +245 -0
- data/lib/erpc_sdk/bridge.rb +1507 -0
- data/lib/erpc_sdk/client.rb +215 -76
- data/lib/erpc_sdk/config.rb +107 -8
- data/lib/erpc_sdk/dex_catalog.rb +207 -0
- data/lib/erpc_sdk/errors.rb +100 -9
- data/lib/erpc_sdk/generated/bridge_capabilities.rb +9 -0
- data/lib/erpc_sdk/generated/dex_catalog.rb +28 -0
- data/lib/erpc_sdk/generated/swap_execution_capabilities.rb +11 -0
- data/lib/erpc_sdk/generated/token_catalog.rb +216 -0
- data/lib/erpc_sdk/generated/token_rankings.rb +25 -0
- data/lib/erpc_sdk/swap.rb +1367 -0
- data/lib/erpc_sdk/token_catalog.rb +170 -0
- data/lib/erpc_sdk/token_rankings.rb +50 -0
- data/lib/erpc_sdk/transport.rb +69 -20
- data/lib/erpc_sdk/version.rb +1 -1
- data/lib/erpc_sdk/websocket.rb +45 -13
- data/lib/erpc_sdk.rb +5 -0
- metadata +12 -2
data/lib/erpc_sdk/client.rb
CHANGED
|
@@ -23,57 +23,165 @@ module ERPC
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
class Client
|
|
26
|
-
attr_reader :solana, :ethereum, :avalanche, :price, :account, :usage
|
|
26
|
+
attr_reader :solana, :ethereum, :avalanche, :price, :account, :usage, :swap
|
|
27
27
|
|
|
28
28
|
def initialize(config, http_adapter: nil, websocket_factory: nil)
|
|
29
29
|
adapter = http_adapter || NetHttpAdapter.new
|
|
30
|
-
|
|
31
|
-
api_key: config.api_key,
|
|
32
|
-
endpoint: config.endpoint,
|
|
33
|
-
headers: config.headers,
|
|
34
|
-
timeout: config.timeout,
|
|
35
|
-
adapter: adapter
|
|
36
|
-
)
|
|
37
|
-
ethereum_transport = HttpJsonRpcTransport.new(
|
|
38
|
-
api_key: config.api_key,
|
|
39
|
-
endpoint: URLs.with_path(config.endpoint, "/eth"),
|
|
40
|
-
headers: config.headers,
|
|
41
|
-
timeout: config.timeout,
|
|
42
|
-
adapter: adapter
|
|
43
|
-
)
|
|
44
|
-
avalanche_transport = HttpJsonRpcTransport.new(
|
|
45
|
-
api_key: config.api_key,
|
|
46
|
-
endpoint: URLs.with_path(config.avalanche_endpoint, "/ava"),
|
|
47
|
-
headers: config.headers,
|
|
48
|
-
timeout: config.timeout,
|
|
49
|
-
adapter: adapter
|
|
50
|
-
)
|
|
51
|
-
avalanche_index_transport = lambda do |path|
|
|
30
|
+
unavailable_http = lambda do |namespace|
|
|
52
31
|
HttpJsonRpcTransport.new(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
headers: config.headers,
|
|
32
|
+
endpoint: URLs.unavailable_endpoint(namespace),
|
|
33
|
+
headers: {},
|
|
56
34
|
timeout: config.timeout,
|
|
57
|
-
adapter: adapter
|
|
35
|
+
adapter: adapter,
|
|
36
|
+
unavailable_namespace: namespace
|
|
58
37
|
)
|
|
59
38
|
end
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
39
|
+
legacy_http = lambda do |endpoint, namespace|
|
|
40
|
+
if config.api_key.nil?
|
|
41
|
+
unavailable_http.call(namespace)
|
|
42
|
+
else
|
|
43
|
+
HttpJsonRpcTransport.new(
|
|
44
|
+
api_key: config.api_key,
|
|
45
|
+
endpoint: endpoint,
|
|
46
|
+
headers: config.headers,
|
|
47
|
+
timeout: config.timeout,
|
|
48
|
+
adapter: adapter
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
direct_http = lambda do |override|
|
|
53
|
+
HttpJsonRpcTransport.new(
|
|
54
|
+
endpoint: override.http_url,
|
|
55
|
+
headers: override.headers,
|
|
56
|
+
timeout: config.timeout,
|
|
57
|
+
adapter: adapter,
|
|
58
|
+
direct: true,
|
|
59
|
+
redactions: Redaction.direct_variants(
|
|
60
|
+
http_url: override.http_url,
|
|
61
|
+
websocket_url: override.websocket_url,
|
|
62
|
+
headers: override.headers
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
selected_http = lambda do |override, endpoint, namespace|
|
|
67
|
+
override.nil? ? legacy_http.call(endpoint, namespace) : direct_http.call(override)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
unavailable_websocket = lambda do |namespace|
|
|
71
|
+
WebSocketJsonRpcTransport.new(
|
|
72
|
+
URLs.unavailable_endpoint(namespace),
|
|
73
|
+
nil,
|
|
74
|
+
config.timeout,
|
|
75
|
+
connection_factory: websocket_factory,
|
|
76
|
+
unavailable_namespace: namespace
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
legacy_websocket = lambda do |endpoint, namespace|
|
|
80
|
+
if config.api_key.nil?
|
|
81
|
+
unavailable_websocket.call(namespace)
|
|
82
|
+
else
|
|
83
|
+
WebSocketJsonRpcTransport.new(
|
|
84
|
+
endpoint,
|
|
85
|
+
config.api_key,
|
|
86
|
+
config.timeout,
|
|
87
|
+
connection_factory: websocket_factory
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
direct_websocket = lambda do |override, namespace|
|
|
92
|
+
if override.websocket_url.nil?
|
|
93
|
+
unavailable_websocket.call(namespace)
|
|
94
|
+
else
|
|
95
|
+
WebSocketJsonRpcTransport.new(
|
|
96
|
+
override.websocket_url,
|
|
97
|
+
nil,
|
|
98
|
+
config.timeout,
|
|
99
|
+
connection_factory: websocket_factory,
|
|
100
|
+
direct: true,
|
|
101
|
+
redactions: Redaction.direct_variants(
|
|
102
|
+
http_url: override.http_url,
|
|
103
|
+
websocket_url: override.websocket_url,
|
|
104
|
+
headers: override.headers
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
selected_websocket = lambda do |override, endpoint, namespace|
|
|
110
|
+
override.nil? ? legacy_websocket.call(endpoint, namespace) : direct_websocket.call(override, namespace)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
solana_transport = selected_http.call(config.solana_rpc, config.endpoint, "solana.rpc")
|
|
114
|
+
solana_aux_transport = if config.solana_rpc || config.api_key
|
|
115
|
+
solana_transport
|
|
116
|
+
else
|
|
117
|
+
unavailable_http.call("solana.das")
|
|
118
|
+
end
|
|
119
|
+
solana_history_transport = if config.solana_rpc || config.api_key
|
|
120
|
+
solana_transport
|
|
121
|
+
else
|
|
122
|
+
unavailable_http.call("solana.history")
|
|
123
|
+
end
|
|
124
|
+
solana_leaders_transport = if config.solana_rpc || config.api_key
|
|
125
|
+
solana_transport
|
|
126
|
+
else
|
|
127
|
+
unavailable_http.call("solana.leaders")
|
|
128
|
+
end
|
|
129
|
+
solana_analytics_transport = if config.solana_rpc || config.api_key
|
|
130
|
+
solana_transport
|
|
131
|
+
else
|
|
132
|
+
unavailable_http.call("solana.analytics")
|
|
133
|
+
end
|
|
134
|
+
ethereum_transport = selected_http.call(
|
|
135
|
+
config.ethereum_rpc,
|
|
136
|
+
URLs.with_path(config.endpoint, "/eth"),
|
|
137
|
+
"ethereum.rpc"
|
|
138
|
+
)
|
|
139
|
+
avalanche_transport = selected_http.call(
|
|
140
|
+
config.avalanche_c_rpc,
|
|
141
|
+
URLs.with_path(config.avalanche_endpoint, "/ava"),
|
|
142
|
+
"avalanche.rpc"
|
|
143
|
+
)
|
|
144
|
+
avalanche_avax_transport = legacy_http.call(
|
|
145
|
+
URLs.with_path(config.avalanche_endpoint, "/ava"),
|
|
146
|
+
"avalanche.avax"
|
|
65
147
|
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
148
|
+
avalanche_x_chain_transport = if config.api_key
|
|
149
|
+
avalanche_avax_transport
|
|
150
|
+
else
|
|
151
|
+
unavailable_http.call("avalanche.x_chain")
|
|
152
|
+
end
|
|
153
|
+
avalanche_p_chain_transport = if config.api_key
|
|
154
|
+
avalanche_avax_transport
|
|
155
|
+
else
|
|
156
|
+
unavailable_http.call("avalanche.p_chain")
|
|
157
|
+
end
|
|
158
|
+
avalanche_proposer_vm_transport = if config.api_key
|
|
159
|
+
avalanche_avax_transport
|
|
160
|
+
else
|
|
161
|
+
unavailable_http.call("avalanche.proposer_vm")
|
|
162
|
+
end
|
|
163
|
+
avalanche_info_transport = if config.api_key
|
|
164
|
+
avalanche_avax_transport
|
|
165
|
+
else
|
|
166
|
+
unavailable_http.call("avalanche.info")
|
|
167
|
+
end
|
|
168
|
+
avalanche_index_transport = lambda do |path, namespace|
|
|
169
|
+
legacy_http.call(URLs.with_path(config.avalanche_endpoint, path), namespace)
|
|
170
|
+
end
|
|
171
|
+
solana_ws = selected_websocket.call(
|
|
172
|
+
config.solana_rpc,
|
|
173
|
+
config.api_key.nil? ? URLs.unavailable_endpoint("solana.subscriptions") : URLs.websocket(config.endpoint, config.api_key, ""),
|
|
174
|
+
"solana.subscriptions"
|
|
175
|
+
)
|
|
176
|
+
ethereum_ws = selected_websocket.call(
|
|
177
|
+
config.ethereum_rpc,
|
|
178
|
+
config.api_key.nil? ? URLs.unavailable_endpoint("ethereum.subscriptions") : URLs.websocket(config.endpoint, config.api_key, "/eth"),
|
|
179
|
+
"ethereum.subscriptions"
|
|
71
180
|
)
|
|
72
|
-
avalanche_ws =
|
|
73
|
-
|
|
74
|
-
config.api_key,
|
|
75
|
-
|
|
76
|
-
connection_factory: websocket_factory
|
|
181
|
+
avalanche_ws = selected_websocket.call(
|
|
182
|
+
config.avalanche_c_rpc,
|
|
183
|
+
config.api_key.nil? ? URLs.unavailable_endpoint("avalanche.subscriptions") : URLs.websocket(config.avalanche_endpoint, config.api_key, "/ava-ws"),
|
|
184
|
+
"avalanche.subscriptions"
|
|
77
185
|
)
|
|
78
186
|
|
|
79
187
|
@solana = SolanaClient.new(
|
|
@@ -83,16 +191,16 @@ module ERPC
|
|
|
83
191
|
parameter_mode: :positional,
|
|
84
192
|
batch_policy: :solana_standard
|
|
85
193
|
),
|
|
86
|
-
das: RpcNamespace.new(
|
|
87
|
-
history: RpcNamespace.new(
|
|
194
|
+
das: RpcNamespace.new(solana_aux_transport, SOLANA_DAS_METHODS, parameter_mode: :named),
|
|
195
|
+
history: RpcNamespace.new(solana_history_transport, SOLANA_HISTORY_METHODS, parameter_mode: :positional),
|
|
88
196
|
leaders: RpcNamespace.new(
|
|
89
|
-
|
|
197
|
+
solana_leaders_transport,
|
|
90
198
|
SOLANA_LEADER_METHODS,
|
|
91
199
|
parameter_mode: :positional,
|
|
92
200
|
batch_policy: :unsupported
|
|
93
201
|
),
|
|
94
202
|
analytics: RpcNamespace.new(
|
|
95
|
-
|
|
203
|
+
solana_analytics_transport,
|
|
96
204
|
SOLANA_ANALYTICS_METHODS,
|
|
97
205
|
parameter_mode: :positional
|
|
98
206
|
),
|
|
@@ -104,65 +212,96 @@ module ERPC
|
|
|
104
212
|
)
|
|
105
213
|
@avalanche = AvalancheClient.new(
|
|
106
214
|
rpc: RpcNamespace.new(avalanche_transport, ETHEREUM_RPC_METHODS, parameter_mode: :positional),
|
|
107
|
-
avax: avalanche_namespace(
|
|
108
|
-
x_chain: avalanche_namespace(
|
|
109
|
-
p_chain: avalanche_namespace(
|
|
215
|
+
avax: avalanche_namespace(avalanche_avax_transport, AVALANCHE_AVAX_METHODS, "avax"),
|
|
216
|
+
x_chain: avalanche_namespace(avalanche_x_chain_transport, AVALANCHE_X_CHAIN_METHODS, "avm"),
|
|
217
|
+
p_chain: avalanche_namespace(avalanche_p_chain_transport, AVALANCHE_P_CHAIN_METHODS, "platform"),
|
|
110
218
|
proposer_vm: avalanche_namespace(
|
|
111
|
-
|
|
219
|
+
avalanche_proposer_vm_transport,
|
|
112
220
|
AVALANCHE_PROPOSER_VM_METHODS,
|
|
113
221
|
"proposervm"
|
|
114
222
|
),
|
|
115
|
-
info: avalanche_namespace(
|
|
223
|
+
info: avalanche_namespace(avalanche_info_transport, AVALANCHE_INFO_METHODS, "info"),
|
|
116
224
|
index: AvalancheIndexClient.new(
|
|
117
225
|
c_chain_blocks: avalanche_namespace(
|
|
118
|
-
avalanche_index_transport.call("/ava/ext/index/C/block"),
|
|
226
|
+
avalanche_index_transport.call("/ava/ext/index/C/block", "avalanche.index.c_chain_blocks"),
|
|
119
227
|
AVALANCHE_INDEX_METHODS,
|
|
120
228
|
"index"
|
|
121
229
|
),
|
|
122
230
|
p_chain_blocks: avalanche_namespace(
|
|
123
|
-
avalanche_index_transport.call("/ava/ext/index/P/block"),
|
|
231
|
+
avalanche_index_transport.call("/ava/ext/index/P/block", "avalanche.index.p_chain_blocks"),
|
|
124
232
|
AVALANCHE_INDEX_METHODS,
|
|
125
233
|
"index"
|
|
126
234
|
),
|
|
127
235
|
x_chain_blocks: avalanche_namespace(
|
|
128
|
-
avalanche_index_transport.call("/ava/ext/index/X/block"),
|
|
236
|
+
avalanche_index_transport.call("/ava/ext/index/X/block", "avalanche.index.x_chain_blocks"),
|
|
129
237
|
AVALANCHE_INDEX_METHODS,
|
|
130
238
|
"index"
|
|
131
239
|
),
|
|
132
240
|
x_chain_transactions: avalanche_namespace(
|
|
133
|
-
avalanche_index_transport.call("/ava/ext/index/X/tx"),
|
|
241
|
+
avalanche_index_transport.call("/ava/ext/index/X/tx", "avalanche.index.x_chain_transactions"),
|
|
134
242
|
AVALANCHE_INDEX_METHODS,
|
|
135
243
|
"index"
|
|
136
244
|
)
|
|
137
245
|
),
|
|
138
246
|
subscriptions: EthereumSubscriptions.new(avalanche_ws)
|
|
139
247
|
)
|
|
248
|
+
@swap = SwapClient.new(
|
|
249
|
+
ethereum_transport: ethereum_transport,
|
|
250
|
+
avalanche_transport: avalanche_transport
|
|
251
|
+
)
|
|
140
252
|
@price = PriceClient.new(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
253
|
+
if config.api_key.nil?
|
|
254
|
+
RestTransport.new(
|
|
255
|
+
endpoint: URLs.unavailable_endpoint("price"),
|
|
256
|
+
timeout: config.timeout,
|
|
257
|
+
adapter: adapter,
|
|
258
|
+
unavailable_namespace: "price"
|
|
259
|
+
)
|
|
260
|
+
else
|
|
261
|
+
RestTransport.new(
|
|
262
|
+
credential: config.api_key,
|
|
263
|
+
endpoint: config.endpoint,
|
|
264
|
+
headers: config.headers,
|
|
265
|
+
timeout: config.timeout,
|
|
266
|
+
adapter: adapter
|
|
267
|
+
)
|
|
268
|
+
end
|
|
148
269
|
)
|
|
149
270
|
@account = AccountClient.new(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
271
|
+
if config.api_key.nil?
|
|
272
|
+
RestTransport.new(
|
|
273
|
+
endpoint: URLs.unavailable_endpoint("account"),
|
|
274
|
+
timeout: config.timeout,
|
|
275
|
+
adapter: adapter,
|
|
276
|
+
unavailable_namespace: "account"
|
|
277
|
+
)
|
|
278
|
+
else
|
|
279
|
+
RestTransport.new(
|
|
280
|
+
credential: config.api_key,
|
|
281
|
+
endpoint: config.account_endpoint,
|
|
282
|
+
headers: config.headers,
|
|
283
|
+
timeout: config.timeout,
|
|
284
|
+
adapter: adapter
|
|
285
|
+
)
|
|
286
|
+
end
|
|
157
287
|
)
|
|
158
288
|
@usage = UsageClient.new(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
289
|
+
if config.api_key.nil?
|
|
290
|
+
RestTransport.new(
|
|
291
|
+
endpoint: URLs.unavailable_endpoint("usage"),
|
|
292
|
+
timeout: config.timeout,
|
|
293
|
+
adapter: adapter,
|
|
294
|
+
unavailable_namespace: "usage"
|
|
295
|
+
)
|
|
296
|
+
else
|
|
297
|
+
RestTransport.new(
|
|
298
|
+
credential: config.api_key,
|
|
299
|
+
endpoint: config.user_endpoint,
|
|
300
|
+
headers: config.headers,
|
|
301
|
+
timeout: config.timeout,
|
|
302
|
+
adapter: adapter
|
|
303
|
+
)
|
|
304
|
+
end
|
|
166
305
|
)
|
|
167
306
|
@closed = false
|
|
168
307
|
end
|
data/lib/erpc_sdk/config.rb
CHANGED
|
@@ -12,6 +12,33 @@ module ERPC
|
|
|
12
12
|
module URLs
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
+
DIRECT_HTTP_SCHEMES = %w[http https].freeze
|
|
16
|
+
DIRECT_WEBSOCKET_SCHEMES = %w[ws wss].freeze
|
|
17
|
+
|
|
18
|
+
def direct_endpoint(value, kind:, namespace: "direct endpoint")
|
|
19
|
+
schemes = kind == :websocket ? DIRECT_WEBSOCKET_SCHEMES : DIRECT_HTTP_SCHEMES
|
|
20
|
+
unless value.is_a?(String)
|
|
21
|
+
raise ConfigError, "#{namespace} must be an absolute #{kind == :websocket ? 'WS(S)' : 'HTTP(S)'} URL", cause: nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
input = value.strip
|
|
25
|
+
scheme_match = input.match?(/\A[a-z][a-z\d+.-]*:\/\//i)
|
|
26
|
+
authority = input[/\A[a-z][a-z\d+.-]*:\/\/([^\/?#]*)/i, 1]
|
|
27
|
+
valid = !input.empty? && scheme_match && !input.include?("#") && authority &&
|
|
28
|
+
!authority.empty? && !authority.include?("@")
|
|
29
|
+
begin
|
|
30
|
+
uri = URI.parse(input)
|
|
31
|
+
valid &&= uri.absolute? && !uri.host.to_s.empty? && schemes.include?(uri.scheme)
|
|
32
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
33
|
+
valid = false
|
|
34
|
+
end
|
|
35
|
+
unless valid
|
|
36
|
+
raise ConfigError, "#{namespace} must be an absolute #{kind == :websocket ? 'WS(S)' : 'HTTP(S)'} URL", cause: nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
input.freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
15
42
|
def normalize_endpoint(value, local_http_only: false)
|
|
16
43
|
uri = URI.parse(value.to_s)
|
|
17
44
|
unless uri.absolute? && uri.host && %w[http https].include?(uri.scheme)
|
|
@@ -29,7 +56,22 @@ module ERPC
|
|
|
29
56
|
uri.path = "/" if uri.path.empty?
|
|
30
57
|
uri.to_s
|
|
31
58
|
rescue URI::InvalidURIError, ArgumentError
|
|
32
|
-
raise ConfigError, "endpoint must be an absolute HTTP(S) URL"
|
|
59
|
+
raise ConfigError, "endpoint must be an absolute HTTP(S) URL", cause: nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def public_endpoint(endpoint)
|
|
63
|
+
uri = endpoint.is_a?(URI::Generic) ? endpoint.dup : URI.parse(endpoint.to_s)
|
|
64
|
+
uri.query = nil
|
|
65
|
+
uri.fragment = nil
|
|
66
|
+
uri.user = nil if uri.respond_to?(:user=)
|
|
67
|
+
uri.to_s
|
|
68
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
69
|
+
"[REDACTED]"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def unavailable_endpoint(namespace)
|
|
73
|
+
safe = namespace.to_s.gsub(/[^a-z\d._-]+/i, "-")
|
|
74
|
+
"https://unconfigured.invalid/#{safe}"
|
|
33
75
|
end
|
|
34
76
|
|
|
35
77
|
def with_path(endpoint, path)
|
|
@@ -56,14 +98,65 @@ module ERPC
|
|
|
56
98
|
end
|
|
57
99
|
end
|
|
58
100
|
|
|
101
|
+
class RpcEndpointConfig
|
|
102
|
+
attr_reader :http_url, :websocket_url, :headers
|
|
103
|
+
|
|
104
|
+
def initialize(http_url:, websocket_url: nil, headers: {})
|
|
105
|
+
@http_url = URLs.direct_endpoint(http_url, kind: :http, namespace: "http_url")
|
|
106
|
+
@websocket_url = if websocket_url.nil?
|
|
107
|
+
nil
|
|
108
|
+
else
|
|
109
|
+
URLs.direct_endpoint(websocket_url, kind: :websocket, namespace: "websocket_url")
|
|
110
|
+
end
|
|
111
|
+
unless headers.is_a?(Hash) || headers.respond_to?(:to_h)
|
|
112
|
+
raise ConfigError, "direct endpoint headers must be a mapping"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
begin
|
|
116
|
+
values = headers.to_h
|
|
117
|
+
rescue StandardError
|
|
118
|
+
raise ConfigError, "direct endpoint headers must be a mapping", cause: nil
|
|
119
|
+
end
|
|
120
|
+
unless values.all? { |name, value| name.is_a?(String) && value.is_a?(String) }
|
|
121
|
+
raise ConfigError, "direct endpoint headers must contain string names and values"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
@headers = values.dup.freeze
|
|
125
|
+
freeze
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def inspect
|
|
129
|
+
websocket = websocket_url.nil? ? "nil" : URLs.public_endpoint(websocket_url).inspect
|
|
130
|
+
"#<#{self.class} http_url=#{URLs.public_endpoint(http_url).inspect} " \
|
|
131
|
+
"websocket_url=#{websocket} " \
|
|
132
|
+
"header_names=#{headers.keys.inspect}>"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
alias to_s inspect
|
|
136
|
+
end
|
|
137
|
+
|
|
59
138
|
class ClientConfig
|
|
60
|
-
attr_reader :api_key, :endpoint, :account_endpoint, :user_endpoint, :headers, :timeout,
|
|
139
|
+
attr_reader :api_key, :endpoint, :account_endpoint, :user_endpoint, :headers, :timeout,
|
|
140
|
+
:avalanche_endpoint, :solana_rpc, :ethereum_rpc, :avalanche_c_rpc
|
|
61
141
|
|
|
62
|
-
def initialize(api_key
|
|
142
|
+
def initialize(api_key: nil, endpoint: DEFAULT_ENDPOINT, account_endpoint: DEFAULT_ACCOUNT_ENDPOINT,
|
|
63
143
|
user_endpoint: DEFAULT_USER_ENDPOINT, headers: {}, timeout: DEFAULT_TIMEOUT,
|
|
64
|
-
avalanche_endpoint: DEFAULT_AVALANCHE_ENDPOINT
|
|
65
|
-
|
|
66
|
-
|
|
144
|
+
avalanche_endpoint: DEFAULT_AVALANCHE_ENDPOINT, solana_rpc: nil,
|
|
145
|
+
ethereum_rpc: nil, avalanche_c_rpc: nil)
|
|
146
|
+
direct_endpoints = [solana_rpc, ethereum_rpc, avalanche_c_rpc]
|
|
147
|
+
unless direct_endpoints.all? { |value| value.nil? || value.is_a?(RpcEndpointConfig) }
|
|
148
|
+
raise ConfigError, "direct RPC overrides must use RpcEndpointConfig"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
@api_key = if api_key.nil?
|
|
152
|
+
nil
|
|
153
|
+
elsif api_key.is_a?(String)
|
|
154
|
+
api_key.strip
|
|
155
|
+
else
|
|
156
|
+
raise ConfigError, "api_key must be a string"
|
|
157
|
+
end
|
|
158
|
+
@api_key = nil if @api_key&.empty?
|
|
159
|
+
raise ConfigError, "api_key must not be empty" if @api_key.nil? && direct_endpoints.compact.empty?
|
|
67
160
|
raise ConfigError, "timeout must be positive" unless timeout.is_a?(Numeric) && timeout.finite? && timeout.positive?
|
|
68
161
|
|
|
69
162
|
@endpoint = URLs.normalize_endpoint(endpoint)
|
|
@@ -72,13 +165,19 @@ module ERPC
|
|
|
72
165
|
@user_endpoint = URLs.normalize_endpoint(user_endpoint)
|
|
73
166
|
@headers = headers.to_h.transform_keys(&:to_s).transform_values(&:to_s).freeze
|
|
74
167
|
@timeout = timeout.to_f
|
|
168
|
+
@solana_rpc = solana_rpc
|
|
169
|
+
@ethereum_rpc = ethereum_rpc
|
|
170
|
+
@avalanche_c_rpc = avalanche_c_rpc
|
|
75
171
|
end
|
|
76
172
|
|
|
77
173
|
def inspect
|
|
78
|
-
|
|
174
|
+
key = api_key.nil? ? nil : "[REDACTED]"
|
|
175
|
+
"#<#{self.class} api_key=#{key.inspect} endpoint=#{endpoint.inspect} " \
|
|
79
176
|
"avalanche_endpoint=#{avalanche_endpoint.inspect} " \
|
|
80
177
|
"account_endpoint=#{account_endpoint.inspect} user_endpoint=#{user_endpoint.inspect} " \
|
|
81
|
-
"header_names=#{headers.keys.inspect} timeout=#{timeout.inspect}
|
|
178
|
+
"header_names=#{headers.keys.inspect} timeout=#{timeout.inspect} " \
|
|
179
|
+
"solana_rpc=#{solana_rpc.inspect} ethereum_rpc=#{ethereum_rpc.inspect} " \
|
|
180
|
+
"avalanche_c_rpc=#{avalanche_c_rpc.inspect}>"
|
|
82
181
|
end
|
|
83
182
|
end
|
|
84
183
|
|