| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 | 
							- /**
 
-  *  [[link-etherscan]] provides a third-party service for connecting to
 
-  *  various blockchains over a combination of JSON-RPC and custom API
 
-  *  endpoints.
 
-  *
 
-  *  **Supported Networks**
 
-  *
 
-  *  - Ethereum Mainnet (``mainnet``)
 
-  *  - Goerli Testnet (``goerli``)
 
-  *  - Sepolia Testnet (``sepolia``)
 
-  *  - Holesky Testnet (``holesky``)
 
-  *  - Arbitrum (``arbitrum``)
 
-  *  - Arbitrum Goerli Testnet (``arbitrum-goerli``)
 
-  *  - Base (``base``)
 
-  *  - Base Sepolia Testnet (``base-sepolia``)
 
-  *  - BNB Smart Chain Mainnet (``bnb``)
 
-  *  - BNB Smart Chain Testnet (``bnbt``)
 
-  *  - Optimism (``optimism``)
 
-  *  - Optimism Goerli Testnet (``optimism-goerli``)
 
-  *  - Polygon (``matic``)
 
-  *  - Polygon Mumbai Testnet (``matic-mumbai``)
 
-  *  - Polygon Amoy Testnet (``matic-amoy``)
 
-  *
 
-  *  @_subsection api/providers/thirdparty:Etherscan  [providers-etherscan]
 
-  */
 
- import { AbiCoder } from "../abi/index.js";
 
- import { Contract } from "../contract/index.js";
 
- import { accessListify, Transaction } from "../transaction/index.js";
 
- import {
 
-     defineProperties,
 
-     hexlify, toQuantity,
 
-     FetchRequest,
 
-     assert, assertArgument, isError,
 
- //    parseUnits,
 
-     toUtf8String
 
-  } from "../utils/index.js";
 
- import { AbstractProvider } from "./abstract-provider.js";
 
- import { Network } from "./network.js";
 
- import { NetworkPlugin } from "./plugins-network.js";
 
- import { showThrottleMessage } from "./community.js";
 
- import { PerformActionRequest } from "./abstract-provider.js";
 
- import type { Networkish } from "./network.js";
 
- //import type { } from "./pagination";
 
- import type { TransactionRequest } from "./provider.js";
 
- const THROTTLE = 2000;
 
- function isPromise<T = any>(value: any): value is Promise<T> {
 
-     return (value && typeof(value.then) === "function");
 
- }
 
- /**
 
-  *  When subscribing to the ``"debug"`` event on an Etherscan-based
 
-  *  provider, the events receive a **DebugEventEtherscanProvider**
 
-  *  payload.
 
-  *
 
-  *  @_docloc: api/providers/thirdparty:Etherscan
 
-  */
 
- export type DebugEventEtherscanProvider = {
 
-     action: "sendRequest",
 
-     id: number,
 
-     url: string,
 
-     payload: Record<string, any>
 
- } | {
 
-     action: "receiveRequest",
 
-     id: number,
 
-     result: any
 
- } | {
 
-     action: "receiveError",
 
-     id: number,
 
-     error: any
 
- };
 
- const EtherscanPluginId = "org.ethers.plugins.provider.Etherscan";
 
- /**
 
-  *  A Network can include an **EtherscanPlugin** to provide
 
-  *  a custom base URL.
 
-  *
 
-  *  @_docloc: api/providers/thirdparty:Etherscan
 
-  */
 
- export class EtherscanPlugin extends NetworkPlugin {
 
-     /**
 
-      *  The Etherscan API base URL.
 
-      */
 
-     readonly baseUrl!: string;
 
-     /**
 
-      *  Creates a new **EtherscanProvider** which will use
 
-      *  %%baseUrl%%.
 
-      */
 
-     constructor(baseUrl: string) {
 
-         super(EtherscanPluginId);
 
-         defineProperties<EtherscanPlugin>(this, { baseUrl });
 
-     }
 
-     clone(): EtherscanPlugin {
 
-         return new EtherscanPlugin(this.baseUrl);
 
-     }
 
- }
 
- const skipKeys = [ "enableCcipRead" ];
 
