github.com/ethereum-optimism/optimism@v1.7.2/packages/sdk/src/utils/contracts.ts (about)

     1  import { getContractInterface, predeploys } from '@eth-optimism/contracts'
     2  import { ethers, Contract } from 'ethers'
     3  import l1StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1StandardBridge.sol/L1StandardBridge.json'
     4  import l2StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L2StandardBridge.sol/L2StandardBridge.json'
     5  import optimismMintableERC20 from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismMintableERC20.sol/OptimismMintableERC20.json'
     6  import optimismPortal from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal.sol/OptimismPortal.json'
     7  import l1CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json'
     8  import l2CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L2CrossDomainMessenger.sol/L2CrossDomainMessenger.json'
     9  import optimismMintableERC20Factory from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismMintableERC20Factory.sol/OptimismMintableERC20Factory.json'
    10  import proxyAdmin from '@eth-optimism/contracts-bedrock/forge-artifacts/ProxyAdmin.sol/ProxyAdmin.json'
    11  import l2OutputOracle from '@eth-optimism/contracts-bedrock/forge-artifacts/L2OutputOracle.sol/L2OutputOracle.json'
    12  import l1ERC721Bridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1ERC721Bridge.sol/L1ERC721Bridge.json'
    13  import l2ERC721Bridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L2ERC721Bridge.sol/L2ERC721Bridge.json'
    14  import l1Block from '@eth-optimism/contracts-bedrock/forge-artifacts/L1Block.sol/L1Block.json'
    15  import l2ToL1MessagePasser from '@eth-optimism/contracts-bedrock/forge-artifacts/L2ToL1MessagePasser.sol/L2ToL1MessagePasser.json'
    16  import gasPriceOracle from '@eth-optimism/contracts-bedrock/forge-artifacts/GasPriceOracle.sol/GasPriceOracle.json'
    17  import disputeGameFactory from '@eth-optimism/contracts-bedrock/forge-artifacts/DisputeGameFactory.sol/DisputeGameFactory.json'
    18  import optimismPortal2 from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal2.sol/OptimismPortal2.json'
    19  import faultDisputeGame from '@eth-optimism/contracts-bedrock/forge-artifacts/FaultDisputeGame.sol/FaultDisputeGame.json'
    20  
    21  import { toAddress } from './coercion'
    22  import { DeepPartial } from './type-utils'
    23  import { CrossChainMessenger } from '../cross-chain-messenger'
    24  import { StandardBridgeAdapter, ETHBridgeAdapter } from '../adapters'
    25  import {
    26    CONTRACT_ADDRESSES,
    27    DEFAULT_L2_CONTRACT_ADDRESSES,
    28    BRIDGE_ADAPTER_DATA,
    29    IGNORABLE_CONTRACTS,
    30  } from './chain-constants'
    31  import {
    32    OEContracts,
    33    OEL1Contracts,
    34    OEL2Contracts,
    35    OEContractsLike,
    36    AddressLike,
    37    BridgeAdapters,
    38    BridgeAdapterData,
    39  } from '../interfaces'
    40  
    41  /**
    42   * We've changed some contract names in this SDK to be a bit nicer. Here we remap these nicer names
    43   * back to the original contract names so we can look them up.
    44   */
    45  const NAME_REMAPPING = {
    46    AddressManager: 'Lib_AddressManager' as const,
    47    OVM_L1BlockNumber: 'iOVM_L1BlockNumber' as const,
    48    WETH: 'WETH9' as const,
    49    BedrockMessagePasser: 'L2ToL1MessagePasser' as const,
    50  }
    51  
    52  export const getContractInterfaceBedrock = (
    53    name: string
    54  ): ethers.utils.Interface => {
    55    let artifact: any = ''
    56    switch (name) {
    57      case 'Lib_AddressManager':
    58      case 'AddressManager':
    59        artifact = ''
    60        break
    61      case 'L1CrossDomainMessenger':
    62        artifact = l1CrossDomainMessenger
    63        break
    64      case 'L1ERC721Bridge':
    65        artifact = l1ERC721Bridge
    66        break
    67      case 'L2OutputOracle':
    68        artifact = l2OutputOracle
    69        break
    70      case 'OptimismMintableERC20Factory':
    71        artifact = optimismMintableERC20Factory
    72        break
    73      case 'ProxyAdmin':
    74        artifact = proxyAdmin
    75        break
    76      case 'L1StandardBridge':
    77        artifact = l1StandardBridge
    78        break
    79      case 'L2StandardBridge':
    80        artifact = l2StandardBridge
    81        break
    82      case 'OptimismPortal':
    83        artifact = optimismPortal
    84        break
    85      case 'L2CrossDomainMessenger':
    86        artifact = l2CrossDomainMessenger
    87        break
    88      case 'OptimismMintableERC20':
    89        artifact = optimismMintableERC20
    90        break
    91      case 'L2ERC721Bridge':
    92        artifact = l2ERC721Bridge
    93        break
    94      case 'L1Block':
    95        artifact = l1Block
    96        break
    97      case 'L2ToL1MessagePasser':
    98        artifact = l2ToL1MessagePasser
    99        break
   100      case 'GasPriceOracle':
   101        artifact = gasPriceOracle
   102        break
   103      case 'DisputeGameFactory':
   104        artifact = disputeGameFactory
   105        break
   106      case 'OptimismPortal2':
   107        artifact = optimismPortal2
   108        break
   109      case 'FaultDisputeGame':
   110        artifact = faultDisputeGame
   111        break
   112    }
   113    return new ethers.utils.Interface(artifact.abi)
   114  }
   115  
   116  /**
   117   * Returns an ethers.Contract object for the given name, connected to the appropriate address for
   118   * the given L2 chain ID. Users can also provide a custom address to connect the contract to
   119   * instead. If the chain ID is not known then the user MUST provide a custom address or this
   120   * function will throw an error.
   121   *
   122   * @param contractName Name of the contract to connect to.
   123   * @param l2ChainId Chain ID for the L2 network.
   124   * @param opts Additional options for connecting to the contract.
   125   * @param opts.address Custom address to connect to the contract.
   126   * @param opts.signerOrProvider Signer or provider to connect to the contract.
   127   * @returns An ethers.Contract object connected to the appropriate address and interface.
   128   */
   129  export const getOEContract = (
   130    contractName: keyof OEL1Contracts | keyof OEL2Contracts,
   131    l2ChainId: number,
   132    opts: {
   133      address?: AddressLike
   134      signerOrProvider?: ethers.Signer | ethers.providers.Provider
   135    } = {}
   136  ): Contract => {
   137    // Generally we want to throw an error if a contract address is not provided but there are some
   138    // exceptions, particularly for contracts that are part of an upgrade that has not yet been
   139    // deployed. It's OK to not provide an address for these contracts with the caveat that this may
   140    // cause a runtime error if the contract does actually need to be used.
   141    const addresses = CONTRACT_ADDRESSES[l2ChainId]
   142    if (addresses === undefined && opts.address === undefined) {
   143      if (IGNORABLE_CONTRACTS.includes(contractName)) {
   144        return undefined
   145      } else {
   146        throw new Error(
   147          `cannot get contract ${contractName} for unknown L2 chain ID ${l2ChainId}, you must provide an address`
   148        )
   149      }
   150    }
   151  
   152    // Bedrock interfaces are backwards compatible. We can prefer Bedrock interfaces over legacy
   153    // interfaces if they exist.
   154    const name = NAME_REMAPPING[contractName] || contractName
   155    let iface: ethers.utils.Interface
   156    try {
   157      iface = getContractInterfaceBedrock(name)
   158    } catch (err) {
   159      iface = getContractInterface(name)
   160    }
   161  
   162    return new Contract(
   163      toAddress(
   164        opts.address || addresses.l1[contractName] || addresses.l2[contractName]
   165      ),
   166      iface,
   167      opts.signerOrProvider
   168    )
   169  }
   170  
   171  /**
   172   * Automatically connects to all contract addresses, both L1 and L2, for the given L2 chain ID. The
   173   * user can provide custom contract address overrides for L1 or L2 contracts. If the given chain ID
   174   * is not known then the user MUST provide custom contract addresses for ALL L1 contracts or this
   175   * function will throw an error.
   176   *
   177   * @param l2ChainId Chain ID for the L2 network.
   178   * @param opts Additional options for connecting to the contracts.
   179   * @param opts.l1SignerOrProvider: Signer or provider to connect to the L1 contracts.
   180   * @param opts.l2SignerOrProvider: Signer or provider to connect to the L2 contracts.
   181   * @param opts.overrides Custom contract address overrides for L1 or L2 contracts.
   182   * @returns An object containing ethers.Contract objects connected to the appropriate addresses on
   183   * both L1 and L2.
   184   */
   185  export const getAllOEContracts = (
   186    l2ChainId: number,
   187    opts: {
   188      l1SignerOrProvider?: ethers.Signer | ethers.providers.Provider
   189      l2SignerOrProvider?: ethers.Signer | ethers.providers.Provider
   190      overrides?: DeepPartial<OEContractsLike>
   191    } = {}
   192  ): OEContracts => {
   193    const addresses = CONTRACT_ADDRESSES[l2ChainId] || {
   194      l1: {
   195        AddressManager: undefined,
   196        L1CrossDomainMessenger: undefined,
   197        L1StandardBridge: undefined,
   198        StateCommitmentChain: undefined,
   199        CanonicalTransactionChain: undefined,
   200        BondManager: undefined,
   201        OptimismPortal: undefined,
   202        L2OutputOracle: undefined,
   203        DisputeGameFactory: undefined,
   204        OptimismPortal2: undefined,
   205      },
   206      l2: DEFAULT_L2_CONTRACT_ADDRESSES,
   207    }
   208  
   209    // Attach all L1 contracts.
   210    const l1Contracts = {} as OEL1Contracts
   211    for (const [contractName, contractAddress] of Object.entries(addresses.l1)) {
   212      l1Contracts[contractName] = getOEContract(
   213        contractName as keyof OEL1Contracts,
   214        l2ChainId,
   215        {
   216          address: opts.overrides?.l1?.[contractName] || contractAddress,
   217          signerOrProvider: opts.l1SignerOrProvider,
   218        }
   219      )
   220    }
   221  
   222    // Attach all L2 contracts.
   223    const l2Contracts = {} as OEL2Contracts
   224    for (const [contractName, contractAddress] of Object.entries(addresses.l2)) {
   225      l2Contracts[contractName] = getOEContract(
   226        contractName as keyof OEL2Contracts,
   227        l2ChainId,
   228        {
   229          address: opts.overrides?.l2?.[contractName] || contractAddress,
   230          signerOrProvider: opts.l2SignerOrProvider,
   231        }
   232      )
   233    }
   234  
   235    return {
   236      l1: l1Contracts,
   237      l2: l2Contracts,
   238    }
   239  }
   240  
   241  /**
   242   * Gets a series of bridge adapters for the given L2 chain ID.
   243   *
   244   * @param l2ChainId Chain ID for the L2 network.
   245   * @param messenger Cross chain messenger to connect to the bridge adapters
   246   * @param opts Additional options for connecting to the custom bridges.
   247   * @param opts.overrides Custom bridge adapters.
   248   * @returns An object containing all bridge adapters
   249   */
   250  export const getBridgeAdapters = (
   251    l2ChainId: number,
   252    messenger: CrossChainMessenger,
   253    opts?: {
   254      overrides?: BridgeAdapterData
   255      contracts?: DeepPartial<OEContractsLike>
   256    }
   257  ): BridgeAdapters => {
   258    const adapterData: BridgeAdapterData = {
   259      ...(CONTRACT_ADDRESSES[l2ChainId] || opts?.contracts?.l1?.L1StandardBridge
   260        ? {
   261            Standard: {
   262              Adapter: StandardBridgeAdapter,
   263              l1Bridge:
   264                opts?.contracts?.l1?.L1StandardBridge ||
   265                CONTRACT_ADDRESSES[l2ChainId].l1.L1StandardBridge,
   266              l2Bridge: predeploys.L2StandardBridge,
   267            },
   268            ETH: {
   269              Adapter: ETHBridgeAdapter,
   270              l1Bridge:
   271                opts?.contracts?.l1?.L1StandardBridge ||
   272                CONTRACT_ADDRESSES[l2ChainId].l1.L1StandardBridge,
   273              l2Bridge: predeploys.L2StandardBridge,
   274            },
   275          }
   276        : {}),
   277      ...(BRIDGE_ADAPTER_DATA[l2ChainId] || {}),
   278      ...(opts?.overrides || {}),
   279    }
   280  
   281    const adapters: BridgeAdapters = {}
   282    for (const [bridgeName, bridgeData] of Object.entries(adapterData)) {
   283      adapters[bridgeName] = new bridgeData.Adapter({
   284        messenger,
   285        l1Bridge: bridgeData.l1Bridge,
   286        l2Bridge: bridgeData.l2Bridge,
   287      })
   288    }
   289  
   290    return adapters
   291  }