| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954 | 
							- /**
 
-  *  One of the most common ways to interact with the blockchain is
 
-  *  by a node running a JSON-RPC interface which can be connected to,
 
-  *  based on the transport, using:
 
-  *
 
-  *  - HTTP or HTTPS - [[JsonRpcProvider]]
 
-  *  - WebSocket - [[WebSocketProvider]]
 
-  *  - IPC - [[IpcSocketProvider]]
 
-  *
 
-  * @_section: api/providers/jsonrpc:JSON-RPC Provider  [about-jsonrpcProvider]
 
-  */
 
- // @TODO:
 
- // - Add the batching API
 
- // https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=true&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false
 
- import { AbiCoder } from "../abi/index.js";
 
- import { getAddress, resolveAddress } from "../address/index.js";
 
- import { TypedDataEncoder } from "../hash/index.js";
 
- import { accessListify } from "../transaction/index.js";
 
- import { defineProperties, getBigInt, hexlify, isHexString, toQuantity, toUtf8Bytes, isError, makeError, assert, assertArgument, FetchRequest, resolveProperties } from "../utils/index.js";
 
- import { AbstractProvider, UnmanagedSubscriber } from "./abstract-provider.js";
 
- import { AbstractSigner } from "./abstract-signer.js";
 
- import { Network } from "./network.js";
 
- import { FilterIdEventSubscriber, FilterIdPendingSubscriber } from "./subscriber-filterid.js";
 
- import { PollingEventSubscriber } from "./subscriber-polling.js";
 
- const Primitive = "bigint,boolean,function,number,string,symbol".split(/,/g);
 
- //const Methods = "getAddress,then".split(/,/g);
 
- function deepCopy(value) {
 
-     if (value == null || Primitive.indexOf(typeof (value)) >= 0) {
 
-         return value;
 
-     }
 
-     // Keep any Addressable
 
-     if (typeof (value.getAddress) === "function") {
 
-         return value;
 
-     }
 
-     if (Array.isArray(value)) {
 
-         return (value.map(deepCopy));
 
-     }
 
-     if (typeof (value) === "object") {
 
-         return Object.keys(value).reduce((accum, key) => {
 
-             accum[key] = value[key];
 
-             return accum;
 
-         }, {});
 
-     }
 
-     throw new Error(`should not happen: ${value} (${typeof (value)})`);
 
- }
 
- function stall(duration) {
 
-     return new Promise((resolve) => { setTimeout(resolve, duration); });
 
- }
 
- function getLowerCase(value) {
 
-     if (value) {
 
-         return value.toLowerCase();
 
-     }
 
-     return value;
 
- }
 
- function isPollable(value) {
 
-     return (value && typeof (value.pollingInterval) === "number");
 
- }
 
- const defaultOptions = {
 
-     polling: false,
 
-     staticNetwork: null,
 
-     batchStallTime: 10,
 
-     batchMaxSize: (1 << 20),
 
-     batchMaxCount: 100,
 
-     cacheTimeout: 250,
 
-     pollingInterval: 4000
 
- };
 
- // @TODO: Unchecked Signers
 
- export class JsonRpcSigner extends AbstractSigner {
 
-     address;
 
-     constructor(provider, address) {
 
-         super(provider);
 
-         address = getAddress(address);
 
-         defineProperties(this, { address });
 
-     }
 
-     connect(provider) {
 
-         assert(false, "cannot reconnect JsonRpcSigner", "UNSUPPORTED_OPERATION", {
 
-             operation: "signer.connect"
 
-         });
 
-     }
 
-     async getAddress() {
 
-         return this.address;
 
-     }
 
-     // JSON-RPC will automatially fill in nonce, etc. so we just check from
 
-     async populateTransaction(tx) {
 
-         return await this.populateCall(tx);
 
-     }
 
-     // Returns just the hash of the transaction after sent, which is what
 
-     // the bare JSON-RPC API does;
 
-     async sendUncheckedTransaction(_tx) {
 
-         const tx = deepCopy(_tx);
 
-         const promises = [];
 
-         // Make sure the from matches the sender
 
-         if (tx.from) {
 
-             const _from = tx.from;
 
-             promises.push((async () => {
 
-                 const from = await resolveAddress(_from, this.provider);
 
-                 assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), "from address mismatch", "transaction", _tx);
 
-                 tx.from = from;
 
-             })());
 
-         }
 
-         else {
 
-             tx.from = this.address;
 
-         }
 
-         // The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
 
-         // wishes to use this, it is easy to specify explicitly, otherwise
 
-         // we look it up for them.
 
-         if (tx.gasLimit == null) {
 
-             promises.push((async () => {
 
-                 tx.gasLimit = await this.provider.estimateGas({ ...tx, from: this.address });
 
-             })());
 
-         }
 
