quantumcoin 7.0.2 → 7.0.4

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.
Files changed (155) hide show
  1. package/.github/workflows/publish-npmjs.yaml +22 -22
  2. package/.gitignore +15 -15
  3. package/LICENSE +21 -21
  4. package/README-SDK.md +756 -756
  5. package/README.md +165 -152
  6. package/SPEC.md +3845 -3845
  7. package/config.d.ts +50 -50
  8. package/config.js +115 -115
  9. package/examples/AllSolidityTypes.sol +184 -184
  10. package/examples/SimpleIERC20.sol +74 -74
  11. package/examples/events.js +41 -35
  12. package/examples/events.ts +35 -0
  13. package/examples/example-generator-sdk-js.js +100 -95
  14. package/examples/example-generator-sdk-js.ts +77 -0
  15. package/examples/example-generator-sdk-ts.js +100 -95
  16. package/examples/example-generator-sdk-ts.ts +77 -0
  17. package/examples/example.js +72 -61
  18. package/examples/example.ts +61 -0
  19. package/examples/offline-signing.js +79 -0
  20. package/examples/offline-signing.ts +66 -0
  21. package/examples/package-lock.json +48 -57
  22. package/examples/package.json +32 -16
  23. package/examples/read-operations.js +32 -27
  24. package/examples/read-operations.ts +31 -0
  25. package/examples/sdk-generator-erc20.inline.json +251 -251
  26. package/examples/solidity-types.ts +43 -43
  27. package/examples/wallet-offline.js +35 -29
  28. package/examples/wallet-offline.ts +34 -0
  29. package/generate-sdk.js +1824 -1383
  30. package/index.js +12 -12
  31. package/package.json +95 -75
  32. package/scripts/copy-declarations.js +31 -0
  33. package/scripts/run-all-one-by-one.js +151 -0
  34. package/src/abi/fragments.d.ts +42 -42
  35. package/src/abi/fragments.js +63 -63
  36. package/src/abi/index.d.ts +13 -13
  37. package/src/abi/index.js +9 -9
  38. package/src/abi/interface.d.ts +128 -132
  39. package/src/abi/interface.js +590 -590
  40. package/src/abi/js-abi-coder.d.ts +8 -0
  41. package/src/abi/js-abi-coder.js +474 -474
  42. package/src/constants.d.ts +66 -61
  43. package/src/constants.js +101 -94
  44. package/src/contract/contract-factory.d.ts +28 -28
  45. package/src/contract/contract-factory.js +105 -105
  46. package/src/contract/contract.d.ts +113 -105
  47. package/src/contract/contract.js +354 -312
  48. package/src/contract/index.d.ts +9 -9
  49. package/src/contract/index.js +9 -9
  50. package/src/errors/index.d.ts +92 -92
  51. package/src/errors/index.js +188 -188
  52. package/src/generator/index.d.ts +74 -0
  53. package/src/generator/index.js +1404 -1201
  54. package/src/index.d.ts +125 -127
  55. package/src/index.js +41 -41
  56. package/src/internal/hex.d.ts +61 -61
  57. package/src/internal/hex.js +144 -144
  58. package/src/providers/extra-providers.d.ts +139 -128
  59. package/src/providers/extra-providers.js +600 -575
  60. package/src/providers/index.d.ts +17 -16
  61. package/src/providers/index.js +10 -10
  62. package/src/providers/json-rpc-provider.d.ts +12 -12
  63. package/src/providers/json-rpc-provider.js +79 -79
  64. package/src/providers/provider.d.ts +207 -196
  65. package/src/providers/provider.js +392 -359
  66. package/src/types/index.d.ts +214 -462
  67. package/src/types/index.js +9 -9
  68. package/src/utils/address.d.ts +72 -72
  69. package/src/utils/address.js +181 -182
  70. package/src/utils/encoding.d.ts +120 -120
  71. package/src/utils/encoding.js +306 -306
  72. package/src/utils/hashing.d.ts +82 -76
  73. package/src/utils/hashing.js +313 -298
  74. package/src/utils/index.d.ts +65 -55
  75. package/src/utils/index.js +13 -13
  76. package/src/utils/result.d.ts +57 -57
  77. package/src/utils/result.js +128 -128
  78. package/src/utils/rlp.d.ts +12 -12
  79. package/src/utils/rlp.js +200 -200
  80. package/src/utils/units.d.ts +29 -29
  81. package/src/utils/units.js +107 -107
  82. package/src/wallet/index.d.ts +10 -10
  83. package/src/wallet/index.js +8 -8
  84. package/src/wallet/wallet.d.ts +160 -160
  85. package/src/wallet/wallet.js +483 -489
  86. package/test/e2e/all-solidity-types.dynamic.test.js +207 -200
  87. package/test/e2e/all-solidity-types.dynamic.test.ts +191 -0
  88. package/test/e2e/all-solidity-types.fixtures.js +231 -231
  89. package/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +387 -361
  90. package/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +350 -0
  91. package/test/e2e/helpers.js +59 -47
  92. package/test/e2e/signing-context-and-fee.e2e.test.js +137 -0
  93. package/test/e2e/signing-context-and-fee.e2e.test.ts +128 -0
  94. package/test/e2e/simple-erc20.generated-sdks.e2e.test.js +168 -144
  95. package/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +141 -0
  96. package/test/e2e/transactional.test.js +245 -191
  97. package/test/e2e/transactional.test.ts +208 -0
  98. package/test/e2e/typed-generator.e2e.test.js +407 -402
  99. package/test/e2e/typed-generator.e2e.test.ts +337 -0
  100. package/test/fixtures/ConstructorParam.sol +23 -23
  101. package/test/fixtures/MultiContracts.sol +37 -37
  102. package/test/fixtures/SimpleStorage.sol +18 -18
  103. package/test/fixtures/StakingContract.abi.json +1 -1
  104. package/test/integration/ipc-provider.test.js +49 -44
  105. package/test/integration/ipc-provider.test.ts +44 -0
  106. package/test/integration/provider.test.js +88 -72
  107. package/test/integration/provider.test.ts +85 -0
  108. package/test/integration/ws-provider.test.js +41 -33
  109. package/test/integration/ws-provider.test.ts +38 -0
  110. package/test/security/malformed-input.test.js +37 -31
  111. package/test/security/malformed-input.test.ts +35 -0
  112. package/test/unit/_encrypted-output.txt +6 -0
  113. package/test/unit/_log-encrypted-jsons.js +45 -0
  114. package/test/unit/_write-keystore-fixture.js +16 -0
  115. package/test/unit/abi-interface.test.js +103 -98
  116. package/test/unit/abi-interface.test.ts +102 -0
  117. package/test/unit/address-wallet.test.js +355 -257
  118. package/test/unit/address-wallet.test.ts +342 -0
  119. package/test/unit/browser-provider.test.js +85 -82
  120. package/test/unit/browser-provider.test.ts +79 -0
  121. package/test/unit/contract.test.js +85 -82
  122. package/test/unit/contract.test.ts +83 -0
  123. package/test/unit/encoding-units-rlp.test.js +92 -89
  124. package/test/unit/encoding-units-rlp.test.ts +91 -0
  125. package/test/unit/errors.test.js +77 -74
  126. package/test/unit/errors.test.ts +76 -0
  127. package/test/unit/filter-by-blockhash.test.js +55 -52
  128. package/test/unit/filter-by-blockhash.test.ts +54 -0
  129. package/test/unit/fixtures/encrypted-keystores-48-32-36.js +9 -0
  130. package/test/unit/generate-contract-cli.test.js +42 -39
  131. package/test/unit/generate-contract-cli.test.ts +41 -0
  132. package/test/unit/generate-sdk-artifacts-json.test.js +113 -110
  133. package/test/unit/generate-sdk-artifacts-json.test.ts +110 -0
  134. package/test/unit/generator.test.js +102 -98
  135. package/test/unit/generator.test.ts +101 -0
  136. package/test/unit/hashing.test.js +68 -54
  137. package/test/unit/hashing.test.ts +67 -0
  138. package/test/unit/init.test.js +39 -36
  139. package/test/unit/init.test.ts +38 -0
  140. package/test/unit/interface.test.js +56 -53
  141. package/test/unit/interface.test.ts +54 -0
  142. package/test/unit/internal-hex.test.js +50 -47
  143. package/test/unit/internal-hex.test.ts +49 -0
  144. package/test/unit/populate-transaction.test.js +65 -0
  145. package/test/unit/populate-transaction.test.ts +64 -0
  146. package/test/unit/providers.test.js +200 -144
  147. package/test/unit/providers.test.ts +196 -0
  148. package/test/unit/result.test.js +80 -77
  149. package/test/unit/result.test.ts +79 -0
  150. package/test/unit/solidity-types.test.js +49 -46
  151. package/test/unit/solidity-types.test.ts +39 -0
  152. package/test/unit/utils.test.js +57 -54
  153. package/test/unit/utils.test.ts +56 -0
  154. package/test/verbose-logger.js +74 -0
  155. package/tsconfig.build.json +14 -0
