contracts.ts 11.6 KB
Newer Older
1 2
import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import { ethers, Contract } from 'ethers'
3 4 5

import { toAddress } from './coercion'
import { DeepPartial } from './type-utils'
6 7 8 9 10 11 12
import {
  OEContracts,
  OEL1Contracts,
  OEL2Contracts,
  OEContractsLike,
  OEL2ContractsLike,
  AddressLike,
13 14
  BridgeAdapters,
  BridgeAdapterData,
15
  ICrossChainMessenger,
16
  Chain,
17
} from '../interfaces'
18 19 20 21 22
import {
  StandardBridgeAdapter,
  ETHBridgeAdapter,
  DAIBridgeAdapter,
} from '../adapters'
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

/**
 * Full list of default L2 contract addresses.
 */
export const DEFAULT_L2_CONTRACT_ADDRESSES: OEL2ContractsLike = {
  L2CrossDomainMessenger: predeploys.L2CrossDomainMessenger,
  L2StandardBridge: predeploys.L2StandardBridge,
  OVM_L1BlockNumber: predeploys.OVM_L1BlockNumber,
  OVM_L2ToL1MessagePasser: predeploys.OVM_L2ToL1MessagePasser,
  OVM_DeployerWhitelist: predeploys.OVM_DeployerWhitelist,
  OVM_ETH: predeploys.OVM_ETH,
  OVM_GasPriceOracle: predeploys.OVM_GasPriceOracle,
  OVM_SequencerFeeVault: predeploys.OVM_SequencerFeeVault,
  WETH: predeploys.WETH9,
}

/**
 * We've changed some contract names in this SDK to be a bit nicer. Here we remap these nicer names
 * back to the original contract names so we can look them up.
 */
const NAME_REMAPPING = {
44 45 46
  AddressManager: 'Lib_AddressManager' as const,
  OVM_L1BlockNumber: 'iOVM_L1BlockNumber' as const,
  WETH: 'WETH9' as const,
47 48 49 50 51 52 53 54 55 56
}

/**
 * Mapping of L1 chain IDs to the appropriate contract addresses for the OE deployments to the
 * given network. Simplifies the process of getting the correct contract addresses for a given
 * contract name.
 */
export const CONTRACT_ADDRESSES: {
  [l1ChainId: number]: OEContractsLike
} = {
57
  [Chain.MAINNET]: {
58
    l1: {
59 60 61 62 63 64 65 66 67
      AddressManager: '0xdE1FCfB0851916CA5101820A69b13a4E276bd81F' as const,
      L1CrossDomainMessenger:
        '0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1' as const,
      L1StandardBridge: '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1' as const,
      StateCommitmentChain:
        '0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19' as const,
      CanonicalTransactionChain:
        '0x5E4e65926BA27467555EB562121fac00D24E9dD2' as const,
      BondManager: '0xcd626E1328b41fCF24737F137BcD4CE0c32bc8d1' as const,
68 69 70
    },
    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
  },
71
  [Chain.KOVAN]: {
72
    l1: {
73 74 75 76 77 78 79 80 81
      AddressManager: '0x100Dd3b414Df5BbA2B542864fF94aF8024aFdf3a' as const,
      L1CrossDomainMessenger:
        '0x4361d0F75A0186C05f971c566dC6bEa5957483fD' as const,
      L1StandardBridge: '0x22F24361D548e5FaAfb36d1437839f080363982B' as const,
      StateCommitmentChain:
        '0xD7754711773489F31A0602635f3F167826ce53C5' as const,
      CanonicalTransactionChain:
        '0xf7B88A133202d41Fe5E2Ab22e6309a1A4D50AF74' as const,
      BondManager: '0xc5a603d273E28185c18Ba4d26A0024B2d2F42740' as const,
82 83 84
    },
    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
  },
85
  [Chain.GOERLI]: {
86
    l1: {
87 88 89 90 91 92 93 94 95
      AddressManager: '0x2F7E3cAC91b5148d336BbffB224B4dC79F09f01D' as const,
      L1CrossDomainMessenger:
        '0xEcC89b9EDD804850C4F343A278Be902be11AaF42' as const,
      L1StandardBridge: '0x73298186A143a54c20ae98EEE5a025bD5979De02' as const,
      StateCommitmentChain:
        '0x1afcA918eff169eE20fF8AB6Be75f3E872eE1C1A' as const,
      CanonicalTransactionChain:
        '0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81' as const,
      BondManager: '0xE5AE60bD6F8DEe4D0c2BC9268e23B92F1cacC58F' as const,
96 97 98
    },
    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
  },
99
  [Chain.HARDHAT_LOCAL]: {
100
    l1: {
101 102 103 104 105 106 107 108 109
      AddressManager: '0x5FbDB2315678afecb367f032d93F642f64180aa3' as const,
      L1CrossDomainMessenger:
        '0x8A791620dd6260079BF849Dc5567aDC3F2FdC318' as const,
      L1StandardBridge: '0x610178dA211FEF7D417bC0e6FeD39F05609AD788' as const,
      StateCommitmentChain:
        '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' as const,
      CanonicalTransactionChain:
        '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' as const,
      BondManager: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' as const,
110 111 112 113 114
    },
    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
  },
}

115 116 117 118 119 120 121 122
/**
 * Mapping of L1 chain IDs to the list of custom bridge addresses for each chain.
 */
export const BRIDGE_ADAPTER_DATA: {
  [l1ChainId: number]: BridgeAdapterData
} = {
  // TODO: Maybe we can pull these automatically from the token list?
  // Alternatively, check against the token list in CI.
123
  [Chain.MAINNET]: {
124 125 126 127 128 129 130 131 132 133 134 135
    Standard: {
      Adapter: StandardBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[1].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    ETH: {
      Adapter: ETHBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[1].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    BitBTC: {
      Adapter: StandardBridgeAdapter,
136 137
      l1Bridge: '0xaBA2c5F108F7E820C049D5Af70B16ac266c8f128' as const,
      l2Bridge: '0x158F513096923fF2d3aab2BcF4478536de6725e2' as const,
138 139 140
    },
    DAI: {
      Adapter: DAIBridgeAdapter,
141 142
      l1Bridge: '0x10E6593CDda8c58a1d0f14C5164B376352a55f2F' as const,
      l2Bridge: '0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65' as const,
143 144
    },
  },
145
  [Chain.KOVAN]: {
146 147 148 149 150 151 152 153 154 155 156 157
    Standard: {
      Adapter: StandardBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[42].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    ETH: {
      Adapter: ETHBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[42].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    BitBTC: {
      Adapter: StandardBridgeAdapter,
158 159
      l1Bridge: '0x0b651A42F32069d62d5ECf4f2a7e5Bd3E9438746' as const,
      l2Bridge: '0x0CFb46528a7002a7D8877a5F7a69b9AaF1A9058e' as const,
160 161 162
    },
    USX: {
      Adapter: StandardBridgeAdapter,
163 164
      l1Bridge: '0x40E862341b2416345F02c41Ac70df08525150dC7' as const,
      l2Bridge: '0xB4d37826b14Cd3CB7257A2A5094507d701fe715f' as const,
165 166 167
    },
    DAI: {
      Adapter: DAIBridgeAdapter,
168 169
      l1Bridge: '0xb415e822C4983ecD6B1c1596e8a5f976cf6CD9e3' as const,
      l2Bridge: '0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65' as const,
170 171
    },
  },
172
  [Chain.GOERLI]: {
173 174 175 176 177 178 179 180 181
    Standard: {
      Adapter: StandardBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[5].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    ETH: {
      Adapter: ETHBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[5].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
182 183
    },
  },
184
  [Chain.HARDHAT_LOCAL]: {
185 186 187 188 189 190 191 192 193
    Standard: {
      Adapter: StandardBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[31337].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
    },
    ETH: {
      Adapter: ETHBridgeAdapter,
      l1Bridge: CONTRACT_ADDRESSES[31337].l1.L1StandardBridge,
      l2Bridge: predeploys.L2StandardBridge,
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    },
  },
}

// TODO: PR is big enough as-is, will add support for SNX in another PR
// MAINNET
// l1: {
//   SNX: '0xCd9D4988C0AE61887B075bA77f08cbFAd2b65068',
// },
// l2: {
//   SNX: '0x3f87Ff1de58128eF8FCb4c807eFD776E1aC72E51',
// },
// KOVAN
// l1: {
//   SNX: '0xD134Db47DDF5A6feB245452af17cCAf92ee53D3c',
// },
// l2: {
//   SNX: '0x5C3f51CEd0C2F6157e2be67c029264D6C44bfe42',
// },

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
/**
 * Returns an ethers.Contract object for the given name, connected to the appropriate address for
 * the given L1 chain ID. Users can also provide a custom address to connect the contract to
 * instead. If the chain ID is not known then the user MUST provide a custom address or this
 * function will throw an error.
 *
 * @param contractName Name of the contract to connect to.
 * @param l1ChainId Chain ID for the L1 network where the OE contracts are deployed.
 * @param opts Additional options for connecting to the contract.
 * @param opts.address Custom address to connect to the contract.
 * @param opts.signerOrProvider Signer or provider to connect to the contract.
 * @returns An ethers.Contract object connected to the appropriate address and interface.
 */
export const getOEContract = (
  contractName: keyof OEL1Contracts | keyof OEL2Contracts,
  l1ChainId: number,
  opts: {
    address?: AddressLike
    signerOrProvider?: ethers.Signer | ethers.providers.Provider
  } = {}
): Contract => {
  const addresses = CONTRACT_ADDRESSES[l1ChainId]
  if (addresses === undefined && opts.address === undefined) {
    throw new Error(
      `cannot get contract ${contractName} for unknown L1 chain ID ${l1ChainId}, you must provide an address`
    )
  }

  return new Contract(
    toAddress(
      opts.address || addresses.l1[contractName] || addresses.l2[contractName]
    ),
    getContractInterface(NAME_REMAPPING[contractName] || contractName),
    opts.signerOrProvider
  )
}

/**
 * Automatically connects to all contract addresses, both L1 and L2, for the given L1 chain ID. The
 * user can provide custom contract address overrides for L1 or L2 contracts. If the given chain ID
 * is not known then the user MUST provide custom contract addresses for ALL L1 contracts or this
 * function will throw an error.
 *
 * @param l1ChainId Chain ID for the L1 network where the OE contracts are deployed.
 * @param opts Additional options for connecting to the contracts.
 * @param opts.l1SignerOrProvider: Signer or provider to connect to the L1 contracts.
 * @param opts.l2SignerOrProvider: Signer or provider to connect to the L2 contracts.
 * @param opts.overrides Custom contract address overrides for L1 or L2 contracts.
 * @returns An object containing ethers.Contract objects connected to the appropriate addresses on
 * both L1 and L2.
 */
export const getAllOEContracts = (
  l1ChainId: number,
  opts: {
    l1SignerOrProvider?: ethers.Signer | ethers.providers.Provider
    l2SignerOrProvider?: ethers.Signer | ethers.providers.Provider
    overrides?: DeepPartial<OEContractsLike>
  } = {}
): OEContracts => {
  const addresses = CONTRACT_ADDRESSES[l1ChainId] || {
    l1: {
      AddressManager: undefined,
      L1CrossDomainMessenger: undefined,
      L1StandardBridge: undefined,
      StateCommitmentChain: undefined,
      CanonicalTransactionChain: undefined,
      BondManager: undefined,
    },
    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
  }

  // Attach all L1 contracts.
286
  const l1Contracts = {} as OEL1Contracts
287
  for (const [contractName, contractAddress] of Object.entries(addresses.l1)) {
288 289 290 291 292 293 294 295
    l1Contracts[contractName] = getOEContract(
      contractName as keyof OEL1Contracts,
      l1ChainId,
      {
        address: opts.overrides?.l1?.[contractName] || contractAddress,
        signerOrProvider: opts.l1SignerOrProvider,
      }
    )
296 297 298
  }

  // Attach all L2 contracts.
299
  const l2Contracts = {} as OEL2Contracts
300
  for (const [contractName, contractAddress] of Object.entries(addresses.l2)) {
301 302 303 304 305 306 307 308
    l2Contracts[contractName] = getOEContract(
      contractName as keyof OEL2Contracts,
      l1ChainId,
      {
        address: opts.overrides?.l2?.[contractName] || contractAddress,
        signerOrProvider: opts.l2SignerOrProvider,
      }
    )
309 310 311 312 313 314 315
  }

  return {
    l1: l1Contracts,
    l2: l2Contracts,
  }
}
316 317

/**
318
 * Gets a series of bridge adapters for the given L1 chain ID.
319 320
 *
 * @param l1ChainId L1 chain ID for the L1 network where the custom bridges are deployed.
321
 * @param messenger Cross chain messenger to connect to the bridge adapters
322
 * @param opts Additional options for connecting to the custom bridges.
323 324
 * @param opts.overrides Custom bridge adapters.
 * @returns An object containing all bridge adapters
325
 */
326
export const getBridgeAdapters = (
327
  l1ChainId: number,
328
  messenger: ICrossChainMessenger,
329 330
  opts?: {
    overrides?: BridgeAdapterData
331
  }
332 333 334 335 336 337 338
): BridgeAdapters => {
  const adapters: BridgeAdapters = {}
  for (const [bridgeName, bridgeData] of Object.entries({
    ...(BRIDGE_ADAPTER_DATA[l1ChainId] || {}),
    ...(opts?.overrides || {}),
  })) {
    adapters[bridgeName] = new bridgeData.Adapter({
339
      messenger,
340 341 342
      l1Bridge: bridgeData.l1Bridge,
      l2Bridge: bridgeData.l2Bridge,
    })
343 344
  }

345
  return adapters
346
}