-         // The address may be an ENS name or Addressable
 
-         if (tx.to != null) {
 
-             const _to = tx.to;
 
-             promises.push((async () => {
 
-                 tx.to = await resolveAddress(_to, this.provider);
 
-             })());
 
-         }
 
-         // Wait until all of our properties are filled in
 
-         if (promises.length) {
 
-             await Promise.all(promises);
 
-         }
 
-         const hexTx = this.provider.getRpcTransaction(tx);
 
-         return this.provider.send("eth_sendTransaction", [hexTx]);
 
-     }
 
-     async sendTransaction(tx) {
 
-         // This cannot be mined any earlier than any recent block
 
-         const blockNumber = await this.provider.getBlockNumber();
 
-         // Send the transaction
 
-         const hash = await this.sendUncheckedTransaction(tx);
 
-         // Unfortunately, JSON-RPC only provides and opaque transaction hash
 
-         // for a response, and we need the actual transaction, so we poll
 
-         // for it; it should show up very quickly
 
-         return await (new Promise((resolve, reject) => {
 
-             const timeouts = [1000, 100];
 
-             let invalids = 0;
 
-             const checkTx = async () => {
 
-                 try {
 
-                     // Try getting the transaction
 
-                     const tx = await this.provider.getTransaction(hash);
 
-                     if (tx != null) {
 
-                         resolve(tx.replaceableTransaction(blockNumber));
 
-                         return;
 
-                     }
 
-                 }
 
-                 catch (error) {
 
-                     // If we were cancelled: stop polling.
 
-                     // If the data is bad: the node returns bad transactions
 
-                     // If the network changed: calling again will also fail
 
-                     // If unsupported: likely destroyed
 
-                     if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") ||
 
-                         isError(error, "NETWORK_ERROR") || isError(error, "UNSUPPORTED_OPERATION")) {
 
-                         if (error.info == null) {
 
-                             error.info = {};
 
-                         }
 
-                         error.info.sendTransactionHash = hash;
 
-                         reject(error);
 
-                         return;
 
-                     }
 
-                     // Stop-gap for misbehaving backends; see #4513
 
-                     if (isError(error, "INVALID_ARGUMENT")) {
 
-                         invalids++;
 
-                         if (error.info == null) {
 
-                             error.info = {};
 
-                         }
 
-                         error.info.sendTransactionHash = hash;
 
-                         if (invalids > 10) {
 
-                             reject(error);
 
-                             return;
 
-                         }
 
-                     }
 
-                     // Notify anyone that cares; but we will try again, since
 
-                     // it is likely an intermittent service error
 
-                     this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
 
-                 }
 
-                 // Wait another 4 seconds
 
-                 this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
 
-             };
 
-             checkTx();
 
-         }));
 
-     }
 
-     async signTransaction(_tx) {
 
-         const tx = deepCopy(_tx);
 
-         // Make sure the from matches the sender
 
-         if (tx.from) {
 
-             const from = await resolveAddress(tx.from, this.provider);
 
-             assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), "from address mismatch", "transaction", _tx);
 
-             tx.from = from;
 
-         }
 
-         else {
 
-             tx.from = this.address;
 
-         }
 
-         const hexTx = this.provider.getRpcTransaction(tx);
 
-         return await this.provider.send("eth_signTransaction", [hexTx]);
 
-     }
 
-     async signMessage(_message) {
 
-         const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message);
 
-         return await this.provider.send("personal_sign", [
 
-             hexlify(message), this.address.toLowerCase()
 
-         ]);
 
-     }
 
-     async signTypedData(domain, types, _value) {
 
-         const value = deepCopy(_value);
 
-         // Populate any ENS names (in-place)
 
-         const populated = await TypedDataEncoder.resolveNames(domain, types, value, async (value) => {
 
-             const address = await resolveAddress(value);
 
-             assertArgument(address != null, "TypedData does not support null address", "value", value);
 
-             return address;
 
-         });
 
-         return await this.provider.send("eth_signTypedData_v4", [
 
-             this.address.toLowerCase(),
 
-             JSON.stringify(TypedDataEncoder.getPayload(populated.domain, types, populated.value))
 
-         ]);
 
-     }
 
-     async unlock(password) {
 
-         return this.provider.send("personal_unlockAccount", [
 
-             this.address.toLowerCase(), password, null
 
-         ]);
 
-     }
 
-     // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
 
-     async _legacySignMessage(_message) {
 
-         const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message);
 
-         return await this.provider.send("eth_sign", [
 
-             this.address.toLowerCase(), hexlify(message)
 
-         ]);
 
-     }
 
- }
 
