From ea0c29c26c9ad46e76c191e10fadade8cc1d4932 Mon Sep 17 00:00:00 2001 From: sujithsomraaj Date: Sat, 11 May 2024 12:41:12 +0700 Subject: [PATCH] chore: improve inline documentation --- src/axelar/AxelarHelper.sol | 58 ++++++++- src/celer/CelerHelper.sol | 120 +++++++++++++----- src/hyperlane/HyperlaneHelper.sol | 78 ++++++++++++ src/layerzero-v2/LayerZeroV2Helper.sol | 73 ++++++++--- src/layerzero/LayerZeroHelper.sol | 93 +++++++++++++- .../automatic-relayer/WormholeHelper.sol | 67 +++++----- .../specialized-relayer/WormholeHelper.sol | 69 +++++++--- 7 files changed, 452 insertions(+), 106 deletions(-) diff --git a/src/axelar/AxelarHelper.sol b/src/axelar/AxelarHelper.sol index b79bf57..b31d0cf 100644 --- a/src/axelar/AxelarHelper.sol +++ b/src/axelar/AxelarHelper.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +/// library imports import "forge-std/Test.sol"; + +/// local imports import {AddressHelper} from "../axelar/lib/AddressHelper.sol"; interface IAxelarExecutable { @@ -15,16 +18,26 @@ interface IAxelarExecutable { interface IAxelarGateway { function approveContractCall(bytes calldata params, bytes32 commandId) external; - function callContract(string calldata destinationChain, string calldata contractAddress, bytes calldata payload) external; } /// @title Axelar Helper -/// @notice helps mock the message transfer using axelar bridge +/// @notice helps simulate the message transfer using axelar bridge contract AxelarHelper is Test { + /// @dev is the default event selector if not specified by the user bytes32 constant MESSAGE_EVENT_SELECTOR = 0x30ae6cc78c27e651745bf2ad08a11de83910ac1e347a52f7ac898c0fbef94dae; + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice helps with multiple destination transfers + /// @param fromChain represents the source chain + /// @param toGateway represents the destination gateway addresses + /// @param expDstChain represents the expected destination chains + /// @param forkId array of destination fork ids (localized to your testing) + /// @param logs array of logs function help( string memory fromChain, address[] memory toGateway, @@ -37,6 +50,13 @@ contract AxelarHelper is Test { } } + /// @notice helps with a single destination transfer with a specific event selector + /// @param fromChain represents the source chain + /// @param toGateway represents the destination gateway address + /// @param expDstChain represents the expected destination chain + /// @param forkId represents the destination fork id (localized to your testing) + /// @param eventSelector represents the event selector + /// @param logs array of logs function help( string memory fromChain, address toGateway, @@ -48,6 +68,12 @@ contract AxelarHelper is Test { _help(fromChain, toGateway, expDstChain, forkId, eventSelector, logs); } + /// @notice helps with a single destination transfer + /// @param fromChain represents the source chain + /// @param toGateway represents the destination gateway address + /// @param expDstChain represents the expected destination chain + /// @param forkId represents the destination fork id (localized to your testing) + /// @param logs array of logs function help( string memory fromChain, address toGateway, @@ -58,6 +84,10 @@ contract AxelarHelper is Test { _help(fromChain, toGateway, expDstChain, forkId, MESSAGE_EVENT_SELECTOR, logs); } + /// @notice finds logs with a specific event selector + /// @param logs array of logs + /// @param length expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory HLLogs) { return _findLogs(logs, MESSAGE_EVENT_SELECTOR, length); } @@ -70,6 +100,17 @@ contract AxelarHelper is Test { bytes payload; } + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice internal function to help with destination transfers + /// @param fromChain represents the source chain + /// @param toGateway represents the destination gateway address + /// @param expDstChain represents the expected destination chain + /// @param forkId represents the destination fork id (localized to your testing) + /// @param eventSelector represents the event selector + /// @param logs array of logs function _help( string memory fromChain, address toGateway, @@ -90,7 +131,7 @@ contract AxelarHelper is Test { if (v.log.topics[0] == eventSelector) { (v.destinationChain, v.destinationContract, v.payload) = abi.decode(v.log.data, (string, string, bytes)); - if (isStringsEqual(expDstChain, v.destinationChain)) { + if (_isStringsEqual(expDstChain, v.destinationChain)) { string memory srcAddress = AddressHelper.toString(address(uint160(uint256(v.log.topics[1])))); address dstContract = AddressHelper.fromString(v.destinationContract); @@ -114,10 +155,19 @@ contract AxelarHelper is Test { vm.selectFork(v.prevForkId); } - function isStringsEqual(string memory a, string memory b) public pure returns (bool) { + /// @notice checks if two strings are equal + /// @param a first string + /// @param b second string + /// @return true if the strings are equal, false otherwise + function _isStringsEqual(string memory a, string memory b) internal pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } + /// @notice internal function to find logs with a specific event selector + /// @param logs array of logs + /// @param dispatchSelector event selector + /// @param length expected number of logs + /// @return AxelarLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 dispatchSelector, uint256 length) internal pure diff --git a/src/celer/CelerHelper.sol b/src/celer/CelerHelper.sol index 6360c09..ffd473d 100644 --- a/src/celer/CelerHelper.sol +++ b/src/celer/CelerHelper.sol @@ -1,25 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +/// library imports import "forge-std/Test.sol"; -import "forge-std/console.sol"; - import "solady/src/utils/LibString.sol"; + +/// local imports import {TypeCasts} from "../libraries/TypeCasts.sol"; interface IMessageBus { - /** - * @notice Send a message to a contract on another chain. - * Sender needs to make sure the uniqueness of the message Id, which is computed as - * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message). - * If messages with the same Id are sent, only one of them will succeed at dst chain.. - * A fee is charged in the native gas token. - * @param _receiver The address of the destination app contract. - * @param _dstChainId The destination chain ID. - * @param _message Arbitrary message bytes to be decoded by the destination app contract. - */ function sendMessage(address _receiver, uint256 _dstChainId, bytes calldata _message) external payable; - function calcFee(bytes calldata _message) external view returns (uint256); } @@ -28,32 +18,34 @@ interface IMessageReceiverApp { Fail, // execution failed, finalized Success, // execution succeeded, finalized Retry // execution rejected, can retry later + } - /** - * @notice Called by MessageBus to execute a message - * @param _sender The address of the source app contract - * @param _srcChainId The source chain ID where the transfer is originated from - * @param _message Arbitrary message bytes originated from and encoded by the source app contract - * @param _executor Address who called the MessageBus execution function - */ function executeMessage(address _sender, uint64 _srcChainId, bytes calldata _message, address _executor) external payable returns (ExecutionStatus); } -/// @title Celer IM Cross-Chain Helper -/// @dev use the `help` and `helpWithEstimates` functions to process any message delivery -/// @notice will help developers test celer im using forked mainnets (Near mainnet execution) -/// @notice supports only EVM chains at this moment & single transfers +/// @title Celer Helper +/// @notice helps simulate the message transfer using celer im message bridge contract CelerHelper is Test { + /// @dev is the default event selector if not specified by the user bytes32 constant MESSAGE_EVENT_SELECTOR = 0xce3972bfffe49d317e1d128047a97a3d86b25c94f6f04409f988ef854d25e0e4; - /// @dev to process multi destination payloads + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice helps with multiple destination transfers + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus addresses + /// @param expDstChainId represents the expected destination chain ids + /// @param forkId array of destination fork ids (localized to your testing) + /// @param logs array of logs function help( uint64 fromChainId, - /// @dev is inevitable, cannot fetch form logs address fromMessageBus, address[] memory toMessageBus, uint64[] memory expDstChainId, @@ -74,9 +66,15 @@ contract CelerHelper is Test { } } + /// @notice helps with a single destination transfer + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus address + /// @param expDstChainId represents the expected destination chain id + /// @param forkId represents the destination fork id (localized to your testing) + /// @param logs array of logs function help( uint64 fromChainId, - /// @dev is inevitable, cannot fetch form logs address fromMessageBus, address toMessageBus, uint64 expDstChainId, @@ -86,9 +84,16 @@ contract CelerHelper is Test { _help(fromChainId, fromMessageBus, toMessageBus, expDstChainId, MESSAGE_EVENT_SELECTOR, forkId, logs, false); } + /// @notice helps with a single destination transfer with a specific event selector + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus address + /// @param expDstChainId represents the expected destination chain id + /// @param eventSelector represents the event selector + /// @param forkId represents the destination fork id (localized to your testing) + /// @param logs array of logs function help( uint64 fromChainId, - /// @dev is inevitable, cannot fetch form logs address fromMessageBus, address toMessageBus, uint64 expDstChainId, @@ -99,9 +104,15 @@ contract CelerHelper is Test { _help(fromChainId, fromMessageBus, toMessageBus, expDstChainId, eventSelector, forkId, logs, false); } + /// @notice helps with a single destination transfer and estimates gas + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus address + /// @param expDstChainId represents the expected destination chain id + /// @param forkId represents the destination fork id (localized to your testing) + /// @param logs array of logs function helpWithEstimates( uint64 fromChainId, - /// @dev is inevitable, cannot fetch form logs address fromMessageBus, address toMessageBus, uint64 expDstChainId, @@ -121,6 +132,14 @@ contract CelerHelper is Test { ); } + /// @notice helps with a single destination transfer with a specific event selector and estimates gas + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus address + /// @param expDstChainId represents the expected destination chain id + /// @param forkId represents the destination fork id (localized to your testing) + /// @param eventSelector represents the event selector + /// @param logs array of logs function helpWithEstimates( uint64 fromChainId, /// @dev is inevitable, cannot fetch form logs @@ -135,6 +154,13 @@ contract CelerHelper is Test { _help(fromChainId, fromMessageBus, toMessageBus, expDstChainId, eventSelector, forkId, logs, enableEstimates); } + /// @notice helps with multiple destination transfers and estimates gas + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address (cannot be fetched from logs) + /// @param toMessageBus represents the destination message bus addresses + /// @param expDstChainId represents the expected destination chain ids + /// @param forkId array of destination fork ids (localized to your testing) + /// @param logs array of logs function helpWithEstimates( uint64 fromChainId, /// @dev is inevitable, cannot fetch form logs @@ -159,10 +185,19 @@ contract CelerHelper is Test { } } + /// @notice finds logs with the default event selector + /// @param logs array of logs + /// @param length expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory HLLogs) { return _findLogs(logs, MESSAGE_EVENT_SELECTOR, length); } + /// @notice finds logs with a specific event selector + /// @param logs array of logs + /// @param eventSelector event selector + /// @param length expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, bytes32 eventSelector, uint256 length) external pure @@ -171,6 +206,19 @@ contract CelerHelper is Test { return _findLogs(logs, eventSelector, length); } + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice internal function to help with destination transfers + /// @param fromChainId represents the source chain id + /// @param fromMessageBus represents the source message bus address + /// @param toMessageBus represents the destination message bus address + /// @param expDstChainId represents the expected destination chain id + /// @param eventSelector represents the event selector + /// @param forkId represents the destination fork id (localized to your testing) + /// @param logs array of logs + /// @param enableEstimates flag to enable gas estimates function _help( uint64 fromChainId, address fromMessageBus, @@ -208,6 +256,11 @@ contract CelerHelper is Test { vm.selectFork(prevForkId); } + /// @notice handles the message execution + /// @param fromChainId represents the source chain id + /// @param sender represents the sender address + /// @param data represents the message data + /// @param expDstChainId represents the expected destination chain id function _handle(uint64 fromChainId, address sender, bytes memory data, uint64 expDstChainId) internal { bytes32 receiver; uint256 dstChainId; @@ -226,6 +279,10 @@ contract CelerHelper is Test { } } + /// @notice estimates gas for message execution + /// @param fromMessageBus represents the source message bus address + /// @param message represents the message data + /// @return gasEstimate the estimated gas function _estimateGas(address fromMessageBus, bytes memory message) internal returns (uint256 gasEstimate) { /// NOTE: In celer two fees are involved, but only the 1st one is /// estimated here @@ -234,6 +291,11 @@ contract CelerHelper is Test { gasEstimate = IMessageBus(fromMessageBus).calcFee(message); } + /// @notice internal function to find logs with a specific event selector + /// @param logs array of logs + /// @param dispatchSelector event selector + /// @param length expected number of logs + /// @return CelerLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 dispatchSelector, uint256 length) internal pure diff --git a/src/hyperlane/HyperlaneHelper.sol b/src/hyperlane/HyperlaneHelper.sol index 1d936b9..c089bde 100644 --- a/src/hyperlane/HyperlaneHelper.sol +++ b/src/hyperlane/HyperlaneHelper.sol @@ -1,18 +1,33 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +/// library imports import "forge-std/Test.sol"; import "solady/src/utils/LibString.sol"; +/// local imports import {TypeCasts} from "../libraries/TypeCasts.sol"; interface IMessageRecipient { function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external; } +/// @title Hyperlane Helper +/// @notice Helps simulate message transfers using the Hyperlane interchain messaging protocol contract HyperlaneHelper is Test { + /// @dev The default dispatch selector if not specified by the user bytes32 constant DISPATCH_SELECTOR = 0x769f711d20c679153d382254f59892613b58a97cc876b249134ac25c80f9c814; + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice helps with multiple destination transfers + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the array of destination mailbox addresses + /// @param expDstDomains represents the array of expected destination domains + /// @param forkId represents the array of destination fork IDs (localized to your testing) + /// @param logs represents the array of logs function help( address fromMailbox, address[] memory toMailbox, @@ -26,10 +41,21 @@ contract HyperlaneHelper is Test { } } + /// @notice helps with a single destination transfer + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the destination mailbox address + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function help(address fromMailbox, address toMailbox, uint256 forkId, Vm.Log[] calldata logs) external { return _help(fromMailbox, toMailbox, 0, DISPATCH_SELECTOR, forkId, logs, false); } + /// @notice helps with a single destination transfer with a specific dispatch selector + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the destination mailbox address + /// @param dispatchSelector represents the dispatch selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function help( address fromMailbox, address toMailbox, @@ -40,6 +66,12 @@ contract HyperlaneHelper is Test { _help(fromMailbox, toMailbox, 0, dispatchSelector, forkId, logs, false); } + /// @notice helps with multiple destination transfers and estimates gas + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the array of destination mailbox addresses + /// @param expDstDomains represents the array of expected destination domains + /// @param forkId represents the array of destination fork IDs (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates( address fromMailbox, address[] memory toMailbox, @@ -54,6 +86,11 @@ contract HyperlaneHelper is Test { } } + /// @notice helps with a single destination transfer and estimates gas + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the destination mailbox address + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates(address fromMailbox, address toMailbox, uint256 forkId, Vm.Log[] calldata logs) external { @@ -61,6 +98,12 @@ contract HyperlaneHelper is Test { _help(fromMailbox, toMailbox, 0, DISPATCH_SELECTOR, forkId, logs, enableEstimates); } + /// @notice helps with a single destination transfer with a specific dispatch selector and estimates gas + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the destination mailbox address + /// @param dispatchSelector represents the dispatch selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates( address fromMailbox, address toMailbox, @@ -72,10 +115,19 @@ contract HyperlaneHelper is Test { _help(fromMailbox, toMailbox, 0, dispatchSelector, forkId, logs, enableEstimates); } + /// @notice finds logs with the default dispatch selector + /// @param logs represents the array of logs + /// @param length represents the expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory HLLogs) { return _findLogs(logs, DISPATCH_SELECTOR, length); } + /// @notice finds logs with a specific dispatch selector + /// @param logs represents the array of logs + /// @param dispatchSelector represents the dispatch selector + /// @param length represents the expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, bytes32 dispatchSelector, uint256 length) external pure @@ -84,6 +136,18 @@ contract HyperlaneHelper is Test { return _findLogs(logs, dispatchSelector, length); } + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice internal function to help with destination transfers + /// @param fromMailbox represents the source mailbox address + /// @param toMailbox represents the destination mailbox address + /// @param expDstDomain represents the expected destination domain + /// @param dispatchSelector represents the dispatch selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs + /// @param enableEstimates flag to enable gas estimates function _help( address fromMailbox, address toMailbox, @@ -111,6 +175,11 @@ contract HyperlaneHelper is Test { vm.selectFork(prevForkId); } + /// @notice estimates gas for message handling + /// @param originDomain represents the origin domain + /// @param destinationDomain represents the destination domain + /// @param handleGas represents the gas used for message handling + /// @return gasEstimate the estimated gas function _estimateGas(uint32 originDomain, uint32 destinationDomain, uint256 handleGas) internal returns (uint256 gasEstimate) @@ -132,6 +201,10 @@ contract HyperlaneHelper is Test { gasEstimate = abi.decode(result, (uint256)); } + /// @notice handles the message + /// @param message represents the message data + /// @param destinationDomain represents the destination domain + /// @param enableEstimates flag to enable gas estimates function _handle(bytes memory message, uint32 destinationDomain, bool enableEstimates) internal { bytes32 _recipient; uint256 _originDomain; @@ -156,6 +229,11 @@ contract HyperlaneHelper is Test { } } + /// @notice internal function to find logs with a specific dispatch selector + /// @param logs represents the array of logs + /// @param dispatchSelector represents the dispatch selector + /// @param length represents the expected number of logs + /// @return HLLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 dispatchSelector, uint256 length) internal pure diff --git a/src/layerzero-v2/LayerZeroV2Helper.sol b/src/layerzero-v2/LayerZeroV2Helper.sol index 213a0e1..ecb0be7 100644 --- a/src/layerzero-v2/LayerZeroV2Helper.sol +++ b/src/layerzero-v2/LayerZeroV2Helper.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +/// library imports import "forge-std/Test.sol"; import "forge-std/console.sol"; @@ -30,14 +31,21 @@ interface ILayerzeroV2Receiver { ) external payable; } +/// @title LayerZero V2 Helper +/// @notice helps simulate message transfers using the LayerZero v2 protocol contract LayerZeroV2Helper is Test { + /// @dev is the default packet selector if not specified by the user bytes32 constant PACKET_SELECTOR = 0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f; - /// help process multiple destination packets in one atomic transaction - /// @param endpoints is the layerzero endpoints on the destination chain - /// @param expChainIds is the layerzero destination chain eids - /// @param forkIds is the layerzero destination chain fork ids - /// @param logs is the recorded message logs + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice helps process multiple destination packets in one atomic transaction + /// @param endpoints represents the layerzero endpoints on the destination chain + /// @param expChainIds represents the layerzero destination chain eids + /// @param forkIds represents the layerzero destina + /// @param logs represents the recorded message logs function help( address[] memory endpoints, uint32[] memory expChainIds, @@ -49,12 +57,12 @@ contract LayerZeroV2Helper is Test { } } - /// help process multiple destination packets in one atomic transaction - /// @param endpoints is the layerzero endpoints on the destination chain - /// @param expChainIds is the layerzero destination chain eids - /// @param forkIds is the layerzero destination chain fork ids - /// @param eventSelector is a custom event selector - /// @param logs is the recorded message logs + /// @notice helps process multiple destination packets in one atomic transaction + /// @param endpoints represents the layerzero endpoints on the destination chain + /// @param expChainIds represents the layerzero destination chain eids + /// @param forkIds represents the layerzero destination chain fork ids + /// @param eventSelector represents a custom event selector + /// @param logs represents the recorded message logs function help( address[] memory endpoints, uint32[] memory expChainIds, @@ -67,24 +75,29 @@ contract LayerZeroV2Helper is Test { } } - /// @notice help process LayerZero v2 packets - /// @param endpoint is the layerzero endpoint on the destination chain - /// @param forkId is the destination chain fork id - /// @param logs is the recorded message logs + /// @notice helps process LayerZero v2 packets + /// @param endpoint represents the layerzero endpoint on the destination chain + /// @param forkId represents the destination chain fork id + /// @param logs represents the recorded message logs function help(address endpoint, uint256 forkId, Vm.Log[] calldata logs) external { _help(endpoint, 0, forkId, PACKET_SELECTOR, logs); } - /// @notice help process LayerZero v2 packets - /// @param endpoint is the layerzero endpoint on the destination chain - /// @param forkId is the destination chain fork id - /// @param eventSelector is custom bytes32 event selector - /// @param logs is the recorded message logs + /// @notice helps process LayerZero v2 packets + /// @param endpoint represents the layerzero endpoint on the destination chain + /// @param forkId represents the destination chain fork id + /// @param eventSelector represents a custom bytes32 event selector + /// @param logs represents the recorded message logs function help(address endpoint, uint256 forkId, bytes32 eventSelector, Vm.Log[] calldata logs) external { _help(endpoint, 0, forkId, eventSelector, logs); } /// @notice internal function to process LayerZero v2 packets based on the provided logs and fork ID + /// @param endpoint represents the layerzero endpoint on the destination chain + /// @param expDstChainId represents the expected destination chain id + /// @param forkId represents the destination chain fork id + /// @param eventSelector represents the event selector + /// @param logs represents the recorded message logs function _help(address endpoint, uint32 expDstChainId, uint256 forkId, bytes32 eventSelector, Vm.Log[] memory logs) internal { @@ -115,6 +128,8 @@ contract LayerZeroV2Helper is Test { } /// @notice helps decode the layerzero encoded payload / packet + /// @param encodedPacket represents the encoded packet + /// @return the decoded packet function decodePacket(bytes calldata encodedPacket) public pure returns (Packet memory) { /// @dev decode the packet header uint8 version = uint8(encodedPacket[0]); @@ -139,25 +154,41 @@ contract LayerZeroV2Helper is Test { }); } + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + /// @notice helper function to convert bytes to uint64 + /// @param data represents the bytes data + /// @param offset represents the offset within the data + /// @return the uint64 value function toUint64(bytes calldata data, uint256 offset) internal pure returns (uint64) { require(offset + 8 <= data.length, "toUint64: out of bounds"); return uint64(bytes8(data[offset:offset + 8])); } /// @notice helper function to convert bytes to uint32 + /// @param data represents the bytes data + /// @param offset represents the offset within the data + /// @return the uint32 value function toUint32(bytes calldata data, uint256 offset) internal pure returns (uint32) { require(offset + 4 <= data.length, "toUint32: out of bounds"); return uint32(bytes4(data[offset:offset + 4])); } /// @notice helper function to convert bytes to address + /// @param data represents the bytes data + /// @param offset represents the offset within the data + /// @return the address value function toAddress(bytes calldata data, uint256 offset) internal pure returns (address) { require(offset + 20 <= data.length, "toAddress: out of bounds"); return address(uint160(bytes20(data[offset + 12:offset + 32]))); } - /// @notice helper function to convert bytes to bytes32 + /// @notice helper function to convert bytes to bytes32 + /// @param data represents the bytes data + /// @param offset represents the offset within the data + /// @return the bytes32 value function toBytes32(bytes calldata data, uint256 offset) internal pure returns (bytes32) { require(offset + 32 <= data.length, "toBytes32: out of bounds"); return bytes32(data[offset:offset + 32]); diff --git a/src/layerzero/LayerZeroHelper.sol b/src/layerzero/LayerZeroHelper.sol index 2f61e51..dd81666 100644 --- a/src/layerzero/LayerZeroHelper.sol +++ b/src/layerzero/LayerZeroHelper.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +/// library imports import "forge-std/Test.sol"; -import "forge-std/console.sol"; +/// local imports import "./lib/LZPacket.sol"; interface ILayerZeroEndpoint { @@ -25,11 +26,26 @@ interface ILayerZeroEndpoint { ) external returns (uint256 nativeFee, uint256 zroFee); } +/// @title LayerZero Helper +/// @notice helps simulate message transfers using the LayerZero protocol (version 1) contract LayerZeroHelper is Test { + /// @dev is the default packet selector if not specified by the user bytes32 constant PACKET_SELECTOR = 0xe9bded5f24a4168e4f3bf44e00298c993b22376aad8c58c7dda9718a54cbea82; + + /// @dev is the default library address if not specified by the user address constant DEFAULT_LIBRARY = 0x4D73AdB72bC3DD368966edD0f0b2148401A178E2; - // handle atomic multi destination packets + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice helps with multiple destination transfers + /// @param endpoints represents the array of endpoint addresses + /// @param expChainIds represents the array of expected chain ids + /// @param gasToSend represents the gas to send for message execution + /// @param forkId represents the array of destination fork IDs (localized to your testing) + /// @param logs represents the array of logs + function help( address[] memory endpoints, uint16[] memory expChainIds, @@ -43,11 +59,22 @@ contract LayerZeroHelper is Test { } } - // hardcoded defaultLibrary on ETH and Packet event selector + /// @notice helps with a single destination transfer (hardcoded default library and packet selector) + /// @param endpoint represents the endpoint address + /// @param gasToSend represents the gas to send for message execution + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function help(address endpoint, uint256 gasToSend, uint256 forkId, Vm.Log[] calldata logs) external { _help(endpoint, 0, DEFAULT_LIBRARY, gasToSend, PACKET_SELECTOR, forkId, logs, false); } + /// @notice helps with a single destination transfer (custom default library and event selector) + /// @param endpoint represents the endpoint address + /// @param defaultLibrary represents the default library address + /// @param gasToSend represents the gas to send for message execution + /// @param eventSelector represents the event selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function help( address endpoint, address defaultLibrary, @@ -59,7 +86,12 @@ contract LayerZeroHelper is Test { _help(endpoint, 0, defaultLibrary, gasToSend, eventSelector, forkId, logs, false); } - // process multi destination payloads + /// @notice helps with multiple destination transfers and estimates gas + /// @param endpoints represents the array of endpoint addresses + /// @param expChainIds represents the array of expected chain ids + /// @param gasToSend represents the gas to send for message execution + /// @param forkId represents the array of destination fork IDs (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates( address[] memory endpoints, uint16[] memory expChainIds, @@ -83,12 +115,23 @@ contract LayerZeroHelper is Test { } } - // hardcoded defaultLibrary on ETH and Packet event selector + /// @notice helps with a single destination transfer and estimates gas (hardcoded default library and packet selector) + /// @param endpoint represents the endpoint address + /// @param gasToSend represents the gas to send for message execution + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates(address endpoint, uint256 gasToSend, uint256 forkId, Vm.Log[] calldata logs) external { bool enableEstimates = vm.envOr("ENABLE_LZ_ESTIMATES", false); _help(endpoint, 0, DEFAULT_LIBRARY, gasToSend, PACKET_SELECTOR, forkId, logs, enableEstimates); } + /// @notice helps with a single destination transfer and estimates gas (custom default library and event selector) + /// @param endpoint represents the endpoint address + /// @param defaultLibrary represents the default library address + /// @param gasToSend represents the gas to send for message execution + /// @param eventSelector represents the event selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs function helpWithEstimates( address endpoint, address defaultLibrary, @@ -101,10 +144,19 @@ contract LayerZeroHelper is Test { _help(endpoint, 0, defaultLibrary, gasToSend, eventSelector, forkId, logs, enableEstimates); } + /// @notice finds logs with the default packet selector + /// @param logs represents the array of logs + /// @param length represents the expected number of logs + /// @return lzLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory lzLogs) { return _findLogs(logs, PACKET_SELECTOR, length); } + /// @notice finds logs with a specific event selector + /// @param logs represents the array of logs + /// @param eventSelector represents the event selector + /// @param length represents the expected number of logs + /// @return lzLogs array of found logs function findLogs(Vm.Log[] calldata logs, bytes32 eventSelector, uint256 length) external pure @@ -113,6 +165,19 @@ contract LayerZeroHelper is Test { return _findLogs(logs, eventSelector, length); } + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// + + /// @notice internal function to help with destination transfers + /// @param endpoint represents the endpoint address + /// @param expChainId represents the expected chain id + /// @param defaultLibrary represents the default library address + /// @param gasToSend represents the gas to send for message execution + /// @param eventSelector represents the event selector + /// @param forkId represents the destination fork ID (localized to your testing) + /// @param logs represents the array of logs + /// @param enableEstimates flag to enable gas estimates function _help( address endpoint, uint16 expChainId, @@ -146,6 +211,14 @@ contract LayerZeroHelper is Test { vm.selectFork(prevForkId); } + /// @notice estimates gas for message execution + /// @param endpoint represents the endpoint address + /// @param destination represents the destination chain id + /// @param userApplication represents the user application address + /// @param payload represents the message payload + /// @param payInZRO flag to indicate if fees should be paid in ZRO tokens + /// @param adapterParam represents the adapter parameters + /// @return gasEstimate the estimated gas function _estimateGas( address endpoint, uint16 destination, @@ -159,6 +232,11 @@ contract LayerZeroHelper is Test { return nativeGas; } + /// @notice receives the payload and executes the message + /// @param endpoint represents the endpoint address + /// @param packet represents the LayerZero packet + /// @param gasToSend represents the gas to send for message execution + /// @param enableEstimates flag to enable gas estimates function _receivePayload( address endpoint, LayerZeroPacket.Packet memory packet, @@ -183,6 +261,11 @@ contract LayerZeroHelper is Test { } } + /// @notice internal function to find logs with a specific event selector + /// @param logs represents the array of logs + /// @param eventSelector represents the event selector + /// @param length represents the expected number of logs + /// @return lzLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 eventSelector, uint256 length) internal pure diff --git a/src/wormhole/automatic-relayer/WormholeHelper.sol b/src/wormhole/automatic-relayer/WormholeHelper.sol index 93cba6e..e6e0083 100644 --- a/src/wormhole/automatic-relayer/WormholeHelper.sol +++ b/src/wormhole/automatic-relayer/WormholeHelper.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.0; /// library imports import "forge-std/Test.sol"; -/// bridge specific imports +/// local imports import "./lib/PayloadDecoder.sol"; import "./lib/InternalStructs.sol"; @@ -23,36 +23,30 @@ interface IWormholeReceiver { } /// @title WormholeHelper -/// @author Sujith Somraaj -/// @dev wormhole bridge helper /// @notice supports only automatic relayer (not specialized relayers) /// MORE INFO: https://docs.wormhole.com/wormhole/quick-start/cross-chain-dev/automatic-relayer contract WormholeHelper is Test { - /*/////////////////////////////////////////////////////////////// - CONSTANTS - //////////////////////////////////////////////////////////////*/ - - /// @dev LogMessagePublished (index_topic_1 address sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel) + /// @dev is the default event selector if not specified by the user bytes32 constant MESSAGE_EVENT_SELECTOR = 0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2; - /*/////////////////////////////////////////////////////////////// - EXTERNAL FUNCTIONS - //////////////////////////////////////////////////////////////*/ + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// /// @dev single dst x default event selector - /// @param srcChainId is the wormhole identifier of the source chain - /// @param dstForkId is the dst fork id to deliver the message - /// @param dstRelayer is wormhole's dst chain relayer - /// @param logs is the logs after message dispatch on src chain + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstRelayer represents wormhole's dst chain relayer + /// @param logs represents the logs after message dispatch on src chain function help(uint16 srcChainId, uint256 dstForkId, address dstRelayer, Vm.Log[] calldata logs) external { _help(srcChainId, dstForkId, address(0), dstRelayer, MESSAGE_EVENT_SELECTOR, logs); } /// @dev single dst x user-specific event selector - /// @param srcChainId is the wormhole identifier of the source chain - /// @param dstForkId is the dst fork id to deliver the message - /// @param dstRelayer is wormhole's dst chain relayer - /// @param logs is the logs after message dispatch on src chain + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstRelayer represents wormhole's dst chain relayer + /// @param logs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256 dstForkId, @@ -64,10 +58,10 @@ contract WormholeHelper is Test { } /// @dev multi dst x default event selector - /// @param srcChainId is the wormhole identifier of the source chain - /// @param dstForkId is the dst fork id to deliver the message - /// @param dstRelayer is wormhole's dst chain relayer - /// @param logs is the logs after message dispatch on src chain + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstRelayer represents wormhole's dst chain relayer + /// @param logs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256[] calldata dstForkId, @@ -85,10 +79,10 @@ contract WormholeHelper is Test { } /// @dev multi dst x user-specific event selector - /// @param srcChainId is the wormhole identifier of the source chain - /// @param dstForkId is the dst fork id to deliver the message - /// @param dstRelayer is wormhole's dst chain relayer - /// @param logs is the logs after message dispatch on src chain + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstRelayer represents wormhole's dst chain relayer + /// @param logs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256[] calldata dstForkId, @@ -107,13 +101,16 @@ contract WormholeHelper is Test { } /// @dev helps find logs of `length` for default event selector + /// @param logs represents the logs after message dispatch on src chain + /// @param length represents the expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory HLLogs) { return _findLogs(logs, MESSAGE_EVENT_SELECTOR, length); } - /*/////////////////////////////////////////////////////////////// - INTERNAL/HELPER FUNCTIONS - //////////////////////////////////////////////////////////////*/ + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// struct LocalVars { uint256 prevForkId; @@ -124,6 +121,12 @@ contract WormholeHelper is Test { } /// @dev helper to process cross-chain messages + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param expDstAddress represents the expected destination address + /// @param dstRelayer represents wormhole's dst chain relayer + /// @param eventSelector represents the event selector + /// @param logs represents the logs after message dispatch on src chain function _help( uint16 srcChainId, uint256 dstForkId, @@ -166,6 +169,10 @@ contract WormholeHelper is Test { } /// @dev helper to get logs + /// @param logs represents the logs after message dispatch on src chain + /// @param dispatchSelector represents the event selector + /// @param length represents the expected number of logs + /// @return WormholeLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 dispatchSelector, uint256 length) internal pure diff --git a/src/wormhole/specialized-relayer/WormholeHelper.sol b/src/wormhole/specialized-relayer/WormholeHelper.sol index b7d109c..e23a6c0 100644 --- a/src/wormhole/specialized-relayer/WormholeHelper.sol +++ b/src/wormhole/specialized-relayer/WormholeHelper.sol @@ -13,29 +13,24 @@ interface IWormholeReceiver { } /// @title WormholeHelper -/// @author Sujith Somraaj /// @dev wormhole helper that uses VAA to deliver messages /// @notice supports specialized relayers (for automatic relayer use WormholeHelper) /// @notice in real-world scenario the off-chain infra will just sign the VAAs but this helpers mocks both signing and relaying /// MORE INFO: https://docs.wormhole.com/wormhole/quick-start/cross-chain-dev/specialized-relayer contract WormholeHelper is Test { - /*/////////////////////////////////////////////////////////////// - CONSTANTS - //////////////////////////////////////////////////////////////*/ - - /// @dev LogMessagePublished (index_topic_1 address sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel) + /// @dev is the default event selector if not specified by the user bytes32 constant MESSAGE_EVENT_SELECTOR = 0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2; - /*/////////////////////////////////////////////////////////////// - EXTERNAL FUNCTIONS - //////////////////////////////////////////////////////////////*/ + ////////////////////////////////////////////////////////////// + // EXTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// /// @dev single dst x default event selector - /// @param srcChainId is the wormhole identifier of the source chain - /// @param dstForkId is the dst fork id to deliver the message - /// @param dstWormhole is the wormhole core contract on dst chain - /// @param dstTarget is the final receiver of the message - /// @param srcLogs is the logs after message dispatch on src chain + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstWormhole represents the wormhole core contract on dst chain + /// @param dstTarget represents the final receiver of the message + /// @param srcLogs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256 dstForkId, @@ -47,6 +42,12 @@ contract WormholeHelper is Test { } /// @dev single dst x user-specified event selector + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstWormhole represents the wormhole core contract on dst chain + /// @param dstTarget represents the final receiver of the message + /// @param msgEventSelector represents the user-specified event selector + /// @param srcLogs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256 dstForkId, @@ -59,6 +60,11 @@ contract WormholeHelper is Test { } /// @dev multi dst x default event selector + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the array of dst fork ids to deliver the message + /// @param dstWormhole represents the array of wormhole core contracts on dst chains + /// @param dstTarget represents the array of final receivers of the message + /// @param srcLogs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256[] memory dstForkId, @@ -76,6 +82,12 @@ contract WormholeHelper is Test { } /// @dev multi dst x user-specified event selector + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the array of dst fork ids to deliver the message + /// @param dstWormhole represents the array of wormhole core contracts on dst chains + /// @param dstTarget represents the array of final receivers of the message + /// @param msgEventSelector represents the user-specified event selector + /// @param srcLogs represents the logs after message dispatch on src chain function help( uint16 srcChainId, uint256[] memory dstForkId, @@ -94,13 +106,16 @@ contract WormholeHelper is Test { } /// @dev helps find logs of `length` for default event selector + /// @param logs represents the logs after message dispatch on src chain + /// @param length represents the expected number of logs + /// @return HLLogs array of found logs function findLogs(Vm.Log[] calldata logs, uint256 length) external pure returns (Vm.Log[] memory HLLogs) { return _findLogs(logs, MESSAGE_EVENT_SELECTOR, length); } - /*/////////////////////////////////////////////////////////////// - INTERNAL HELPER FUNCTIONS - //////////////////////////////////////////////////////////////*/ + ////////////////////////////////////////////////////////////// + // INTERNAL FUNCTIONS // + ////////////////////////////////////////////////////////////// struct LocalVars { uint256 prevForkId; @@ -111,6 +126,13 @@ contract WormholeHelper is Test { address dstAddress; } + /// @dev internal function to help with message delivery + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param dstForkId represents the dst fork id to deliver the message + /// @param dstWormhole represents the wormhole core contract on dst chain + /// @param dstTarget represents the final receiver of the message + /// @param eventSelector represents the event selector + /// @param srcLogs represents the logs after message dispatch on src chain function _help( uint16 srcChainId, uint256 dstForkId, @@ -155,6 +177,7 @@ contract WormholeHelper is Test { /// @dev overrides the guardian set by choosing slot /// @notice overrides the current guardian set with a set of known guardian set + /// @param dstWormhole represents the wormhole core contract on dst chain function _prepareWormhole(address dstWormhole) internal { IWormhole wormhole = IWormhole(dstWormhole); @@ -174,6 +197,14 @@ contract WormholeHelper is Test { } /// @dev generates the encoded vaa + /// @param srcChainId represents the wormhole identifier of the source chain + /// @param nonce represents the nonce of the message + /// @param emitterAddress represents the emitter address + /// @param sequence represents the sequence of the message + /// @param consistencyLevel represents the consistency level + /// @param payload represents the message payload + /// @param dstWormhole represents the wormhole core contract on dst chain + /// @return encodedVAA the encoded VAA function _generateVAA( uint16 srcChainId, uint32 nonce, @@ -235,6 +266,10 @@ contract WormholeHelper is Test { } /// @dev helper to get logs + /// @param logs represents the logs after message dispatch on src chain + /// @param dispatchSelector represents the event selector + /// @param length represents the expected number of logs + /// @return WormholeLogs array of found logs function _findLogs(Vm.Log[] memory logs, bytes32 dispatchSelector, uint256 length) internal pure