mirror of
https://github.com/lux-amm/v4-periphery.git
synced 2026-10-30 20:54:39 +00:00
220 lines
8.6 KiB
Solidity
220 lines
8.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "forge-std/Test.sol";
|
|
import {MockMulticall, RevertContract} from "./mocks/MockMulticall.sol";
|
|
|
|
contract MulticallTest is Test {
|
|
MockMulticall multicall;
|
|
|
|
function setUp() public {
|
|
multicall = new MockMulticall();
|
|
}
|
|
|
|
function test_multicall() public {
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 10, 20);
|
|
calls[1] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 1, 2);
|
|
|
|
bytes[] memory results = multicall.multicall(calls);
|
|
|
|
(uint256 a, uint256 b) = abi.decode(results[0], (uint256, uint256));
|
|
assertEq(a, 10);
|
|
assertEq(b, 20);
|
|
|
|
(a, b) = abi.decode(results[1], (uint256, uint256));
|
|
assertEq(a, 1);
|
|
assertEq(b, 2);
|
|
}
|
|
|
|
function test_multicall_firstRevert() public {
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(
|
|
MockMulticall(multicall).functionThatRevertsWithString.selector, "First call failed"
|
|
);
|
|
calls[1] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 1, 2);
|
|
|
|
vm.expectRevert("First call failed");
|
|
multicall.multicall(calls);
|
|
}
|
|
|
|
function test_multicall_secondRevert() public {
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 1, 2);
|
|
calls[1] = abi.encodeWithSelector(
|
|
MockMulticall(multicall).functionThatRevertsWithString.selector, "Second call failed"
|
|
);
|
|
|
|
vm.expectRevert("Second call failed");
|
|
multicall.multicall(calls);
|
|
}
|
|
|
|
function test_multicall_payableStoresMsgValue() public {
|
|
assertEq(address(multicall).balance, 0);
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValue.selector);
|
|
multicall.multicall{value: 100}(calls);
|
|
assertEq(address(multicall).balance, 100);
|
|
assertEq(multicall.msgValue(), 100);
|
|
}
|
|
|
|
function test_multicall_returnSender() public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).returnSender.selector);
|
|
bytes[] memory results = multicall.multicall(calls);
|
|
address sender = abi.decode(results[0], (address));
|
|
assertEq(sender, address(this));
|
|
}
|
|
|
|
function test_multicall_returnSender_prank() public {
|
|
address alice = makeAddr("ALICE");
|
|
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).returnSender.selector, alice);
|
|
vm.prank(alice);
|
|
bytes[] memory results = multicall.multicall(calls);
|
|
address sender = abi.decode(results[0], (address));
|
|
assertEq(sender, alice);
|
|
}
|
|
|
|
function test_multicall_double_send() public {
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValue.selector);
|
|
calls[1] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValue.selector);
|
|
|
|
multicall.multicall{value: 100}(calls);
|
|
assertEq(address(multicall).balance, 100);
|
|
assertEq(multicall.msgValue(), 100);
|
|
}
|
|
|
|
function test_multicall_unpayableRevert() public {
|
|
// first call is payable, second is not which causes a revert
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValue.selector);
|
|
calls[1] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 10, 20);
|
|
|
|
vm.expectRevert();
|
|
multicall.multicall{value: 100}(calls);
|
|
}
|
|
|
|
function test_multicall_bothPayable() public {
|
|
// msg.value is provided to both calls
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValue.selector);
|
|
calls[1] = abi.encodeWithSelector(MockMulticall(multicall).payableStoresMsgValueDouble.selector);
|
|
|
|
multicall.multicall{value: 100}(calls);
|
|
assertEq(address(multicall).balance, 100);
|
|
assertEq(multicall.msgValue(), 100);
|
|
assertEq(multicall.msgValueDouble(), 200);
|
|
}
|
|
|
|
// revert bubbling
|
|
function test_multicall_bubbleRevert_string() public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] =
|
|
abi.encodeWithSelector(MockMulticall(multicall).functionThatRevertsWithString.selector, "errorString");
|
|
|
|
vm.expectRevert("errorString");
|
|
multicall.multicall(calls);
|
|
}
|
|
|
|
function test_multicall_bubbleRevert_4bytes() public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).revertWith4Bytes.selector);
|
|
|
|
// revert is caught
|
|
vm.expectRevert(MockMulticall.Error4Bytes.selector);
|
|
multicall.multicall(calls);
|
|
|
|
// confirm expected length of the revert
|
|
try multicall.revertWith4Bytes() {}
|
|
catch (bytes memory reason) {
|
|
assertEq(reason.length, 4);
|
|
}
|
|
}
|
|
|
|
function test_fuzz_multicall_bubbleRevert_36bytes(uint8 num) public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).revertWith36Bytes.selector, num);
|
|
|
|
// revert is caught
|
|
vm.expectRevert(abi.encodeWithSelector(MockMulticall.Error36Bytes.selector, num));
|
|
multicall.multicall(calls);
|
|
|
|
// confirm expected length of the revert
|
|
try multicall.revertWith36Bytes(num) {}
|
|
catch (bytes memory reason) {
|
|
assertEq(reason.length, 36);
|
|
}
|
|
}
|
|
|
|
function test_fuzz_multicall_bubbleRevert_68bytes(uint256 a, uint256 b) public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).revertWith68Bytes.selector, a, b);
|
|
|
|
// revert is caught
|
|
vm.expectRevert(abi.encodeWithSelector(MockMulticall.Error68Bytes.selector, a, b));
|
|
multicall.multicall(calls);
|
|
|
|
// confirm expected length of the revert
|
|
try multicall.revertWith68Bytes(a, b) {}
|
|
catch (bytes memory reason) {
|
|
assertEq(reason.length, 68);
|
|
}
|
|
}
|
|
|
|
function test_fuzz_multicall_bubbleRevert_arbitraryBytes(uint16 length) public {
|
|
length = uint16(bound(length, 0, 4096));
|
|
bytes memory data = new bytes(length);
|
|
for (uint256 i = 0; i < data.length; i++) {
|
|
data[i] = bytes1(uint8(i));
|
|
}
|
|
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).revertWithBytes.selector, data);
|
|
|
|
// revert is caught
|
|
vm.expectRevert(abi.encodeWithSelector(MockMulticall.ErrorBytes.selector, data));
|
|
multicall.multicall(calls);
|
|
|
|
// confirm expected length of the revert
|
|
try multicall.revertWithBytes(data) {}
|
|
catch (bytes memory reason) {
|
|
// errors with 0 bytes are by default 64 bytes of data (length & pointer?) + 4 bytes of selector
|
|
if (length == 0) {
|
|
assertEq(reason.length, 68);
|
|
} else {
|
|
uint256 expectedLength = 64 + 4; // default length + selector
|
|
// 32 bytes added to the reason for each 32 bytes of data
|
|
expectedLength += (((data.length - 1) / 32) + 1) * 32;
|
|
assertEq(reason.length, expectedLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
function test_multicall_bubbleRevert_externalRevertString() public {
|
|
bytes[] memory calls = new bytes[](2);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).externalRevertString.selector, "errorString");
|
|
|
|
vm.expectRevert("errorString");
|
|
multicall.multicall(calls);
|
|
}
|
|
|
|
function test_multicall_bubbleRevert_externalRevertSimple() public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).externalRevertError1.selector);
|
|
|
|
vm.expectRevert(RevertContract.Error1.selector);
|
|
multicall.multicall(calls);
|
|
}
|
|
|
|
function test_multicall_bubbleRevert_externalRevertWithParams(uint256 a, uint256 b) public {
|
|
bytes[] memory calls = new bytes[](1);
|
|
calls[0] = abi.encodeWithSelector(MockMulticall(multicall).externalRevertError2.selector, a, b);
|
|
|
|
vm.expectRevert(abi.encodeWithSelector(RevertContract.Error2.selector, a, b));
|
|
multicall.multicall(calls);
|
|
}
|
|
}
|