- /**
 
-  *  The JsonRpcApiProvider is an abstract class and **MUST** be
 
-  *  sub-classed.
 
-  *
 
-  *  It provides the base for all JSON-RPC-based Provider interaction.
 
-  *
 
-  *  Sub-classing Notes:
 
-  *  - a sub-class MUST override _send
 
-  *  - a sub-class MUST call the `_start()` method once connected
 
-  */
 
- export class JsonRpcApiProvider extends AbstractProvider {
 
-     #options;
 
-     // The next ID to use for the JSON-RPC ID field
 
-     #nextId;
 
-     // Payloads are queued and triggered in batches using the drainTimer
 
-     #payloads;
 
-     #drainTimer;
 
-     #notReady;
 
-     #network;
 
-     #pendingDetectNetwork;
 
-     #scheduleDrain() {
 
-         if (this.#drainTimer) {
 
-             return;
 
-         }
 
-         // If we aren't using batching, no harm in sending it immediately
 
-         const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
 
-         this.#drainTimer = setTimeout(() => {
 
-             this.#drainTimer = null;
 
-             const payloads = this.#payloads;
 
-             this.#payloads = [];
 
-             while (payloads.length) {
 
-                 // Create payload batches that satisfy our batch constraints
 
-                 const batch = [(payloads.shift())];
 
-                 while (payloads.length) {
 
-                     if (batch.length === this.#options.batchMaxCount) {
 
-                         break;
 
-                     }
 
-                     batch.push((payloads.shift()));
 
-                     const bytes = JSON.stringify(batch.map((p) => p.payload));
 
-                     if (bytes.length > this.#options.batchMaxSize) {
 
-                         payloads.unshift((batch.pop()));
 
-                         break;
 
-                     }
 
-                 }
 
-                 // Process the result to each payload
 
-                 (async () => {
 
-                     const payload = ((batch.length === 1) ? batch[0].payload : batch.map((p) => p.payload));
 
-                     this.emit("debug", { action: "sendRpcPayload", payload });
 
-                     try {
 
-                         const result = await this._send(payload);
 
-                         this.emit("debug", { action: "receiveRpcResult", result });
 
-                         // Process results in batch order
 
-                         for (const { resolve, reject, payload } of batch) {
 
-                             if (this.destroyed) {
 
-                                 reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
 
-                                 continue;
 
-                             }
 
-                             // Find the matching result
 
-                             const resp = result.filter((r) => (r.id === payload.id))[0];
 
-                             // No result; the node failed us in unexpected ways
 
-                             if (resp == null) {
 
-                                 const error = makeError("missing response for request", "BAD_DATA", {
 
-                                     value: result, info: { payload }
 
-                                 });
 
-                                 this.emit("error", error);
 
-                                 reject(error);
 
-                                 continue;
 
-                             }
 
-                             // The response is an error
 
-                             if ("error" in resp) {
 
-                                 reject(this.getRpcError(payload, resp));
 
-                                 continue;
 
-                             }
 
-                             // All good; send the result
 
-                             resolve(resp.result);
 
-                         }
 
-                     }
 
-                     catch (error) {
 
-                         this.emit("debug", { action: "receiveRpcError", error });
 
-                         for (const { reject } of batch) {
 
-                             // @TODO: augment the error with the payload
 
-                             reject(error);
 
-                         }
 
-                     }
 
-                 })();
 
-             }
 
-         }, stallTime);
 
-     }
 
-     constructor(network, options) {
 
-         super(network, options);
 
-         this.#nextId = 1;
 
-         this.#options = Object.assign({}, defaultOptions, options || {});
 
-         this.#payloads = [];
 
-         this.#drainTimer = null;
 
-         this.#network = null;
 
-         this.#pendingDetectNetwork = null;
 
-         {
 
-             let resolve = null;
 
-             const promise = new Promise((_resolve) => {
 
-                 resolve = _resolve;
 
-             });
 
-             this.#notReady = { promise, resolve };
 
-         }
 
-         const staticNetwork = this._getOption("staticNetwork");
 
-         if (typeof (staticNetwork) === "boolean") {
 
-             assertArgument(!staticNetwork || network !== "any", "staticNetwork cannot be used on special network 'any'", "options", options);
 
-             if (staticNetwork && network != null) {
 
-                 this.#network = Network.from(network);
 
-             }
 
-         }
 
-         else if (staticNetwork) {
 
-             // Make sure any static network is compatbile with the provided netwrok
 
-             assertArgument(network == null || staticNetwork.matches(network), "staticNetwork MUST match network object", "options", options);
 
-             this.#network = staticNetwork;
 
-         }
 
-     }
 
-     /**
 
-      *  Returns the value associated with the option %%key%%.
 
-      *
 
-      *  Sub-classes can use this to inquire about configuration options.
 
-      */
 
-     _getOption(key) {
 
-         return this.#options[key];
 
-     }
 
