feat: (WIP) Support selection of transfer into router
- For protocols like Balancer and Curve, which expect funds to be in the router at the time of swap, we must support also transferring funds from the user into the router. Doing this in the router would mean we are dealing with transfers in two different places: in the router main methods and in the executors. To avoid this, we are now performing transfers just in the executors, and two transfer types have been added to support transfers into the router. TODO: - Add this for Balancer and Curve (only added for USV4 atm). - TODO consider renaming TRANSFER_FROM and TRANSFER_PERMIT2 to include "pool" in the name
This commit is contained in:
committed by
Diana Carvalho
parent
59a80dc392
commit
a301a1cef3
@@ -16,9 +16,17 @@ contract TokenTransfer {
|
||||
// Assume funds are in the TychoRouter - transfer into the pool
|
||||
TRANSFER,
|
||||
// Assume funds are in msg.sender's wallet - transferFrom into the pool
|
||||
TRANSFERFROM,
|
||||
TRANSFER_FROM,
|
||||
// Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool
|
||||
TRANSFERPERMIT2,
|
||||
TRANSFER_PERMIT2,
|
||||
// Assume funds are in msg.sender's wallet - but the pool requires it to be
|
||||
// in the router contract when calling swap - transferFrom into the router
|
||||
// contract
|
||||
TRANSFER_TO_ROUTER,
|
||||
// Assume funds are in msg.sender's wallet - but the pool requires it to be
|
||||
// in the router contract when calling swap - transferFrom into the router
|
||||
// contract using permit2
|
||||
TRANSFER_PERMIT2_TO_ROUTER,
|
||||
// Assume funds have already been transferred into the pool. Do nothing.
|
||||
NONE
|
||||
}
|
||||
@@ -31,21 +39,31 @@ contract TokenTransfer {
|
||||
}
|
||||
|
||||
function _transfer(
|
||||
IERC20 tokenIn,
|
||||
address tokenIn,
|
||||
address sender,
|
||||
address receiver,
|
||||
uint256 amount,
|
||||
TransferType transferType
|
||||
) internal {
|
||||
if (transferType == TransferType.TRANSFER) {
|
||||
tokenIn.safeTransfer(receiver, amount);
|
||||
} else if (transferType == TransferType.TRANSFERFROM) {
|
||||
if (tokenIn == address(0)) {
|
||||
payable(receiver).transfer(amount);
|
||||
} else {
|
||||
IERC20(tokenIn).safeTransfer(receiver, amount);
|
||||
}
|
||||
} else if (transferType == TransferType.TRANSFER_FROM) {
|
||||
// slither-disable-next-line arbitrary-send-erc20
|
||||
tokenIn.safeTransferFrom(sender, receiver, amount);
|
||||
} else if (transferType == TransferType.TRANSFERPERMIT2) {
|
||||
IERC20(tokenIn).safeTransferFrom(sender, receiver, amount);
|
||||
} else if (transferType == TransferType.TRANSFER_PERMIT2) {
|
||||
// Permit2.permit is already called from the TychoRouter
|
||||
permit2.transferFrom(sender, receiver, uint160(amount), tokenIn);
|
||||
} else if (transferType == TransferType.TRANSFER_TO_ROUTER) {
|
||||
// slither-disable-next-line arbitrary-send-erc20
|
||||
IERC20(tokenIn).safeTransferFrom(sender, address(this), amount);
|
||||
} else if (transferType == TransferType.TRANSFER_PERMIT2_TO_ROUTER) {
|
||||
// Permit2.permit is already called from the TychoRouter
|
||||
permit2.transferFrom(
|
||||
sender, receiver, uint160(amount), address(tokenIn)
|
||||
sender, address(this), uint160(amount), tokenIn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
|
||||
_verifyPairAddress(target);
|
||||
|
||||
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
|
||||
_transfer(tokenIn, msg.sender, target, givenAmount, transferType);
|
||||
_transfer(
|
||||
address(tokenIn), msg.sender, target, givenAmount, transferType
|
||||
);
|
||||
|
||||
IUniswapV2Pair pool = IUniswapV2Pair(target);
|
||||
if (zeroForOne) {
|
||||
|
||||
@@ -111,7 +111,7 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
|
||||
uint256 amountOwed =
|
||||
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
|
||||
|
||||
_transfer(IERC20(tokenIn), sender, msg.sender, amountOwed, transferType);
|
||||
_transfer(tokenIn, sender, msg.sender, amountOwed, transferType);
|
||||
|
||||
return abi.encode(amountOwed, tokenIn);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@ import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
||||
import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol";
|
||||
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
||||
import {ICallback} from "@interfaces/ICallback.sol";
|
||||
import {TokenTransfer} from "./TokenTransfer.sol";
|
||||
|
||||
error UniswapV4Executor__InvalidDataLength();
|
||||
|
||||
contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
contract UniswapV4Executor is IExecutor, V4Router, ICallback, TokenTransfer {
|
||||
using SafeERC20 for IERC20;
|
||||
using CurrencyLibrary for Currency;
|
||||
|
||||
@@ -30,7 +31,10 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
int24 tickSpacing;
|
||||
}
|
||||
|
||||
constructor(IPoolManager _poolManager) V4Router(_poolManager) {}
|
||||
constructor(IPoolManager _poolManager, address _permit2)
|
||||
V4Router(_poolManager)
|
||||
TokenTransfer(_permit2)
|
||||
{}
|
||||
|
||||
function swap(uint256 amountIn, bytes calldata data)
|
||||
external
|
||||
@@ -41,9 +45,19 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool zeroForOne,
|
||||
TransferType transferType,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||
) = _decodeData(data);
|
||||
|
||||
// TODO move this into callback when we implement callback transfer type support
|
||||
_transfer(
|
||||
tokenIn,
|
||||
msg.sender,
|
||||
address(this), // irrelevant attribute
|
||||
amountIn,
|
||||
transferType
|
||||
);
|
||||
|
||||
bytes memory swapData;
|
||||
if (pools.length == 1) {
|
||||
PoolKey memory key = PoolKey({
|
||||
@@ -138,6 +152,7 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool zeroForOne,
|
||||
TransferType transferType,
|
||||
UniswapV4Pool[] memory pools
|
||||
)
|
||||
{
|
||||
@@ -148,10 +163,11 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
tokenIn = address(bytes20(data[0:20]));
|
||||
tokenOut = address(bytes20(data[20:40]));
|
||||
zeroForOne = (data[40] != 0);
|
||||
transferType = TransferType(uint8(data[41]));
|
||||
|
||||
uint256 poolsLength = (data.length - 41) / 26; // 26 bytes per pool object
|
||||
uint256 poolsLength = (data.length - 42) / 26; // 26 bytes per pool object
|
||||
pools = new UniswapV4Pool[](poolsLength);
|
||||
bytes memory poolsData = data[41:];
|
||||
bytes memory poolsData = data[42:];
|
||||
uint256 offset = 0;
|
||||
for (uint256 i = 0; i < poolsLength; i++) {
|
||||
address intermediaryToken;
|
||||
|
||||
Reference in New Issue
Block a user