github.com/ethereum-optimism/optimism@v1.7.2/packages/sdk/test/cross-chain-messenger.spec.ts (about)

     1  import { Provider } from '@ethersproject/abstract-provider'
     2  import { expectApprox, hashCrossDomainMessage } from '@eth-optimism/core-utils'
     3  import { predeploys } from '@eth-optimism/contracts'
     4  import { Contract } from 'ethers'
     5  import { ethers } from 'hardhat'
     6  
     7  import { expect } from './setup'
     8  import {
     9    MessageDirection,
    10    CONTRACT_ADDRESSES,
    11    omit,
    12    MessageStatus,
    13    CrossChainMessage,
    14    CrossChainMessenger,
    15    StandardBridgeAdapter,
    16    ETHBridgeAdapter,
    17    L1ChainID,
    18    L2ChainID,
    19    IGNORABLE_CONTRACTS,
    20  } from '../src'
    21  import { DUMMY_MESSAGE, DUMMY_EXTENDED_MESSAGE } from './helpers'
    22  
    23  describe('CrossChainMessenger', () => {
    24    let l1Signer: any
    25    let l2Signer: any
    26    before(async () => {
    27      ;[l1Signer, l2Signer] = await ethers.getSigners()
    28    })
    29  
    30    describe('construction', () => {
    31      describe('when given an ethers provider for the L1 provider', () => {
    32        it('should use the provider as the L1 provider', () => {
    33          const messenger = new CrossChainMessenger({
    34            bedrock: false,
    35            l1SignerOrProvider: ethers.provider,
    36            l2SignerOrProvider: ethers.provider,
    37            l1ChainId: L1ChainID.MAINNET,
    38            l2ChainId: L2ChainID.OPTIMISM,
    39          })
    40  
    41          expect(messenger.l1Provider).to.equal(ethers.provider)
    42        })
    43      })
    44  
    45      describe('when given an ethers provider for the L2 provider', () => {
    46        it('should use the provider as the L2 provider', () => {
    47          const messenger = new CrossChainMessenger({
    48            bedrock: false,
    49            l1SignerOrProvider: ethers.provider,
    50            l2SignerOrProvider: ethers.provider,
    51            l1ChainId: L1ChainID.MAINNET,
    52            l2ChainId: L2ChainID.OPTIMISM,
    53          })
    54  
    55          expect(messenger.l2Provider).to.equal(ethers.provider)
    56        })
    57      })
    58  
    59      describe('when given a string as the L1 provider', () => {
    60        it('should create a JSON-RPC provider for the L1 provider', () => {
    61          const messenger = new CrossChainMessenger({
    62            bedrock: false,
    63            l1SignerOrProvider: 'https://localhost:8545',
    64            l2SignerOrProvider: ethers.provider,
    65            l1ChainId: L1ChainID.MAINNET,
    66            l2ChainId: L2ChainID.OPTIMISM,
    67          })
    68  
    69          expect(Provider.isProvider(messenger.l1Provider)).to.be.true
    70        })
    71      })
    72  
    73      describe('when given a string as the L2 provider', () => {
    74        it('should create a JSON-RPC provider for the L2 provider', () => {
    75          const messenger = new CrossChainMessenger({
    76            bedrock: false,
    77            l1SignerOrProvider: ethers.provider,
    78            l2SignerOrProvider: 'https://localhost:8545',
    79            l1ChainId: L1ChainID.MAINNET,
    80            l2ChainId: L2ChainID.OPTIMISM,
    81          })
    82  
    83          expect(Provider.isProvider(messenger.l2Provider)).to.be.true
    84        })
    85      })
    86  
    87      describe('when given a bad L1 chain ID', () => {
    88        it('should throw an error', () => {
    89          expect(() => {
    90            new CrossChainMessenger({
    91              bedrock: false,
    92              l1SignerOrProvider: ethers.provider,
    93              l2SignerOrProvider: ethers.provider,
    94              l1ChainId: undefined as any,
    95              l2ChainId: L2ChainID.OPTIMISM,
    96            })
    97          }).to.throw('L1 chain ID is missing or invalid')
    98        })
    99      })
   100  
   101      describe('when given a bad L2 chain ID', () => {
   102        it('should throw an error', () => {
   103          expect(() => {
   104            new CrossChainMessenger({
   105              bedrock: false,
   106              l1SignerOrProvider: ethers.provider,
   107              l2SignerOrProvider: ethers.provider,
   108              l1ChainId: L1ChainID.MAINNET,
   109              l2ChainId: undefined as any,
   110            })
   111          }).to.throw('L2 chain ID is missing or invalid')
   112        })
   113      })
   114  
   115      describe('when no custom contract addresses are provided', () => {
   116        describe('when given a known chain ID', () => {
   117          it('should use the contract addresses for the known chain ID', () => {
   118            const messenger = new CrossChainMessenger({
   119              bedrock: false,
   120              l1SignerOrProvider: ethers.provider,
   121              l2SignerOrProvider: 'https://localhost:8545',
   122              l1ChainId: L1ChainID.MAINNET,
   123              l2ChainId: L2ChainID.OPTIMISM,
   124            })
   125  
   126            const addresses = CONTRACT_ADDRESSES[messenger.l2ChainId]
   127            for (const [contractName, contractAddress] of Object.entries(
   128              addresses.l1
   129            )) {
   130              const contract = messenger.contracts.l1[contractName]
   131              expect(contract.address).to.equal(contractAddress)
   132            }
   133            for (const [contractName, contractAddress] of Object.entries(
   134              addresses.l2
   135            )) {
   136              const contract = messenger.contracts.l2[contractName]
   137              expect(contract.address).to.equal(contractAddress)
   138            }
   139          })
   140        })
   141  
   142        describe('when given an unknown L2 chain ID', () => {
   143          it('should throw an error', () => {
   144            expect(() => {
   145              new CrossChainMessenger({
   146                bedrock: false,
   147                l1SignerOrProvider: ethers.provider,
   148                l2SignerOrProvider: 'https://localhost:8545',
   149                l1ChainId: L1ChainID.MAINNET,
   150                l2ChainId: 1234,
   151              })
   152            }).to.throw()
   153          })
   154        })
   155      })
   156  
   157      describe('when custom contract addresses are provided', () => {
   158        describe('when given a known chain ID', () => {
   159          it('should use known addresses except where custom addresses are given', () => {
   160            const overrides = {
   161              l1: {
   162                L1CrossDomainMessenger: '0x' + '11'.repeat(20),
   163              },
   164              l2: {
   165                L2CrossDomainMessenger: '0x' + '22'.repeat(20),
   166              },
   167            }
   168            const messenger = new CrossChainMessenger({
   169              bedrock: false,
   170              l1SignerOrProvider: ethers.provider,
   171              l2SignerOrProvider: 'https://localhost:8545',
   172              l1ChainId: L1ChainID.MAINNET,
   173              l2ChainId: L2ChainID.OPTIMISM,
   174              contracts: overrides,
   175            })
   176  
   177            const addresses = CONTRACT_ADDRESSES[messenger.l2ChainId]
   178            for (const [contractName, contractAddress] of Object.entries(
   179              addresses.l1
   180            )) {
   181              if (overrides.l1[contractName]) {
   182                const contract = messenger.contracts.l1[contractName]
   183                expect(contract.address).to.equal(overrides.l1[contractName])
   184              } else {
   185                const contract = messenger.contracts.l1[contractName]
   186                expect(contract.address).to.equal(contractAddress)
   187              }
   188            }
   189            for (const [contractName, contractAddress] of Object.entries(
   190              addresses.l2
   191            )) {
   192              if (overrides.l2[contractName]) {
   193                const contract = messenger.contracts.l2[contractName]
   194                expect(contract.address).to.equal(overrides.l2[contractName])
   195              } else {
   196                const contract = messenger.contracts.l2[contractName]
   197                expect(contract.address).to.equal(contractAddress)
   198              }
   199            }
   200          })
   201        })
   202  
   203        describe('when given an unknown L2 chain ID', () => {
   204          describe('when all L1 addresses are provided', () => {
   205            it('should use custom addresses where provided', () => {
   206              const overrides = {
   207                l1: {
   208                  AddressManager: '0x' + '11'.repeat(20),
   209                  L1CrossDomainMessenger: '0x' + '12'.repeat(20),
   210                  L1StandardBridge: '0x' + '13'.repeat(20),
   211                  StateCommitmentChain: '0x' + '14'.repeat(20),
   212                  CanonicalTransactionChain: '0x' + '15'.repeat(20),
   213                  BondManager: '0x' + '16'.repeat(20),
   214                  OptimismPortal: '0x' + '17'.repeat(20),
   215                  L2OutputOracle: '0x' + '18'.repeat(20),
   216                },
   217                l2: {
   218                  L2CrossDomainMessenger: '0x' + '22'.repeat(20),
   219                },
   220              }
   221  
   222              const messenger = new CrossChainMessenger({
   223                bedrock: false,
   224                l1SignerOrProvider: ethers.provider,
   225                l2SignerOrProvider: 'https://localhost:8545',
   226                l1ChainId: L1ChainID.MAINNET,
   227                l2ChainId: 1234,
   228                contracts: overrides,
   229              })
   230  
   231              const addresses = CONTRACT_ADDRESSES[L2ChainID.OPTIMISM]
   232              for (const [contractName, contractAddress] of Object.entries(
   233                addresses.l1
   234              )) {
   235                if (overrides.l1[contractName]) {
   236                  const contract = messenger.contracts.l1[contractName]
   237                  expect(contract.address).to.equal(overrides.l1[contractName])
   238                } else if (!IGNORABLE_CONTRACTS.includes(contractName)) {
   239                  const contract = messenger.contracts.l1[contractName]
   240                  expect(contract.address).to.equal(contractAddress)
   241                }
   242              }
   243              for (const [contractName, contractAddress] of Object.entries(
   244                addresses.l2
   245              )) {
   246                if (overrides.l2[contractName]) {
   247                  const contract = messenger.contracts.l2[contractName]
   248                  expect(contract.address).to.equal(overrides.l2[contractName])
   249                } else {
   250                  const contract = messenger.contracts.l2[contractName]
   251                  expect(contract.address).to.equal(contractAddress)
   252                }
   253              }
   254            })
   255          })
   256  
   257          describe('when not all L1 addresses are provided', () => {
   258            it('should throw an error', () => {
   259              expect(() => {
   260                new CrossChainMessenger({
   261                  bedrock: false,
   262                  l1SignerOrProvider: ethers.provider,
   263                  l2SignerOrProvider: 'https://localhost:8545',
   264                  l1ChainId: L1ChainID.MAINNET,
   265                  l2ChainId: 1234,
   266                  contracts: {
   267                    l1: {
   268                      // Missing some required L1 addresses
   269                      AddressManager: '0x' + '11'.repeat(20),
   270                      L1CrossDomainMessenger: '0x' + '12'.repeat(20),
   271                      L1StandardBridge: '0x' + '13'.repeat(20),
   272                    },
   273                    l2: {
   274                      L2CrossDomainMessenger: '0x' + '22'.repeat(20),
   275                    },
   276                  },
   277                })
   278              }).to.throw()
   279            })
   280          })
   281        })
   282      })
   283    })
   284  
   285    describe('getMessagesByTransaction', () => {
   286      let l1Messenger: Contract
   287      let l2Messenger: Contract
   288      let messenger: CrossChainMessenger
   289      beforeEach(async () => {
   290        l1Messenger = (await (
   291          await ethers.getContractFactory('MockMessenger')
   292        ).deploy()) as any
   293        l2Messenger = (await (
   294          await ethers.getContractFactory('MockMessenger')
   295        ).deploy()) as any
   296  
   297        messenger = new CrossChainMessenger({
   298          bedrock: false,
   299          l1SignerOrProvider: ethers.provider,
   300          l2SignerOrProvider: ethers.provider,
   301          l1ChainId: L1ChainID.HARDHAT_LOCAL,
   302          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
   303          contracts: {
   304            l1: {
   305              L1CrossDomainMessenger: l1Messenger.address,
   306            },
   307            l2: {
   308              L2CrossDomainMessenger: l2Messenger.address,
   309            },
   310          },
   311        })
   312      })
   313  
   314      describe('when a direction is specified', () => {
   315        describe('when the transaction exists', () => {
   316          describe('when the transaction has messages', () => {
   317            for (const n of [1, 2, 4, 8]) {
   318              it(`should find ${n} messages when the transaction emits ${n} messages`, async () => {
   319                const messages = [...Array(n)].map(() => {
   320                  return DUMMY_MESSAGE
   321                })
   322  
   323                const tx = await l1Messenger.triggerSentMessageEvents(messages)
   324                const found = await messenger.getMessagesByTransaction(tx, {
   325                  direction: MessageDirection.L1_TO_L2,
   326                })
   327                expect(found).to.deep.equal(
   328                  messages.map((message, i) => {
   329                    return {
   330                      direction: MessageDirection.L1_TO_L2,
   331                      sender: message.sender,
   332                      target: message.target,
   333                      message: message.message,
   334                      messageNonce: ethers.BigNumber.from(message.messageNonce),
   335                      minGasLimit: ethers.BigNumber.from(message.minGasLimit),
   336                      value: ethers.BigNumber.from(message.value),
   337                      logIndex: i,
   338                      blockNumber: tx.blockNumber,
   339                      transactionHash: tx.hash,
   340                    }
   341                  })
   342                )
   343              })
   344            }
   345          })
   346  
   347          describe('when the transaction has no messages', () => {
   348            it('should find nothing', async () => {
   349              const tx = await l1Messenger.doNothing()
   350              const found = await messenger.getMessagesByTransaction(tx, {
   351                direction: MessageDirection.L1_TO_L2,
   352              })
   353              expect(found).to.deep.equal([])
   354            })
   355          })
   356        })
   357  
   358        describe('when the transaction does not exist in the specified direction', () => {
   359          it('should throw an error', async () => {
   360            await expect(
   361              messenger.getMessagesByTransaction('0x' + '11'.repeat(32), {
   362                direction: MessageDirection.L1_TO_L2,
   363              })
   364            ).to.be.rejectedWith('unable to find transaction receipt')
   365          })
   366        })
   367      })
   368  
   369      describe('when a direction is not specified', () => {
   370        describe('when the transaction exists only on L1', () => {
   371          describe('when the transaction has messages', () => {
   372            for (const n of [1, 2, 4, 8]) {
   373              it(`should find ${n} messages when the transaction emits ${n} messages`, async () => {
   374                const messages = [...Array(n)].map(() => {
   375                  return DUMMY_MESSAGE
   376                })
   377  
   378                const tx = await l1Messenger.triggerSentMessageEvents(messages)
   379                const found = await messenger.getMessagesByTransaction(tx)
   380                expect(found).to.deep.equal(
   381                  messages.map((message, i) => {
   382                    return {
   383                      direction: MessageDirection.L1_TO_L2,
   384                      sender: message.sender,
   385                      target: message.target,
   386                      message: message.message,
   387                      messageNonce: ethers.BigNumber.from(message.messageNonce),
   388                      minGasLimit: ethers.BigNumber.from(message.minGasLimit),
   389                      value: ethers.BigNumber.from(message.value),
   390                      logIndex: i,
   391                      blockNumber: tx.blockNumber,
   392                      transactionHash: tx.hash,
   393                    }
   394                  })
   395                )
   396              })
   397            }
   398          })
   399  
   400          describe('when the transaction has no messages', () => {
   401            it('should find nothing', async () => {
   402              const tx = await l1Messenger.doNothing()
   403              const found = await messenger.getMessagesByTransaction(tx)
   404              expect(found).to.deep.equal([])
   405            })
   406          })
   407        })
   408  
   409        describe('when the transaction exists only on L2', () => {
   410          describe('when the transaction has messages', () => {
   411            for (const n of [1, 2, 4, 8]) {
   412              it(`should find ${n} messages when the transaction emits ${n} messages`, () => {
   413                // TODO: Need support for simulating more than one network.
   414              })
   415            }
   416          })
   417  
   418          describe('when the transaction has no messages', () => {
   419            it('should find nothing', () => {
   420              // TODO: Need support for simulating more than one network.
   421            })
   422          })
   423        })
   424  
   425        describe('when the transaction does not exist', () => {
   426          it('should throw an error', async () => {
   427            await expect(
   428              messenger.getMessagesByTransaction('0x' + '11'.repeat(32))
   429            ).to.be.rejectedWith('unable to find transaction receipt')
   430          })
   431        })
   432  
   433        describe('when the transaction exists on both L1 and L2', () => {
   434          it('should throw an error', async () => {
   435            // TODO: Need support for simulating more than one network.
   436          })
   437        })
   438      })
   439    })
   440  
   441    // Skipped until getMessagesByAddress can be implemented
   442    describe.skip('getMessagesByAddress', () => {
   443      describe('when the address has sent messages', () => {
   444        describe('when no direction is specified', () => {
   445          it('should find all messages sent by the address')
   446        })
   447  
   448        describe('when a direction is specified', () => {
   449          it('should find all messages only in the given direction')
   450        })
   451  
   452        describe('when a block range is specified', () => {
   453          it('should find all messages within the block range')
   454        })
   455  
   456        describe('when both a direction and a block range are specified', () => {
   457          it(
   458            'should find all messages only in the given direction and within the block range'
   459          )
   460        })
   461      })
   462  
   463      describe('when the address has not sent messages', () => {
   464        it('should find nothing')
   465      })
   466    })
   467  
   468    describe('toCrossChainMessage', () => {
   469      let l1Bridge: Contract
   470      let l2Bridge: Contract
   471      let l1Messenger: Contract
   472      let l2Messenger: Contract
   473      let messenger: CrossChainMessenger
   474      beforeEach(async () => {
   475        l1Messenger = (await (
   476          await ethers.getContractFactory('MockMessenger')
   477        ).deploy()) as any
   478        l2Messenger = (await (
   479          await ethers.getContractFactory('MockMessenger')
   480        ).deploy()) as any
   481        l1Bridge = (await (
   482          await ethers.getContractFactory('MockBridge')
   483        ).deploy(l1Messenger.address)) as any
   484        l2Bridge = (await (
   485          await ethers.getContractFactory('MockBridge')
   486        ).deploy(l2Messenger.address)) as any
   487  
   488        messenger = new CrossChainMessenger({
   489          bedrock: false,
   490          l1SignerOrProvider: ethers.provider,
   491          l2SignerOrProvider: ethers.provider,
   492          l1ChainId: L1ChainID.HARDHAT_LOCAL,
   493          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
   494          contracts: {
   495            l1: {
   496              L1CrossDomainMessenger: l1Messenger.address,
   497              L1StandardBridge: l1Bridge.address,
   498            },
   499            l2: {
   500              L2CrossDomainMessenger: l2Messenger.address,
   501              L2StandardBridge: l2Bridge.address,
   502            },
   503          },
   504          bridges: {
   505            Standard: {
   506              Adapter: StandardBridgeAdapter,
   507              l1Bridge: l1Bridge.address,
   508              l2Bridge: l2Bridge.address,
   509            },
   510          },
   511        })
   512      })
   513  
   514      describe('when the input is a CrossChainMessage', () => {
   515        it('should return the input', async () => {
   516          const message = {
   517            ...DUMMY_EXTENDED_MESSAGE,
   518            direction: MessageDirection.L1_TO_L2,
   519          }
   520  
   521          expect(await messenger.toCrossChainMessage(message)).to.deep.equal(
   522            message
   523          )
   524        })
   525      })
   526  
   527      describe('when the input is a TokenBridgeMessage', () => {
   528        // TODO: There are some edge cases here with custom bridges that conform to the interface but
   529        // not to the behavioral spec. Possibly worth testing those. For now this is probably
   530        // sufficient.
   531        it('should return the sent message event that came after the deposit or withdrawal', async () => {
   532          const from = '0x' + '99'.repeat(20)
   533          const deposit = {
   534            l1Token: '0x' + '11'.repeat(20),
   535            l2Token: '0x' + '22'.repeat(20),
   536            from,
   537            to: '0x' + '44'.repeat(20),
   538            amount: ethers.BigNumber.from(1234),
   539            data: '0x1234',
   540          }
   541  
   542          const tx = await l1Bridge.emitERC20DepositInitiated(deposit)
   543  
   544          const foundCrossChainMessages =
   545            await messenger.getMessagesByTransaction(tx)
   546          const foundTokenBridgeMessages = await messenger.getDepositsByAddress(
   547            from
   548          )
   549          const resolved = await messenger.toCrossChainMessage(
   550            foundTokenBridgeMessages[0]
   551          )
   552  
   553          expect(resolved).to.deep.equal(foundCrossChainMessages[0])
   554        })
   555      })
   556  
   557      describe('when the input is a TransactionLike', () => {
   558        describe('when the transaction sent exactly one message', () => {
   559          it('should return the CrossChainMessage sent in the transaction', async () => {
   560            const tx = await l1Messenger.triggerSentMessageEvents([DUMMY_MESSAGE])
   561            const foundCrossChainMessages =
   562              await messenger.getMessagesByTransaction(tx)
   563            const resolved = await messenger.toCrossChainMessage(tx)
   564            expect(resolved).to.deep.equal(foundCrossChainMessages[0])
   565          })
   566        })
   567  
   568        describe('when the transaction sent more than one message', () => {
   569          it('should be able to get second message by passing in an idex', async () => {
   570            const messages = [...Array(2)].map(() => {
   571              return DUMMY_MESSAGE
   572            })
   573  
   574            const tx = await l1Messenger.triggerSentMessageEvents(messages)
   575            const foundCrossChainMessages =
   576              await messenger.getMessagesByTransaction(tx)
   577            expect(await messenger.toCrossChainMessage(tx, 1)).to.deep.eq(
   578              foundCrossChainMessages[1]
   579            )
   580          })
   581        })
   582  
   583        describe('when the transaction sent no messages', () => {
   584          it('should throw an out of bounds error', async () => {
   585            const tx = await l1Messenger.triggerSentMessageEvents([])
   586            await expect(messenger.toCrossChainMessage(tx)).to.be.rejectedWith(
   587              `withdrawal index 0 out of bounds. There are 0 withdrawals`
   588            )
   589          })
   590        })
   591      })
   592    })
   593  
   594    describe('getMessageStatus', () => {
   595      let scc: Contract
   596      let l1Messenger: Contract
   597      let l2Messenger: Contract
   598      let messenger: CrossChainMessenger
   599      beforeEach(async () => {
   600        // TODO: Get rid of the nested awaits here. Could be a good first issue for someone.
   601        scc = (await (await ethers.getContractFactory('MockSCC')).deploy()) as any
   602        l1Messenger = (await (
   603          await ethers.getContractFactory('MockMessenger')
   604        ).deploy()) as any
   605        l2Messenger = (await (
   606          await ethers.getContractFactory('MockMessenger')
   607        ).deploy()) as any
   608  
   609        messenger = new CrossChainMessenger({
   610          bedrock: false,
   611          l1SignerOrProvider: ethers.provider,
   612          l2SignerOrProvider: ethers.provider,
   613          l1ChainId: L1ChainID.HARDHAT_LOCAL,
   614          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
   615          contracts: {
   616            l1: {
   617              L1CrossDomainMessenger: l1Messenger.address,
   618              StateCommitmentChain: scc.address,
   619            },
   620            l2: {
   621              L2CrossDomainMessenger: l2Messenger.address,
   622            },
   623          },
   624        })
   625      })
   626  
   627      const sendAndGetDummyMessage = async (direction: MessageDirection) => {
   628        const mockMessenger =
   629          direction === MessageDirection.L1_TO_L2 ? l1Messenger : l2Messenger
   630        const tx = await mockMessenger.triggerSentMessageEvents([DUMMY_MESSAGE])
   631        return (
   632          await messenger.getMessagesByTransaction(tx, {
   633            direction,
   634          })
   635        )[0]
   636      }
   637  
   638      const submitStateRootBatchForMessage = async (
   639        message: CrossChainMessage
   640      ) => {
   641        await scc.setSBAParams({
   642          batchIndex: 0,
   643          batchRoot: ethers.constants.HashZero,
   644          batchSize: 1,
   645          prevTotalElements: message.blockNumber,
   646          extraData: '0x',
   647        })
   648        await scc.appendStateBatch([ethers.constants.HashZero], 0)
   649      }
   650  
   651      describe('when the message is an L1 => L2 message', () => {
   652        describe('when the message has not been executed on L2 yet', () => {
   653          it('should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE', async () => {
   654            const message = await sendAndGetDummyMessage(
   655              MessageDirection.L1_TO_L2
   656            )
   657  
   658            expect(await messenger.getMessageStatus(message)).to.equal(
   659              MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE
   660            )
   661          })
   662        })
   663  
   664        describe('when the message has been executed on L2', () => {
   665          it('should return a status of RELAYED', async () => {
   666            const message = await sendAndGetDummyMessage(
   667              MessageDirection.L1_TO_L2
   668            )
   669  
   670            await l2Messenger.triggerRelayedMessageEvents([
   671              hashCrossDomainMessage(
   672                message.messageNonce,
   673                message.sender,
   674                message.target,
   675                message.value,
   676                message.minGasLimit,
   677                message.message
   678              ),
   679            ])
   680  
   681            expect(await messenger.getMessageStatus(message)).to.equal(
   682              MessageStatus.RELAYED
   683            )
   684          })
   685        })
   686  
   687        describe('when the message has been executed but failed', () => {
   688          it('should return a status of FAILED_L1_TO_L2_MESSAGE', async () => {
   689            const message = await sendAndGetDummyMessage(
   690              MessageDirection.L1_TO_L2
   691            )
   692  
   693            await l2Messenger.triggerFailedRelayedMessageEvents([
   694              hashCrossDomainMessage(
   695                message.messageNonce,
   696                message.sender,
   697                message.target,
   698                message.value,
   699                message.minGasLimit,
   700                message.message
   701              ),
   702            ])
   703  
   704            expect(await messenger.getMessageStatus(message)).to.equal(
   705              MessageStatus.FAILED_L1_TO_L2_MESSAGE
   706            )
   707          })
   708        })
   709      })
   710  
   711      describe('when the message is an L2 => L1 message', () => {
   712        describe('when the message state root has not been published', () => {
   713          it('should return a status of STATE_ROOT_NOT_PUBLISHED', async () => {
   714            const message = await sendAndGetDummyMessage(
   715              MessageDirection.L2_TO_L1
   716            )
   717  
   718            expect(await messenger.getMessageStatus(message)).to.equal(
   719              MessageStatus.STATE_ROOT_NOT_PUBLISHED
   720            )
   721          })
   722        })
   723  
   724        describe('when the message state root is still in the challenge period', () => {
   725          it('should return a status of IN_CHALLENGE_PERIOD', async () => {
   726            const message = await sendAndGetDummyMessage(
   727              MessageDirection.L2_TO_L1
   728            )
   729  
   730            await submitStateRootBatchForMessage(message)
   731  
   732            expect(await messenger.getMessageStatus(message)).to.equal(
   733              MessageStatus.IN_CHALLENGE_PERIOD
   734            )
   735          })
   736        })
   737  
   738        describe('when the message is no longer in the challenge period', () => {
   739          describe('when the message has been relayed successfully', () => {
   740            it('should return a status of RELAYED', async () => {
   741              const message = await sendAndGetDummyMessage(
   742                MessageDirection.L2_TO_L1
   743              )
   744  
   745              await submitStateRootBatchForMessage(message)
   746  
   747              const challengePeriod = await messenger.getChallengePeriodSeconds()
   748              ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
   749              ethers.provider.send('evm_mine', [])
   750  
   751              await l1Messenger.triggerRelayedMessageEvents([
   752                hashCrossDomainMessage(
   753                  message.messageNonce,
   754                  message.sender,
   755                  message.target,
   756                  message.value,
   757                  message.minGasLimit,
   758                  message.message
   759                ),
   760              ])
   761  
   762              expect(await messenger.getMessageStatus(message)).to.equal(
   763                MessageStatus.RELAYED
   764              )
   765            })
   766          })
   767  
   768          describe('when the message has been relayed but the relay failed', () => {
   769            it('should return a status of READY_FOR_RELAY', async () => {
   770              const message = await sendAndGetDummyMessage(
   771                MessageDirection.L2_TO_L1
   772              )
   773  
   774              await submitStateRootBatchForMessage(message)
   775  
   776              const challengePeriod = await messenger.getChallengePeriodSeconds()
   777              ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
   778              ethers.provider.send('evm_mine', [])
   779  
   780              await l1Messenger.triggerFailedRelayedMessageEvents([
   781                hashCrossDomainMessage(
   782                  message.messageNonce,
   783                  message.sender,
   784                  message.target,
   785                  message.value,
   786                  message.minGasLimit,
   787                  message.message
   788                ),
   789              ])
   790  
   791              expect(await messenger.getMessageStatus(message)).to.equal(
   792                MessageStatus.READY_FOR_RELAY
   793              )
   794            })
   795          })
   796  
   797          describe('when the message has not been relayed', () => {
   798            it('should return a status of READY_FOR_RELAY', async () => {
   799              const message = await sendAndGetDummyMessage(
   800                MessageDirection.L2_TO_L1
   801              )
   802  
   803              await submitStateRootBatchForMessage(message)
   804  
   805              const challengePeriod = await messenger.getChallengePeriodSeconds()
   806              ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
   807              ethers.provider.send('evm_mine', [])
   808  
   809              expect(await messenger.getMessageStatus(message)).to.equal(
   810                MessageStatus.READY_FOR_RELAY
   811              )
   812            })
   813          })
   814        })
   815      })
   816  
   817      describe('when the message does not exist', () => {
   818        // TODO: Figure out if this is the correct behavior. Mark suggests perhaps returning null.
   819        it('should throw an error')
   820      })
   821    })
   822  
   823    describe('getMessageReceipt', () => {
   824      let l1Bridge: Contract
   825      let l2Bridge: Contract
   826      let l1Messenger: Contract
   827      let l2Messenger: Contract
   828      let messenger: CrossChainMessenger
   829      beforeEach(async () => {
   830        l1Messenger = (await (
   831          await ethers.getContractFactory('MockMessenger')
   832        ).deploy()) as any
   833        l2Messenger = (await (
   834          await ethers.getContractFactory('MockMessenger')
   835        ).deploy()) as any
   836        l1Bridge = (await (
   837          await ethers.getContractFactory('MockBridge')
   838        ).deploy(l1Messenger.address)) as any
   839        l2Bridge = (await (
   840          await ethers.getContractFactory('MockBridge')
   841        ).deploy(l2Messenger.address)) as any
   842  
   843        messenger = new CrossChainMessenger({
   844          bedrock: false,
   845          l1SignerOrProvider: ethers.provider,
   846          l2SignerOrProvider: ethers.provider,
   847          l1ChainId: L1ChainID.HARDHAT_LOCAL,
   848          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
   849          contracts: {
   850            l1: {
   851              L1CrossDomainMessenger: l1Messenger.address,
   852              L1StandardBridge: l1Bridge.address,
   853            },
   854            l2: {
   855              L2CrossDomainMessenger: l2Messenger.address,
   856              L2StandardBridge: l2Bridge.address,
   857            },
   858          },
   859        })
   860      })
   861  
   862      describe('when the message has been relayed', () => {
   863        describe('when the relay was successful', () => {
   864          it('should return the receipt of the transaction that relayed the message', async () => {
   865            const message = {
   866              ...DUMMY_EXTENDED_MESSAGE,
   867              direction: MessageDirection.L1_TO_L2,
   868            }
   869  
   870            const tx = await l2Messenger.triggerRelayedMessageEvents([
   871              hashCrossDomainMessage(
   872                message.messageNonce,
   873                message.sender,
   874                message.target,
   875                message.value,
   876                message.minGasLimit,
   877                message.message
   878              ),
   879            ])
   880  
   881            const messageReceipt = await messenger.getMessageReceipt(message)
   882            expect(messageReceipt.receiptStatus).to.equal(1)
   883            expect(
   884              omit(messageReceipt.transactionReceipt, 'confirmations')
   885            ).to.deep.equal(
   886              omit(
   887                await ethers.provider.getTransactionReceipt(tx.hash),
   888                'confirmations'
   889              )
   890            )
   891          })
   892        })
   893  
   894        describe('when the relay failed', () => {
   895          it('should return the receipt of the transaction that attempted to relay the message', async () => {
   896            const message = {
   897              ...DUMMY_EXTENDED_MESSAGE,
   898              direction: MessageDirection.L1_TO_L2,
   899            }
   900  
   901            const tx = await l2Messenger.triggerFailedRelayedMessageEvents([
   902              hashCrossDomainMessage(
   903                message.messageNonce,
   904                message.sender,
   905                message.target,
   906                message.value,
   907                message.minGasLimit,
   908                message.message
   909              ),
   910            ])
   911  
   912            const messageReceipt = await messenger.getMessageReceipt(message)
   913            expect(messageReceipt.receiptStatus).to.equal(0)
   914            expect(
   915              omit(messageReceipt.transactionReceipt, 'confirmations')
   916            ).to.deep.equal(
   917              omit(
   918                await ethers.provider.getTransactionReceipt(tx.hash),
   919                'confirmations'
   920              )
   921            )
   922          })
   923        })
   924  
   925        describe('when the relay failed more than once', () => {
   926          it('should return the receipt of the last transaction that attempted to relay the message', async () => {
   927            const message = {
   928              ...DUMMY_EXTENDED_MESSAGE,
   929              direction: MessageDirection.L1_TO_L2,
   930            }
   931  
   932            await l2Messenger.triggerFailedRelayedMessageEvents([
   933              hashCrossDomainMessage(
   934                message.messageNonce,
   935                message.sender,
   936                message.target,
   937                message.value,
   938                message.minGasLimit,
   939                message.message
   940              ),
   941            ])
   942  
   943            const tx = await l2Messenger.triggerFailedRelayedMessageEvents([
   944              hashCrossDomainMessage(
   945                message.messageNonce,
   946                message.sender,
   947                message.target,
   948                message.value,
   949                message.minGasLimit,
   950                message.message
   951              ),
   952            ])
   953  
   954            const messageReceipt = await messenger.getMessageReceipt(message)
   955            expect(messageReceipt.receiptStatus).to.equal(0)
   956            expect(
   957              omit(messageReceipt.transactionReceipt, 'confirmations')
   958            ).to.deep.equal(
   959              omit(
   960                await ethers.provider.getTransactionReceipt(tx.hash),
   961                'confirmations'
   962              )
   963            )
   964          })
   965        })
   966      })
   967  
   968      describe('when the message has not been relayed', () => {
   969        it('should return null', async () => {
   970          const message = {
   971            ...DUMMY_EXTENDED_MESSAGE,
   972            direction: MessageDirection.L1_TO_L2,
   973          }
   974  
   975          await l2Messenger.doNothing()
   976  
   977          const messageReceipt = await messenger.getMessageReceipt(message)
   978          expect(messageReceipt).to.equal(null)
   979        })
   980      })
   981  
   982      // TODO: Go over all of these tests and remove the empty functions so we can accurately keep
   983      // track of
   984    })
   985  
   986    describe('waitForMessageReceipt', () => {
   987      let l2Messenger: Contract
   988      let messenger: CrossChainMessenger
   989      beforeEach(async () => {
   990        l2Messenger = (await (
   991          await ethers.getContractFactory('MockMessenger')
   992        ).deploy()) as any
   993  
   994        messenger = new CrossChainMessenger({
   995          bedrock: false,
   996          l1SignerOrProvider: ethers.provider,
   997          l2SignerOrProvider: ethers.provider,
   998          l1ChainId: L1ChainID.HARDHAT_LOCAL,
   999          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1000          contracts: {
  1001            l2: {
  1002              L2CrossDomainMessenger: l2Messenger.address,
  1003            },
  1004          },
  1005        })
  1006      })
  1007  
  1008      describe('when the message receipt already exists', () => {
  1009        it('should immediately return the receipt', async () => {
  1010          const message = {
  1011            ...DUMMY_EXTENDED_MESSAGE,
  1012            direction: MessageDirection.L1_TO_L2,
  1013          }
  1014  
  1015          const tx = await l2Messenger.triggerRelayedMessageEvents([
  1016            hashCrossDomainMessage(
  1017              message.messageNonce,
  1018              message.sender,
  1019              message.target,
  1020              message.value,
  1021              message.minGasLimit,
  1022              message.message
  1023            ),
  1024          ])
  1025  
  1026          const messageReceipt = await messenger.waitForMessageReceipt(message)
  1027          expect(messageReceipt.receiptStatus).to.equal(1)
  1028          expect(
  1029            omit(messageReceipt.transactionReceipt, 'confirmations')
  1030          ).to.deep.equal(
  1031            omit(
  1032              await ethers.provider.getTransactionReceipt(tx.hash),
  1033              'confirmations'
  1034            )
  1035          )
  1036        })
  1037      })
  1038  
  1039      describe('when the message receipt does not exist already', () => {
  1040        describe('when no extra options are provided', () => {
  1041          it('should wait for the receipt to be published', async () => {
  1042            const message = {
  1043              ...DUMMY_EXTENDED_MESSAGE,
  1044              direction: MessageDirection.L1_TO_L2,
  1045            }
  1046  
  1047            setTimeout(async () => {
  1048              await l2Messenger.triggerRelayedMessageEvents([
  1049                hashCrossDomainMessage(
  1050                  message.messageNonce,
  1051                  message.sender,
  1052                  message.target,
  1053                  message.value,
  1054                  message.minGasLimit,
  1055                  message.message
  1056                ),
  1057              ])
  1058            }, 5000)
  1059  
  1060            const tick = Date.now()
  1061            const messageReceipt = await messenger.waitForMessageReceipt(message)
  1062            const tock = Date.now()
  1063            expect(messageReceipt.receiptStatus).to.equal(1)
  1064            expect(tock - tick).to.be.greaterThan(5000)
  1065          })
  1066  
  1067          it('should wait forever for the receipt if the receipt is never published', () => {
  1068            // Not sure how to easily test this without introducing some sort of cancellation token
  1069            // I don't want the promise to loop forever and make the tests never finish.
  1070          })
  1071        })
  1072  
  1073        describe('when a timeout is provided', () => {
  1074          it('should throw an error if the timeout is reached', async () => {
  1075            const message = {
  1076              ...DUMMY_EXTENDED_MESSAGE,
  1077              direction: MessageDirection.L1_TO_L2,
  1078            }
  1079  
  1080            await expect(
  1081              messenger.waitForMessageReceipt(message, {
  1082                timeoutMs: 10000,
  1083              })
  1084            ).to.be.rejectedWith('timed out waiting for message receipt')
  1085          })
  1086        })
  1087      })
  1088    })
  1089  
  1090    describe('estimateL2MessageGasLimit', () => {
  1091      let messenger: CrossChainMessenger
  1092      beforeEach(async () => {
  1093        messenger = new CrossChainMessenger({
  1094          bedrock: false,
  1095          l1SignerOrProvider: ethers.provider,
  1096          l2SignerOrProvider: ethers.provider,
  1097          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1098          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1099        })
  1100      })
  1101  
  1102      describe('when the message is an L1 to L2 message', () => {
  1103        it('should return an accurate gas estimate plus a ~20% buffer', async () => {
  1104          const message = {
  1105            direction: MessageDirection.L1_TO_L2,
  1106            target: '0x' + '11'.repeat(20),
  1107            sender: '0x' + '22'.repeat(20),
  1108            message: '0x' + '33'.repeat(64),
  1109            messageNonce: 1234,
  1110            logIndex: 0,
  1111            blockNumber: 1234,
  1112            transactionHash: '0x' + '44'.repeat(32),
  1113          }
  1114  
  1115          const estimate = await ethers.provider.estimateGas({
  1116            to: message.target,
  1117            from: message.sender,
  1118            data: message.message,
  1119          })
  1120  
  1121          // Approximately 20% greater than the estimate, +/- 1%.
  1122          expectApprox(
  1123            await messenger.estimateL2MessageGasLimit(message),
  1124            estimate.mul(120).div(100),
  1125            {
  1126              percentUpperDeviation: 1,
  1127              percentLowerDeviation: 1,
  1128            }
  1129          )
  1130        })
  1131  
  1132        it('should return an accurate gas estimate when a custom buffer is provided', async () => {
  1133          const message = {
  1134            direction: MessageDirection.L1_TO_L2,
  1135            target: '0x' + '11'.repeat(20),
  1136            sender: '0x' + '22'.repeat(20),
  1137            message: '0x' + '33'.repeat(64),
  1138            messageNonce: 1234,
  1139            logIndex: 0,
  1140            blockNumber: 1234,
  1141            transactionHash: '0x' + '44'.repeat(32),
  1142          }
  1143  
  1144          const estimate = await ethers.provider.estimateGas({
  1145            to: message.target,
  1146            from: message.sender,
  1147            data: message.message,
  1148          })
  1149  
  1150          // Approximately 30% greater than the estimate, +/- 1%.
  1151          expectApprox(
  1152            await messenger.estimateL2MessageGasLimit(message, {
  1153              bufferPercent: 30,
  1154            }),
  1155            estimate.mul(130).div(100),
  1156            {
  1157              percentUpperDeviation: 1,
  1158              percentLowerDeviation: 1,
  1159            }
  1160          )
  1161        })
  1162      })
  1163  
  1164      describe('when the message is an L2 to L1 message', () => {
  1165        it('should throw an error', async () => {
  1166          const message = {
  1167            direction: MessageDirection.L2_TO_L1,
  1168            target: '0x' + '11'.repeat(20),
  1169            sender: '0x' + '22'.repeat(20),
  1170            message: '0x' + '33'.repeat(64),
  1171            messageNonce: 1234,
  1172            logIndex: 0,
  1173            blockNumber: 1234,
  1174            transactionHash: '0x' + '44'.repeat(32),
  1175          }
  1176  
  1177          await expect(messenger.estimateL2MessageGasLimit(message)).to.be
  1178            .rejected
  1179        })
  1180      })
  1181    })
  1182  
  1183    describe('estimateMessageWaitTimeSeconds', () => {
  1184      let scc: Contract
  1185      let l1Messenger: Contract
  1186      let l2Messenger: Contract
  1187      let messenger: CrossChainMessenger
  1188      beforeEach(async () => {
  1189        // TODO: Get rid of the nested awaits here. Could be a good first issue for someone.
  1190        scc = (await (await ethers.getContractFactory('MockSCC')).deploy()) as any
  1191        l1Messenger = (await (
  1192          await ethers.getContractFactory('MockMessenger')
  1193        ).deploy()) as any
  1194        l2Messenger = (await (
  1195          await ethers.getContractFactory('MockMessenger')
  1196        ).deploy()) as any
  1197  
  1198        messenger = new CrossChainMessenger({
  1199          bedrock: false,
  1200          l1SignerOrProvider: ethers.provider,
  1201          l2SignerOrProvider: ethers.provider,
  1202          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1203          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1204          contracts: {
  1205            l1: {
  1206              L1CrossDomainMessenger: l1Messenger.address,
  1207              StateCommitmentChain: scc.address,
  1208            },
  1209            l2: {
  1210              L2CrossDomainMessenger: l2Messenger.address,
  1211            },
  1212          },
  1213        })
  1214      })
  1215  
  1216      const sendAndGetDummyMessage = async (direction: MessageDirection) => {
  1217        const mockMessenger =
  1218          direction === MessageDirection.L1_TO_L2 ? l1Messenger : l2Messenger
  1219        const tx = await mockMessenger.triggerSentMessageEvents([DUMMY_MESSAGE])
  1220        return (
  1221          await messenger.getMessagesByTransaction(tx, {
  1222            direction,
  1223          })
  1224        )[0]
  1225      }
  1226  
  1227      const submitStateRootBatchForMessage = async (
  1228        message: CrossChainMessage
  1229      ) => {
  1230        await scc.setSBAParams({
  1231          batchIndex: 0,
  1232          batchRoot: ethers.constants.HashZero,
  1233          batchSize: 1,
  1234          prevTotalElements: message.blockNumber,
  1235          extraData: '0x',
  1236        })
  1237        await scc.appendStateBatch([ethers.constants.HashZero], 0)
  1238      }
  1239  
  1240      describe('when the message is an L1 => L2 message', () => {
  1241        describe('when the message has not been executed on L2 yet', () => {
  1242          it('should return the estimated seconds until the message will be confirmed on L2', async () => {
  1243            const message = await sendAndGetDummyMessage(
  1244              MessageDirection.L1_TO_L2
  1245            )
  1246  
  1247            await l1Messenger.triggerSentMessageEvents([message])
  1248  
  1249            expect(
  1250              await messenger.estimateMessageWaitTimeSeconds(message)
  1251            ).to.equal(1)
  1252          })
  1253        })
  1254  
  1255        describe('when the message has been executed on L2', () => {
  1256          it('should return 0', async () => {
  1257            const message = await sendAndGetDummyMessage(
  1258              MessageDirection.L1_TO_L2
  1259            )
  1260  
  1261            await l1Messenger.triggerSentMessageEvents([message])
  1262            await l2Messenger.triggerRelayedMessageEvents([
  1263              hashCrossDomainMessage(
  1264                message.messageNonce,
  1265                message.sender,
  1266                message.target,
  1267                message.value,
  1268                message.minGasLimit,
  1269                message.message
  1270              ),
  1271            ])
  1272  
  1273            expect(
  1274              await messenger.estimateMessageWaitTimeSeconds(message)
  1275            ).to.equal(0)
  1276          })
  1277        })
  1278      })
  1279  
  1280      describe('when the message is an L2 => L1 message', () => {
  1281        describe('when the state root has not been published', () => {
  1282          it('should return the estimated seconds until the state root will be published and pass the challenge period', async () => {
  1283            const message = await sendAndGetDummyMessage(
  1284              MessageDirection.L2_TO_L1
  1285            )
  1286  
  1287            expect(
  1288              await messenger.estimateMessageWaitTimeSeconds(message)
  1289            ).to.equal(await messenger.getChallengePeriodSeconds())
  1290          })
  1291        })
  1292  
  1293        describe('when the state root is within the challenge period', () => {
  1294          it('should return the estimated seconds until the state root passes the challenge period', async () => {
  1295            const message = await sendAndGetDummyMessage(
  1296              MessageDirection.L2_TO_L1
  1297            )
  1298  
  1299            await submitStateRootBatchForMessage(message)
  1300  
  1301            const challengePeriod = await messenger.getChallengePeriodSeconds()
  1302            ethers.provider.send('evm_increaseTime', [challengePeriod / 2])
  1303            ethers.provider.send('evm_mine', [])
  1304  
  1305            expectApprox(
  1306              await messenger.estimateMessageWaitTimeSeconds(message),
  1307              challengePeriod / 2,
  1308              {
  1309                percentUpperDeviation: 5,
  1310                percentLowerDeviation: 5,
  1311              }
  1312            )
  1313          })
  1314        })
  1315  
  1316        describe('when the state root passes the challenge period', () => {
  1317          it('should return 0', async () => {
  1318            const message = await sendAndGetDummyMessage(
  1319              MessageDirection.L2_TO_L1
  1320            )
  1321  
  1322            await submitStateRootBatchForMessage(message)
  1323  
  1324            const challengePeriod = await messenger.getChallengePeriodSeconds()
  1325            ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
  1326            ethers.provider.send('evm_mine', [])
  1327  
  1328            expect(
  1329              await messenger.estimateMessageWaitTimeSeconds(message)
  1330            ).to.equal(0)
  1331          })
  1332        })
  1333  
  1334        describe('when the message has been executed', () => {
  1335          it('should return 0', async () => {
  1336            const message = await sendAndGetDummyMessage(
  1337              MessageDirection.L2_TO_L1
  1338            )
  1339  
  1340            await l2Messenger.triggerSentMessageEvents([message])
  1341            await l1Messenger.triggerRelayedMessageEvents([
  1342              hashCrossDomainMessage(
  1343                message.messageNonce,
  1344                message.sender,
  1345                message.target,
  1346                message.value,
  1347                message.minGasLimit,
  1348                message.message
  1349              ),
  1350            ])
  1351  
  1352            expect(
  1353              await messenger.estimateMessageWaitTimeSeconds(message)
  1354            ).to.equal(0)
  1355          })
  1356        })
  1357      })
  1358    })
  1359  
  1360    describe('sendMessage', () => {
  1361      let l1Messenger: Contract
  1362      let l2Messenger: Contract
  1363      let messenger: CrossChainMessenger
  1364      beforeEach(async () => {
  1365        l1Messenger = (await (
  1366          await ethers.getContractFactory('MockMessenger')
  1367        ).deploy()) as any
  1368        l2Messenger = (await (
  1369          await ethers.getContractFactory('MockMessenger')
  1370        ).deploy()) as any
  1371  
  1372        messenger = new CrossChainMessenger({
  1373          bedrock: false,
  1374          l1SignerOrProvider: l1Signer,
  1375          l2SignerOrProvider: l2Signer,
  1376          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1377          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1378          contracts: {
  1379            l1: {
  1380              L1CrossDomainMessenger: l1Messenger.address,
  1381            },
  1382            l2: {
  1383              L2CrossDomainMessenger: l2Messenger.address,
  1384            },
  1385          },
  1386        })
  1387      })
  1388  
  1389      describe('when the message is an L1 to L2 message', () => {
  1390        describe('when no l2GasLimit is provided', () => {
  1391          it('should send a message with an estimated l2GasLimit', async () => {
  1392            const message = {
  1393              direction: MessageDirection.L1_TO_L2,
  1394              target: '0x' + '11'.repeat(20),
  1395              message: '0x' + '22'.repeat(32),
  1396            }
  1397  
  1398            const estimate = await messenger.estimateL2MessageGasLimit(message)
  1399            await expect(messenger.sendMessage(message))
  1400              .to.emit(l1Messenger, 'SentMessage')
  1401              .withArgs(
  1402                message.target,
  1403                await l1Signer.getAddress(),
  1404                message.message,
  1405                0,
  1406                estimate
  1407              )
  1408          })
  1409        })
  1410  
  1411        describe('when an l2GasLimit is provided', () => {
  1412          it('should send a message with the provided l2GasLimit', async () => {
  1413            const message = {
  1414              direction: MessageDirection.L1_TO_L2,
  1415              target: '0x' + '11'.repeat(20),
  1416              message: '0x' + '22'.repeat(32),
  1417            }
  1418  
  1419            await expect(
  1420              messenger.sendMessage(message, {
  1421                l2GasLimit: 1234,
  1422              })
  1423            )
  1424              .to.emit(l1Messenger, 'SentMessage')
  1425              .withArgs(
  1426                message.target,
  1427                await l1Signer.getAddress(),
  1428                message.message,
  1429                0,
  1430                1234
  1431              )
  1432          })
  1433        })
  1434      })
  1435  
  1436      describe('when the message is an L2 to L1 message', () => {
  1437        it('should send a message', async () => {
  1438          const message = {
  1439            direction: MessageDirection.L2_TO_L1,
  1440            target: '0x' + '11'.repeat(20),
  1441            message: '0x' + '22'.repeat(32),
  1442          }
  1443  
  1444          await expect(messenger.sendMessage(message))
  1445            .to.emit(l2Messenger, 'SentMessage')
  1446            .withArgs(
  1447              message.target,
  1448              await l2Signer.getAddress(),
  1449              message.message,
  1450              0,
  1451              0
  1452            )
  1453        })
  1454      })
  1455    })
  1456  
  1457    describe('resendMessage', () => {
  1458      let l1Messenger: Contract
  1459      let l2Messenger: Contract
  1460      let messenger: CrossChainMessenger
  1461      beforeEach(async () => {
  1462        l1Messenger = (await (
  1463          await ethers.getContractFactory('MockMessenger')
  1464        ).deploy()) as any
  1465        l2Messenger = (await (
  1466          await ethers.getContractFactory('MockMessenger')
  1467        ).deploy()) as any
  1468  
  1469        messenger = new CrossChainMessenger({
  1470          bedrock: false,
  1471          l1SignerOrProvider: l1Signer,
  1472          l2SignerOrProvider: l2Signer,
  1473          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1474          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1475          contracts: {
  1476            l1: {
  1477              L1CrossDomainMessenger: l1Messenger.address,
  1478            },
  1479            l2: {
  1480              L2CrossDomainMessenger: l2Messenger.address,
  1481            },
  1482          },
  1483        })
  1484      })
  1485  
  1486      describe('when resending an L1 to L2 message', () => {
  1487        it('should resend the message with the new gas limit', async () => {
  1488          const message = {
  1489            direction: MessageDirection.L1_TO_L2,
  1490            target: '0x' + '11'.repeat(20),
  1491            message: '0x' + '22'.repeat(32),
  1492          }
  1493  
  1494          const sent = await messenger.sendMessage(message, {
  1495            l2GasLimit: 1234,
  1496          })
  1497  
  1498          await expect(messenger.resendMessage(sent, 10000))
  1499            .to.emit(l1Messenger, 'SentMessage')
  1500            .withArgs(
  1501              message.target,
  1502              await l1Signer.getAddress(),
  1503              message.message,
  1504              1, // nonce is now 1
  1505              10000
  1506            )
  1507        })
  1508      })
  1509  
  1510      describe('when resending an L2 to L1 message', () => {
  1511        it('should throw an error', async () => {
  1512          const message = {
  1513            direction: MessageDirection.L2_TO_L1,
  1514            target: '0x' + '11'.repeat(20),
  1515            message: '0x' + '22'.repeat(32),
  1516          }
  1517  
  1518          const sent = await messenger.sendMessage(message, {
  1519            l2GasLimit: 1234,
  1520          })
  1521  
  1522          await expect(messenger.resendMessage(sent, 10000)).to.be.rejected
  1523        })
  1524      })
  1525    })
  1526  
  1527    describe('finalizeMessage', () => {
  1528      describe('when the message being finalized exists', () => {
  1529        describe('when the message is ready to be finalized', () => {
  1530          it('should finalize the message')
  1531        })
  1532  
  1533        describe('when the message is not ready to be finalized', () => {
  1534          it('should throw an error')
  1535        })
  1536  
  1537        describe('when the message has already been finalized', () => {
  1538          it('should throw an error')
  1539        })
  1540      })
  1541  
  1542      describe('when the message being finalized does not exist', () => {
  1543        it('should throw an error')
  1544      })
  1545    })
  1546  
  1547    describe('depositETH', () => {
  1548      let l1Messenger: Contract
  1549      let l2Messenger: Contract
  1550      let l1Bridge: Contract
  1551      let l2Bridge: Contract
  1552      let messenger: CrossChainMessenger
  1553      beforeEach(async () => {
  1554        l1Messenger = (await (
  1555          await ethers.getContractFactory('MockMessenger')
  1556        ).deploy()) as any
  1557        l1Bridge = (await (
  1558          await ethers.getContractFactory('MockBridge')
  1559        ).deploy(l1Messenger.address)) as any
  1560        l2Messenger = (await (
  1561          await ethers.getContractFactory('MockMessenger')
  1562        ).deploy()) as any
  1563        l2Bridge = (await (
  1564          await ethers.getContractFactory('MockBridge')
  1565        ).deploy(l2Messenger.address)) as any
  1566  
  1567        messenger = new CrossChainMessenger({
  1568          bedrock: false,
  1569          l1SignerOrProvider: l1Signer,
  1570          l2SignerOrProvider: l2Signer,
  1571          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1572          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1573          contracts: {
  1574            l1: {
  1575              L1CrossDomainMessenger: l1Messenger.address,
  1576              L1StandardBridge: l1Bridge.address,
  1577            },
  1578            l2: {
  1579              L2CrossDomainMessenger: l2Messenger.address,
  1580              L2StandardBridge: l2Bridge.address,
  1581            },
  1582          },
  1583          bridges: {
  1584            ETH: {
  1585              Adapter: ETHBridgeAdapter,
  1586              l1Bridge: l1Bridge.address,
  1587              l2Bridge: l2Bridge.address,
  1588            },
  1589          },
  1590        })
  1591      })
  1592  
  1593      it('should trigger the deposit ETH function with the given amount', async () => {
  1594        await expect(messenger.depositETH(100000))
  1595          .to.emit(l1Bridge, 'ETHDepositInitiated')
  1596          .withArgs(
  1597            await l1Signer.getAddress(),
  1598            await l1Signer.getAddress(),
  1599            100000,
  1600            '0x'
  1601          )
  1602      })
  1603    })
  1604  
  1605    describe('withdrawETH', () => {
  1606      let l1Messenger: Contract
  1607      let l2Messenger: Contract
  1608      let l1Bridge: Contract
  1609      let l2Bridge: Contract
  1610      let messenger: CrossChainMessenger
  1611      beforeEach(async () => {
  1612        l1Messenger = (await (
  1613          await ethers.getContractFactory('MockMessenger')
  1614        ).deploy()) as any
  1615        l1Bridge = (await (
  1616          await ethers.getContractFactory('MockBridge')
  1617        ).deploy(l1Messenger.address)) as any
  1618        l2Messenger = (await (
  1619          await ethers.getContractFactory('MockMessenger')
  1620        ).deploy()) as any
  1621        l2Bridge = (await (
  1622          await ethers.getContractFactory('MockBridge')
  1623        ).deploy(l2Messenger.address)) as any
  1624  
  1625        messenger = new CrossChainMessenger({
  1626          bedrock: false,
  1627          l1SignerOrProvider: l1Signer,
  1628          l2SignerOrProvider: l2Signer,
  1629          l1ChainId: L1ChainID.HARDHAT_LOCAL,
  1630          l2ChainId: L2ChainID.OPTIMISM_HARDHAT_LOCAL,
  1631          contracts: {
  1632            l1: {
  1633              L1CrossDomainMessenger: l1Messenger.address,
  1634              L1StandardBridge: l1Bridge.address,
  1635            },
  1636            l2: {
  1637              L2CrossDomainMessenger: l2Messenger.address,
  1638              L2StandardBridge: l2Bridge.address,
  1639            },
  1640          },
  1641          bridges: {
  1642            ETH: {
  1643              Adapter: ETHBridgeAdapter,
  1644              l1Bridge: l1Bridge.address,
  1645              l2Bridge: l2Bridge.address,
  1646            },
  1647          },
  1648        })
  1649      })
  1650  
  1651      it('should trigger the withdraw ETH function with the given amount', async () => {
  1652        await expect(messenger.withdrawETH(100000))
  1653          .to.emit(l2Bridge, 'WithdrawalInitiated')
  1654          .withArgs(
  1655            ethers.constants.AddressZero,
  1656            predeploys.OVM_ETH,
  1657            await l2Signer.getAddress(),
  1658            await l2Signer.getAddress(),
  1659            100000,
  1660            '0x'
  1661          )
  1662      })
  1663    })
  1664  })