utils.ts 6.79 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
import {
  getContractFactory,
  getContractInterface,
  predeploys,
} from '@eth-optimism/contracts'
import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils'
17
import { cleanEnv, str, num, bool, makeValidator } from 'envalid'
18
import dotenv from 'dotenv'
19
dotenv.config()
20 21 22

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

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

29 30 31 32 33 34 35 36 37
export const HARDHAT_CHAIN_ID = 31337
export const DEFAULT_TEST_GAS_L1 = 330_000
export const DEFAULT_TEST_GAS_L2 = 1_300_000
export const ON_CHAIN_GAS_PRICE = 'onchain'

const gasPriceValidator = makeValidator((gasPrice) => {
  if (gasPrice === 'onchain') {
    return gasPrice
  }
38

39 40 41 42 43 44 45
  return num()._parse(gasPrice).toString()
})

const procEnv = cleanEnv(process.env, {
  L1_GAS_PRICE: gasPriceValidator({
    default: '0',
  }),
46
  L1_URL: str({ default: 'http://localhost:9545' }),
47
  L1_POLLING_INTERVAL: num({ default: 10 }),
48 49 50 51 52 53

  L2_CHAINID: num({ default: 420 }),
  L2_GAS_PRICE: gasPriceValidator({
    default: 'onchain',
  }),
  L2_URL: str({ default: 'http://localhost:8545' }),
54
  L2_POLLING_INTERVAL: num({ default: 10 }),
55 56 57 58 59 60 61 62
  L2_WALLET_MIN_BALANCE_ETH: num({
    default: 2,
  }),
  L2_WALLET_TOP_UP_AMOUNT_ETH: num({
    default: 3,
  }),

  REPLICA_URL: str({ default: 'http://localhost:8549' }),
63
  REPLICA_POLLING_INTERVAL: num({ default: 10 }),
64

65 66 67 68 69 70 71
  PRIVATE_KEY: str({
    default:
      '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
  }),
  ADDRESS_MANAGER: str({
    default: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  }),
72 73 74 75
  GAS_PRICE_ORACLE_PRIVATE_KEY: str({
    default:
      '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
  }),
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

  OVMCONTEXT_SPEC_NUM_TXS: num({
    default: 5,
  }),
  DTL_ENQUEUE_CONFIRMATIONS: num({
    default: 0,
  }),

  RUN_WITHDRAWAL_TESTS: bool({
    default: true,
  }),
  RUN_REPLICA_TESTS: bool({
    default: true,
  }),
  RUN_DEBUG_TRACE_TESTS: bool({
    default: true,
  }),
  RUN_STRESS_TESTS: bool({
    default: true,
  }),
96 97 98
  RUN_NIGHTLY_TESTS: bool({
    default: false,
  }),
99 100 101 102 103 104 105

  MOCHA_TIMEOUT: num({
    default: 120_000,
  }),
  MOCHA_BAIL: bool({
    default: false,
  }),
106 107
})

108 109
export const envConfig = procEnv

110
// The hardhat instance
111 112
export const l1Provider = new providers.JsonRpcProvider(procEnv.L1_URL)
l1Provider.pollingInterval = procEnv.L1_POLLING_INTERVAL
113

114
export const l2Provider = injectL2Context(
115
  new providers.JsonRpcProvider(procEnv.L2_URL)
116
)
117
l2Provider.pollingInterval = procEnv.L2_POLLING_INTERVAL
118

119
export const replicaProvider = injectL2Context(
120
  new providers.JsonRpcProvider(procEnv.REPLICA_URL)
121
)
122
replicaProvider.pollingInterval = procEnv.REPLICA_POLLING_INTERVAL
123

124
// The sequencer private key which is funded on L1
125
export const l1Wallet = new Wallet(procEnv.PRIVATE_KEY, l1Provider)
126 127 128 129 130

// 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)

131 132
// The owner of the GasPriceOracle on L2
export const gasPriceOracleWallet = new Wallet(
133
  procEnv.GAS_PRICE_ORACLE_PRIVATE_KEY,
134 135 136
  l2Provider
)

137 138 139
// Predeploys
export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS =
  '0x4200000000000000000000000000000000000004'
