// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.26; import "../TestUtils.sol"; import "@src/executors/BalancerV2Executor.sol"; import {Constants} from "../Constants.sol"; contract BalancerV2ExecutorExposed is BalancerV2Executor { constructor(address _permit2) BalancerV2Executor(_permit2) {} function decodeParams(bytes calldata data) external pure returns ( IERC20 tokenIn, IERC20 tokenOut, bytes32 poolId, address receiver, bool needsApproval ) { return _decodeData(data); } } contract BalancerV2ExecutorTest is Constants, TestUtils { using SafeERC20 for IERC20; BalancerV2ExecutorExposed balancerV2Exposed; IERC20 WETH = IERC20(WETH_ADDR); IERC20 BAL = IERC20(BAL_ADDR); bytes32 constant WETH_BAL_POOL_ID = 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014; function setUp() public { uint256 forkBlock = 17323404; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); balancerV2Exposed = new BalancerV2ExecutorExposed(PERMIT2_ADDRESS); } function testDecodeParams() public view { bytes memory params = abi.encodePacked( WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, address(2), true ); ( IERC20 tokenIn, IERC20 tokenOut, bytes32 poolId, address receiver, bool needsApproval ) = balancerV2Exposed.decodeParams(params); assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenOut), BAL_ADDR); assertEq(poolId, WETH_BAL_POOL_ID); assertEq(receiver, address(2)); assertEq(needsApproval, true); } function testDecodeParamsInvalidDataLength() public { bytes memory invalidParams = abi.encodePacked(WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, address(2)); vm.expectRevert(BalancerV2Executor__InvalidDataLength.selector); balancerV2Exposed.decodeParams(invalidParams); } function testSwap() public { uint256 amountIn = 10 ** 18; bytes memory protocolData = abi.encodePacked(WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, BOB, true); deal(WETH_ADDR, address(balancerV2Exposed), amountIn); uint256 balanceBefore = BAL.balanceOf(BOB); uint256 amountOut = balancerV2Exposed.swap(amountIn, protocolData); uint256 balanceAfter = BAL.balanceOf(BOB); assertGt(balanceAfter, balanceBefore); assertEq(balanceAfter - balanceBefore, amountOut); } function testDecodeIntegration() public view { bytes memory protocolData = loadCallDataFromFile("test_encode_balancer_v2"); ( IERC20 tokenIn, IERC20 tokenOut, bytes32 poolId, address receiver, bool needsApproval ) = balancerV2Exposed.decodeParams(protocolData); assertEq(address(tokenIn), WETH_ADDR); assertEq(address(tokenOut), BAL_ADDR); assertEq(poolId, WETH_BAL_POOL_ID); assertEq(receiver, BOB); assertEq(needsApproval, true); } function testSwapIntegration() public { // Generated by the SwapEncoder - test_encode_balancer_v2 bytes memory protocolData = loadCallDataFromFile("test_encode_balancer_v2"); uint256 amountIn = 10 ** 18; deal(WETH_ADDR, address(balancerV2Exposed), amountIn); uint256 balanceBefore = BAL.balanceOf(BOB); uint256 amountOut = balancerV2Exposed.swap(amountIn, protocolData); uint256 balanceAfter = BAL.balanceOf(BOB); assertGt(balanceAfter, balanceBefore); assertEq(balanceAfter - balanceBefore, amountOut); } }