- let nextId = 1;
 
- /**
 
-  *  The **EtherscanBaseProvider** is the super-class of
 
-  *  [[EtherscanProvider]], which should generally be used instead.
 
-  *
 
-  *  Since the **EtherscanProvider** includes additional code for
 
-  *  [[Contract]] access, in //rare cases// that contracts are not
 
-  *  used, this class can reduce code size.
 
-  *
 
-  *  @_docloc: api/providers/thirdparty:Etherscan
 
-  */
 
- export class EtherscanProvider extends AbstractProvider {
 
-     /**
 
-      *  The connected network.
 
-      */
 
-     readonly network!: Network;
 
-     /**
 
-      *  The API key or null if using the community provided bandwidth.
 
-      */
 
-     readonly apiKey!: null | string;
 
-     readonly #plugin: null | EtherscanPlugin;
 
-     /**
 
-      *  Creates a new **EtherscanBaseProvider**.
 
-      */
 
-     constructor(_network?: Networkish, _apiKey?: string) {
 
-         const apiKey = (_apiKey != null) ? _apiKey: null;
 
-         super();
 
-         const network = Network.from(_network);
 
-         this.#plugin = network.getPlugin<EtherscanPlugin>(EtherscanPluginId);
 
-         defineProperties<EtherscanProvider>(this, { apiKey, network });
 
-         // Test that the network is supported by Etherscan
 
-         this.getBaseUrl();
 
-     }
 
-     /**
 
-      *  Returns the base URL.
 
-      *
 
-      *  If an [[EtherscanPlugin]] is configured on the
 
-      *  [[EtherscanBaseProvider_network]], returns the plugin's
 
-      *  baseUrl.
 
-      */
 
-     getBaseUrl(): string {
 
-         if (this.#plugin) { return this.#plugin.baseUrl; }
 
-         switch(this.network.name) {
 
-             case "mainnet":
 
-                 return "https:/\/api.etherscan.io";
 
-             case "goerli":
 
-                 return "https:/\/api-goerli.etherscan.io";
 
-             case "sepolia":
 
-                 return "https:/\/api-sepolia.etherscan.io";
 
-             case "holesky":
 
-                 return "https:/\/api-holesky.etherscan.io";
 
-             case "arbitrum":
 
-                 return "https:/\/api.arbiscan.io";
 
-             case "arbitrum-goerli":
 
-                 return "https:/\/api-goerli.arbiscan.io";
 
-            case "base":
 
-                 return "https:/\/api.basescan.org";
 
-             case "base-sepolia":
 
-                 return "https:/\/api-sepolia.basescan.org";
 
-             case "bnb":
 
-                 return "https:/\/api.bscscan.com";
 
-             case "bnbt":
 
-                 return "https:/\/api-testnet.bscscan.com";
 
-             case "matic":
 
-                 return "https:/\/api.polygonscan.com";
 
-             case "matic-amoy":
 
-                 return "https:/\/api-amoy.polygonscan.com";
 
-             case "matic-mumbai":
 
-                 return "https:/\/api-testnet.polygonscan.com";
 
-             case "optimism":
 
-                 return "https:/\/api-optimistic.etherscan.io";
 
-             case "optimism-goerli":
 
-                 return "https:/\/api-goerli-optimistic.etherscan.io";
 
-             default:
 
-         }
 
-         assertArgument(false, "unsupported network", "network", this.network);
 
-     }
 
-     /**
 
-      *  Returns the URL for the %%module%% and %%params%%.
 
-      */
 
-     getUrl(module: string, params: Record<string, string>): string {
 
-         const query = Object.keys(params).reduce((accum, key) => {
 
-             const value = params[key];
 
-             if (value != null) {
 
-                 accum += `&${ key }=${ value }`
 
-             }
 
-             return accum
 
-         }, "");
 
-         const apiKey = ((this.apiKey) ? `&apikey=${ this.apiKey }`: "");
 
-         return `${ this.getBaseUrl() }/api?module=${ module }${ query }${ apiKey }`;
 
-     }
 
-     /**
 
-      *  Returns the URL for using POST requests.
 
-      */
 
-     getPostUrl(): string {
 
-         return `${ this.getBaseUrl() }/api`;
 
-     }
 
-     /**
 
-      *  Returns the parameters for using POST requests.
 
-      */
 
-     getPostData(module: string, params: Record<string, any>): Record<string, any> {
 
-         params.module = module;
 
-         params.apikey = this.apiKey;
 
-         return params;
 
-     }
 
-     async detectNetwork(): Promise<Network> {
 
-         return this.network;
 
-     }
 
-     /**
 
-      *  Resolves to the result of calling %%module%% with %%params%%.
 
-      *
 
-      *  If %%post%%, the request is made as a POST request.
 
-      */
 
-     async fetch(module: string, params: Record<string, any>, post?: boolean): Promise<any> {
 
-         const id = nextId++;
 
-         const url = (post ? this.getPostUrl(): this.getUrl(module, params));
 
-         const payload = (post ? this.getPostData(module, params): null);
 
-         this.emit("debug", { action: "sendRequest", id, url, payload: payload });
 
-         const request = new FetchRequest(url);
 
-         request.setThrottleParams({ slotInterval: 1000 });
 
-         request.retryFunc = (req, resp, attempt: number) => {
 
-             if (this.isCommunityResource()) {
 
-                 showThrottleMessage("Etherscan");
 
-             }
 
-             return Promise.resolve(true);
 
-         };
 
-         request.processFunc = async (request, response) => {
 
-             const result = response.hasBody() ? JSON.parse(toUtf8String(response.body)): { };
 
-             const throttle = ((typeof(result.result) === "string") ? result.result: "").toLowerCase().indexOf("rate limit") >= 0;
 
-             if (module === "proxy") {
 
-                 // This JSON response indicates we are being throttled
 
-                 if (result && result.status == 0 && result.message == "NOTOK" && throttle) {
 
-                     this.emit("debug", { action: "receiveError", id, reason: "proxy-NOTOK", error: result });
 
-                     response.throwThrottleError(result.result, THROTTLE);
 
-                 }
 
-             } else {
 
-                 if (throttle) {
 
-                     this.emit("debug", { action: "receiveError", id, reason: "null result", error: result.result });
 
-                     response.throwThrottleError(result.result, THROTTLE);
 
-                 }
 
-             }
 
-             return response;
 
-         };
 
-         if (payload) {
 
-             request.setHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
 
-             request.body = Object.keys(payload).map((k) => `${ k }=${ payload[k] }`).join("&");
 
-         }
 
-         const response = await request.send();
 
-         try {
 
-             response.assertOk();
 
-         } catch (error) {
 
-             this.emit("debug", { action: "receiveError", id, error, reason: "assertOk" });
 
-             assert(false, "response error", "SERVER_ERROR", { request, response });
 
-         }
 
-         if (!response.hasBody()) {
 
-             this.emit("debug", { action: "receiveError", id, error: "missing body", reason: "null body" });
 
-             assert(false, "missing response", "SERVER_ERROR", { request, response });
 
-         }
 
-         const result = JSON.parse(toUtf8String(response.body));
 
-         if (module === "proxy") {
 
-             if (result.jsonrpc != "2.0") {
 
-                 this.emit("debug", { action: "receiveError", id, result, reason: "invalid JSON-RPC" });
 
-                 assert(false, "invalid JSON-RPC response (missing jsonrpc='2.0')", "SERVER_ERROR", { request, response, info: { result } });
 
-             }
 
-             if (result.error) {
 
-                 this.emit("debug", { action: "receiveError", id, result, reason: "JSON-RPC error" });
 
-                 assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
 
-             }
 
-             this.emit("debug", { action: "receiveRequest", id, result });
 
-             return result.result;
 
-         } else {
 
-             // getLogs, getHistory have weird success responses
 
-             if (result.status == 0 && (result.message === "No records found" || result.message === "No transactions found")) {
 
-                 this.emit("debug", { action: "receiveRequest", id, result });
 
-                 return result.result;
 
-             }
 
-             if (result.status != 1 || (typeof(result.message) === "string" && !result.message.match(/^OK/))) {
 
-                 this.emit("debug", { action: "receiveError", id, result });
 
-                 assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
 
-             }
 
-             this.emit("debug", { action: "receiveRequest", id, result });
 
-             return result.result;
 
-         }
 
-     }
 
-     /**
 
-      *  Returns %%transaction%% normalized for the Etherscan API.
 
-      */
 
-     _getTransactionPostData(transaction: TransactionRequest): Record<string, string> {
 
-         const result: Record<string, string> = { };
 
-         for (let key in transaction) {
 
-             if (skipKeys.indexOf(key) >= 0) { continue; }
 
-             if ((<any>transaction)[key] == null) { continue; }
 
-             let value = (<any>transaction)[key];
 
-             if (key === "type" && value === 0) { continue; }
 
-             if (key === "blockTag" && value === "latest") { continue; }
 
-             // Quantity-types require no leading zero, unless 0
 
-             if ((<any>{ type: true, gasLimit: true, gasPrice: true, maxFeePerGs: true, maxPriorityFeePerGas: true, nonce: true, value: true })[key]) {
 
-                 value = toQuantity(value);
 
-             } else if (key === "accessList") {
 
-                 value = "[" + accessListify(value).map((set) => {
 
-                     return `{address:"${ set.address }",storageKeys:["${ set.storageKeys.join('","') }"]}`;
 
-                 }).join(",") + "]";
 
-             } else if (key === "blobVersionedHashes") {
 
-                 if (value.length === 0) { continue; }
 
-                 // @TODO: update this once the API supports blobs
 
-                 assert(false, "Etherscan API does not support blobVersionedHashes", "UNSUPPORTED_OPERATION", {
 
-                     operation: "_getTransactionPostData",
 
-                     info: { transaction }
 
-                 });
 
-             } else {
 
-                 value = hexlify(value);
 
-             }
 
-             result[key] = value;
 
-         }
 
-         return result;
 
-     }
 
-     /**
 
-      *  Throws the normalized Etherscan error.
 
-      */
 
-     _checkError(req: PerformActionRequest, error: Error, transaction: any): never {
 
-         // Pull any message out if, possible
 
-         let message = "";
 
-         if (isError(error, "SERVER_ERROR")) {
 
-             // Check for an error emitted by a proxy call
 
-             try {
 
-                 message = (<any>error).info.result.error.message;
 
-             } catch (e) { }
 
-             if (!message) {
 
-                 try {
 
-                     message = (<any>error).info.message;
 
-                 } catch (e) { }
 
-             }
 
-         }
 
-         if (req.method === "estimateGas") {
 
-             if (!message.match(/revert/i) && message.match(/insufficient funds/i)) {
 
-                 assert(false, "insufficient funds", "INSUFFICIENT_FUNDS", {
 
-                     transaction: req.transaction
 
-                 });
 
-             }
 
-         }
 
-         if (req.method === "call" || req.method === "estimateGas") {
 
-             if (message.match(/execution reverted/i)) {
 
-                 let data = "";
 
-                 try {
 
-                     data = (<any>error).info.result.error.data;
 
-                 } catch (error) { }
 
-                 const e = AbiCoder.getBuiltinCallException(req.method, <any>req.transaction, data);
 
-                 e.info = { request: req, error }
 
-                 throw e;
 
-             }
 
-         }
 
-         if (message) {
 
-             if (req.method === "broadcastTransaction") {
 
-                 const transaction = Transaction.from(req.signedTransaction);
 
-                 if (message.match(/replacement/i) && message.match(/underpriced/i)) {
 
-                     assert(false, "replacement fee too low", "REPLACEMENT_UNDERPRICED", {
 
-                         transaction
 
-                     });
 
-                 }
 
-                 if (message.match(/insufficient funds/)) {
 
-                     assert(false, "insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", {
 
-                        transaction
 
-                     });
 
-                 }
 
-                 if (message.match(/same hash was already imported|transaction nonce is too low|nonce too low/)) {
 
-                     assert(false, "nonce has already been used", "NONCE_EXPIRED", {
 
-                        transaction
 
-                     });
 
-                 }
 
-             }
 
-         }
 
-         // Something we could not process
 
-         throw error;
 
-     }
 
-     async _detectNetwork(): Promise<Network> {
 
-         return this.network;
 
-     }
 
-     async _perform(req: PerformActionRequest): Promise<any> {
 
-         switch (req.method) {
 
-             case "chainId":
 
-                 return this.network.chainId;
 
-             case "getBlockNumber":
 
-                 return this.fetch("proxy", { action: "eth_blockNumber" });
 
-             case "getGasPrice":
 
-                 return this.fetch("proxy", { action: "eth_gasPrice" });
 
-             case "getPriorityFee":
 
-                 // This is temporary until Etherscan completes support
 
-                 if (this.network.name === "mainnet") {
 
-                     return "1000000000";
 
-                 } else if (this.network.name === "optimism") {
 
-                     return "1000000";
 
-                 } else {
 
-                     throw new Error("fallback onto the AbstractProvider default");
 
-                 }
 
-                 /* Working with Etherscan to get this added:
 
-                 try {
 
-                     const test = await this.fetch("proxy", {
 
-                         action: "eth_maxPriorityFeePerGas"
 
-                     });
 
-                     console.log(test);
 
-                     return test;
 
-                 } catch (e) {
 
-                     console.log("DEBUG", e);
 
-                     throw e;
 
-                 }
 
-                 */
 
-                 /* This might be safe; but due to rounding neither myself
 
-                    or Etherscan are necessarily comfortable with this. :)
 
-                 try {
 
-                     const result = await this.fetch("gastracker", { action: "gasoracle" });
 
-                     console.log(result);
 
-                     const gasPrice = parseUnits(result.SafeGasPrice, "gwei");
 
-                     const baseFee = parseUnits(result.suggestBaseFee, "gwei");
 
-                     const priorityFee = gasPrice - baseFee;
 
-                     if (priorityFee < 0) { throw new Error("negative priority fee; defer to abstract provider default"); }
 
-                     return priorityFee;
 
-                 } catch (error) {
 
-                     console.log("DEBUG", error);
 
-                     throw error;
 
-                 }
 
-                 */
 
-             case "getBalance":
 
-                 // Returns base-10 result
 
-                 return this.fetch("account", {
 
-                     action: "balance",
 
-                     address: req.address,
 
-                     tag: req.blockTag
 
-                 });
 
-            case "getTransactionCount":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_getTransactionCount",
 
-                     address: req.address,
 
-                     tag: req.blockTag
 
-                 });
 
-             case "getCode":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_getCode",
 
-                     address: req.address,
 
-                     tag: req.blockTag
 
-                 });
 