140
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
141

142
export const L2_CHAINID = procEnv.L2_CHAINID
143

144 145 146
export const getAddressManager = (provider: any) => {
  return getContractFactory('Lib_AddressManager')
    .connect(provider)
147
    .attach(procEnv.ADDRESS_MANAGER)
148
}
149

150 151
// Gets the bridge contract
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
152
  const l1BridgeInterface = getContractInterface('L1StandardBridge')
153
  const ProxyBridgeAddress = await AddressManager.getAddress(
154
    'Proxy__OVM_L1StandardBridge'
155
  )
156 157 158 159 160

  if (
    !utils.isAddress(ProxyBridgeAddress) ||
    ProxyBridgeAddress === constants.AddressZero
  ) {
161
    throw new Error('Proxy__OVM_L1StandardBridge not found')
162 163
  }

164
  return new Contract(ProxyBridgeAddress, l1BridgeInterface, wallet)
165 166 167
}

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

170
  return new Contract(predeploys.L2StandardBridge, L2BridgeInterface, wallet)
171 172 173
}

export const getOvmEth = (wallet: Wallet) => {
174
  return new Contract(OVM_ETH_ADDRESS, getContractInterface('OVM_ETH'), wallet)
175 176 177 178
}

export const fundUser = async (
  watcher: Watcher,
179
  bridge: Contract,
180 181 182 183 184
  amount: BigNumberish,
  recipient?: string
) => {
  const value = BigNumber.from(amount)
  const tx = recipient
185 186 187 188 189 190 191 192
    ? bridge.depositETHTo(recipient, DEFAULT_TEST_GAS_L2, '0x', {
        value,
        gasLimit: DEFAULT_TEST_GAS_L1,
      })
    : bridge.depositETH(DEFAULT_TEST_GAS_L2, '0x', {
        value,
        gasLimit: DEFAULT_TEST_GAS_L1,
      })
193

194 195 196
  await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2)
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
export const conditionalTest = (
  condition: (env?: OptimismEnv) => Promise<boolean>,
  name,
  fn,
  message?: string,
  timeout?: number
) => {
  it(name, async function () {
    const shouldRun = await condition()
    if (!shouldRun) {
      console.log(message)
      this.skip()
      return
    }

    await fn()
  }).timeout(timeout || envConfig.MOCHA_TIMEOUT)
}

export const withdrawalTest = (name, fn, timeout?: number) =>
  conditionalTest(
    () => Promise.resolve(procEnv.RUN_WITHDRAWAL_TESTS),
    name,
    fn,
    `Skipping withdrawal test.`,
    timeout
  )

export const hardhatTest = (name, fn) =>
  conditionalTest(
    isHardhat,
    name,
    fn,
    'Skipping test on non-Hardhat environment.'
  )

233
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
234 235 236 237 238

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

240 241 242 243 244 245 246 247
export const defaultTransactionFactory = () => {
  return {
    to: '0x' + '1234'.repeat(10),
    gasLimit: 8_000_000,
    gasPrice: BigNumber.from(0),
    data: '0x',
    value: 0,
  }
248
}
249

250 251 252
export const gasPriceForL2 = async () => {
  if (procEnv.L2_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
    return l2Wallet.getGasPrice()
Matthew Slipper's avatar
Matthew Slipper committed
253 254
  }

255
  return utils.parseUnits(procEnv.L2_GAS_PRICE, 'wei')
256 257
}

258 259 260
export const gasPriceForL1 = async () => {
  if (procEnv.L1_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
    return l1Wallet.getGasPrice()
261
  }
262 263

  return utils.parseUnits(procEnv.L1_GAS_PRICE, 'wei')
264
}
Matthew Slipper's avatar
Matthew Slipper committed
265

266 267 268
export const isHardhat = async () => {
  const chainId = await l1Wallet.getChainId()
  return chainId === HARDHAT_CHAIN_ID
Matthew Slipper's avatar
Matthew Slipper committed
269
}
270 271 272 273 274 275 276 277 278

export const die = (...args) => {
  console.log(...args)
  process.exit(1)
}

export const logStderr = (msg: string) => {
  process.stderr.write(`${msg}\n`)
}