-     /**
 
-      *  Gets the [[Network]] this provider has committed to. On each call, the network
 
-      *  is detected, and if it has changed, the call will reject.
 
-      */
 
-     get _network() {
 
-         assert(this.#network, "network is not available yet", "NETWORK_ERROR");
 
-         return this.#network;
 
-     }
 
-     /**
 
-      *  Resolves to the non-normalized value by performing %%req%%.
 
-      *
 
-      *  Sub-classes may override this to modify behavior of actions,
 
-      *  and should generally call ``super._perform`` as a fallback.
 
-      */
 
-     async _perform(req) {
 
-         // Legacy networks do not like the type field being passed along (which
 
-         // is fair), so we delete type if it is 0 and a non-EIP-1559 network
 
-         if (req.method === "call" || req.method === "estimateGas") {
 
-             let tx = req.transaction;
 
-             if (tx && tx.type != null && getBigInt(tx.type)) {
 
-                 // If there are no EIP-1559 or newer properties, it might be pre-EIP-1559
 
-                 if (tx.maxFeePerGas == null && tx.maxPriorityFeePerGas == null) {
 
-                     const feeData = await this.getFeeData();
 
-                     if (feeData.maxFeePerGas == null && feeData.maxPriorityFeePerGas == null) {
 
-                         // Network doesn't know about EIP-1559 (and hence type)
 
-                         req = Object.assign({}, req, {
 
-                             transaction: Object.assign({}, tx, { type: undefined })
 
-                         });
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         const request = this.getRpcRequest(req);
 
-         if (request != null) {
 
-             return await this.send(request.method, request.args);
 
-         }
 
-         return super._perform(req);
 
-     }
 
-     /**
 
-      *  Sub-classes may override this; it detects the *actual* network that
 
-      *  we are **currently** connected to.
 
-      *
 
-      *  Keep in mind that [[send]] may only be used once [[ready]], otherwise the
 
-      *  _send primitive must be used instead.
 
-      */
 
-     async _detectNetwork() {
 
-         const network = this._getOption("staticNetwork");
 
-         if (network) {
 
-             if (network === true) {
 
-                 if (this.#network) {
 
-                     return this.#network;
 
-                 }
 
-             }
 
-             else {
 
-                 return network;
 
-             }
 
-         }
 
-         if (this.#pendingDetectNetwork) {
 
-             return await this.#pendingDetectNetwork;
 
-         }
 
-         // If we are ready, use ``send``, which enabled requests to be batched
 
-         if (this.ready) {
 
-             this.#pendingDetectNetwork = (async () => {
 
-                 try {
 
-                     const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
 
-                     this.#pendingDetectNetwork = null;
 
-                     return result;
 
-                 }
 
-                 catch (error) {
 
-                     this.#pendingDetectNetwork = null;
 
-                     throw error;
 
-                 }
 
-             })();
 
-             return await this.#pendingDetectNetwork;
 
-         }
 
-         // We are not ready yet; use the primitive _send
 
-         this.#pendingDetectNetwork = (async () => {
 
-             const payload = {
 
-                 id: this.#nextId++, method: "eth_chainId", params: [], jsonrpc: "2.0"
 
-             };
 
-             this.emit("debug", { action: "sendRpcPayload", payload });
 
-             let result;
 
-             try {
 
-                 result = (await this._send(payload))[0];
 
-                 this.#pendingDetectNetwork = null;
 
-             }
 
-             catch (error) {
 
-                 this.#pendingDetectNetwork = null;
 
-                 this.emit("debug", { action: "receiveRpcError", error });
 
-                 throw error;
 
-             }
 
-             this.emit("debug", { action: "receiveRpcResult", result });
 
-             if ("result" in result) {
 
-                 return Network.from(getBigInt(result.result));
 
-             }
 
-             throw this.getRpcError(payload, result);
 
-         })();
 
-         return await this.#pendingDetectNetwork;
 
-     }
 
-     /**
 
-      *  Sub-classes **MUST** call this. Until [[_start]] has been called, no calls
 
-      *  will be passed to [[_send]] from [[send]]. If it is overridden, then
 
-      *  ``super._start()`` **MUST** be called.
 
-      *
 
-      *  Calling it multiple times is safe and has no effect.
 
-      */
 
-     _start() {
 
-         if (this.#notReady == null || this.#notReady.resolve == null) {
 
-             return;
 
-         }
 
-         this.#notReady.resolve();
 
-         this.#notReady = null;
 
-         (async () => {
 
-             // Bootstrap the network
 
-             while (this.#network == null && !this.destroyed) {
 
-                 try {
 
-                     this.#network = await this._detectNetwork();
 
-                 }
 
-                 catch (error) {
 
-                     if (this.destroyed) {
 
-                         break;
 
-                     }
 
-                     console.log("JsonRpcProvider failed to detect network and cannot start up; retry in 1s (perhaps the URL is wrong or the node is not started)");
 
-                     this.emit("error", makeError("failed to bootstrap network detection", "NETWORK_ERROR", { event: "initial-network-discovery", info: { error } }));
 
-                     await stall(1000);
 
-                 }
 
-             }
 
-             // Start dispatching requests
 
-             this.#scheduleDrain();
 
-         })();
 
-     }
 
-     /**
 
-      *  Resolves once the [[_start]] has been called. This can be used in
 
-      *  sub-classes to defer sending data until the connection has been
 
-      *  established.
 
-      */
 
-     async _waitUntilReady() {
 
-         if (this.#notReady == null) {
 
-             return;
 
-         }
 
-         return await this.#notReady.promise;
 
-     }
 
-     /**
 
-      *  Return a Subscriber that will manage the %%sub%%.
 
-      *
 
-      *  Sub-classes may override this to modify the behavior of
 
-      *  subscription management.
 
-      */
 
-     _getSubscriber(sub) {
 
-         // Pending Filters aren't availble via polling
 
-         if (sub.type === "pending") {
 
-             return new FilterIdPendingSubscriber(this);
 
-         }
 
-         if (sub.type === "event") {
 
-             if (this._getOption("polling")) {
 
-                 return new PollingEventSubscriber(this, sub.filter);
 
-             }
 
-             return new FilterIdEventSubscriber(this, sub.filter);
 
-         }
 
-         // Orphaned Logs are handled automatically, by the filter, since
 
-         // logs with removed are emitted by it
 
-         if (sub.type === "orphan" && sub.filter.orphan === "drop-log") {
 
-             return new UnmanagedSubscriber("orphan");
 
-         }
 
-         return super._getSubscriber(sub);
 
-     }
 
-     /**
 
-      *  Returns true only if the [[_start]] has been called.
 
-      */
 
-     get ready() { return this.#notReady == null; }
 
-     /**
 
-      *  Returns %%tx%% as a normalized JSON-RPC transaction request,
 
-      *  which has all values hexlified and any numeric values converted
 
-      *  to Quantity values.
 
-      */
 
-     getRpcTransaction(tx) {
 
-         const result = {};
 
-         // JSON-RPC now requires numeric values to be "quantity" values
 
-         ["chainId", "gasLimit", "gasPrice", "type", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "value"].forEach((key) => {
 
-             if (tx[key] == null) {
 
-                 return;
 
-             }
 
-             let dstKey = key;
 
-             if (key === "gasLimit") {
 
-                 dstKey = "gas";
 
-             }
 
-             result[dstKey] = toQuantity(getBigInt(tx[key], `tx.${key}`));
 
-         });
 
-         // Make sure addresses and data are lowercase
 
-         ["from", "to", "data"].forEach((key) => {
 
-             if (tx[key] == null) {
 
-                 return;
 
-             }
 
-             result[key] = hexlify(tx[key]);
 
-         });
 
-         // Normalize the access list object
 
-         if (tx.accessList) {
 
-             result["accessList"] = accessListify(tx.accessList);
 
-         }
 
-         if (tx.blobVersionedHashes) {
 
-             // @TODO: Remove this <any> case once EIP-4844 added to prepared tx
 
-             result["blobVersionedHashes"] = tx.blobVersionedHashes.map(h => h.toLowerCase());
 
-         }
 
-         // @TODO: blobs should probably also be copied over, optionally
 
-         // accounting for the kzg property to backfill blobVersionedHashes
 
-         // using the commitment. Or should that be left as an exercise to
 
-         // the caller?
 
-         return result;
 
-     }
 
-     /**
 
-      *  Returns the request method and arguments required to perform
 
-      *  %%req%%.
 
-      */
 
-     getRpcRequest(req) {
 
-         switch (req.method) {
 
-             case "chainId":
 
-                 return { method: "eth_chainId", args: [] };
 
-             case "getBlockNumber":
 
-                 return { method: "eth_blockNumber", args: [] };
 
-             case "getGasPrice":
 
-                 return { method: "eth_gasPrice", args: [] };
 
-             case "getPriorityFee":
 
-                 return { method: "eth_maxPriorityFeePerGas", args: [] };
 
-             case "getBalance":
 
-                 return {
 
-                     method: "eth_getBalance",
 
-                     args: [getLowerCase(req.address), req.blockTag]
 
-                 };
 
-             case "getTransactionCount":
 
-                 return {
 
-                     method: "eth_getTransactionCount",
 
-                     args: [getLowerCase(req.address), req.blockTag]
 
-                 };
 
-             case "getCode":
 
-                 return {
 
-                     method: "eth_getCode",
 
-                     args: [getLowerCase(req.address), req.blockTag]
 
-                 };
 
-             case "getStorage":
 
-                 return {
 
-                     method: "eth_getStorageAt",
 
-                     args: [
 
-                         getLowerCase(req.address),
 
-                         ("0x" + req.position.toString(16)),
 
-                         req.blockTag
 
-                     ]
 
-                 };
 
-             case "broadcastTransaction":
 
-                 return {
 
-                     method: "eth_sendRawTransaction",
 
-                     args: [req.signedTransaction]
 
-                 };
 
-             case "getBlock":
 
-                 if ("blockTag" in req) {
 
-                     return {
 
-                         method: "eth_getBlockByNumber",
 
-                         args: [req.blockTag, !!req.includeTransactions]
 
-                     };
 
-                 }
 
-                 else if ("blockHash" in req) {
 
-                     return {
 
-                         method: "eth_getBlockByHash",
 
-                         args: [req.blockHash, !!req.includeTransactions]
 
-                     };
 
-                 }
 
-                 break;
 
-             case "getTransaction":
 
-                 return {
 
-                     method: "eth_getTransactionByHash",
 
-                     args: [req.hash]
 
-                 };
 
-             case "getTransactionReceipt":
 
-                 return {
 
-                     method: "eth_getTransactionReceipt",
 
-                     args: [req.hash]
 
-                 };
 
-             case "call":
 
-                 return {
 
-                     method: "eth_call",
 
-                     args: [this.getRpcTransaction(req.transaction), req.blockTag]
 
-                 };
 
-             case "estimateGas": {
 
-                 return {
 
-                     method: "eth_estimateGas",
 
-                     args: [this.getRpcTransaction(req.transaction)]
 
-                 };
 
-             }
 
-             case "getLogs":
 
-                 if (req.filter && req.filter.address != null) {
 
-                     if (Array.isArray(req.filter.address)) {
 
-                         req.filter.address = req.filter.address.map(getLowerCase);
 
-                     }
 
-                     else {
 
-                         req.filter.address = getLowerCase(req.filter.address);
 
-                     }
 
-                 }
 
-                 return { method: "eth_getLogs", args: [req.filter] };
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      *  Returns an ethers-style Error for the given JSON-RPC error
 
-      *  %%payload%%, coalescing the various strings and error shapes
 
-      *  that different nodes return, coercing them into a machine-readable
 
-      *  standardized error.
 
-      */
 
-     getRpcError(payload, _error) {
 
-         const { method } = payload;
 
-         const { error } = _error;
 
-         if (method === "eth_estimateGas" && error.message) {
 
-             const msg = error.message;
 
-             if (!msg.match(/revert/i) && msg.match(/insufficient funds/i)) {
 
-                 return makeError("insufficient funds", "INSUFFICIENT_FUNDS", {
 
-                     transaction: (payload.params[0]),
 
-                     info: { payload, error }
 
-                 });
 
-             }
 
-         }
 
-         if (method === "eth_call" || method === "eth_estimateGas") {
 
-             const result = spelunkData(error);
 
-             const e = AbiCoder.getBuiltinCallException((method === "eth_call") ? "call" : "estimateGas", (payload.params[0]), (result ? result.data : null));
 
-             e.info = { error, payload };
 
-             return e;
 
-         }
 
-         // Only estimateGas and call can return arbitrary contract-defined text, so now we
 
-         // we can process text safely.
 
-         const message = JSON.stringify(spelunkMessage(error));
 
-         if (typeof (error.message) === "string" && error.message.match(/user denied|ethers-user-denied/i)) {
 
-             const actionMap = {
 
-                 eth_sign: "signMessage",
 
-                 personal_sign: "signMessage",
 
-                 eth_signTypedData_v4: "signTypedData",
 
-                 eth_signTransaction: "signTransaction",
 
-                 eth_sendTransaction: "sendTransaction",
 
-                 eth_requestAccounts: "requestAccess",
 
-                 wallet_requestAccounts: "requestAccess",
 
-             };
 
-             return makeError(`user rejected action`, "ACTION_REJECTED", {
 
-                 action: (actionMap[method] || "unknown"),
 
-                 reason: "rejected",
 
-                 info: { payload, error }
 
-             });
 
-         }
 
-         if (method === "eth_sendRawTransaction" || method === "eth_sendTransaction") {
 
-             const transaction = (payload.params[0]);
 
-             if (message.match(/insufficient funds|base fee exceeds gas limit/i)) {
 
-                 return makeError("insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", {
 
-                     transaction, info: { error }
 
-                 });
 
-             }
 
-             if (message.match(/nonce/i) && message.match(/too low/i)) {
 
-                 return makeError("nonce has already been used", "NONCE_EXPIRED", { transaction, info: { error } });
 
-             }
 
-             // "replacement transaction underpriced"
 
-             if (message.match(/replacement transaction/i) && message.match(/underpriced/i)) {
 
-                 return makeError("replacement fee too low", "REPLACEMENT_UNDERPRICED", { transaction, info: { error } });
 
-             }
 
-             if (message.match(/only replay-protected/i)) {
 
-                 return makeError("legacy pre-eip-155 transactions not supported", "UNSUPPORTED_OPERATION", {
 
-                     operation: method, info: { transaction, info: { error } }
 
-                 });
 
-             }
 
-         }
 
-         let unsupported = !!message.match(/the method .* does not exist/i);
 
-         if (!unsupported) {
 
-             if (error && error.details && error.details.startsWith("Unauthorized method:")) {
 
-                 unsupported = true;
 
-             }
 
-         }
 
-         if (unsupported) {
 
-             return makeError("unsupported operation", "UNSUPPORTED_OPERATION", {
 
-                 operation: payload.method, info: { error, payload }
 
-             });
 
-         }
 
-         return makeError("could not coalesce error", "UNKNOWN_ERROR", { error, payload });
 
-     }
 
-     /**
 
-      *  Requests the %%method%% with %%params%% via the JSON-RPC protocol
 
-      *  over the underlying channel. This can be used to call methods
 
-      *  on the backend that do not have a high-level API within the Provider
 
-      *  API.
 
-      *
 
-      *  This method queues requests according to the batch constraints
 
-      *  in the options, assigns the request a unique ID.
 
-      *
 
-      *  **Do NOT override** this method in sub-classes; instead
 
-      *  override [[_send]] or force the options values in the
 
-      *  call to the constructor to modify this method's behavior.
 
-      */
 
-     send(method, params) {
 
-         // @TODO: cache chainId?? purge on switch_networks
 
-         // We have been destroyed; no operations are supported anymore
 
-         if (this.destroyed) {
 
-             return Promise.reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: method }));
 
-         }
 
-         const id = this.#nextId++;
 
-         const promise = new Promise((resolve, reject) => {
 
-             this.#payloads.push({
 
-                 resolve, reject,
 
-                 payload: { method, params, id, jsonrpc: "2.0" }
 
-             });
 
-         });
 
