Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SygmaMessageRelayer #24

Merged
merged 5 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
55 changes: 8 additions & 47 deletions packages/evm/contracts/adapters/Sygma/SygmaHeaderReporter.sol
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import "./interfaces/IBridge.sol";
import "./interfaces/ISygmaAdapter.sol";
import "../../utils/HeaderStorage.sol";
import { SygmaReporter } from "./SygmaReporter.sol";
import { HeaderStorage } from "../../utils/HeaderStorage.sol";

contract SygmaHeaderReporter {
IBridge public immutable _bridge;
contract SygmaHeaderReporter is SygmaReporter {
HeaderStorage public immutable _headerStorage;
bytes32 public immutable _resourceID;
uint8 public immutable _defaultDestinationDomainID;
address public immutable _defaultSygmaAdapter;

event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader);

constructor(
IBridge bridge,
address bridge,
HeaderStorage headerStorage,
bytes32 resourceID,
uint8 defaultDestinationDomainID,
address defaultSygmaAdapter
) {
_bridge = bridge;
) SygmaReporter(bridge, resourceID, defaultDestinationDomainID, defaultSygmaAdapter) {
_headerStorage = headerStorage;
_resourceID = resourceID;
_defaultDestinationDomainID = defaultDestinationDomainID;
_defaultSygmaAdapter = defaultSygmaAdapter;
}

/**
Expand All @@ -48,7 +39,7 @@ contract SygmaHeaderReporter {
uint256[] memory blockNumbers,
address sygmaAdapter,
uint8 destinationDomainID,
bytes calldata feeData
bytes memory feeData
) public payable {
_reportHeaders(blockNumbers, sygmaAdapter, destinationDomainID, feeData);
}
Expand All @@ -57,42 +48,12 @@ contract SygmaHeaderReporter {
uint256[] memory blockNumbers,
address sygmaAdapter,
uint8 destinationDomainID,
bytes calldata feeData
bytes memory feeData
) internal {
bytes32[] memory blockHeaders = _headerStorage.storeBlockHeaders(blockNumbers);
bytes memory depositData = abi.encodePacked(
// uint256 maxFee
uint256(0),
// uint16 len(executeFuncSignature)
uint16(4),
// bytes executeFuncSignature
ISygmaAdapter(address(0)).storeHashes.selector,
// uint8 len(executeContractAddress)
uint8(20),
// bytes executeContractAddress
sygmaAdapter,
// uint8 len(executionDataDepositor)
uint8(20),
// bytes executionDataDepositor
address(this),
// bytes executionDataDepositor + executionData
prepareDepositData(blockNumbers, blockHeaders)
);
IBridge(_bridge).deposit{ value: msg.value }(destinationDomainID, _resourceID, depositData, feeData);
_reportData(blockNumbers, blockHeaders, sygmaAdapter, destinationDomainID, feeData);
for (uint i = 0; i < blockNumbers.length; i++) {
emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]);
}
}

function slice(bytes calldata input, uint256 position) public pure returns (bytes memory) {
return input[position:];
}

function prepareDepositData(
uint256[] memory blockNumbers,
bytes32[] memory blockHeaders
) public view returns (bytes memory) {
bytes memory encoded = abi.encode(address(0), blockNumbers, blockHeaders);
return this.slice(encoded, 32);
}
}
39 changes: 39 additions & 0 deletions packages/evm/contracts/adapters/Sygma/SygmaMessageRelayer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { IMessageRelay } from "../../interfaces/IMessageRelay.sol";
import { SygmaReporter } from "./SygmaReporter.sol";
import { Yaho } from "../../Yaho.sol";

contract SygmaMessageRelayer is SygmaReporter, IMessageRelay {
Yaho public immutable _yaho;

event MessageRelayed(address indexed emitter, uint256 indexed messageId);

constructor(
address bridge,
Yaho yaho,
bytes32 resourceID,
uint8 defaultDestinationDomainID,
address defaultSygmaAdapter,
bytes memory defaultFeeData
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
) SygmaReporter(bridge, resourceID, defaultDestinationDomainID, defaultSygmaAdapter) {
_yaho = yaho;
}

/**
@dev Relays the messages via the Sygma bridge to default domain.
@param messageIds IDs of the messages to pass over the Sygma bridge.
@param sygmaAdapter Address of the Sygma adapter on the target chain.
*/
function relayMessages(uint256[] memory messageIds, address sygmaAdapter) public payable returns (bytes32) {
bytes32[] memory hashes = new bytes32[](messageIds.length);
for (uint256 i = 0; i < messageIds.length; i++) {
uint256 id = messageIds[i];
hashes[i] = _yaho.hashes(id);
emit MessageRelayed(address(this), messageIds[i]);
}
(uint64 depositNonce, ) = _reportData(messageIds, hashes, sygmaAdapter, _defaultDestinationDomainID, "");
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
return bytes32(uint256(depositNonce));
}
}
59 changes: 59 additions & 0 deletions packages/evm/contracts/adapters/Sygma/SygmaReporter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import "./interfaces/ISygmaAdapter.sol";
import "./interfaces/IBridge.sol";

