Commit 7ac0c8a7 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Add OVM_L1MultiMessageRelayer

This commit adds a simple contract which accepts an array of messages, and loops over them to forward to the OVM_L1CrossDomainMessenger, thus saving gas from having to submit multiple transactions
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
Co-authored-by: default avatarKevin Ho <kevinjho1996@gmail.com>
parent 570a1641
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1MultiMessageRelayer } from "../../iOVM/bridge/iOVM_L1MultiMessageRelayer.sol";
/* Contract Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/**
* @title OVM_L1MultiMessageRelayer
* @dev The L1 Multi-Message Relayer contract is a gas efficiency optimization which enables the
* relayer to submit multiple messages in a single transaction to be relayed by the L1 Cross Domain
* Message Sender.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1MultiMessageRelayer is iOVM_L1MultiMessageRelayer, Lib_AddressResolver {
/***************
* Constructor *
***************/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{}
/**********************
* Function Modifiers *
**********************/
modifier onlyBatchRelayer() {
require(
msg.sender == resolve("OVM_L2BatchMessageRelayer"),
"OVM_L1MultiMessageRelayer: Function can only be called by the OVM_L2BatchMessageRelayer"
);
_;
}
/********************
* Public Functions *
********************/
/**
* @notice Forwards multiple cross domain messages to the L1 Cross Domain Messenger for relaying
* @param _messages An array of L2 to L1 messages
*/
function batchRelayMessages(L2ToL1Message[] calldata _messages)
override
external
onlyBatchRelayer
{
iOVM_L1CrossDomainMessenger messenger = iOVM_L1CrossDomainMessenger(resolve("Proxy__OVM_L1CrossDomainMessenger"));
for (uint256 i = 0; i < _messages.length; i++) {
L2ToL1Message memory message = _messages[i];
messenger.relayMessage(
message.target,
message.sender,
message.message,
message.messageNonce,
message.proof
);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L1CrossDomainMessenger.sol";
interface iOVM_L1MultiMessageRelayer {
struct L2ToL1Message {
address target;
address sender;
bytes message;
uint256 messageNonce;
iOVM_L1CrossDomainMessenger.L2MessageInclusionProof proof;
}
function batchRelayMessages(L2ToL1Message[] calldata _messages) external;
}
......@@ -110,6 +110,10 @@ export const makeContractDeployConfig = async (
)
},
},
OVM_L1MultiMessageRelayer: {
factory: getContractFactory('OVM_L1MultiMessageRelayer'),
params: [AddressManager.address],
},
OVM_CanonicalTransactionChain: {
factory: getContractFactory('OVM_CanonicalTransactionChain'),
params: [
......
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS,
DUMMY_BATCH_PROOFS,
toHexString,
} from '../../../helpers'
import { sign } from 'crypto'
describe('OVM_L1MultiMessageRelayer', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
let Factory__OVM_L1MultiMessageRelayer: ContractFactory
let Mock__OVM_L1CrossDomainMessenger: MockContract
let messages: any[]
before(async () => {
// We do all the 'reusable setup' in here, ie. creating factories, mocks and setting addresses
// for everything but the contract under test
AddressManager = await makeAddressManager()
// create a mock for the L1CrossDomainMessenger implementation
Mock__OVM_L1CrossDomainMessenger = await smockit(
await ethers.getContractFactory('OVM_L1CrossDomainMessenger')
)
// set the address of the mock contract to target
await AddressManager.setAddress(
// 'Proxy__OVM_L1CrossDomainMessenger' is the string used by the contract under test to lookup
// the target contract. On mainnet the target is a proxy which points to the implementation of
// the L1CrossDomainMessenger.
// In order to keep the tests simple, we skip the proxy here, and point directly to the impl.
'Proxy__OVM_L1CrossDomainMessenger',
Mock__OVM_L1CrossDomainMessenger.address
)
// set the signer as the address required by access control
await AddressManager.setAddress(
'OVM_L2BatchMessageRelayer',
signer.getAddress()
)
// define a dummy proof to satisfy the abi
let dummyProof = {
stateRoot: NON_NULL_BYTES32,
stateRootBatchHeader: DUMMY_BATCH_HEADERS[0],
stateRootProof: DUMMY_BATCH_PROOFS[0],
stateTrieWitness: toHexString('some bytes'),
storageTrieWitness: toHexString('some more bytes'),
}
// create a few dummy messages to relay
let m1 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 1,
proof: dummyProof,
}
let m2 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 2,
proof: dummyProof,
}
let m3 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 2,
proof: dummyProof,
}
messages = [m1, m2, m3]
})
let OVM_L1MultiMessageRelayer: Contract
beforeEach(async () => {
// setup a factory and deploy a new test-contract for each unit test
Factory__OVM_L1MultiMessageRelayer = await ethers.getContractFactory(
'OVM_L1MultiMessageRelayer'
)
OVM_L1MultiMessageRelayer = await Factory__OVM_L1MultiMessageRelayer.deploy(
AddressManager.address
)
// set the address of the OVM_L1MultiMessageRelayer, which the OVM_L1CrossDomainMessenger will
// check in its onlyRelayer modifier.
// The string currently used in the AddressManager is 'OVM_L2MessageRelayer'
await AddressManager.setAddress(
'OVM_L2MessageRelayer',
OVM_L1MultiMessageRelayer.address
)
// set the mock return value
Mock__OVM_L1CrossDomainMessenger.smocked.relayMessage.will.return()
})
describe('batchRelayMessages', () => {
it('Successfully relay multiple messages', async () => {
await OVM_L1MultiMessageRelayer.batchRelayMessages(messages)
await expect(
Mock__OVM_L1CrossDomainMessenger.smocked.relayMessage.calls.length
).to.deep.equal(messages.length)
})
it('should revert if called by the wrong account', async () => {
// set the wrong address to use for ACL
await AddressManager.setAddress(
'OVM_L2BatchMessageRelayer',
NON_ZERO_ADDRESS
)
await expect(
OVM_L1MultiMessageRelayer.batchRelayMessages(messages)
).to.be.revertedWith(
'OVM_L1MultiMessageRelayer: Function can only be called by the OVM_L2BatchMessageRelayer'
)
})
})
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment