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