-             case "getStorage":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_getStorageAt",
 
-                     address: req.address,
 
-                     position: req.position,
 
-                     tag: req.blockTag
 
-                 });
 
-             case "broadcastTransaction":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_sendRawTransaction",
 
-                     hex: req.signedTransaction
 
-                 }, true).catch((error) => {
 
-                     return this._checkError(req, <Error>error, req.signedTransaction);
 
-                 });
 
-             case "getBlock":
 
-                 if ("blockTag" in req) {
 
-                     return this.fetch("proxy", {
 
-                         action: "eth_getBlockByNumber",
 
-                         tag: req.blockTag,
 
-                         boolean: (req.includeTransactions ? "true": "false")
 
-                     });
 
-                 }
 
-                 assert(false, "getBlock by blockHash not supported by Etherscan", "UNSUPPORTED_OPERATION", {
 
-                     operation: "getBlock(blockHash)"
 
-                 });
 
-             case "getTransaction":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_getTransactionByHash",
 
-                     txhash: req.hash
 
-                 });
 
-             case "getTransactionReceipt":
 
-                 return this.fetch("proxy", {
 
-                     action: "eth_getTransactionReceipt",
 
-                     txhash: req.hash
 
-                 });
 
-             case "call": {
 
-                 if (req.blockTag !== "latest") {
 
-                     throw new Error("EtherscanProvider does not support blockTag for call");
 
-                 }
 
-                 const postData = this._getTransactionPostData(req.transaction);
 
-                 postData.module = "proxy";
 
-                 postData.action = "eth_call";
 
-                 try {
 
-                     return await this.fetch("proxy", postData, true);
 
-                 } catch (error) {
 
-                     return this._checkError(req, <Error>error, req.transaction);
 
-                 }
 
-             }
 
