@talismn/chain-connectors 0.0.13 → 0.0.15
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.
- package/dist/index.d.mts +139 -0
- package/dist/index.d.ts +139 -0
- package/dist/{talismn-chain-connectors.cjs.dev.js → index.js} +496 -510
- package/dist/index.js.map +1 -0
- package/dist/{talismn-chain-connectors.esm.js → index.mjs} +443 -468
- package/dist/index.mjs.map +1 -0
- package/package.json +26 -22
- package/dist/declarations/src/dot/ChainConnectorDot.d.ts +0 -79
- package/dist/declarations/src/dot/ChainConnectorDotStub.d.ts +0 -11
- package/dist/declarations/src/dot/IChainConnectorDot.d.ts +0 -10
- package/dist/declarations/src/dot/Websocket.d.ts +0 -111
- package/dist/declarations/src/dot/helpers.d.ts +0 -15
- package/dist/declarations/src/dot/index.d.ts +0 -3
- package/dist/declarations/src/eth/ChainConnectorEth.d.ts +0 -10
- package/dist/declarations/src/eth/ChainConnectorEthStub.d.ts +0 -10
- package/dist/declarations/src/eth/IChainConnectorEth.d.ts +0 -7
- package/dist/declarations/src/eth/getChainFromEvmNetwork.d.ts +0 -4
- package/dist/declarations/src/eth/getEvmNetworkPublicClient.d.ts +0 -4
- package/dist/declarations/src/eth/getEvmNetworkWalletClient.d.ts +0 -7
- package/dist/declarations/src/eth/getTransportForEvmNetwork.d.ts +0 -8
- package/dist/declarations/src/eth/index.d.ts +0 -3
- package/dist/declarations/src/index.d.ts +0 -3
- package/dist/declarations/src/log.d.ts +0 -2
- package/dist/declarations/src/sol/ChainConnectorSol.d.ts +0 -8
- package/dist/declarations/src/sol/ChainConnectorSolStub.d.ts +0 -8
- package/dist/declarations/src/sol/IChainConnectorSol.d.ts +0 -5
- package/dist/declarations/src/sol/getSolConnection.d.ts +0 -3
- package/dist/declarations/src/sol/index.d.ts +0 -3
- package/dist/talismn-chain-connectors.cjs.d.ts +0 -1
- package/dist/talismn-chain-connectors.cjs.js +0 -7
- package/dist/talismn-chain-connectors.cjs.prod.js +0 -1243
|
@@ -1,25 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
// src/dot/ChainConnectorDot.ts
|
|
2
|
+
import { Deferred, isTruthy, sleep, throwAfter } from "@talismn/util";
|
|
3
|
+
|
|
4
|
+
// src/log.ts
|
|
5
|
+
import anylogger from "anylogger";
|
|
6
|
+
|
|
7
|
+
// package.json
|
|
8
|
+
var package_default = {
|
|
9
|
+
name: "@talismn/chain-connectors",
|
|
10
|
+
version: "0.0.15",
|
|
11
|
+
author: "Talisman",
|
|
12
|
+
homepage: "https://talisman.xyz",
|
|
13
|
+
license: "GPL-3.0-or-later",
|
|
14
|
+
publishConfig: {
|
|
15
|
+
access: "public"
|
|
16
|
+
},
|
|
17
|
+
repository: {
|
|
18
|
+
directory: "packages/chain-connectors",
|
|
19
|
+
type: "git",
|
|
20
|
+
url: "https://github.com/TalismanSociety/talisman.git"
|
|
21
|
+
},
|
|
22
|
+
main: "./dist/index.js",
|
|
23
|
+
module: "./dist/index.mjs",
|
|
24
|
+
files: [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
engines: {
|
|
28
|
+
node: ">=20"
|
|
29
|
+
},
|
|
30
|
+
scripts: {
|
|
31
|
+
test: "vitest run",
|
|
32
|
+
clean: "rm -rf dist .turbo node_modules",
|
|
33
|
+
build: "tsup --silent",
|
|
34
|
+
typecheck: "tsc --noEmit"
|
|
35
|
+
},
|
|
36
|
+
dependencies: {
|
|
37
|
+
"@solana/web3.js": "^1.98.2",
|
|
38
|
+
"@talismn/chaindata-provider": "workspace:*",
|
|
39
|
+
"@talismn/connection-meta": "workspace:*",
|
|
40
|
+
"@talismn/util": "workspace:*",
|
|
41
|
+
anylogger: "^1.0.11",
|
|
42
|
+
eventemitter3: "^5.0.0",
|
|
43
|
+
"lodash-es": "4.17.21",
|
|
44
|
+
viem: "^2.27.3"
|
|
45
|
+
},
|
|
46
|
+
devDependencies: {
|
|
47
|
+
"@polkadot/rpc-provider": "16.1.2",
|
|
48
|
+
"@polkadot/util": "13.5.3",
|
|
49
|
+
"@polkadot/x-global": "13.5.3",
|
|
50
|
+
"@polkadot/x-ws": "13.5.3",
|
|
51
|
+
"@talismn/tsconfig": "workspace:*",
|
|
52
|
+
typescript: "^5.6.3"
|
|
53
|
+
},
|
|
54
|
+
peerDependencies: {
|
|
55
|
+
"@polkadot/rpc-provider": "*",
|
|
56
|
+
"@polkadot/util": "*",
|
|
57
|
+
"@polkadot/x-global": "*",
|
|
58
|
+
"@polkadot/x-ws": "*"
|
|
59
|
+
},
|
|
60
|
+
types: "./dist/index.d.ts",
|
|
61
|
+
exports: {
|
|
62
|
+
".": {
|
|
63
|
+
"@talismn/source": "./src/index.ts",
|
|
64
|
+
import: {
|
|
65
|
+
types: "./dist/index.d.mts",
|
|
66
|
+
default: "./dist/index.mjs"
|
|
67
|
+
},
|
|
68
|
+
require: {
|
|
69
|
+
types: "./dist/index.d.ts",
|
|
70
|
+
default: "./dist/index.js"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
19
75
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
76
|
+
// src/log.ts
|
|
77
|
+
var log_default = anylogger(package_default.name);
|
|
78
|
+
|
|
79
|
+
// src/dot/Websocket.ts
|
|
80
|
+
import { RpcCoder } from "@polkadot/rpc-provider/coder";
|
|
81
|
+
import { getWSErrorString } from "@polkadot/rpc-provider/ws/errors";
|
|
82
|
+
import { isChildClass, isNull, isUndefined, objectSpread } from "@polkadot/util";
|
|
83
|
+
import { xglobal } from "@polkadot/x-global";
|
|
84
|
+
import { WebSocket } from "@polkadot/x-ws";
|
|
85
|
+
import EventEmitter from "eventemitter3";
|
|
86
|
+
|
|
87
|
+
// src/dot/helpers.ts
|
|
88
|
+
var twoSecondsMs = 2 * 1e3;
|
|
89
|
+
var twoMinutesMs = 2 * 60 * 1e3;
|
|
90
|
+
var ExponentialBackoff = class {
|
|
23
91
|
#minInterval;
|
|
24
92
|
#maxInterval;
|
|
25
93
|
#nextInterval = 0;
|
|
@@ -69,46 +137,25 @@ class ExponentialBackoff {
|
|
|
69
137
|
#capMax(value) {
|
|
70
138
|
return Math.min(this.#maxInterval, value);
|
|
71
139
|
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// to account for new requirement for generic arg in this type https://github.com/polkadot-js/api/commit/f4c2b150d3d69d43c56699613666b96dd0a763f4#diff-f87c17bc7fae027ec6d43bac5fc089614d9fa097f466aa2be333b44cee81f0fd
|
|
75
|
-
// TODO incrementally replace 'unknown' with proper types where possible
|
|
140
|
+
};
|
|
76
141
|
|
|
77
|
-
|
|
142
|
+
// src/dot/Websocket.ts
|
|
143
|
+
var ALIASES = {
|
|
78
144
|
chain_finalisedHead: "chain_finalizedHead",
|
|
79
145
|
chain_subscribeFinalisedHeads: "chain_subscribeFinalizedHeads",
|
|
80
146
|
chain_unsubscribeFinalisedHeads: "chain_unsubscribeFinalizedHeads"
|
|
81
147
|
};
|
|
82
|
-
|
|
83
|
-
|
|
148
|
+
var DEFAULT_TIMEOUT_MS = 60 * 1e3;
|
|
149
|
+
var TIMEOUT_INTERVAL = 5e3;
|
|
84
150
|
function eraseRecord(record, cb) {
|
|
85
|
-
Object.keys(record).forEach(key => {
|
|
151
|
+
Object.keys(record).forEach((key) => {
|
|
86
152
|
if (cb) {
|
|
87
153
|
cb(record[key]);
|
|
88
154
|
}
|
|
89
155
|
delete record[key];
|
|
90
156
|
});
|
|
91
157
|
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* # @talismn/chain-connector/Websocket
|
|
95
|
-
*
|
|
96
|
-
* @name Websocket
|
|
97
|
-
*
|
|
98
|
-
* @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes.
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* <BR>
|
|
102
|
-
*
|
|
103
|
-
* ```javascript
|
|
104
|
-
* import { Websocket } from '@talismn/chain-connector';
|
|
105
|
-
*
|
|
106
|
-
* const provider = new Websocket('ws://127.0.0.1:9944');
|
|
107
|
-
* ```
|
|
108
|
-
*
|
|
109
|
-
* @see [[HttpProvider]]
|
|
110
|
-
*/
|
|
111
|
-
class Websocket {
|
|
158
|
+
var Websocket = class _Websocket {
|
|
112
159
|
#coder;
|
|
113
160
|
#endpoints;
|
|
114
161
|
#headers;
|
|
@@ -124,7 +171,6 @@ class Websocket {
|
|
|
124
171
|
#timeoutId = null;
|
|
125
172
|
#websocket;
|
|
126
173
|
#timeout;
|
|
127
|
-
|
|
128
174
|
/**
|
|
129
175
|
* @param {string | string[]} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`, may provide an array of endpoint strings.
|
|
130
176
|
* @param {Record<string, string>} headers The headers provided to the underlying WebSocket
|
|
@@ -135,9 +181,9 @@ class Websocket {
|
|
|
135
181
|
if (endpoints.length === 0) {
|
|
136
182
|
throw new Error("Websocket requires at least one Endpoint");
|
|
137
183
|
}
|
|
138
|
-
endpoints.forEach(
|
|
139
|
-
if (!/^(wss|ws):\/\//.test(
|
|
140
|
-
throw new Error(`Endpoint should start with 'ws://', received '${
|
|
184
|
+
endpoints.forEach((endpoint2) => {
|
|
185
|
+
if (!/^(wss|ws):\/\//.test(endpoint2)) {
|
|
186
|
+
throw new Error(`Endpoint should start with 'ws://', received '${endpoint2}'`);
|
|
141
187
|
}
|
|
142
188
|
});
|
|
143
189
|
this.#eventemitter = new EventEmitter();
|
|
@@ -151,30 +197,26 @@ class Websocket {
|
|
|
151
197
|
this.#timeout = timeout || DEFAULT_TIMEOUT_MS;
|
|
152
198
|
if (this.#autoConnectBackoff.isActive) {
|
|
153
199
|
this.connectWithRetry().catch(() => {
|
|
154
|
-
// does not throw
|
|
155
200
|
});
|
|
156
201
|
}
|
|
157
|
-
this.#isReadyPromise = new Promise(resolve => {
|
|
202
|
+
this.#isReadyPromise = new Promise((resolve) => {
|
|
158
203
|
this.#eventemitter.once("connected", () => {
|
|
159
204
|
resolve(this);
|
|
160
205
|
});
|
|
161
206
|
});
|
|
162
207
|
}
|
|
163
|
-
|
|
164
208
|
/**
|
|
165
209
|
* @summary `true` when this provider supports subscriptions
|
|
166
210
|
*/
|
|
167
211
|
get hasSubscriptions() {
|
|
168
212
|
return true;
|
|
169
213
|
}
|
|
170
|
-
|
|
171
214
|
/**
|
|
172
215
|
* @summary `true` when this provider supports clone()
|
|
173
216
|
*/
|
|
174
217
|
get isClonable() {
|
|
175
218
|
return true;
|
|
176
219
|
}
|
|
177
|
-
|
|
178
220
|
/**
|
|
179
221
|
* @summary Whether the node is connected or not.
|
|
180
222
|
* @return {boolean} true if connected
|
|
@@ -182,7 +224,6 @@ class Websocket {
|
|
|
182
224
|
get isConnected() {
|
|
183
225
|
return this.#isConnected;
|
|
184
226
|
}
|
|
185
|
-
|
|
186
227
|
/**
|
|
187
228
|
* @description Promise that resolves the first time we are connected and loaded
|
|
188
229
|
*/
|
|
@@ -192,54 +233,46 @@ class Websocket {
|
|
|
192
233
|
get endpoint() {
|
|
193
234
|
return this.#endpoints[this.#endpointIndex];
|
|
194
235
|
}
|
|
195
|
-
|
|
196
236
|
/**
|
|
197
237
|
* @description Returns a clone of the object
|
|
198
238
|
*/
|
|
199
239
|
clone() {
|
|
200
|
-
return new
|
|
240
|
+
return new _Websocket(this.#endpoints);
|
|
201
241
|
}
|
|
202
242
|
selectEndpointIndex(endpoints) {
|
|
203
243
|
this.#endpointsTriedSinceLastConnection += 1;
|
|
204
244
|
return (this.#endpointIndex + 1) % endpoints.length;
|
|
205
245
|
}
|
|
206
|
-
|
|
207
246
|
/**
|
|
208
247
|
* @summary Manually connect
|
|
209
248
|
* @description The [[Websocket]] connects automatically by default, however if you decided otherwise, you may
|
|
210
249
|
* connect manually using this method.
|
|
211
250
|
*/
|
|
212
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
213
251
|
async connect() {
|
|
214
252
|
if (this.#websocket) {
|
|
215
253
|
throw new Error("WebSocket is already connected");
|
|
216
254
|
}
|
|
217
255
|
try {
|
|
218
256
|
this.#endpointIndex = this.selectEndpointIndex(this.#endpoints);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
headers: this.#headers
|
|
226
|
-
});
|
|
257
|
+
this.#websocket = typeof xglobal.WebSocket !== "undefined" && isChildClass(xglobal.WebSocket, WebSocket) ? new WebSocket(this.endpoint) : (
|
|
258
|
+
// @ts-expect-error - WS may be an instance of ws, which supports options
|
|
259
|
+
new WebSocket(this.endpoint, void 0, {
|
|
260
|
+
headers: this.#headers
|
|
261
|
+
})
|
|
262
|
+
);
|
|
227
263
|
if (this.#websocket) {
|
|
228
264
|
this.#websocket.onclose = this.#onSocketClose;
|
|
229
265
|
this.#websocket.onerror = this.#onSocketError;
|
|
230
266
|
this.#websocket.onmessage = this.#onSocketMessage;
|
|
231
267
|
this.#websocket.onopen = this.#onSocketOpen;
|
|
232
268
|
}
|
|
233
|
-
|
|
234
|
-
// timeout any handlers that have not had a response
|
|
235
269
|
this.#timeoutId = setInterval(() => this.#timeoutHandlers(), TIMEOUT_INTERVAL);
|
|
236
270
|
} catch (error) {
|
|
237
|
-
|
|
271
|
+
log_default.error(error);
|
|
238
272
|
this.#emit("error", error);
|
|
239
273
|
throw error;
|
|
240
274
|
}
|
|
241
275
|
}
|
|
242
|
-
|
|
243
276
|
/**
|
|
244
277
|
* @description Connect, never throwing an error, but rather forcing a retry
|
|
245
278
|
*/
|
|
@@ -247,48 +280,39 @@ class Websocket {
|
|
|
247
280
|
if (!this.#autoConnectBackoff.isActive) return;
|
|
248
281
|
try {
|
|
249
282
|
await this.connect();
|
|
250
|
-
} catch
|
|
283
|
+
} catch {
|
|
251
284
|
this.scheduleNextRetry();
|
|
252
285
|
}
|
|
253
286
|
}
|
|
254
287
|
scheduleNextRetry() {
|
|
255
288
|
if (!this.#autoConnectBackoff.isActive) return;
|
|
256
289
|
const haveTriedAllEndpoints = this.#endpointsTriedSinceLastConnection > 0 && this.#endpointsTriedSinceLastConnection % this.#endpoints.length === 0;
|
|
257
|
-
setTimeout(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
290
|
+
setTimeout(
|
|
291
|
+
() => {
|
|
292
|
+
this.connectWithRetry().catch(() => {
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
haveTriedAllEndpoints ? this.#autoConnectBackoff.next : 0
|
|
296
|
+
);
|
|
264
297
|
if (haveTriedAllEndpoints) this.#autoConnectBackoff.increase();
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// but haven't successfully connected to any of them
|
|
268
|
-
if (haveTriedAllEndpoints) this.#emit("stale-rpcs", {
|
|
269
|
-
nextBackoffInterval: this.#autoConnectBackoff.next
|
|
270
|
-
});
|
|
298
|
+
if (haveTriedAllEndpoints)
|
|
299
|
+
this.#emit("stale-rpcs", { nextBackoffInterval: this.#autoConnectBackoff.next });
|
|
271
300
|
}
|
|
272
|
-
|
|
273
301
|
/**
|
|
274
302
|
* @description Manually disconnect from the connection, clearing auto-connect logic
|
|
275
303
|
*/
|
|
276
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
277
304
|
async disconnect() {
|
|
278
|
-
// switch off autoConnect, we are in manual mode now
|
|
279
305
|
this.#autoConnectBackoff.disable();
|
|
280
306
|
try {
|
|
281
307
|
if (this.#websocket) {
|
|
282
|
-
|
|
283
|
-
this.#websocket.close(1000);
|
|
308
|
+
this.#websocket.close(1e3);
|
|
284
309
|
}
|
|
285
310
|
} catch (error) {
|
|
286
|
-
|
|
311
|
+
log_default.error(error);
|
|
287
312
|
this.#emit("error", error);
|
|
288
313
|
throw error;
|
|
289
314
|
}
|
|
290
315
|
}
|
|
291
|
-
|
|
292
316
|
/**
|
|
293
317
|
* @summary Listens on events after having subscribed using the [[subscribe]] function.
|
|
294
318
|
* @param {ProviderInterfaceEmitted} type Event
|
|
@@ -301,16 +325,14 @@ class Websocket {
|
|
|
301
325
|
this.#eventemitter.removeListener(type, sub);
|
|
302
326
|
};
|
|
303
327
|
}
|
|
304
|
-
|
|
305
328
|
/**
|
|
306
329
|
* @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue.
|
|
307
330
|
* @param method The RPC methods to execute
|
|
308
331
|
* @param params Encoded parameters as applicable for the method
|
|
309
332
|
* @param subscription Subscription details (internally used)
|
|
310
333
|
*/
|
|
311
|
-
//
|
|
312
|
-
send(method, params,
|
|
313
|
-
isCacheable, subscription) {
|
|
334
|
+
// biome-ignore lint/suspicious/noExplicitAny: legacy
|
|
335
|
+
send(method, params, _isCacheable, subscription) {
|
|
314
336
|
const [id, body] = this.#coder.encodeJson(method, params);
|
|
315
337
|
const resultPromise = this.#send(id, body, method, params, subscription);
|
|
316
338
|
return resultPromise;
|
|
@@ -324,9 +346,6 @@ class Websocket {
|
|
|
324
346
|
const callback = (error, result) => {
|
|
325
347
|
error ? reject(error) : resolve(result);
|
|
326
348
|
};
|
|
327
|
-
|
|
328
|
-
// log.debug(() => ["calling", method, body])
|
|
329
|
-
|
|
330
349
|
this.#handlers[id] = {
|
|
331
350
|
callback,
|
|
332
351
|
method,
|
|
@@ -340,7 +359,6 @@ class Websocket {
|
|
|
340
359
|
}
|
|
341
360
|
});
|
|
342
361
|
}
|
|
343
|
-
|
|
344
362
|
/**
|
|
345
363
|
* @name subscribe
|
|
346
364
|
* @summary Allows subscribing to a specific event.
|
|
@@ -360,42 +378,32 @@ class Websocket {
|
|
|
360
378
|
* ```
|
|
361
379
|
*/
|
|
362
380
|
subscribe(type, method, params, callback) {
|
|
363
|
-
return this.send(method, params, false, {
|
|
364
|
-
callback,
|
|
365
|
-
type
|
|
366
|
-
});
|
|
381
|
+
return this.send(method, params, false, { callback, type });
|
|
367
382
|
}
|
|
368
|
-
|
|
369
383
|
/**
|
|
370
384
|
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
|
|
371
385
|
*/
|
|
372
386
|
async unsubscribe(type, method, id) {
|
|
373
387
|
const subscription = `${type}::${id}`;
|
|
374
|
-
|
|
375
|
-
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
|
|
376
|
-
// the assigned id now does not match what the API user originally received. It has
|
|
377
|
-
// a slight complication in solving - since we cannot rely on the send id, but rather
|
|
378
|
-
// need to find the actual subscription id to map it
|
|
379
388
|
if (isUndefined(this.#subscriptions[subscription])) {
|
|
380
|
-
// log.debug(() => `Unable to find active subscription=${subscription}`)
|
|
381
|
-
|
|
382
389
|
return false;
|
|
383
390
|
}
|
|
384
391
|
delete this.#subscriptions[subscription];
|
|
385
392
|
try {
|
|
386
393
|
return this.isConnected && !isNull(this.#websocket) ? this.send(method, [id]) : true;
|
|
387
|
-
} catch
|
|
394
|
+
} catch {
|
|
388
395
|
return false;
|
|
389
396
|
}
|
|
390
397
|
}
|
|
391
398
|
#emit = (type, ...args) => {
|
|
392
399
|
this.#eventemitter.emit(type, ...args);
|
|
393
400
|
};
|
|
394
|
-
#onSocketClose = event => {
|
|
395
|
-
const error = new Error(
|
|
401
|
+
#onSocketClose = (event) => {
|
|
402
|
+
const error = new Error(
|
|
403
|
+
`disconnected from ${this.endpoint}: ${event.code}:: ${event.reason || getWSErrorString(event.code)}`
|
|
404
|
+
);
|
|
396
405
|
if (this.#autoConnectBackoff.isActive) {
|
|
397
|
-
|
|
398
|
-
if (event.code !== 1000) log.error(error.message);
|
|
406
|
+
if (event.code !== 1e3) log_default.error(error.message);
|
|
399
407
|
}
|
|
400
408
|
this.#isConnected = false;
|
|
401
409
|
if (this.#websocket) {
|
|
@@ -409,52 +417,36 @@ class Websocket {
|
|
|
409
417
|
clearInterval(this.#timeoutId);
|
|
410
418
|
this.#timeoutId = null;
|
|
411
419
|
}
|
|
412
|
-
|
|
413
|
-
// reject all hanging requests
|
|
414
|
-
eraseRecord(this.#handlers, h => {
|
|
420
|
+
eraseRecord(this.#handlers, (h) => {
|
|
415
421
|
try {
|
|
416
|
-
h.callback(error,
|
|
422
|
+
h.callback(error, void 0);
|
|
417
423
|
} catch (err) {
|
|
418
|
-
|
|
419
|
-
log.error(err);
|
|
424
|
+
log_default.error(err);
|
|
420
425
|
}
|
|
421
426
|
});
|
|
422
427
|
eraseRecord(this.#waitingForId);
|
|
423
428
|
this.#emit("disconnected");
|
|
424
429
|
this.scheduleNextRetry();
|
|
425
430
|
};
|
|
426
|
-
#onSocketError = error => {
|
|
427
|
-
// log.debug(() => ["socket error", error])
|
|
431
|
+
#onSocketError = (error) => {
|
|
428
432
|
this.#emit("error", error);
|
|
429
433
|
};
|
|
430
|
-
#onSocketMessage = message => {
|
|
431
|
-
// log.debug(() => ["received", message.data])
|
|
434
|
+
#onSocketMessage = (message) => {
|
|
432
435
|
try {
|
|
433
436
|
const response = JSON.parse(message.data);
|
|
434
437
|
return isUndefined(response.method) ? this.#onSocketMessageResult(response) : this.#onSocketMessageSubscribe(response);
|
|
435
438
|
} catch (e) {
|
|
436
|
-
this.#emit("error", new Error("Invalid websocket message received", {
|
|
437
|
-
cause: e
|
|
438
|
-
}));
|
|
439
|
+
this.#emit("error", new Error("Invalid websocket message received", { cause: e }));
|
|
439
440
|
}
|
|
440
441
|
};
|
|
441
|
-
#onSocketMessageResult = response => {
|
|
442
|
+
#onSocketMessageResult = (response) => {
|
|
442
443
|
const handler = this.#handlers[response.id];
|
|
443
444
|
if (!handler) {
|
|
444
|
-
// log.debug(() => `Unable to find handler for id=${response.id}`)
|
|
445
|
-
|
|
446
445
|
return;
|
|
447
446
|
}
|
|
448
447
|
try {
|
|
449
|
-
const {
|
|
450
|
-
method,
|
|
451
|
-
params,
|
|
452
|
-
subscription
|
|
453
|
-
} = handler;
|
|
448
|
+
const { method, params, subscription } = handler;
|
|
454
449
|
const result = this.#coder.decodeResponse(response);
|
|
455
|
-
|
|
456
|
-
// first send the result - in case of subs, we may have an update
|
|
457
|
-
// immediately if we have some queued results already
|
|
458
450
|
handler.callback(null, result);
|
|
459
451
|
if (subscription) {
|
|
460
452
|
const subId = `${subscription.type}::${result}`;
|
|
@@ -462,46 +454,35 @@ class Websocket {
|
|
|
462
454
|
method,
|
|
463
455
|
params
|
|
464
456
|
});
|
|
465
|
-
|
|
466
|
-
// if we have a result waiting for this subscription already
|
|
467
457
|
if (this.#waitingForId[subId]) {
|
|
468
458
|
this.#onSocketMessageSubscribe(this.#waitingForId[subId]);
|
|
469
459
|
}
|
|
470
460
|
}
|
|
471
461
|
} catch (error) {
|
|
472
|
-
handler.callback(error,
|
|
462
|
+
handler.callback(error, void 0);
|
|
473
463
|
}
|
|
474
464
|
delete this.#handlers[response.id];
|
|
475
465
|
};
|
|
476
|
-
#onSocketMessageSubscribe = response => {
|
|
466
|
+
#onSocketMessageSubscribe = (response) => {
|
|
477
467
|
const method = ALIASES[response.method] || response.method || "invalid";
|
|
478
468
|
const subId = `${method}::${response.params.subscription}`;
|
|
479
469
|
const handler = this.#subscriptions[subId];
|
|
480
470
|
if (!handler) {
|
|
481
|
-
// store the JSON, we could have out-of-order subid coming in
|
|
482
471
|
this.#waitingForId[subId] = response;
|
|
483
|
-
|
|
484
|
-
// log.debug(() => `Unable to find handler for subscription=${subId}`)
|
|
485
|
-
|
|
486
472
|
return;
|
|
487
473
|
}
|
|
488
|
-
|
|
489
|
-
// housekeeping
|
|
490
474
|
delete this.#waitingForId[subId];
|
|
491
475
|
try {
|
|
492
476
|
const result = this.#coder.decodeResponse(response);
|
|
493
477
|
handler.callback(null, result);
|
|
494
478
|
} catch (error) {
|
|
495
|
-
handler.callback(error,
|
|
479
|
+
handler.callback(error, void 0);
|
|
496
480
|
}
|
|
497
481
|
};
|
|
498
482
|
#onSocketOpen = () => {
|
|
499
483
|
if (this.#websocket === null) {
|
|
500
484
|
throw new Error("WebSocket cannot be null in onOpen");
|
|
501
485
|
}
|
|
502
|
-
|
|
503
|
-
// log.debug(() => ["connected to", this.endpoint])
|
|
504
|
-
|
|
505
486
|
this.#isConnected = true;
|
|
506
487
|
this.#endpointsTriedSinceLastConnection = 0;
|
|
507
488
|
this.#autoConnectBackoff.reset();
|
|
@@ -512,26 +493,19 @@ class Websocket {
|
|
|
512
493
|
#resubscribe = () => {
|
|
513
494
|
const subscriptions = this.#subscriptions;
|
|
514
495
|
this.#subscriptions = {};
|
|
515
|
-
Promise.all(
|
|
516
|
-
|
|
517
|
-
callback,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
try {
|
|
530
|
-
await this.subscribe(type, method, params, callback);
|
|
531
|
-
} catch (error) {
|
|
532
|
-
log.error(error);
|
|
533
|
-
}
|
|
534
|
-
})).catch(log.error);
|
|
496
|
+
Promise.all(
|
|
497
|
+
Object.keys(subscriptions).map(async (id) => {
|
|
498
|
+
const { callback, method, params, type } = subscriptions[id];
|
|
499
|
+
if (type.startsWith("author_")) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
await this.subscribe(type, method, params, callback);
|
|
504
|
+
} catch (error) {
|
|
505
|
+
log_default.error(error);
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
).catch(log_default.error);
|
|
535
509
|
};
|
|
536
510
|
#timeoutHandlers = () => {
|
|
537
511
|
const now = Date.now();
|
|
@@ -540,63 +514,65 @@ class Websocket {
|
|
|
540
514
|
const handler = this.#handlers[ids[i]];
|
|
541
515
|
if (now - handler.start > this.#timeout) {
|
|
542
516
|
try {
|
|
543
|
-
handler.callback(
|
|
517
|
+
handler.callback(
|
|
518
|
+
new Error(`No response received from RPC endpoint in ${this.#timeout / 1e3}s`),
|
|
519
|
+
void 0
|
|
520
|
+
);
|
|
544
521
|
} catch {
|
|
545
|
-
// ignore
|
|
546
522
|
}
|
|
547
523
|
delete this.#handlers[ids[i]];
|
|
548
524
|
}
|
|
549
525
|
}
|
|
550
526
|
};
|
|
551
|
-
}
|
|
527
|
+
};
|
|
552
528
|
|
|
553
|
-
//
|
|
554
|
-
|
|
555
|
-
const BAD_RPC_ERRORS = {
|
|
529
|
+
// src/dot/ChainConnectorDot.ts
|
|
530
|
+
var BAD_RPC_ERRORS = {
|
|
556
531
|
"-32097": "Rate limit exceeded",
|
|
557
532
|
"-32098": "Capacity exceeded"
|
|
558
533
|
};
|
|
559
|
-
|
|
534
|
+
var ChainConnectionError = class extends Error {
|
|
535
|
+
type;
|
|
536
|
+
chainId;
|
|
560
537
|
constructor(chainId, options) {
|
|
561
538
|
super(`Unable to connect to chain ${chainId}`, options);
|
|
562
539
|
this.type = "CHAIN_CONNECTION_ERROR";
|
|
563
540
|
this.chainId = chainId;
|
|
564
541
|
}
|
|
565
|
-
}
|
|
566
|
-
|
|
542
|
+
};
|
|
543
|
+
var StaleRpcError = class extends Error {
|
|
544
|
+
type;
|
|
545
|
+
chainId;
|
|
567
546
|
constructor(chainId, options) {
|
|
568
547
|
super(`RPCs are stale/unavailable for chain ${chainId}`, options);
|
|
569
548
|
this.type = "STALE_RPC_ERROR";
|
|
570
549
|
this.chainId = chainId;
|
|
571
550
|
}
|
|
572
|
-
}
|
|
573
|
-
|
|
551
|
+
};
|
|
552
|
+
var WebsocketAllocationExhaustedError = class extends Error {
|
|
553
|
+
type;
|
|
554
|
+
chainId;
|
|
574
555
|
constructor(chainId, options) {
|
|
575
|
-
super(
|
|
556
|
+
super(
|
|
557
|
+
`No websockets are available from the browser pool to connect to chain ${chainId}`,
|
|
558
|
+
options
|
|
559
|
+
);
|
|
576
560
|
this.type = "WEBSOCKET_ALLOCATION_EXHAUSTED_ERROR";
|
|
577
561
|
this.chainId = chainId;
|
|
578
562
|
}
|
|
579
|
-
}
|
|
580
|
-
|
|
563
|
+
};
|
|
564
|
+
var CallerUnsubscribedError = class extends Error {
|
|
565
|
+
type;
|
|
566
|
+
chainId;
|
|
567
|
+
unsubscribeMethod;
|
|
581
568
|
constructor(chainId, unsubscribeMethod, options) {
|
|
582
569
|
super(`Caller unsubscribed from ${chainId}`, options);
|
|
583
570
|
this.type = "CALLER_UNSUBSCRIBED_ERROR";
|
|
584
571
|
this.chainId = chainId;
|
|
585
572
|
this.unsubscribeMethod = unsubscribeMethod;
|
|
586
573
|
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
* ChainConnector provides an interface similar to WsProvider, but with three points of difference:
|
|
590
|
-
*
|
|
591
|
-
* 1. ChainConnector methods all accept a `chainId` instead of an array of RPCs. RPCs are then fetched internally from chaindata.
|
|
592
|
-
* 2. ChainConnector creates only one `WsProvider` per chain and ensures that all downstream requests to a chain share the one socket connection.
|
|
593
|
-
* 3. Subscriptions return a callable `unsubscribe` method instead of an id.
|
|
594
|
-
*
|
|
595
|
-
* Additionally, when run on the clientside of a dapp where `window.talismanSub` is available, instead of spinning up new websocket
|
|
596
|
-
* connections this class will forward all requests through to the wallet backend - where another instance of this class will
|
|
597
|
-
* handle the websocket connections.
|
|
598
|
-
*/
|
|
599
|
-
class ChainConnectorDot {
|
|
574
|
+
};
|
|
575
|
+
var ChainConnectorDot = class {
|
|
600
576
|
#chaindataChainProvider;
|
|
601
577
|
#connectionMetaDb;
|
|
602
578
|
#socketConnections = {};
|
|
@@ -606,14 +582,12 @@ class ChainConnectorDot {
|
|
|
606
582
|
this.#chaindataChainProvider = chaindataChainProvider;
|
|
607
583
|
this.#connectionMetaDb = connectionMetaDb;
|
|
608
584
|
if (this.#connectionMetaDb) {
|
|
609
|
-
this.#chaindataChainProvider.getNetworkIds("polkadot").then(chainIds => {
|
|
610
|
-
// tidy up connectionMeta for chains which no longer exist
|
|
585
|
+
this.#chaindataChainProvider.getNetworkIds("polkadot").then((chainIds) => {
|
|
611
586
|
this.#connectionMetaDb?.chainPriorityRpcs.where("id").noneOf(chainIds).delete();
|
|
612
587
|
this.#connectionMetaDb?.chainBackoffInterval.where("id").noneOf(chainIds).delete();
|
|
613
588
|
});
|
|
614
589
|
}
|
|
615
590
|
}
|
|
616
|
-
|
|
617
591
|
/**
|
|
618
592
|
* Creates a facade over this ChainConnector which conforms to the PJS ProviderInterface
|
|
619
593
|
* @example // Using a chainConnector as a Provider for an ApiPromise
|
|
@@ -621,7 +595,7 @@ class ChainConnectorDot {
|
|
|
621
595
|
* const api = new ApiPromise({ provider })
|
|
622
596
|
*/
|
|
623
597
|
asProvider(chainId) {
|
|
624
|
-
const unsubHandler = new Map();
|
|
598
|
+
const unsubHandler = /* @__PURE__ */ new Map();
|
|
625
599
|
const providerFacade = {
|
|
626
600
|
hasSubscriptions: true,
|
|
627
601
|
isClonable: false,
|
|
@@ -629,12 +603,15 @@ class ChainConnectorDot {
|
|
|
629
603
|
clone: () => providerFacade,
|
|
630
604
|
connect: () => Promise.resolve(),
|
|
631
605
|
disconnect: () => Promise.resolve(),
|
|
632
|
-
on: () => () => {
|
|
633
|
-
|
|
606
|
+
on: () => () => {
|
|
607
|
+
},
|
|
608
|
+
// biome-ignore lint/suspicious/noExplicitAny: legacy
|
|
634
609
|
send: async (method, params, isCacheable) => await this.send(chainId, method, params, isCacheable),
|
|
635
610
|
subscribe: async (type, method, params, cb) => {
|
|
636
611
|
const unsubscribe = await this.subscribe(chainId, method, type, params, cb);
|
|
637
|
-
const subscriptionId = this.getExclusiveRandomId(
|
|
612
|
+
const subscriptionId = this.getExclusiveRandomId(
|
|
613
|
+
[...unsubHandler.keys()].map(Number)
|
|
614
|
+
).toString();
|
|
638
615
|
unsubHandler.set(subscriptionId, unsubscribe);
|
|
639
616
|
return subscriptionId;
|
|
640
617
|
},
|
|
@@ -646,141 +623,122 @@ class ChainConnectorDot {
|
|
|
646
623
|
};
|
|
647
624
|
return providerFacade;
|
|
648
625
|
}
|
|
649
|
-
|
|
650
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
626
|
+
// biome-ignore lint/suspicious/noExplicitAny: legacy
|
|
651
627
|
async send(chainId, method, params, isCacheable, extraOptions) {
|
|
652
628
|
const talismanSub = this.getTalismanSub();
|
|
653
|
-
if (talismanSub !==
|
|
629
|
+
if (talismanSub !== void 0) {
|
|
654
630
|
try {
|
|
655
631
|
const chain = await this.#chaindataChainProvider.getNetworkById(chainId, "polkadot");
|
|
656
632
|
if (!chain) throw new Error(`Chain ${chainId} not found in store`);
|
|
657
|
-
const {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (typeof genesisHash !== "string") throw new Error(`Chain ${chainId} has no genesisHash in store`);
|
|
633
|
+
const { genesisHash } = chain;
|
|
634
|
+
if (typeof genesisHash !== "string")
|
|
635
|
+
throw new Error(`Chain ${chainId} has no genesisHash in store`);
|
|
661
636
|
return await talismanSub.send(genesisHash, method, params);
|
|
662
637
|
} catch (error) {
|
|
663
|
-
|
|
638
|
+
log_default.warn(
|
|
639
|
+
`Failed to make wallet-proxied send request for chain ${chainId}. Falling back to plain websocket`,
|
|
640
|
+
error
|
|
641
|
+
);
|
|
664
642
|
}
|
|
665
643
|
}
|
|
666
644
|
try {
|
|
667
|
-
// eslint-disable-next-line no-var
|
|
668
645
|
var [socketUserId, ws] = await this.connectChainSocket(chainId);
|
|
669
646
|
} catch (error) {
|
|
670
|
-
throw new StaleRpcError(chainId, {
|
|
671
|
-
cause: error
|
|
672
|
-
});
|
|
647
|
+
throw new StaleRpcError(chainId, { cause: error });
|
|
673
648
|
}
|
|
674
649
|
try {
|
|
675
|
-
|
|
676
|
-
const timeout = 15_000; // 15 seconds in milliseconds
|
|
650
|
+
const timeout = 15e3;
|
|
677
651
|
await this.waitForWs(ws, timeout);
|
|
678
652
|
} catch (error) {
|
|
679
653
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
680
|
-
throw new ChainConnectionError(chainId, {
|
|
681
|
-
cause: error
|
|
682
|
-
});
|
|
654
|
+
throw new ChainConnectionError(chainId, { cause: error });
|
|
683
655
|
}
|
|
684
656
|
try {
|
|
685
|
-
const timeout =
|
|
686
|
-
|
|
687
|
-
|
|
657
|
+
const timeout = 3e4;
|
|
658
|
+
var response = await Promise.race([
|
|
659
|
+
ws.send(method, params, isCacheable),
|
|
660
|
+
throwAfter(timeout, "TIMEOUT")
|
|
661
|
+
]);
|
|
688
662
|
} catch (err) {
|
|
689
663
|
const error = err;
|
|
690
664
|
if (error?.message === "TIMEOUT") {
|
|
691
|
-
|
|
692
|
-
chainId,
|
|
693
|
-
endpoint: ws.endpoint,
|
|
694
|
-
error
|
|
695
|
-
});
|
|
665
|
+
log_default.error(`ChainConnector timeout`, { chainId, endpoint: ws.endpoint, error });
|
|
696
666
|
await this.updateRpcPriority(chainId, ws.endpoint, "last");
|
|
697
667
|
await this.reset(chainId);
|
|
698
668
|
throw new Error("Timeout");
|
|
699
669
|
}
|
|
700
670
|
const badRpcError = BAD_RPC_ERRORS[error?.code?.toString() ?? ""];
|
|
701
671
|
if (badRpcError) {
|
|
702
|
-
|
|
703
|
-
error,
|
|
704
|
-
chainId,
|
|
705
|
-
endpoint: ws.endpoint
|
|
706
|
-
});
|
|
672
|
+
log_default.error(`ChainConnector ${badRpcError}`, { error, chainId, endpoint: ws.endpoint });
|
|
707
673
|
await this.updateRpcPriority(chainId, ws.endpoint, "last");
|
|
708
674
|
await this.reset(chainId);
|
|
709
675
|
throw new Error(badRpcError);
|
|
710
676
|
}
|
|
711
|
-
if (!extraOptions?.expectErrors)
|
|
712
|
-
error
|
|
713
|
-
|
|
714
|
-
|
|
677
|
+
if (!extraOptions?.expectErrors)
|
|
678
|
+
log_default.error(
|
|
679
|
+
`Failed to send ${method} on chain ${chainId}
|
|
680
|
+
params: ${JSON.stringify(params)}`,
|
|
681
|
+
{
|
|
682
|
+
error,
|
|
683
|
+
endpoint: ws.endpoint
|
|
684
|
+
}
|
|
685
|
+
);
|
|
715
686
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
716
687
|
throw error;
|
|
717
688
|
}
|
|
718
689
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
719
690
|
return response;
|
|
720
691
|
}
|
|
721
|
-
async subscribe(chainId, subscribeMethod, responseMethod, params, callback, timeout =
|
|
722
|
-
) {
|
|
692
|
+
async subscribe(chainId, subscribeMethod, responseMethod, params, callback, timeout = 3e4) {
|
|
723
693
|
const talismanSub = this.getTalismanSub();
|
|
724
|
-
if (talismanSub !==
|
|
694
|
+
if (talismanSub !== void 0) {
|
|
725
695
|
try {
|
|
726
696
|
const chain = await this.#chaindataChainProvider.getNetworkById(chainId, "polkadot");
|
|
727
697
|
if (!chain) throw new Error(`Chain ${chainId} not found in store`);
|
|
728
|
-
const {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
698
|
+
const { genesisHash } = chain;
|
|
699
|
+
if (typeof genesisHash !== "string")
|
|
700
|
+
throw new Error(`Chain ${chainId} has no genesisHash in store`);
|
|
701
|
+
const subscriptionId = await talismanSub.subscribe(
|
|
702
|
+
genesisHash,
|
|
703
|
+
subscribeMethod,
|
|
704
|
+
responseMethod,
|
|
705
|
+
params,
|
|
706
|
+
callback,
|
|
707
|
+
timeout
|
|
708
|
+
);
|
|
709
|
+
return (unsubscribeMethod) => talismanSub.unsubscribe(subscriptionId, unsubscribeMethod);
|
|
734
710
|
} catch (error) {
|
|
735
|
-
|
|
711
|
+
log_default.warn(
|
|
712
|
+
`Failed to create wallet-proxied subscription for chain ${chainId}. Falling back to plain websocket`,
|
|
713
|
+
error
|
|
714
|
+
);
|
|
736
715
|
}
|
|
737
716
|
}
|
|
738
717
|
try {
|
|
739
|
-
// eslint-disable-next-line no-var
|
|
740
718
|
var [socketUserId, ws] = await this.connectChainSocket(chainId);
|
|
741
719
|
} catch (error) {
|
|
742
|
-
throw new StaleRpcError(chainId, {
|
|
743
|
-
cause: error
|
|
744
|
-
});
|
|
720
|
+
throw new StaleRpcError(chainId, { cause: error });
|
|
745
721
|
}
|
|
746
|
-
|
|
747
|
-
// by using this `Deferred` promise
|
|
748
|
-
// (a promise which can be resolved or rejected by code outside of the scope of the promise's constructor)
|
|
749
|
-
// we can queue up our async cleanup on the promise and then immediately return an unsubscribe method to the caller
|
|
750
722
|
const unsubDeferred = Deferred();
|
|
751
|
-
|
|
752
|
-
const unsubscribe = unsubscribeMethod => unsubDeferred.reject(new CallerUnsubscribedError(chainId, unsubscribeMethod));
|
|
753
|
-
// we queue up our work to clean up our subscription when this promise rejects
|
|
723
|
+
const unsubscribe = (unsubscribeMethod) => unsubDeferred.reject(new CallerUnsubscribedError(chainId, unsubscribeMethod));
|
|
754
724
|
const callerUnsubscribed = unsubDeferred.promise;
|
|
755
|
-
|
|
756
|
-
// used to detect when there are no more websockets available from the browser websocket pool
|
|
757
|
-
// in this scenario, we'll be waiting for ws.isReady until some existing sockets are closed
|
|
758
|
-
//
|
|
759
|
-
// while we're waiting, we'll send an error back to the caller so that they can show some useful
|
|
760
|
-
// info to the user
|
|
761
|
-
let noMoreSocketsTimeout = undefined
|
|
762
|
-
|
|
763
|
-
// create subscription asynchronously so that the caller can unsubscribe without waiting for
|
|
764
|
-
// the subscription to be created (which can take some time if e.g. the connection can't be established)
|
|
765
|
-
;
|
|
725
|
+
let noMoreSocketsTimeout;
|
|
766
726
|
(async () => {
|
|
767
|
-
// wait for ws to be ready, but don't wait forever
|
|
768
|
-
// if timeout is number, cancel when timeout is reached (or caller unsubscribes)
|
|
769
|
-
// if timeout is false, only cancel when the caller unsubscribes
|
|
770
727
|
let unsubRpcStatus = null;
|
|
771
728
|
try {
|
|
772
|
-
const unsubStale = ws.on(
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
729
|
+
const unsubStale = ws.on(
|
|
730
|
+
"stale-rpcs",
|
|
731
|
+
({ nextBackoffInterval } = {}) => {
|
|
732
|
+
callback(new StaleRpcError(chainId), null);
|
|
733
|
+
if (this.#connectionMetaDb && nextBackoffInterval) {
|
|
734
|
+
const id = chainId;
|
|
735
|
+
this.#connectionMetaDb.chainBackoffInterval.put(
|
|
736
|
+
{ id, interval: nextBackoffInterval },
|
|
737
|
+
id
|
|
738
|
+
);
|
|
739
|
+
}
|
|
782
740
|
}
|
|
783
|
-
|
|
741
|
+
);
|
|
784
742
|
const unsubConnected = ws.on("connected", () => {
|
|
785
743
|
if (this.#connectionMetaDb) this.#connectionMetaDb.chainBackoffInterval.delete(chainId);
|
|
786
744
|
});
|
|
@@ -788,57 +746,58 @@ class ChainConnectorDot {
|
|
|
788
746
|
unsubStale();
|
|
789
747
|
unsubConnected();
|
|
790
748
|
};
|
|
791
|
-
noMoreSocketsTimeout = setTimeout(
|
|
749
|
+
noMoreSocketsTimeout = setTimeout(
|
|
750
|
+
() => callback(new WebsocketAllocationExhaustedError(chainId), null),
|
|
751
|
+
3e4
|
|
752
|
+
// 30 seconds in ms
|
|
792
753
|
);
|
|
793
|
-
if (timeout) await Promise.race([this.waitForWs(ws, timeout), callerUnsubscribed]);
|
|
754
|
+
if (timeout) await Promise.race([this.waitForWs(ws, timeout), callerUnsubscribed]);
|
|
755
|
+
else await Promise.race([ws.isReady, callerUnsubscribed]);
|
|
794
756
|
clearTimeout(noMoreSocketsTimeout);
|
|
795
|
-
} catch
|
|
757
|
+
} catch {
|
|
796
758
|
clearTimeout(noMoreSocketsTimeout);
|
|
797
|
-
unsubRpcStatus
|
|
759
|
+
unsubRpcStatus?.();
|
|
798
760
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
799
761
|
return;
|
|
800
762
|
}
|
|
801
|
-
|
|
802
|
-
// create subscription on ws
|
|
803
|
-
// handle the scenarios where the caller unsubscribes before the subscription has been created and:
|
|
804
|
-
// - the subscriptionId is already set
|
|
805
|
-
// - the subscriptionId is not set yet, but will be
|
|
806
763
|
let subscriptionId = null;
|
|
807
764
|
let disconnected = false;
|
|
808
|
-
let unsubscribeMethod
|
|
765
|
+
let unsubscribeMethod;
|
|
809
766
|
try {
|
|
810
|
-
await Promise.race([
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
767
|
+
await Promise.race([
|
|
768
|
+
ws.subscribe(responseMethod, subscribeMethod, params, callback).then((id) => {
|
|
769
|
+
if (disconnected) {
|
|
770
|
+
unsubscribeMethod && ws.unsubscribe(responseMethod, unsubscribeMethod, id);
|
|
771
|
+
} else subscriptionId = id;
|
|
772
|
+
}),
|
|
773
|
+
callerUnsubscribed
|
|
774
|
+
]);
|
|
815
775
|
} catch (error) {
|
|
816
776
|
if (error instanceof CallerUnsubscribedError) unsubscribeMethod = error.unsubscribeMethod;
|
|
817
|
-
unsubRpcStatus
|
|
777
|
+
unsubRpcStatus?.();
|
|
818
778
|
disconnected = true;
|
|
819
|
-
if (subscriptionId !== null && unsubscribeMethod)
|
|
779
|
+
if (subscriptionId !== null && unsubscribeMethod)
|
|
780
|
+
await ws.unsubscribe(responseMethod, unsubscribeMethod, subscriptionId);
|
|
820
781
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
821
782
|
return;
|
|
822
783
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
if (subscriptionId !== null && unsubscribeMethod) await ws.unsubscribe(responseMethod, unsubscribeMethod, subscriptionId);
|
|
784
|
+
callerUnsubscribed.catch(async (error) => {
|
|
785
|
+
let unsubscribeMethod2;
|
|
786
|
+
if (error instanceof CallerUnsubscribedError) unsubscribeMethod2 = error.unsubscribeMethod;
|
|
787
|
+
unsubRpcStatus?.();
|
|
788
|
+
if (subscriptionId !== null && unsubscribeMethod2)
|
|
789
|
+
await ws.unsubscribe(responseMethod, unsubscribeMethod2, subscriptionId);
|
|
830
790
|
await this.disconnectChainSocket(chainId, socketUserId);
|
|
831
|
-
}).catch(error =>
|
|
791
|
+
}).catch((error) => log_default.warn(error));
|
|
832
792
|
})();
|
|
833
793
|
return unsubscribe;
|
|
834
794
|
}
|
|
835
|
-
|
|
836
795
|
/**
|
|
837
796
|
* Kills current websocket if any
|
|
838
797
|
* Useful after changing rpc order to make sure it's applied for futher requests
|
|
839
798
|
*/
|
|
840
799
|
async reset(chainId) {
|
|
841
|
-
|
|
800
|
+
log_default.info("ChainConnector reset", chainId);
|
|
842
801
|
const ws = this.#socketConnections[chainId];
|
|
843
802
|
if (!ws) return;
|
|
844
803
|
try {
|
|
@@ -847,21 +806,18 @@ class ChainConnectorDot {
|
|
|
847
806
|
delete this.#socketUsers[chainId];
|
|
848
807
|
await ws.disconnect();
|
|
849
808
|
} catch (error) {
|
|
850
|
-
|
|
809
|
+
log_default.warn(`Error occurred reseting socket ${chainId}`, error);
|
|
851
810
|
}
|
|
852
811
|
}
|
|
853
|
-
|
|
854
812
|
/**
|
|
855
813
|
* Wait for websocket to be ready, but don't wait forever
|
|
856
814
|
*/
|
|
857
|
-
async waitForWs(ws, timeout =
|
|
858
|
-
) {
|
|
815
|
+
async waitForWs(ws, timeout = 3e4) {
|
|
859
816
|
const timer = timeout ? sleep(timeout).then(() => {
|
|
860
817
|
throw new Error(`RPC connect timeout reached: ${ws.endpoint}`);
|
|
861
818
|
}) : false;
|
|
862
819
|
await Promise.race([ws.isReady, timer].filter(isTruthy));
|
|
863
820
|
}
|
|
864
|
-
|
|
865
821
|
/**
|
|
866
822
|
* Connect to an RPC via chainId
|
|
867
823
|
*
|
|
@@ -870,42 +826,45 @@ class ChainConnectorDot {
|
|
|
870
826
|
async connectChainSocket(chainId) {
|
|
871
827
|
const rpcs = await this.getEndpoints(chainId);
|
|
872
828
|
const socketUserId = this.addSocketUser(chainId);
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (this.#connectionMetaDb) nextBackoffInterval = (await this.#connectionMetaDb.chainBackoffInterval.get(chainId))?.interval;
|
|
877
|
-
|
|
878
|
-
// NOTE: Make sure there are no calls to `await` between this check and the
|
|
879
|
-
// next step where we assign a `new Websocket` to `this.#socketConnections[chainId]`
|
|
880
|
-
//
|
|
881
|
-
// If there is an `await` between these two steps then there will be a race condition introduced.
|
|
882
|
-
// The result of this race condition will be the unnecessary creation of multiple instances of
|
|
883
|
-
// `Websocket` per chain, rather than the intended behaviour where every call to send/subscribe
|
|
884
|
-
// shares a single `Websocket` per chain.
|
|
829
|
+
let nextBackoffInterval;
|
|
830
|
+
if (this.#connectionMetaDb)
|
|
831
|
+
nextBackoffInterval = (await this.#connectionMetaDb.chainBackoffInterval.get(chainId))?.interval;
|
|
885
832
|
if (this.#socketConnections[chainId]) return [socketUserId, this.#socketConnections[chainId]];
|
|
886
|
-
if (rpcs.length)
|
|
833
|
+
if (rpcs.length)
|
|
834
|
+
this.#socketConnections[chainId] = new Websocket(
|
|
835
|
+
rpcs,
|
|
836
|
+
void 0,
|
|
837
|
+
void 0,
|
|
838
|
+
nextBackoffInterval
|
|
839
|
+
);
|
|
840
|
+
else {
|
|
887
841
|
throw new Error(`No healthy RPCs available for chain ${chainId}`);
|
|
888
842
|
}
|
|
889
|
-
|
|
890
|
-
// on ws connected event, store current rpc as most recently connected rpc
|
|
891
843
|
if (this.#connectionMetaDb) {
|
|
892
844
|
this.#socketConnections[chainId].on("connected", () => {
|
|
893
845
|
if (!this.#connectionMetaDb) return;
|
|
894
846
|
const id = chainId;
|
|
895
847
|
const url = this.#socketConnections[chainId]?.endpoint;
|
|
896
848
|
if (!url) return;
|
|
897
|
-
this.updateRpcPriority(id, url, "first").catch(
|
|
849
|
+
this.updateRpcPriority(id, url, "first").catch(
|
|
850
|
+
(err) => log_default.warn(`updateRpcPriority failed`, err)
|
|
851
|
+
);
|
|
898
852
|
});
|
|
899
853
|
}
|
|
854
|
+
;
|
|
900
855
|
(async () => {
|
|
901
|
-
if (!this.#socketConnections[chainId])
|
|
856
|
+
if (!this.#socketConnections[chainId])
|
|
857
|
+
return log_default.warn(`ignoring ${chainId} rpc ws healthcheck initialization: ws is not defined`);
|
|
902
858
|
await this.#socketConnections[chainId].isReady;
|
|
903
|
-
if (this.#socketKeepAliveIntervals[chainId])
|
|
904
|
-
|
|
859
|
+
if (this.#socketKeepAliveIntervals[chainId])
|
|
860
|
+
clearInterval(this.#socketKeepAliveIntervals[chainId]);
|
|
861
|
+
const intervalMs = 1e4;
|
|
905
862
|
this.#socketKeepAliveIntervals[chainId] = setInterval(() => {
|
|
906
|
-
if (!this.#socketConnections[chainId])
|
|
907
|
-
|
|
908
|
-
this.#socketConnections[chainId].
|
|
863
|
+
if (!this.#socketConnections[chainId])
|
|
864
|
+
return log_default.warn(`skipping ${chainId} rpc ws healthcheck: ws is not defined`);
|
|
865
|
+
if (!this.#socketConnections[chainId].isConnected)
|
|
866
|
+
return log_default.warn(`skipping ${chainId} rpc ws healthcheck: ws is not connected`);
|
|
867
|
+
this.#socketConnections[chainId].send("system_health", []).catch((error) => log_default.warn(`Failed keep-alive for socket ${chainId}`, error));
|
|
909
868
|
}, intervalMs);
|
|
910
869
|
})();
|
|
911
870
|
return [socketUserId, this.#socketConnections[chainId]];
|
|
@@ -913,11 +872,12 @@ class ChainConnectorDot {
|
|
|
913
872
|
async disconnectChainSocket(chainId, socketUserId) {
|
|
914
873
|
this.removeSocketUser(chainId, socketUserId);
|
|
915
874
|
if (this.#socketUsers[chainId].length > 0) return;
|
|
916
|
-
if (!this.#socketConnections[chainId])
|
|
875
|
+
if (!this.#socketConnections[chainId])
|
|
876
|
+
return log_default.warn(`Failed to disconnect socket: socket ${chainId} not found`);
|
|
917
877
|
try {
|
|
918
878
|
this.#socketConnections[chainId].disconnect();
|
|
919
879
|
} catch (error) {
|
|
920
|
-
|
|
880
|
+
log_default.warn(`Error occurred disconnecting socket ${chainId}`, error);
|
|
921
881
|
}
|
|
922
882
|
delete this.#socketConnections[chainId];
|
|
923
883
|
clearInterval(this.#socketKeepAliveIntervals[chainId]);
|
|
@@ -931,10 +891,12 @@ class ChainConnectorDot {
|
|
|
931
891
|
}
|
|
932
892
|
removeSocketUser(chainId, socketUserId) {
|
|
933
893
|
const userIndex = this.#socketUsers[chainId].indexOf(socketUserId);
|
|
934
|
-
if (userIndex === -1)
|
|
894
|
+
if (userIndex === -1)
|
|
895
|
+
throw new Error(
|
|
896
|
+
`Can't remove user ${socketUserId} from socket ${chainId}: user not in list ${this.#socketUsers[chainId].join(", ")}`
|
|
897
|
+
);
|
|
935
898
|
this.#socketUsers[chainId].splice(userIndex, 1);
|
|
936
899
|
}
|
|
937
|
-
|
|
938
900
|
/** continues to generate a random number until it finds one which is not present in the exclude list */
|
|
939
901
|
getExclusiveRandomId(exclude = []) {
|
|
940
902
|
let id = this.getRandomId();
|
|
@@ -945,13 +907,10 @@ class ChainConnectorDot {
|
|
|
945
907
|
}
|
|
946
908
|
/** generates a random number */
|
|
947
909
|
getRandomId() {
|
|
948
|
-
return Math.trunc(Math.random() *
|
|
910
|
+
return Math.trunc(Math.random() * 10 ** 8);
|
|
949
911
|
}
|
|
950
912
|
getTalismanSub() {
|
|
951
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
952
913
|
const talismanSub = typeof window !== "undefined" && window.talismanSub;
|
|
953
|
-
|
|
954
|
-
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
955
914
|
const rpcByGenesisHashSend = talismanSub?.rpcByGenesisHashSend;
|
|
956
915
|
const rpcByGenesisHashSubscribe = talismanSub?.rpcByGenesisHashSubscribe;
|
|
957
916
|
const rpcByGenesisHashUnsubscribe = talismanSub?.rpcByGenesisHashUnsubscribe;
|
|
@@ -959,9 +918,16 @@ class ChainConnectorDot {
|
|
|
959
918
|
if (typeof rpcByGenesisHashSubscribe !== "function") return;
|
|
960
919
|
if (typeof rpcByGenesisHashUnsubscribe !== "function") return;
|
|
961
920
|
return {
|
|
962
|
-
//
|
|
921
|
+
// biome-ignore lint/suspicious/noExplicitAny: legacy
|
|
963
922
|
send: (genesisHash, method, params) => rpcByGenesisHashSend(genesisHash, method, params),
|
|
964
|
-
subscribe: (genesisHash, subscribeMethod, responseMethod, params, callback, timeout) => rpcByGenesisHashSubscribe(
|
|
923
|
+
subscribe: (genesisHash, subscribeMethod, responseMethod, params, callback, timeout) => rpcByGenesisHashSubscribe(
|
|
924
|
+
genesisHash,
|
|
925
|
+
subscribeMethod,
|
|
926
|
+
responseMethod,
|
|
927
|
+
params,
|
|
928
|
+
callback,
|
|
929
|
+
timeout
|
|
930
|
+
),
|
|
965
931
|
unsubscribe: (subscriptionId, unsubscribeMethod) => rpcByGenesisHashUnsubscribe(subscriptionId, unsubscribeMethod)
|
|
966
932
|
};
|
|
967
933
|
}
|
|
@@ -969,81 +935,86 @@ class ChainConnectorDot {
|
|
|
969
935
|
if (!this.#connectionMetaDb) return;
|
|
970
936
|
const rpcs = await this.getEndpoints(chainId);
|
|
971
937
|
if (!rpcs.includes(rpc)) throw new Error(`Unknown rpc for chain ${chainId} : ${rpc}`);
|
|
972
|
-
const urls = rpcs.filter(r => r !== rpc);
|
|
938
|
+
const urls = rpcs.filter((r) => r !== rpc);
|
|
973
939
|
if (priority === "first") urls.unshift(rpc);
|
|
974
940
|
if (priority === "last") urls.push(rpc);
|
|
975
941
|
if (!isEqual(urls, rpcs)) {
|
|
976
|
-
|
|
977
|
-
await this.#connectionMetaDb.chainPriorityRpcs.put({
|
|
978
|
-
id: chainId,
|
|
979
|
-
urls
|
|
980
|
-
}, chainId);
|
|
942
|
+
await this.#connectionMetaDb.chainPriorityRpcs.put({ id: chainId, urls }, chainId);
|
|
981
943
|
}
|
|
982
944
|
}
|
|
983
945
|
async getEndpoints(chainId) {
|
|
984
946
|
const chain = await this.#chaindataChainProvider.getNetworkById(chainId, "polkadot");
|
|
985
947
|
if (!chain) throw new Error(`Chain ${chainId} not found in store`);
|
|
986
|
-
let rpcs = chain.rpcs.concat();
|
|
987
|
-
const priorityRpcs = this.#connectionMetaDb ? await this.#connectionMetaDb.chainPriorityRpcs.get(chainId) :
|
|
948
|
+
let rpcs = chain.rpcs.concat();
|
|
949
|
+
const priorityRpcs = this.#connectionMetaDb ? await this.#connectionMetaDb.chainPriorityRpcs.get(chainId) : void 0;
|
|
988
950
|
if (priorityRpcs) {
|
|
989
|
-
|
|
990
|
-
|
|
951
|
+
rpcs = [
|
|
952
|
+
...priorityRpcs.urls.filter((rpc) => rpcs.includes(rpc)),
|
|
953
|
+
...rpcs.filter((rpc) => !priorityRpcs.urls.includes(rpc))
|
|
954
|
+
];
|
|
991
955
|
}
|
|
992
956
|
return rpcs;
|
|
993
957
|
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
958
|
+
};
|
|
959
|
+
var isEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
960
|
+
|
|
961
|
+
// src/dot/ChainConnectorDotStub.ts
|
|
962
|
+
import { WsProvider } from "@polkadot/rpc-provider";
|
|
963
|
+
import { throwAfter as throwAfter2 } from "@talismn/util";
|
|
964
|
+
var AUTO_CONNECT_TIMEOUT = 3e3;
|
|
965
|
+
var TIMEOUT = 1e4;
|
|
966
|
+
var ChainConnectorDotStub = class {
|
|
967
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: legacy
|
|
1000
968
|
#network;
|
|
1001
969
|
#provider;
|
|
1002
970
|
constructor(network) {
|
|
1003
971
|
this.#network = network;
|
|
1004
|
-
this.#provider = new WsProvider(network.rpcs, AUTO_CONNECT_TIMEOUT,
|
|
972
|
+
this.#provider = new WsProvider(network.rpcs, AUTO_CONNECT_TIMEOUT, void 0, TIMEOUT);
|
|
1005
973
|
}
|
|
1006
974
|
asProvider() {
|
|
1007
975
|
return this.#provider;
|
|
1008
976
|
}
|
|
1009
|
-
async send(
|
|
977
|
+
async send(_chainId, method, params, isCacheable) {
|
|
1010
978
|
await this.#provider.isReady;
|
|
1011
979
|
return this.#provider.send(method, params, isCacheable);
|
|
1012
980
|
}
|
|
1013
|
-
async subscribe(
|
|
981
|
+
async subscribe(_chainId, subscribeMethod, responseMethod, params, callback, timeout) {
|
|
1014
982
|
await this.#provider.isReady;
|
|
1015
|
-
const subId = await Promise.race([
|
|
1016
|
-
|
|
983
|
+
const subId = await Promise.race([
|
|
984
|
+
throwAfter2(timeout || TIMEOUT, `Subscription timed out after ${timeout}ms`),
|
|
985
|
+
this.#provider.subscribe(responseMethod, subscribeMethod, params, callback)
|
|
986
|
+
]);
|
|
987
|
+
return (unsubscribeMethod) => {
|
|
1017
988
|
this.#provider.unsubscribe(responseMethod, unsubscribeMethod, subId);
|
|
1018
989
|
};
|
|
1019
990
|
}
|
|
1020
991
|
reset() {
|
|
1021
992
|
throw new Error("ChainConnectorDotStub does not implement reset");
|
|
1022
993
|
}
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// exclude zoraTestnet which uses Hyperliquid's chain id
|
|
1026
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1027
|
-
const {
|
|
1028
|
-
zoraTestnet,
|
|
1029
|
-
...validViemChains
|
|
1030
|
-
} = viemChains;
|
|
994
|
+
};
|
|
1031
995
|
|
|
1032
|
-
//
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
996
|
+
// src/eth/getEvmNetworkPublicClient.ts
|
|
997
|
+
import { createPublicClient } from "viem";
|
|
998
|
+
|
|
999
|
+
// src/eth/getChainFromEvmNetwork.ts
|
|
1000
|
+
import { camelCase, fromPairs, toPairs } from "lodash-es";
|
|
1001
|
+
import * as viemChains from "viem/chains";
|
|
1002
|
+
var { zoraTestnet, ...validViemChains } = viemChains;
|
|
1003
|
+
var VIEM_CHAINS = Object.keys(validViemChains).reduce(
|
|
1004
|
+
(acc, curr) => {
|
|
1005
|
+
const chain = validViemChains[curr];
|
|
1006
|
+
acc[chain.id] = chain;
|
|
1007
|
+
return acc;
|
|
1008
|
+
},
|
|
1009
|
+
{}
|
|
1010
|
+
);
|
|
1011
|
+
var chainsCache = /* @__PURE__ */ new Map();
|
|
1012
|
+
var clearChainsCache = (networkId) => {
|
|
1013
|
+
if (networkId) chainsCache.delete(networkId);
|
|
1014
|
+
else chainsCache.clear();
|
|
1041
1015
|
};
|
|
1042
|
-
|
|
1043
|
-
const {
|
|
1044
|
-
symbol,
|
|
1045
|
-
decimals
|
|
1046
|
-
} = network.nativeCurrency;
|
|
1016
|
+
var getChainFromEvmNetwork = (network) => {
|
|
1017
|
+
const { symbol, decimals } = network.nativeCurrency;
|
|
1047
1018
|
if (!chainsCache.has(network.id)) {
|
|
1048
1019
|
const chainRpcs = network.rpcs ?? [];
|
|
1049
1020
|
const viemChain = VIEM_CHAINS[Number(network.id)] ?? {};
|
|
@@ -1052,12 +1023,8 @@ const getChainFromEvmNetwork = network => {
|
|
|
1052
1023
|
id: Number(network.id),
|
|
1053
1024
|
name: network.name ?? `Ethereum Chain ${network.id}`,
|
|
1054
1025
|
rpcUrls: {
|
|
1055
|
-
public: {
|
|
1056
|
-
|
|
1057
|
-
},
|
|
1058
|
-
default: {
|
|
1059
|
-
http: chainRpcs
|
|
1060
|
-
}
|
|
1026
|
+
public: { http: chainRpcs },
|
|
1027
|
+
default: { http: chainRpcs }
|
|
1061
1028
|
},
|
|
1062
1029
|
nativeCurrency: {
|
|
1063
1030
|
symbol,
|
|
@@ -1066,9 +1033,12 @@ const getChainFromEvmNetwork = network => {
|
|
|
1066
1033
|
},
|
|
1067
1034
|
contracts: {
|
|
1068
1035
|
...viemChain.contracts,
|
|
1069
|
-
...
|
|
1070
|
-
address
|
|
1071
|
-
|
|
1036
|
+
...network.contracts ? fromPairs(
|
|
1037
|
+
toPairs(network.contracts).map(([name, address]) => [
|
|
1038
|
+
camelCase(name),
|
|
1039
|
+
{ address }
|
|
1040
|
+
])
|
|
1041
|
+
) : {}
|
|
1072
1042
|
}
|
|
1073
1043
|
};
|
|
1074
1044
|
chainsCache.set(network.id, chain);
|
|
@@ -1076,41 +1046,34 @@ const getChainFromEvmNetwork = network => {
|
|
|
1076
1046
|
return chainsCache.get(network.id);
|
|
1077
1047
|
};
|
|
1078
1048
|
|
|
1079
|
-
|
|
1049
|
+
// src/eth/getTransportForEvmNetwork.ts
|
|
1050
|
+
import { fallback, http } from "viem";
|
|
1051
|
+
var getTransportForEvmNetwork = (evmNetwork, options = {}) => {
|
|
1080
1052
|
if (!evmNetwork.rpcs?.length) throw new Error("No RPCs found for EVM network");
|
|
1081
|
-
const {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
retryCount: 0
|
|
1087
|
-
})), {
|
|
1088
|
-
retryCount: 0
|
|
1089
|
-
});
|
|
1053
|
+
const { batch } = options;
|
|
1054
|
+
return fallback(
|
|
1055
|
+
evmNetwork.rpcs.map((url) => http(url, { batch, retryCount: 0 })),
|
|
1056
|
+
{ retryCount: 0 }
|
|
1057
|
+
);
|
|
1090
1058
|
};
|
|
1091
1059
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
const clearPublicClientCache = evmNetworkId => {
|
|
1060
|
+
// src/eth/getEvmNetworkPublicClient.ts
|
|
1061
|
+
var MUTLICALL_BATCH_WAIT = 25;
|
|
1062
|
+
var MUTLICALL_BATCH_SIZE = 100;
|
|
1063
|
+
var HTTP_BATCH_WAIT = 25;
|
|
1064
|
+
var HTTP_BATCH_SIZE_WITH_MULTICALL = 10;
|
|
1065
|
+
var HTTP_BATCH_SIZE_WITHOUT_MULTICALL = 30;
|
|
1066
|
+
var publicClientCache = /* @__PURE__ */ new Map();
|
|
1067
|
+
var clearPublicClientCache = (evmNetworkId) => {
|
|
1101
1068
|
clearChainsCache(evmNetworkId);
|
|
1102
|
-
if (evmNetworkId) publicClientCache.delete(evmNetworkId);
|
|
1069
|
+
if (evmNetworkId) publicClientCache.delete(evmNetworkId);
|
|
1070
|
+
else publicClientCache.clear();
|
|
1103
1071
|
};
|
|
1104
|
-
|
|
1072
|
+
var getEvmNetworkPublicClient = (network) => {
|
|
1105
1073
|
const chain = getChainFromEvmNetwork(network);
|
|
1106
1074
|
if (!publicClientCache.has(network.id)) {
|
|
1107
1075
|
if (!network.rpcs.length) throw new Error("No RPCs found for Ethereum network");
|
|
1108
|
-
const batch = chain.contracts?.multicall3 ? {
|
|
1109
|
-
multicall: {
|
|
1110
|
-
wait: MUTLICALL_BATCH_WAIT,
|
|
1111
|
-
batchSize: MUTLICALL_BATCH_SIZE
|
|
1112
|
-
}
|
|
1113
|
-
} : undefined;
|
|
1076
|
+
const batch = chain.contracts?.multicall3 ? { multicall: { wait: MUTLICALL_BATCH_WAIT, batchSize: MUTLICALL_BATCH_SIZE } } : void 0;
|
|
1114
1077
|
const transportOptions = {
|
|
1115
1078
|
batch: {
|
|
1116
1079
|
batchSize: chain.contracts?.multicall3 ? HTTP_BATCH_SIZE_WITH_MULTICALL : HTTP_BATCH_SIZE_WITHOUT_MULTICALL,
|
|
@@ -1118,26 +1081,28 @@ const getEvmNetworkPublicClient = network => {
|
|
|
1118
1081
|
}
|
|
1119
1082
|
};
|
|
1120
1083
|
const transport = getTransportForEvmNetwork(network, transportOptions);
|
|
1121
|
-
publicClientCache.set(
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1084
|
+
publicClientCache.set(
|
|
1085
|
+
network.id,
|
|
1086
|
+
createPublicClient({
|
|
1087
|
+
chain,
|
|
1088
|
+
transport,
|
|
1089
|
+
batch
|
|
1090
|
+
})
|
|
1091
|
+
);
|
|
1126
1092
|
}
|
|
1127
1093
|
return publicClientCache.get(network.id);
|
|
1128
1094
|
};
|
|
1129
1095
|
|
|
1130
|
-
|
|
1096
|
+
// src/eth/getEvmNetworkWalletClient.ts
|
|
1097
|
+
import { createWalletClient } from "viem";
|
|
1098
|
+
var getEvmNetworkWalletClient = (network, options = {}) => {
|
|
1131
1099
|
const chain = getChainFromEvmNetwork(network);
|
|
1132
1100
|
const transport = getTransportForEvmNetwork(network);
|
|
1133
|
-
return createWalletClient({
|
|
1134
|
-
chain,
|
|
1135
|
-
transport,
|
|
1136
|
-
account: options.account
|
|
1137
|
-
});
|
|
1101
|
+
return createWalletClient({ chain, transport, account: options.account });
|
|
1138
1102
|
};
|
|
1139
1103
|
|
|
1140
|
-
|
|
1104
|
+
// src/eth/ChainConnectorEth.ts
|
|
1105
|
+
var ChainConnectorEth = class {
|
|
1141
1106
|
#chaindataProvider;
|
|
1142
1107
|
constructor(chaindataProvider) {
|
|
1143
1108
|
this.#chaindataProvider = chaindataProvider;
|
|
@@ -1150,16 +1115,15 @@ class ChainConnectorEth {
|
|
|
1150
1115
|
async getWalletClientForEvmNetwork(evmNetworkId, account) {
|
|
1151
1116
|
const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, "ethereum");
|
|
1152
1117
|
if (!network) return null;
|
|
1153
|
-
return getEvmNetworkWalletClient(network, {
|
|
1154
|
-
account
|
|
1155
|
-
});
|
|
1118
|
+
return getEvmNetworkWalletClient(network, { account });
|
|
1156
1119
|
}
|
|
1157
1120
|
clearRpcProvidersCache(evmNetworkId) {
|
|
1158
1121
|
clearPublicClientCache(evmNetworkId);
|
|
1159
1122
|
}
|
|
1160
|
-
}
|
|
1123
|
+
};
|
|
1161
1124
|
|
|
1162
|
-
|
|
1125
|
+
// src/eth/ChainConnectorEthStub.ts
|
|
1126
|
+
var ChainConnectorEthStub = class {
|
|
1163
1127
|
#network;
|
|
1164
1128
|
constructor(network) {
|
|
1165
1129
|
this.#network = network;
|
|
@@ -1167,24 +1131,23 @@ class ChainConnectorEthStub {
|
|
|
1167
1131
|
async getPublicClientForEvmNetwork() {
|
|
1168
1132
|
return getEvmNetworkPublicClient(this.#network);
|
|
1169
1133
|
}
|
|
1170
|
-
async getWalletClientForEvmNetwork(
|
|
1171
|
-
return getEvmNetworkWalletClient(this.#network, {
|
|
1172
|
-
account
|
|
1173
|
-
});
|
|
1134
|
+
async getWalletClientForEvmNetwork(_networkId, account) {
|
|
1135
|
+
return getEvmNetworkWalletClient(this.#network, { account });
|
|
1174
1136
|
}
|
|
1175
1137
|
clearRpcProvidersCache() {
|
|
1176
|
-
// No-op for stub
|
|
1177
1138
|
}
|
|
1178
|
-
}
|
|
1139
|
+
};
|
|
1179
1140
|
|
|
1180
|
-
//
|
|
1181
|
-
|
|
1141
|
+
// src/sol/getSolConnection.ts
|
|
1142
|
+
import { Connection } from "@solana/web3.js";
|
|
1143
|
+
var getSolConnection = (_networkId, rpcs) => {
|
|
1182
1144
|
return new Connection(rpcs[0], {
|
|
1183
1145
|
commitment: "confirmed"
|
|
1184
1146
|
});
|
|
1185
1147
|
};
|
|
1186
1148
|
|
|
1187
|
-
|
|
1149
|
+
// src/sol/ChainConnectorSol.ts
|
|
1150
|
+
var ChainConnectorSol = class {
|
|
1188
1151
|
#chaindataProvider;
|
|
1189
1152
|
constructor(chaindataProvider) {
|
|
1190
1153
|
this.#chaindataProvider = chaindataProvider;
|
|
@@ -1194,16 +1157,28 @@ class ChainConnectorSol {
|
|
|
1194
1157
|
if (!network) throw new Error(`Network not found: ${networkId}`);
|
|
1195
1158
|
return getSolConnection(networkId, network.rpcs);
|
|
1196
1159
|
}
|
|
1197
|
-
}
|
|
1160
|
+
};
|
|
1198
1161
|
|
|
1199
|
-
|
|
1162
|
+
// src/sol/ChainConnectorSolStub.ts
|
|
1163
|
+
import { Connection as Connection2 } from "@solana/web3.js";
|
|
1164
|
+
var ChainConnectorSolStub = class {
|
|
1200
1165
|
#connection;
|
|
1201
1166
|
constructor(networkOrConnection) {
|
|
1202
|
-
this.#connection = networkOrConnection instanceof
|
|
1167
|
+
this.#connection = networkOrConnection instanceof Connection2 ? networkOrConnection : getSolConnection(networkOrConnection.id, networkOrConnection.rpcs);
|
|
1203
1168
|
}
|
|
1204
1169
|
async getConnection() {
|
|
1205
1170
|
return this.#connection;
|
|
1206
1171
|
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1172
|
+
};
|
|
1173
|
+
export {
|
|
1174
|
+
ChainConnectionError,
|
|
1175
|
+
ChainConnectorDot,
|
|
1176
|
+
ChainConnectorDotStub,
|
|
1177
|
+
ChainConnectorEth,
|
|
1178
|
+
ChainConnectorEthStub,
|
|
1179
|
+
ChainConnectorSol,
|
|
1180
|
+
ChainConnectorSolStub,
|
|
1181
|
+
StaleRpcError,
|
|
1182
|
+
WebsocketAllocationExhaustedError
|
|
1183
|
+
};
|
|
1184
|
+
//# sourceMappingURL=index.mjs.map
|