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/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, } }