diff --git a/.changeset/fair-icons-argue.md b/.changeset/fair-icons-argue.md new file mode 100644 index 000000000000..b1e90110a7ce --- /dev/null +++ b/.changeset/fair-icons-argue.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts': patch +--- + +"Adds connectL1Contracts and connectL2Contracts utility functions" diff --git a/.changeset/lazy-ghosts-shop.md b/.changeset/lazy-ghosts-shop.md new file mode 100644 index 000000000000..9fb797f017e9 --- /dev/null +++ b/.changeset/lazy-ghosts-shop.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/message-relayer': patch +--- + +Update relayer package JSON to correctly export all files in dist diff --git a/.changeset/tidy-rivers-press.md b/.changeset/tidy-rivers-press.md new file mode 100644 index 000000000000..16e413fc8e17 --- /dev/null +++ b/.changeset/tidy-rivers-press.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/integration-tests': patch +'@eth-optimism/data-transport-layer': patch +--- + +Add replica sync test to integration tests; handle 0 L2 blocks in DTL diff --git a/.github/workflows/sync-tests.yml b/.github/workflows/sync-tests.yml index a6b2a38cc51a..2e3c94d37f3c 100644 --- a/.github/workflows/sync-tests.yml +++ b/.github/workflows/sync-tests.yml @@ -34,6 +34,7 @@ jobs: working-directory: ./integration-tests run: | yarn + yarn build:integration yarn test:sync - name: Collect docker logs on failure diff --git a/integration-tests/sync-tests/sync-verifier.spec.ts b/integration-tests/sync-tests/1-sync-verifier.spec.ts similarity index 90% rename from integration-tests/sync-tests/sync-verifier.spec.ts rename to integration-tests/sync-tests/1-sync-verifier.spec.ts index 2c931eb87c4f..acc5963b3ec1 100644 --- a/integration-tests/sync-tests/sync-verifier.spec.ts +++ b/integration-tests/sync-tests/1-sync-verifier.spec.ts @@ -2,7 +2,12 @@ import chai, { expect } from 'chai' import { Wallet, BigNumber, providers } from 'ethers' import { injectL2Context } from '@eth-optimism/core-utils' -import { sleep, l2Provider, verifierProvider } from '../test/shared/utils' +import { + sleep, + l2Provider, + verifierProvider, + waitForL2Geth, +} from '../test/shared/utils' import { OptimismEnv } from '../test/shared/env' import { DockerComposeNetwork } from '../test/shared/docker-compose' @@ -33,14 +38,7 @@ describe('Syncing a verifier', () => { verifier = new DockerComposeNetwork(['verifier']) await verifier.up({ commandOptions: ['--scale', 'verifier=1'] }) - // Wait for verifier to be looping - let logs = await verifier.logs() - while (!logs.out.includes('Starting Sequencer Loop')) { - await sleep(500) - logs = await verifier.logs() - } - - provider = injectL2Context(verifierProvider) + provider = await waitForL2Geth(verifierProvider) } const syncVerifier = async (sequencerBlockNumber: number) => { diff --git a/integration-tests/sync-tests/2-sync-replica.spec.ts b/integration-tests/sync-tests/2-sync-replica.spec.ts new file mode 100644 index 000000000000..ae8a108fbd03 --- /dev/null +++ b/integration-tests/sync-tests/2-sync-replica.spec.ts @@ -0,0 +1,119 @@ +import chai, { expect } from 'chai' +import { Wallet, Contract, ContractFactory, providers } from 'ethers' +import { ethers } from 'hardhat' +import { injectL2Context } from '@eth-optimism/core-utils' + +import { + sleep, + l2Provider, + replicaProvider, + waitForL2Geth, +} from '../test/shared/utils' +import { OptimismEnv } from '../test/shared/env' +import { DockerComposeNetwork } from '../test/shared/docker-compose' + +describe('Syncing a replica', () => { + let env: OptimismEnv + let wallet: Wallet + let replica: DockerComposeNetwork + let provider: providers.JsonRpcProvider + + const sequencerProvider = injectL2Context(l2Provider) + + /* Helper functions */ + + const startReplica = async () => { + // Bring up new replica + replica = new DockerComposeNetwork(['replica']) + await replica.up({ + commandOptions: ['--scale', 'replica=1'], + }) + + provider = await waitForL2Geth(replicaProvider) + } + + const syncReplica = async (sequencerBlockNumber: number) => { + // Wait until replica has caught up to the sequencer + let latestReplicaBlock = (await provider.getBlock('latest')) as any + while (latestReplicaBlock.number < sequencerBlockNumber) { + await sleep(500) + latestReplicaBlock = (await provider.getBlock('latest')) as any + } + + return provider.getBlock(sequencerBlockNumber) + } + + before(async () => { + env = await OptimismEnv.new() + wallet = env.l2Wallet + }) + + after(async () => { + await replica.stop('replica') + await replica.rm() + }) + + describe('Basic transactions and ERC20s', () => { + const initialAmount = 1000 + const tokenName = 'OVM Test' + const tokenDecimals = 8 + const TokenSymbol = 'OVM' + + let other: Wallet + let Factory__ERC20: ContractFactory + let ERC20: Contract + + before(async () => { + other = Wallet.createRandom().connect(ethers.provider) + Factory__ERC20 = await ethers.getContractFactory('ERC20', wallet) + }) + + it('should sync dummy transaction', async () => { + const tx = { + to: '0x' + '1234'.repeat(10), + gasLimit: 4000000, + gasPrice: 0, + data: '0x', + value: 0, + } + const result = await wallet.sendTransaction(tx) + await result.wait() + + const latestSequencerBlock = (await sequencerProvider.getBlock( + 'latest' + )) as any + + await startReplica() + + const matchingReplicaBlock = (await syncReplica( + latestSequencerBlock.number + )) as any + + expect(matchingReplicaBlock.stateRoot).to.eq( + latestSequencerBlock.stateRoot + ) + }) + + it('should sync ERC20 deployment and transfer', async () => { + ERC20 = await Factory__ERC20.deploy( + initialAmount, + tokenName, + tokenDecimals, + TokenSymbol + ) + + const transfer = await ERC20.transfer(other.address, 100) + await transfer.wait() + + const latestSequencerBlock = (await provider.getBlock('latest')) as any + + const matchingReplicaBlock = (await syncReplica( + latestSequencerBlock.number + )) as any + + expect(matchingReplicaBlock.stateRoot).to.eq( + latestSequencerBlock.stateRoot + ) + }) + }) +}) diff --git a/integration-tests/test/shared/docker-compose.ts b/integration-tests/test/shared/docker-compose.ts index d283fd0eae65..8ecf6beae112 100644 --- a/integration-tests/test/shared/docker-compose.ts +++ b/integration-tests/test/shared/docker-compose.ts @@ -8,6 +8,7 @@ type ServiceNames = | 'l2geth' | 'relayer' | 'verifier' + | 'replica' const OPS_DIRECTORY = path.join(process.cwd(), '../ops') const DEFAULT_SERVICES: ServiceNames[] = [ diff --git a/integration-tests/test/shared/utils.ts b/integration-tests/test/shared/utils.ts index 47fdca63851e..a6add155999f 100644 --- a/integration-tests/test/shared/utils.ts +++ b/integration-tests/test/shared/utils.ts @@ -7,7 +7,7 @@ import { getContractInterface, predeploys, } from '@eth-optimism/contracts' -import { remove0x, Watcher } from '@eth-optimism/core-utils' +import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils' import { Contract, Wallet, @@ -25,9 +25,11 @@ const env = cleanEnv(process.env, { L1_URL: str({ default: 'http://localhost:9545' }), L2_URL: str({ default: 'http://localhost:8545' }), VERIFIER_URL: str({ default: 'http://localhost:8547' }), + REPLICA_URL: str({ default: 'http://localhost:8549' }), L1_POLLING_INTERVAL: num({ default: 10 }), L2_POLLING_INTERVAL: num({ default: 10 }), VERIFIER_POLLING_INTERVAL: num({ default: 10 }), + REPLICA_POLLING_INTERVAL: num({ default: 10 }), PRIVATE_KEY: str({ default: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', @@ -47,6 +49,9 @@ l2Provider.pollingInterval = env.L2_POLLING_INTERVAL export const verifierProvider = new providers.JsonRpcProvider(env.VERIFIER_URL) verifierProvider.pollingInterval = env.VERIFIER_POLLING_INTERVAL +export const replicaProvider = new providers.JsonRpcProvider(env.REPLICA_URL) +replicaProvider.pollingInterval = env.REPLICA_POLLING_INTERVAL + // The sequencer private key which is funded on L1 export const l1Wallet = new Wallet(env.PRIVATE_KEY, l1Provider) @@ -168,3 +173,18 @@ export const expectApprox = ( `Actual value is more than ${lowerDeviation}% less than target` ).to.be.true } + +export const waitForL2Geth = async ( + provider: providers.JsonRpcProvider +): Promise => { + let ready: boolean = false + while (!ready) { + try { + await provider.getNetwork() + ready = true + } catch (error) { + await sleep(1000) + } + } + return injectL2Context(provider) +} diff --git a/ops/README.md b/ops/README.md index 1dc7858efb0e..6838b666ec53 100644 --- a/ops/README.md +++ b/ops/README.md @@ -30,12 +30,14 @@ docker-compose \ up --build --detach ``` -Optionally, run a verifier along the rest of the stack. +Optionally, run a verifier along the rest of the stack. Run a replica with the same command by switching the service name! ``` -docker-compose up --scale verifier=1 \ +docker-compose up --scale \ + verifier=1 \ --build --detach ``` + A Makefile has been provided for convience. The following targets are available. - make up - make down diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml index 496f3738705f..9791365d6404 100644 --- a/ops/docker-compose.yml +++ b/ops/docker-compose.yml @@ -62,6 +62,7 @@ services: # connect to the 2 layers DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT: http://l1_chain:8545 DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT: http://l2geth:8545 + DATA_TRANSPORT_LAYER__SYNC_FROM_L2: 'true' DATA_TRANSPORT_LAYER__L2_CHAIN_ID: 420 ports: - ${DTL_PORT:-7878}:7878 @@ -141,23 +142,46 @@ services: build: context: .. dockerfile: ./ops/docker/Dockerfile.geth - # override with the geth script and the env vars required for it entrypoint: sh ./geth.sh env_file: - ./envs/geth.env environment: ETH1_HTTP: http://l1_chain:8545 ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json - # used for getting the addresses URL: http://deployer:8081/addresses.json - # connecting to the DTL ROLLUP_CLIENT_HTTP: http://dtl:7878 + ROLLUP_BACKEND: 'l1' ETH1_CTC_DEPLOYMENT_HEIGHT: 8 RETRIES: 60 - IS_VERIFIER: "true" + ROLLUP_VERIFIER_ENABLE: 'true' ports: - ${VERIFIER_HTTP_PORT:-8547}:8545 - ${VERIFIER_WS_PORT:-8548}:8546 + + replica: + depends_on: + - dtl + image: ethereumoptimism/l2geth + deploy: + replicas: 0 + build: + context: .. + dockerfile: ./ops/docker/Dockerfile.geth + entrypoint: sh ./geth.sh + env_file: + - ./envs/geth.env + environment: + ETH1_HTTP: http://l1_chain:8545 + ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json + URL: http://deployer:8081/addresses.json + ROLLUP_CLIENT_HTTP: http://dtl:7878 + ROLLUP_BACKEND: 'l2' + ROLLUP_VERIFIER_ENABLE: 'true' + ETH1_CTC_DEPLOYMENT_HEIGHT: 8 + RETRIES: 60 + ports: + - ${L2GETH_HTTP_PORT:-8549}:8545 + - ${L2GETH_WS_PORT:-8550}:8546 integration_tests: image: ethereumoptimism/integration-tests diff --git a/ops/docker/Dockerfile.batch-submitter b/ops/docker/Dockerfile.batch-submitter index 1d5c92e758a6..07b14a9b3c09 100644 --- a/ops/docker/Dockerfile.batch-submitter +++ b/ops/docker/Dockerfile.batch-submitter @@ -17,6 +17,7 @@ COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common- COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist COPY --from=builder /optimism/packages/contracts/package.json ./packages/contracts/package.json +COPY --from=builder /optimism/packages/contracts/deployments ./packages/contracts/deployments COPY --from=builder /optimism/packages/contracts/dist ./packages/contracts/dist COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/artifacts COPY --from=builder /optimism/packages/contracts/artifacts-ovm ./packages/contracts/artifacts-ovm diff --git a/ops/docker/Dockerfile.data-transport-layer b/ops/docker/Dockerfile.data-transport-layer index e6bcdfee2b89..dfc1c8278b78 100644 --- a/ops/docker/Dockerfile.data-transport-layer +++ b/ops/docker/Dockerfile.data-transport-layer @@ -18,6 +18,7 @@ COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common- COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist COPY --from=builder /optimism/packages/contracts/package.json ./packages/contracts/package.json +COPY --from=builder /optimism/packages/contracts/deployments ./packages/contracts/deployments COPY --from=builder /optimism/packages/contracts/dist ./packages/contracts/dist COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/artifacts COPY --from=builder /optimism/packages/contracts/artifacts-ovm ./packages/contracts/artifacts-ovm diff --git a/ops/docker/Dockerfile.message-relayer b/ops/docker/Dockerfile.message-relayer index 92614be95492..5c976daee892 100644 --- a/ops/docker/Dockerfile.message-relayer +++ b/ops/docker/Dockerfile.message-relayer @@ -18,6 +18,7 @@ COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common- COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist COPY --from=builder /optimism/packages/contracts/package.json ./packages/contracts/package.json +COPY --from=builder /optimism/packages/contracts/deployments ./packages/contracts/deployments COPY --from=builder /optimism/packages/contracts/dist ./packages/contracts/dist COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/artifacts COPY --from=builder /optimism/packages/contracts/artifacts-ovm ./packages/contracts/artifacts-ovm diff --git a/packages/contracts/src/connect-contracts.ts b/packages/contracts/src/connect-contracts.ts new file mode 100644 index 000000000000..d68ae07b7bb1 --- /dev/null +++ b/packages/contracts/src/connect-contracts.ts @@ -0,0 +1,125 @@ +import { Signer, Contract, providers, ethers } from 'ethers' +import { Provider } from '@ethersproject/abstract-provider' +import { getL1ContractData, getL2ContractData } from './contract-data' + +export type Network = 'goerli' | 'kovan' | 'mainnet' +interface L1Contracts { + addressManager: Contract + canonicalTransactionChain: Contract + executionManager: Contract + fraudVerifier: Contract + ethGateway: Contract + multiMessageRelayer: Contract + stateCommitmentChain: Contract + xDomainMessengerProxy: Contract + l1EthGatewayProxy: Contract + bondManager: Contract +} + +interface L2Contracts { + eth: Contract + xDomainMessenger: Contract + messagePasser: Contract + messageSender: Contract + deployerWhiteList: Contract + ecdsaContractAccount: Contract + sequencerEntrypoint: Contract + erc1820Registry: Contract + addressManager: Contract +} + +/** + * Validates user provided a singer or provider & throws error if not + * + * @param signerOrProvider + */ +const checkSignerType = (signerOrProvider: Signer | Provider) => { + if (!signerOrProvider) { + throw Error('signerOrProvider argument is undefined') + } + if ( + !Provider.isProvider(signerOrProvider) && + !Signer.isSigner(signerOrProvider) + ) { + throw Error('signerOrProvider argument is the wrong type') + } +} + +/** + * Connects a signer/provider to layer 1 contracts on a given network + * + * @param signerOrProvider ethers signer or provider + * @param network string denoting network + * @returns l1 contracts connected to signer/provider + */ +export const connectL1Contracts = async ( + signerOrProvider: Signer | Provider, + network: Network +): Promise => { + checkSignerType(signerOrProvider) + + if (!['mainnet', 'kovan', 'goerli'].includes(network)) { + throw Error('Must specify network: mainnet, kovan, or goerli.') + } + + const l1ContractData = getL1ContractData(network) + + const toEthersContract = (data) => + new Contract(data.address, data.abi, signerOrProvider) + + return { + addressManager: toEthersContract(l1ContractData.Lib_AddressManager), + canonicalTransactionChain: toEthersContract( + l1ContractData.OVM_CanonicalTransactionChain + ), + executionManager: toEthersContract(l1ContractData.OVM_ExecutionManager), + fraudVerifier: toEthersContract(l1ContractData.OVM_FraudVerifier), + ethGateway: toEthersContract(l1ContractData.OVM_L1ETHGateway), + multiMessageRelayer: toEthersContract( + l1ContractData.OVM_L1MultiMessageRelayer + ), + stateCommitmentChain: toEthersContract( + l1ContractData.OVM_StateCommitmentChain + ), + xDomainMessengerProxy: toEthersContract( + l1ContractData.Proxy__OVM_L1CrossDomainMessenger + ), + l1EthGatewayProxy: toEthersContract(l1ContractData.Proxy__OVM_L1ETHGateway), + // TODO: update this with actual bond manager when its ready + bondManager: toEthersContract(l1ContractData.mockOVM_BondManager), + } +} + +/** + * Connects a signer/provider to layer 2 contracts (network agnostic) + * + * @param signerOrProvider ethers signer or provider + * @returns l2 contracts connected to signer/provider + */ +export const connectL2Contracts = async ( + signerOrProvider +): Promise => { + const l2ContractData = await getL2ContractData() + checkSignerType(signerOrProvider) + + const toEthersContract = (data) => + new Contract(data.address, data.abi, signerOrProvider) + + return { + eth: toEthersContract(l2ContractData.OVM_ETH), + xDomainMessenger: toEthersContract( + l2ContractData.OVM_L2CrossDomainMessenger + ), + messagePasser: toEthersContract(l2ContractData.OVM_L2ToL1MessagePasser), + messageSender: toEthersContract(l2ContractData.OVM_L1MessageSender), + deployerWhiteList: toEthersContract(l2ContractData.OVM_DeployerWhitelist), + ecdsaContractAccount: toEthersContract( + l2ContractData.OVM_ECDSAContractAccount + ), + sequencerEntrypoint: toEthersContract( + l2ContractData.OVM_SequencerEntrypoint + ), + erc1820Registry: toEthersContract(l2ContractData.ERC1820Registry), + addressManager: toEthersContract(l2ContractData.Lib_AddressManager), + } +} diff --git a/packages/contracts/src/contract-data.ts b/packages/contracts/src/contract-data.ts new file mode 100644 index 000000000000..f492c798f109 --- /dev/null +++ b/packages/contracts/src/contract-data.ts @@ -0,0 +1,180 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { predeploys as l2Addresses } from './predeploys' +import { Network } from './connect-contracts' + +/** + * This file is necessarily not DRY because it needs to be usable + * in a browser context and can't take advantage of dynamic imports + * (ie: the json needs to all be imported when transpiled) + */ + +const Mainnet__Lib_AddressManager = require('../deployments/mainnet/Lib_AddressManager.json') +const Mainnet__OVM_CanonicalTransactionChain = require('../deployments/mainnet/OVM_CanonicalTransactionChain.json') +const Mainnet__OVM_ExecutionManager = require('../deployments/mainnet/OVM_ExecutionManager.json') +const Mainnet__OVM_FraudVerifier = require('../deployments/mainnet/OVM_FraudVerifier.json') +const Mainnet__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/OVM_L1CrossDomainMessenger.json') +const Mainnet__OVM_L1ETHGateway = require('../deployments/mainnet/OVM_L1ETHGateway.json') +const Mainnet__OVM_L1MultiMessageRelayer = require('../deployments/mainnet/OVM_L1MultiMessageRelayer.json') +const Mainnet__OVM_SafetyChecker = require('../deployments/mainnet/OVM_SafetyChecker.json') +const Mainnet__OVM_StateCommitmentChain = require('../deployments/mainnet/OVM_StateCommitmentChain.json') +const Mainnet__OVM_StateManagerFactory = require('../deployments/mainnet/OVM_StateManagerFactory.json') +const Mainnet__OVM_StateTransitionerFactory = require('../deployments/mainnet/OVM_StateTransitionerFactory.json') +const Mainnet__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/Proxy__OVM_L1CrossDomainMessenger.json') +const Mainnet__Proxy__OVM_L1ETHGateway = require('../deployments/mainnet/Proxy__OVM_L1ETHGateway.json') +const Mainnet__mockOVM_BondManager = require('../deployments/mainnet/mockOVM_BondManager.json') + +const Kovan__Lib_AddressManager = require('../deployments/kovan/Lib_AddressManager.json') +const Kovan__OVM_CanonicalTransactionChain = require('../deployments/kovan/OVM_CanonicalTransactionChain.json') +const Kovan__OVM_ExecutionManager = require('../deployments/kovan/OVM_ExecutionManager.json') +const Kovan__OVM_FraudVerifier = require('../deployments/kovan/OVM_FraudVerifier.json') +const Kovan__OVM_L1CrossDomainMessenger = require('../deployments/kovan/OVM_L1CrossDomainMessenger.json') +const Kovan__OVM_L1ETHGateway = require('../deployments/kovan/OVM_L1ETHGateway.json') +const Kovan__OVM_L1MultiMessageRelayer = require('../deployments/kovan/OVM_L1MultiMessageRelayer.json') +const Kovan__OVM_SafetyChecker = require('../deployments/kovan/OVM_SafetyChecker.json') +const Kovan__OVM_StateCommitmentChain = require('../deployments/kovan/OVM_StateCommitmentChain.json') +const Kovan__OVM_StateManagerFactory = require('../deployments/kovan/OVM_StateManagerFactory.json') +const Kovan__OVM_StateTransitionerFactory = require('../deployments/kovan/OVM_StateTransitionerFactory.json') +const Kovan__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/kovan/Proxy__OVM_L1CrossDomainMessenger.json') +const Kovan__Proxy__OVM_L1ETHGateway = require('../deployments/kovan/Proxy__OVM_L1ETHGateway.json') +const Kovan__mockOVM_BondManager = require('../deployments/kovan/mockOVM_BondManager.json') + +const Goerli__Lib_AddressManager = require('../deployments/goerli/Lib_AddressManager.json') +const Goerli__OVM_CanonicalTransactionChain = require('../deployments/goerli/OVM_CanonicalTransactionChain.json') +const Goerli__OVM_ExecutionManager = require('../deployments/goerli/OVM_ExecutionManager.json') +const Goerli__OVM_FraudVerifier = require('../deployments/goerli/OVM_FraudVerifier.json') +const Goerli__OVM_L1CrossDomainMessenger = require('../deployments/goerli/OVM_L1CrossDomainMessenger.json') +const Goerli__OVM_L1ETHGateway = require('../deployments/goerli/OVM_L1ETHGateway.json') +const Goerli__OVM_L1MultiMessageRelayer = require('../deployments/goerli/OVM_L1MultiMessageRelayer.json') +const Goerli__OVM_SafetyChecker = require('../deployments/goerli/OVM_SafetyChecker.json') +const Goerli__OVM_StateCommitmentChain = require('../deployments/goerli/OVM_StateCommitmentChain.json') +const Goerli__OVM_StateManagerFactory = require('../deployments/goerli/OVM_StateManagerFactory.json') +const Goerli__OVM_StateTransitionerFactory = require('../deployments/goerli/OVM_StateTransitionerFactory.json') +const Goerli__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/goerli/Proxy__OVM_L1CrossDomainMessenger.json') +const Goerli__Proxy__OVM_L1ETHGateway = require('../deployments/goerli/Proxy__OVM_L1ETHGateway.json') +const Goerli__mockOVM_BondManager = require('../deployments/goerli/mockOVM_BondManager.json') + +export const getL1ContractData = (network: Network) => { + return { + Lib_AddressManager: { + mainnet: Mainnet__Lib_AddressManager, + kovan: Kovan__Lib_AddressManager, + goerli: Goerli__Lib_AddressManager, + }[network], + OVM_CanonicalTransactionChain: { + mainnet: Mainnet__OVM_CanonicalTransactionChain, + kovan: Kovan__OVM_CanonicalTransactionChain, + goerli: Goerli__OVM_CanonicalTransactionChain, + }[network], + OVM_ExecutionManager: { + mainnet: Mainnet__OVM_ExecutionManager, + kovan: Kovan__OVM_ExecutionManager, + goerli: Goerli__OVM_ExecutionManager, + }[network], + OVM_FraudVerifier: { + mainnet: Mainnet__OVM_FraudVerifier, + kovan: Kovan__OVM_FraudVerifier, + goerli: Goerli__OVM_FraudVerifier, + }[network], + OVM_L1CrossDomainMessenger: { + mainnet: Mainnet__OVM_L1CrossDomainMessenger, + kovan: Kovan__OVM_L1CrossDomainMessenger, + goerli: Goerli__OVM_L1CrossDomainMessenger, + }[network], + OVM_L1ETHGateway: { + mainnet: Mainnet__OVM_L1ETHGateway, + kovan: Kovan__OVM_L1ETHGateway, + goerli: Goerli__OVM_L1ETHGateway, + }[network], + OVM_L1MultiMessageRelayer: { + mainnet: Mainnet__OVM_L1MultiMessageRelayer, + kovan: Kovan__OVM_L1MultiMessageRelayer, + goerli: Goerli__OVM_L1MultiMessageRelayer, + }[network], + OVM_SafetyChecker: { + mainnet: Mainnet__OVM_SafetyChecker, + kovan: Kovan__OVM_SafetyChecker, + goerli: Goerli__OVM_SafetyChecker, + }[network], + OVM_StateCommitmentChain: { + mainnet: Mainnet__OVM_StateCommitmentChain, + kovan: Kovan__OVM_StateCommitmentChain, + goerli: Goerli__OVM_StateCommitmentChain, + }[network], + OVM_StateManagerFactory: { + mainnet: Mainnet__OVM_StateManagerFactory, + kovan: Kovan__OVM_StateManagerFactory, + goerli: Goerli__OVM_StateManagerFactory, + }[network], + OVM_StateTransitionerFactory: { + mainnet: Mainnet__OVM_StateTransitionerFactory, + kovan: Kovan__OVM_StateTransitionerFactory, + goerli: Goerli__OVM_StateTransitionerFactory, + }[network], + Proxy__OVM_L1CrossDomainMessenger: { + mainnet: Mainnet__Proxy__OVM_L1CrossDomainMessenger, + kovan: Kovan__Proxy__OVM_L1CrossDomainMessenger, + goerli: Goerli__Proxy__OVM_L1CrossDomainMessenger, + }[network], + Proxy__OVM_L1ETHGateway: { + mainnet: Mainnet__Proxy__OVM_L1ETHGateway, + kovan: Kovan__Proxy__OVM_L1ETHGateway, + goerli: Goerli__Proxy__OVM_L1ETHGateway, + }[network], + mockOVM_BondManager: { + mainnet: Mainnet__mockOVM_BondManager, + kovan: Kovan__mockOVM_BondManager, + goerli: Goerli__mockOVM_BondManager, + }[network], + } +} + +const OVM_ETH = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/OVM_ETH.sol/OVM_ETH.json') +const OVM_L2CrossDomainMessenger = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L2CrossDomainMessenger.sol/OVM_L2CrossDomainMessenger.json') +const OVM_L2ToL1MessagePasser = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol/OVM_L2ToL1MessagePasser.json') +const OVM_L1MessageSender = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/OVM_L1MessageSender.sol/OVM_L1MessageSender.json') +const OVM_DeployerWhitelist = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/OVM_DeployerWhitelist.sol/OVM_DeployerWhitelist.json') +const OVM_ECDSAContractAccount = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol/OVM_ECDSAContractAccount.json') +const OVM_SequencerEntrypoint = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/OVM_SequencerEntrypoint.sol/OVM_SequencerEntrypoint.json') +const ERC1820Registry = require('../artifacts-ovm/contracts/optimistic-ethereum/OVM/predeploys/ERC1820Registry.sol/ERC1820Registry.json') +const Lib_AddressManager = require('../artifacts-ovm/contracts/optimistic-ethereum/libraries/resolver/Lib_AddressManager.sol/Lib_AddressManager.json') + +export const getL2ContractData = () => { + return { + OVM_ETH: { + abi: OVM_ETH.abi, + address: l2Addresses.OVM_ETH, + }, + OVM_L2CrossDomainMessenger: { + abi: OVM_L2CrossDomainMessenger.abi, + address: l2Addresses.OVM_L2CrossDomainMessenger, + }, + OVM_L2ToL1MessagePasser: { + abi: OVM_L2ToL1MessagePasser.abi, + address: l2Addresses.OVM_L2ToL1MessagePasser, + }, + OVM_L1MessageSender: { + abi: OVM_L1MessageSender.abi, + address: l2Addresses.OVM_L1MessageSender, + }, + OVM_DeployerWhitelist: { + abi: OVM_DeployerWhitelist.abi, + address: l2Addresses.OVM_DeployerWhitelist, + }, + OVM_ECDSAContractAccount: { + abi: OVM_ECDSAContractAccount.abi, + address: l2Addresses.OVM_ECDSAContractAccount, + }, + OVM_SequencerEntrypoint: { + abi: OVM_SequencerEntrypoint.abi, + address: l2Addresses.OVM_SequencerEntrypoint, + }, + ERC1820Registry: { + abi: ERC1820Registry.abi, + address: l2Addresses.ERC1820Registry, + }, + Lib_AddressManager: { + abi: Lib_AddressManager.abi, + address: l2Addresses.Lib_AddressManager, + }, + } +} diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 85ec437261a7..b38a30ad8e55 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -2,3 +2,4 @@ export * from './contract-defs' export * from './state-dump/get-dump' export * from './contract-deployment' export * from './predeploys' +export * from './connect-contracts' diff --git a/packages/contracts/test/connect-contracts.spec.ts b/packages/contracts/test/connect-contracts.spec.ts new file mode 100644 index 000000000000..fdf5684507d6 --- /dev/null +++ b/packages/contracts/test/connect-contracts.spec.ts @@ -0,0 +1,61 @@ +import { ethers } from 'hardhat' +import { Signer, Contract } from 'ethers' +import { + connectL1Contracts, + connectL2Contracts, +} from '../dist/connect-contracts' +import { expect } from './setup' + +describe('connectL1Contracts', () => { + let user: Signer + const l1ContractNames = [ + 'addressManager', + 'canonicalTransactionChain', + 'executionManager', + 'fraudVerifier', + 'ethGateway', + 'multiMessageRelayer', + 'stateCommitmentChain', + 'xDomainMessengerProxy', + 'l1EthGatewayProxy', + 'bondManager', + ] + + const l2ContractNames = [ + 'eth', + 'xDomainMessenger', + 'messagePasser', + 'messageSender', + 'deployerWhiteList', + 'ecdsaContractAccount', + 'sequencerEntrypoint', + 'erc1820Registry', + 'addressManager', + ] + + before(async () => { + ;[user] = await ethers.getSigners() + }) + + it(`connectL1Contracts should throw error if signer or provider isn't provided.`, async () => { + try { + await connectL1Contracts(undefined, 'mainnet') + } catch (err) { + expect(err.message).to.be.equal('signerOrProvider argument is undefined') + } + }) + + for (const name of l1ContractNames) { + it(`connectL1Contracts should return a contract assigned to a field named "${name}"`, async () => { + const l1Contracts = await connectL1Contracts(user, 'mainnet') + expect(l1Contracts[name]).to.be.an.instanceOf(Contract) + }) + } + + for (const name of l2ContractNames) { + it(`connectL2Contracts should return a contract assigned to a field named "${name}"`, async () => { + const l2Contracts = await connectL2Contracts(user) + expect(l2Contracts[name]).to.be.an.instanceOf(Contract) + }) + } +}) diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index aa33388023ee..b4e5ff40ae0e 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "resolveJsonModule": true + "resolveJsonModule": true, } } diff --git a/packages/data-transport-layer/src/services/l2-ingestion/service.ts b/packages/data-transport-layer/src/services/l2-ingestion/service.ts index 9b3eead27be5..dae374f7c756 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/service.ts @@ -88,7 +88,11 @@ export class L2IngestionService extends BaseService { ) // We're already at the head, so no point in attempting to sync. - if (highestSyncedL2BlockNumber === targetL2Block) { + // Also wait on edge case of no L2 transactions + if ( + highestSyncedL2BlockNumber === targetL2Block || + currentL2Block === 0 + ) { await sleep(this.options.pollingInterval) continue } diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 9d6f1ffd4c32..ad171597ebe7 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -5,7 +5,7 @@ "main": "dist/index", "types": "dist/index", "files": [ - "dist/index" + "dist/*" ], "scripts": { "start": "node ./exec/run-message-relayer.js",