From a022093ce03f55db7ba2cac36e365d1af39ac45b Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Wed, 7 Oct 2020 17:28:18 -0400 Subject: [PATCH] Added CommunityResourcable to mark Providers as highly throttled. --- packages/providers/src.ts/alchemy-provider.ts | 26 +++++++++++++--- .../providers/src.ts/etherscan-provider.ts | 6 +++- .../providers/src.ts/fallback-provider.ts | 9 ++++-- packages/providers/src.ts/formatter.ts | 12 +++++++ packages/providers/src.ts/index.ts | 15 ++++++--- packages/providers/src.ts/infura-provider.ts | 31 ++++++++++++++++--- .../providers/src.ts/url-json-rpc-provider.ts | 7 ++++- 7 files changed, 90 insertions(+), 16 deletions(-) diff --git a/packages/providers/src.ts/alchemy-provider.ts b/packages/providers/src.ts/alchemy-provider.ts index e56c564453..82da72f39a 100644 --- a/packages/providers/src.ts/alchemy-provider.ts +++ b/packages/providers/src.ts/alchemy-provider.ts @@ -1,9 +1,10 @@ "use strict"; import { Network, Networkish } from "@ethersproject/networks"; +import { defineReadOnly } from "@ethersproject/properties"; import { ConnectionInfo } from "@ethersproject/web"; -import { showThrottleMessage } from "./formatter"; +import { CommunityResourcable, showThrottleMessage } from "./formatter"; import { WebSocketProvider } from "./websocket-provider"; import { Logger } from "@ethersproject/logger"; @@ -19,15 +20,28 @@ import { UrlJsonRpcProvider } from "./url-json-rpc-provider"; const defaultApiKey = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC" -export class AlchemyProvider extends UrlJsonRpcProvider { +export class AlchemyWebSocketProvider extends WebSocketProvider implements CommunityResourcable { + readonly apiKey: string; - static getWebSocketProvider(network?: Networkish, apiKey?: any): WebSocketProvider { + constructor(network?: Networkish, apiKey?: any) { const provider = new AlchemyProvider(network, apiKey); const url = provider.connection.url.replace(/^http/i, "ws") .replace(".alchemyapi.", ".ws.alchemyapi."); - return new WebSocketProvider(url, provider.network); + super(url, provider.network); + defineReadOnly(this, "apiKey", provider.apiKey); + } + + isCommunityResource(): boolean { + return (this.apiKey === defaultApiKey); + } +} + +export class AlchemyProvider extends UrlJsonRpcProvider { + + static getWebSocketProvider(network?: Networkish, apiKey?: any): AlchemyWebSocketProvider { + return new AlchemyWebSocketProvider(network, apiKey); } static getApiKey(apiKey: any): any { @@ -70,4 +84,8 @@ export class AlchemyProvider extends UrlJsonRpcProvider { } }; } + + isCommunityResource(): boolean { + return (this.apiKey === defaultApiKey); + } } diff --git a/packages/providers/src.ts/etherscan-provider.ts b/packages/providers/src.ts/etherscan-provider.ts index 6d76cea597..a220f3b526 100644 --- a/packages/providers/src.ts/etherscan-provider.ts +++ b/packages/providers/src.ts/etherscan-provider.ts @@ -189,7 +189,7 @@ export class EtherscanProvider extends BaseProvider{ url: url, throttleSlotInterval: 1000, throttleCallback: (attempt: number, url: string) => { - if (this.apiKey === defaultApiKey) { + if (this.isCommunityResource()) { showThrottleMessage(); } return Promise.resolve(true); @@ -424,4 +424,8 @@ export class EtherscanProvider extends BaseProvider{ }); }); } + + isCommunityResource(): boolean { + return (this.apiKey === defaultApiKey); + } } diff --git a/packages/providers/src.ts/fallback-provider.ts b/packages/providers/src.ts/fallback-provider.ts index 2577930593..e05f391707 100644 --- a/packages/providers/src.ts/fallback-provider.ts +++ b/packages/providers/src.ts/fallback-provider.ts @@ -9,6 +9,7 @@ import { shuffled } from "@ethersproject/random"; import { poll } from "@ethersproject/web"; import { BaseProvider } from "./base-provider"; +import { isCommunityResource } from "./formatter"; import { Logger } from "@ethersproject/logger"; import { version } from "./_version"; @@ -417,13 +418,17 @@ export class FallbackProvider extends BaseProvider { const providerConfigs: Array = providers.map((configOrProvider, index) => { if (Provider.isProvider(configOrProvider)) { - return Object.freeze({ provider: configOrProvider, weight: 1, stallTimeout: 750, priority: 1 }); + const stallTimeout = isCommunityResource(configOrProvider) ? 2000: 750; + const priority = 1; + return Object.freeze({ provider: configOrProvider, weight: 1, stallTimeout, priority }); } const config: FallbackProviderConfig = shallowCopy(configOrProvider); if (config.priority == null) { config.priority = 1; } - if (config.stallTimeout == null) { config.stallTimeout = 750; } + if (config.stallTimeout == null) { + config.stallTimeout = isCommunityResource(configOrProvider) ? 2000: 750; + } if (config.weight == null) { config.weight = 1; } const weight = config.weight; diff --git a/packages/providers/src.ts/formatter.ts b/packages/providers/src.ts/formatter.ts index a90da339e5..531875cbfb 100644 --- a/packages/providers/src.ts/formatter.ts +++ b/packages/providers/src.ts/formatter.ts @@ -455,6 +455,18 @@ export class Formatter { } } +export interface CommunityResourcable { + isCommunityResource(): boolean; +} + +export function isCommunityResourcable(value: any): value is CommunityResourcable { + return (value && typeof(value.isCommunityResource) === "function"); +} + +export function isCommunityResource(value: any): boolean { + return (isCommunityResourcable(value) && value.isCommunityResource()); +} + // Show the throttle message only once let throttleMessage = false; export function showThrottleMessage() { diff --git a/packages/providers/src.ts/index.ts b/packages/providers/src.ts/index.ts index 4a99737452..63b88582ce 100644 --- a/packages/providers/src.ts/index.ts +++ b/packages/providers/src.ts/index.ts @@ -18,12 +18,12 @@ import { Network, Networkish } from "@ethersproject/networks"; import { BaseProvider, EnsProvider, EnsResolver, Resolver } from "./base-provider"; -import { AlchemyProvider } from "./alchemy-provider"; +import { AlchemyProvider, AlchemyWebSocketProvider } from "./alchemy-provider"; import { CloudflareProvider } from "./cloudflare-provider"; import { EtherscanProvider } from "./etherscan-provider"; import { FallbackProvider } from "./fallback-provider"; import { IpcProvider } from "./ipc-provider"; -import { InfuraProvider } from "./infura-provider"; +import { InfuraProvider, InfuraWebSocketProvider } from "./infura-provider"; import { JsonRpcProvider, JsonRpcSigner } from "./json-rpc-provider"; import { NodesmithProvider } from "./nodesmith-provider"; import { StaticJsonRpcProvider, UrlJsonRpcProvider } from "./url-json-rpc-provider"; @@ -31,7 +31,7 @@ import { Web3Provider } from "./web3-provider"; import { WebSocketProvider } from "./websocket-provider"; import { ExternalProvider, JsonRpcFetchFunc } from "./web3-provider"; -import { Formatter } from "./formatter"; +import { CommunityResourcable, Formatter, isCommunityResourcable, isCommunityResource, showThrottleMessage } from "./formatter"; import { Logger } from "@ethersproject/logger"; import { version } from "./_version"; @@ -103,9 +103,11 @@ export { FallbackProvider, AlchemyProvider, + AlchemyWebSocketProvider, CloudflareProvider, EtherscanProvider, InfuraProvider, + InfuraWebSocketProvider, JsonRpcProvider, NodesmithProvider, StaticJsonRpcProvider, @@ -126,6 +128,9 @@ export { getDefaultProvider, getNetwork, + isCommunityResource, + isCommunityResourcable, + showThrottleMessage, /////////////////////// @@ -154,6 +159,8 @@ export { Networkish, EnsProvider, - EnsResolver + EnsResolver, + + CommunityResourcable }; diff --git a/packages/providers/src.ts/infura-provider.ts b/packages/providers/src.ts/infura-provider.ts index 2a9f21a041..455fa48c5d 100644 --- a/packages/providers/src.ts/infura-provider.ts +++ b/packages/providers/src.ts/infura-provider.ts @@ -1,10 +1,11 @@ "use strict"; import { Network, Networkish } from "@ethersproject/networks"; +import { defineReadOnly } from "@ethersproject/properties"; import { ConnectionInfo } from "@ethersproject/web"; import { WebSocketProvider } from "./websocket-provider"; -import { showThrottleMessage } from "./formatter"; +import { CommunityResourcable, showThrottleMessage } from "./formatter"; import { Logger } from "@ethersproject/logger"; import { version } from "./_version"; @@ -15,11 +16,12 @@ import { UrlJsonRpcProvider } from "./url-json-rpc-provider"; const defaultProjectId = "84842078b09946638c03157f83405213" -export class InfuraProvider extends UrlJsonRpcProvider { +export class InfuraWebSocketProvider extends WebSocketProvider implements CommunityResourcable { + readonly apiKey: string; readonly projectId: string; readonly projectSecret: string; - static getWebSocketProvider(network?: Networkish, apiKey?: any): WebSocketProvider { + constructor(network?: Networkish, apiKey?: any) { const provider = new InfuraProvider(network, apiKey); const connection = provider.connection; if (connection.password) { @@ -29,7 +31,24 @@ export class InfuraProvider extends UrlJsonRpcProvider { } const url = connection.url.replace(/^http/i, "ws").replace("/v3/", "/ws/v3/"); - return new WebSocketProvider(url, network); + super(url, network); + + defineReadOnly(this, "apiKey", provider.projectId); + defineReadOnly(this, "projectId", provider.projectId); + defineReadOnly(this, "projectSecret", provider.projectSecret); + } + + isCommunityResource(): boolean { + return (this.projectId === defaultProjectId); + } +} + +export class InfuraProvider extends UrlJsonRpcProvider { + readonly projectId: string; + readonly projectSecret: string; + + static getWebSocketProvider(network?: Networkish, apiKey?: any): InfuraWebSocketProvider { + return new InfuraWebSocketProvider(network, apiKey); } static getApiKey(apiKey: any): any { @@ -104,4 +123,8 @@ export class InfuraProvider extends UrlJsonRpcProvider { return connection; } + + isCommunityResource(): boolean { + return (this.projectId === defaultProjectId); + } } diff --git a/packages/providers/src.ts/url-json-rpc-provider.ts b/packages/providers/src.ts/url-json-rpc-provider.ts index 7ba71c88a8..cbb924ef77 100644 --- a/packages/providers/src.ts/url-json-rpc-provider.ts +++ b/packages/providers/src.ts/url-json-rpc-provider.ts @@ -9,6 +9,7 @@ import { Logger } from "@ethersproject/logger"; import { version } from "./_version"; const logger = new Logger(version); +import { CommunityResourcable } from "./formatter"; import { JsonRpcProvider, JsonRpcSigner } from "./json-rpc-provider"; type getUrlFunc = (network: Network, apiKey: string) => string | ConnectionInfo; @@ -46,7 +47,7 @@ export class StaticJsonRpcProvider extends JsonRpcProvider { } } -export abstract class UrlJsonRpcProvider extends StaticJsonRpcProvider { +export abstract class UrlJsonRpcProvider extends StaticJsonRpcProvider implements CommunityResourcable { readonly apiKey: any; constructor(network?: Networkish, apiKey?: any) { @@ -73,6 +74,10 @@ export abstract class UrlJsonRpcProvider extends StaticJsonRpcProvider { logger.warn("WARNING: API provider does not support pending filters"); } + isCommunityResource(): boolean { + return false; + } + getSigner(address?: string): JsonRpcSigner { return logger.throwError( "API provider does not support signing",