-         // If there is not a pending drainTimer, set one
 
-         this.#scheduleDrain();
 
-         return promise;
 
-     }
 
-     /**
 
-      *  Resolves to the [[Signer]] account for  %%address%% managed by
 
-      *  the client.
 
-      *
 
-      *  If the %%address%% is a number, it is used as an index in the
 
-      *  the accounts from [[listAccounts]].
 
-      *
 
-      *  This can only be used on clients which manage accounts (such as
 
-      *  Geth with imported account or MetaMask).
 
-      *
 
-      *  Throws if the account doesn't exist.
 
-      */
 
-     async getSigner(address) {
 
-         if (address == null) {
 
-             address = 0;
 
-         }
 
-         const accountsPromise = this.send("eth_accounts", []);
 
-         // Account index
 
-         if (typeof (address) === "number") {
 
-             const accounts = (await accountsPromise);
 
-             if (address >= accounts.length) {
 
-                 throw new Error("no such account");
 
-             }
 
-             return new JsonRpcSigner(this, accounts[address]);
 
-         }
 
-         const { accounts } = await resolveProperties({
 
-             network: this.getNetwork(),
 
-             accounts: accountsPromise
 
-         });
 
-         // Account address
 
-         address = getAddress(address);
 
-         for (const account of accounts) {
 
-             if (getAddress(account) === address) {
 
-                 return new JsonRpcSigner(this, address);
 
-             }
 
-         }
 
-         throw new Error("invalid account");
 
-     }
 
