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 }