utils.ts 5.36 KB
Newer Older
1 2
import { expect } from 'chai'

3 4
import { Direction, waitForXDomainTransaction } from './watcher-utils'

5 6 7
import {
  getContractFactory,
  getContractInterface,
8
  predeploys,
9
} from '@eth-optimism/contracts'
10
import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils'
11 12 13 14 15 16 17
import {
  Contract,
  Wallet,
  constants,
  providers,
  BigNumberish,
  BigNumber,
18
  utils,
19
} from 'ethers'
20
import { cleanEnv, str, num } from 'envalid'
21

22 23
export const GWEI = BigNumber.from(1e9)

24
const env = cleanEnv(process.env, {
25 26
  L1_URL: str({ default: 'http://localhost:9545' }),
  L2_URL: str({ default: 'http://localhost:8545' }),
27
  VERIFIER_URL: str({ default: 'http://localhost:8547' }),
28
  REPLICA_URL: str({ default: 'http://localhost:8549' }),
29 30
  L1_POLLING_INTERVAL: num({ default: 10 }),
  L2_POLLING_INTERVAL: num({ default: 10 }),
31
  VERIFIER_POLLING_INTERVAL: num({ default: 10 }),
32
  REPLICA_POLLING_INTERVAL: num({ default: 10 }),
33 34 35 36 37 38 39
  PRIVATE_KEY: str({
    default:
      '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
  }),
  ADDRESS_MANAGER: str({
    default: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  }),
40 41
})

42
// The hardhat instance
43 44 45 46 47
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
48

49 50 51
export const verifierProvider = new providers.JsonRpcProvider(env.VERIFIER_URL)
verifierProvider.pollingInterval = env.VERIFIER_POLLING_INTERVAL

52 53 54
export const replicaProvider = new providers.JsonRpcProvider(env.REPLICA_URL)
replicaProvider.pollingInterval = env.REPLICA_POLLING_INTERVAL

55
// The sequencer private key which is funded on L1
56
export const l1Wallet = new Wallet(env.PRIVATE_KEY, l1Provider)
57 58 59 60 61 62 63 64

// 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'
65
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
66 67 68 69

export const getAddressManager = (provider: any) => {
  return getContractFactory('Lib_AddressManager')
    .connect(provider)
70
    .attach(env.ADDRESS_MANAGER)
71
}
72

73 74 75 76 77
// Gets the bridge contract
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
  const l1BridgeInterface = getContractInterface('OVM_L1StandardBridge')
  const ProxyBridgeAddress = await AddressManager.getAddress(
    'Proxy__OVM_L1StandardBridge'
78
  )
79 80 81 82 83 84 85 86 87 88 89

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

  const OVM_L1StandardBridge = new Contract(
    ProxyBridgeAddress,
    l1BridgeInterface,
90 91
    wallet
  )
92 93 94 95 96
  return OVM_L1StandardBridge
}

export const getL2Bridge = async (wallet: Wallet) => {
  const L2BridgeInterface = getContractInterface('OVM_L2StandardBridge')
97

98 99 100 101 102 103
  const OVM_L2StandardBridge = new Contract(
    predeploys.OVM_L2StandardBridge,
    L2BridgeInterface,
    wallet
  )
  return OVM_L2StandardBridge
104 105 106 107 108 109 110 111 112 113 114 115 116 117
}

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,
118
  bridge: Contract,
119 120 121 122 123
  amount: BigNumberish,
  recipient?: string
) => {
  const value = BigNumber.from(amount)
  const tx = recipient
124 125
    ? bridge.depositETHTo(recipient, 1_300_000, '0x', { value })
    : bridge.depositETH(1_300_000, '0x', { value })
126

127 128 129 130
  await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2)
}

export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
131 132 133 134 135

const abiCoder = new utils.AbiCoder()
export const encodeSolidityRevertMessage = (_reason: string): string => {
  return '0x08c379a0' + remove0x(abiCoder.encode(['string'], [_reason]))
}
136 137 138 139 140 141 142 143

export const DEFAULT_TRANSACTION = {
  to: '0x' + '1234'.repeat(10),
  gasLimit: 33600000000001,
  gasPrice: 0,
  data: '0x',
  value: 0,
}
144

145 146 147 148 149
interface percentDeviationRange {
  upperPercentDeviation: number
  lowerPercentDeviation?: number
}

150 151 152
export const expectApprox = (
  actual: BigNumber | number,
  target: BigNumber | number,
153
  { upperPercentDeviation, lowerPercentDeviation = 100 }: percentDeviationRange
154 155 156 157 158
) => {
  actual = BigNumber.from(actual)
  target = BigNumber.from(target)

  const validDeviations =
159 160 161 162
    upperPercentDeviation >= 0 &&
    upperPercentDeviation <= 100 &&
    lowerPercentDeviation >= 0 &&
    lowerPercentDeviation <= 100
163 164 165 166 167
  if (!validDeviations) {
    throw new Error(
      'Upper and lower deviation percentage arguments should be between 0 and 100'
    )
  }
168 169
  const upper = target.mul(100 + upperPercentDeviation).div(100)
  const lower = target.mul(100 - lowerPercentDeviation).div(100)
170 171 172

  expect(
    actual.lte(upper),
173
    `Actual value is more than ${upperPercentDeviation}% greater than target`
174 175 176
  ).to.be.true
  expect(
    actual.gte(lower),
177
    `Actual value is more than ${lowerPercentDeviation}% less than target`
178 179
  ).to.be.true
}
180

181 182 183 184 185 186 187 188 189 190 191 192 193 194
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)
}