Skip to content

Commit

Permalink
Merge pull request #24 from viatrix/sygma-message-relayer
Browse files Browse the repository at this point in the history
feat: add SygmaMessageRelayer
  • Loading branch information
allemanfredi authored Oct 18, 2023
2 parents 5e3cadb + cd1deb9 commit c1fc772
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 49 deletions.
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);
}
}
38 changes: 38 additions & 0 deletions packages/evm/contracts/adapters/Sygma/SygmaMessageRelayer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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
) 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, "");
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 messageIds,
bytes32[] memory hashes,
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(messageIds, hashes)
);
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 messageIds,
bytes32[] memory hashes
) public view returns (bytes memory) {
bytes memory encoded = abi.encode(address(0), messageIds, hashes);
return this.slice(encoded, 32);
}
}
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 @@ -4,6 +4,8 @@ pragma solidity ^0.8.17;
import "../interfaces/IBridge.sol";

contract MockSygmaBridge {
error CallReverted();

event Deposit(
uint8 destinationDomainID,
bytes32 resourceID,
Expand All @@ -18,7 +20,59 @@ 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);

bool success = _executeProposal(resourceID, depositData);

if (!success) revert CallReverted();

return (1, bytes("2"));
}

function _executeProposal(bytes32 resourceID, bytes calldata data) internal returns (bool success) {
uint16 lenExecuteFuncSignature;
bytes4 executeFuncSignature;
uint8 lenExecuteContractAddress;
address executeContractAddress;
uint8 lenExecutionDataDepositor;
address executionDataDepositor;
bytes memory executionData;

lenExecuteFuncSignature = uint16(bytes2(data[32:34]));
executeFuncSignature = bytes4(data[34:34 + lenExecuteFuncSignature]);
lenExecuteContractAddress = uint8(bytes1(data[34 + lenExecuteFuncSignature:35 + lenExecuteFuncSignature]));
executeContractAddress = address(
uint160(
bytes20(data[35 + lenExecuteFuncSignature:35 + lenExecuteFuncSignature + lenExecuteContractAddress])
)
);
lenExecutionDataDepositor = uint8(
bytes1(
data[35 + lenExecuteFuncSignature + lenExecuteContractAddress:36 +
lenExecuteFuncSignature +
lenExecuteContractAddress]
)
);
executionDataDepositor = address(
uint160(
bytes20(
data[36 + lenExecuteFuncSignature + lenExecuteContractAddress:36 +
lenExecuteFuncSignature +
lenExecuteContractAddress +
lenExecutionDataDepositor]
)
)
);
executionData = bytes(
data[36 + lenExecuteFuncSignature + lenExecuteContractAddress + lenExecutionDataDepositor:]
);

bytes memory callData = abi.encodePacked(
executeFuncSignature,
abi.encode(executionDataDepositor),
executionData
);
(success, ) = executeContractAddress.call(callData);
}
}
120 changes: 120 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,120 @@
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 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")
const SygmaAdapter = await ethers.getContractFactory("SygmaAdapter")
const sygmaAdapter = await SygmaAdapter.deploy(sygmaBridge.address)
// IBridge bridge, HeaderStorage headerStorage, bytes32 resourceID, uint8 defaultDestinationDomainID, address defaultSygmaAdapter
const sygmaMessageRelayer = await SygmaMessageRelayer.deploy(
sygmaBridge.address,
yaho.address,
resourceID,
DOMAIN_ID,
sygmaAdapter.address,
)

await sygmaAdapter.setReporter(sygmaMessageRelayer.address, DOMAIN_ID, true)

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,
sygmaAdapter,
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")
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, sygmaAdapter, 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(sygmaAdapter.address)
})
})

describe("relayMessages()", function () {
it("Relays messages to Sygma to default domain", async function () {
const { sender, sygmaMessageRelayer, sygmaAdapter, 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],
sygmaAdapter.address,
)
expect(await sygmaMessageRelayer.callStatic.relayMessages([0, 1], sygmaAdapter.address)).to.equal(
"0x0000000000000000000000000000000000000000000000000000000000000001",
)
await expect(sygmaMessageRelayer.relayMessages([0, 1], sygmaAdapter.address))
.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")
})
})
})
Loading

0 comments on commit c1fc772

Please sign in to comment.