Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma solidity ^0.5.0;

contract ICrossDomainMessenger {
address public crossDomainMsgSender;

/**
* Relays a message to a given target contract.
* @param _target Address of the target contract.
* @param _sender Address of the message sender.
* @param _message Calldata to relay.
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message
) public;

/**
* Sends a message to another cross domain messenger to be relayed.
* @param _target Address of the target contract.
* @param _message Calldata to relay.
*/
function sendMessage(
address _target,
bytes memory _message
) public;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
pragma solidity ^0.5.0;

/* Interface Imports */
import { ICrossDomainMessenger } from "./CrossDomainMessenger.interface.sol";

/**
* @title MockCrossDomainMessenger
*/
contract MockCrossDomainMessenger is ICrossDomainMessenger {
/*
* Contract Variables
*/

ICrossDomainMessenger targetMessenger;
address public crossDomainMsgSender;

/*
* Public Functions
*/

/**
* Relays a message to a target contract.
* .inheritdoc ICrossDomainMessenger
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message
)
public
{
crossDomainMsgSender = _sender;
(bool success,) = _target.call(_message);
require(success, "Received message reverted during execution.");
}

/**
* Sends a message to the target messenger.
* .inheritdoc ICrossDomainMessenger
*/
function sendMessage(
address _target,
bytes memory _message
)
public
{
require(
address(targetMessenger) != address(0),
"Cannot send a message without setting the target messenger."
);

targetMessenger.relayMessage(
_target,
msg.sender,
_message
);
}

/**
* Sets the target messenger.
* @param _messenger Target messenger address.
*/
function setTargetMessenger(
address _messenger
)
public
{
targetMessenger = ICrossDomainMessenger(_messenger);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.5.0;

import { ICrossDomainMessenger} from "../optimistic-ethereum/bridge/CrossDomainMessenger.interface.sol";
import { SimpleStorage } from "./SimpleStorage.sol";

contract CrossDomainSimpleStorage is SimpleStorage {
ICrossDomainMessenger crossDomainMessenger;
address public crossDomainMsgSender;

function setMessenger(address _crossDomainMessengerAddress) public {
crossDomainMessenger = ICrossDomainMessenger(_crossDomainMessengerAddress);
}

function crossDomainSetStorage(bytes32 key, bytes32 value) public {
crossDomainMsgSender = crossDomainMessenger.crossDomainMsgSender();
setStorage(key, value);
}
}
5 changes: 4 additions & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
"test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces",
"coverage": "yarn run coverage:contracts",
"coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"",
"build": "yarn run build:contracts && yarn run build:typescript",
"build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy",
"build:contracts": "buidler compile",
"build:typescript": "tsc -p .",
"build:copy": "yarn run build:copy:contracts",
"build:copy:contracts": "copyfiles -u 2 \"contracts/optimistic-ethereum/**/*.sol\" \"build/contracts\"",
Comment on lines +32 to +33

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this needed for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's useful for being able to import the contracts into other packages

"clean": "rm -rf ./artifacts ./build ./cache",
"lint": "yarn run lint:typescript",
"lint:typescript": "tslint --format stylish --project .",
Expand Down Expand Up @@ -61,6 +63,7 @@
"typescript": "^3.9.5"
},
"devDependencies": {
"copyfiles": "^2.3.0",
"cross-env": "^7.0.2",
"glob": "^7.1.6",
"solidity-coverage": "^0.7.9"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { expect } from '../../setup'

/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { ContractFactory, Contract, Signer } from 'ethers'
import { NULL_ADDRESS } from '@eth-optimism/core-utils'

describe('MockCrossDomainMessenger', () => {
let wallet: Signer
before(async () => {
;[wallet] = await ethers.getSigners()
})

let MockCrossDomainMessengerFactory: ContractFactory
let CrossDomainSimpleStorageFactory: ContractFactory
before(async () => {
MockCrossDomainMessengerFactory = await ethers.getContractFactory(
'MockCrossDomainMessenger'
)
CrossDomainSimpleStorageFactory = await ethers.getContractFactory(
'CrossDomainSimpleStorage'
)
})

let L1MockCrossDomainMessenger: Contract
let L2MockCrossDomainMessenger: Contract
beforeEach(async () => {
L1MockCrossDomainMessenger = await MockCrossDomainMessengerFactory.deploy()
L2MockCrossDomainMessenger = await MockCrossDomainMessengerFactory.deploy()

await L1MockCrossDomainMessenger.setTargetMessenger(
L2MockCrossDomainMessenger.address
)
await L2MockCrossDomainMessenger.setTargetMessenger(
L1MockCrossDomainMessenger.address
)
})

let L2SimpleStorage: Contract
beforeEach(async () => {
L2SimpleStorage = await CrossDomainSimpleStorageFactory.deploy()

await L2SimpleStorage.setMessenger(L2MockCrossDomainMessenger.address)
})
describe('relayMessage', () => {
it('should successfully relay a message to the target receiver', async () => {
const expectedStorageKey = ethers.utils.keccak256('0x1234')
const expectedStorageValue = ethers.utils.keccak256('0x5678')

const calldata = L2SimpleStorage.interface.encodeFunctionData(
'crossDomainSetStorage',
[expectedStorageKey, expectedStorageValue]
)

const expectedMessage = [await wallet.getAddress(), calldata]

await L2MockCrossDomainMessenger.relayMessage(
L2SimpleStorage.address,
...expectedMessage
)

const actualStorageValue = await L2SimpleStorage.getStorage(
expectedStorageKey
)
expect(actualStorageValue).to.equal(expectedStorageValue)
expect(await L2SimpleStorage.crossDomainMsgSender()).to.equal(
await wallet.getAddress()
)
})
})

describe('sendMessage', () => {
it('should successfully send a message to another messenger', async () => {
const expectedStorageKey = ethers.utils.keccak256('0x1234')
const expectedStorageValue = ethers.utils.keccak256('0x5678')

const calldata = L2SimpleStorage.interface.encodeFunctionData(
'crossDomainSetStorage',
[expectedStorageKey, expectedStorageValue]
)

await L1MockCrossDomainMessenger.sendMessage(
L2SimpleStorage.address,
calldata,
{
from: await wallet.getAddress(),
}
)

const currentBlock = await ethers.provider.getBlock('latest')
const expectedMessage = [await wallet.getAddress(), calldata]

const actualStorageValue = await L2SimpleStorage.getStorage(
expectedStorageKey
)
expect(actualStorageValue).to.equal(expectedStorageValue)
expect(await L2SimpleStorage.crossDomainMsgSender()).to.equal(
await wallet.getAddress()
)
})

it('should revert if its target messenger is not set', async () => {
const expectedStorageKey = ethers.utils.keccak256('0x1234')
const expectedStorageValue = ethers.utils.keccak256('0x5678')

const calldata = L2SimpleStorage.interface.encodeFunctionData(
'setStorage',
[expectedStorageKey, expectedStorageValue]
)

await L1MockCrossDomainMessenger.setTargetMessenger(NULL_ADDRESS)

await expect(
L1MockCrossDomainMessenger.sendMessage(
L2SimpleStorage.address,
calldata,
{
from: await wallet.getAddress(),
}
)
).to.be.revertedWith(
'Cannot send a message without setting the target messenger.'
)
})
})
})
Loading