utils.ts 5.59 KB
Newer Older
1
/* Imports: External */
2 3 4 5 6 7 8
import {
  Contract,
  Wallet,
  constants,
  providers,
  BigNumberish,
  BigNumber,
9
  utils,
10
} from 'ethers'
11 12 13 14 15 16 17 18 19 20 21
import {
  getContractFactory,
  getContractInterface,
  predeploys,
} from '@eth-optimism/contracts'
import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils'
import { cleanEnv, str, num, bool } from 'envalid'
import dotenv from 'dotenv'

/* Imports: Internal */
import { Direction, waitForXDomainTransaction } from './watcher-utils'
22
import { OptimismEnv } from './env'
23

24 25
export const GWEI = BigNumber.from(1e9)

26 27 28 29 30
export const isLiveNetwork = () => {
  return process.env.IS_LIVE_NETWORK === 'true'
}

if (isLiveNetwork()) {
31 32 33
  dotenv.config()
}

34
const env = cleanEnv(process.env, {
35 36
  L1_URL: str({ default: 'http://localhost:9545' }),
  L2_URL: str({ default: 'http://localhost:8545' }),
37
  VERIFIER_URL: str({ default: 'http://localhost:8547' }),
38
  REPLICA_URL: str({ default: 'http://localhost:8549' }),
39 40
  L1_POLLING_INTERVAL: num({ default: 10 }),
  L2_POLLING_INTERVAL: num({ default: 10 }),
41
  VERIFIER_POLLING_INTERVAL: num({ default: 10 }),
42
  REPLICA_POLLING_INTERVAL: num({ default: 10 }),
43 44 45 46 47 48 49
  PRIVATE_KEY: str({
    default:
      '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
  }),
  ADDRESS_MANAGER: str({
    default: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  }),
50 51
  L2_CHAINID: num({ default: 420 }),
  IS_LIVE_NETWORK: bool({ default: false }),
52 53
})

54
// The hardhat instance
55 56 57 58 59
export const l1Provider = new providers.JsonRpcProvider(env.L1_URL)
l1Provider.pollingInterval = env.L1_POLLING_INTERVAL

export const l2Provider = new providers.JsonRpcProvider(env.L2_URL)
l2Provider.pollingInterval = env.L2_POLLING_INTERVAL
60

61 62 63
export const verifierProvider = new providers.JsonRpcProvider(env.VERIFIER_URL)
verifierProvider.pollingInterval = env.VERIFIER_POLLING_INTERVAL

64 65 66
export const replicaProvider = new providers.JsonRpcProvider(env.REPLICA_URL)
replicaProvider.pollingInterval = env.REPLICA_POLLING_INTERVAL

67
// The sequencer private key which is funded on L1
68
export const l1Wallet = new Wallet(env.PRIVATE_KEY, l1Provider)
69 70 71 72 73 74 75 76

// A random private key which should always be funded with deposits from L1 -> L2
// if it's using non-0 gas price
export const l2Wallet = l1Wallet.connect(l2Provider)

// Predeploys
export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS =
  '0x4200000000000000000000000000000000000004'
77
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
78

79 80 81
export const L2_CHAINID = env.L2_CHAINID
export const IS_LIVE_NETWORK = env.IS_LIVE_NETWORK

82 83 84
export const getAddressManager = (provider: any) => {
  return getContractFactory('Lib_AddressManager')
    .connect(provider)
85
    .attach(env.ADDRESS_MANAGER)
86
}
87

88 89
// Gets the bridge contract
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
90
  const l1BridgeInterface = getContractInterface('L1StandardBridge')
91
  const ProxyBridgeAddress = await AddressManager.getAddress(
92
    'Proxy__OVM_L1StandardBridge'
93
  )
94 95 96 97 98

  if (
    !utils.isAddress(ProxyBridgeAddress) ||
    ProxyBridgeAddress === constants.AddressZero
  ) {
99
    throw new Error('Proxy__OVM_L1StandardBridge not found')
100 101
  }

102
  const L1StandardBridge = new Contract(
103 104
    ProxyBridgeAddress,
    l1BridgeInterface,
105 106
    wallet
  )
107
  return L1StandardBridge
108 109 110
}

export const getL2Bridge = async (wallet: Wallet) => {
111
  const L2BridgeInterface = getContractInterface('L2StandardBridge')
112

113 114
  const L2StandardBridge = new Contract(
    predeploys.L2StandardBridge,
115 116 117
    L2BridgeInterface,
    wallet
  )
118
  return L2StandardBridge
119 120 121 122 123 124 125 126 127 128 129 130 131 132
}

export const getOvmEth = (wallet: Wallet) => {
  const OVM_ETH = new Contract(
    OVM_ETH_ADDRESS,
    getContractInterface('OVM_ETH'),
    wallet
  )

  return OVM_ETH
}

export const fundUser = async (
  watcher: Watcher,
133
  bridge: Contract,
134 135 136 137 138
  amount: BigNumberish,
  recipient?: string
) => {
  const value = BigNumber.from(amount)
  const tx = recipient
139 140
    ? bridge.depositETHTo(recipient, 1_300_000, '0x', { value })
    : bridge.depositETH(1_300_000, '0x', { value })
141

142 143 144 145
  await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2)
}

export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
146 147 148 149 150

const abiCoder = new utils.AbiCoder()
export const encodeSolidityRevertMessage = (_reason: string): string => {
  return '0x08c379a0' + remove0x(abiCoder.encode(['string'], [_reason]))
}
151

152 153 154 155 156 157 158 159
export const defaultTransactionFactory = () => {
  return {
    to: '0x' + '1234'.repeat(10),
    gasLimit: 8_000_000,
    gasPrice: BigNumber.from(0),
    data: '0x',
    value: 0,
  }
160
}
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175
export const waitForL2Geth = async (
  provider: providers.JsonRpcProvider
): Promise<providers.JsonRpcProvider> => {
  let ready: boolean = false
  while (!ready) {
    try {
      await provider.getNetwork()
      ready = true
    } catch (error) {
      await sleep(1000)
    }
  }
  return injectL2Context(provider)
}
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

export const awaitCondition = async (
  cond: () => Promise<boolean>,
  rate = 1000,
  attempts = 10
) => {
  for (let i = 0; i < attempts; i++) {
    const ok = await cond()
    if (ok) {
      return
    }

    await sleep(rate)
  }
  throw new Error('Timed out.')
}

export const gasPriceForL2 = async () => {
  if (isLiveNetwork()) {
    return Promise.resolve(BigNumber.from(10000))
  }

  return Promise.resolve(BigNumber.from(0))
}

// eslint-disable-next-line @typescript-eslint/no-shadow
export const gasPriceForL1 = async (env: OptimismEnv) => {
  const chainId = await env.l1Wallet.getChainId()

  switch (chainId) {
    case 1:
      return env.l1Wallet.getGasPrice()
    case 3:
    case 42:
      return utils.parseUnits('10', 'gwei')
    case 5:
      return utils.parseUnits('2', 'gwei')
    default:
      return BigNumber.from(0)
  }
}