UNPKG

16.9 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = function (d, b) {
4 extendStatics = Object.setPrototypeOf ||
5 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7 return extendStatics(d, b);
8 };
9 return function (d, b) {
10 if (typeof b !== "function" && b !== null)
11 throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12 extendStatics(d, b);
13 function __() { this.constructor = d; }
14 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15 };
16})();
17var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19 return new (P || (P = Promise))(function (resolve, reject) {
20 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23 step((generator = generator.apply(thisArg, _arguments || [])).next());
24 });
25};
26var __generator = (this && this.__generator) || function (thisArg, body) {
27 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29 function verb(n) { return function (v) { return step([n, v]); }; }
30 function step(op) {
31 if (f) throw new TypeError("Generator is already executing.");
32 while (_) try {
33 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34 if (y = 0, t) op = [op[0] & 2, t.value];
35 switch (op[0]) {
36 case 0: case 1: t = op; break;
37 case 4: _.label++; return { value: op[1], done: false };
38 case 5: _.label++; y = op[1]; op = [0]; continue;
39 case 7: op = _.ops.pop(); _.trys.pop(); continue;
40 default:
41 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45 if (t[2]) _.ops.pop();
46 _.trys.pop(); continue;
47 }
48 op = body.call(thisArg, _);
49 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51 }
52};
53Object.defineProperty(exports, "__esModule", { value: true });
54exports.WebSocketProvider = void 0;
55var bignumber_1 = require("@ethersproject/bignumber");
56var properties_1 = require("@ethersproject/properties");
57var json_rpc_provider_1 = require("./json-rpc-provider");
58var ws_1 = require("./ws");
59var logger_1 = require("@ethersproject/logger");
60var _version_1 = require("./_version");
61var logger = new logger_1.Logger(_version_1.version);
62/**
63 * Notes:
64 *
65 * This provider differs a bit from the polling providers. One main
66 * difference is how it handles consistency. The polling providers
67 * will stall responses to ensure a consistent state, while this
68 * WebSocket provider assumes the connected backend will manage this.
69 *
70 * For example, if a polling provider emits an event which indicates
71 * the event occurred in blockhash XXX, a call to fetch that block by
72 * its hash XXX, if not present will retry until it is present. This
73 * can occur when querying a pool of nodes that are mildly out of sync
74 * with each other.
75 */
76var NextId = 1;
77// For more info about the Real-time Event API see:
78// https://geth.ethereum.org/docs/rpc/pubsub
79var WebSocketProvider = /** @class */ (function (_super) {
80 __extends(WebSocketProvider, _super);
81 function WebSocketProvider(url, network) {
82 var _this = this;
83 // This will be added in the future; please open an issue to expedite
84 if (network === "any") {
85 logger.throwError("WebSocketProvider does not support 'any' network yet", logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
86 operation: "network:any"
87 });
88 }
89 if (typeof (url) === "string") {
90 _this = _super.call(this, url, network) || this;
91 }
92 else {
93 _this = _super.call(this, "_websocket", network) || this;
94 }
95 _this._pollingInterval = -1;
96 _this._wsReady = false;
97 if (typeof (url) === "string") {
98 (0, properties_1.defineReadOnly)(_this, "_websocket", new ws_1.WebSocket(_this.connection.url));
99 }
100 else {
101 (0, properties_1.defineReadOnly)(_this, "_websocket", url);
102 }
103 (0, properties_1.defineReadOnly)(_this, "_requests", {});
104 (0, properties_1.defineReadOnly)(_this, "_subs", {});
105 (0, properties_1.defineReadOnly)(_this, "_subIds", {});
106 (0, properties_1.defineReadOnly)(_this, "_detectNetwork", _super.prototype.detectNetwork.call(_this));
107 // Stall sending requests until the socket is open...
108 _this.websocket.onopen = function () {
109 _this._wsReady = true;
110 Object.keys(_this._requests).forEach(function (id) {
111 _this.websocket.send(_this._requests[id].payload);
112 });
113 };
114 _this.websocket.onmessage = function (messageEvent) {
115 var data = messageEvent.data;
116 var result = JSON.parse(data);
117 if (result.id != null) {
118 var id = String(result.id);
119 var request = _this._requests[id];
120 delete _this._requests[id];
121 if (result.result !== undefined) {
122 request.callback(null, result.result);
123 _this.emit("debug", {
124 action: "response",
125 request: JSON.parse(request.payload),
126 response: result.result,
127 provider: _this
128 });
129 }
130 else {
131 var error = null;
132 if (result.error) {
133 error = new Error(result.error.message || "unknown error");
134 (0, properties_1.defineReadOnly)(error, "code", result.error.code || null);
135 (0, properties_1.defineReadOnly)(error, "response", data);
136 }
137 else {
138 error = new Error("unknown error");
139 }
140 request.callback(error, undefined);
141 _this.emit("debug", {
142 action: "response",
143 error: error,
144 request: JSON.parse(request.payload),
145 provider: _this
146 });
147 }
148 }
149 else if (result.method === "eth_subscription") {
150 // Subscription...
151 var sub = _this._subs[result.params.subscription];
152 if (sub) {
153 //this.emit.apply(this, );
154 sub.processFunc(result.params.result);
155 }
156 }
157 else {
158 console.warn("this should not happen");
159 }
160 };
161 // This Provider does not actually poll, but we want to trigger
162 // poll events for things that depend on them (like stalling for
163 // block and transaction lookups)
164 var fauxPoll = setInterval(function () {
165 _this.emit("poll");
166 }, 1000);
167 if (fauxPoll.unref) {
168 fauxPoll.unref();
169 }
170 return _this;
171 }
172 Object.defineProperty(WebSocketProvider.prototype, "websocket", {
173 // Cannot narrow the type of _websocket, as that is not backwards compatible
174 // so we add a getter and let the WebSocket be a public API.
175 get: function () { return this._websocket; },
176 enumerable: false,
177 configurable: true
178 });
179 WebSocketProvider.prototype.detectNetwork = function () {
180 return this._detectNetwork;
181 };
182 Object.defineProperty(WebSocketProvider.prototype, "pollingInterval", {
183 get: function () {
184 return 0;
185 },
186 set: function (value) {
187 logger.throwError("cannot set polling interval on WebSocketProvider", logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
188 operation: "setPollingInterval"
189 });
190 },
191 enumerable: false,
192 configurable: true
193 });
194 WebSocketProvider.prototype.resetEventsBlock = function (blockNumber) {
195 logger.throwError("cannot reset events block on WebSocketProvider", logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
196 operation: "resetEventBlock"
197 });
198 };
199 WebSocketProvider.prototype.poll = function () {
200 return __awaiter(this, void 0, void 0, function () {
201 return __generator(this, function (_a) {
202 return [2 /*return*/, null];
203 });
204 });
205 };
206 Object.defineProperty(WebSocketProvider.prototype, "polling", {
207 set: function (value) {
208 if (!value) {
209 return;
210 }
211 logger.throwError("cannot set polling on WebSocketProvider", logger_1.Logger.errors.UNSUPPORTED_OPERATION, {
212 operation: "setPolling"
213 });
214 },
215 enumerable: false,
216 configurable: true
217 });
218 WebSocketProvider.prototype.send = function (method, params) {
219 var _this = this;
220 var rid = NextId++;
221 return new Promise(function (resolve, reject) {
222 function callback(error, result) {
223 if (error) {
224 return reject(error);
225 }
226 return resolve(result);
227 }
228 var payload = JSON.stringify({
229 method: method,
230 params: params,
231 id: rid,
232 jsonrpc: "2.0"
233 });
234 _this.emit("debug", {
235 action: "request",
236 request: JSON.parse(payload),
237 provider: _this
238 });
239 _this._requests[String(rid)] = { callback: callback, payload: payload };
240 if (_this._wsReady) {
241 _this.websocket.send(payload);
242 }
243 });
244 };
245 WebSocketProvider.defaultUrl = function () {
246 return "ws:/\/localhost:8546";
247 };
248 WebSocketProvider.prototype._subscribe = function (tag, param, processFunc) {
249 return __awaiter(this, void 0, void 0, function () {
250 var subIdPromise, subId;
251 var _this = this;
252 return __generator(this, function (_a) {
253 switch (_a.label) {
254 case 0:
255 subIdPromise = this._subIds[tag];
256 if (subIdPromise == null) {
257 subIdPromise = Promise.all(param).then(function (param) {
258 return _this.send("eth_subscribe", param);
259 });
260 this._subIds[tag] = subIdPromise;
261 }
262 return [4 /*yield*/, subIdPromise];
263 case 1:
264 subId = _a.sent();
265 this._subs[subId] = { tag: tag, processFunc: processFunc };
266 return [2 /*return*/];
267 }
268 });
269 });
270 };
271 WebSocketProvider.prototype._startEvent = function (event) {
272 var _this = this;
273 switch (event.type) {
274 case "block":
275 this._subscribe("block", ["newHeads"], function (result) {
276 var blockNumber = bignumber_1.BigNumber.from(result.number).toNumber();
277 _this._emitted.block = blockNumber;
278 _this.emit("block", blockNumber);
279 });
280 break;
281 case "pending":
282 this._subscribe("pending", ["newPendingTransactions"], function (result) {
283 _this.emit("pending", result);
284 });
285 break;
286 case "filter":
287 this._subscribe(event.tag, ["logs", this._getFilter(event.filter)], function (result) {
288 if (result.removed == null) {
289 result.removed = false;
290 }
291 _this.emit(event.filter, _this.formatter.filterLog(result));
292 });
293 break;
294 case "tx": {
295 var emitReceipt_1 = function (event) {
296 var hash = event.hash;
297 _this.getTransactionReceipt(hash).then(function (receipt) {
298 if (!receipt) {
299 return;
300 }
301 _this.emit(hash, receipt);
302 });
303 };
304 // In case it is already mined
305 emitReceipt_1(event);
306 // To keep things simple, we start up a single newHeads subscription
307 // to keep an eye out for transactions we are watching for.
308 // Starting a subscription for an event (i.e. "tx") that is already
309 // running is (basically) a nop.
310 this._subscribe("tx", ["newHeads"], function (result) {
311 _this._events.filter(function (e) { return (e.type === "tx"); }).forEach(emitReceipt_1);
312 });
313 break;
314 }
315 // Nothing is needed
316 case "debug":
317 case "poll":
318 case "willPoll":
319 case "didPoll":
320 case "error":
321 break;
322 default:
323 console.log("unhandled:", event);
324 break;
325 }
326 };
327 WebSocketProvider.prototype._stopEvent = function (event) {
328 var _this = this;
329 var tag = event.tag;
330 if (event.type === "tx") {
331 // There are remaining transaction event listeners
332 if (this._events.filter(function (e) { return (e.type === "tx"); }).length) {
333 return;
334 }
335 tag = "tx";
336 }
337 else if (this.listenerCount(event.event)) {
338 // There are remaining event listeners
339 return;
340 }
341 var subId = this._subIds[tag];
342 if (!subId) {
343 return;
344 }
345 delete this._subIds[tag];
346 subId.then(function (subId) {
347 if (!_this._subs[subId]) {
348 return;
349 }
350 delete _this._subs[subId];
351 _this.send("eth_unsubscribe", [subId]);
352 });
353 };
354 WebSocketProvider.prototype.destroy = function () {
355 return __awaiter(this, void 0, void 0, function () {
356 var _this = this;
357 return __generator(this, function (_a) {
358 switch (_a.label) {
359 case 0:
360 if (!(this.websocket.readyState === ws_1.WebSocket.CONNECTING)) return [3 /*break*/, 2];
361 return [4 /*yield*/, (new Promise(function (resolve) {
362 _this.websocket.onopen = function () {
363 resolve(true);
364 };
365 _this.websocket.onerror = function () {
366 resolve(false);
367 };
368 }))];
369 case 1:
370 _a.sent();
371 _a.label = 2;
372 case 2:
373 // Hangup
374 // See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
375 this.websocket.close(1000);
376 return [2 /*return*/];
377 }
378 });
379 });
380 };
381 return WebSocketProvider;
382}(json_rpc_provider_1.JsonRpcProvider));
383exports.WebSocketProvider = WebSocketProvider;
384//# sourceMappingURL=websocket-provider.js.map
\No newline at end of file