contract SygmaReporter {
address public immutable _bridge;
bytes32 public immutable _resourceID;
uint8 public immutable _defaultDestinationDomainID;
address public immutable _defaultSygmaAdapter;

constructor(address bridge, bytes32 resourceID, uint8 defaultDestinationDomainID, address defaultSygmaAdapter) {
_bridge = bridge;
_resourceID = resourceID;
_defaultDestinationDomainID = defaultDestinationDomainID;
_defaultSygmaAdapter = defaultSygmaAdapter;
}

function _reportData(
uint256[] memory ids,
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
bytes32[] memory items,
address sygmaAdapter,
uint8 destinationDomainID,
bytes memory feeData
) internal returns (uint64 depositNonce, bytes memory handlerResponse) {
bytes memory depositData = abi.encodePacked(
// uint256 maxFee
uint256(0),
// uint16 len(executeFuncSignature)
uint16(4),
// bytes executeFuncSignature
ISygmaAdapter(address(0)).storeHashes.selector,
// uint8 len(executeContractAddress)
uint8(20),
// bytes executeContractAddress
sygmaAdapter,
// uint8 len(executionDataDepositor)
uint8(20),
// bytes executionDataDepositor
address(this),
// bytes executionDataDepositor + executionData
prepareDepositData(ids, items)
);
return IBridge(_bridge).deposit{ value: msg.value }(destinationDomainID, _resourceID, depositData, feeData);
}

function slice(bytes calldata input, uint256 position) public pure returns (bytes memory) {
return input[position:];
}

function prepareDepositData(
uint256[] memory blockNumbers,
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
bytes32[] memory blockHeaders
) public view returns (bytes memory) {
bytes memory encoded = abi.encode(address(0), blockNumbers, blockHeaders);
return this.slice(encoded, 32);
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ interface IBridge {
bytes32 resourceID,
bytes calldata depositData,
bytes calldata feeData
) external payable;
) external payable returns (uint64 depositNonce, bytes memory handlerResponse);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ contract MockSygmaBridge {
bytes32 resourceID,
bytes calldata depositData,
bytes calldata feeData
) external payable {
) external payable returns (uint64 depositNonce, bytes memory handlerResponse) {
emit Deposit(destinationDomainID, resourceID, 1, msg.sender, depositData, feeData);
return (1, bytes("2"));
}
}
110 changes: 110 additions & 0 deletions packages/evm/test/adapters/Sygma/03_SygmaMessageRelayer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { expect } from "chai"
import { ethers, network } from "hardhat"

const DOMAIN_ID = 5
const resourceID = "0x0000000000000000000000000000000000000000000000000000000000000500"

