Skip to main content
Version: Endpoint V2 Docs

Foundry

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);
}
}