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:
Function | Description | Parameters |
---|---|---|
setUpEndpoints | Sets up multiple simulated endpoints to mimic different blockchains. | uint8 numEndpoints, LibraryType libraryType |
setupOApps | Deploys multiple instances of OApps with specific parameters for testing. | bytes memory _oappCreationCode, uint8 _startEid, uint8 _oappNum |
setupOFTs | Deploys multiple OFTs with specific attributes for testing behavior. | bytes memory _oftCreationCode, uint8 _oftNum, string[] memory _names, string[] memory _symbols, uint8[] memory _localDecimals |
deliverPackets | Manually 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);
}
}