const setup = async () => {
await network.provider.request({ method: "hardhat_reset", params: [] })
const signers = await ethers.getSigners()
const sender = signers[0].address
const adapter = signers[1].address
const otherAddress = signers[2].address
const Yaho = await ethers.getContractFactory("Yaho")
const yaho = await Yaho.deploy()
const SygmaBridge = await ethers.getContractFactory("MockSygmaBridge")
const sygmaBridge = await SygmaBridge.deploy()
const SygmaMessageRelayer = await ethers.getContractFactory("SygmaMessageRelayer")
// IBridge bridge, HeaderStorage headerStorage, bytes32 resourceID, uint8 defaultDestinationDomainID, address defaultSygmaAdapter
const sygmaMessageRelayer = await SygmaMessageRelayer.deploy(
sygmaBridge.address,
yaho.address,
resourceID,
DOMAIN_ID,
adapter,
"0x00",
)
const PingPong = await ethers.getContractFactory("PingPong")
const pingPong = await PingPong.deploy()
const message_1 = {
to: pingPong.address,
toChainId: 1,
data: pingPong.interface.getSighash("ping"),
}
await yaho.dispatchMessages([message_1, message_1])
// await mine(10)
return {
sender,
adapter,
otherAddress,
yaho,
sygmaBridge,
sygmaMessageRelayer,
pingPong,
message_1,
}
}

const prepareDepositData = async (reporterAddress: string, ids: string[], hashes: string[], adapter: string) => {
const abiCoder = ethers.utils.defaultAbiCoder
const executionData = abiCoder
.encode(["address", "uint256[]", "bytes32[]"], [ethers.constants.AddressZero, ids, hashes])
.substring(66)

const SygmaAdapter = await ethers.getContractFactory("SygmaAdapter")
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
const functionSig = SygmaAdapter.interface.getSighash("storeHashes")

// bytes memory depositData = abi.encodePacked(
// uint256(0),
// uint16(4),
// IDepositAdapterTarget(address(0)).execute.selector,
// uint8(20),
// _targetDepositAdapter,
// uint8(20),
// _depositorAddress,
// abi.encode(depositContractCalldata)
// );

const depositData =
ethers.utils.hexZeroPad("0x0", 32) +
"0004" +
functionSig.substring(2) +
"14" +
adapter.toLowerCase().substring(2) +
"14" +
reporterAddress.toLowerCase().substring(2) +
executionData
return depositData
}

describe("SygmaMessageRelayer", function () {
describe("Deploy", function () {
it("Successfully deploys contract", async function () {
const { sygmaBridge, yaho, adapter, sygmaMessageRelayer } = await setup()
expect(await sygmaMessageRelayer.deployed())
expect(await sygmaMessageRelayer._bridge()).to.equal(sygmaBridge.address)
expect(await sygmaMessageRelayer._yaho()).to.equal(yaho.address)
expect(await sygmaMessageRelayer._resourceID()).to.equal(resourceID)
expect(await sygmaMessageRelayer._defaultDestinationDomainID()).to.equal(DOMAIN_ID)
expect(await sygmaMessageRelayer._defaultSygmaAdapter()).to.equal(adapter)
})
})

describe("relayMessages()", function () {
it("Relays messages to Sygma to default domain", async function () {
const { sender, sygmaMessageRelayer, adapter, sygmaBridge, yaho, message_1 } = await setup()
const hash0 = await yaho.calculateHash(network.config.chainId, 0, yaho.address, sender, message_1)
const hash1 = await yaho.calculateHash(network.config.chainId, 1, yaho.address, sender, message_1)
const depositData = await prepareDepositData(sygmaMessageRelayer.address, ["0", "1"], [hash0, hash1], adapter)
await expect(sygmaMessageRelayer.callStatic.relayMessages([0, 1], adapter)).to.equal(2)
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
await expect(sygmaMessageRelayer.relayMessages([0, 1], adapter))
.to.emit(sygmaMessageRelayer, "MessageRelayed")
.withArgs(sygmaMessageRelayer.address, 0)
.and.to.emit(sygmaMessageRelayer, "MessageRelayed")
.withArgs(sygmaMessageRelayer.address, 1)
.and.to.emit(sygmaBridge, "Deposit")
// (destinationDomainID, resourceID, 1, msg.sender, depositData, feeData);
.withArgs(DOMAIN_ID, resourceID, 1, sygmaMessageRelayer.address, depositData, "0x")
})
})
allemanfredi marked this conversation as resolved.
Show resolved Hide resolved
})
Loading