1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | import { BigNumber } from "@ethersproject/bignumber";
|
12 | import { defineReadOnly } from "@ethersproject/properties";
|
13 | import { JsonRpcProvider } from "./json-rpc-provider";
|
14 | import { WebSocket } from "./ws";
|
15 | import { Logger } from "@ethersproject/logger";
|
16 | import { version } from "./_version";
|
17 | const logger = new Logger(version);
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | let NextId = 1;
|
33 |
|
34 |
|
35 | export class WebSocketProvider extends JsonRpcProvider {
|
36 | constructor(url, network) {
|
37 |
|
38 | if (network === "any") {
|
39 | logger.throwError("WebSocketProvider does not support 'any' network yet", Logger.errors.UNSUPPORTED_OPERATION, {
|
40 | operation: "network:any"
|
41 | });
|
42 | }
|
43 | if (typeof (url) === "string") {
|
44 | super(url, network);
|
45 | }
|
46 | else {
|
47 | super("_websocket", network);
|
48 | }
|
49 | this._pollingInterval = -1;
|
50 | this._wsReady = false;
|
51 | if (typeof (url) === "string") {
|
52 | defineReadOnly(this, "_websocket", new WebSocket(this.connection.url));
|
53 | }
|
54 | else {
|
55 | defineReadOnly(this, "_websocket", url);
|
56 | }
|
57 | defineReadOnly(this, "_requests", {});
|
58 | defineReadOnly(this, "_subs", {});
|
59 | defineReadOnly(this, "_subIds", {});
|
60 | defineReadOnly(this, "_detectNetwork", super.detectNetwork());
|
61 |
|
62 | this.websocket.onopen = () => {
|
63 | this._wsReady = true;
|
64 | Object.keys(this._requests).forEach((id) => {
|
65 | this.websocket.send(this._requests[id].payload);
|
66 | });
|
67 | };
|
68 | this.websocket.onmessage = (messageEvent) => {
|
69 | const data = messageEvent.data;
|
70 | const result = JSON.parse(data);
|
71 | if (result.id != null) {
|
72 | const id = String(result.id);
|
73 | const request = this._requests[id];
|
74 | delete this._requests[id];
|
75 | if (result.result !== undefined) {
|
76 | request.callback(null, result.result);
|
77 | this.emit("debug", {
|
78 | action: "response",
|
79 | request: JSON.parse(request.payload),
|
80 | response: result.result,
|
81 | provider: this
|
82 | });
|
83 | }
|
84 | else {
|
85 | let error = null;
|
86 | if (result.error) {
|
87 | error = new Error(result.error.message || "unknown error");
|
88 | defineReadOnly(error, "code", result.error.code || null);
|
89 | defineReadOnly(error, "response", data);
|
90 | }
|
91 | else {
|
92 | error = new Error("unknown error");
|
93 | }
|
94 | request.callback(error, undefined);
|
95 | this.emit("debug", {
|
96 | action: "response",
|
97 | error: error,
|
98 | request: JSON.parse(request.payload),
|
99 | provider: this
|
100 | });
|
101 | }
|
102 | }
|
103 | else if (result.method === "eth_subscription") {
|
104 |
|
105 | const sub = this._subs[result.params.subscription];
|
106 | if (sub) {
|
107 |
|
108 | sub.processFunc(result.params.result);
|
109 | }
|
110 | }
|
111 | else {
|
112 | console.warn("this should not happen");
|
113 | }
|
114 | };
|
115 |
|
116 |
|
117 |
|
118 | const fauxPoll = setInterval(() => {
|
119 | this.emit("poll");
|
120 | }, 1000);
|
121 | if (fauxPoll.unref) {
|
122 | fauxPoll.unref();
|
123 | }
|
124 | }
|
125 |
|
126 |
|
127 | get websocket() { return this._websocket; }
|
128 | detectNetwork() {
|
129 | return this._detectNetwork;
|
130 | }
|
131 | get pollingInterval() {
|
132 | return 0;
|
133 | }
|
134 | resetEventsBlock(blockNumber) {
|
135 | logger.throwError("cannot reset events block on WebSocketProvider", Logger.errors.UNSUPPORTED_OPERATION, {
|
136 | operation: "resetEventBlock"
|
137 | });
|
138 | }
|
139 | set pollingInterval(value) {
|
140 | logger.throwError("cannot set polling interval on WebSocketProvider", Logger.errors.UNSUPPORTED_OPERATION, {
|
141 | operation: "setPollingInterval"
|
142 | });
|
143 | }
|
144 | poll() {
|
145 | return __awaiter(this, void 0, void 0, function* () {
|
146 | return null;
|
147 | });
|
148 | }
|
149 | set polling(value) {
|
150 | if (!value) {
|
151 | return;
|
152 | }
|
153 | logger.throwError("cannot set polling on WebSocketProvider", Logger.errors.UNSUPPORTED_OPERATION, {
|
154 | operation: "setPolling"
|
155 | });
|
156 | }
|
157 | send(method, params) {
|
158 | const rid = NextId++;
|
159 | return new Promise((resolve, reject) => {
|
160 | function callback(error, result) {
|
161 | if (error) {
|
162 | return reject(error);
|
163 | }
|
164 | return resolve(result);
|
165 | }
|
166 | const payload = JSON.stringify({
|
167 | method: method,
|
168 | params: params,
|
169 | id: rid,
|
170 | jsonrpc: "2.0"
|
171 | });
|
172 | this.emit("debug", {
|
173 | action: "request",
|
174 | request: JSON.parse(payload),
|
175 | provider: this
|
176 | });
|
177 | this._requests[String(rid)] = { callback, payload };
|
178 | if (this._wsReady) {
|
179 | this.websocket.send(payload);
|
180 | }
|
181 | });
|
182 | }
|
183 | static defaultUrl() {
|
184 | return "ws:/\/localhost:8546";
|
185 | }
|
186 | _subscribe(tag, param, processFunc) {
|
187 | return __awaiter(this, void 0, void 0, function* () {
|
188 | let subIdPromise = this._subIds[tag];
|
189 | if (subIdPromise == null) {
|
190 | subIdPromise = Promise.all(param).then((param) => {
|
191 | return this.send("eth_subscribe", param);
|
192 | });
|
193 | this._subIds[tag] = subIdPromise;
|
194 | }
|
195 | const subId = yield subIdPromise;
|
196 | this._subs[subId] = { tag, processFunc };
|
197 | });
|
198 | }
|
199 | _startEvent(event) {
|
200 | switch (event.type) {
|
201 | case "block":
|
202 | this._subscribe("block", ["newHeads"], (result) => {
|
203 | const blockNumber = BigNumber.from(result.number).toNumber();
|
204 | this._emitted.block = blockNumber;
|
205 | this.emit("block", blockNumber);
|
206 | });
|
207 | break;
|
208 | case "pending":
|
209 | this._subscribe("pending", ["newPendingTransactions"], (result) => {
|
210 | this.emit("pending", result);
|
211 | });
|
212 | break;
|
213 | case "filter":
|
214 | this._subscribe(event.tag, ["logs", this._getFilter(event.filter)], (result) => {
|
215 | if (result.removed == null) {
|
216 | result.removed = false;
|
217 | }
|
218 | this.emit(event.filter, this.formatter.filterLog(result));
|
219 | });
|
220 | break;
|
221 | case "tx": {
|
222 | const emitReceipt = (event) => {
|
223 | const hash = event.hash;
|
224 | this.getTransactionReceipt(hash).then((receipt) => {
|
225 | if (!receipt) {
|
226 | return;
|
227 | }
|
228 | this.emit(hash, receipt);
|
229 | });
|
230 | };
|
231 |
|
232 | emitReceipt(event);
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | this._subscribe("tx", ["newHeads"], (result) => {
|
238 | this._events.filter((e) => (e.type === "tx")).forEach(emitReceipt);
|
239 | });
|
240 | break;
|
241 | }
|
242 |
|
243 | case "debug":
|
244 | case "poll":
|
245 | case "willPoll":
|
246 | case "didPoll":
|
247 | case "error":
|
248 | break;
|
249 | default:
|
250 | console.log("unhandled:", event);
|
251 | break;
|
252 | }
|
253 | }
|
254 | _stopEvent(event) {
|
255 | let tag = event.tag;
|
256 | if (event.type === "tx") {
|
257 |
|
258 | if (this._events.filter((e) => (e.type === "tx")).length) {
|
259 | return;
|
260 | }
|
261 | tag = "tx";
|
262 | }
|
263 | else if (this.listenerCount(event.event)) {
|
264 |
|
265 | return;
|
266 | }
|
267 | const subId = this._subIds[tag];
|
268 | if (!subId) {
|
269 | return;
|
270 | }
|
271 | delete this._subIds[tag];
|
272 | subId.then((subId) => {
|
273 | if (!this._subs[subId]) {
|
274 | return;
|
275 | }
|
276 | delete this._subs[subId];
|
277 | this.send("eth_unsubscribe", [subId]);
|
278 | });
|
279 | }
|
280 | destroy() {
|
281 | return __awaiter(this, void 0, void 0, function* () {
|
282 |
|
283 | if (this.websocket.readyState === WebSocket.CONNECTING) {
|
284 | yield (new Promise((resolve) => {
|
285 | this.websocket.onopen = function () {
|
286 | resolve(true);
|
287 | };
|
288 | this.websocket.onerror = function () {
|
289 | resolve(false);
|
290 | };
|
291 | }));
|
292 | }
|
293 |
|
294 |
|
295 | this.websocket.close(1000);
|
296 | });
|
297 | }
|
298 | }
|
299 |
|
\ | No newline at end of file |