diff --git a/.changeset/eleven-moles-pull.md b/.changeset/eleven-moles-pull.md new file mode 100644 index 000000000000..086434bad654 --- /dev/null +++ b/.changeset/eleven-moles-pull.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/core-utils': patch +'@eth-optimism/data-transport-layer': patch +--- + +Add fallback provider support to DTL using helper function in core-utils diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index 18a22353ab46..1a348c65d695 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -47,6 +47,7 @@ }, "dependencies": { "@ethersproject/abstract-provider": "^5.4.1", + "@ethersproject/providers": "^5.4.5", "ethers": "^5.4.5", "lodash": "^4.17.21" } diff --git a/packages/core-utils/src/index.ts b/packages/core-utils/src/index.ts index ad17c4e611da..f60de21b06c5 100644 --- a/packages/core-utils/src/index.ts +++ b/packages/core-utils/src/index.ts @@ -6,3 +6,4 @@ export * from './events' export * from './batches' export * from './bcfg' export * from './fees' +export * from './provider' diff --git a/packages/core-utils/src/provider.ts b/packages/core-utils/src/provider.ts new file mode 100644 index 000000000000..66adfe013068 --- /dev/null +++ b/packages/core-utils/src/provider.ts @@ -0,0 +1,40 @@ +/** + * Provider Utilities + */ + +import { ethers } from 'ethers' +import { Provider } from '@ethersproject/providers' + +// Copied from @ethersproject/providers since it is not +// currently exported +export interface FallbackProviderConfig { + // The Provider + provider: Provider + // The priority to favour this Provider; higher values are used first + priority?: number + // Timeout before also triggering the next provider; this does not stop + // this provider and if its result comes back before a quorum is reached + // it will be incorporated into the vote + // - lower values will cause more network traffic but may result in a + // faster retult. + stallTimeout?: number + // How much this provider contributes to the quorum; sometimes a specific + // provider may be more reliable or trustworthy than others, but usually + // this should be left as the default + weight?: number +} + +export const FallbackProvider = (config: string | FallbackProviderConfig[]) => { + const configs = [] + if (typeof config === 'string') { + const urls = config.split(',') + for (const [i, url] of urls.entries()) { + configs.push({ + priority: i, + provider: new ethers.providers.StaticJsonRpcProvider(url), + }) + } + return new ethers.providers.FallbackProvider(configs) + } + return new ethers.providers.FallbackProvider(config) +} diff --git a/packages/data-transport-layer/src/services/l1-ingestion/service.ts b/packages/data-transport-layer/src/services/l1-ingestion/service.ts index 4c1df60f3ef7..6b1ad0943ec5 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/service.ts @@ -1,7 +1,7 @@ /* Imports: External */ -import { fromHexString } from '@eth-optimism/core-utils' +import { fromHexString, FallbackProvider } from '@eth-optimism/core-utils' import { BaseService, Metrics } from '@eth-optimism/common-ts' -import { StaticJsonRpcProvider } from '@ethersproject/providers' +import { StaticJsonRpcProvider, BaseProvider } from '@ethersproject/providers' import { LevelUp } from 'levelup' import { ethers, constants } from 'ethers' import { Gauge, Counter } from 'prom-client' @@ -80,7 +80,7 @@ const optionSettings = { }, l1RpcProvider: { validate: (val: any) => { - return validators.isUrl(val) || validators.isJsonRpcProvider(val) + return validators.isString(val) || validators.isJsonRpcProvider(val) }, }, l2ChainId: { @@ -98,7 +98,7 @@ export class L1IngestionService extends BaseService { private state: { db: TransportDB contracts: OptimismContracts - l1RpcProvider: StaticJsonRpcProvider + l1RpcProvider: BaseProvider startingL1BlockNumber: number } = {} as any @@ -107,10 +107,11 @@ export class L1IngestionService extends BaseService { this.l1IngestionMetrics = registerMetrics(this.metrics) - this.state.l1RpcProvider = - typeof this.options.l1RpcProvider === 'string' - ? new StaticJsonRpcProvider(this.options.l1RpcProvider) - : this.options.l1RpcProvider + if (typeof this.options.l1RpcProvider === 'string') { + this.state.l1RpcProvider = FallbackProvider(this.options.l1RpcProvider) + } else { + this.state.l1RpcProvider = this.options.l1RpcProvider + } this.logger.info('Using AddressManager', { addressManager: this.options.addressManager, diff --git a/packages/data-transport-layer/src/types/event-handler-types.ts b/packages/data-transport-layer/src/types/event-handler-types.ts index 1839796b527e..4a099957ea8a 100644 --- a/packages/data-transport-layer/src/types/event-handler-types.ts +++ b/packages/data-transport-layer/src/types/event-handler-types.ts @@ -1,4 +1,4 @@ -import { JsonRpcProvider } from '@ethersproject/providers' +import { BaseProvider } from '@ethersproject/providers' import { BigNumber, Event } from 'ethers' import { TransportDB } from '../db/transport-db' @@ -15,7 +15,7 @@ export type TypedEthersEvent = Event & { export type GetExtraDataHandler = ( event?: TypedEthersEvent, - l1RpcProvider?: JsonRpcProvider + l1RpcProvider?: BaseProvider ) => Promise export type ParseEventHandler = ( diff --git a/packages/data-transport-layer/src/utils/contracts.ts b/packages/data-transport-layer/src/utils/contracts.ts index 09a741f6ca53..21746a838771 100644 --- a/packages/data-transport-layer/src/utils/contracts.ts +++ b/packages/data-transport-layer/src/utils/contracts.ts @@ -1,12 +1,12 @@ /* Imports: External */ import { constants, Contract, Signer } from 'ethers' -import { JsonRpcProvider } from '@ethersproject/providers' +import { BaseProvider } from '@ethersproject/providers' import { getContractInterface } from '@eth-optimism/contracts' export const loadContract = ( name: string, address: string, - provider: JsonRpcProvider + provider: BaseProvider ): Contract => { return new Contract(address, getContractInterface(name) as any, provider) } @@ -15,7 +15,7 @@ export const loadProxyFromManager = async ( name: string, proxy: string, Lib_AddressManager: Contract, - provider: JsonRpcProvider + provider: BaseProvider ): Promise => { const address = await Lib_AddressManager.getAddress(proxy) @@ -36,7 +36,7 @@ export interface OptimismContracts { } export const loadOptimismContracts = async ( - l1RpcProvider: JsonRpcProvider, + l1RpcProvider: BaseProvider, addressManagerAddress: string, signer?: Signer ): Promise => { diff --git a/yarn.lock b/yarn.lock index 79dda43db377..bfe54993ff34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -922,6 +922,31 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/providers@^5.4.5": + version "5.4.5" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928" + integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/basex" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/networks" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/web" "^5.4.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/random@5.4.0", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16"