utils.ts 6.47 KB
Newer Older
1
/* Imports: External */
2 3
import { Wallet, providers, BigNumber, utils } from 'ethers'
import { predeploys } from '@eth-optimism/contracts'
4 5 6 7 8 9
import { remove0x } from '@eth-optimism/core-utils'
import {
  CrossChainMessenger,
  NumberLike,
  asL2Provider,
} from '@eth-optimism/sdk'
10
import { cleanEnv, str, num, bool, makeValidator } from 'envalid'
11
import dotenv from 'dotenv'
12
dotenv.config()
13 14

/* Imports: Internal */
15
import { OptimismEnv } from './env'
16

17 18 19 20
export const isLiveNetwork = () => {
  return process.env.IS_LIVE_NETWORK === 'true'
}

21 22 23 24 25 26 27 28 29
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
  }
30

31 32 33
  return num()._parse(gasPrice).toString()
})

34
const addressValidator = makeValidator((addr) => {
35 36 37 38 39 40 41
  if (!addr) {
    return ''
  } else if (utils.isAddress(addr)) {
    return addr
  } else {
    throw new Error('Expected an address')
  }
42 43
})

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

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

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

67 68
  VERIFIER_URL: str({ default: 'http://localhost:8547' }),

69 70
  HEALTHCHECK_URL: str({ default: 'http://localhost:7300/metrics' }),

71 72 73 74
  PRIVATE_KEY: str({
    default:
      '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
  }),
75 76 77 78
  GAS_PRICE_ORACLE_PRIVATE_KEY: str({
    default:
      '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
  }),
79 80 81 82 83 84 85 86 87 88 89 90 91 92

  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,
  }),
93 94 95
  RUN_HEALTHCHECK_TESTS: bool({
    default: true,
  }),
96 97 98 99 100 101
  RUN_DEBUG_TRACE_TESTS: bool({
    default: true,
  }),
  RUN_STRESS_TESTS: bool({
    default: true,
  }),
102 103 104
  RUN_VERIFIER_TESTS: bool({
    default: true,
  }),
105 106 107
  RUN_SYSTEM_ADDRESS_TESTS: bool({
    default: false,
  }),
108 109 110 111 112 113 114

  MOCHA_TIMEOUT: num({
    default: 120_000,
  }),
  MOCHA_BAIL: bool({
    default: false,
  }),
115 116 117
  BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: str({
    default: 'zlib',
  }),
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

  ADDRESS_MANAGER: addressValidator({
    default: '',
  }),
  L1_CROSS_DOMAIN_MESSENGER: addressValidator({
    default: '',
  }),
  L1_STANDARD_BRIDGE: addressValidator({
    default: '',
  }),
  STATE_COMMITMENT_CHAIN: addressValidator({
    default: '',
  }),
  CANONICAL_TRANSACTION_CHAIN: addressValidator({
    default: '',
  }),
  BOND_MANAGER: addressValidator({
    default: '',
  }),
137 138
})

139 140
export const envConfig = procEnv

141
// The hardhat instance
142 143
export const l1Provider = new providers.JsonRpcProvider(procEnv.L1_URL)
l1Provider.pollingInterval = procEnv.L1_POLLING_INTERVAL
144

145
export const l2Provider = asL2Provider(
146
  new providers.JsonRpcProvider(procEnv.L2_URL)
147
)
148
l2Provider.pollingInterval = procEnv.L2_POLLING_INTERVAL
149

150
export const replicaProvider = asL2Provider(
151
  new providers.JsonRpcProvider(procEnv.REPLICA_URL)
152
)
153
replicaProvider.pollingInterval = procEnv.REPLICA_POLLING_INTERVAL
154

155
export const verifierProvider = asL2Provider(
156 157 158 159
  new providers.JsonRpcProvider(procEnv.VERIFIER_URL)
)
verifierProvider.pollingInterval = procEnv.L2_POLLING_INTERVAL

160
// The sequencer private key which is funded on L1
161
export const l1Wallet = new Wallet(procEnv.PRIVATE_KEY, l1Provider)
162 163 164 165 166

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

167 168
// The owner of the GasPriceOracle on L2
export const gasPriceOracleWallet = new Wallet(
169
  procEnv.GAS_PRICE_ORACLE_PRIVATE_KEY,
170 171 172
  l2Provider
)

173
// Predeploys
174
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
175

176
export const L2_CHAINID = procEnv.L2_CHAINID
177

178
export const fundUser = async (
179 180
  messenger: CrossChainMessenger,
  amount: NumberLike,
181 182
  recipient?: string
) => {
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
  await messenger.waitForMessageReceipt(
    await messenger.depositETH(amount, {
      l2GasLimit: DEFAULT_TEST_GAS_L2,
      overrides: {
        gasPrice: DEFAULT_TEST_GAS_L1,
      },
    })
  )

  if (recipient !== undefined) {
    const tx = await messenger.l2Signer.sendTransaction({
      to: recipient,
      value: amount,
    })
    await tx.wait()
  }
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
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()
217
  }).timeout(timeout || envConfig.MOCHA_TIMEOUT * 2)
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
}

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.'
  )

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

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

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

257
  return utils.parseUnits(procEnv.L2_GAS_PRICE, 'wei')
258 259
}

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

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

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

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

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