feat: Add TokenTransfer class to BalancerV2
- This needed to be in BalancerV2 so that the executor can also take care of transfers from the user into the tycho router, to avoid having transfer actions mixed between the router and executors
This commit is contained in:
committed by
Diana Carvalho
parent
462be5463b
commit
3a73fef933
@@ -10,14 +10,17 @@ import {
|
|||||||
import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
|
import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
|
||||||
// slither-disable-next-line solc-version
|
// slither-disable-next-line solc-version
|
||||||
import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
|
import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
|
||||||
|
import {TokenTransfer} from "./TokenTransfer.sol";
|
||||||
|
|
||||||
error BalancerV2Executor__InvalidDataLength();
|
error BalancerV2Executor__InvalidDataLength();
|
||||||
|
|
||||||
contract BalancerV2Executor is IExecutor {
|
contract BalancerV2Executor is IExecutor, TokenTransfer {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
|
address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
|
||||||
|
|
||||||
|
constructor(address _permit2) TokenTransfer(_permit2) {}
|
||||||
|
|
||||||
// slither-disable-next-line locked-ether
|
// slither-disable-next-line locked-ether
|
||||||
function swap(uint256 givenAmount, bytes calldata data)
|
function swap(uint256 givenAmount, bytes calldata data)
|
||||||
external
|
external
|
||||||
@@ -29,9 +32,20 @@ contract BalancerV2Executor is IExecutor {
|
|||||||
IERC20 tokenOut,
|
IERC20 tokenOut,
|
||||||
bytes32 poolId,
|
bytes32 poolId,
|
||||||
address receiver,
|
address receiver,
|
||||||
bool needsApproval
|
bool needsApproval,
|
||||||
|
TransferType transferType
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
|
|
||||||
|
_transfer(
|
||||||
|
address(tokenIn),
|
||||||
|
msg.sender,
|
||||||
|
// Receiver can never be the pool, since the pool expects funds in the router contract
|
||||||
|
// Thus, this call will only ever be used to transfer funds from the user into the router.
|
||||||
|
address(this),
|
||||||
|
givenAmount,
|
||||||
|
transferType
|
||||||
|
);
|
||||||
|
|
||||||
if (needsApproval) {
|
if (needsApproval) {
|
||||||
// slither-disable-next-line unused-return
|
// slither-disable-next-line unused-return
|
||||||
tokenIn.approve(VAULT, type(uint256).max);
|
tokenIn.approve(VAULT, type(uint256).max);
|
||||||
@@ -67,10 +81,11 @@ contract BalancerV2Executor is IExecutor {
|
|||||||
IERC20 tokenOut,
|
IERC20 tokenOut,
|
||||||
bytes32 poolId,
|
bytes32 poolId,
|
||||||
address receiver,
|
address receiver,
|
||||||
bool needsApproval
|
bool needsApproval,
|
||||||
|
TransferType transferType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (data.length != 93) {
|
if (data.length != 94) {
|
||||||
revert BalancerV2Executor__InvalidDataLength();
|
revert BalancerV2Executor__InvalidDataLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,5 +94,6 @@ contract BalancerV2Executor is IExecutor {
|
|||||||
poolId = bytes32(data[40:72]);
|
poolId = bytes32(data[40:72]);
|
||||||
receiver = address(bytes20(data[72:92]));
|
receiver = address(bytes20(data[72:92]));
|
||||||
needsApproval = uint8(data[92]) > 0;
|
needsApproval = uint8(data[92]) > 0;
|
||||||
|
transferType = TransferType(uint8(data[93]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,15 @@ contract CurveExecutor is IExecutor, TokenTransfer {
|
|||||||
TransferType transferType
|
TransferType transferType
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
|
|
||||||
_transfer(tokenIn, msg.sender, pool, amountIn, transferType);
|
_transfer(
|
||||||
|
tokenIn,
|
||||||
|
msg.sender,
|
||||||
|
// Receiver can never be the pool, since the pool expects funds in the router contract
|
||||||
|
// Thus, this call will only ever be used to transfer funds from the user into the router.
|
||||||
|
address(this),
|
||||||
|
amountIn,
|
||||||
|
transferType
|
||||||
|
);
|
||||||
|
|
||||||
if (tokenApprovalNeeded && tokenIn != nativeToken) {
|
if (tokenApprovalNeeded && tokenIn != nativeToken) {
|
||||||
// slither-disable-next-line unused-return
|
// slither-disable-next-line unused-return
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback, TokenTransfer {
|
|||||||
_transfer(
|
_transfer(
|
||||||
tokenIn,
|
tokenIn,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
address(this), // irrelevant attribute
|
// Receiver can never be the pool, since the pool expects funds in the router contract
|
||||||
|
// Thus, this call will only ever be used to transfer funds from the user into the router.
|
||||||
|
address(this),
|
||||||
amountIn,
|
amountIn,
|
||||||
transferType
|
transferType
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
|
|||||||
pancakev3Executor = new UniswapV3Executor(
|
pancakev3Executor = new UniswapV3Executor(
|
||||||
factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS
|
factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS
|
||||||
);
|
);
|
||||||
balancerv2Executor = new BalancerV2Executor();
|
balancerv2Executor = new BalancerV2Executor(PERMIT2_ADDRESS);
|
||||||
ekuboExecutor = new EkuboExecutor(ekuboCore);
|
ekuboExecutor = new EkuboExecutor(ekuboCore);
|
||||||
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
|
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {Test} from "../../lib/forge-std/src/Test.sol";
|
|||||||
import {Constants} from "../Constants.sol";
|
import {Constants} from "../Constants.sol";
|
||||||
|
|
||||||
contract BalancerV2ExecutorExposed is BalancerV2Executor {
|
contract BalancerV2ExecutorExposed is BalancerV2Executor {
|
||||||
|
constructor(address _permit2) BalancerV2Executor(_permit2) {}
|
||||||
|
|
||||||
function decodeParams(bytes calldata data)
|
function decodeParams(bytes calldata data)
|
||||||
external
|
external
|
||||||
pure
|
pure
|
||||||
@@ -14,18 +16,15 @@ contract BalancerV2ExecutorExposed is BalancerV2Executor {
|
|||||||
IERC20 tokenOut,
|
IERC20 tokenOut,
|
||||||
bytes32 poolId,
|
bytes32 poolId,
|
||||||
address receiver,
|
address receiver,
|
||||||
bool needsApproval
|
bool needsApproval,
|
||||||
|
TransferType transferType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return _decodeData(data);
|
return _decodeData(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract BalancerV2ExecutorTest is
|
contract BalancerV2ExecutorTest is Test, Constants {
|
||||||
BalancerV2ExecutorExposed,
|
|
||||||
Test,
|
|
||||||
Constants
|
|
||||||
{
|
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
BalancerV2ExecutorExposed balancerV2Exposed;
|
BalancerV2ExecutorExposed balancerV2Exposed;
|
||||||
@@ -37,12 +36,17 @@ contract BalancerV2ExecutorTest is
|
|||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 17323404;
|
uint256 forkBlock = 17323404;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
balancerV2Exposed = new BalancerV2ExecutorExposed();
|
balancerV2Exposed = new BalancerV2ExecutorExposed(PERMIT2_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeParams() public view {
|
function testDecodeParams() public view {
|
||||||
bytes memory params = abi.encodePacked(
|
bytes memory params = abi.encodePacked(
|
||||||
WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, address(2), true
|
WETH_ADDR,
|
||||||
|
BAL_ADDR,
|
||||||
|
WETH_BAL_POOL_ID,
|
||||||
|
address(2),
|
||||||
|
true,
|
||||||
|
TokenTransfer.TransferType.NONE
|
||||||
);
|
);
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -50,7 +54,8 @@ contract BalancerV2ExecutorTest is
|
|||||||
IERC20 tokenOut,
|
IERC20 tokenOut,
|
||||||
bytes32 poolId,
|
bytes32 poolId,
|
||||||
address receiver,
|
address receiver,
|
||||||
bool needsApproval
|
bool needsApproval,
|
||||||
|
TokenTransfer.TransferType transferType
|
||||||
) = balancerV2Exposed.decodeParams(params);
|
) = balancerV2Exposed.decodeParams(params);
|
||||||
|
|
||||||
assertEq(address(tokenIn), WETH_ADDR);
|
assertEq(address(tokenIn), WETH_ADDR);
|
||||||
@@ -58,6 +63,7 @@ contract BalancerV2ExecutorTest is
|
|||||||
assertEq(poolId, WETH_BAL_POOL_ID);
|
assertEq(poolId, WETH_BAL_POOL_ID);
|
||||||
assertEq(receiver, address(2));
|
assertEq(receiver, address(2));
|
||||||
assertEq(needsApproval, true);
|
assertEq(needsApproval, true);
|
||||||
|
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeParamsInvalidDataLength() public {
|
function testDecodeParamsInvalidDataLength() public {
|
||||||
@@ -70,8 +76,14 @@ contract BalancerV2ExecutorTest is
|
|||||||
|
|
||||||
function testSwap() public {
|
function testSwap() public {
|
||||||
uint256 amountIn = 10 ** 18;
|
uint256 amountIn = 10 ** 18;
|
||||||
bytes memory protocolData =
|
bytes memory protocolData = abi.encodePacked(
|
||||||
abi.encodePacked(WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, BOB, true);
|
WETH_ADDR,
|
||||||
|
BAL_ADDR,
|
||||||
|
WETH_BAL_POOL_ID,
|
||||||
|
BOB,
|
||||||
|
true,
|
||||||
|
TokenTransfer.TransferType.NONE
|
||||||
|
);
|
||||||
|
|
||||||
deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
|
deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
|
||||||
uint256 balanceBefore = BAL.balanceOf(BOB);
|
uint256 balanceBefore = BAL.balanceOf(BOB);
|
||||||
@@ -86,14 +98,15 @@ contract BalancerV2ExecutorTest is
|
|||||||
function testDecodeIntegration() public view {
|
function testDecodeIntegration() public view {
|
||||||
// Generated by the SwapEncoder - test_encode_balancer_v2
|
// Generated by the SwapEncoder - test_encode_balancer_v2
|
||||||
bytes memory protocolData =
|
bytes memory protocolData =
|
||||||
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01";
|
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105";
|
||||||
|
|
||||||
(
|
(
|
||||||
IERC20 tokenIn,
|
IERC20 tokenIn,
|
||||||
IERC20 tokenOut,
|
IERC20 tokenOut,
|
||||||
bytes32 poolId,
|
bytes32 poolId,
|
||||||
address receiver,
|
address receiver,
|
||||||
bool needsApproval
|
bool needsApproval,
|
||||||
|
TokenTransfer.TransferType transferType
|
||||||
) = balancerV2Exposed.decodeParams(protocolData);
|
) = balancerV2Exposed.decodeParams(protocolData);
|
||||||
|
|
||||||
assertEq(address(tokenIn), WETH_ADDR);
|
assertEq(address(tokenIn), WETH_ADDR);
|
||||||
@@ -101,12 +114,13 @@ contract BalancerV2ExecutorTest is
|
|||||||
assertEq(poolId, WETH_BAL_POOL_ID);
|
assertEq(poolId, WETH_BAL_POOL_ID);
|
||||||
assertEq(receiver, BOB);
|
assertEq(receiver, BOB);
|
||||||
assertEq(needsApproval, true);
|
assertEq(needsApproval, true);
|
||||||
|
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSwapIntegration() public {
|
function testSwapIntegration() public {
|
||||||
// Generated by the SwapEncoder - test_encode_balancer_v2
|
// Generated by the SwapEncoder - test_encode_balancer_v2
|
||||||
bytes memory protocolData =
|
bytes memory protocolData =
|
||||||
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01";
|
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105";
|
||||||
|
|
||||||
uint256 amountIn = 10 ** 18;
|
uint256 amountIn = 10 ** 18;
|
||||||
deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
|
deal(WETH_ADDR, address(balancerV2Exposed), amountIn);
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ impl SwapEncoder for BalancerV2SwapEncoder {
|
|||||||
component_id,
|
component_id,
|
||||||
bytes_to_address(&encoding_context.receiver)?,
|
bytes_to_address(&encoding_context.receiver)?,
|
||||||
approval_needed,
|
approval_needed,
|
||||||
|
(encoding_context.transfer_type as u8).to_be_bytes(),
|
||||||
);
|
);
|
||||||
Ok(args.abi_encode_packed())
|
Ok(args.abi_encode_packed())
|
||||||
}
|
}
|
||||||
@@ -703,7 +704,7 @@ mod tests {
|
|||||||
router_address: Some(Bytes::zero(20)),
|
router_address: Some(Bytes::zero(20)),
|
||||||
group_token_in: token_in.clone(),
|
group_token_in: token_in.clone(),
|
||||||
group_token_out: token_out.clone(),
|
group_token_out: token_out.clone(),
|
||||||
transfer_type: TransferType::Transfer,
|
transfer_type: TransferType::None,
|
||||||
};
|
};
|
||||||
let encoder = BalancerV2SwapEncoder::new(
|
let encoder = BalancerV2SwapEncoder::new(
|
||||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||||
@@ -731,7 +732,9 @@ mod tests {
|
|||||||
// receiver
|
// receiver
|
||||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||||
// approval needed
|
// approval needed
|
||||||
"01"
|
"01",
|
||||||
|
// transfer type
|
||||||
|
"05"
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user