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