-             case "estimateGas": {
 
-                 const postData = this._getTransactionPostData(req.transaction);
 
-                 postData.module = "proxy";
 
-                 postData.action = "eth_estimateGas";
 
-                 try {
 
-                     return await this.fetch("proxy", postData, true);
 
-                 } catch (error) {
 
-                     return this._checkError(req, <Error>error, req.transaction);
 
-                 }
 
-             }
 
- /*
 
-             case "getLogs": {
 
-                 // Needs to complain if more than one address is passed in
 
-                 const args: Record<string, any> = { action: "getLogs" }
 
-                 if (params.filter.fromBlock) {
 
-                     args.fromBlock = checkLogTag(params.filter.fromBlock);
 
-                 }
 
-                 if (params.filter.toBlock) {
 
-                     args.toBlock = checkLogTag(params.filter.toBlock);
 
-                 }
 
-                 if (params.filter.address) {
 
-                     args.address = params.filter.address;
 
-                 }
 
-                 // @TODO: We can handle slightly more complicated logs using the logs API
 
-                 if (params.filter.topics && params.filter.topics.length > 0) {
 
-                     if (params.filter.topics.length > 1) {
 
-                         logger.throwError("unsupported topic count", Logger.Errors.UNSUPPORTED_OPERATION, { topics: params.filter.topics });
 
-                     }
 
-                     if (params.filter.topics.length === 1) {
 
-                         const topic0 = params.filter.topics[0];
 
-                         if (typeof(topic0) !== "string" || topic0.length !== 66) {
 
-                             logger.throwError("unsupported topic format", Logger.Errors.UNSUPPORTED_OPERATION, { topic0: topic0 });
 
-                         }
 
-                         args.topic0 = topic0;
 
-                     }
 
-                 }
 
-                 const logs: Array<any> = await this.fetch("logs", args);
 
-                 // Cache txHash => blockHash
 
-                 let blocks: { [tag: string]: string } = {};
 
-                 // Add any missing blockHash to the logs
 
-                 for (let i = 0; i < logs.length; i++) {
 
-                     const log = logs[i];
 
-                     if (log.blockHash != null) { continue; }
 
-                     if (blocks[log.blockNumber] == null) {
 
-                         const block = await this.getBlock(log.blockNumber);
 
-                         if (block) {
 
-                             blocks[log.blockNumber] = block.hash;
 
-                         }
 
-                     }
 
-                     log.blockHash = blocks[log.blockNumber];
 
-                 }
 
-                 return logs;
 
-             }
 
- */
 
