feat: Transfer Optimizations in MaverickV2
- Also added integration test to test the optimizations, where we can see the in and out transfers being optimized if we enable verbose foundry testing - Fixed typo in swap encoder builder initialization
This commit is contained in:
@@ -3,18 +3,19 @@ pragma solidity ^0.8.26;
|
||||
|
||||
import "@interfaces/IExecutor.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "./TokenTransfer.sol";
|
||||
|
||||
error MaverickV2Executor__InvalidDataLength();
|
||||
error MaverickV2Executor__InvalidTarget();
|
||||
error MaverickV2Executor__InvalidFactory();
|
||||
|
||||
contract MaverickV2Executor is IExecutor {
|
||||
contract MaverickV2Executor is IExecutor, TokenTransfer {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address public immutable factory;
|
||||
address private immutable self;
|
||||
|
||||
constructor(address _factory) {
|
||||
constructor(address _factory, address _permit2) TokenTransfer(_permit2) {
|
||||
if (_factory == address(0)) {
|
||||
revert MaverickV2Executor__InvalidFactory();
|
||||
}
|
||||
@@ -31,8 +32,9 @@ contract MaverickV2Executor is IExecutor {
|
||||
address target;
|
||||
address receiver;
|
||||
IERC20 tokenIn;
|
||||
TransferType transferType;
|
||||
|
||||
(tokenIn, target, receiver) = _decodeData(data);
|
||||
(tokenIn, target, receiver, transferType) = _decodeData(data);
|
||||
|
||||
_verifyPairAddress(target);
|
||||
IMaverickV2Pool pool = IMaverickV2Pool(target);
|
||||
@@ -46,21 +48,31 @@ contract MaverickV2Executor is IExecutor {
|
||||
exactOutput: false,
|
||||
tickLimit: tickLimit
|
||||
});
|
||||
IERC20(tokenIn).safeTransfer(target, givenAmount);
|
||||
|
||||
_transfer(
|
||||
address(tokenIn), msg.sender, target, givenAmount, transferType
|
||||
);
|
||||
// slither-disable-next-line unused-return
|
||||
(, calculatedAmount) = pool.swap(receiver, swapParams, "");
|
||||
}
|
||||
|
||||
function _decodeData(bytes calldata data)
|
||||
internal
|
||||
pure
|
||||
returns (IERC20 inToken, address target, address receiver)
|
||||
returns (
|
||||
IERC20 inToken,
|
||||
address target,
|
||||
address receiver,
|
||||
TransferType transferType
|
||||
)
|
||||
{
|
||||
if (data.length != 60) {
|
||||
if (data.length != 61) {
|
||||
revert MaverickV2Executor__InvalidDataLength();
|
||||
}
|
||||
inToken = IERC20(address(bytes20(data[0:20])));
|
||||
target = address(bytes20(data[20:40]));
|
||||
receiver = address(bytes20(data[40:60]));
|
||||
transferType = TransferType(uint8(data[60]));
|
||||
}
|
||||
|
||||
function _verifyPairAddress(address target) internal view {
|
||||
|
||||
@@ -186,6 +186,27 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup {
|
||||
assertEq(balanceAfter - balanceBefore, 1474406268748155809);
|
||||
}
|
||||
|
||||
function testSingleMaverickIntegration() public {
|
||||
vm.stopPrank();
|
||||
|
||||
deal(GHO_ADDR, ALICE, 1 ether);
|
||||
uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max);
|
||||
|
||||
bytes memory callData =
|
||||
loadCallDataFromFile("test_single_encoding_strategy_maverick");
|
||||
(bool success,) = tychoRouterAddr.call(callData);
|
||||
|
||||
uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGe(balanceAfter - balanceBefore, 999725);
|
||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||
}
|
||||
|
||||
function testSingleEkuboIntegration() public {
|
||||
vm.stopPrank();
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol";
|
||||
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
|
||||
import {Permit2TestHelper} from "./Permit2TestHelper.sol";
|
||||
import "./TestUtils.sol";
|
||||
import {MaverickV2Executor} from "../src/executors/MaverickV2Executor.sol";
|
||||
|
||||
contract TychoRouterExposed is TychoRouter {
|
||||
constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {}
|
||||
@@ -53,6 +54,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
BalancerV2Executor public balancerv2Executor;
|
||||
EkuboExecutor public ekuboExecutor;
|
||||
CurveExecutor public curveExecutor;
|
||||
MaverickV2Executor public maverickv2Executor;
|
||||
MockERC20[] tokens;
|
||||
|
||||
function setUp() public {
|
||||
@@ -110,8 +112,10 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
balancerv2Executor = new BalancerV2Executor(PERMIT2_ADDRESS);
|
||||
ekuboExecutor = new EkuboExecutor(ekuboCore, PERMIT2_ADDRESS);
|
||||
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
|
||||
maverickv2Executor =
|
||||
new MaverickV2Executor(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS);
|
||||
|
||||
address[] memory executors = new address[](7);
|
||||
address[] memory executors = new address[](8);
|
||||
executors[0] = address(usv2Executor);
|
||||
executors[1] = address(usv3Executor);
|
||||
executors[2] = address(pancakev3Executor);
|
||||
@@ -119,6 +123,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
executors[4] = address(balancerv2Executor);
|
||||
executors[5] = address(ekuboExecutor);
|
||||
executors[6] = address(curveExecutor);
|
||||
executors[7] = address(maverickv2Executor);
|
||||
return executors;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,3 +24,4 @@ test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754
|
||||
test_ekubo_encode_swap_multi:00ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
|
||||
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
|
||||
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001
|
||||
test_single_encoding_strategy_maverick:20144a070000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc201000000000000000000000000000000
|
||||
|
||||
@@ -6,12 +6,19 @@ import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
|
||||
contract MaverickV2ExecutorExposed is MaverickV2Executor {
|
||||
constructor(address _factory) MaverickV2Executor(_factory) {}
|
||||
constructor(address _factory, address _permit2)
|
||||
MaverickV2Executor(_factory, _permit2)
|
||||
{}
|
||||
|
||||
function decodeParams(bytes calldata data)
|
||||
external
|
||||
pure
|
||||
returns (IERC20 tokenIn, address target, address receiver)
|
||||
returns (
|
||||
IERC20 tokenIn,
|
||||
address target,
|
||||
address receiver,
|
||||
TransferType transferType
|
||||
)
|
||||
{
|
||||
return _decodeData(data);
|
||||
}
|
||||
@@ -27,24 +34,37 @@ contract MaverickV2ExecutorTest is Test, Constants {
|
||||
function setUp() public {
|
||||
uint256 forkBlock = 22096000;
|
||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||
maverickV2Exposed = new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY);
|
||||
maverickV2Exposed =
|
||||
new MaverickV2ExecutorExposed(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS);
|
||||
}
|
||||
|
||||
function testDecodeParams() public view {
|
||||
bytes memory params =
|
||||
abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2));
|
||||
bytes memory params = abi.encodePacked(
|
||||
GHO_ADDR,
|
||||
GHO_USDC_POOL,
|
||||
address(2),
|
||||
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
|
||||
);
|
||||
|
||||
(IERC20 tokenIn, address target, address receiver) =
|
||||
maverickV2Exposed.decodeParams(params);
|
||||
(
|
||||
IERC20 tokenIn,
|
||||
address target,
|
||||
address receiver,
|
||||
TokenTransfer.TransferType transferType
|
||||
) = maverickV2Exposed.decodeParams(params);
|
||||
|
||||
assertEq(address(tokenIn), GHO_ADDR);
|
||||
assertEq(target, GHO_USDC_POOL);
|
||||
assertEq(receiver, address(2));
|
||||
assertEq(
|
||||
uint8(transferType),
|
||||
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
|
||||
);
|
||||
}
|
||||
|
||||
function testDecodeParamsInvalidDataLength() public {
|
||||
bytes memory invalidParams =
|
||||
abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2), true);
|
||||
abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, address(2));
|
||||
|
||||
vm.expectRevert(MaverickV2Executor__InvalidDataLength.selector);
|
||||
maverickV2Exposed.decodeParams(invalidParams);
|
||||
@@ -52,8 +72,12 @@ contract MaverickV2ExecutorTest is Test, Constants {
|
||||
|
||||
function testSwap() public {
|
||||
uint256 amountIn = 10e18;
|
||||
bytes memory protocolData =
|
||||
abi.encodePacked(GHO_ADDR, GHO_USDC_POOL, BOB);
|
||||
bytes memory protocolData = abi.encodePacked(
|
||||
GHO_ADDR,
|
||||
GHO_USDC_POOL,
|
||||
BOB,
|
||||
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
|
||||
);
|
||||
|
||||
deal(GHO_ADDR, address(maverickV2Exposed), amountIn);
|
||||
uint256 balanceBefore = USDC.balanceOf(BOB);
|
||||
@@ -68,20 +92,28 @@ contract MaverickV2ExecutorTest is Test, Constants {
|
||||
function testDecodeIntegration() public view {
|
||||
// Generated by the SwapEncoder - test_encode_maverick_v2
|
||||
bytes memory protocolData =
|
||||
hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e";
|
||||
hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00";
|
||||
|
||||
(IERC20 tokenIn, address pool, address receiver) =
|
||||
maverickV2Exposed.decodeParams(protocolData);
|
||||
(
|
||||
IERC20 tokenIn,
|
||||
address pool,
|
||||
address receiver,
|
||||
TokenTransfer.TransferType transferType
|
||||
) = maverickV2Exposed.decodeParams(protocolData);
|
||||
|
||||
assertEq(address(tokenIn), GHO_ADDR);
|
||||
assertEq(pool, GHO_USDC_POOL);
|
||||
assertEq(receiver, BOB);
|
||||
assertEq(
|
||||
uint8(transferType),
|
||||
uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL)
|
||||
);
|
||||
}
|
||||
|
||||
function testSwapIntegration() public {
|
||||
// Generated by the SwapEncoder - test_encode_maverick_v2
|
||||
bytes memory protocolData =
|
||||
hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e";
|
||||
hex"40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00";
|
||||
|
||||
uint256 amountIn = 10 ** 18;
|
||||
deal(GHO_ADDR, address(maverickV2Exposed), amountIn);
|
||||
|
||||
Reference in New Issue
Block a user