-     async listAccounts() {
 
-         const accounts = await this.send("eth_accounts", []);
 
-         return accounts.map((a) => new JsonRpcSigner(this, a));
 
-     }
 
-     destroy() {
 
-         // Stop processing requests
 
-         if (this.#drainTimer) {
 
-             clearTimeout(this.#drainTimer);
 
-             this.#drainTimer = null;
 
-         }
 
-         // Cancel all pending requests
 
-         for (const { payload, reject } of this.#payloads) {
 
-             reject(makeError("provider destroyed; cancelled request", "UNSUPPORTED_OPERATION", { operation: payload.method }));
 
-         }
 
-         this.#payloads = [];
 
-         // Parent clean-up
 
-         super.destroy();
 
-     }
 
- }
 
- // @TODO: remove this in v7, it is not exported because this functionality
 
- // is exposed in the JsonRpcApiProvider by setting polling to true. It should
 
- // be safe to remove regardless, because it isn't reachable, but just in case.
 
- /**
 
-  *  @_ignore:
 
-  */
 
- export class JsonRpcApiPollingProvider extends JsonRpcApiProvider {
 
-     #pollingInterval;
 
-     constructor(network, options) {
 
-         super(network, options);
 
-         let pollingInterval = this._getOption("pollingInterval");
 
-         if (pollingInterval == null) {
 
-             pollingInterval = defaultOptions.pollingInterval;
 
-         }
 
-         this.#pollingInterval = pollingInterval;
 
-     }
 
-     _getSubscriber(sub) {
 
-         const subscriber = super._getSubscriber(sub);
 
-         if (isPollable(subscriber)) {
 
-             subscriber.pollingInterval = this.#pollingInterval;
 
-         }
 
-         return subscriber;
 
-     }
 
-     /**
 
-      *  The polling interval (default: 4000 ms)
 
-      */
 
-     get pollingInterval() { return this.#pollingInterval; }
 
-     set pollingInterval(value) {
 
-         if (!Number.isInteger(value) || value < 0) {
 
-             throw new Error("invalid interval");
 
-         }
 
-         this.#pollingInterval = value;
 
-         this._forEachSubscriber((sub) => {
 
-             if (isPollable(sub)) {
 
-                 sub.pollingInterval = this.#pollingInterval;
 
-             }
 
-         });
 
-     }
 
- }
 
- /**
 
-  *  The JsonRpcProvider is one of the most common Providers,
 
-  *  which performs all operations over HTTP (or HTTPS) requests.
 
-  *
 
-  *  Events are processed by polling the backend for the current block
 
-  *  number; when it advances, all block-base events are then checked
 
-  *  for updates.
 
-  */
 
- export class JsonRpcProvider extends JsonRpcApiPollingProvider {
 
-     #connect;
 
-     constructor(url, network, options) {
 
-         if (url == null) {
 
-             url = "http:/\/localhost:8545";
 
-         }
 
-         super(network, options);
 
-         if (typeof (url) === "string") {
 
-             this.#connect = new FetchRequest(url);
 
-         }
 
-         else {
 
-             this.#connect = url.clone();
 
-         }
 
-     }
 
-     _getConnection() {
 
-         return this.#connect.clone();
 
-     }
 
-     async send(method, params) {
 
-         // All requests are over HTTP, so we can just start handling requests
 
-         // We do this here rather than the constructor so that we don't send any
 
-         // requests to the network (i.e. eth_chainId) until we absolutely have to.
 
-         await this._start();
 
-         return await super.send(method, params);
 
-     }
 
-     async _send(payload) {
 
-         // Configure a POST connection for the requested method
 
-         const request = this._getConnection();
 
-         request.body = JSON.stringify(payload);
 
-         request.setHeader("content-type", "application/json");
 
-         const response = await request.send();
 
-         response.assertOk();
 
-         let resp = response.bodyJson;
 
-         if (!Array.isArray(resp)) {
 
-             resp = [resp];
 
-         }
 
-         return resp;
 
-     }
 
- }
 
- function spelunkData(value) {
 
-     if (value == null) {
 
-         return null;
 
-     }
 
-     // These *are* the droids we're looking for.
 
-     if (typeof (value.message) === "string" && value.message.match(/revert/i) && isHexString(value.data)) {
 
-         return { message: value.message, data: value.data };
 
-     }
 
-     // Spelunk further...
 
-     if (typeof (value) === "object") {
 
-         for (const key in value) {
 
-             const result = spelunkData(value[key]);
 
-             if (result) {
 
-                 return result;
 
-             }
 
-         }
 
-         return null;
 
-     }
 
-     // Might be a JSON string we can further descend...
 
-     if (typeof (value) === "string") {
 
-         try {
 
-             return spelunkData(JSON.parse(value));
 
-         }
 
-         catch (error) { }
 
-     }
 
-     return null;
 
- }
 
- function _spelunkMessage(value, result) {
 
-     if (value == null) {
 
-         return;
 
-     }
 
-     // These *are* the droids we're looking for.
 
-     if (typeof (value.message) === "string") {
 
-         result.push(value.message);
 
-     }
 
-     // Spelunk further...
 
-     if (typeof (value) === "object") {
 
-         for (const key in value) {
 
-             _spelunkMessage(value[key], result);
 
-         }
 
-     }
 
-     // Might be a JSON string we can further descend...
 
-     if (typeof (value) === "string") {
 
-         try {
 
-             return _spelunkMessage(JSON.parse(value), result);
 
-         }
 
-         catch (error) { }
 
-     }
 
- }
 
- function spelunkMessage(value) {
 
-     const result = [];
 
-     _spelunkMessage(value, result);
 
-     return result;
 
- }
 
- //# sourceMappingURL=provider-jsonrpc.js.map
 
 
  |