You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.3 KiB
182 lines
6.3 KiB
9 months ago
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
pragma solidity ^0.8.13;
|
||
|
|
||
|
import {Test} from "forge-std/Test.sol";
|
||
|
import {LZEndpointMock} from "@layerzerolabs/solidity-examples/contracts/lzApp/mocks/LZEndpointMock.sol";
|
||
|
import {OmniCounter} from "@layerzerolabs/solidity-examples/contracts/examples/OmniCounter.sol";
|
||
|
import {TypeCasts} from "../../../contracts/libs/TypeCasts.sol";
|
||
|
import {StandardHookMetadata} from "../../../contracts/hooks/libs/StandardHookMetadata.sol";
|
||
|
import {TestMailbox} from "../../../contracts/test/TestMailbox.sol";
|
||
|
import {TestPostDispatchHook} from "../../../contracts/test/TestPostDispatchHook.sol";
|
||
|
import {LayerZeroV1Hook, LayerZeroMetadata} from "../../../contracts/hooks/layer-zero/LayerZeroV1Hook.sol";
|
||
|
import {IPostDispatchHook} from "../../../contracts/interfaces/hooks/IPostDispatchHook.sol";
|
||
|
|
||
|
import "forge-std/console.sol";
|
||
|
|
||
|
contract LayerZeroV1HookTest is Test {
|
||
|
using TypeCasts for address;
|
||
|
|
||
|
OmniCounter crossChainCounterApp;
|
||
|
LZEndpointMock lZEndpointMock;
|
||
|
TestMailbox public mailbox;
|
||
|
TestPostDispatchHook requiredHook;
|
||
|
LayerZeroV1Hook hook;
|
||
|
address alice = makeAddr("alice");
|
||
|
|
||
|
function setUp() public {
|
||
|
lZEndpointMock = new LZEndpointMock(uint16(block.chainid));
|
||
|
crossChainCounterApp = new OmniCounter(address(lZEndpointMock));
|
||
|
requiredHook = new TestPostDispatchHook();
|
||
|
mailbox = new TestMailbox(0);
|
||
|
hook = new LayerZeroV1Hook(address(mailbox), address(lZEndpointMock));
|
||
|
|
||
|
mailbox.setRequiredHook(address(requiredHook));
|
||
|
|
||
|
// Sets the endpoint destinations
|
||
|
lZEndpointMock.setDestLzEndpoint(
|
||
|
address(crossChainCounterApp),
|
||
|
address(lZEndpointMock)
|
||
|
);
|
||
|
|
||
|
// set hook as a trusted remote
|
||
|
crossChainCounterApp.setTrustedRemote(
|
||
|
uint16(block.chainid),
|
||
|
abi.encodePacked(address(hook), address(crossChainCounterApp))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testLzV1Hook_ParseLzMetadata_returnsCorrectData() public {
|
||
|
// format Lz metadata
|
||
|
uint16 dstChainId = uint16(block.chainid);
|
||
|
address userApplication = address(crossChainCounterApp);
|
||
|
address refundAddress = alice;
|
||
|
bytes memory payload = "Hello World!";
|
||
|
bytes memory destination = abi.encodePacked(
|
||
|
userApplication,
|
||
|
address(lZEndpointMock)
|
||
|
); // remoteAndLocalAddresses
|
||
|
bytes memory adapterParam = "";
|
||
|
LayerZeroMetadata memory layerZeroMetadata = LayerZeroMetadata(
|
||
|
dstChainId,
|
||
|
userApplication,
|
||
|
refundAddress,
|
||
|
payload,
|
||
|
destination,
|
||
|
adapterParam
|
||
|
);
|
||
|
bytes memory formattedMetadata = hook.formatLzMetadata(
|
||
|
layerZeroMetadata
|
||
|
);
|
||
|
|
||
|
LayerZeroMetadata memory parsedLayerZeroMetadata = hook.parseLzMetadata(
|
||
|
formattedMetadata
|
||
|
);
|
||
|
assertEq(parsedLayerZeroMetadata.dstChainId, dstChainId);
|
||
|
assertEq(parsedLayerZeroMetadata.userApplication, userApplication);
|
||
|
assertEq(parsedLayerZeroMetadata.refundAddress, refundAddress);
|
||
|
assertEq(parsedLayerZeroMetadata.payload, payload);
|
||
|
assertEq(parsedLayerZeroMetadata.destination, destination);
|
||
|
assertEq(parsedLayerZeroMetadata.adapterParam, adapterParam);
|
||
|
}
|
||
|
|
||
|
function testLzV1Hook_QuoteDispatch_returnsFeeAmount()
|
||
|
public
|
||
|
returns (uint256 nativeFee, bytes memory metadata)
|
||
|
{
|
||
|
// Build metadata to include LZ-specific data
|
||
|
uint16 dstChainId = uint16(block.chainid);
|
||
|
address userApplication = address(crossChainCounterApp);
|
||
|
address refundAddress = alice;
|
||
|
bytes memory payload = "Hello World!";
|
||
|
bytes memory destination = abi.encodePacked(
|
||
|
userApplication,
|
||
|
address(lZEndpointMock)
|
||
|
); // remoteAndLocalAddresses
|
||
|
bytes memory adapterParam = "";
|
||
|
LayerZeroMetadata memory layerZeroMetadata = LayerZeroMetadata(
|
||
|
dstChainId,
|
||
|
userApplication,
|
||
|
refundAddress,
|
||
|
payload,
|
||
|
destination,
|
||
|
adapterParam
|
||
|
);
|
||
|
bytes memory formattedLzMetadata = hook.formatLzMetadata(
|
||
|
layerZeroMetadata
|
||
|
);
|
||
|
metadata = StandardHookMetadata.formatMetadata(
|
||
|
0,
|
||
|
0,
|
||
|
refundAddress,
|
||
|
formattedLzMetadata
|
||
|
);
|
||
|
bytes memory message = mailbox.buildOutboundMessage(
|
||
|
0,
|
||
|
address(lZEndpointMock).addressToBytes32(),
|
||
|
payload
|
||
|
);
|
||
|
nativeFee = hook.quoteDispatch(metadata, message);
|
||
|
|
||
|
// It costs something
|
||
|
assertGt(nativeFee, 0);
|
||
|
}
|
||
|
|
||
|
function testLzV1Hook_PostDispatch_executesCrossChain() public {
|
||
|
(
|
||
|
uint256 nativeFee,
|
||
|
bytes memory metadata
|
||
|
) = testLzV1Hook_QuoteDispatch_returnsFeeAmount();
|
||
|
|
||
|
// dispatch also executes L0 call to increment counter
|
||
|
assertEq(crossChainCounterApp.counter(), 0);
|
||
|
mailbox.dispatch{value: nativeFee}(
|
||
|
0,
|
||
|
address(crossChainCounterApp).addressToBytes32(),
|
||
|
"Hello World!",
|
||
|
metadata,
|
||
|
hook
|
||
|
);
|
||
|
assertEq(crossChainCounterApp.counter(), 1);
|
||
|
}
|
||
|
|
||
|
function testLzV1Hook_PostDispatch_notEnoughFee() public {
|
||
|
(
|
||
|
uint256 nativeFee,
|
||
|
bytes memory metadata
|
||
|
) = testLzV1Hook_QuoteDispatch_returnsFeeAmount();
|
||
|
|
||
|
vm.expectRevert("LayerZeroMock: not enough native for fees");
|
||
|
mailbox.dispatch{value: nativeFee - 1}(
|
||
|
0,
|
||
|
address(crossChainCounterApp).addressToBytes32(),
|
||
|
"Hello World!",
|
||
|
metadata,
|
||
|
hook
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testLzV1Hook_PostDispatch_refundExtraFee() public {
|
||
|
(
|
||
|
uint256 nativeFee,
|
||
|
bytes memory metadata
|
||
|
) = testLzV1Hook_QuoteDispatch_returnsFeeAmount();
|
||
|
|
||
|
uint256 balanceBefore = address(alice).balance;
|
||
|
mailbox.dispatch{value: nativeFee + 1}(
|
||
|
0,
|
||
|
address(crossChainCounterApp).addressToBytes32(),
|
||
|
"Hello World!",
|
||
|
metadata,
|
||
|
hook
|
||
|
);
|
||
|
uint256 balanceAfter = address(alice).balance;
|
||
|
|
||
|
assertEq(balanceAfter, balanceBefore + 1);
|
||
|
}
|
||
|
|
||
|
// TODO test failed/retry
|
||
|
function testLzV1Hook_HookType() public {
|
||
|
assertEq(hook.hookType(), uint8(IPostDispatchHook.Types.LAYER_ZERO_V1));
|
||
|
}
|
||
|
}
|