diff --git a/packages/evm/contracts/adapters/AMB/AMBAdapter.sol b/packages/evm/contracts/adapters/AMB/AMBAdapter.sol index 4b0fe473..aa532aa7 100644 --- a/packages/evm/contracts/adapters/AMB/AMBAdapter.sol +++ b/packages/evm/contracts/adapters/AMB/AMBAdapter.sol @@ -2,42 +2,37 @@ pragma solidity ^0.8.17; import { IAMB } from "./IAMB.sol"; -import { OracleAdapter } from "../OracleAdapter.sol"; import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract AMBAdapter is OracleAdapter, BlockHashOracleAdapter { - IAMB public amb; - address public reporter; - bytes32 public chainId; +contract AMBAdapter is BlockHashOracleAdapter { + string public constant PROVIDER = "amb"; - error ArrayLengthMissmatch(address emitter); - error UnauthorizedAMB(address emitter, address sender); - error UnauthorizedChainId(address emitter, bytes32 chainId); - error UnauthorizedHashReporter(address emitter, address reporter); + IAMB public immutable AMB; + address public immutable REPORTER; + bytes32 public immutable SOURCE_CHAIN_ID; - constructor(IAMB _amb, address _reporter, bytes32 _chainId) { - amb = _amb; - reporter = _reporter; - chainId = _chainId; + error ArrayLengthMissmatch(); + error UnauthorizedAMB(address sender, address expectedSender); + error UnauthorizedChainId(bytes32 sourceChainId, bytes32 expectedSourceChainId); + error UnauthorizedHashReporter(address reporter, address expectedReporter); + + constructor(address amb, address reporter, bytes32 sourceChainId) { + AMB = IAMB(amb); + REPORTER = reporter; + SOURCE_CHAIN_ID = sourceChainId; } - /// @dev Check that the amb, chainId, and owner are valid. modifier onlyValid() { - if (msg.sender != address(amb)) revert UnauthorizedAMB(address(this), msg.sender); - if (amb.messageSourceChainId() != chainId) revert UnauthorizedChainId(address(this), chainId); - if (amb.messageSender() != reporter) revert UnauthorizedHashReporter(address(this), reporter); + bytes32 ambSourceChainId = AMB.messageSourceChainId(); + address ambMessageSender = AMB.messageSender(); + if (msg.sender != address(AMB)) revert UnauthorizedAMB(msg.sender, address(AMB)); + if (ambSourceChainId != SOURCE_CHAIN_ID) revert UnauthorizedChainId(ambSourceChainId, SOURCE_CHAIN_ID); + if (ambMessageSender != REPORTER) revert UnauthorizedHashReporter(ambMessageSender, REPORTER); _; } - /// @dev Stores the hashes for a given array of idss. - /// @param ids Array of ids number for which to set the hashes. - /// @param _hashes Array of hashes to set for the given ids. - /// @notice Only callable by `amb` with a message passed from `reporter. - /// @notice Will revert if given array lengths do not match. function storeHashes(uint256[] memory ids, bytes32[] memory _hashes) public onlyValid { - if (ids.length != _hashes.length) revert ArrayLengthMissmatch(address(this)); - for (uint256 i = 0; i < ids.length; i++) { - _storeHash(uint256(chainId), ids[i], _hashes[i]); - } + if (ids.length != _hashes.length) revert ArrayLengthMissmatch(); + _storeHashes(uint256(SOURCE_CHAIN_ID), ids, _hashes); } } diff --git a/packages/evm/contracts/adapters/AMB/AMBHeaderReporter.sol b/packages/evm/contracts/adapters/AMB/AMBHeaderReporter.sol deleted file mode 100644 index 773df949..00000000 --- a/packages/evm/contracts/adapters/AMB/AMBHeaderReporter.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderStorage } from "../../utils/HeaderStorage.sol"; -import { IAMB } from "./IAMB.sol"; -import { AMBAdapter } from "./AMBAdapter.sol"; - -contract AMBHeaderReporter { - IAMB public immutable amb; - HeaderStorage public immutable headerStorage; - - event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader); - - constructor(IAMB _amb, HeaderStorage _headerStorage) { - amb = _amb; - headerStorage = _headerStorage; - } - - /// @dev Reports the given block headers to the oracleAdapter via the AMB. - /// @param blockNumbers Uint256 array of block number to pass over the AMB. - /// @param ambAdapter Address of the oracle adapter to pass the header to over the AMB. - /// @param receipt Bytes32 receipt for the transaction. - function reportHeaders( - uint256[] memory blockNumbers, - address ambAdapter, - uint256 gas - ) public returns (bytes32 receipt) { - bytes32[] memory blockHeaders = headerStorage.storeBlockHeaders(blockNumbers); - bytes memory data = abi.encodeCall(AMBAdapter.storeHashes, (blockNumbers, blockHeaders)); - receipt = amb.requireToPassMessage(ambAdapter, data, gas); - for (uint256 i = 0; i < blockNumbers.length; i++) { - emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]); - } - } -} diff --git a/packages/evm/contracts/adapters/AMB/AMBMessageRelayer.sol b/packages/evm/contracts/adapters/AMB/AMBMessageRelayer.sol deleted file mode 100644 index a2a4ed79..00000000 --- a/packages/evm/contracts/adapters/AMB/AMBMessageRelayer.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { IMessageRelay } from "../../interfaces/IMessageRelay.sol"; -import { IAMB } from "./IAMB.sol"; -import { Yaho } from "../../Yaho.sol"; -import { AMBAdapter } from "./AMBAdapter.sol"; - -contract AMBMessageRelay is IMessageRelay { - IAMB public immutable amb; - Yaho public immutable yaho; - - event MessageRelayed(address indexed emitter, uint256 indexed messageId); - - constructor(IAMB _amb, Yaho _yaho) { - amb = _amb; - yaho = _yaho; - } - - function relayMessages(uint256[] memory messageIds, address ambAdapter) public payable returns (bytes32 receipt) { - 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]); - } - bytes memory data = abi.encodeCall(AMBAdapter.storeHashes, (messageIds, hashes)); - receipt = amb.requireToPassMessage(ambAdapter, data, 0); - } -} diff --git a/packages/evm/contracts/adapters/AMB/AMBReporter.sol b/packages/evm/contracts/adapters/AMB/AMBReporter.sol new file mode 100644 index 00000000..6380b9ca --- /dev/null +++ b/packages/evm/contracts/adapters/AMB/AMBReporter.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { Reporter } from "../Reporter.sol"; +import { AMBAdapter } from "./AMBAdapter.sol"; +import { IOracleAdapter } from "../../interfaces/IOracleAdapter.sol"; +import { IAMB } from "./IAMB.sol"; + +contract AMBReporter is Reporter { + string public constant PROVIDER = "amb"; + + address public immutable AMB; + uint256 public immutable GAS; + uint256 public immutable TO_CHAIN_ID; + + error InvalidToChainId(uint256 chainId, uint256 expectedChainId); + + constructor( + address headerStorage, + address yaho, + address amb, + uint256 toChainId, + uint256 gas + ) Reporter(headerStorage, yaho) { + AMB = amb; + TO_CHAIN_ID = toChainId; + GAS = gas; + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + if (toChainId != TO_CHAIN_ID) revert InvalidToChainId(toChainId, TO_CHAIN_ID); + bytes memory payload = abi.encodeCall(AMBAdapter.storeHashes, (ids, hashes)); + return IAMB(AMB).requireToPassMessage(adapter, payload, GAS); + } +} diff --git a/packages/evm/contracts/adapters/Axelar/AxelarAdapter.sol b/packages/evm/contracts/adapters/Axelar/AxelarAdapter.sol index 61ae424c..951d7c85 100644 --- a/packages/evm/contracts/adapters/Axelar/AxelarAdapter.sol +++ b/packages/evm/contracts/adapters/Axelar/AxelarAdapter.sol @@ -1,30 +1,32 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { AxelarExecutable } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract AxelarAdapter is HeaderOracleAdapter, AxelarExecutable { +contract AxelarAdapter is BlockHashOracleAdapter, Ownable, AxelarExecutable { string public constant PROVIDER = "axelar"; - string public AXELAR_REPORTER_CHAIN; // Immutable - bytes32 public immutable AXELAR_REPORTER_CHAIN_HASH; - string public AXELAR_REPORTER_ADDRESS; // Immutable - bytes32 public immutable AXELAR_REPORTER_ADDRESS_HASH; + + mapping(bytes32 => bytes32) public enabledReporters; + mapping(bytes32 => uint256) public chainIds; error UnauthorizedAxelarReceive(); error ExecutionWithTokenNotSupported(); - constructor( - uint256 reporterChain, - address reporterAddress, - address axelarGateway, - string memory axelarReporterChain, - string memory axelarReporterAddress - ) HeaderOracleAdapter(reporterChain, reporterAddress) AxelarExecutable(axelarGateway) { - AXELAR_REPORTER_CHAIN = axelarReporterChain; - AXELAR_REPORTER_CHAIN_HASH = keccak256(bytes(axelarReporterChain)); - AXELAR_REPORTER_ADDRESS = axelarReporterAddress; - AXELAR_REPORTER_ADDRESS_HASH = keccak256(bytes(axelarReporterAddress)); + event ReporterSet(uint256 indexed chainId, string name, string indexed reporter); + + constructor(address axelarGateway) AxelarExecutable(axelarGateway) {} + + function setReporterByChain( + uint256 chainId, + string calldata chainName, + string calldata reporter + ) external onlyOwner { + bytes32 chainNameHash = keccak256(bytes(chainName)); + enabledReporters[chainNameHash] = keccak256(bytes(reporter)); + chainIds[chainNameHash] = chainId; + emit ReporterSet(chainId, chainName, reporter); } function _execute( @@ -32,13 +34,14 @@ contract AxelarAdapter is HeaderOracleAdapter, AxelarExecutable { string calldata sourceAddress, bytes calldata payload ) internal override { - if ( - keccak256(bytes(sourceChain)) != AXELAR_REPORTER_CHAIN_HASH || - keccak256(bytes(sourceAddress)) != AXELAR_REPORTER_ADDRESS_HASH - ) { + bytes32 chainNameHash = keccak256(bytes(sourceChain)); + bytes32 expectedSourceAddressHash = enabledReporters[chainNameHash]; + uint256 sourceChainId = chainIds[chainNameHash]; + if (expectedSourceAddressHash != keccak256(bytes(sourceAddress)) || sourceChainId == 0) { revert UnauthorizedAxelarReceive(); } - _receivePayload(payload); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(payload, (uint256[], bytes32[])); + _storeHashes(sourceChainId, ids, hashes); } function _executeWithToken( diff --git a/packages/evm/contracts/adapters/Axelar/AxelarHeaderReporter.sol b/packages/evm/contracts/adapters/Axelar/AxelarHeaderReporter.sol deleted file mode 100644 index aebf04ae..00000000 --- a/packages/evm/contracts/adapters/Axelar/AxelarHeaderReporter.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { AxelarReporter } from "./AxelarReporter.sol"; - -contract AxelarHeaderReporter is HeaderReporter, AxelarReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address axelarGateway, - address axelarGasService, - string memory axelarAdapterChain - ) HeaderReporter(headerStorage, adapterChain) AxelarReporter(axelarGateway, axelarGasService, axelarAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _axelarSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Axelar/AxelarMessageRelay.sol b/packages/evm/contracts/adapters/Axelar/AxelarMessageRelay.sol deleted file mode 100644 index 82044c6c..00000000 --- a/packages/evm/contracts/adapters/Axelar/AxelarMessageRelay.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { AxelarReporter } from "./AxelarReporter.sol"; - -contract AxelarMessageRelay is MessageRelay, AxelarReporter { - constructor( - address yaho, - uint256 adapterChain, - address axelarGateway, - address axelarGasService, - string memory axelarAdapterChain - ) MessageRelay(yaho, adapterChain) AxelarReporter(axelarGateway, axelarGasService, axelarAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _axelarSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Axelar/AxelarReporter.sol b/packages/evm/contracts/adapters/Axelar/AxelarReporter.sol index 5f7a3c04..87a31647 100644 --- a/packages/evm/contracts/adapters/Axelar/AxelarReporter.sol +++ b/packages/evm/contracts/adapters/Axelar/AxelarReporter.sol @@ -1,37 +1,64 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IAxelarGateway } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import { IAxelarGasService } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract AxelarReporter { +contract AxelarReporter is Reporter, Ownable { using Strings for uint256; string public constant PROVIDER = "axelar"; + bytes32 private constant NULL_STRING = keccak256(""); IAxelarGateway public immutable AXELAR_GATEWAY; IAxelarGasService public immutable AXELAR_GAS_SERVICE; - string public AXELAR_ADAPTER_CHAIN; // Immutable - constructor(address axelarGateway, address axelarGasService, string memory axelarAdapterChain) { + mapping(uint256 => string) public chainNames; + + error ChainIdNotSupported(uint256 chainId); + + event ChainNameSet(uint256 indexed chainId, string indexed chainName); + + constructor( + address headerStorage, + address yaho, + address axelarGateway, + address axelarGasService + ) Reporter(headerStorage, yaho) { AXELAR_GATEWAY = IAxelarGateway(axelarGateway); AXELAR_GAS_SERVICE = IAxelarGasService(axelarGasService); - AXELAR_ADAPTER_CHAIN = axelarAdapterChain; } - function _axelarSend(bytes memory payload, address adapter) internal { + function setChainNameByChainId(uint256 chainId, string calldata chainName) external onlyOwner { + chainNames[chainId] = chainName; + emit ChainNameSet(chainId, chainName); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + string memory chainName = chainNames[toChainId]; + if (keccak256(abi.encode(chainName)) == NULL_STRING) revert ChainIdNotSupported(toChainId); + string memory sAdapter = uint256(uint160(adapter)).toHexString(20); + bytes memory payload = abi.encode(ids, hashes); if (msg.value > 0) { AXELAR_GAS_SERVICE.payNativeGasForContractCall{ value: msg.value }( address(this), - AXELAR_ADAPTER_CHAIN, + chainName, sAdapter, payload, msg.sender ); } - AXELAR_GATEWAY.callContract(AXELAR_ADAPTER_CHAIN, sAdapter, payload); + AXELAR_GATEWAY.callContract(chainName, sAdapter, payload); + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/BlockHashOracleAdapter.sol b/packages/evm/contracts/adapters/BlockHashOracleAdapter.sol index c324de4e..ae3fe0b0 100644 --- a/packages/evm/contracts/adapters/BlockHashOracleAdapter.sol +++ b/packages/evm/contracts/adapters/BlockHashOracleAdapter.sol @@ -2,16 +2,13 @@ pragma solidity ^0.8.17; import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol"; - import { OracleAdapter } from "./OracleAdapter.sol"; +import { IBlockHashOracleAdapter } from "../interfaces/IBlockHashOracleAdapter.sol"; -abstract contract BlockHashOracleAdapter is OracleAdapter { +abstract contract BlockHashOracleAdapter is IBlockHashOracleAdapter, OracleAdapter { using RLPReader for RLPReader.RLPItem; - /// @dev Proves and stores valid ancestral block hashes for a given chain ID. - /// @param chainId The ID of the chain to prove block hashes for. - /// @param blockHeaders The RLP encoded block headers to prove the hashes for. - /// @notice Block headers should be ordered by descending block number and should start with a known block header. + /// @inheritdoc IBlockHashOracleAdapter function proveAncestralBlockHashes(uint256 chainId, bytes[] memory blockHeaders) external { for (uint256 i = 0; i < blockHeaders.length; i++) { RLPReader.RLPItem memory blockHeaderRLP = RLPReader.toRlpItem(blockHeaders[i]); @@ -28,7 +25,7 @@ abstract contract BlockHashOracleAdapter is OracleAdapter { uint256 blockNumber = uint256(blockHeaderContent[8].toUint()); bytes32 reportedBlockHash = keccak256(blockHeaders[i]); - bytes32 storedBlockHash = hashes[chainId][blockNumber]; + bytes32 storedBlockHash = getHashFromOracle(chainId, blockNumber); if (reportedBlockHash != storedBlockHash) revert ConflictingBlockHeader(blockNumber, reportedBlockHash, storedBlockHash); diff --git a/packages/evm/contracts/adapters/Celer/CelerAdapter.sol b/packages/evm/contracts/adapters/Celer/CelerAdapter.sol index a6c8a309..fa0d6f5f 100644 --- a/packages/evm/contracts/adapters/Celer/CelerAdapter.sol +++ b/packages/evm/contracts/adapters/Celer/CelerAdapter.sol @@ -1,24 +1,22 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMessageReceiverApp } from "./interfaces/IMessageReceiverApp.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract CelerAdapter is HeaderOracleAdapter, IMessageReceiverApp { +contract CelerAdapter is BlockHashOracleAdapter, Ownable, IMessageReceiverApp { string public constant PROVIDER = "celer"; address public immutable CELER_BUS; - uint32 public immutable CELER_REPORTER_CHAIN; + + mapping(uint64 => address) public enabledReporters; error UnauthorizedCelerReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address celerBus, - uint32 celerReporterChain - ) HeaderOracleAdapter(reporterChain, reporterAddress) { + event ReporterSet(uint64 indexed chainId, address indexed reporter); + + constructor(address celerBus) { CELER_BUS = celerBus; - CELER_REPORTER_CHAIN = celerReporterChain; } function executeMessage( @@ -27,10 +25,15 @@ contract CelerAdapter is HeaderOracleAdapter, IMessageReceiverApp { bytes calldata message, address /* executor */ ) external payable returns (ExecutionStatus) { - if (msg.sender != CELER_BUS || srcChainId != CELER_REPORTER_CHAIN || sender != REPORTER_ADDRESS) - revert UnauthorizedCelerReceive(); - - _receivePayload(message); + address expectedReporter = enabledReporters[srcChainId]; + if (msg.sender != CELER_BUS || sender != expectedReporter) revert UnauthorizedCelerReceive(); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message, (uint256[], bytes32[])); + _storeHashes(srcChainId, ids, hashes); return ExecutionStatus.Success; } + + function setReporterByChainId(uint64 chainId, address reporter) external onlyOwner { + enabledReporters[chainId] = reporter; + emit ReporterSet(chainId, reporter); + } } diff --git a/packages/evm/contracts/adapters/Celer/CelerHeaderReporter.sol b/packages/evm/contracts/adapters/Celer/CelerHeaderReporter.sol deleted file mode 100644 index d4e8cb1e..00000000 --- a/packages/evm/contracts/adapters/Celer/CelerHeaderReporter.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { CelerReporter } from "./CelerReporter.sol"; - -contract CelerHeaderReporter is HeaderReporter, CelerReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address celerBus, - uint32 celerAdapterChain - ) HeaderReporter(headerStorage, adapterChain) CelerReporter(celerBus, celerAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal virtual override { - _celerSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Celer/CelerMessageRelay.sol b/packages/evm/contracts/adapters/Celer/CelerMessageRelay.sol deleted file mode 100644 index f46eaa30..00000000 --- a/packages/evm/contracts/adapters/Celer/CelerMessageRelay.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { CelerReporter } from "./CelerReporter.sol"; - -contract CelerMessageRelay is MessageRelay, CelerReporter { - constructor( - address yaho, - uint256 adapterChain, - address celerBus, - uint32 celerAdapterChain - ) MessageRelay(yaho, adapterChain) CelerReporter(celerBus, celerAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal virtual override { - _celerSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Celer/CelerReporter.sol b/packages/evm/contracts/adapters/Celer/CelerReporter.sol index 1ca6f7bf..93cfda79 100644 --- a/packages/evm/contracts/adapters/Celer/CelerReporter.sol +++ b/packages/evm/contracts/adapters/Celer/CelerReporter.sol @@ -1,19 +1,25 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Reporter } from "../Reporter.sol"; import { IMessageBus } from "./interfaces/IMessageBus.sol"; -abstract contract CelerReporter { +contract CelerReporter is Reporter { string public constant PROVIDER = "celer"; IMessageBus public immutable CELER_BUS; - uint64 public immutable CELER_ADAPTER_CHAIN; - constructor(address celerBus, uint32 celerAdapterChain) { + constructor(address headerStorage, address yaho, address celerBus) Reporter(headerStorage, yaho) { CELER_BUS = IMessageBus(celerBus); - CELER_ADAPTER_CHAIN = celerAdapterChain; } - function _celerSend(bytes memory payload, address adapter) internal { - CELER_BUS.sendMessage{ value: msg.value }(adapter, CELER_ADAPTER_CHAIN, payload); + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + bytes memory payload = abi.encode(ids, hashes); + CELER_BUS.sendMessage{ value: msg.value }(adapter, toChainId, payload); + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/Chainlink/CCIPAdapter.sol b/packages/evm/contracts/adapters/Chainlink/CCIPAdapter.sol index a7a4f9c0..a31cc642 100644 --- a/packages/evm/contracts/adapters/Chainlink/CCIPAdapter.sol +++ b/packages/evm/contracts/adapters/Chainlink/CCIPAdapter.sol @@ -1,31 +1,35 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { CCIPReceiver } from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract CCIPAdapter is HeaderOracleAdapter, CCIPReceiver { +contract CCIPAdapter is BlockHashOracleAdapter, Ownable, CCIPReceiver { string public constant PROVIDER = "ccip"; - uint64 public immutable CCIP_REPORTER_CHAIN; + + mapping(uint64 => address) public enabledReporters; + mapping(uint64 => uint256) public chainIds; error UnauthorizedCCIPReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address ccipRouter, - uint64 ccipReporterChain - ) HeaderOracleAdapter(reporterChain, reporterAddress) CCIPReceiver(ccipRouter) { - CCIP_REPORTER_CHAIN = ccipReporterChain; + event ReporterSet(uint256 indexed chainId, uint64 indexed chainSelector, address indexed reporter); + + constructor(address ccipRouter) CCIPReceiver(ccipRouter) {} + + function setReporterByChain(uint256 chainId, uint64 chainSelector, address reporter) external onlyOwner { + enabledReporters[chainSelector] = reporter; + chainIds[chainSelector] = chainId; + emit ReporterSet(chainId, chainSelector, reporter); } function _ccipReceive(Client.Any2EVMMessage memory message) internal override { - // Validity of `msg.sender` is ensured by `CCIPReceiver` prior this internal function invocation - if ( - message.sourceChainSelector != CCIP_REPORTER_CHAIN || - abi.decode(message.sender, (address)) != REPORTER_ADDRESS - ) revert UnauthorizedCCIPReceive(); - _receivePayload(message.data); + // NOTE: validity of `msg.sender` is ensured by `CCIPReceiver` prior this internal function invocation + address sender = abi.decode(message.sender, (address)); + if (enabledReporters[message.sourceChainSelector] != sender) revert UnauthorizedCCIPReceive(); + uint256 sourceChainId = chainIds[message.sourceChainSelector]; + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message.data, (uint256[], bytes32[])); + _storeHashes(sourceChainId, ids, hashes); } } diff --git a/packages/evm/contracts/adapters/Chainlink/CCIPHeaderReporter.sol b/packages/evm/contracts/adapters/Chainlink/CCIPHeaderReporter.sol deleted file mode 100644 index b5671bf0..00000000 --- a/packages/evm/contracts/adapters/Chainlink/CCIPHeaderReporter.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { CCIPReporter } from "./CCIPReporter.sol"; - -contract CCIPHeaderReporter is HeaderReporter, CCIPReporter { - constructor( - address headerStorage, - uint64 adapterChain, - address ccipRouter, - uint64 ccipAdapterChain - ) HeaderReporter(headerStorage, adapterChain) CCIPReporter(ccipRouter, ccipAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _ccipSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Chainlink/CCIPMessageRelay.sol b/packages/evm/contracts/adapters/Chainlink/CCIPMessageRelay.sol deleted file mode 100644 index 382aeba7..00000000 --- a/packages/evm/contracts/adapters/Chainlink/CCIPMessageRelay.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { CCIPReporter } from "./CCIPReporter.sol"; - -contract CCIPMessageRelay is MessageRelay, CCIPReporter { - constructor( - address yaho, - uint64 adapterChain, - address ccipRouter, - uint64 ccipAdapterChain - ) MessageRelay(yaho, adapterChain) CCIPReporter(ccipRouter, ccipAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _ccipSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Chainlink/CCIPReporter.sol b/packages/evm/contracts/adapters/Chainlink/CCIPReporter.sol index 22544602..07a6e736 100644 --- a/packages/evm/contracts/adapters/Chainlink/CCIPReporter.sol +++ b/packages/evm/contracts/adapters/Chainlink/CCIPReporter.sol @@ -1,20 +1,40 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract CCIPReporter { +contract CCIPReporter is Reporter, Ownable { string public constant PROVIDER = "ccip"; + IRouterClient public immutable CCIP_ROUTER; - uint64 public immutable CCIP_ADAPTER_CHAIN; - constructor(address ccipRouter, uint64 ccipAdapterChain) { + mapping(uint256 => uint64) public chainSelectors; + + error ChainSelectorNotAvailable(); + + event ChainSelectorSet(uint256 indexed chainId, uint64 indexed chainSelector); + + constructor(address headerStorage, address yaho, address ccipRouter) Reporter(headerStorage, yaho) { CCIP_ROUTER = IRouterClient(ccipRouter); - CCIP_ADAPTER_CHAIN = ccipAdapterChain; } - function _ccipSend(bytes memory payload, address adapter) internal { + function setChainSelectorByChainId(uint256 chainId, uint64 chainSelector) external onlyOwner { + chainSelectors[chainId] = chainSelector; + emit ChainSelectorSet(chainId, chainSelector); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + uint64 chainSelector = chainSelectors[toChainId]; + if (chainSelector == 0) revert ChainSelectorNotAvailable(); + bytes memory payload = abi.encode(ids, hashes); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(adapter), data: payload, @@ -22,6 +42,7 @@ abstract contract CCIPReporter { extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 200_000, strict: false })), feeToken: address(0) // Pay fees with native }); - CCIP_ROUTER.ccipSend{ value: msg.value }(CCIP_ADAPTER_CHAIN, message); + CCIP_ROUTER.ccipSend{ value: msg.value }(chainSelector, message); + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/Connext/ConnextAdapter.sol b/packages/evm/contracts/adapters/Connext/ConnextAdapter.sol index 7dc046cb..b39a1d09 100644 --- a/packages/evm/contracts/adapters/Connext/ConnextAdapter.sol +++ b/packages/evm/contracts/adapters/Connext/ConnextAdapter.sol @@ -1,24 +1,29 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IXReceiver } from "@connext/interfaces/core/IXReceiver.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract ConnextAdapter is HeaderOracleAdapter, IXReceiver { +contract ConnextAdapter is BlockHashOracleAdapter, Ownable, IXReceiver { string public constant PROVIDER = "connext"; address public immutable CONNEXT; - uint32 public immutable CONNEXT_REPORTER_CHAIN; + + mapping(uint32 => address) public enabledReporters; + mapping(uint32 => uint256) public chainIds; error UnauthorizedConnextReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address connext, - uint32 connextReporterChain - ) HeaderOracleAdapter(reporterChain, reporterAddress) { + event ReporterSet(uint256 indexed chainId, uint32 indexed domainId, address indexed reporter); + + constructor(address connext) { CONNEXT = connext; - CONNEXT_REPORTER_CHAIN = connextReporterChain; + } + + function setReporterByChain(uint256 chainId, uint32 domainId, address reporter) external onlyOwner { + enabledReporters[domainId] = reporter; + chainIds[domainId] = chainId; + emit ReporterSet(chainId, domainId, reporter); } function xReceive( @@ -29,9 +34,10 @@ contract ConnextAdapter is HeaderOracleAdapter, IXReceiver { uint32 origin, bytes memory callData ) external returns (bytes memory) { - if (msg.sender != CONNEXT || origin != CONNEXT_REPORTER_CHAIN || originSender != REPORTER_ADDRESS) - revert UnauthorizedConnextReceive(); - _receivePayload(callData); + if (msg.sender != CONNEXT || enabledReporters[origin] != originSender) revert UnauthorizedConnextReceive(); + uint256 sourceChainId = chainIds[origin]; + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(callData, (uint256[], bytes32[])); + _storeHashes(sourceChainId, ids, hashes); return ""; } } diff --git a/packages/evm/contracts/adapters/Connext/ConnextHeaderReporter.sol b/packages/evm/contracts/adapters/Connext/ConnextHeaderReporter.sol deleted file mode 100644 index 1e1eecdd..00000000 --- a/packages/evm/contracts/adapters/Connext/ConnextHeaderReporter.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { ConnextReporter } from "./ConnextReporter.sol"; - -contract ConnextHeaderReporter is HeaderReporter, ConnextReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address connext, - uint32 connextAdapterChain - ) HeaderReporter(headerStorage, adapterChain) ConnextReporter(connext, connextAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _connextSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Connext/ConnextMessageRelay.sol b/packages/evm/contracts/adapters/Connext/ConnextMessageRelay.sol deleted file mode 100644 index 219e866f..00000000 --- a/packages/evm/contracts/adapters/Connext/ConnextMessageRelay.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { ConnextReporter } from "./ConnextReporter.sol"; - -contract ConnextMessageRelay is MessageRelay, ConnextReporter { - constructor( - address yaho, - uint256 adapterChain, - address connext, - uint32 connextAdapterChain - ) MessageRelay(yaho, adapterChain) ConnextReporter(connext, connextAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _connextSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Connext/ConnextReporter.sol b/packages/evm/contracts/adapters/Connext/ConnextReporter.sol index 540dbb68..337451f1 100644 --- a/packages/evm/contracts/adapters/Connext/ConnextReporter.sol +++ b/packages/evm/contracts/adapters/Connext/ConnextReporter.sol @@ -1,23 +1,41 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IConnext } from "@connext/interfaces/core/IConnext.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract ConnextReporter { +contract ConnextReporter is Reporter, Ownable { string public constant PROVIDER = "connext"; IConnext public immutable CONNEXT; - uint32 public immutable CONNEXT_ADAPTER_CHAIN; + mapping(uint256 => uint32) public domainIds; + + error DomainIdNotAvailable(); + + event DomainIdSet(uint256 indexed chainId, uint32 indexed domainId); event ConnextTransfer(bytes32 transferId); - constructor(address connext, uint32 connextAdapterChain) { + constructor(address headerStorage, address yaho, address connext) Reporter(headerStorage, yaho) { CONNEXT = IConnext(connext); - CONNEXT_ADAPTER_CHAIN = connextAdapterChain; } - function _connextSend(bytes memory payload, address adapter) internal { + function setDomainIdByChainId(uint256 chainId, uint32 domainId) external onlyOwner { + domainIds[chainId] = domainId; + emit DomainIdSet(chainId, domainId); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + uint32 domainId = domainIds[toChainId]; + if (domainId == 0) revert DomainIdNotAvailable(); + bytes memory payload = abi.encode(ids, hashes); bytes32 transferId = CONNEXT.xcall{ value: msg.value }( - CONNEXT_ADAPTER_CHAIN, // _destination: Domain ID of the destination chain + domainId, // _destination: Domain ID of the destination chain adapter, // _to: address of the target contract address(0), // _asset: use address zero for 0-value transfers msg.sender, // _delegate: address that can revert or forceLocal on destination @@ -26,5 +44,7 @@ abstract contract ConnextReporter { payload // _callData: the encoded calldata to send ); emit ConnextTransfer(transferId); + + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/HeaderOracleAdapter.sol b/packages/evm/contracts/adapters/HeaderOracleAdapter.sol deleted file mode 100644 index a5f13631..00000000 --- a/packages/evm/contracts/adapters/HeaderOracleAdapter.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { BlockHashOracleAdapter } from "./BlockHashOracleAdapter.sol"; - -abstract contract HeaderOracleAdapter is BlockHashOracleAdapter { - uint256 public immutable REPORTER_CHAIN; - address public immutable REPORTER_ADDRESS; - - /// @dev Constructs base adapter abstracted from specific message transport - /// @param reporterChain Chain ID of the reporter this adapter is served by - /// @param reporterAddress Address of the reporter this adapter is served by - constructor(uint256 reporterChain, address reporterAddress) { - REPORTER_CHAIN = reporterChain; - REPORTER_ADDRESS = reporterAddress; - } - - function _receivePayload(bytes memory payload) internal { - (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(payload, (uint256[], bytes32[])); - for (uint256 i = 0; i < ids.length; i++) { - _storeHash(REPORTER_CHAIN, ids[i], hashes[i]); - } - } -} diff --git a/packages/evm/contracts/adapters/HeaderReporter.sol b/packages/evm/contracts/adapters/HeaderReporter.sol deleted file mode 100644 index c9900cd7..00000000 --- a/packages/evm/contracts/adapters/HeaderReporter.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { IHeaderReporter } from "../interfaces/IHeaderReporter.sol"; -import { HeaderStorage } from "../utils/HeaderStorage.sol"; - -abstract contract HeaderReporter is IHeaderReporter { - HeaderStorage public immutable HEADER_STORAGE; - uint256 public immutable ADAPTER_CHAIN; - - event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader); - - /// @dev Constructs base reporter abstracted from specific message transport - /// @param headerStorage HeaderStorage contract on this chain to use for block hash obtaining - /// @param adapterChain Chain ID of the adapter that is served by this reporter - constructor(address headerStorage, uint256 adapterChain) { - HEADER_STORAGE = HeaderStorage(headerStorage); - ADAPTER_CHAIN = adapterChain; - } - - function reportHeaders(uint256[] memory blockNumbers, address adapter) external payable { - bytes32[] memory blockHeaders = HEADER_STORAGE.storeBlockHeaders(blockNumbers); - bytes memory payload = abi.encode(blockNumbers, blockHeaders); - _sendPayload(payload, adapter); - for (uint i = 0; i < blockNumbers.length; i++) { - emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]); - } - } - - function _sendPayload(bytes memory payload, address adapter) internal virtual; -} diff --git a/packages/evm/contracts/adapters/Hyperlane/HyperlaneAdapter.sol b/packages/evm/contracts/adapters/Hyperlane/HyperlaneAdapter.sol index f581afd5..4a9a764e 100644 --- a/packages/evm/contracts/adapters/Hyperlane/HyperlaneAdapter.sol +++ b/packages/evm/contracts/adapters/Hyperlane/HyperlaneAdapter.sol @@ -1,40 +1,38 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMessageRecipient } from "@hyperlane-xyz/core/contracts/interfaces/IMessageRecipient.sol"; import { IInterchainSecurityModule } from "@hyperlane-xyz/core/contracts/interfaces/IInterchainSecurityModule.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract HyperlaneAdapter is HeaderOracleAdapter, IMessageRecipient { +contract HyperlaneAdapter is BlockHashOracleAdapter, Ownable, IMessageRecipient { string public constant PROVIDER = "hyperlane"; + address public immutable HYPERLANE_MAILBOX; - uint32 public immutable HYPERLANE_REPORTER_CHAIN; - bytes32 public immutable HYPERLANE_REPORTER_ADDRESS; - IInterchainSecurityModule public interchainSecurityModule; + mapping(uint32 => bytes32) public enabledReporters; + mapping(uint32 => uint256) public chainIds; error UnauthorizedHyperlaneReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address hyperlaneMailbox, - uint32 hyperlaneReporterChain, - bytes32 hyperlaneReporterAddress, - address hyperlaneISM - ) HeaderOracleAdapter(reporterChain, reporterAddress) { + event ReporterSet(uint256 indexed chainId, uint32 indexed domain, bytes32 indexed reporter); + + constructor(address hyperlaneMailbox) { HYPERLANE_MAILBOX = hyperlaneMailbox; - HYPERLANE_REPORTER_CHAIN = hyperlaneReporterChain; - HYPERLANE_REPORTER_ADDRESS = hyperlaneReporterAddress; - interchainSecurityModule = IInterchainSecurityModule(hyperlaneISM); } function handle(uint32 origin, bytes32 sender, bytes calldata message) external payable { - if ( - msg.sender != HYPERLANE_MAILBOX || - origin != HYPERLANE_REPORTER_CHAIN || - sender != HYPERLANE_REPORTER_ADDRESS - ) revert UnauthorizedHyperlaneReceive(); - _receivePayload(message); + if (msg.sender != HYPERLANE_MAILBOX || enabledReporters[origin] != sender) + revert UnauthorizedHyperlaneReceive(); + uint256 sourceChainId = chainIds[origin]; + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message, (uint256[], bytes32[])); + _storeHashes(sourceChainId, ids, hashes); + } + + function setReporterByChain(uint256 chainId, uint32 domain, bytes32 reporter) external onlyOwner { + enabledReporters[domain] = reporter; + chainIds[domain] = chainId; + emit ReporterSet(chainId, domain, reporter); } } diff --git a/packages/evm/contracts/adapters/Hyperlane/HyperlaneHeaderReporter.sol b/packages/evm/contracts/adapters/Hyperlane/HyperlaneHeaderReporter.sol deleted file mode 100644 index ab43805a..00000000 --- a/packages/evm/contracts/adapters/Hyperlane/HyperlaneHeaderReporter.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { HyperlaneReporter } from "./HyperlaneReporter.sol"; - -contract HyperlaneHeaderReporter is HeaderReporter, HyperlaneReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address hyperlaneMailbox, - uint32 hyperlaneAdapterChain - ) HeaderReporter(headerStorage, adapterChain) HyperlaneReporter(hyperlaneMailbox, hyperlaneAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _hyperlaneSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Hyperlane/HyperlaneMessageRelay.sol b/packages/evm/contracts/adapters/Hyperlane/HyperlaneMessageRelay.sol deleted file mode 100644 index 6b6827cc..00000000 --- a/packages/evm/contracts/adapters/Hyperlane/HyperlaneMessageRelay.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { HyperlaneReporter } from "./HyperlaneReporter.sol"; - -contract HyperlaneMessageRelay is MessageRelay, HyperlaneReporter { - constructor( - address yaho, - uint256 adapterChain, - address hyperlaneMailbox, - uint32 hyperlaneAdapterChain - ) MessageRelay(yaho, adapterChain) HyperlaneReporter(hyperlaneMailbox, hyperlaneAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _hyperlaneSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/Hyperlane/HyperlaneReporter.sol b/packages/evm/contracts/adapters/Hyperlane/HyperlaneReporter.sol index 34e53210..84d57b07 100644 --- a/packages/evm/contracts/adapters/Hyperlane/HyperlaneReporter.sol +++ b/packages/evm/contracts/adapters/Hyperlane/HyperlaneReporter.sol @@ -1,26 +1,43 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMailbox } from "@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol"; import { TypeCasts } from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract HyperlaneReporter { +contract HyperlaneReporter is Reporter, Ownable { using TypeCasts for address; string public constant PROVIDER = "hyperlane"; IMailbox public immutable HYPERLANE_MAILBOX; - uint32 public immutable HYPERLANE_ADAPTER_CHAIN; - constructor(address hyperlaneMailbox, uint32 hyperlaneAdapterChain) { + mapping(uint256 => uint32) public domains; + + error DomainNotAvailable(); + + event DomainSet(uint256 indexed chainId, uint32 indexed domain); + + constructor(address headerStorage, address yaho, address hyperlaneMailbox) Reporter(headerStorage, yaho) { HYPERLANE_MAILBOX = IMailbox(hyperlaneMailbox); - HYPERLANE_ADAPTER_CHAIN = hyperlaneAdapterChain; } - function _hyperlaneSend(bytes memory payload, address adapter) internal { - HYPERLANE_MAILBOX.dispatch{ value: msg.value }( - HYPERLANE_ADAPTER_CHAIN, // _destinationDomain - adapter.addressToBytes32(), // _recipientAddress - payload // _messageBody - ); + function setDomainByChainId(uint256 chainId, uint32 domain) external onlyOwner { + domains[chainId] = domain; + emit DomainSet(chainId, domain); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + uint32 destinationDomain = domains[toChainId]; + if (destinationDomain == 0) revert DomainNotAvailable(); + bytes memory payload = abi.encode(ids, hashes); + HYPERLANE_MAILBOX.dispatch{ value: msg.value }(destinationDomain, adapter.addressToBytes32(), payload); + + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol index 80ab6951..6eb47265 100644 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol +++ b/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol @@ -1,35 +1,36 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ILayerZeroReceiver } from "./interfaces/ILayerZeroReceiver.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract LayerZeroAdapter is HeaderOracleAdapter, ILayerZeroReceiver { +contract LayerZeroAdapter is BlockHashOracleAdapter, Ownable, ILayerZeroReceiver { string public constant PROVIDER = "layer-zero"; address public immutable LAYER_ZERO_ENDPOINT; - uint32 public immutable LAYER_ZERO_REPORTER_CHAIN; - bytes32 public immutable LAYER_ZERO_REPORTER_PATH_HASH; + + mapping(uint32 => bytes32) public enabledReportersPaths; + mapping(uint32 => uint256) public chainIds; error UnauthorizedLayerZeroReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address lzEndpoint, - uint16 lzReporterChain - ) HeaderOracleAdapter(reporterChain, reporterAddress) { + event ReporterSet(uint256 indexed chainId, uint16 indexed endpointId, address indexed reporter); + + constructor(address lzEndpoint) { LAYER_ZERO_ENDPOINT = lzEndpoint; - LAYER_ZERO_REPORTER_CHAIN = lzReporterChain; - bytes memory path = abi.encodePacked(reporterAddress, address(this)); - LAYER_ZERO_REPORTER_PATH_HASH = keccak256(path); } - function lzReceive(uint16 srcChainId, bytes memory srcAddress, uint64 /* nonce */, bytes memory payload) external { - if ( - msg.sender != LAYER_ZERO_ENDPOINT || - srcChainId != LAYER_ZERO_REPORTER_CHAIN || - keccak256(srcAddress) != LAYER_ZERO_REPORTER_PATH_HASH - ) revert UnauthorizedLayerZeroReceive(); - _receivePayload(payload); + function lzReceive(uint16 srcEndpointId, bytes memory srcPath, uint64 /* nonce */, bytes memory payload) external { + if (msg.sender != LAYER_ZERO_ENDPOINT || enabledReportersPaths[srcEndpointId] != keccak256(srcPath)) + revert UnauthorizedLayerZeroReceive(); + uint256 sourceChainId = chainIds[srcEndpointId]; + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(payload, (uint256[], bytes32[])); + _storeHashes(sourceChainId, ids, hashes); + } + + function setReporterByChain(uint256 chainId, uint16 endpointId, address reporter) external onlyOwner { + enabledReportersPaths[endpointId] = keccak256(abi.encodePacked(reporter, address(this))); + chainIds[endpointId] = chainId; + emit ReporterSet(chainId, endpointId, reporter); } } diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroHeaderReporter.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroHeaderReporter.sol deleted file mode 100644 index 75254dfc..00000000 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroHeaderReporter.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { LayerZeroReporter } from "./LayerZeroReporter.sol"; - -contract LayerZeroHeaderReporter is HeaderReporter, LayerZeroReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address lzEndpoint, - uint16 lzAdapterChain - ) HeaderReporter(headerStorage, adapterChain) LayerZeroReporter(lzEndpoint, lzAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _lzSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroMessageRelay.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroMessageRelay.sol deleted file mode 100644 index 369849ab..00000000 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroMessageRelay.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { LayerZeroReporter } from "./LayerZeroReporter.sol"; - -contract LayerZeroMessageRelay is MessageRelay, LayerZeroReporter { - constructor( - address yaho, - uint256 adapterChain, - address lzEndpoint, - uint16 lzAdapterChain - ) MessageRelay(yaho, adapterChain) LayerZeroReporter(lzEndpoint, lzAdapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _lzSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol index f07ffb98..fd1eb5b9 100644 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol +++ b/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol @@ -1,28 +1,48 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ILayerZeroEndpoint } from "./interfaces/ILayerZeroEndpoint.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract LayerZeroReporter { +contract LayerZeroReporter is Reporter, Ownable { string public constant PROVIDER = "layer-zero"; ILayerZeroEndpoint public immutable LAYER_ZERO_ENDPOINT; - uint16 public immutable LAYER_ZERO_ADAPTER_CHAIN; - constructor(address lzEndpoint, uint16 lzAdapterChain) { + mapping(uint256 => uint16) public endpointIds; + + error EndpointIdNotAvailable(); + + event EndpointIdSet(uint256 indexed chainId, uint16 indexed endpointId); + + constructor(address headerStorage, address yaho, address lzEndpoint) Reporter(headerStorage, yaho) { LAYER_ZERO_ENDPOINT = ILayerZeroEndpoint(lzEndpoint); - LAYER_ZERO_ADAPTER_CHAIN = lzAdapterChain; } - function _lzSend(bytes memory payload, address adapter) internal { + function setEndpointIdByChainId(uint256 chainId, uint16 endpointId) external onlyOwner { + endpointIds[chainId] = endpointId; + emit EndpointIdSet(chainId, endpointId); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + uint16 endpointId = endpointIds[toChainId]; + if (endpointId == 0) revert EndpointIdNotAvailable(); + bytes memory payload = abi.encode(ids, hashes); bytes memory path = abi.encodePacked(adapter, address(this)); // solhint-disable-next-line check-send-result LAYER_ZERO_ENDPOINT.send{ value: msg.value }( - LAYER_ZERO_ADAPTER_CHAIN, // _dstChainId: destination LayerZero chainId - path, // _destination: send to this address on the destination - payload, // _payload: bytes payload + endpointId, + path, + payload, payable(msg.sender), // _refundAddress: refund address address(0), // _zroPaymentAddress: future parameter bytes("") // _adapterParams: adapterParams (see "Advanced Features") ); + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/MessageRelay.sol b/packages/evm/contracts/adapters/MessageRelay.sol deleted file mode 100644 index 8aba498e..00000000 --- a/packages/evm/contracts/adapters/MessageRelay.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { IMessageRelay } from "../interfaces/IMessageRelay.sol"; -import { Yaho } from "../Yaho.sol"; - -abstract contract MessageRelay is IMessageRelay { - Yaho public immutable YAHO; - uint256 public immutable ADAPTER_CHAIN; - - event MessageRelayed(address indexed emitter, uint256 indexed messageId); - - /// @dev Constructs base reporter abstracted from specific message transport - /// @param yaho Yaho contract that is served to dispatch and relay messages - /// @param adapterChain Chain ID of the adapter that is served by this reporter - constructor(address yaho, uint256 adapterChain) { - YAHO = Yaho(yaho); - ADAPTER_CHAIN = adapterChain; - } - - function relayMessages(uint256[] memory messageIds, address adapter) external payable returns (bytes32) { - bytes32[] memory hashes = new bytes32[](messageIds.length); - for (uint256 i = 0; i < messageIds.length; i++) { - hashes[i] = YAHO.hashes(messageIds[i]); - emit MessageRelayed(address(this), messageIds[i]); - } - bytes memory payload = abi.encode(messageIds, hashes); - _sendPayload(payload, adapter); - return keccak256(abi.encode(true)); - } - - function _sendPayload(bytes memory payload, address adapter) internal virtual; -} diff --git a/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter.sol b/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter.sol deleted file mode 100644 index 448308e3..00000000 --- a/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; -import { IHeaderStorage } from "../../interfaces/IHeaderStorage.sol"; - -contract L1CrossDomainMessengerHeaderReporter { - // The first 1.92 million gas on L2 is free. See here: - // https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions - uint32 internal constant GAS_LIMIT = 1_920_000; - - ICrossDomainMessenger public immutable l1CrossDomainMessenger; - IHeaderStorage public immutable headerStorage; - - event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader); - - constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IHeaderStorage headerStorage_) { - l1CrossDomainMessenger = l1CrossDomainMessenger_; - headerStorage = headerStorage_; - } - - /// @dev Reports the given block headers to the oracleAdapter via the L1CrossDomainMessenger. - /// @param blockNumbers Uint256 array of block number to pass over the L1CrossDomainMessenger. - /// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain. - function reportHeaders(uint256[] memory blockNumbers, address adapter) external payable { - bytes32[] memory blockHeaders = headerStorage.storeBlockHeaders(blockNumbers); - bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", blockNumbers, blockHeaders); - l1CrossDomainMessenger.sendMessage(adapter, message, GAS_LIMIT); - for (uint256 i = 0; i < blockNumbers.length; i++) { - emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]); - } - } -} diff --git a/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay.sol b/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay.sol deleted file mode 100644 index 6bc367af..00000000 --- a/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; -import { IYaho } from "../../interfaces/IYaho.sol"; - -contract L1CrossDomainMessengerMessageRelay { - // The first 1.92 million gas on L2 is free. See here: - // https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions - uint32 internal constant GAS_LIMIT = 1_920_000; - - ICrossDomainMessenger public immutable l1CrossDomainMessenger; - IYaho public immutable yaho; - - event MessageRelayed(address indexed emitter, uint256 indexed messageId); - - constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IYaho yaho_) { - l1CrossDomainMessenger = l1CrossDomainMessenger_; - yaho = yaho_; - } - - /// @dev Reports the given messages to the adapter via the L1CrossDomainMessenger. - /// @param messageIds Uint256 array message ids to pass over the L1CrossDomainMessenger. - /// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain. - function relayMessages(uint256[] memory messageIds, address adapter) external payable returns (bytes32 receipt) { - 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]); - } - bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", messageIds, hashes); - l1CrossDomainMessenger.sendMessage{ value: msg.value }(adapter, message, GAS_LIMIT); - return keccak256(abi.encode(true)); - } -} diff --git a/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerReporter.sol b/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerReporter.sol new file mode 100644 index 00000000..c9be745b --- /dev/null +++ b/packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerReporter.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { Reporter } from "../Reporter.sol"; +import { IOracleAdapter } from "../../interfaces/IOracleAdapter.sol"; +import { ICrossDomainMessenger } from "./interfaces/ICrossDomainMessenger.sol"; + +contract L1CrossDomainMessengerReporter is Reporter { + string public constant PROVIDER = "optimism"; + // The first 1.92 million gas on L2 is free. See here: + // https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions + uint32 internal constant GAS_LIMIT = 1_920_000; + + ICrossDomainMessenger public immutable L1_CROSS_DOMAIN_MESSENGER; + uint256 public immutable TO_CHAIN_ID; + + error InvalidToChainId(uint256 chainId, uint256 expectedChainId); + + constructor( + address headerStorage, + address yaho, + address l1CrossDomainMessenger, + uint256 toChainId + ) Reporter(headerStorage, yaho) { + L1_CROSS_DOMAIN_MESSENGER = ICrossDomainMessenger(l1CrossDomainMessenger); + TO_CHAIN_ID = toChainId; + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + if (toChainId != TO_CHAIN_ID) revert InvalidToChainId(toChainId, TO_CHAIN_ID); + bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", ids, hashes); + L1_CROSS_DOMAIN_MESSENGER.sendMessage(adapter, message, GAS_LIMIT); + return bytes32(0); + } +} diff --git a/packages/evm/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter.sol b/packages/evm/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter.sol index 3dc33575..425bdb82 100644 --- a/packages/evm/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter.sol +++ b/packages/evm/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter.sol @@ -1,43 +1,36 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; -import { OracleAdapter } from "../OracleAdapter.sol"; +import { ICrossDomainMessenger } from "./interfaces/ICrossDomainMessenger.sol"; import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract L2CrossDomainMessengerAdapter is OracleAdapter, BlockHashOracleAdapter { - ICrossDomainMessenger public immutable l2CrossDomainMessenger; - address public immutable reporter; - uint256 public immutable chainId; +contract L2CrossDomainMessengerAdapter is BlockHashOracleAdapter { + string public constant PROVIDER = "optimism"; - error ArrayLengthMissmatch(address emitter); - error UnauthorizedHashReporter(address emitter, address reporter); - error UnauthorizedL2CrossDomainMessenger(address emitter, address sender); + ICrossDomainMessenger public immutable L2_CROSS_DOMAIN_MESSENGER; + address public immutable REPORTER; + uint256 public immutable SOURCE_CHAIN_ID; - constructor(ICrossDomainMessenger l2CrossDomainMessenger_, address reporter_, uint256 chainId_) { - l2CrossDomainMessenger = l2CrossDomainMessenger_; - reporter = reporter_; - chainId = chainId_; + error ArrayLengthMissmatch(); + error UnauthorizedHashReporter(address sender, address expectedSender); + error UnauthorizedL2CrossDomainMessenger(address domainMessageSender, address expectedDomainMessageSender); + + constructor(ICrossDomainMessenger l2CrossDomainMessenger_, address reporter, uint256 sourceChainId) { + L2_CROSS_DOMAIN_MESSENGER = ICrossDomainMessenger(l2CrossDomainMessenger_); + REPORTER = reporter; + SOURCE_CHAIN_ID = sourceChainId; } - /// @dev Check that the l2CrossDomainMessenger and xDomainMessageSender are valid. modifier onlyValid() { - if (msg.sender != address(l2CrossDomainMessenger)) - revert UnauthorizedL2CrossDomainMessenger(address(this), msg.sender); - if (l2CrossDomainMessenger.xDomainMessageSender() != reporter) - revert UnauthorizedHashReporter(address(this), reporter); + if (msg.sender != address(L2_CROSS_DOMAIN_MESSENGER)) + revert UnauthorizedL2CrossDomainMessenger(msg.sender, address(L2_CROSS_DOMAIN_MESSENGER)); + address xDomainMessageSender = L2_CROSS_DOMAIN_MESSENGER.xDomainMessageSender(); + if (xDomainMessageSender != REPORTER) revert UnauthorizedHashReporter(xDomainMessageSender, REPORTER); _; } - /// @dev Stores the hashes for a given array of ids. - /// @param ids Array of ids number for which to set the hashes. - /// @param hashes Array of hashes to set for the given ids. - /// @notice Only callable by `l2CrossDomainMessenger` with a message passed from `reporter`. - /// @notice Will revert if given array lengths do not match. function storeHashes(uint256[] memory ids, bytes32[] memory hashes) external onlyValid { - if (ids.length != hashes.length) revert ArrayLengthMissmatch(address(this)); - for (uint256 i = 0; i < ids.length; i++) { - _storeHash(chainId, ids[i], hashes[i]); - } + if (ids.length != hashes.length) revert ArrayLengthMissmatch(); + _storeHashes(SOURCE_CHAIN_ID, ids, hashes); } } diff --git a/packages/evm/contracts/adapters/Optimism/ICrossDomainMessenger.sol b/packages/evm/contracts/adapters/Optimism/interfaces/ICrossDomainMessenger.sol similarity index 100% rename from packages/evm/contracts/adapters/Optimism/ICrossDomainMessenger.sol rename to packages/evm/contracts/adapters/Optimism/interfaces/ICrossDomainMessenger.sol diff --git a/packages/evm/contracts/adapters/OracleAdapter.sol b/packages/evm/contracts/adapters/OracleAdapter.sol index 2e6d454c..4324323f 100644 --- a/packages/evm/contracts/adapters/OracleAdapter.sol +++ b/packages/evm/contracts/adapters/OracleAdapter.sol @@ -4,25 +4,26 @@ pragma solidity ^0.8.17; import { IOracleAdapter } from "../interfaces/IOracleAdapter.sol"; abstract contract OracleAdapter is IOracleAdapter { - mapping(uint256 => mapping(uint256 => bytes32)) public hashes; + mapping(uint256 => mapping(uint256 => bytes32)) private _hashes; - /// @dev Returns the hash for a given domain and ID, as reported by the oracle. - /// @param domain Identifier for the domain to query. - /// @param id Identifier for the ID to query. - /// @return hash Bytes32 hash reported by the oracle for the given ID on the given domain. - /// @notice MUST return bytes32(0) if the oracle has not yet reported a hash for the given ID. - function getHashFromOracle(uint256 domain, uint256 id) external view returns (bytes32 hash) { - hash = hashes[domain][id]; + /// @inheritdoc IOracleAdapter + function getHashFromOracle(uint256 domain, uint256 id) public view returns (bytes32) { + return _hashes[domain][id]; + } + + function _storeHashes(uint256 domain, uint256[] memory ids, bytes32[] memory hashes) internal { + for (uint256 i = 0; i < ids.length; ) { + _storeHash(domain, ids[i], hashes[i]); + unchecked { + ++i; + } + } } - /// @dev Stores a hash for a given domain and ID. - /// @param domain Identifier for the domain. - /// @param id Identifier for the ID of the hash. - /// @param hash Bytes32 hash value to store. function _storeHash(uint256 domain, uint256 id, bytes32 hash) internal { - bytes32 currentHash = hashes[domain][id]; + bytes32 currentHash = _hashes[domain][id]; if (currentHash != hash) { - hashes[domain][id] = hash; + _hashes[domain][id] = hash; emit HashStored(id, hash); } } diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol index 96d28a3f..16f1fac3 100644 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol @@ -1,24 +1,39 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; -import { PNetworkBase } from "./PNetworkBase.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC777Recipient } from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; +import { IERC1820RegistryUpgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; + +contract PNetworkAdapter is BlockHashOracleAdapter, Ownable { + string public constant PROVIDER = "pnetwork"; + + address public immutable VAULT; + address public immutable TOKEN; + IERC1820RegistryUpgradeable private constant ERC1820 = + IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + mapping(bytes4 => address) public enabledReporters; + mapping(bytes4 => uint256) public chainIds; -contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { error InvalidSender(address sender, address expected); - error InvalidNetworkId(bytes4 networkId, bytes4 expected); + error InvalidToken(address token, address expected); error UnauthorizedPNetworkReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address pNetworkVault, - address pNetworkToken, - bytes4 pNetworkReporterNetworkId - ) - HeaderOracleAdapter(reporterChain, reporterAddress) - PNetworkBase(pNetworkVault, pNetworkToken, pNetworkReporterNetworkId) - {} // solhint-disable no-empty-blocks + event ReporterSet(uint256 indexed chainId, bytes4 indexed networkId, address indexed reporter); + + modifier onlySupportedToken(address _tokenAddress) { + if (_tokenAddress != TOKEN) revert InvalidToken(_tokenAddress, TOKEN); + _; + } + + constructor(address pNetworkVault, address pNetworkToken) { + VAULT = pNetworkVault; + TOKEN = pNetworkToken; + ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + } // Implement the ERC777TokensRecipient interface function tokensReceived( @@ -28,14 +43,20 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { uint256, bytes calldata data, bytes calldata - ) external override onlySupportedToken(msg.sender) { + ) external onlySupportedToken(msg.sender) { if (from != VAULT) revert InvalidSender(from, VAULT); (, bytes memory userData, bytes4 networkId, address sender) = abi.decode( data, (bytes1, bytes, bytes4, address) ); - if (networkId != PNETWORK_REF_NETWORK_ID) revert InvalidNetworkId(networkId, PNETWORK_REF_NETWORK_ID); - if (sender != REPORTER_ADDRESS) revert UnauthorizedPNetworkReceive(); - _receivePayload(userData); + if (enabledReporters[networkId] != sender) revert UnauthorizedPNetworkReceive(); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(userData, (uint256[], bytes32[])); + _storeHashes(chainIds[networkId], ids, hashes); + } + + function setReporterByChain(uint256 chainId, bytes4 networkId, address reporter) external onlyOwner { + enabledReporters[networkId] = reporter; + chainIds[networkId] = chainId; + emit ReporterSet(chainId, networkId, reporter); } } diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkBase.sol deleted file mode 100644 index 70c8ace2..00000000 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkBase.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; -import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; - -abstract contract PNetworkBase is IERC777Recipient { - address public immutable VAULT; - address public immutable TOKEN; - bytes4 public immutable PNETWORK_REF_NETWORK_ID; - IERC1820RegistryUpgradeable private constant ERC1820 = - IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - - error InvalidToken(address token, address expected); - error InvalidReceiver(address receiver, address expected); - - constructor(address pNetworkVault, address pNetworkToken, bytes4 pNetworkRefNetworkId) { - VAULT = pNetworkVault; - TOKEN = pNetworkToken; - PNETWORK_REF_NETWORK_ID = pNetworkRefNetworkId; - ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); - } - - // Implement the ERC777TokensRecipient interface - function tokensReceived( - address, - address, - address to, - uint256, - bytes calldata, - bytes calldata - ) external virtual onlySupportedToken(msg.sender) { - if (to != address(this)) revert InvalidReceiver(to, address(this)); - } - - modifier onlySupportedToken(address _tokenAddress) { - if (_tokenAddress != TOKEN) revert InvalidToken(_tokenAddress, TOKEN); - _; - } -} diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkHeaderReporter.sol deleted file mode 100644 index 40e35bdb..00000000 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkHeaderReporter.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { PNetworkReporter } from "./PNetworkReporter.sol"; - -contract PNetworkHeaderReporter is HeaderReporter, PNetworkReporter { - constructor( - address headerStorage, - uint64 adapterChain, - address pNetworkVault, - address pNetworkToken, - bytes4 pNetworkAdapterNetworkId - ) - HeaderReporter(headerStorage, adapterChain) - PNetworkReporter(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) - {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _pNetworkSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkMessageRelay.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkMessageRelay.sol deleted file mode 100644 index 40d081d1..00000000 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkMessageRelay.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { PNetworkReporter } from "./PNetworkReporter.sol"; - -contract PNetworkMessageRelay is MessageRelay, PNetworkReporter { - constructor( - address yaho, - uint64 adapterChain, - address pNetworkVault, - address pNetworktoken, - bytes4 pNetworkAdapterNetworkId - ) MessageRelay(yaho, adapterChain) PNetworkReporter(pNetworkVault, pNetworktoken, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _pNetworkSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol index 27b2bb32..27901b19 100644 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol @@ -2,32 +2,55 @@ pragma solidity ^0.8.17; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { Reporter } from "../Reporter.sol"; import { IErc20Vault } from "./interfaces/IErc20Vault.sol"; import { IPToken } from "./interfaces/IPToken.sol"; -import { PNetworkBase } from "./PNetworkBase.sol"; -abstract contract PNetworkReporter is PNetworkBase { +contract PNetworkReporter is Reporter, Ownable { + string public constant PROVIDER = "pnetwork"; uint256 private constant SWAP_AMOUNT = 1; + address public immutable VAULT; + address public immutable TOKEN; + + mapping(uint256 => bytes4) public networkIds; + + error NetworkIdNotAvailable(); + + event NetworkIdSet(uint256 indexed chainId, bytes4 indexed networkId); + constructor( + address headerStorage, + address yaho, address pNetworkVault, - address pNetworkToken, - bytes4 pNetworkAdapterNetworkId - ) PNetworkBase(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks + address pNetworkToken + ) Reporter(headerStorage, yaho) { + VAULT = pNetworkVault; + TOKEN = pNetworkToken; + } - function _char(bytes1 b) internal pure returns (bytes1 c) { - if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); - else return bytes1(uint8(b) + 0x57); + function setNetworkIdByChainId(uint256 chainId, bytes4 networkId) external onlyOwner { + networkIds[chainId] = networkId; + emit NetworkIdSet(chainId, networkId); } - function _pNetworkSend(bytes memory payload, address adapter) internal { + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + bytes4 networkId = networkIds[toChainId]; + if (networkId == 0) revert NetworkIdNotAvailable(); + bytes memory payload = abi.encode(ids, hashes); if (VAULT != address(0)) { IERC20(TOKEN).approve(VAULT, SWAP_AMOUNT); - IErc20Vault(VAULT).pegIn(SWAP_AMOUNT, TOKEN, _toAsciiString(adapter), payload, PNETWORK_REF_NETWORK_ID); + IErc20Vault(VAULT).pegIn(SWAP_AMOUNT, TOKEN, _toAsciiString(adapter), payload, networkId); } else { - IPToken(TOKEN).redeem(SWAP_AMOUNT, payload, _toAsciiString(adapter), PNETWORK_REF_NETWORK_ID); + IPToken(TOKEN).redeem(SWAP_AMOUNT, payload, _toAsciiString(adapter), networkId); } + return bytes32(0); } function _toAsciiString(address x) internal pure returns (string memory) { @@ -41,4 +64,9 @@ abstract contract PNetworkReporter is PNetworkBase { } return string(s); } + + function _char(bytes1 b) internal pure returns (bytes1 c) { + if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); + else return bytes1(uint8(b) + 0x57); + } } diff --git a/packages/evm/contracts/adapters/Reporter.sol b/packages/evm/contracts/adapters/Reporter.sol new file mode 100644 index 00000000..f31991ee --- /dev/null +++ b/packages/evm/contracts/adapters/Reporter.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { IHeaderStorage } from "../interfaces/IHeaderStorage.sol"; +import { IReporter } from "../interfaces/IReporter.sol"; +import { IOracleAdapter } from "../interfaces/IOracleAdapter.sol"; + +abstract contract Reporter is IReporter { + address public immutable HEADER_STORAGE; + address public immutable YAHO; + + modifier onlyYaho() { + if (msg.sender != YAHO) revert NotYaho(msg.sender, YAHO); + _; + } + + constructor(address headerStorage, address yaho) { + HEADER_STORAGE = headerStorage; + YAHO = yaho; + } + + /// @inheritdoc IReporter + function dispatchBlocks( + uint256 toChainId, + IOracleAdapter adapter, + uint256[] memory blockNumbers + ) external payable returns (bytes32) { + bytes32[] memory blockHeaders = IHeaderStorage(HEADER_STORAGE).storeBlockHeaders(blockNumbers); + for (uint256 i = 0; i < blockNumbers.length; ) { + emit BlockDispatched(toChainId, adapter, blockNumbers[i], blockHeaders[i]); + unchecked { + ++i; + } + } + return _dispatch(toChainId, address(adapter), blockNumbers, blockHeaders); + } + + /// @inheritdoc IReporter + function dispatchMessages( + uint256 toChainId, + IOracleAdapter adapter, + uint256[] memory messageIds, + bytes32[] memory messageHashes + ) external payable onlyYaho returns (bytes32) { + for (uint256 i = 0; i < messageIds.length; ) { + emit MessageDispatched(toChainId, adapter, messageIds[i], messageHashes[i]); + unchecked { + ++i; + } + } + return _dispatch(toChainId, address(adapter), messageIds, messageHashes); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory messageIds, + bytes32[] memory messageHashes + ) internal virtual returns (bytes32); +} diff --git a/packages/evm/contracts/adapters/Sygma/SygmaAdapter.sol b/packages/evm/contracts/adapters/Sygma/SygmaAdapter.sol index 451e659d..251a649f 100644 --- a/packages/evm/contracts/adapters/Sygma/SygmaAdapter.sol +++ b/packages/evm/contracts/adapters/Sygma/SygmaAdapter.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import "@openzeppelin/contracts/access/AccessControl.sol"; -import "../OracleAdapter.sol"; +import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import "../BlockHashOracleAdapter.sol"; -contract SygmaAdapter is AccessControl, OracleAdapter, BlockHashOracleAdapter { +contract SygmaAdapter is AccessControl, BlockHashOracleAdapter { + string public constant PROVIDER = "sygma"; + struct Reporter { uint128 chainID; bool enabled; @@ -21,9 +22,6 @@ contract SygmaAdapter is AccessControl, OracleAdapter, BlockHashOracleAdapter { event ReporterSet(address reporterAddress, uint256 chainID, bool enabled); - /** - @param handler Contract address of the generic handler. - */ constructor(address handler) { _handler = handler; _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); @@ -34,35 +32,17 @@ contract SygmaAdapter is AccessControl, OracleAdapter, BlockHashOracleAdapter { _; } - /** - @dev Sets parameters of a source chain hash reporter. - @param reporterAddress Hash reporter address on the source chain. - @param chainID ChainID of the source chain. - @param enabled Status of the reporter. - */ function setReporter(address reporterAddress, uint128 chainID, bool enabled) public onlyAdmin { reporters[reporterAddress] = Reporter(chainID, enabled); emit ReporterSet(reporterAddress, chainID, enabled); } - /** - @dev Stores the hashes for a given array of ids. - @param reporterAddress Hash reporter address on the source chain. - @param ids Array of block numbers for which to set the hashes. - @param hashes Array of hashes to set for the given block numbers. - @notice Only callable by `_handler` with a message passed from an authorized reporter. - @notice Will revert if array lengths do not match. - */ function storeHashes(address reporterAddress, uint256[] calldata ids, bytes32[] calldata hashes) public { if (ids.length != hashes.length) revert ArrayLengthMismatch(); if (msg.sender != _handler) revert InvalidHandler(msg.sender); - Reporter memory reporter = reporters[reporterAddress]; if (!reporter.enabled) revert InvalidReporter(reporterAddress); uint256 chainID = uint256(reporter.chainID); - - for (uint i = 0; i < ids.length; i++) { - _storeHash(chainID, ids[i], hashes[i]); - } + _storeHashes(chainID, ids, hashes); } } diff --git a/packages/evm/contracts/adapters/Sygma/SygmaHeaderReporter.sol b/packages/evm/contracts/adapters/Sygma/SygmaHeaderReporter.sol deleted file mode 100644 index f7c95f87..00000000 --- a/packages/evm/contracts/adapters/Sygma/SygmaHeaderReporter.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { SygmaReporter } from "./SygmaReporter.sol"; -import { HeaderStorage } from "../../utils/HeaderStorage.sol"; - -contract SygmaHeaderReporter is SygmaReporter { - HeaderStorage public immutable _headerStorage; - - event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader); - - constructor( - address bridge, - HeaderStorage headerStorage, - bytes32 resourceID, - uint8 defaultDestinationDomainID, - address defaultSygmaAdapter - ) SygmaReporter(bridge, resourceID, defaultDestinationDomainID, defaultSygmaAdapter) { - _headerStorage = headerStorage; - } - - /** - @dev Reports the given block headers to the oracleAdapter via the Sygma bridge to default domain. - @param blockNumbers Uint256 array of block numbers to pass over the Sygma bridge. - @param feeData Additional data to be passed to the fee handler. - */ - function reportHeaders(uint256[] memory blockNumbers, bytes calldata feeData) public payable { - _reportHeaders(blockNumbers, _defaultSygmaAdapter, _defaultDestinationDomainID, feeData); - } - - /** - @dev Reports the given block headers to the oracleAdapter via the Sygma bridge to specified domain. - @param blockNumbers Uint256 array of block numbers to pass over the Sygma bridge. - @param sygmaAdapter Address of the Sygma adapter on the target chain. - @param destinationDomainID Destination domain ID. - @param feeData Additional data to be passed to the fee handler. - */ - function reportHeadersToDomain( - uint256[] memory blockNumbers, - address sygmaAdapter, - uint8 destinationDomainID, - bytes memory feeData - ) public payable { - _reportHeaders(blockNumbers, sygmaAdapter, destinationDomainID, feeData); - } - - function _reportHeaders( - uint256[] memory blockNumbers, - address sygmaAdapter, - uint8 destinationDomainID, - bytes memory feeData - ) internal { - bytes32[] memory blockHeaders = _headerStorage.storeBlockHeaders(blockNumbers); - _reportData(blockNumbers, blockHeaders, sygmaAdapter, destinationDomainID, feeData); - for (uint i = 0; i < blockNumbers.length; i++) { - emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]); - } - } -} diff --git a/packages/evm/contracts/adapters/Sygma/SygmaMessageRelay.sol b/packages/evm/contracts/adapters/Sygma/SygmaMessageRelay.sol deleted file mode 100644 index 74aafad2..00000000 --- a/packages/evm/contracts/adapters/Sygma/SygmaMessageRelay.sol +++ /dev/null @@ -1,38 +0,0 @@ -// 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 SygmaMessageRelay 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)); - } -} diff --git a/packages/evm/contracts/adapters/Sygma/SygmaReporter.sol b/packages/evm/contracts/adapters/Sygma/SygmaReporter.sol index 2fcaee60..4a792cd5 100644 --- a/packages/evm/contracts/adapters/Sygma/SygmaReporter.sol +++ b/packages/evm/contracts/adapters/Sygma/SygmaReporter.sol @@ -1,29 +1,46 @@ // 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; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { Reporter } from "../Reporter.sol"; +import { ISygmaAdapter } from "./interfaces/ISygmaAdapter.sol"; +import { IBridge } from "./interfaces/IBridge.sol"; + +contract SygmaReporter is Reporter, Ownable { + string public constant PROVIDER = "sygma"; + + IBridge public immutable BRIDGE; + + mapping(uint256 => uint8) public domainIds; + mapping(uint256 => bytes32) public resourceIds; + + error DomainIdNotAvailable(); + error ResourceIdNotAvailable(); + + event DomainIdSet(uint256 indexed chainId, uint8 indexed domainId); + event ResourceIdSet(uint256 indexed chainId, bytes32 indexed resourceId); + + constructor(address headerStorage, address yaho, address bridge) Reporter(headerStorage, yaho) { + BRIDGE = IBridge(bridge); } - function _reportData( - uint256[] memory messageIds, - bytes32[] memory hashes, - address sygmaAdapter, - uint8 destinationDomainID, - bytes memory feeData - ) internal returns (uint64 depositNonce, bytes memory handlerResponse) { + function setDomainIdAndResourceIdByChainId(uint256 chainId, uint8 domainId, bytes32 resourceId) external onlyOwner { + domainIds[chainId] = domainId; + resourceIds[chainId] = resourceId; + emit DomainIdSet(chainId, domainId); + emit ResourceIdSet(chainId, resourceId); + } + + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + uint8 domainId = domainIds[toChainId]; + if (domainId == 0) revert DomainIdNotAvailable(); + bytes32 resourceId = resourceIds[toChainId]; + if (resourceId == bytes32(0)) revert ResourceIdNotAvailable(); bytes memory depositData = abi.encodePacked( // uint256 maxFee uint256(950000), @@ -34,15 +51,21 @@ contract SygmaReporter { // uint8 len(executeContractAddress) uint8(20), // bytes executeContractAddress - sygmaAdapter, + adapter, // uint8 len(executionDataDepositor) uint8(20), // bytes executionDataDepositor address(this), // bytes executionDataDepositor + executionData - prepareDepositData(messageIds, hashes) + prepareDepositData(ids, hashes) + ); + (uint64 nonce, bytes memory handlerResponse) = BRIDGE.deposit{ value: msg.value }( + domainId, + resourceId, + depositData, + "" // feeData ); - return IBridge(_bridge).deposit{ value: msg.value }(destinationDomainID, _resourceID, depositData, feeData); + return bytes32(keccak256(abi.encode(nonce, handlerResponse))); } function slice(bytes calldata input, uint256 position) public pure returns (bytes memory) { diff --git a/packages/evm/contracts/adapters/Sygma/test/SygmaTestContracts.sol b/packages/evm/contracts/adapters/Sygma/test/SygmaTestContracts.sol deleted file mode 100644 index 4c1b7058..00000000 --- a/packages/evm/contracts/adapters/Sygma/test/SygmaTestContracts.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.17; - -import "../interfaces/IBridge.sol"; - -contract MockSygmaBridge { - error CallReverted(); - - event Deposit( - uint8 destinationDomainID, - bytes32 resourceID, - uint64 depositNonce, - address indexed user, - bytes data, - bytes handlerResponse - ); - - function deposit( - uint8 destinationDomainID, - bytes32 resourceID, - bytes calldata depositData, - bytes calldata feeData - ) 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); - } -} diff --git a/packages/evm/contracts/adapters/Wormhole/WormholeAdapter.sol b/packages/evm/contracts/adapters/Wormhole/WormholeAdapter.sol index c2b8be41..0cd29fba 100644 --- a/packages/evm/contracts/adapters/Wormhole/WormholeAdapter.sol +++ b/packages/evm/contracts/adapters/Wormhole/WormholeAdapter.sol @@ -1,37 +1,36 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { IWormhole, VM } from "./IWormhole.sol"; -import { OracleAdapter } from "../OracleAdapter.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; +import { IWormhole, VM } from "./interfaces/IWormhole.sol"; -contract WormholeAdapter is OracleAdapter, BlockHashOracleAdapter { - IWormhole public immutable wormhole; - bytes32 public immutable reporter; - uint16 public immutable wormholeSourceChainId; - uint256 public immutable sourceChainId; - - error InvalidMessage(address emitter, VM vm, string reason); - error InvalidEmitterChainId(address emitter, uint16 chainId); - error InvalidReporter(address emitter, bytes32 reporter); - - constructor(IWormhole wormhole_, address reporter_, uint256 sourceChainId_, uint16 wormholeSourceChainId_) { - wormhole = wormhole_; - reporter = bytes32(uint256(uint160(reporter_))); - sourceChainId = sourceChainId_; - wormholeSourceChainId = wormholeSourceChainId_; +contract WormholeAdapter is BlockHashOracleAdapter, Ownable { + IWormhole public immutable WORMHOLE; + + mapping(uint32 => bytes32) public enabledReporters; + mapping(uint32 => uint256) public chainIds; + + error InvalidMessage(VM vm, string reason); + error UnauthorizedWormholeReceive(); + + event ReporterSet(uint256 indexed chainId, uint16 indexed endpointId, address indexed reporter); + + constructor(address wormhole) { + WORMHOLE = IWormhole(wormhole); + } + + function setReporterByChain(uint256 chainId, uint16 wormholeChainId, address reporter) external onlyOwner { + enabledReporters[wormholeChainId] = bytes32(uint256(uint160(reporter))); + chainIds[wormholeChainId] = wormholeChainId; + emit ReporterSet(chainId, wormholeChainId, reporter); } - /// @dev Stores the block header for a given block. - /// @param encodedVM Encoded data reflecting the content of the Wormhole Verified Action Approval. function storeHashesByEncodedVM(bytes calldata encodedVM) external { - (VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVM); - if (!valid) revert InvalidMessage(address(this), vm, reason); - if (vm.emitterChainId != wormholeSourceChainId) revert InvalidEmitterChainId(address(this), vm.emitterChainId); - if (vm.emitterAddress != reporter) revert InvalidReporter(address(this), vm.emitterAddress); - (uint256[] memory ids, bytes32[] memory _hashes) = abi.decode(vm.payload, (uint256[], bytes32[])); - for (uint256 i = 0; i < ids.length; i++) { - _storeHash(sourceChainId, ids[i], _hashes[i]); - } + (VM memory vm, bool valid, string memory reason) = WORMHOLE.parseAndVerifyVM(encodedVM); + if (!valid) revert InvalidMessage(vm, reason); + if (enabledReporters[vm.emitterChainId] != vm.emitterAddress) revert UnauthorizedWormholeReceive(); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(vm.payload, (uint256[], bytes32[])); + _storeHashes(chainIds[vm.emitterChainId], ids, hashes); } } diff --git a/packages/evm/contracts/adapters/Wormhole/WormholeHeaderReporter.sol b/packages/evm/contracts/adapters/Wormhole/WormholeHeaderReporter.sol deleted file mode 100644 index ecd05042..00000000 --- a/packages/evm/contracts/adapters/Wormhole/WormholeHeaderReporter.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { IHeaderStorage } from "../../interfaces/IHeaderStorage.sol"; -import { IWormhole } from "./IWormhole.sol"; - -contract WormholeHeaderReporter { - IWormhole public immutable wormhole; - IHeaderStorage public immutable headerStorage; - - constructor(IWormhole wormhole_, IHeaderStorage headerStorage_) { - wormhole = wormhole_; - headerStorage = headerStorage_; - } - - /// @dev Reports the given block header to the adapter via the Wormhole. - /// @param blockNumbers Uint256 array of block numbers to pass over the Wormhole. - /// @param sequence Uint64 value used to retrive generated VAA from the wormhole network. - function reportHeaders(uint256[] calldata blockNumbers) external returns (uint64 sequence) { - bytes32[] memory blockHeaders = new bytes32[](blockNumbers.length); - for (uint256 i = 0; i < blockNumbers.length; i++) { - blockHeaders[i] = headerStorage.storeBlockHeader(blockNumbers[i]); - } - bytes memory payload = abi.encode(blockNumbers, blockHeaders); - uint32 nonce = 0; - uint8 consistencyLevel = 201; - sequence = wormhole.publishMessage(nonce, payload, consistencyLevel); - } -} diff --git a/packages/evm/contracts/adapters/Wormhole/WormholeMessageRelay.sol b/packages/evm/contracts/adapters/Wormhole/WormholeMessageRelay.sol deleted file mode 100644 index 1be67dbd..00000000 --- a/packages/evm/contracts/adapters/Wormhole/WormholeMessageRelay.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { IYaho } from "../../interfaces/IYaho.sol"; -import { IWormhole } from "./IWormhole.sol"; - -contract WormholeMessageRelay { - IWormhole public immutable wormhole; - IYaho public immutable yaho; - - event MessageRelayed(address indexed emitter, uint256 indexed messageId); - - constructor(IWormhole wormhole_, IYaho yaho_) { - wormhole = wormhole_; - yaho = yaho_; - } - - function relayMessages(uint256[] memory messageIds, address) external payable returns (bytes32) { - bytes32[] memory hashes = new bytes32[](messageIds.length); - for (uint256 i = 0; i < messageIds.length; i++) { - hashes[i] = yaho.hashes(messageIds[i]); - emit MessageRelayed(address(this), messageIds[i]); - } - bytes memory payload = abi.encode(messageIds, hashes); - uint32 nonce = 0; - uint8 consistencyLevel = 201; - uint64 sequence = wormhole.publishMessage(nonce, payload, consistencyLevel); - return bytes32(abi.encode(sequence)); - } -} diff --git a/packages/evm/contracts/adapters/Wormhole/WormholeReporter.sol b/packages/evm/contracts/adapters/Wormhole/WormholeReporter.sol new file mode 100644 index 00000000..27572af2 --- /dev/null +++ b/packages/evm/contracts/adapters/Wormhole/WormholeReporter.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { Reporter } from "../Reporter.sol"; +import { IOracleAdapter } from "../../interfaces/IOracleAdapter.sol"; +import { IWormhole } from "./interfaces/IWormhole.sol"; + +contract WormholeReporter is Reporter { + string public constant PROVIDER = "wormhole"; + + IWormhole public immutable WOMRHOLE; + + constructor(address headerStorage, address yaho, address wormhole) Reporter(headerStorage, yaho) { + WOMRHOLE = IWormhole(wormhole); + } + + function _dispatch( + uint256, + address, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + bytes memory payload = abi.encode(ids, hashes); + uint32 nonce = 0; + uint8 consistencyLevel = 201; + uint64 sequence = WOMRHOLE.publishMessage(nonce, payload, consistencyLevel); + return bytes32(abi.encode(sequence)); + } +} diff --git a/packages/evm/contracts/adapters/Wormhole/IWormhole.sol b/packages/evm/contracts/adapters/Wormhole/interfaces/IWormhole.sol similarity index 100% rename from packages/evm/contracts/adapters/Wormhole/IWormhole.sol rename to packages/evm/contracts/adapters/Wormhole/interfaces/IWormhole.sol diff --git a/packages/evm/contracts/adapters/ZetaChain/ZetaAdapter.sol b/packages/evm/contracts/adapters/ZetaChain/ZetaAdapter.sol index 7d21960e..3fb5ca7d 100644 --- a/packages/evm/contracts/adapters/ZetaChain/ZetaAdapter.sol +++ b/packages/evm/contracts/adapters/ZetaChain/ZetaAdapter.sol @@ -1,35 +1,36 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ZetaReceiver, ZetaInterfaces } from "./interfaces/ZetaInterfaces.sol"; -import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; -contract ZetaAdapter is HeaderOracleAdapter, ZetaReceiver { +contract ZetaAdapter is BlockHashOracleAdapter, Ownable, ZetaReceiver { string public constant PROVIDER = "zeta"; address public immutable ZETA_CONNECTOR; - bytes public ZETA_REPORTER_ADDRESS; // Immutable - bytes32 public immutable ZETA_REPORTER_ADDRESS_HASH; + + mapping(uint256 => bytes32) public enabledReporters; error UnauthorizedZetaChainReceive(); - constructor( - uint256 reporterChain, - address reporterAddress, - address zetaConnector, - bytes memory zetaReporterAddress - ) HeaderOracleAdapter(reporterChain, reporterAddress) { + event ReporterSet(uint256 indexed chainId, address indexed reporter); + + constructor(address zetaConnector) { ZETA_CONNECTOR = zetaConnector; - ZETA_REPORTER_ADDRESS = zetaReporterAddress; - ZETA_REPORTER_ADDRESS_HASH = keccak256(zetaReporterAddress); } function onZetaMessage(ZetaInterfaces.ZetaMessage calldata zetaMessage) external { - // Auth adapted from "ZetaInteractor" contract's "isValidMessageCall" modifier + // NOTE: auth adapted from "ZetaInteractor" contract's "isValidMessageCall" modifier if ( msg.sender != ZETA_CONNECTOR || - zetaMessage.sourceChainId != REPORTER_CHAIN || - keccak256(zetaMessage.zetaTxSenderAddress) != ZETA_REPORTER_ADDRESS_HASH + enabledReporters[zetaMessage.sourceChainId] != keccak256(zetaMessage.zetaTxSenderAddress) ) revert UnauthorizedZetaChainReceive(); - _receivePayload(zetaMessage.message); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(zetaMessage.message, (uint256[], bytes32[])); + _storeHashes(zetaMessage.sourceChainId, ids, hashes); + } + + function setReporterByChainId(uint256 chainId, address reporter) external onlyOwner { + enabledReporters[chainId] = keccak256(abi.encodePacked(reporter)); + emit ReporterSet(chainId, reporter); } } diff --git a/packages/evm/contracts/adapters/ZetaChain/ZetaHeaderReporter.sol b/packages/evm/contracts/adapters/ZetaChain/ZetaHeaderReporter.sol deleted file mode 100644 index ba051dd1..00000000 --- a/packages/evm/contracts/adapters/ZetaChain/ZetaHeaderReporter.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { HeaderReporter } from "../HeaderReporter.sol"; -import { ZetaReporter } from "./ZetaReporter.sol"; - -contract ZetaHeaderReporter is HeaderReporter, ZetaReporter { - constructor( - address headerStorage, - uint256 adapterChain, - address zetaConnector, - address zetaToken, - address zetaConsumer - ) HeaderReporter(headerStorage, adapterChain) ZetaReporter(zetaConnector, zetaToken, zetaConsumer, adapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _zetaSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/ZetaChain/ZetaMessageRelay.sol b/packages/evm/contracts/adapters/ZetaChain/ZetaMessageRelay.sol deleted file mode 100644 index 8bb624d8..00000000 --- a/packages/evm/contracts/adapters/ZetaChain/ZetaMessageRelay.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -import { MessageRelay } from "../MessageRelay.sol"; -import { ZetaReporter } from "./ZetaReporter.sol"; - -contract ZetaMessageRelay is MessageRelay, ZetaReporter { - constructor( - address yaho, - uint256 adapterChain, - address zetaConnector, - address zetaToken, - address zetaConsumer - ) MessageRelay(yaho, adapterChain) ZetaReporter(zetaConnector, zetaToken, zetaConsumer, adapterChain) {} // solhint-disable no-empty-blocks - - function _sendPayload(bytes memory payload, address adapter) internal override { - _zetaSend(payload, adapter); - } -} diff --git a/packages/evm/contracts/adapters/ZetaChain/ZetaReporter.sol b/packages/evm/contracts/adapters/ZetaChain/ZetaReporter.sol index bee3a574..591a005b 100644 --- a/packages/evm/contracts/adapters/ZetaChain/ZetaReporter.sol +++ b/packages/evm/contracts/adapters/ZetaChain/ZetaReporter.sol @@ -1,33 +1,45 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { ZetaConnector, ZetaTokenConsumer, ZetaInterfaces } from "./interfaces/ZetaInterfaces.sol"; +import { Reporter } from "../Reporter.sol"; -abstract contract ZetaReporter { +contract ZetaReporter is Reporter, Ownable { using SafeERC20 for IERC20; string public constant PROVIDER = "zeta"; ZetaConnector public immutable ZETA_CONNECTOR; address public immutable ZETA_TOKEN; ZetaTokenConsumer public immutable ZETA_CONSUMER; - uint256 public immutable ZETA_ADAPTER_CHAIN; - constructor(address zetaConnector, address zetaToken, address zetaConsumer, uint256 zetaAdapterChain) { + constructor( + address headerStorage, + address yaho, + address zetaConnector, + address zetaToken, + address zetaConsumer + ) Reporter(headerStorage, yaho) { ZETA_CONNECTOR = ZetaConnector(zetaConnector); ZETA_TOKEN = zetaToken; ZETA_CONSUMER = ZetaTokenConsumer(zetaConsumer); - ZETA_ADAPTER_CHAIN = zetaAdapterChain; } - function _zetaSend(bytes memory payload, address adapter) internal { + function _dispatch( + uint256 toChainId, + address adapter, + uint256[] memory ids, + bytes32[] memory hashes + ) internal override returns (bytes32) { + bytes memory payload = abi.encode(ids, hashes); uint256 zetaAmount = ZETA_CONSUMER.getZetaFromEth{ value: msg.value }(address(this), 0); IERC20(ZETA_TOKEN).safeApprove(address(ZETA_CONNECTOR), zetaAmount); // solhint-disable-next-line check-send-result ZETA_CONNECTOR.send( ZetaInterfaces.SendInput({ - destinationChainId: ZETA_ADAPTER_CHAIN, + destinationChainId: toChainId, destinationAddress: abi.encodePacked(adapter), destinationGasLimit: 200_000, message: payload, @@ -35,5 +47,7 @@ abstract contract ZetaReporter { zetaParams: abi.encode("") }) ); + + return bytes32(0); } } diff --git a/packages/evm/contracts/adapters/axiom/AxiomV02.sol b/packages/evm/contracts/adapters/axiom/AxiomV02.sol deleted file mode 100644 index 535af775..00000000 --- a/packages/evm/contracts/adapters/axiom/AxiomV02.sol +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: MIT -// WARNING! This smart contract and the associated zk-SNARK verifiers have not been audited. -// DO NOT USE THIS CONTRACT FOR PRODUCTION -pragma solidity ^0.8.12; - -import { IAxiomV0 } from "./IAxiomV0.sol"; -import { Ownable } from "./Ownable.sol"; - -uint8 constant TREE_DEPTH = 10; -uint32 constant NUM_LEAVES = 2 ** 10; - -// array indices for reading from the ZKP calldata -uint32 constant PUBLIC_BYTES_START_IDX = 4 * 3 * 32; -uint32 constant ROOT_BYTES_START_IDX = PUBLIC_BYTES_START_IDX + 5 * 32; - -// constants for batch import of historical block hashes -uint8 constant HISTORICAL_TREE_DEPTH = 17; -uint32 constant HISTORICAL_NUM_LEAVES = 2 ** 17; -uint32 constant HISTORICAL_NUM_ROOTS = 2 ** 7; // HISTORICAL_NUM_LEAVES / NUM_LEAVES - -function calcMerkleRoot(bytes32[HISTORICAL_NUM_ROOTS] calldata leaves) pure returns (bytes32) { - uint256 len = HISTORICAL_NUM_ROOTS >> 1; - bytes32[] memory roots = new bytes32[](len); - for (uint256 i = 0; i < len; i++) { - roots[i] = keccak256(abi.encodePacked(leaves[i << 1], leaves[(i << 1) | 1])); - } - while (len > 1) { - len >>= 1; - for (uint256 i = 0; i < len; i++) { - roots[i] = keccak256(abi.encodePacked(roots[i << 1], roots[(i << 1) | 1])); - } - } - return roots[0]; -} - -contract AxiomV02 is IAxiomV0, Ownable { - string public constant VERSION = "0.2"; - - /// @notice The address of the snark verifier contract. */ - address public verifierAddress; - /** @dev The address of AxiomV0 version "0.1". We will read from the storage of the old contract for old block hashes. */ - address public oldAxiomAddress; - /// @notice The block the contract was created at - uint256 public creationBlockNumber; - - // historicalRoots[startBlockNumber] is 0 unless (startBlockNumber % NUM_LEAVES == 0) - // historicalRoots[startBlockNumber] holds the hash of - // prevHash || root || numFinal - // where - // - prevHash is the parent hash of block startBlockNumber - // - root is the partial Merkle root of blockhashes of block numbers - // [startBlockNumber, startBlockNumber + NUM_LEAVES) - // where unconfirmed block hashes are 0's - // - numFinal is the number of confirmed consecutive roots in [startBlockNumber, startBlockNumber + NUM_LEAVES) - mapping(uint32 => bytes32) internal _historicalRoots; - - event UpdateSnarkVerifierAddress(address newAddress); - - /** @dev We do not re-import all historical blockhashes in this v0.2 contract, so we use the old v0.1 contract for older block numbers */ - function historicalRoots(uint32 startBlockNumber) public view returns (bytes32) { - if (startBlockNumber < creationBlockNumber) { - return IAxiomV0(oldAxiomAddress).historicalRoots(startBlockNumber); - } else { - return _historicalRoots[startBlockNumber]; - } - } - - constructor(address _oldAddress, address _verifierAddress) { - creationBlockNumber = block.number; - oldAxiomAddress = _oldAddress; - verifierAddress = _verifierAddress; - } - - function updateSnarkVerifierAddress(address _verifierAddress) external onlyOwner { - verifierAddress = _verifierAddress; - emit UpdateSnarkVerifierAddress(_verifierAddress); - } - - function verifyRaw(bytes calldata input) private returns (bool) { - (bool success, ) = verifierAddress.call(input); - return success; - } - - function getEmptyHash(uint256 depth) public pure returns (bytes32) { - // emptyHashes[idx] is the Merkle root of a tree of depth idx with 0's as leaves - bytes32[TREE_DEPTH] memory emptyHashes = [ - bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - bytes32(0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5), - bytes32(0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30), - bytes32(0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85), - bytes32(0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344), - bytes32(0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d), - bytes32(0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968), - bytes32(0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83), - bytes32(0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af), - bytes32(0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0) - ]; - return emptyHashes[depth]; - } - - // The ZKP has block headers for [startBlockNumber, endBlockNumber] blocks. - // We extract some common information from the calldata. - function getBoundaryBlockData( - bytes calldata proofData - ) - internal - pure - returns (bytes32 prevHash, bytes32 endHash, uint32 startBlockNumber, uint32 endBlockNumber, bytes32 root) - { - prevHash = bytes32( - (uint256(bytes32(proofData[PUBLIC_BYTES_START_IDX:PUBLIC_BYTES_START_IDX + 32])) << 128) | - uint128(bytes16(proofData[PUBLIC_BYTES_START_IDX + 32 + 16:PUBLIC_BYTES_START_IDX + 2 * 32])) - ); - endHash = bytes32( - (uint256(bytes32(proofData[PUBLIC_BYTES_START_IDX + 2 * 32:PUBLIC_BYTES_START_IDX + 3 * 32])) << 128) | - uint128(bytes16(proofData[PUBLIC_BYTES_START_IDX + 3 * 32 + 16:PUBLIC_BYTES_START_IDX + 4 * 32])) - ); - startBlockNumber = uint32( - bytes4(proofData[PUBLIC_BYTES_START_IDX + 5 * 32 - 8:PUBLIC_BYTES_START_IDX + 5 * 32 - 4]) - ); - endBlockNumber = uint32(bytes4(proofData[PUBLIC_BYTES_START_IDX + 5 * 32 - 4:PUBLIC_BYTES_START_IDX + 5 * 32])); - root = bytes32( - (uint256(bytes32(proofData[ROOT_BYTES_START_IDX:ROOT_BYTES_START_IDX + 32])) << 128) | - uint128(bytes16(proofData[ROOT_BYTES_START_IDX + 48:ROOT_BYTES_START_IDX + 64])) - ); - } - - // update blocks in the "backward" direction, anchoring on a "recent" end blockhash that is within last 256 blocks - // * startBlockNumber must be a multiple of NUM_LEAVES - // * roots[idx] is the root of a Merkle tree of height 2**(TREE_DEPTH - idx) in a Merkle mountain - // range which stores block hashes in the interval [startBlockNumber, endBlockNumber] - function updateRecent(bytes calldata proofData) external { - ( - bytes32 prevHash, - bytes32 endHash, - uint32 startBlockNumber, - uint32 endBlockNumber, - bytes32 root - ) = getBoundaryBlockData(proofData); - bytes32[TREE_DEPTH] memory roots; - for (uint256 idx = 1; idx <= TREE_DEPTH; idx++) { - roots[idx - 1] = bytes32( - (uint256(bytes32(proofData[ROOT_BYTES_START_IDX + idx * 64:ROOT_BYTES_START_IDX + idx * 64 + 32])) << - 128) | - uint128( - bytes16( - proofData[ROOT_BYTES_START_IDX + idx * 64 + 16 + 32:ROOT_BYTES_START_IDX + idx * 64 + 64] - ) - ) - ); - } - - uint32 numFinal = endBlockNumber - startBlockNumber + 1; - require(numFinal <= NUM_LEAVES); // "Updating too many blocks at once"); - require(startBlockNumber % NUM_LEAVES == 0); // "startBlockNumber not a multiple of NUM_LEAVES"); - require(block.number - endBlockNumber <= 256); // "Not a recent endBlock"); - require(endBlockNumber < block.number); // "Not a recent endBlock"); - require(blockhash(endBlockNumber) == endHash); // "endHash does not match"); - require(verifyRaw(proofData)); // "ZKP does not verify"); - - if (root == bytes32(0)) { - // compute Merkle root of completed Merkle mountain range with 0s for unconfirmed blockhashes - for (uint256 round = 1; round <= TREE_DEPTH; round++) { - if (roots[TREE_DEPTH - round] != 0) { - root = keccak256(abi.encodePacked(roots[TREE_DEPTH - round], root)); - } else { - root = keccak256(abi.encodePacked(root, getEmptyHash(round - 1))); - } - } - } - _historicalRoots[startBlockNumber] = keccak256(abi.encodePacked(prevHash, root, numFinal)); - emit UpdateEvent(startBlockNumber, prevHash, root, numFinal); - } - - // update older blocks in "backwards" direction, anchoring on more recent trusted blockhash - // must be batch of NUM_LEAVES blocks - function updateOld(bytes32 nextRoot, uint32 nextNumFinal, bytes calldata proofData) external { - ( - bytes32 prevHash, - bytes32 endHash, - uint32 startBlockNumber, - uint32 endBlockNumber, - bytes32 root - ) = getBoundaryBlockData(proofData); - - require(startBlockNumber % NUM_LEAVES == 0, "aa"); // "startBlockNumber not a multiple of NUM_LEAVES"); - require(endBlockNumber - startBlockNumber == NUM_LEAVES - 1, "bb"); // "Updating with incorrect number of blocks"); - - require( - historicalRoots(endBlockNumber + 1) == keccak256(abi.encodePacked(endHash, nextRoot, nextNumFinal)), - "cc" - ); - // "endHash does not match" - require(verifyRaw(proofData)); // "ZKP does not verify") - - _historicalRoots[startBlockNumber] = keccak256(abi.encodePacked(prevHash, root, NUM_LEAVES)); - emit UpdateEvent(startBlockNumber, prevHash, root, NUM_LEAVES); - } - - function isRecentBlockHashValid(uint32 blockNumber, bytes32 claimedBlockHash) public view returns (bool) { - bytes32 blockHash = blockhash(blockNumber); - require(blockHash != 0x0); // "Must supply block hash of one of 256 most recent blocks" - return (blockHash == claimedBlockHash); - } - - function isBlockHashValid(BlockHashWitness calldata witness) public view returns (bool) { - require(witness.claimedBlockHash != 0x0); // "Claimed block hash cannot be 0" - uint32 side = witness.blockNumber % NUM_LEAVES; - uint32 startBlockNumber = witness.blockNumber - side; - bytes32 merkleRoot = historicalRoots(startBlockNumber); - require(merkleRoot != 0); // "Merkle root must be stored already" - // compute Merkle root of blockhash - bytes32 root = witness.claimedBlockHash; - for (uint8 depth = 0; depth < TREE_DEPTH; depth++) { - // 0 for left, 1 for right - if ((side >> depth) & 1 == 0) { - root = keccak256(abi.encodePacked(root, witness.merkleProof[depth])); - } else { - root = keccak256(abi.encodePacked(witness.merkleProof[depth], root)); - } - } - return (merkleRoot == keccak256(abi.encodePacked(witness.prevHash, root, witness.numFinal))); - } -} diff --git a/packages/evm/contracts/adapters/axiom/AxiomV02StoragePf.sol b/packages/evm/contracts/adapters/axiom/AxiomV02StoragePf.sol deleted file mode 100644 index 97f50fab..00000000 --- a/packages/evm/contracts/adapters/axiom/AxiomV02StoragePf.sol +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: MIT -// WARNING! This smart contract and the associated zk-SNARK verifiers have not been audited. -// DO NOT USE THIS CONTRACT FOR PRODUCTION -pragma solidity ^0.8.12; - -import { IAxiomV0 } from "./IAxiomV0.sol"; -import { IAxiomV0StoragePf } from "./IAxiomV0StoragePf.sol"; -// import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; -import { Ownable } from "./Ownable.sol"; -import { IHashi } from "../../interfaces/IHashi.sol"; -import { IOracleAdapter } from "../../interfaces/IOracleAdapter.sol"; - -uint8 constant SLOT_NUMBER = 10; - -contract AxiomV02StoragePf is Ownable, IAxiomV0StoragePf { - string public constant VERSION = "0.2"; - - address private axiomAddress; // address of deployed AxiomV0 contract - address private verifierAddress; // address of deployed ZKP verifier for storage proofs - address private hashiAddress; // address of deployed Hashi contract - - address private cryptoPunk420OwnerAtBlock10Mil; // address of attested owner of CryptoPunk#420 at Block 10000000 - - // slotAttestations[keccak256(blockNumber || addr || slot || slotValue)] = true - // if and only if it has been checked that: - // at block number `blockNumber`, the account storage of `addr` has value `slotValue` at slot `slot` - mapping(bytes32 => bool) public slotAttestations; - - event UpdateAxiomAddress(address newAddress); - event UpdateSnarkVerifierAddress(address newAddress); - - constructor(address _axiomAddress, address _verifierAddress, address _hashiAddress) { - axiomAddress = _axiomAddress; - verifierAddress = _verifierAddress; - hashiAddress = _hashiAddress; - } - - function updateAxiomAddress(address _axiomAddress) external onlyOwner { - axiomAddress = _axiomAddress; - emit UpdateAxiomAddress(_axiomAddress); - } - - function updateSnarkVerifierAddress(address _verifierAddress) external onlyOwner { - verifierAddress = _verifierAddress; - emit UpdateSnarkVerifierAddress(_verifierAddress); - } - - function isSlotAttestationValid( - uint32 blockNumber, - address addr, - uint256 slot, - uint256 slotValue - ) external view returns (bool) { - return slotAttestations[keccak256(abi.encodePacked(blockNumber, addr, slot, slotValue))]; - } - - // Verify a storage proof for 10 storage slots in a single account at a single block - function attestSlots(IAxiomV0.BlockHashWitness calldata blockData, bytes calldata proof) external { - if (block.number - blockData.blockNumber <= 256) { - require( - IAxiomV0(axiomAddress).isRecentBlockHashValid(blockData.blockNumber, blockData.claimedBlockHash), - "Block hash was not validated in cache" - ); - } else { - require(IAxiomV0(axiomAddress).isBlockHashValid(blockData), "Block hash was not validated in cache"); - } - - // Extract instances from proof - uint256 _blockHash = (uint256(bytes32(proof[384:384 + 32])) << 128) | - uint128(bytes16(proof[384 + 48:384 + 64])); - uint256 _blockNumber = uint256(bytes32(proof[384 + 64:384 + 96])); - address account = address(bytes20(proof[384 + 108:384 + 128])); - - // Check block hash and block number - require(_blockHash == uint256(blockData.claimedBlockHash), "Invalid block hash in instance"); - require(_blockNumber == blockData.blockNumber, "Invalid block number in instance"); - - (bool success, ) = verifierAddress.call(proof); - if (!success) { - revert("Proof verification failed"); - } - - for (uint16 i = 0; i < SLOT_NUMBER; i++) { - uint256 slot = (uint256(bytes32(proof[384 + 128 + 128 * i:384 + 160 + 128 * i])) << 128) | - uint128(bytes16(proof[384 + 176 + 128 * i:384 + 192 + 128 * i])); - uint256 slotValue = (uint256(bytes32(proof[384 + 192 + 128 * i:384 + 224 + 128 * i])) << 128) | - uint128(bytes16(proof[384 + 240 + 128 * i:384 + 256 + 128 * i])); - slotAttestations[keccak256(abi.encodePacked(blockData.blockNumber, account, slot, slotValue))] = true; - emit SlotAttestationEvent(blockData.blockNumber, account, slot, slotValue); - } - } - - // Verify a storage proof for 10 storage slots in a single account at a single block - function attestSlotsWithHashi( - bytes calldata proof, - uint256 domain, - uint32 blockNumber, - bytes32 blockHash, - IOracleAdapter[] memory oracleAdapters - ) external { - bytes32 hashFromHashi = IHashi(hashiAddress).getHash(domain, blockNumber, oracleAdapters); - require(hashFromHashi == blockHash, "block hash mismatch with hash block hash"); - - // Extract instances from proof - uint256 _blockHash = (uint256(bytes32(proof[384:384 + 32])) << 128) | - uint128(bytes16(proof[384 + 48:384 + 64])); - uint256 _blockNumber = uint256(bytes32(proof[384 + 64:384 + 96])); - address account = address(bytes20(proof[384 + 108:384 + 128])); - - // Check block hash and block number - require(_blockHash == uint256(blockHash), "Invalid block hash in instance"); - require(_blockNumber == blockNumber, "Invalid block number in instance"); - - (bool success, ) = verifierAddress.call(proof); - if (!success) { - revert("Proof verification failed"); - } - - for (uint16 i = 0; i < SLOT_NUMBER; i++) { - uint256 slot = (uint256(bytes32(proof[384 + 128 + 128 * i:384 + 160 + 128 * i])) << 128) | - uint128(bytes16(proof[384 + 176 + 128 * i:384 + 192 + 128 * i])); - uint256 slotValue = (uint256(bytes32(proof[384 + 192 + 128 * i:384 + 224 + 128 * i])) << 128) | - uint128(bytes16(proof[384 + 240 + 128 * i:384 + 256 + 128 * i])); - bytes32 hashedVal = keccak256(abi.encodePacked(blockNumber, account, slot, slotValue)); - slotAttestations[hashedVal] = true; - emit SlotAttestationEvent(blockNumber, account, slot, slotValue); - } - } - - // Verify a storage proof for 10 storage slots in a single account at a single block - function attestCryptoPunk420AddressWithHashi( - bytes calldata proof, - uint256 domain, - uint32 blockNumber, - bytes32 blockHash, - IOracleAdapter[] memory oracleAdapters - ) external returns (address) { - bytes32 hashFromHashi = IHashi(hashiAddress).getHash(domain, blockNumber, oracleAdapters); - require(hashFromHashi == blockHash, "block hash mismatch with hash block hash"); - - // Extract instances from proof - uint256 _blockHash = (uint256(bytes32(proof[384:384 + 32])) << 128) | - uint128(bytes16(proof[384 + 48:384 + 64])); - uint256 _blockNumber = uint256(bytes32(proof[384 + 64:384 + 96])); - address account = address(bytes20(proof[384 + 108:384 + 128])); - - // Check block hash and block number - require(_blockHash == uint256(blockHash), "Invalid block hash in instance"); - require(_blockNumber == blockNumber, "Invalid block number in instance"); - - (bool success, ) = verifierAddress.call(proof); - if (!success) { - revert("Proof verification failed"); - } - - uint256 slot = (uint256(bytes32(proof[384 + 128:384 + 160])) << 128) | - uint128(bytes16(proof[384 + 176:384 + 192])); - uint256 slotValue = (uint256(bytes32(proof[384 + 192:384 + 224])) << 128) | - uint128(bytes16(proof[384 + 240:384 + 256])); - bytes32 hashedVal = keccak256(abi.encodePacked(blockNumber, account, slot, slotValue)); - slotAttestations[hashedVal] = true; - cryptoPunk420OwnerAtBlock10Mil = address(uint160(slotValue)); - // CryptoPunk#420 address at the slot for the given block - return cryptoPunk420OwnerAtBlock10Mil; - } - - // Return the stored CryptoPunk#420 owner at block 10,000,000 on Ethereum - function getCryptoPunk420OwnerAtBlock10Mil() external view returns (address) { - return cryptoPunk420OwnerAtBlock10Mil; - } -} diff --git a/packages/evm/contracts/adapters/axiom/IAxiomV0.sol b/packages/evm/contracts/adapters/axiom/IAxiomV0.sol deleted file mode 100644 index d41d7a62..00000000 --- a/packages/evm/contracts/adapters/axiom/IAxiomV0.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -// WARNING! This smart contract and the associated zk-SNARK verifiers have not been audited. -// DO NOT USE THIS CONTRACT FOR PRODUCTION -pragma solidity ^0.8.12; - -interface IAxiomV0 { - // historicalRoots(startBlockNumber) is 0 unless (startBlockNumber % 1024 == 0) - // historicalRoots(startBlockNumber) holds the hash of - // prevHash || root || numFinal - // where - // - prevHash is the parent hash of block startBlockNumber - // - root is the partial Merkle root of blockhashes of block numbers - // [startBlockNumber, startBlockNumber + 1024) - // where unconfirmed block hashes are 0's - // - numFinal is the number of confirmed consecutive roots in [startBlockNumber, startBlockNumber + 1024) - function historicalRoots(uint32 startBlockNumber) external view returns (bytes32); - - event UpdateEvent(uint32 startBlockNumber, bytes32 prevHash, bytes32 root, uint32 numFinal); - - struct BlockHashWitness { - uint32 blockNumber; - bytes32 claimedBlockHash; - bytes32 prevHash; - uint32 numFinal; - bytes32[10] merkleProof; - } - - // returns Merkle root of a tree of depth `depth` with 0's as leaves - function getEmptyHash(uint256 depth) external pure returns (bytes32); - - // update blocks in the "backward" direction, anchoring on a "recent" end blockhash that is within last 256 blocks - // * startBlockNumber must be a multiple of 1024 - // * roots[idx] is the root of a Merkle tree of height 2**(10 - idx) in a Merkle mountain - // range which stores block hashes in the interval [startBlockNumber, endBlockNumber] - function updateRecent(bytes calldata proofData) external; - - // update older blocks in "backwards" direction, anchoring on more recent trusted blockhash - // must be batch of 1024 blocks - function updateOld(bytes32 nextRoot, uint32 nextNumFinal, bytes calldata proofData) external; - - function isRecentBlockHashValid(uint32 blockNumber, bytes32 claimedBlockHash) external view returns (bool); - - function isBlockHashValid(BlockHashWitness calldata witness) external view returns (bool); -} diff --git a/packages/evm/contracts/adapters/axiom/IAxiomV0StoragePf.sol b/packages/evm/contracts/adapters/axiom/IAxiomV0StoragePf.sol deleted file mode 100644 index 231fa355..00000000 --- a/packages/evm/contracts/adapters/axiom/IAxiomV0StoragePf.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// WARNING! This smart contract and the associated zk-SNARK verifiers have not been audited. -// DO NOT USE THIS CONTRACT FOR PRODUCTION -pragma solidity ^0.8.12; - -import "./IAxiomV0.sol"; - -interface IAxiomV0StoragePf { - // slotAttestations[keccak256(blockNumber || addr || slot || slotValue)] = true - // if and only if it has been checked that: - // at block number `blockNumber`, the account storage of `addr` has value `slotValue` at slot `slot` - function slotAttestations(bytes32 hash) external view returns (bool); - - event SlotAttestationEvent(uint32 blockNumber, address addr, uint256 slot, uint256 slotValue); - - function isSlotAttestationValid( - uint32 blockNumber, - address addr, - uint256 slot, - uint256 slotValue - ) external view returns (bool); - - // Verify a storage proof for 10 storage slots in a single account at a single block - function attestSlots(IAxiomV0.BlockHashWitness calldata blockData, bytes calldata proof) external; -} diff --git a/packages/evm/contracts/adapters/axiom/Ownable.sol b/packages/evm/contracts/adapters/axiom/Ownable.sol deleted file mode 100644 index 2e1d70be..00000000 --- a/packages/evm/contracts/adapters/axiom/Ownable.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - -pragma solidity ^0.8.4; - -import { Context } from "./utils/Context.sol"; - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} diff --git a/packages/evm/contracts/adapters/axiom/utils/Context.sol b/packages/evm/contracts/adapters/axiom/utils/Context.sol deleted file mode 100644 index 84fc8427..00000000 --- a/packages/evm/contracts/adapters/axiom/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.4; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} diff --git a/packages/evm/contracts/interfaces/IReporter.sol b/packages/evm/contracts/interfaces/IReporter.sol index d81ffeac..c9cfbacf 100644 --- a/packages/evm/contracts/interfaces/IReporter.sol +++ b/packages/evm/contracts/interfaces/IReporter.sol @@ -4,17 +4,31 @@ pragma solidity ^0.8.17; import { IOracleAdapter } from "./IOracleAdapter.sol"; interface IReporter { + event BlockDispatched( + uint256 indexed toChainId, + IOracleAdapter adapter, + uint256 indexed blockNumber, + bytes32 blockHeader + ); + event MessageDispatched( + uint256 indexed toChainId, + IOracleAdapter adapter, + uint256 indexed messageId, + bytes32 messageHash + ); + + error NotYaho(address sender, address expectedYaho); + function dispatchBlocks( uint256 toChainId, IOracleAdapter adapter, - uint256[] memory blockNumbers, - bytes32[] memory blockHeaders - ) external returns (bytes32); + uint256[] memory blockNumbers + ) external payable returns (bytes32); function dispatchMessages( uint256 toChainId, IOracleAdapter adapter, uint256[] memory messageIds, bytes32[] memory messageHashes - ) external returns (bytes32); + ) external payable returns (bytes32); } diff --git a/packages/evm/contracts/test/MockReporter.sol b/packages/evm/contracts/test/MockReporter.sol index a2ad50b7..44491b2b 100644 --- a/packages/evm/contracts/test/MockReporter.sol +++ b/packages/evm/contracts/test/MockReporter.sol @@ -1,42 +1,11 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { IReporter } from "../interfaces/IReporter.sol"; +import { Reporter } from "../adapters/Reporter.sol"; import { IOracleAdapter } from "../interfaces/IOracleAdapter.sol"; -contract MockReporter is IReporter { - address public immutable YAHO; +contract MockReporter is Reporter { + constructor(address headerStorage, address yaho) Reporter(headerStorage, yaho) {} - error NotYaho(); - - event MessageReported(uint256 toChainId, IOracleAdapter adapter, uint256 messageId, bytes32 messageHash); - - modifier onlyYaho() { - if (msg.sender != YAHO) revert NotYaho(); - _; - } - - constructor(address yaho) { - YAHO = yaho; - } - - function dispatchBlocks( - uint256 toChainId, - IOracleAdapter adapter, - uint256[] memory blockNumbers, - bytes32[] memory blockHeaders - ) external returns (bytes32) {} - - function dispatchMessages( - uint256 toChainId, - IOracleAdapter adapter, - uint256[] memory messageIds, - bytes32[] memory messageHashes - ) external onlyYaho returns (bytes32) { - // _sendPayload(abi.encode(messageIds, messageHashes)); - for (uint256 i = 0; i < messageIds.length; i++) { - emit MessageReported(toChainId, adapter, messageIds[i], messageHashes[i]); - } - return (bytes32(0)); - } + function _dispatch(uint256, address, uint256[] memory, bytes32[] memory) internal override returns (bytes32) {} } diff --git a/packages/evm/test/04_Yaho.spec.ts b/packages/evm/test/04_Yaho.spec.ts index 86799ae8..695f8256 100644 --- a/packages/evm/test/04_Yaho.spec.ts +++ b/packages/evm/test/04_Yaho.spec.ts @@ -10,6 +10,7 @@ import { Chains } from "./utils/constants" let reporter1: Contract, reporter2: Contract, + headerStorage: Contract, yaho: Contract, receiver1: SignerWithAddress, receiver2: SignerWithAddress, @@ -20,6 +21,7 @@ describe("Yaho", () => { beforeEach(async () => { const Yaho = await ethers.getContractFactory("Yaho") const Reporter = await ethers.getContractFactory("MockReporter") + const HeaderStorage = await ethers.getContractFactory("HeaderStorage") const signers = await ethers.getSigners() receiver1 = await signers[1] @@ -27,9 +29,10 @@ describe("Yaho", () => { adapter1 = await signers[3] adapter2 = await signers[4] + headerStorage = await HeaderStorage.deploy() yaho = await Yaho.deploy() - reporter1 = await Reporter.deploy(yaho.address) - reporter2 = await Reporter.deploy(yaho.address) + reporter1 = await Reporter.deploy(headerStorage.address, yaho.address) + reporter2 = await Reporter.deploy(headerStorage.address, yaho.address) }) describe("dispatchMessage", () => { @@ -143,9 +146,9 @@ describe("Yaho", () => { await expect(tx) .to.emit(yaho, "MessageDispatched") .withArgs(message.id, anyValue) // FIXME: https://github.com/NomicFoundation/hardhat/issues/3833 - .and.to.emit(reporter1, "MessageReported") + .and.to.emit(reporter1, "MessageDispatched") .withArgs(toChainId, adapter1.address, message.id, await yaho.calculateMessageHash(message.serialize())) - .and.to.emit(reporter2, "MessageReported") + .and.to.emit(reporter2, "MessageDispatched") .withArgs(toChainId, adapter2.address, message.id, await yaho.calculateMessageHash(message.serialize())) expect(await yaho.getPendingMessageHash(message.id)).to.be.eq(toBytes32(0)) }) @@ -227,13 +230,13 @@ describe("Yaho", () => { await expect(tx) .to.emit(yaho, "MessageDispatched") .withArgs(message1.id, anyValue) // FIXME: https://github.com/NomicFoundation/hardhat/issues/3833 - .and.to.emit(reporter1, "MessageReported") + .and.to.emit(reporter1, "MessageDispatched") .withArgs(toChainId, adapter1.address, message1.id, await yaho.calculateMessageHash(message1.serialize())) - .and.to.emit(reporter1, "MessageReported") + .and.to.emit(reporter1, "MessageDispatched") .withArgs(toChainId, adapter1.address, message2.id, await yaho.calculateMessageHash(message2.serialize())) - .and.to.emit(reporter2, "MessageReported") + .and.to.emit(reporter2, "MessageDispatched") .withArgs(toChainId, adapter2.address, message1.id, await yaho.calculateMessageHash(message1.serialize())) - .and.to.emit(reporter2, "MessageReported") + .and.to.emit(reporter2, "MessageDispatched") .withArgs(toChainId, adapter2.address, message2.id, await yaho.calculateMessageHash(message2.serialize())) expect(await yaho.getPendingMessageHash(message1.id)).to.be.eq(toBytes32(0)) expect(await yaho.getPendingMessageHash(message2.id)).to.be.eq(toBytes32(0)) @@ -410,13 +413,13 @@ describe("Yaho", () => { await yaho.calculateMessageHash(message2.serialize()), ) await expect(yaho.relayMessagesToAdapters([message1, message2])) - .to.emit(reporter1, "MessageReported") + .to.emit(reporter1, "MessageDispatched") .withArgs(toChainId, adapter1.address, message1.id, await yaho.calculateMessageHash(message1.serialize())) - .and.to.emit(reporter1, "MessageReported") + .and.to.emit(reporter1, "MessageDispatched") .withArgs(toChainId, adapter1.address, message2.id, await yaho.calculateMessageHash(message2.serialize())) - .and.to.emit(reporter2, "MessageReported") + .and.to.emit(reporter2, "MessageDispatched") .withArgs(toChainId, adapter2.address, message1.id, await yaho.calculateMessageHash(message1.serialize())) - .and.to.emit(reporter2, "MessageReported") + .and.to.emit(reporter2, "MessageDispatched") .withArgs(toChainId, adapter2.address, message2.id, await yaho.calculateMessageHash(message2.serialize())) expect(await yaho.getPendingMessageHash(message1.id)).to.be.eq(toBytes32(0)) expect(await yaho.getPendingMessageHash(message2.id)).to.be.eq(toBytes32(0)) diff --git a/packages/evm/test/05_Yaru.spec.ts b/packages/evm/test/05_Yaru.spec.ts index e719ec03..59e21666 100644 --- a/packages/evm/test/05_Yaru.spec.ts +++ b/packages/evm/test/05_Yaru.spec.ts @@ -9,6 +9,7 @@ let reporter1: Contract, reporter2: Contract, reporter3: Contract, reporter4: Contract, + headerStorage: Contract, yaho: Contract, yaru: Contract, hashi: Contract, @@ -27,14 +28,16 @@ describe("Yaru", () => { const Reporter = await ethers.getContractFactory("MockReporter") const Adapter = await ethers.getContractFactory("MockOracleAdapter") const PingPong = await ethers.getContractFactory("PingPong") + const HeaderStorage = await ethers.getContractFactory("HeaderStorage") hashi = await Hashi.deploy() yaho = await Yaho.deploy() + headerStorage = await HeaderStorage.deploy() yaru = await Yaru.deploy(hashi.address, yaho.address, Chains.Hardhat) - reporter1 = await Reporter.deploy(yaho.address) - reporter2 = await Reporter.deploy(yaho.address) - reporter3 = await Reporter.deploy(yaho.address) - reporter4 = await Reporter.deploy(yaho.address) + reporter1 = await Reporter.deploy(headerStorage.address, yaho.address) + reporter2 = await Reporter.deploy(headerStorage.address, yaho.address) + reporter3 = await Reporter.deploy(headerStorage.address, yaho.address) + reporter4 = await Reporter.deploy(headerStorage.address, yaho.address) adapter1 = await Adapter.deploy() adapter2 = await Adapter.deploy() adapter3 = await Adapter.deploy() diff --git a/packages/evm/test/adapters/03_Reporter.spec.ts b/packages/evm/test/adapters/03_Reporter.spec.ts new file mode 100644 index 00000000..4e05fbde --- /dev/null +++ b/packages/evm/test/adapters/03_Reporter.spec.ts @@ -0,0 +1,69 @@ +import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs" +import { mine } from "@nomicfoundation/hardhat-network-helpers" +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address" +import { expect } from "chai" +import { Contract } from "ethers" +import { ethers, network } from "hardhat" + +import { toBytes32 } from "../utils" +import { Chains } from "../utils/constants" + +let reporter: Contract, + headerStorage: Contract, + fakeYaho: SignerWithAddress, + fakeAdapter: SignerWithAddress, + user: SignerWithAddress + +describe("Reporter", () => { + beforeEach(async () => { + await network.provider.request({ method: "hardhat_reset", params: [] }) + + const HeaderStorage = await ethers.getContractFactory("HeaderStorage") + const Reporter = await ethers.getContractFactory("MockReporter") + + const signers = await ethers.getSigners() + user = signers[0] + fakeAdapter = signers[1] + fakeYaho = signers[2] + + headerStorage = await HeaderStorage.deploy() + reporter = await Reporter.deploy(headerStorage.address, fakeYaho.address) + }) + + describe("dispatchBlocks", () => { + it("should be able to dispatch 2 blocks", async () => { + const toChainId = Chains.Gnosis + const blockNumbers = [998, 999] + await mine(1000) + await expect(reporter.dispatchBlocks(toChainId, fakeAdapter.address, blockNumbers)) + .to.emit(reporter, "BlockDispatched") + .withArgs(toChainId, fakeAdapter.address, blockNumbers[0], anyValue) + .and.to.emit(reporter, "BlockDispatched") + .withArgs(toChainId, fakeAdapter.address, blockNumbers[1], anyValue) + }) + }) + + describe("dispatchMessages", () => { + it("should not be able to call dispatchMessages if it's not yaho", async () => { + const toChainId = Chains.Gnosis + const messageIds = [1, 2] + const messageHashes = [toBytes32(1), toBytes32(2)] + await expect(reporter.dispatchMessages(toChainId, fakeAdapter.address, messageIds, messageHashes)) + .to.be.revertedWithCustomError(reporter, "NotYaho") + .withArgs(user.address, fakeYaho.address) + }) + + it("should be able to dispatch 2 messages", async () => { + const toChainId = Chains.Gnosis + const messageIds = [1, 2] + const messageHashes = [toBytes32(1), toBytes32(2)] + await expect( + reporter.connect(fakeYaho).dispatchMessages(toChainId, fakeAdapter.address, messageIds, messageHashes), + ) + .to.emit(reporter, "MessageDispatched") + .withArgs(toChainId, fakeAdapter.address, messageIds[0], messageHashes[0]) + .and.to.emit(reporter, "MessageDispatched") + .withArgs(toChainId, fakeAdapter.address, messageIds[1], messageHashes[1]) + }) + }) +}) diff --git a/packages/evm/test/adapters/AMB/01_AMBAdapter.spec.ts b/packages/evm/test/adapters/AMB/01_AMBAdapter.spec.ts deleted file mode 100644 index 16df4a81..00000000 --- a/packages/evm/test/adapters/AMB/01_AMBAdapter.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { expect } from "chai" -import { ethers, network } from "hardhat" - -const GAS = 10000000 -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" -const ID_ONE = 1 -const ID_TWO = 2 -const HASH_ONE = "0x0000000000000000000000000000000000000000000000000000000000000001" -const HASH_TWO = "0x0000000000000000000000000000000000000000000000000000000000000002" - -const setup = async () => { - await network.provider.request({ method: "hardhat_reset", params: [] }) - const [wallet] = await ethers.getSigners() - const HeaderStorage = await ethers.getContractFactory("HeaderStorage") - const headerStorage = await HeaderStorage.deploy() - const AMB = await ethers.getContractFactory("MockAMB") - const amb = await AMB.deploy() - const AMBAdapter = await ethers.getContractFactory("AMBAdapter") - const ambAdapter = await AMBAdapter.deploy(amb.address, wallet.address, DOMAIN_ID) - return { - wallet, - amb, - headerStorage, - ambAdapter, - } -} - -describe("AMBAdapter", function () { - describe("Constructor", function () { - it("Successfully deploys contract with correct state", async function () { - const { amb, ambAdapter, wallet } = await setup() - expect(await ambAdapter.deployed()) - expect(await ambAdapter.amb()).to.equal(amb.address) - expect(await ambAdapter.reporter()).to.equal(wallet.address) - expect(await ambAdapter.chainId()).to.equal(DOMAIN_ID) - }) - }) - - describe("StoreHashes()", function () { - it("Stores hashes", async function () { - const { amb, ambAdapter } = await setup() - const call = await ambAdapter.populateTransaction.storeHashes([ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO]) - await amb.requireToPassMessage(ambAdapter.address, call.data, GAS) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - }) - it("Overwrites previous hashes", async function () { - const { amb, ambAdapter } = await setup() - let call = await ambAdapter.populateTransaction.storeHashes([ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO]) - await amb.requireToPassMessage(ambAdapter.address, call.data, GAS) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - call = await ambAdapter.populateTransaction.storeHashes([ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO]) - await amb.requireToPassMessage(ambAdapter.address, call.data, GAS) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) - }) - }) - - describe("getHashFromOracle()", function () { - it("Returns 0 if no header is stored", async function () { - const { ambAdapter } = await setup() - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - }) - it("Returns stored hash", async function () { - const { amb, ambAdapter } = await setup() - const call = await ambAdapter.populateTransaction.storeHashes([ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO]) - await amb.requireToPassMessage(ambAdapter.address, call.data, GAS) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - }) - }) -}) diff --git a/packages/evm/test/adapters/AMB/02_AMBHeaderReporter.spec.ts b/packages/evm/test/adapters/AMB/02_AMBHeaderReporter.spec.ts deleted file mode 100644 index 3070ed4a..00000000 --- a/packages/evm/test/adapters/AMB/02_AMBHeaderReporter.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { mine } from "@nomicfoundation/hardhat-network-helpers" -import { expect } from "chai" -import { ethers, network } from "hardhat" - -const GAS = 1000000 -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" - -const setup = async () => { - await network.provider.request({ method: "hardhat_reset", params: [] }) - const [wallet] = await ethers.getSigners() - const HeaderStorage = await ethers.getContractFactory("HeaderStorage") - const headerStorage = await HeaderStorage.deploy() - const AMB = await ethers.getContractFactory("MockAMB") - const amb = await AMB.deploy() - const AMBHeaderReporter = await ethers.getContractFactory("AMBHeaderReporter") - const ambHeaderReporter = await AMBHeaderReporter.deploy(amb.address, headerStorage.address) - const AMBAdapter = await ethers.getContractFactory("AMBAdapter") - const ambAdapter = await AMBAdapter.deploy(amb.address, ambHeaderReporter.address, DOMAIN_ID) - await mine(1000) - return { - wallet, - headerStorage, - ambHeaderReporter, - ambAdapter, - } -} - -describe("AMBHeaderReporter", function () { - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { ambHeaderReporter } = await setup() - expect(await ambHeaderReporter.deployed()) - }) - }) - - describe("reportHeaders()", function () { - it("Reports headers to AMB", async function () { - const { ambHeaderReporter, ambAdapter } = await setup() - const block = await ethers.provider._getBlock(999) - const block2 = await ethers.provider._getBlock(998) - await expect(ambHeaderReporter.reportHeaders([999, 998], ambAdapter.address, GAS)) - .to.emit(ambHeaderReporter, "HeaderReported") - .withArgs(ambHeaderReporter.address, 999, block.hash) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, 999)).to.equal(block.hash) - expect(await ambAdapter.getHashFromOracle(DOMAIN_ID, 998)).to.equal(block2.hash) - }) - it("Reports headers to AMB", async function () { - const { ambHeaderReporter, ambAdapter } = await setup() - const block = await ethers.provider._getBlock(999) - const block2 = await ethers.provider._getBlock(998) - const receipt = await ambHeaderReporter.reportHeaders([999, 998], ambAdapter.address, GAS) - await expect(receipt) - .to.emit(ambHeaderReporter, "HeaderReported") - .withArgs(ambHeaderReporter.address, 999, block.hash) - await expect(receipt) - .to.emit(ambHeaderReporter, "HeaderReported") - .withArgs(ambHeaderReporter.address, 998, block2.hash) - }) - it("Returns receipt", async function () { - const { ambHeaderReporter, ambAdapter } = await setup() - const receipt = await ambHeaderReporter.callStatic.reportHeaders([999], ambAdapter.address, GAS) - expect(receipt).is.not.null - }) - }) -}) diff --git a/packages/evm/test/adapters/AMB/03_AMBMessageRelay.spec.ts b/packages/evm/test/adapters/AMB/03_AMBMessageRelay.spec.ts deleted file mode 100644 index bf9218e9..00000000 --- a/packages/evm/test/adapters/AMB/03_AMBMessageRelay.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { expect } from "chai" -import { ethers, network } from "hardhat" - -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" - -const setup = async () => { - await network.provider.request({ method: "hardhat_reset", params: [] }) - const [wallet] = await ethers.getSigners() - const Yaho = await ethers.getContractFactory("Yaho") - const yaho = await Yaho.deploy() - const AMB = await ethers.getContractFactory("MockAMB") - const amb = await AMB.deploy() - const AMBMessageRelay = await ethers.getContractFactory("AMBMessageRelay") - const ambMessageRelay = await AMBMessageRelay.deploy(amb.address, yaho.address) - const AMBAdapter = await ethers.getContractFactory("AMBAdapter") - const ambAdapter = await AMBAdapter.deploy(amb.address, ambMessageRelay.address, DOMAIN_ID) - 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]) - - return { - amb, - wallet, - yaho, - ambMessageRelay, - ambAdapter, - message_1, - pingPong, - } -} - -describe("AMBMessageRelayer", function () { - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { ambMessageRelay } = await setup() - expect(await ambMessageRelay.deployed()) - }) - }) - - describe("relayMessages()", function () { - it("Relays message hashes over AMB", async function () { - const { ambMessageRelay, ambAdapter } = await setup() - const receipt = await ambMessageRelay.relayMessages([0, 1], ambAdapter.address) - await expect(receipt).to.emit(ambMessageRelay, "MessageRelayed").withArgs(ambMessageRelay.address, 0) - }) - it("Reports headers to AMB", async function () { - const { ambMessageRelay, amb, ambAdapter, message_1, yaho, wallet } = await setup() - const receipt = await ambMessageRelay.relayMessages([0, 1], ambAdapter.address) - await expect(receipt).to.emit(ambMessageRelay, "MessageRelayed").withArgs(ambMessageRelay.address, 0) - await expect(receipt).to.emit(ambMessageRelay, "MessageRelayed").withArgs(ambMessageRelay.address, 1) - const hash0 = await yaho.calculateHash(network.config.chainId, 0, yaho.address, wallet.address, message_1) - const hash1 = await yaho.calculateHash(network.config.chainId, 1, yaho.address, wallet.address, message_1) - const tx = await ambAdapter.populateTransaction.storeHashes([0, 1], [hash0, hash1]) - await expect(receipt).to.emit(amb, "MessagePassed").withArgs(ambMessageRelay.address, tx.data) - }) - it("Returns receipt", async function () { - const { ambMessageRelay, ambAdapter } = await setup() - const receipt = await ambMessageRelay.callStatic.relayMessages([0, 1], ambAdapter.address) - expect(receipt).is.not.null - }) - }) -}) diff --git a/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts deleted file mode 100644 index 8bb69bfa..00000000 --- a/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { expect } from "chai" -import { ethers } from "hardhat" - -import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" - -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" - -describe("PNetworkMessageRelay", function () { - describe("Native Network", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - const Yaho = await ethers.getContractFactory("Yaho") - const yaho = await Yaho.deploy() - await deployErc1820Registry(wallet) - const ERC777Token = await ethers.getContractFactory("ERC777Token") - const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) - const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) - const Vault = await ethers.getContractFactory("MockVault") - const vault = await Vault.deploy() - await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") - - const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") - const pNetworkMessageRelay = await PNetworkMessageRelay.deploy( - yaho.address, - DOMAIN_ID, - vault.address, - erc777Token.address, - "0x005fe7f9", - ) - - await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") - - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - pNetworkMessageRelay.address, - vault.address, - pNetworkMessageRelay.address, - "0x005fe7f9", - ) - - 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]) - - return { - erc777Token, - anotherErc777Token, - vault, - wallet, - yaho, - pNetworkMessageRelay, - pNetworkAdapter, - message_1, - pingPong, - } - } - - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { pNetworkMessageRelay, erc777Token, vault, yaho } = await setup() - expect(await pNetworkMessageRelay.deployed()) - expect(await pNetworkMessageRelay.TOKEN()).to.equal(erc777Token.address) - expect(await pNetworkMessageRelay.VAULT()).to.equal(vault.address) - expect(await pNetworkMessageRelay.YAHO()).to.equal(yaho.address) - }) - }) - - describe("relayMessages()", function () { - it("Relays message hashes over pNetwork", async function () { - const { pNetworkMessageRelay, pNetworkAdapter, vault, erc777Token, yaho } = await setup() - const ids = [0, 1] - const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) - const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [ids, hashes]) - await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) - .to.emit(pNetworkMessageRelay, "MessageRelayed") - .withArgs(pNetworkMessageRelay.address, 0) - .and.to.emit(vault, "PegIn") - .withArgs( - erc777Token.address, - pNetworkMessageRelay.address, - 1, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x12345678", - "0x005fe7f9", - ) - }) - }) - }) - - describe("Host Network", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - const Yaho = await ethers.getContractFactory("Yaho") - const yaho = await Yaho.deploy() - await deployErc1820Registry(wallet) - const PToken = await ethers.getContractFactory("PToken") - const pToken = await PToken.deploy("pToken", "P", []) - const anotherPToken = await PToken.deploy("Another ERC777 Token", "A777", []) - const Vault = await ethers.getContractFactory("MockVault") - const vault = await Vault.deploy() - await vault.initialize([pToken.address, anotherPToken.address], "0x12345678") - - const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") - const pNetworkMessageRelay = await PNetworkMessageRelay.deploy( - yaho.address, - DOMAIN_ID, - ZERO_ADDRESS, - pToken.address, - "0x005fe7f9", - ) - - await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") - - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - pNetworkMessageRelay.address, - vault.address, - pNetworkMessageRelay.address, - "0x005fe7f9", - ) - - 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]) - - return { - pToken, - anotherPToken, - vault, - wallet, - yaho, - pNetworkMessageRelay, - pNetworkAdapter, - } - } - - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { pNetworkMessageRelay, pToken, yaho } = await setup() - expect(await pNetworkMessageRelay.deployed()) - expect(await pNetworkMessageRelay.TOKEN()).to.equal(pToken.address) - expect(await pNetworkMessageRelay.VAULT()).to.equal(ZERO_ADDRESS) - expect(await pNetworkMessageRelay.YAHO()).to.equal(yaho.address) - }) - }) - - describe("relayMessages()", function () { - it("Relays message hashes over pNetwork", async function () { - const { pNetworkMessageRelay, pNetworkAdapter, pToken, yaho } = await setup() - const ids = [0, 1] - const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) - const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [ids, hashes]) - await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) - .to.emit(pNetworkMessageRelay, "MessageRelayed") - .withArgs(pNetworkMessageRelay.address, 0) - .and.to.emit(pToken, "Redeem") - .withArgs( - pNetworkMessageRelay.address, - 1, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x87654321", - "0x005fe7f9", - ) - }) - }) - }) -}) diff --git a/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts b/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts deleted file mode 100644 index af1984cb..00000000 --- a/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { expect } from "chai" -import { ethers } from "hardhat" - -import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" - -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" - -describe("PNetworkHeaderReporter", function () { - describe("Native Network", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - const HeaderStorage = await ethers.getContractFactory("HeaderStorage") - const headerStorage = await HeaderStorage.deploy() - await deployErc1820Registry(wallet) - const ERC777Token = await ethers.getContractFactory("ERC777Token") - const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) - const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) - const Vault = await ethers.getContractFactory("MockVault") - const vault = await Vault.deploy() - await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") - - const PNetworkHeaderReporter = await ethers.getContractFactory("PNetworkHeaderReporter") - const pNetworkHeaderReporter = await PNetworkHeaderReporter.deploy( - headerStorage.address, - DOMAIN_ID, - vault.address, - erc777Token.address, - "0x005fe7f9", - ) - - await erc777Token.connect(wallet).send(pNetworkHeaderReporter.address, 10000, "0x") - - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - pNetworkHeaderReporter.address, - vault.address, - pNetworkHeaderReporter.address, - "0x005fe7f9", - ) - - return { - erc777Token, - anotherErc777Token, - vault, - wallet, - headerStorage, - pNetworkHeaderReporter, - pNetworkAdapter, - } - } - - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { pNetworkHeaderReporter, erc777Token, vault, headerStorage } = await setup() - expect(await pNetworkHeaderReporter.deployed()) - expect(await pNetworkHeaderReporter.TOKEN()).to.equal(erc777Token.address) - expect(await pNetworkHeaderReporter.VAULT()).to.equal(vault.address) - expect(await pNetworkHeaderReporter.HEADER_STORAGE()).to.equal(headerStorage.address) - }) - }) - - describe("reportHeaders()", function () { - it("Reports headers over pNetwork", async function () { - const { pNetworkHeaderReporter, pNetworkAdapter, vault, erc777Token, headerStorage } = await setup() - const blockIds = [0, 1] - const blocks = await Promise.all(blockIds.map((_id) => ethers.provider._getBlock(_id))) - const hashes = blocks.map((_block) => _block.hash) - const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [blockIds, hashes]) - await expect(pNetworkHeaderReporter.reportHeaders(blockIds, pNetworkAdapter.address)) - .to.emit(pNetworkHeaderReporter, "HeaderReported") - .withArgs(pNetworkHeaderReporter.address, blockIds[0], hashes[0]) - .and.to.emit(pNetworkHeaderReporter, "HeaderReported") - .withArgs(pNetworkHeaderReporter.address, blockIds[1], hashes[1]) - .and.to.emit(vault, "PegIn") - .withArgs( - erc777Token.address, - pNetworkHeaderReporter.address, - 1, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x12345678", - "0x005fe7f9", - ) - expect(await headerStorage.headers(blockIds[0])).to.equal(hashes[0]) - expect(await headerStorage.headers(blockIds[1])).to.equal(hashes[1]) - }) - }) - }) - - describe("Host Network", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - const HeaderStorage = await ethers.getContractFactory("HeaderStorage") - const headerStorage = await HeaderStorage.deploy() - await deployErc1820Registry(wallet) - const PToken = await ethers.getContractFactory("PToken") - const pToken = await PToken.deploy("pToken", "P", []) - const anotherPToken = await PToken.deploy("Another ERC777 Token", "A777", []) - const Vault = await ethers.getContractFactory("MockVault") - const vault = await Vault.deploy() - await vault.initialize([pToken.address, anotherPToken.address], "0x12345678") - - const PNetworkHeaderReporter = await ethers.getContractFactory("PNetworkHeaderReporter") - const pNetworkHeaderReporter = await PNetworkHeaderReporter.deploy( - headerStorage.address, - DOMAIN_ID, - ZERO_ADDRESS, - pToken.address, - "0x005fe7f9", - ) - - await pToken.connect(wallet).send(pNetworkHeaderReporter.address, 10000, "0x") - - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - pNetworkHeaderReporter.address, - vault.address, - pNetworkHeaderReporter.address, - "0x005fe7f9", - ) - - return { - pToken, - anotherPToken, - vault, - wallet, - headerStorage, - pNetworkHeaderReporter, - pNetworkAdapter, - } - } - - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { pNetworkHeaderReporter, pToken, headerStorage } = await setup() - expect(await pNetworkHeaderReporter.deployed()) - expect(await pNetworkHeaderReporter.TOKEN()).to.equal(pToken.address) - expect(await pNetworkHeaderReporter.VAULT()).to.equal(ZERO_ADDRESS) - expect(await pNetworkHeaderReporter.HEADER_STORAGE()).to.equal(headerStorage.address) - }) - }) - - describe("reportHeaders()", function () { - it("Reports headers over pNetwork", async function () { - const { pNetworkHeaderReporter, pNetworkAdapter, pToken, headerStorage } = await setup() - const blockIds = [0, 1] - const blocks = await Promise.all(blockIds.map((_id) => ethers.provider._getBlock(_id))) - const hashes = blocks.map((_block) => _block.hash) - const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [blockIds, hashes]) - await expect(pNetworkHeaderReporter.reportHeaders(blockIds, pNetworkAdapter.address)) - .to.emit(pNetworkHeaderReporter, "HeaderReported") - .withArgs(pNetworkHeaderReporter.address, blockIds[0], hashes[0]) - .and.to.emit(pNetworkHeaderReporter, "HeaderReported") - .withArgs(pNetworkHeaderReporter.address, blockIds[1], hashes[1]) - .and.to.emit(pToken, "Redeem") - .withArgs( - pNetworkHeaderReporter.address, - 1, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x87654321", - "0x005fe7f9", - ) - expect(await headerStorage.headers(blockIds[0])).to.equal(hashes[0]) - expect(await headerStorage.headers(blockIds[1])).to.equal(hashes[1]) - }) - }) - }) -}) diff --git a/packages/evm/test/adapters/PNetwork/03_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/PNetwork/03_PNetworkAdapter.spec.ts deleted file mode 100644 index 75012675..00000000 --- a/packages/evm/test/adapters/PNetwork/03_PNetworkAdapter.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { expect } from "chai" -import { ethers } from "hardhat" - -import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" - -const REPORTER_ADDRESS = "0xd5e099c71b797516c10ed0f0d895f429c2781142" -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" -const ID_ONE = 1 -const ID_TWO = 2 -const HASH_ZERO = "0x0000000000000000000000000000000000000000000000000000000000000000" -const HASH_ONE = "0x0000000000000000000000000000000000000000000000000000000000000001" -const HASH_TWO = "0x0000000000000000000000000000000000000000000000000000000000000002" - -const encodeToMetadata = (userData: string, originChainId: string, sender: string, version = 3) => - new ethers.utils.AbiCoder().encode( - ["bytes1", "bytes", "bytes4", "address"], - [version, userData, originChainId, sender], - ) - -describe("PNetworkAdapter", function () { - describe("Host Blockchain", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - await deployErc1820Registry(wallet) - const PToken = await ethers.getContractFactory("PToken") - const pToken = await PToken.deploy("pToken", "P", []) - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - REPORTER_ADDRESS, - ZERO_ADDRESS, - pToken.address, - "0x005fe7f9", - ) - return { - wallet, - pNetworkAdapter, - pToken, - } - } - - describe("Constructor", function () { - it("Successfully deploys contract with correct state", async function () { - const { pNetworkAdapter, pToken } = await setup() - - expect(await pNetworkAdapter.deployed()) - expect(await pNetworkAdapter.TOKEN()).to.equal(pToken.address) - expect(await pNetworkAdapter.VAULT()).to.equal(ZERO_ADDRESS) - }) - }) - - describe("StoreHashes()", function () { - it("Should store hashes", async function () { - const { pNetworkAdapter, wallet, pToken } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - }) - - it("Should not store hashes when receiving another token", async function () { - const { pNetworkAdapter, wallet, pToken } = await setup() - - const PToken = await ethers.getContractFactory("PToken") - const fakePToken = await PToken.deploy("pToken", "P", []) - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")) - .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidToken") - .withArgs(fakePToken.address, pToken.address) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Should not store hashes when tokens are not minted", async function () { - const { pNetworkAdapter, wallet, pToken } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") - .withArgs(wallet.address, ZERO_ADDRESS) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Overwrites previous hashes", async function () { - const { pNetworkAdapter, wallet, pToken } = await setup() - const coder = new ethers.utils.AbiCoder() - let userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - let data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_TWO, ID_ONE], - [HASH_ONE, HASH_TWO], - ], - ) - data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) - }) - - it("Returns 0 if no header is stored", async function () { - const { pNetworkAdapter } = await setup() - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - }) - }) - }) - - describe("Native Blockchain", () => { - const setup = async () => { - await resetNetwork() - const [wallet] = await ethers.getSigners() - await deployErc1820Registry(wallet) - const ERC777Token = await ethers.getContractFactory("ERC777Token") - const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) - const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) - const Vault = await ethers.getContractFactory("MockVault") - const vault = await Vault.deploy() - await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") - - const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - REPORTER_ADDRESS, - vault.address, - erc777Token.address, - "0x005fe7f9", - ) - - const coder = new ethers.utils.AbiCoder() - const data = coder.encode( - ["bytes32", "string", "bytes4"], - [ethers.utils.keccak256(ethers.utils.toUtf8Bytes("ERC777-pegIn")), "destination-address", "0x87654321"], - ) - await expect(erc777Token.connect(wallet).send(vault.address, 100, data)).to.emit(vault, "PegIn") - await expect(anotherErc777Token.connect(wallet).send(vault.address, 100, data)).to.emit(vault, "PegIn") - return { - wallet, - vault, - pNetworkAdapter, - erc777Token, - anotherErc777Token, - } - } - - describe("Constructor", function () { - it("Successfully deploys contract with correct state", async function () { - const { pNetworkAdapter, erc777Token, vault } = await setup() - expect(await pNetworkAdapter.deployed()) - expect(await pNetworkAdapter.TOKEN()).to.equal(erc777Token.address) - expect(await pNetworkAdapter.VAULT()).to.equal(vault.address) - }) - }) - - describe("StoreHashes()", function () { - it("Stores hashes", async function () { - const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - }) - - it("Should not store hashes when receiving another token", async function () { - const { pNetworkAdapter, wallet, vault, erc777Token, anotherErc777Token } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidToken") - .withArgs(anotherErc777Token.address, erc777Token.address) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Should not store hashes when tokens are not sent by the vault", async function () { - const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") - .withArgs(wallet.address, vault.address) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Should not store hashes when data is received from another chain", async function () { - const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x00e4b170", REPORTER_ADDRESS) - await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidNetworkId") - .withArgs("0x00e4b170", "0x005fe7f9") - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Should not store hashes when data originated from another address", async function () { - const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() - const WRONG_REPORTER_ADDRESS = "0xa5e099c71b797516c10ed0f0d895f429c2781142" - - const coder = new ethers.utils.AbiCoder() - const userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - const data = encodeToMetadata(userData, "0x005fe7f9", WRONG_REPORTER_ADDRESS) - await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "UnauthorizedPNetworkReceive") - .withArgs() - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - - it("Overwrites previous hashes", async function () { - const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() - const coder = new ethers.utils.AbiCoder() - let userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], - ) - let data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - userData = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_TWO, ID_ONE], - [HASH_ONE, HASH_TWO], - ], - ) - data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) - }) - - it("Returns 0 if no header is stored", async function () { - const { pNetworkAdapter } = await setup() - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - }) - }) - }) -}) diff --git a/packages/evm/test/adapters/PNetwork/utils.spec.ts b/packages/evm/test/adapters/PNetwork/utils.spec.ts deleted file mode 100644 index ad1cadfb..00000000 --- a/packages/evm/test/adapters/PNetwork/utils.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" -import { ethers, network } from "hardhat" - -const ERC1820_DEPLOYER = "0xa990077c3205cbDf861e17Fa532eeB069cE9fF96" -const ERC1820_PAYLOAD = - "0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820" - -export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" - -export const resetNetwork = async () => { - await network.provider.request({ method: "hardhat_reset", params: [] }) -} - -export const deployErc1820Registry = async (wallet: SignerWithAddress) => { - // deploy erc1820 registry - await ethers.provider.send("eth_sendTransaction", [ - { - from: wallet.address, - to: ERC1820_DEPLOYER, - value: "0x11c37937e080000", - }, - ]) - await ethers.provider.send("eth_sendRawTransaction", [ERC1820_PAYLOAD]) -} diff --git a/packages/evm/test/adapters/Sygma/01_SygmaAdapter.spec.ts b/packages/evm/test/adapters/Sygma/01_SygmaAdapter.spec.ts deleted file mode 100644 index d5ada782..00000000 --- a/packages/evm/test/adapters/Sygma/01_SygmaAdapter.spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { expect } from "chai" -import { ethers, network } from "hardhat" - -const DOMAIN_ID = 5 -const ID_ONE = 1 -const ID_TWO = 2 -const HASH_ONE = "0x0000000000000000000000000000000000000000000000000000000000000001" -const HASH_TWO = "0x0000000000000000000000000000000000000000000000000000000000000002" - -const setup = async () => { - await network.provider.request({ method: "hardhat_reset", params: [] }) - const signers = await ethers.getSigners() - const admin = signers[0] - const reporter = signers[1] - const handler = signers[2] - const otherAddress = signers[3] - const SygmaAdapter = await ethers.getContractFactory("SygmaAdapter") - const sygmaAdapter = await SygmaAdapter.deploy(handler.address) - return { - admin, - reporter, - handler, - otherAddress, - sygmaAdapter, - } -} - -describe("SygmaAdapter", function () { - describe("Constructor", function () { - it("Successfully deploys contract with correct state", async function () { - const { handler, sygmaAdapter } = await setup() - expect(await sygmaAdapter.deployed()) - expect(await sygmaAdapter._handler()).to.equal(handler.address) - }) - }) - - describe("setReporter()", function () { - it("Enables the reporter and sets its chainID", async function () { - const { reporter, sygmaAdapter } = await setup() - expect(await sygmaAdapter.deployed()) - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - const result = await sygmaAdapter.reporters(reporter.address) - expect(result[0].toNumber()).to.equal(DOMAIN_ID) - expect(result[1]).to.equal(true) - }) - - it("Disables the reporter", async function () { - const { reporter, sygmaAdapter } = await setup() - expect(await sygmaAdapter.deployed()) - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - let result = await sygmaAdapter.reporters(reporter.address) - expect(result[1]).to.equal(true) - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, false)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, false) - result = await sygmaAdapter.reporters(reporter.address) - expect(result[1]).to.equal(false) - }) - - it("Doesn't set the reporter if the sender is unauthorized", async function () { - const { reporter, sygmaAdapter, otherAddress } = await setup() - expect(await sygmaAdapter.deployed()) - await expect( - sygmaAdapter.connect(otherAddress).setReporter(reporter.address, DOMAIN_ID, true), - ).to.be.revertedWithCustomError(sygmaAdapter, "Unauthorized") - }) - }) - - describe("StoreHashes()", function () { - it("Stores hashes", async function () { - const { handler, reporter, sygmaAdapter } = await setup() - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - await expect(sygmaAdapter.connect(handler).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO])) - .to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_ONE, HASH_ONE) - .and.to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_TWO, HASH_TWO) - expect(await sygmaAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - }) - - it("Reverts if array lengths mismatch", async function () { - const { handler, reporter, sygmaAdapter } = await setup() - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - await expect( - sygmaAdapter.connect(handler).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_ONE]), - ).to.be.revertedWithCustomError(sygmaAdapter, "ArrayLengthMismatch") - }) - - it("Reverts if sender is not the authorized handler", async function () { - const { otherAddress, reporter, sygmaAdapter } = await setup() - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - await expect( - sygmaAdapter.connect(otherAddress).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO]), - ) - .to.be.revertedWithCustomError(sygmaAdapter, "InvalidHandler") - .withArgs(otherAddress.address) - }) - - it("Reverts if the reporter is not enabled", async function () { - const { handler, reporter, sygmaAdapter } = await setup() - await expect(sygmaAdapter.connect(handler).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO])) - .to.be.revertedWithCustomError(sygmaAdapter, "InvalidReporter") - .withArgs(reporter.address) - }) - - it("Overwrites previous hashes", async function () { - const { handler, reporter, sygmaAdapter } = await setup() - await expect(sygmaAdapter.setReporter(reporter.address, DOMAIN_ID, true)) - .to.emit(sygmaAdapter, "ReporterSet") - .withArgs(reporter.address, DOMAIN_ID, true) - await expect(sygmaAdapter.connect(handler).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO])) - .to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_ONE, HASH_ONE) - .and.to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_TWO, HASH_TWO) - expect(await sygmaAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) - expect(await sygmaAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - - await expect(sygmaAdapter.connect(handler).storeHashes(reporter.address, [ID_ONE, ID_TWO], [HASH_TWO, HASH_ONE])) - .to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_ONE, HASH_TWO) - .and.to.emit(sygmaAdapter, "HashStored") - .withArgs(ID_TWO, HASH_ONE) - expect(await sygmaAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) - expect(await sygmaAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) - }) - }) -}) diff --git a/packages/evm/test/adapters/Sygma/02_SygmaHeaderReporter.spec.ts b/packages/evm/test/adapters/Sygma/02_SygmaHeaderReporter.spec.ts deleted file mode 100644 index f41de8ce..00000000 --- a/packages/evm/test/adapters/Sygma/02_SygmaHeaderReporter.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { mine } from "@nomicfoundation/hardhat-network-helpers" -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 adapter = signers[1].address - const otherAddress = signers[2].address - const HeaderStorage = await ethers.getContractFactory("HeaderStorage") - const headerStorage = await HeaderStorage.deploy() - const SygmaBridge = await ethers.getContractFactory("MockSygmaBridge") - const sygmaBridge = await SygmaBridge.deploy() - const SygmaHeaderReporter = await ethers.getContractFactory("SygmaHeaderReporter") - // IBridge bridge, HeaderStorage headerStorage, bytes32 resourceID, uint8 defaultDestinationDomainID, address defaultSygmaAdapter - const sygmaHeaderReporter = await SygmaHeaderReporter.deploy( - sygmaBridge.address, - headerStorage.address, - resourceID, - DOMAIN_ID, - adapter, - ) - await mine(10) - return { - adapter, - otherAddress, - headerStorage, - sygmaBridge, - sygmaHeaderReporter, - } -} - -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("0xe7ef0", 32) + - "0004" + - functionSig.substring(2) + - "14" + - adapter.toLowerCase().substring(2) + - "14" + - reporterAddress.toLowerCase().substring(2) + - executionData - return depositData -} - -describe("SygmaHeaderReporter", function () { - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { sygmaBridge, headerStorage, adapter, sygmaHeaderReporter } = await setup() - expect(await sygmaHeaderReporter.deployed()) - expect(await sygmaHeaderReporter._bridge()).to.equal(sygmaBridge.address) - expect(await sygmaHeaderReporter._headerStorage()).to.equal(headerStorage.address) - expect(await sygmaHeaderReporter._resourceID()).to.equal(resourceID) - expect(await sygmaHeaderReporter._defaultDestinationDomainID()).to.equal(DOMAIN_ID) - expect(await sygmaHeaderReporter._defaultSygmaAdapter()).to.equal(adapter) - }) - }) - - describe("reportHeaders()", function () { - it("Reports headers to Sygma to default domain", async function () { - const { sygmaHeaderReporter, adapter, sygmaBridge, headerStorage } = await setup() - const block = await ethers.provider._getBlock(9) - const block2 = await ethers.provider._getBlock(8) - const depositData = await prepareDepositData( - sygmaHeaderReporter.address, - [9, 8], - [block.hash, block2.hash], - adapter, - ) - - await expect(sygmaHeaderReporter.reportHeaders([9, 8], "0x00")) - .to.emit(sygmaHeaderReporter, "HeaderReported") - .withArgs(sygmaHeaderReporter.address, 9, block.hash) - .and.to.emit(sygmaHeaderReporter, "HeaderReported") - .withArgs(sygmaHeaderReporter.address, 8, block2.hash) - .and.to.emit(sygmaBridge, "Deposit") - // (destinationDomainID, resourceID, 1, msg.sender, depositData, feeData); - .withArgs(DOMAIN_ID, resourceID, 1, sygmaHeaderReporter.address, depositData, "0x00") - expect(await headerStorage.headers(9)).to.equal(block.hash) - expect(await headerStorage.headers(8)).to.equal(block2.hash) - }) - }) - - describe("reportHeadersToDomain()", function () { - it("Reports headers to Sygma to specified domain", async function () { - const { sygmaHeaderReporter, otherAddress, sygmaBridge, headerStorage } = await setup() - const otherDomain = 4 - const block = await ethers.provider._getBlock(9) - const block2 = await ethers.provider._getBlock(8) - const depositData = await prepareDepositData( - sygmaHeaderReporter.address, - [9, 8], - [block.hash, block2.hash], - otherAddress, - ) - - await expect(sygmaHeaderReporter.reportHeadersToDomain([9, 8], otherAddress, otherDomain, "0x00")) - .to.emit(sygmaHeaderReporter, "HeaderReported") - .withArgs(sygmaHeaderReporter.address, 9, block.hash) - .and.to.emit(sygmaHeaderReporter, "HeaderReported") - .withArgs(sygmaHeaderReporter.address, 8, block2.hash) - .and.to.emit(sygmaBridge, "Deposit") - // (destinationDomainID, resourceID, 1, msg.sender, depositData, feeData); - .withArgs(otherDomain, resourceID, 1, sygmaHeaderReporter.address, depositData, "0x00") - expect(await headerStorage.headers(9)).to.equal(block.hash) - expect(await headerStorage.headers(8)).to.equal(block2.hash) - }) - }) -}) diff --git a/packages/evm/test/adapters/Sygma/03_SygmaMessageRelay.spec.ts b/packages/evm/test/adapters/Sygma/03_SygmaMessageRelay.spec.ts deleted file mode 100644 index bc26eacb..00000000 --- a/packages/evm/test/adapters/Sygma/03_SygmaMessageRelay.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -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 SygmaMessageRelay = await ethers.getContractFactory("SygmaMessageRelay") - const SygmaAdapter = await ethers.getContractFactory("SygmaAdapter") - const sygmaAdapter = await SygmaAdapter.deploy(sygmaBridge.address) - // IBridge bridge, HeaderStorage headerStorage, bytes32 resourceID, uint8 defaultDestinationDomainID, address defaultSygmaAdapter - const sygmaMessageRelay = await SygmaMessageRelay.deploy( - sygmaBridge.address, - yaho.address, - resourceID, - DOMAIN_ID, - sygmaAdapter.address, - ) - - await sygmaAdapter.setReporter(sygmaMessageRelay.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, - sygmaMessageRelay, - 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("0xe7ef0", 32) + - "0004" + - functionSig.substring(2) + - "14" + - adapter.toLowerCase().substring(2) + - "14" + - reporterAddress.toLowerCase().substring(2) + - executionData - return depositData -} - -describe("SygmaMessageRelay", function () { - describe("Deploy", function () { - it("Successfully deploys contract", async function () { - const { sygmaBridge, yaho, sygmaAdapter, sygmaMessageRelay } = await setup() - expect(await sygmaMessageRelay.deployed()) - expect(await sygmaMessageRelay._bridge()).to.equal(sygmaBridge.address) - expect(await sygmaMessageRelay._yaho()).to.equal(yaho.address) - expect(await sygmaMessageRelay._resourceID()).to.equal(resourceID) - expect(await sygmaMessageRelay._defaultDestinationDomainID()).to.equal(DOMAIN_ID) - expect(await sygmaMessageRelay._defaultSygmaAdapter()).to.equal(sygmaAdapter.address) - }) - }) - - describe("relayMessages()", function () { - it("Relays messages to Sygma to default domain", async function () { - const { sender, sygmaMessageRelay, 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( - sygmaMessageRelay.address, - ["0", "1"], - [hash0, hash1], - sygmaAdapter.address, - ) - expect(await sygmaMessageRelay.callStatic.relayMessages([0, 1], sygmaAdapter.address)).to.equal( - "0x0000000000000000000000000000000000000000000000000000000000000001", - ) - await expect(sygmaMessageRelay.relayMessages([0, 1], sygmaAdapter.address)) - .to.emit(sygmaMessageRelay, "MessageRelayed") - .withArgs(sygmaMessageRelay.address, 0) - .and.to.emit(sygmaMessageRelay, "MessageRelayed") - .withArgs(sygmaMessageRelay.address, 1) - .and.to.emit(sygmaBridge, "Deposit") - // (destinationDomainID, resourceID, 1, msg.sender, depositData, feeData); - .withArgs(DOMAIN_ID, resourceID, 1, sygmaMessageRelay.address, depositData, "0x") - }) - }) -}) diff --git a/packages/evm/test/adapters/Sygma/04_Sygma_E2E.spec.ts b/packages/evm/test/adapters/Sygma/04_Sygma_E2E.spec.ts deleted file mode 100644 index 368ba917..00000000 --- a/packages/evm/test/adapters/Sygma/04_Sygma_E2E.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* -Note that these E2E tests simulate cross-chain interactions but, -for the sake of convenience, use only one network as both the origin and destination chain. - -*/ -import { expect } from "chai" -import { ethers, network } from "hardhat" - -// Source chain ID -const CHAIN_ID = network.config.chainId -// Destination domain ID -const DOMAIN_ID = 5 -const resourceID = "0x0000000000000000000000000000000000000000000000000000000000000500" - -const ID_ZERO = 0 - -const baseSetup = async () => { - const [wallet] = await ethers.getSigners() - - // deploy hashi - const Hashi = await ethers.getContractFactory("Hashi") - const hashi = await Hashi.deploy() - - // deploy ShoyuBashi - const ShoyuBashi = await ethers.getContractFactory("ShoyuBashi") - const shoyuBashi = ShoyuBashi.deploy(wallet.address, hashi.address) - - // deploy Yaho - const Yaho = await ethers.getContractFactory("Yaho") - const yaho = await Yaho.deploy() - - // deploy Mock Sygma Bridge - const SygmaBridge = await ethers.getContractFactory("MockSygmaBridge") - const sygmaBridge = await SygmaBridge.deploy() - - // deploy Sygma Adapter - const SygmaAdapter = await ethers.getContractFactory("SygmaAdapter") - const sygmaAdapter = await SygmaAdapter.deploy(sygmaBridge.address) - - // deploy Sygma Message Relayer - const SygmaMessageRelay = await ethers.getContractFactory("SygmaMessageRelay") - const sygmaMessageRelay = await SygmaMessageRelay.deploy( - sygmaBridge.address, - yaho.address, - resourceID, - DOMAIN_ID, - sygmaAdapter.address, - ) - - await sygmaAdapter.setReporter(sygmaMessageRelay.address, CHAIN_ID, true) - - // deploy Yaru - const Yaru = await ethers.getContractFactory("Yaru") - const yaru = await Yaru.deploy(hashi.address, yaho.address, CHAIN_ID) - - // deploy avatar - const Avatar = await ethers.getContractFactory("TestAvatar") - const avatar = await Avatar.deploy() - - // const deploy PingPong test contract - const PingPong = await ethers.getContractFactory("PingPong") - const pingPong = await PingPong.deploy() - - return { - avatar, - sygmaBridge, - sygmaMessageRelay, - sygmaAdapter, - wallet, - hashi, - shoyuBashi, - yaho, - yaru, - pingPong, - } -} - -const setupTestWithTestAvatar = async () => { - const base = await baseSetup() - const Module = await ethers.getContractFactory("HashiModule") - const provider = await ethers.getDefaultProvider() - const network = await provider.getNetwork() - const module = await Module.deploy( - base.avatar.address, - base.avatar.address, - base.avatar.address, - base.yaru.address, - base.wallet.address, - CHAIN_ID, - ) - await base.avatar.setModule(module.address) - return { ...base, Module, module, network } -} - -describe("SygmaMessageRelay End-to-End", function () { - describe("executeTransaction()", function () { - it("executes a transaction", async () => { - const { pingPong, yaho, sygmaMessageRelay, sygmaAdapter, module, wallet, yaru } = await setupTestWithTestAvatar() - const calldata = await pingPong.interface.encodeFunctionData("ping", []) - const tx = await module.interface.encodeFunctionData("executeTransaction", [pingPong.address, 0, calldata, 0]) - const message = { - to: module.address, - toChainId: DOMAIN_ID, - data: tx, - } - const pingCount = await pingPong.count() - - // dispatch message - await yaho.dispatchMessagesToAdapters([message], [sygmaMessageRelay.address], [sygmaAdapter.address]) - // execute messages - await yaru.executeMessages([message], [ID_ZERO], [wallet.address], [sygmaAdapter.address]) - - expect(await pingPong.count()).to.equal(pingCount + 1) - }) - }) -})