Skip to main content
The Foundry TestHelper contract is designed to simulate a LayerZero environment, enabling developers to rigorously test omnichain applications within Foundry. The contract allows for the setup of multiple simulated LayerZero endpoints to help test how a smart contract interacts with the protocol, and queue message packets to mimic real-world handling of message traffic. Developers can test how their contracts handle multiple incoming and outgoing messages, including their order and delivery:
FunctionDescriptionParameters
setUpEndpointsSets up multiple simulated endpoints to mimic different blockchains.uint8 numEndpoints, LibraryType libraryType
setupOAppsDeploys multiple instances of OApps with specific parameters for testing.bytes memory _oappCreationCode, uint8 _startEid, uint8 _oappNum
setupOFTsDeploys multiple OFTs with specific attributes for testing behavior.bytes memory _oftCreationCode, uint8 _oftNum, string[] memory _names, string[] memory _symbols, uint8[] memory _localDecimals
deliverPacketsManually delivers message packets to a specified destination contract.uint32 _dstEid, address _dstAddress

Unit Testing

For example, see how we test the HelloLayerZero contract in the Getting started guide:
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.15;

import "@layerzerolabs/contracts/TestHelper.sol";
import "@layerzerolabs/contracts/OptionsBuilder.sol";
import "@layerzerolabs/contracts/examples/HelloLayerZero.sol";

/**
 * @title HelloLayerZeroTest
 * @dev This contract is a test suite for HelloLayerZero, which demonstrates simple message passing using LayerZero.
 */
contract HelloLayerZeroTest is TestHelper {
    using OptionsBuilder for bytes;

    uint32 private aEid = 1;
    uint32 private bEid = 2;

    HelloLayerZero private aHelloLayerZero;
    HelloLayerZero private bHelloLayerZero;

    /**
     * @dev Sets up the testing environment.
     * This includes setting up endpoints and deploying instances of HelloLayerZero on different chains.
     */
    function setUp() public virtual override {
        super.setUp();

        // Set up two endpoints to simulate two different chains
        setUpEndpoints(2, LibraryType.UltraLightNode);

        // Deploy instances of HelloLayerZero on these chains
        address[] memory uas = setupOApps(type(HelloLayerZero).creationCode, 1, 2);
        aHelloLayerZero = HelloLayerZero(payable(uas[0]));
        bHelloLayerZero = HelloLayerZero(payable(uas[1]));
    }

    /**
     * @dev Tests the basic message passing functionality from Chain A to Chain B using HelloLayerZero.
     * It sends a message from aHelloLayerZero to bHelloLayerZero and checks if the message is received correctly.
     */
    function test_message() public {
        string memory messageBefore = "Nothing received yet";
        string memory message = "test message";
        bytes memory payload = abi.encode(message);
        bytes memory _options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0);

        // Estimate fees for sending the message and send it
        (uint nativeFee, ) = aHelloLayerZero.estimateFees(bEid, payload, _options);
        aHelloLayerZero.send{value: nativeFee}(bEid, message, _options);

        // Ensure the message at the destination is not changed before delivery
        assertEq(bHelloLayerZero.data(), messageBefore, "shouldn't change message until packet is delivered");

        // Manually deliver the packet to the destination contract
        deliverPackets(bEid, addressToBytes32(address(bHelloLayerZero)));

        // Check if the message has been updated after packet delivery
        assertEq(bHelloLayerZero.data(), message, "message storage failure");

        // Log the options used for debugging purposes
        console.logBytes(_options);
    }
}