feat: Support new transfer logic in all executors

TODO:
- Fix failing tests
- Remove permit2 from initialization of contracts
This commit is contained in:
TAMARA LIPOWSKI
2025-05-14 20:42:19 -04:00
parent 0f9af65846
commit 27dfde3118
22 changed files with 378 additions and 462 deletions

View File

@@ -3,7 +3,6 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol";
import {ICallback} from "@interfaces/ICallback.sol";
import {TokenTransfer} from "./TokenTransfer.sol";
import {
IERC20,
SafeERC20
@@ -23,6 +22,7 @@ import {IUnlockCallback} from
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {TransientStateLibrary} from
"@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import "../OneTransferFromOnly.sol";
error UniswapV4Executor__InvalidDataLength();
error UniswapV4Executor__NotPoolManager();
@@ -37,7 +37,7 @@ contract UniswapV4Executor is
IExecutor,
IUnlockCallback,
ICallback,
TokenTransfer
OneTransferFromOnly
{
using SafeERC20 for IERC20;
using CurrencyLibrary for Currency;
@@ -57,7 +57,7 @@ contract UniswapV4Executor is
}
constructor(IPoolManager _poolManager, address _permit2)
TokenTransfer(_permit2)
OneTransferFromOnly(_permit2)
{
poolManager = _poolManager;
_self = address(this);
@@ -82,7 +82,8 @@ contract UniswapV4Executor is
address tokenIn,
address tokenOut,
bool zeroForOne,
TransferType transferType,
bool transferFromNeeded,
bool transferNeeded,
address receiver,
UniswapV4Executor.UniswapV4Pool[] memory pools
) = _decodeData(data);
@@ -100,8 +101,8 @@ contract UniswapV4Executor is
key,
zeroForOne,
amountIn,
msg.sender,
transferType,
transferFromNeeded,
transferNeeded,
receiver,
bytes("")
);
@@ -123,8 +124,8 @@ contract UniswapV4Executor is
currencyIn,
path,
amountIn,
msg.sender,
transferType,
transferFromNeeded,
transferNeeded,
receiver
);
}
@@ -142,24 +143,26 @@ contract UniswapV4Executor is
address tokenIn,
address tokenOut,
bool zeroForOne,
TransferType transferType,
bool transferFromNeeded,
bool transferNeeded,
address receiver,
UniswapV4Pool[] memory pools
)
{
if (data.length < 88) {
if (data.length < 89) {
revert UniswapV4Executor__InvalidDataLength();
}
tokenIn = address(bytes20(data[0:20]));
tokenOut = address(bytes20(data[20:40]));
zeroForOne = (data[40] != 0);
transferType = TransferType(uint8(data[41]));
receiver = address(bytes20(data[42:62]));
transferFromNeeded = (data[41] != 0);
transferNeeded = (data[42] != 0);
receiver = address(bytes20(data[43:63]));
uint256 poolsLength = (data.length - 62) / 26; // 26 bytes per pool object
uint256 poolsLength = (data.length - 63) / 26; // 26 bytes per pool object
pools = new UniswapV4Pool[](poolsLength);
bytes memory poolsData = data[62:];
bytes memory poolsData = data[63:];
uint256 offset = 0;
for (uint256 i = 0; i < poolsLength; i++) {
address intermediaryToken;
@@ -239,8 +242,8 @@ contract UniswapV4Executor is
* @param poolKey The key of the pool to swap in.
* @param zeroForOne Whether the swap is from token0 to token1 (true) or vice versa (false).
* @param amountIn The amount of tokens to swap in.
* @param sender The address of the sender.
* @param transferType The type of transfer in to use.
* @param transferFromNeeded Whether to transferFrom input tokens into the core contract from the swapper's wallet .
* @param transferNeeded Whether to transfer input tokens into the core contract from the router contract
* @param receiver The address of the receiver.
* @param hookData Additional data for hook contracts.
*/
@@ -248,8 +251,8 @@ contract UniswapV4Executor is
PoolKey memory poolKey,
bool zeroForOne,
uint128 amountIn,
address sender,
TransferType transferType,
bool transferFromNeeded,
bool transferNeeded,
address receiver,
bytes calldata hookData
) external returns (uint128) {
@@ -262,7 +265,7 @@ contract UniswapV4Executor is
if (amount > amountIn) {
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
}
_settle(currencyIn, amount, sender, transferType);
_settle(currencyIn, amount, transferFromNeeded, transferNeeded);
Currency currencyOut =
zeroForOne ? poolKey.currency1 : poolKey.currency0;
@@ -275,16 +278,16 @@ contract UniswapV4Executor is
* @param currencyIn The currency of the input token.
* @param path The path to swap along.
* @param amountIn The amount of tokens to swap in.
* @param sender The address of the sender.
* @param transferType The type of transfer in to use.
* @param transferFromNeeded Whether to transferFrom input tokens into the core contract from the swapper's wallet .
* @param transferNeeded Whether to transfer input tokens into the core contract from the router contract
* @param receiver The address of the receiver.
*/
function swapExactInput(
Currency currencyIn,
PathKey[] calldata path,
uint128 amountIn,
address sender,
TransferType transferType,
bool transferFromNeeded,
bool transferNeeded,
address receiver
) external returns (uint128) {
uint128 amountOut = 0;
@@ -315,7 +318,7 @@ contract UniswapV4Executor is
if (amount > amountIn) {
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
}
_settle(currencyIn, amount, sender, transferType);
_settle(currencyIn, amount, transferFromNeeded, transferNeeded);
_take(
swapCurrencyIn, // at the end of the loop this is actually currency out
@@ -387,15 +390,17 @@ contract UniswapV4Executor is
* @dev The implementing contract must ensure that the `payer` is a secure address.
* @param currency The currency to settle.
* @param amount The amount to send.
* @param sender The address of the payer.
* @param transferType The type of transfer to use.
* @param transferFromNeeded Whether to manually transferFrom input tokens into the
* core contract from the swapper.
* @param transferNeeded Whether to manually transfer input tokens into the
* core contract from the router.
* @dev Returns early if the amount is 0.
*/
function _settle(
Currency currency,
uint256 amount,
address sender,
TransferType transferType
bool transferFromNeeded,
bool transferNeeded
) internal {
if (amount == 0) return;
poolManager.sync(currency);
@@ -403,13 +408,18 @@ contract UniswapV4Executor is
// slither-disable-next-line unused-return
poolManager.settle{value: amount}();
} else {
_transfer(
Currency.unwrap(currency),
sender,
address(poolManager),
amount,
transferType
);
if (transferFromNeeded) {
// transferFrom swapper's wallet into the core contract
_transfer(msg.sender);
} else if (transferNeeded) {
address tokenIn = Currency.unwrap(currency);
// transfer from router contract into the core contract
if (tokenIn == address(0)) {
payable(msg.sender).transfer(amount);
} else {
IERC20(tokenIn).safeTransfer(msg.sender, amount);
}
}
// slither-disable-next-line unused-return
poolManager.settle();
}