@@ -1,575 +1,600 @@
1
- /**
2
- * @fileoverview Additional provider classes (compatibility stubs).
3
- *
4
- * These are included to match the API shape described in SPEC.md. Only
5
- * JsonRpcProvider is fully implemented for network operations.
6
- */
7
-
8
- const { AbstractProvider } = require("./provider");
9
- const { JsonRpcProvider } = require("./json-rpc-provider");
10
- const { makeError } = require("../errors");
11
- const net = require("node:net");
12
- const { JsonRpcSigner } = require("../wallet/wallet");
13
- const { normalizeHex, isHexString } = require("../internal/hex");
14
-
15
- let _wsRpcId = 1;
16
- let _ipcRpcId = 1;
17
-
18
- /**
19
- * Extract the first complete JSON object from a stream buffer.
20
- *
21
- * geth's IPC JSON-RPC typically uses newline-delimited JSON, but we also support
22
- * non-newline framing by scanning for balanced braces and respecting strings.
23
- *
24
- * @param {string} buffer
25
- * @returns {{ json: string, rest: string } | null}
26
- */
27
- function _extractFirstJsonObject(buffer) {
28
- const start = buffer.indexOf("{");
29
- if (start === -1) return null;
30
-
31
- let depth = 0;
32
- let inString = false;
33
- let escape = false;
34
-
35
- for (let i = start; i < buffer.length; i++) {
36
- const ch = buffer[i];
37
-
38
- if (inString) {
39
- if (escape) {
40
- escape = false;
41
- continue;
42
- }
43
- if (ch === "\\") {
44
- escape = true;
45
- continue;
46
- }
47
- if (ch === '"') {
48
- inString = false;
49
- }
50
- continue;
51
- }
52
-
53
- if (ch === '"') {
54
- inString = true;
55
- continue;
56
- }
57
-
58
- if (ch === "{") depth++;
59
- if (ch === "}") depth--;
60
-
61
- if (depth === 0) {
62
- const json = buffer.slice(start, i + 1);
63
- const rest = buffer.slice(i + 1);
64
- return { json, rest };
65
- }
66
- }
67
-
68
- return null;
69
- }
70
-
71
- class WebSocketProvider extends AbstractProvider {
72
- /**
73
- * Create a WebSocket JSON-RPC provider.
74
- *
75
- * This uses the built-in global `WebSocket` available in recent Node.js
76
- * versions (via undici). No additional npm dependencies are required.
77
- *
78
- * @param {string} url WebSocket endpoint (e.g. ws://127.0.0.1:8546)
79
- * @param {number=} chainId Optional chain id (compat)
80
- */
81
- constructor(url, chainId) {
82
- super();
83
- if (typeof url !== "string" || url.trim().length === 0) {
84
- throw makeError("missing WebSocket url", "INVALID_ARGUMENT", { url });
85
- }
86
- this.url = url.trim();
87
- this.chainId = chainId == null ? 123123 : chainId;
88
-
89
- /** @type {any|null} */
90
- this._ws = null;
91
- /** @type {Promise<void>|null} */
92
- this._wsReady = null;
93
- /** @type {Map<number, { resolve: Function, reject: Function, timer: any }>} */
94
- this._pending = new Map();
95
- }
96
-
97
- /**
98
- * Close the underlying WebSocket connection and reject any pending requests.
99
- * This is important for tests so the Node.js event loop can exit cleanly.
100
- */
101
- destroy() {
102
- try {
103
- if (this._ws && typeof this._ws.close === "function") this._ws.close();
104
- } catch {
105
- // ignore
106
- }
107
- this._ws = null;
108
- this._wsReady = null;
109
- this._rejectAllPending(makeError("WebSocket closed", "UNKNOWN_ERROR", { url: this.url }));
110
- }
111
-
112
- _rejectAllPending(err) {
113
- for (const [id, p] of this._pending.entries()) {
114
- try {
115
- clearTimeout(p.timer);
116
- p.reject(err);
117
- } catch {
118
- // ignore
119
- }
120
- this._pending.delete(id);
121
- }
122
- }
123
-
124
- async _connect() {
125
- if (this._ws && this._ws.readyState === 1) return;
126
- if (this._wsReady) return this._wsReady;
127
-
128
- const WS = globalThis.WebSocket;
129
- if (typeof WS !== "function") {
130
- throw makeError("WebSocket not available in this Node.js runtime", "NOT_IMPLEMENTED", {});
131
- }
132
-
133
- this._wsReady = new Promise((resolve, reject) => {
134
- const ws = new WS(this.url);
135
- this._ws = ws;
136
-
137
- const connectTimer = setTimeout(() => {
138
- try {
139
- ws.close();
140
- } catch {
141
- // ignore
142
- }
143
- reject(makeError("WebSocket connect timeout", "UNKNOWN_ERROR", { url: this.url }));
144
- }, 10_000);
145
-
146
- const onOpen = () => {
147
- clearTimeout(connectTimer);
148
- resolve();
149
- };
150
-
151
- const onError = (event) => {
152
- clearTimeout(connectTimer);
153
- reject(
154
- makeError("WebSocket error", "UNKNOWN_ERROR", {
155
- url: this.url,
156
- error: event && event.message ? event.message : String(event),
157
- }),
158
- );
159
- };
160
-
161
- const onClose = () => {
162
- clearTimeout(connectTimer);
163
- this._ws = null;
164
- this._wsReady = null;
165
- this._rejectAllPending(makeError("WebSocket closed", "UNKNOWN_ERROR", { url: this.url }));
166
- };
167
-
168
- const onMessage = (event) => {
169
- const data = event && event.data != null ? event.data : "";
170
- const text = typeof data === "string" ? data : data.toString();
171
- let msg;
172
- try {
173
- msg = JSON.parse(text);
174
- } catch {
175
- return;
176
- }
177
- if (!msg || typeof msg !== "object") return;
178
- if (typeof msg.id !== "number") return;
179
-
180
- const pending = this._pending.get(msg.id);
181
- if (!pending) return;
182
- this._pending.delete(msg.id);
183
- clearTimeout(pending.timer);
184
-
185
- if (msg.error) {
186
- pending.reject(makeError("JSON-RPC error", "UNKNOWN_ERROR", { error: msg.error }));
187
- return;
188
- }
189
- pending.resolve(msg.result);
190
- };
191
-
192
- // Prefer addEventListener API, but fall back to on* properties if needed.
193
- if (typeof ws.addEventListener === "function") {
194
- ws.addEventListener("open", onOpen);
195
- ws.addEventListener("error", onError);
196
- ws.addEventListener("close", onClose);
197
- ws.addEventListener("message", onMessage);
198
- } else {
199
- ws.onopen = onOpen;
200
- ws.onerror = onError;
201
- ws.onclose = onClose;
202
- ws.onmessage = onMessage;
203
- }
204
- }).finally(() => {
205
- // If connection failed, allow retries.
206
- if (!this._ws || this._ws.readyState !== 1) {
207
- this._ws = null;
208
- this._wsReady = null;
209
- }
210
- });
211
-
212
- return this._wsReady;
213
- }
214
-
215
- /**
216
- * @param {string} method
217
- * @param {any[]=} params
218
- * @returns {Promise<any>}
219
- */
220
- async _perform(method, params) {
221
- await this._connect();
222
- const ws = this._ws;
223
- if (!ws || ws.readyState !== 1) {
224
- throw makeError("WebSocket not connected", "UNKNOWN_ERROR", { url: this.url, method });
225
- }
226
-
227
- const id = _wsRpcId++;
228
- const body = JSON.stringify({ jsonrpc: "2.0", id, method, params: params || [] });
229
-
230
- return new Promise((resolve, reject) => {
231
- const timer = setTimeout(() => {
232
- this._pending.delete(id);
233
- reject(makeError("WebSocket JSON-RPC timeout", "UNKNOWN_ERROR", { url: this.url, method }));
234
- }, 30_000);
235
-
236
- this._pending.set(id, { resolve, reject, timer });
237
-
238
- try {
239
- ws.send(body);
240
- } catch (e) {
241
- clearTimeout(timer);
242
- this._pending.delete(id);
243
- reject(
244
- makeError("WebSocket send failed", "UNKNOWN_ERROR", {
245
- url: this.url,
246
- method,
247
- error: e && e.message ? e.message : String(e),
248
- }),
249
- );
250
- }
251
- });
252
- }
253
- }
254
-
255
- class IpcSocketProvider extends AbstractProvider {
256
- /**
257
- * Create an IPC provider.
258
- *
259
- * On Windows, use a named pipe path like: `\\\\.\\pipe\\geth.ipc`
260
- * On Unix, use a domain socket path like: `/path/to/geth.ipc`
261
- *
262
- * @param {string} path IPC socket path
263
- */
264
- constructor(path) {
265
- super();
266
- if (typeof path !== "string" || path.length === 0) {
267
- throw makeError("missing IPC path", "INVALID_ARGUMENT", { path });
268
- }
269
- this.path = path;
270
- }
271
-
272
- /**
273
- * @param {string} method
274
- * @param {any[]=} params
275
- * @returns {Promise<any>}
276
- */
277
- async _perform(method, params) {
278
- const id = _ipcRpcId++;
279
- const body =
280
- JSON.stringify({
281
- jsonrpc: "2.0",
282
- id,
283
- method,
284
- params: params || [],
285
- }) + "\n";
286
-
287
- return new Promise((resolve, reject) => {
288
- /** @type {boolean} */
289
- let done = false;
290
- /** @type {string} */
291
- let buffer = "";
292
-
293
- const socket = net.createConnection(this.path);
294
- socket.setEncoding("utf8");
295
- socket.setTimeout(30_000);
296
-
297
- function finish(err, value) {
298
- if (done) return;
299
- done = true;
300
- try {
301
- socket.destroy();
302
- } catch {
303
- // ignore
304
- }
305
- if (err) reject(err);
306
- else resolve(value);
307
- }
308
-
309
- socket.on("connect", () => {
310
- try {
311
- socket.write(body);
312
- } catch (e) {
313
- finish(
314
- makeError("IPC write failed", "UNKNOWN_ERROR", {
315
- method,
316
- path: this.path,
317
- error: e && e.message ? e.message : String(e),
318
- }),
319
- );
320
- }
321
- });
322
-
323
- socket.on("timeout", () => {
324
- finish(makeError("IPC request timeout", "UNKNOWN_ERROR", { method, path: this.path }));
325
- });
326
-
327
- socket.on("error", (e) => {
328
- finish(
329
- makeError("IPC socket error", "UNKNOWN_ERROR", {
330
- method,
331
- path: this.path,
332
- error: e && e.message ? e.message : String(e),
333
- }),
334
- );
335
- });
336
-
337
- socket.on("data", (chunk) => {
338
- buffer += String(chunk);
339
-
340
- // Fast path: newline-delimited JSON responses.
341
- while (buffer.includes("\n")) {
342
- const idx = buffer.indexOf("\n");
343
- const line = buffer.slice(0, idx).trim();
344
- buffer = buffer.slice(idx + 1);
345
- if (!line) continue;
346
- try {
347
- const json = JSON.parse(line);
348
- if (json && json.id === id) {
349
- if (json.error) {
350
- finish(makeError("JSON-RPC error", "UNKNOWN_ERROR", { method, error: json.error }));
351
- return;
352
- }
353
- finish(null, json.result);
354
- return;
355
- }
356
- } catch {
357
- // If parsing fails, fall through to brace-balanced extraction on the accumulated buffer.
358
- buffer = line + "\n" + buffer;
359
- break;
360
- }
361
- }
362
-
363
- // Robust path: find a complete JSON object by balanced braces.
364
- while (true) {
365
- const extracted = _extractFirstJsonObject(buffer);
366
- if (!extracted) return;
367
- buffer = extracted.rest;
368
- try {
369
- const json = JSON.parse(extracted.json);
370
- if (json && json.id === id) {
371
- if (json.error) {
372
- finish(makeError("JSON-RPC error", "UNKNOWN_ERROR", { method, error: json.error }));
373
- return;
374
- }
375
- finish(null, json.result);
376
- return;
377
- }
378
- } catch {
379
- // ignore malformed object and continue scanning remainder
380
- }
381
- }
382
- });
383
-
384
- socket.on("end", () => {
385
- if (!done) finish(makeError("IPC connection ended before response", "UNKNOWN_ERROR", { method, path: this.path }));
386
- });
387
- });
388
- }
389
- }
390
-
391
- class BrowserProvider extends AbstractProvider {
392
- /**
393
- * Create a BrowserProvider from an EIP-1193 provider (e.g. MetaMask).
394
- *
395
- * This is a lightweight implementation that focuses on the core behaviors:
396
- * - `send(method, params)` dispatches EIP-1193 requests
397
- * - `getSigner()` resolves the connected account
398
- * - emits ethers-like `debug` events for request/response tracking
399
- *
400
- * @param {{ request: Function }} eip1193Provider
401
- * @param {any=} network Unused (compat)
402
- * @param {{ providerInfo?: any }=} options
403
- */
404
- constructor(eip1193Provider, network, options) {
405
- super();
406
- void network;
407
- this.provider = eip1193Provider;
408
- this.providerInfo = options && options.providerInfo ? options.providerInfo : null;
409
-
410
- if (!this.provider || typeof this.provider.request !== "function") {
411
- throw makeError("invalid EIP-1193 provider (missing request)", "INVALID_ARGUMENT", { provider: this.provider });
412
- }
413
- }
414
-
415
- /**
416
- * Send an EIP-1193 JSON-RPC request.
417
- * @param {string} method
418
- * @param {any[]|Record<string, any>=} params
419
- * @returns {Promise<any>}
420
- */
421
- async send(method, params) {
422
- const payload = { method, params: params == null ? [] : params };
423
- this.emit("debug", { action: "sendEip1193Payload", payload });
424
-
425
- try {
426
- const result = await this.provider.request(payload);
427
- this.emit("debug", { action: "receiveEip1193Result", result });
428
- return result;
429
- } catch (e) {
430
- const err = this.getRpcError(payload, e);
431
- this.emit("debug", { action: "receiveEip1193Error", error: err });
432
- throw err;
433
- }
434
- }
435
-
436
- /**
437
- * Map an EIP-1193 error into a normalized Error.
438
- * @param {{ method: string, params?: any }} payload
439
- * @param {any} error
440
- * @returns {Error}
441
- */
442
- getRpcError(payload, error) {
443
- if (error instanceof Error) return error;
444
- return makeError("EIP-1193 error", "UNKNOWN_ERROR", { payload, error });
445
- }
446
-
447
- /**
448
- * Ethers compatibility: internal send for single or batched payloads.
449
- * @param {any|any[]} payload
450
- * @returns {Promise<any>}
451
- */
452
- async _send(payload) {
453
- if (Array.isArray(payload)) {
454
- const out = [];
455
- for (const p of payload) {
456
- // eslint-disable-next-line no-await-in-loop
457
- out.push(await this.send(p.method, p.params));
458
- }
459
- return out;
460
- }
461
- return this.send(payload.method, payload.params);
462
- }
463
-
464
- /**
465
- * @param {string} method
466
- * @param {any[]=} params
467
- * @returns {Promise<any>}
468
- */
469
- async _perform(method, params) {
470
- return this.send(method, params || []);
471
- }
472
-
473
- /**
474
- * Returns a signer for the specified account index or address.
475
- * @param {number|string=} address
476
- * @returns {Promise<JsonRpcSigner>}
477
- */
478
- async getSigner(address) {
479
- const accounts = await this.send("eth_accounts", []);
480
- if (!Array.isArray(accounts) || accounts.length === 0) {
481
- throw makeError("no accounts available from EIP-1193 provider", "UNKNOWN_ERROR", {});
482
- }
483
-
484
- if (address == null) {
485
- return new JsonRpcSigner(this, accounts[0]);
486
- }
487
-
488
- if (typeof address === "number") {
489
- const a = accounts[address];
490
- if (!a) throw makeError("account index out of range", "INVALID_ARGUMENT", { address });
491
- return new JsonRpcSigner(this, a);
492
- }
493
-
494
- if (typeof address === "string") {
495
- const found = accounts.find((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
496
- if (!found) throw makeError("account not found", "INVALID_ARGUMENT", { address });
497
- return new JsonRpcSigner(this, found);
498
- }
499
-
500
- throw makeError("invalid signer address/index", "INVALID_ARGUMENT", { address });
501
- }
502
-
503
- /**
504
- * Resolve whether this provider manages the address/index.
505
- * @param {number|string} address
506
- * @returns {Promise<boolean>}
507
- */
508
- async hasSigner(address) {
509
- const accounts = await this.send("eth_accounts", []);
510
- if (!Array.isArray(accounts)) return false;
511
- if (typeof address === "number") return address >= 0 && address < accounts.length;
512
- if (typeof address === "string") return accounts.some((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
513
- return false;
514
- }
515
- }
516
-
517
- /**
518
- * FallbackProvider - uses the first provider in the list.
519
- */
520
- class FallbackProvider extends AbstractProvider {
521
- /**
522
- * @param {AbstractProvider[]|AbstractProvider} providers
523
- */
524
- constructor(providers) {
525
- super();
526
- this.providers = Array.isArray(providers) ? providers : [providers];
527
- if (!this.providers.length) throw makeError("no providers provided", "INVALID_ARGUMENT", {});
528
- }
529
-
530
- async _perform(method, params) {
531
- // Simple strategy: try providers in order.
532
- let lastErr;
533
- for (const p of this.providers) {
534
- try {
535
- // eslint-disable-next-line no-await-in-loop
536
- return await p._perform(method, params);
537
- } catch (e) {
538
- lastErr = e;
539
- }
540
- }
541
- throw lastErr || makeError("all providers failed", "UNKNOWN_ERROR", { method });
542
- }
543
- }
544
-
545
- /**
546
- * FilterByBlockHash placeholder.
547
- */
548
- class FilterByBlockHash {
549
- constructor(blockHash, address, topics) {
550
- if (typeof blockHash !== "string" || !isHexString(blockHash, 32)) {
551
- throw makeError("invalid blockHash", "INVALID_ARGUMENT", { blockHash });
552
- }
553
- this.blockHash = normalizeHex(blockHash);
554
- if (address != null) this.address = address;
555
- if (topics != null) this.topics = topics;
556
- }
557
-
558
- toJSON() {
559
- // Ensure JSON serialization includes only the filter fields.
560
- return {
561
- blockHash: this.blockHash,
562
- address: this.address,
563
- topics: this.topics,
564
- };
565
- }
566
- }
567
-
568
- module.exports = {
569
- WebSocketProvider,
570
- IpcSocketProvider,
571
- BrowserProvider,
572
- FallbackProvider,
573
- FilterByBlockHash,
574
- };
575
-
1
+ /**
2
+ * @fileoverview Additional provider classes (compatibility stubs).
3
+ *
4
+ * These are included to match the API shape described in SPEC.md. Only
5
+ * JsonRpcProvider is fully implemented for network operations.
6
+ */
7
+
8
+ const { AbstractProvider } = require("./provider");
9
+ const { JsonRpcProvider } = require("./json-rpc-provider");
10
+ const { makeError } = require("../errors");
11
+ const net = require("node:net");
12
+ const { JsonRpcSigner } = require("../wallet/wallet");
13
+ const { normalizeHex, isHexString } = require("../internal/hex");
14
+
15
+ let _wsRpcId = 1;
16
+ let _ipcRpcId = 1;
17
+
18
+ /**
19
+ * Extract the first complete JSON object from a stream buffer.
20
+ *
21
+ * geth's IPC JSON-RPC typically uses newline-delimited JSON, but we also support
22
+ * non-newline framing by scanning for balanced braces and respecting strings.
23
+ *
24
+ * @param {string} buffer
25
+ * @returns {{ json: string, rest: string } | null}
26
+ */
27
+ function _extractFirstJsonObject(buffer) {
28
+ const start = buffer.indexOf("{");
29
+ if (start === -1) return null;
30
+
31
+ let depth = 0;
32
+ let inString = false;
33
+ let escape = false;
34
+
35
+ for (let i = start; i < buffer.length; i++) {
36
+ const ch = buffer[i];
37
+
38
+ if (inString) {
39
+ if (escape) {
40
+ escape = false;
41
+ continue;
42
+ }
43
+ if (ch === "\\") {
44
+ escape = true;
45
+ continue;
46
+ }
47
+ if (ch === '"') {
48
+ inString = false;
49
+ }
50
+ continue;
51
+ }
52
+
53
+ if (ch === '"') {
54
+ inString = true;
55
+ continue;
56
+ }
57
+
58
+ if (ch === "{") depth++;
59
+ if (ch === "}") depth--;
60
+
61
+ if (depth === 0) {
62
+ const json = buffer.slice(start, i + 1);
63
+ const rest = buffer.slice(i + 1);
64
+ return { json, rest };
65
+ }
66
+ }
67
+
68
+ return null;
69
+ }
70
+
71
+ class WebSocketProvider extends AbstractProvider {
72
+ /**
73
+ * Create a WebSocket JSON-RPC provider.
74
+ *
75
+ * This uses the built-in global `WebSocket` available in recent Node.js
76
+ * versions (via undici). No additional npm dependencies are required.
77
+ *
78
+ * @param {string} url WebSocket endpoint (e.g. ws://127.0.0.1:8546)
79
+ * @param {number=} chainId Optional chain id (compat)
80
+ */
81
+ constructor(url, chainId) {
82
+ super();
83
+ if (typeof url !== "string" || url.trim().length === 0) {
84
+ throw makeError("missing WebSocket url", "INVALID_ARGUMENT", { url });
85
+ }
86
+ this.url = url.trim();
87
+ this.chainId = chainId == null ? 123123 : chainId;
88
+
89
+ /** @type {any|null} */
90
+ this._ws = null;
91
+ /** @type {Promise<void>|null} */
92
+ this._wsReady = null;
93
+ /** @type {Map<number, { resolve: Function, reject: Function, timer: any }>} */
94
+ this._pending = new Map();
95
+ }
96
+
97
+ /**
98
+ * Close the underlying WebSocket connection and reject any pending requests.
99
+ * This is important for tests so the Node.js event loop can exit cleanly.
100
+ */
101
+ destroy() {
102
+ try {
103
+ if (this._ws && typeof this._ws.close === "function") this._ws.close();
104
+ } catch {
105
+ // ignore
106
+ }
107
+ this._ws = null;
108
+ this._wsReady = null;
109
+ this._rejectAllPending(makeError("WebSocket closed", "UNKNOWN_ERROR", { url: this.url }));
110
+ }
111
+
112
+ _rejectAllPending(err) {
113
+ for (const [id, p] of this._pending.entries()) {
114
+ try {
115
+ clearTimeout(p.timer);
116
+ p.reject(err);
117
+ } catch {
118
+ // ignore
119
+ }
120
+ this._pending.delete(id);
121
+ }
122
+ }
123
+
124
+ async _connect() {
125
+ if (this._ws && this._ws.readyState === 1) return;
126
+ if (this._wsReady) return this._wsReady;
127
+
128
+ const WS = globalThis.WebSocket;
129
+ if (typeof WS !== "function") {
130
+ throw makeError("WebSocket not available in this Node.js runtime", "NOT_IMPLEMENTED", {});
131
+ }
132
+
133
+ this._wsReady = new Promise((resolve, reject) => {
134
+ const ws = new WS(this.url);
135
+ this._ws = ws;
136
+
137
+ const connectTimer = setTimeout(() => {
138
+ try {
139
+ ws.close();
140
+ } catch {
141
+ // ignore
142
+ }
143
+ reject(makeError("WebSocket connect timeout", "UNKNOWN_ERROR", { url: this.url }));
144
+ }, 10_000);
145
+
146
+ const onOpen = () => {
147
+ clearTimeout(connectTimer);
148
+ resolve();
149
+ };
150
+
151
+ const onError = (event) => {
152
+ clearTimeout(connectTimer);
153
+ reject(
154
+ makeError("WebSocket error", "UNKNOWN_ERROR", {
155
+ url: this.url,
156
+ error: event && event.message ? event.message : String(event),
157
+ }),
158
+ );
159
+ };
160
+
161
+ const onClose = () => {
162
+ clearTimeout(connectTimer);
163
+ this._ws = null;
164
+ this._wsReady = null;
165
+ this._rejectAllPending(makeError("WebSocket closed", "UNKNOWN_ERROR", { url: this.url }));
166
+ };
167
+
168
+ const onMessage = (event) => {
169
+ const data = event && event.data != null ? event.data : "";
170
+ const text = typeof data === "string" ? data : data.toString();
171
+ let msg;
172
+ try {
173
+ msg = JSON.parse(text);
174
+ } catch {
175
+ return;
176
+ }
177
+ if (!msg || typeof msg !== "object") return;
178
+ if (typeof msg.id !== "number") return;
179
+
180
+ const pending = this._pending.get(msg.id);
181
+ if (!pending) return;
182
+ this._pending.delete(msg.id);
183
+ clearTimeout(pending.timer);
184
+
185
+ if (msg.error) {
186
+ pending.reject(makeError("JSON-RPC error", "UNKNOWN_ERROR", { error: msg.error }));
187
+ return;
188
+ }
189
+ pending.resolve(msg.result);
190
+ };
191
+
192
+ // Prefer addEventListener API, but fall back to on* properties if needed.
193
+ if (typeof ws.addEventListener === "function") {
194
+ ws.addEventListener("open", onOpen);
195
+ ws.addEventListener("error", onError);
196
+ ws.addEventListener("close", onClose);
197
+ ws.addEventListener("message", onMessage);
198
+ } else {
199
+ ws.onopen = onOpen;
200
+ ws.onerror = onError;
201
+ ws.onclose = onClose;
202
+ ws.onmessage = onMessage;
203
+ }
204
+ }).finally(() => {
205
+ // If connection failed, allow retries.
206
+ if (!this._ws || this._ws.readyState !== 1) {
207
+ this._ws = null;
208
+ this._wsReady = null;
209
+ }
210
+ });
211
+
212
+ return this._wsReady;
213
+ }
214
+
215
+ /**
216
+ * @param {string} method
217
+ * @param {any[]=} params
218
+ * @returns {Promise<any>}
219
+ */
220
+ async _perform(method, params) {
221
+ await this._connect();
222
+ const ws = this._ws;
223
+ if (!ws || ws.readyState !== 1) {
224
+ throw makeError("WebSocket not connected", "UNKNOWN_ERROR", { url: this.url, method });
225
+ }
226
+
227
+ const id = _wsRpcId++;
228
+ const body = JSON.stringify({ jsonrpc: "2.0", id, method, params: params || [] });
229
+
230
+ return new Promise((resolve, reject) => {
231
+ const timer = setTimeout(() => {
232
+ this._pending.delete(id);
233
+ reject(makeError("WebSocket JSON-RPC timeout", "UNKNOWN_ERROR", { url: this.url, method }));
234
+ }, 30_000);
235
+
236
+ this._pending.set(id, { resolve, reject, timer });
237
+
238
+ try {
239
+ ws.send(body);
240
+ } catch (e) {
241
+ clearTimeout(timer);
242
+ this._pending.delete(id);
243
+ reject(
244
+ makeError("WebSocket send failed", "UNKNOWN_ERROR", {
245
+ url: this.url,
246
+ method,
247
+ error: e && e.message ? e.message : String(e),
248
+ }),
249
+ );
250
+ }
251
+ });
252
+ }
253
+ }
254
+
255
+ class IpcSocketProvider extends AbstractProvider {
256
+ /**
257
+ * Create an IPC provider.
258
+ *
259
+ * On Windows, use a named pipe path like: `\\\\.\\pipe\\geth.ipc`
260
+ * On Unix, use a domain socket path like: `/path/to/geth.ipc`
261
+ *
262
+ * @param {string} path IPC socket path
263
+ */
264
+ constructor(path) {
265
+ super();
266
+ if (typeof path !== "string" || path.length === 0) {
267
+ throw makeError("missing IPC path", "INVALID_ARGUMENT", { path });
268
+ }
269
+ this.path = path;
270
+ }
271
+
272
+ /**
273
+ * @param {string} method
274
+ * @param {any[]=} params
275
+ * @returns {Promise<any>}
276
+ */
277
+ async _perform(method, params) {
278
+ const id = _ipcRpcId++;
279
+ const body =
280
+ JSON.stringify({
281
+ jsonrpc: "2.0",
282
+ id,
283
+ method,
284
+ params: params || [],
285
+ }) + "\n";
286
+
287
+ return new Promise((resolve, reject) => {
288
+ /** @type {boolean} */
289
+ let done = false;
290
+ /** @type {string} */
291
+ let buffer = "";
292
+
293
+ const socket = net.createConnection(this.path);
294
+ socket.setEncoding("utf8");
295
+ socket.setTimeout(30_000);
296
+
297
+ function finish(err, value) {
298
+ if (done) return;
299
+ done = true;
300
+ try {
301
+ socket.destroy();
302
+ } catch {
303
+ // ignore
304
+ }
305
+ if (err) reject(err);
306
+ else resolve(value);
307
+ }
308
+
309
+ socket.on("connect", () => {
310
+ try {
311
+ socket.write(body);
312
+ } catch (e) {
313
+ finish(
314
+ makeError("IPC write failed", "UNKNOWN_ERROR", {
315
+ method,
316
+ path: this.path,
317
+ error: e && e.message ? e.message : String(e),
318
+ }),
319
+ );
320
+ }
321
+ });
322
+
323
+ socket.on("timeout", () => {
324
+ finish(makeError("IPC request timeout", "UNKNOWN_ERROR", { method, path: this.path }));
325
+ });
326
+
327
+ socket.on("error", (e) => {
328
+ finish(
329
+ makeError("IPC socket error", "UNKNOWN_ERROR", {
330
+ method,
331
+ path: this.path,
332
+ error: e && e.message ? e.message : String(e),
333
+ }),
334
+ );
335
+ });
336
+
337
+ socket.on("data", (chunk) => {
338
+ buffer += String(chunk);
339
+
340
+ // Fast path: newline-delimited JSON responses.
341
+ while (buffer.includes("\n")) {
342
+ const idx = buffer.indexOf("\n");
343
+ const line = buffer.slice(0, idx).trim();
344
+ buffer = buffer.slice(idx + 1);
345
+ if (!line) continue;
346
+ try {
347
+ const json = JSON.parse(line);
348
+ if (json && json.id === id) {
349
+ if (json.error) {
350
+ finish(makeError("JSON-RPC error", "UNKNOWN_ERROR", { method, error: json.error }));
351
+ return;
352
+ }
353
+ finish(null, json.result);
354
+ return;
355
+ }
356
+ } catch {
357
+ // If parsing fails, fall through to brace-balanced extraction on the accumulated buffer.
358
+ buffer = line + "\n" + buffer;
359
+ break;
360
+ }
361
+ }
362
+
363
+ // Robust path: find a complete JSON object by balanced braces.
364
+ while (true) {
365
+ const extracted = _extractFirstJsonObject(buffer);
366
+ if (!extracted) return;
367
+ buffer = extracted.rest;
368
+ try {
369
+ const json = JSON.parse(extracted.json);
370
+ if (json && json.id === id) {
371
+ if (json.error) {
372
+ finish(makeError("JSON-RPC error", "UNKNOWN_ERROR", { method, error: json.error }));
373
+ return;
374
+ }
375
+ finish(null, json.result);
376
+ return;
377
+ }
378
+ } catch {
379
+ // ignore malformed object and continue scanning remainder
380
+ }
381
+ }
382
+ });
383
+
384
+ socket.on("end", () => {
385
+ if (!done) finish(makeError("IPC connection ended before response", "UNKNOWN_ERROR", { method, path: this.path }));
386
+ });
387
+ });
388
+ }
389
+ }
390
+
391
+ class BrowserProvider extends AbstractProvider {
392
+ /**
393
+ * Create a BrowserProvider from an EIP-1193 provider (e.g. MetaMask).
394
+ *
395
+ * This is a lightweight implementation that focuses on the core behaviors:
396
+ * - `send(method, params)` dispatches EIP-1193 requests
397
+ * - `getSigner()` resolves the connected account
398
+ * - emits ethers-like `debug` events for request/response tracking
399
+ *
400
+ * @param {{ request: Function }} eip1193Provider
401
+ * @param {any=} network Unused (compat)
402
+ * @param {{ providerInfo?: any }=} options
403
+ */
404
+ constructor(eip1193Provider, network, options) {
405
+ super();
406
+ void network;
407
+ this.provider = eip1193Provider;
408
+ this.providerInfo = options && options.providerInfo ? options.providerInfo : null;
409
+
410
+ if (!this.provider || typeof this.provider.request !== "function") {
411
+ throw makeError("invalid EIP-1193 provider (missing request)", "INVALID_ARGUMENT", { provider: this.provider });
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Send an EIP-1193 JSON-RPC request.
417
+ * @param {string} method
418
+ * @param {any[]|Record<string, any>=} params
419
+ * @returns {Promise<any>}
420
+ */
421
+ async send(method, params) {
422
+ const payload = { method, params: params == null ? [] : params };
423
+ this.emit("debug", { action: "sendEip1193Payload", payload });
424
+
425
+ try {
426
+ const result = await this.provider.request(payload);
427
+ this.emit("debug", { action: "receiveEip1193Result", result });
428
+ return result;
429
+ } catch (e) {
430
+ const err = this.getRpcError(payload, e);
431
+ this.emit("debug", { action: "receiveEip1193Error", error: err });
432
+ throw err;
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Map an EIP-1193 error into a normalized Error.
438
+ * @param {{ method: string, params?: any }} payload
439
+ * @param {any} error
440
+ * @returns {Error}
441
+ */
442
+ getRpcError(payload, error) {
443
+ if (error instanceof Error) return error;
444
+ return makeError("EIP-1193 error", "UNKNOWN_ERROR", { payload, error });
445
+ }
446
+
447
+ /**
448
+ * Ethers compatibility: internal send for single or batched payloads.
449
+ * @param {any|any[]} payload
450
+ * @returns {Promise<any>}
451
+ */
452
+ async _send(payload) {
453
+ if (Array.isArray(payload)) {
454
+ const out = [];
455
+ for (const p of payload) {
456
+ // eslint-disable-next-line no-await-in-loop
457
+ out.push(await this.send(p.method, p.params));
458
+ }
459
+ return out;
460
+ }
461
+ return this.send(payload.method, payload.params);
462
+ }
463
+
464
+ /**
465
+ * @param {string} method
466
+ * @param {any[]=} params
467
+ * @returns {Promise<any>}
468
+ */
469
+ async _perform(method, params) {
470
+ return this.send(method, params || []);
471
+ }
472
+
473
+ /**
474
+ * Returns a signer for the specified account index or address.
475
+ * @param {number|string=} address
476
+ * @returns {Promise<JsonRpcSigner>}
477
+ */
478
+ async getSigner(address) {
479
+ const accounts = await this.send("eth_accounts", []);
480
+ if (!Array.isArray(accounts) || accounts.length === 0) {
481
+ throw makeError("no accounts available from EIP-1193 provider", "UNKNOWN_ERROR", {});
482
+ }
483
+
484
+ if (address == null) {
485
+ return new JsonRpcSigner(this, accounts[0]);
486
+ }
487
+
488
+ if (typeof address === "number") {
489
+ const a = accounts[address];
490
+ if (!a) throw makeError("account index out of range", "INVALID_ARGUMENT", { address });
491
+ return new JsonRpcSigner(this, a);
492
+ }
493
+
494
+ if (typeof address === "string") {
495
+ const found = accounts.find((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
496
+ if (!found) throw makeError("account not found", "INVALID_ARGUMENT", { address });
497
+ return new JsonRpcSigner(this, found);
498
+ }
499
+
500
+ throw makeError("invalid signer address/index", "INVALID_ARGUMENT", { address });
501
+ }
502
+
503
+ /**
504
+ * Resolve whether this provider manages the address/index.
505
+ * @param {number|string} address
506
+ * @returns {Promise<boolean>}
507
+ */
508
+ async hasSigner(address) {
509
+ const accounts = await this.send("eth_accounts", []);
510
+ if (!Array.isArray(accounts)) return false;
511
+ if (typeof address === "number") return address >= 0 && address < accounts.length;
512
+ if (typeof address === "string") return accounts.some((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
513
+ return false;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * FallbackProvider - uses the first provider in the list.
519
+ */
520
+ class FallbackProvider extends AbstractProvider {
521
+ /**
522
+ * @param {AbstractProvider[]|AbstractProvider} providers
523
+ */
524
+ constructor(providers) {
525
+ super();
526
+ this.providers = Array.isArray(providers) ? providers : [providers];
527
+ if (!this.providers.length) throw makeError("no providers provided", "INVALID_ARGUMENT", {});
528
+ }
529
+
530
+ async _perform(method, params) {
531
+ // Simple strategy: try providers in order.
532
+ let lastErr;
533
+ for (const p of this.providers) {
534
+ try {
535
+ // eslint-disable-next-line no-await-in-loop
536
+ return await p._perform(method, params);
537
+ } catch (e) {
538
+ lastErr = e;
539
+ }
540
+ }
541
+ throw lastErr || makeError("all providers failed", "UNKNOWN_ERROR", { method });
542
+ }
543
+ }
544
+
545
+ /**
546
+ * FilterByBlockHash placeholder.
547
+ */
548
+ class FilterByBlockHash {
549
+ constructor(blockHash, address, topics) {
550
+ if (typeof blockHash !== "string" || !isHexString(blockHash, 32)) {
551
+ throw makeError("invalid blockHash", "INVALID_ARGUMENT", { blockHash });
552
+ }
553
+ this.blockHash = normalizeHex(blockHash);
554
+ if (address != null) this.address = address;
555
+ if (topics != null) this.topics = topics;
556
+ }
557
+
558
+ toJSON() {
559
+ // Ensure JSON serialization includes only the filter fields.
560
+ return {
561
+ blockHash: this.blockHash,
562
+ address: this.address,
563
+ topics: this.topics,
564
+ };
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Create a provider from an endpoint string. Detects connection type by scheme/path:
570
+ * - http:// or https:// → JsonRpcProvider
571
+ * - ws:// or wss:// → WebSocketProvider
572
+ * - otherwise (e.g. \\\\.\\pipe\\geth.ipc or /path/to/geth.ipc) → IpcSocketProvider
573
+ *
574
+ * @param {string=} endpoint - RPC URL (http/https), WebSocket URL (ws/wss), or IPC path. If omitted or empty, uses default from Config (HTTP).
575
+ * @param {number=} chainId - Chain ID (default 123123). Used for HTTP and WebSocket; ignored for IPC.
576
+ * @returns {AbstractProvider}
577
+ */
578
+ function getProvider(endpoint, chainId) {
579
+ const url = typeof endpoint === "string" ? endpoint.trim() : "";
580
+ const lower = url.toLowerCase();
581
+ if (!url || lower.startsWith("http://") || lower.startsWith("https://")) {
582
+ return new JsonRpcProvider(url || undefined, chainId);
583
+ }
584
+ if (lower.startsWith("ws://") || lower.startsWith("wss://")) {
585
+ return new WebSocketProvider(url, chainId);
586
+ }
587
+ // IPC path (e.g. \\.\pipe\geth.ipc on Windows or /path/to/geth.ipc on Unix)
588
+ if (!url) throw makeError("missing endpoint", "INVALID_ARGUMENT", { endpoint });
589
+ return new IpcSocketProvider(url);
590
+ }
591
+
592
+ module.exports = {
593
+ WebSocketProvider,
594
+ IpcSocketProvider,
595
+ BrowserProvider,
596
+ FallbackProvider,
597
+ FilterByBlockHash,
598
+ getProvider,
599
+ };
600
+