-             default:
 
-                 break;
 
-         }
 
-         return super._perform(req);
 
-     }
 
-     async getNetwork(): Promise<Network> {
 
-         return this.network;
 
-     }
 
-     /**
 
-      *  Resolves to the current price of ether.
 
-      *
 
-      *  This returns ``0`` on any network other than ``mainnet``.
 
-      */
 
-     async getEtherPrice(): Promise<number> {
 
-         if (this.network.name !== "mainnet") { return 0.0; }
 
-         return parseFloat((await this.fetch("stats", { action: "ethprice" })).ethusd);
 
-     }
 
-     /**
 
-      *  Resolves to a [Contract]] for %%address%%, using the
 
-      *  Etherscan API to retreive the Contract ABI.
 
-      */
 
-     async getContract(_address: string): Promise<null | Contract> {
 
-         let address = this._getAddress(_address);
 
-         if (isPromise(address)) { address = await address; }
 
-         try {
 
-             const resp = await this.fetch("contract", {
 
-             action: "getabi", address });
 
-             const abi = JSON.parse(resp);
 
-             return new Contract(address, abi, this);
 
-         } catch (error) {
 
-             return null;
 
-         }
 
-     }
 
-     isCommunityResource(): boolean {
 
-         return (this.apiKey == null);
 
-     }
 
- }
 
 
  |