Merge branch 'main' into router/dc/ENG-4454-remove-executor-script

This commit is contained in:
dianacarvalho1
2025-05-15 16:18:33 +01:00
committed by GitHub
10 changed files with 106 additions and 76 deletions

View File

@@ -127,7 +127,9 @@ contract Dispatcher {
tstore(_CURRENTLY_SWAPPING_EXECUTOR_SLOT, 0)
}
// this is necessary because the delegatecall will prepend extra bytes we don't want like the length and prefix
// The final callback result should not be ABI encoded. That is why we are decoding here.
// ABI encoding is very gas expensive and we want to avoid it if possible.
// The result from `handleCallback` is always ABI encoded.
bytes memory decodedResult = abi.decode(result, (bytes));
return decodedResult;
}

View File

@@ -436,15 +436,14 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
Address.sendValue(payable(receiver), amountOut);
}
if (tokenIn != tokenOut) {
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(
userAmount, amountOut
);
}
}
_verifyAmountOutWasReceived(
tokenIn,
tokenOut,
initialBalanceTokenOut,
amountOut,
receiver,
amountIn
);
}
/**
@@ -493,15 +492,14 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
Address.sendValue(payable(receiver), amountOut);
}
if (tokenIn != tokenOut) {
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(
userAmount, amountOut
);
}
}
_verifyAmountOutWasReceived(
tokenIn,
tokenOut,
initialBalanceTokenOut,
amountOut,
receiver,
amountIn
);
}
/**
@@ -546,16 +544,14 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
_unwrapETH(amountOut);
Address.sendValue(payable(receiver), amountOut);
}
if (tokenIn != tokenOut) {
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(
userAmount, amountOut
);
}
}
_verifyAmountOutWasReceived(
tokenIn,
tokenOut,
initialBalanceTokenOut,
amountOut,
receiver,
amountIn
);
}
/**
@@ -657,13 +653,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
/**
* @dev We use the fallback function to allow flexibility on callback.
*/
fallback() external {
bytes memory result = _callHandleCallbackOnExecutor(msg.data);
// slither-disable-next-line assembly
assembly ("memory-safe") {
// Propagate the result
return(add(result, 32), mload(result))
}
fallback(bytes calldata data) external returns (bytes memory) {
return _callHandleCallbackOnExecutor(data);
}
/**
@@ -778,18 +769,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
require(msg.sender.code.length != 0);
}
/**
* @dev Called by UniswapV4 pool manager after achieving unlock state.
*/
function unlockCallback(bytes calldata data)
external
returns (bytes memory)
{
if (data.length < 24) revert TychoRouter__InvalidDataLength();
bytes memory result = _callHandleCallbackOnExecutor(data);
return result;
}
/**
* @dev Gets balance of a token for a given address. Supports both native ETH and ERC20 tokens.
*/
@@ -801,4 +780,27 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
return
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
}
/**
* @dev Verifies that the expected amount of output tokens was received by the receiver.
* It also handles the case of arbitrage swaps where the input and output tokens are the same.
*/
function _verifyAmountOutWasReceived(
address tokenIn,
address tokenOut,
uint256 initialBalanceTokenOut,
uint256 amountOut,
address receiver,
uint256 amountIn
) internal view {
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
if (tokenIn == tokenOut) {
// If it is an arbitrage, we need to remove the amountIn from the initial balance to get a correct userAmount
initialBalanceTokenOut -= amountIn;
}
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
}
}
}

View File

@@ -149,6 +149,11 @@ contract CurveExecutor is IExecutor, TokenTransfer {
receiver = address(bytes20(data[65:85]));
}
/**
* @dev Even though this contract is mostly called through delegatecall, we still want to be able to receive ETH.
* This is needed when using the executor directly and it makes testing easier.
* There are some curve pools that take ETH directly.
*/
receive() external payable {
require(msg.sender.code.length != 0);
}

View File

@@ -13,14 +13,12 @@ contract MaverickV2Executor is IExecutor, TokenTransfer {
using SafeERC20 for IERC20;
address public immutable factory;
address private immutable self;
constructor(address _factory, address _permit2) TokenTransfer(_permit2) {
if (_factory == address(0)) {
revert MaverickV2Executor__InvalidFactory();
}
factory = _factory;
self = address(this);
}
// slither-disable-next-line locked-ether

View File

@@ -6,6 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
error TokenTransfer__AddressZero();
error TokenTransfer__InvalidTransferType();
contract TokenTransfer {
using SafeERC20 for IERC20;
@@ -45,7 +46,9 @@ contract TokenTransfer {
uint256 amount,
TransferType transferType
) internal {
if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
if (transferType == TransferType.NONE) {
return;
} else if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
if (tokenIn == address(0)) {
payable(receiver).transfer(amount);
} else {
@@ -65,6 +68,8 @@ contract TokenTransfer {
permit2.transferFrom(
sender, address(this), uint160(amount), tokenIn
);
} else {
revert TokenTransfer__InvalidTransferType();
}
}
}

View File

@@ -184,8 +184,11 @@ contract UniswapV4Executor is
external
returns (bytes memory)
{
verifyCallback(data);
return _unlockCallback(data);
bytes calldata stripped = data[68:];
verifyCallback(stripped);
// Our general callback logic returns a not ABI encoded result.
// However, the pool manager expects the result to be ABI encoded. That is why we need to encode it here again.
return abi.encode(_unlockCallback(stripped));
}
function verifyCallback(bytes calldata) public view poolManagerOnly {}