env.ts 5.42 KB
Newer Older
1
/* Imports: External */
2
import { utils, Wallet, providers, Transaction } from 'ethers'
3 4 5 6
import {
  TransactionResponse,
  TransactionReceipt,
} from '@ethersproject/providers'
7
import { getChainId, sleep } from '@eth-optimism/core-utils'
8 9 10 11
import {
  CrossChainMessenger,
  MessageStatus,
  MessageDirection,
12 13 14
  StandardBridgeAdapter,
  ETHBridgeAdapter,
  BridgeAdapterData,
15
} from '@eth-optimism/sdk'
16
import { predeploys } from '@eth-optimism/contracts'
17 18

/* Imports: Internal */
19 20 21
import {
  l1Provider,
  l2Provider,
22
  replicaProvider,
23
  verifierProvider,
24 25 26
  l1Wallet,
  l2Wallet,
  fundUser,
27
  envConfig,
28
} from './utils'
29 30 31 32 33 34 35

export interface CrossDomainMessagePair {
  tx: Transaction
  receipt: TransactionReceipt
  remoteTx: Transaction
  remoteReceipt: TransactionReceipt
}
36 37 38 39 40 41 42

/// Helper class for instantiating a test environment with a funded account
export class OptimismEnv {
  // The wallets
  l1Wallet: Wallet
  l2Wallet: Wallet

43
  // The providers
44
  messenger: CrossChainMessenger
45 46
  l1Provider: providers.JsonRpcProvider
  l2Provider: providers.JsonRpcProvider
47
  replicaProvider: providers.JsonRpcProvider
48
  verifierProvider: providers.JsonRpcProvider
49

50 51 52
  constructor(args: any) {
    this.l1Wallet = args.l1Wallet
    this.l2Wallet = args.l2Wallet
53
    this.messenger = args.messenger
54 55
    this.l1Provider = args.l1Provider
    this.l2Provider = args.l2Provider
56
    this.replicaProvider = args.replicaProvider
57
    this.verifierProvider = args.verifierProvider
58 59 60
  }

  static async new(): Promise<OptimismEnv> {
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    let bridgeOverrides: BridgeAdapterData
    if (envConfig.L1_STANDARD_BRIDGE) {
      bridgeOverrides = {
        Standard: {
          Adapter: StandardBridgeAdapter,
          l1Bridge: envConfig.L1_STANDARD_BRIDGE,
          l2Bridge: predeploys.L2StandardBridge,
        },
        ETH: {
          Adapter: ETHBridgeAdapter,
          l1Bridge: envConfig.L1_STANDARD_BRIDGE,
          l2Bridge: predeploys.L2StandardBridge,
        },
      }
    }

77 78 79
    const messenger = new CrossChainMessenger({
      l1SignerOrProvider: l1Wallet,
      l2SignerOrProvider: l2Wallet,
80 81
      l1ChainId: await getChainId(l1Provider),
      l2ChainId: await getChainId(l2Provider),
82 83 84 85 86 87 88 89
      contracts: {
        l1: {
          AddressManager: envConfig.ADDRESS_MANAGER,
          L1CrossDomainMessenger: envConfig.L1_CROSS_DOMAIN_MESSENGER,
          L1StandardBridge: envConfig.L1_STANDARD_BRIDGE,
          StateCommitmentChain: envConfig.STATE_COMMITMENT_CHAIN,
          CanonicalTransactionChain: envConfig.CANONICAL_TRANSACTION_CHAIN,
          BondManager: envConfig.BOND_MANAGER,
90 91 92
        },
      },
      bridges: bridgeOverrides,
93 94 95 96 97 98 99 100 101 102
    })

    // fund the user if needed
    const balance = await l2Wallet.getBalance()
    const min = envConfig.L2_WALLET_MIN_BALANCE_ETH.toString()
    const topUp = envConfig.L2_WALLET_TOP_UP_AMOUNT_ETH.toString()
    if (balance.lt(utils.parseEther(min))) {
      await fundUser(messenger, utils.parseEther(topUp))
    }

103 104 105
    return new OptimismEnv({
      l1Wallet,
      l2Wallet,
106
      messenger,
107 108
      l1Provider,
      l2Provider,
109
      verifierProvider,
110
      replicaProvider,
111 112 113 114
    })
  }

  async waitForXDomainTransaction(
115
    tx: Promise<TransactionResponse> | TransactionResponse
116
  ): Promise<CrossDomainMessagePair> {
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    // await it if needed
    tx = await tx

    const receipt = await tx.wait()
    const resolved = await this.messenger.toCrossChainMessage(tx)
    const messageReceipt = await this.messenger.waitForMessageReceipt(tx)
    let fullTx: any
    let remoteTx: any
    if (resolved.direction === MessageDirection.L1_TO_L2) {
      fullTx = await this.messenger.l1Provider.getTransaction(tx.hash)
      remoteTx = await this.messenger.l2Provider.getTransaction(
        messageReceipt.transactionReceipt.transactionHash
      )
    } else {
      fullTx = await this.messenger.l2Provider.getTransaction(tx.hash)
      remoteTx = await this.messenger.l1Provider.getTransaction(
        messageReceipt.transactionReceipt.transactionHash
      )
    }

    return {
      tx: fullTx,
      receipt,
      remoteTx,
      remoteReceipt: messageReceipt.transactionReceipt,
    }
143
  }
144 145 146 147 148 149 150 151 152 153

  /**
   * Relays all L2 => L1 messages found in a given L2 transaction.
   *
   * @param tx Transaction to find messages in.
   */
  async relayXDomainMessages(
    tx: Promise<TransactionResponse> | TransactionResponse
  ): Promise<void> {
    tx = await tx
154
    await tx.wait()
155

156 157 158
    const messages = await this.messenger.getMessagesByTransaction(tx)
    if (messages.length === 0) {
      return
159 160
    }

161
    for (const message of messages) {
162 163 164 165
      await this.messenger.waitForMessageStatus(
        message,
        MessageStatus.READY_FOR_RELAY
      )
166 167 168

      let relayed = false
      while (!relayed) {
169
        try {
170 171
          await this.messenger.finalizeMessage(message)
          relayed = true
172
        } catch (err) {
173 174 175
          if (
            err.message.includes('Nonce too low') ||
            err.message.includes('transaction was replaced') ||
176 177 178 179
            err.message.includes(
              'another transaction with same nonce in the queue'
            )
          ) {
180
            // Sometimes happens when we run tests in parallel.
181
            await sleep(5000)
182 183 184
          } else if (
            err.message.includes('message has already been received')
          ) {
185 186
            // Message already relayed, this is fine.
            relayed = true
187 188 189 190 191
          } else {
            throw err
          }
        }
      }
192 193

      await this.messenger.waitForMessageReceipt(message)
194 195 196
    }
  }
}