From edea0ec301f81a0ac2276e808179426f78147e47 Mon Sep 17 00:00:00 2001
From: Richard Moore <github@ricmoo.com>
Date: Fri, 2 Apr 2021 03:53:48 -0400
Subject: [PATCH] Added experimental _JsonRpcBatchProvider (#62, #656, #892).

---
 .../src.ts/json-rpc-batch-provider.ts         | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 packages/providers/src.ts/json-rpc-batch-provider.ts

diff --git a/packages/providers/src.ts/json-rpc-batch-provider.ts b/packages/providers/src.ts/json-rpc-batch-provider.ts
new file mode 100644
index 0000000000..29f3755584
--- /dev/null
+++ b/packages/providers/src.ts/json-rpc-batch-provider.ts
@@ -0,0 +1,97 @@
+
+import { deepCopy } from "@ethersproject/properties";
+import { fetchJson } from "@ethersproject/web";
+
+import { JsonRpcProvider } from "./json-rpc-provider";
+
+// Experimental
+
+export class _JsonRpcBatchProvider extends JsonRpcProvider {
+    _pendingBatchAggregator: NodeJS.Timer;
+    _pendingBatch: Array<{
+        request: { method: string, params: Array<any>, id: number, jsonrpc: "2.0" },
+        resolve: (result: any) => void,
+        reject: (error: Error) => void
+    }>;
+
+    send(method: string, params: Array<any>): Promise<any> {
+        const request = {
+            method: method,
+            params: params,
+            id: (this._nextId++),
+            jsonrpc: "2.0"
+        };
+
+        if (this._pendingBatch == null) {
+            this._pendingBatch = [ ];
+        }
+
+        const inflightRequest: any = { request, resolve: null, reject: null };
+
+        const promise = new Promise((resolve, reject) => {
+            inflightRequest.resolve = resolve;
+            inflightRequest.reject = reject;
+        });
+
+        this._pendingBatch.push(inflightRequest);
+
+        if (!this._pendingBatchAggregator) {
+            // Schedule batch for next event loop + short duration
+            this._pendingBatchAggregator = setTimeout(() => {
+
+                // Get teh current batch and clear it, so new requests
+                // go into the next batch
+                const batch = this._pendingBatch;
+                this._pendingBatch = null;
+                this._pendingBatchAggregator = null;
+
+                // Get the request as an array of requests
+                const request = batch.map((inflight) => inflight.request);
+
+                this.emit("debug", {
+                    action: "requestBatch",
+                    request: deepCopy(request),
+                    provider: this
+                });
+
+                return fetchJson(this.connection, JSON.stringify(request)).then((result) => {
+                    this.emit("debug", {
+                        action: "response",
+                        request: request,
+                        response: result,
+                        provider: this
+                    });
+
+                    // For each result, feed it to the correct Promise, depending
+                    // on whether it was a success or error
+                    batch.forEach((inflightRequest, index) => {
+                        const payload = result[index];
+                        if (payload.error) {
+                            const error = new Error(payload.error.message);
+                            (<any>error).code = payload.error.code;
+                            (<any>error).data = payload.error.data;
+                            inflightRequest.reject(error);
+                        } else {
+                            inflightRequest.resolve(payload.result);
+                        }
+                    });
+
+                }, (error) => {
+                    this.emit("debug", {
+                        action: "response",
+                        error: error,
+                        request: request,
+                        provider: this
+                    });
+
+                    batch.forEach((inflightRequest) => {
+                        inflightRequest.reject(error);
+                    });
+                });
+
+            }, 10);
+        }
+
+        return promise;
+    }
+}