feat: Refactor callback to use transient storage
With this, we don't need the univ3 specific method in the router contract. This should be flexible enough for most protocols that integrate TODO: is this safe enough?? --- don't change below this line --- ENG-4411 Took 1 hour 52 minutes Took 4 minutes Took 5 minutes
This commit is contained in:
@@ -52,7 +52,7 @@ contract Dispatcher {
|
||||
* @dev Calls an executor, assumes swap.protocolData contains
|
||||
* protocol-specific data required by the executor.
|
||||
*/
|
||||
// slither-disable-next-line delegatecall-loop
|
||||
// slither-disable-next-line delegatecall-loop,assembly
|
||||
function _callExecutor(
|
||||
address executor,
|
||||
uint256 amount,
|
||||
@@ -62,6 +62,10 @@ contract Dispatcher {
|
||||
revert Dispatcher__UnapprovedExecutor();
|
||||
}
|
||||
|
||||
assembly {
|
||||
tstore(0, executor)
|
||||
}
|
||||
|
||||
// slither-disable-next-line controlled-delegatecall,low-level-calls
|
||||
(bool success, bytes memory result) = executor.delegatecall(
|
||||
abi.encodeWithSelector(IExecutor.swap.selector, amount, data)
|
||||
@@ -80,8 +84,12 @@ contract Dispatcher {
|
||||
calculatedAmount = abi.decode(result, (uint256));
|
||||
}
|
||||
|
||||
// slither-disable-next-line assembly
|
||||
function _handleCallback(bytes calldata data) internal {
|
||||
address executor = address(uint160(bytes20(data[data.length - 20:])));
|
||||
address executor;
|
||||
assembly {
|
||||
executor := tload(0)
|
||||
}
|
||||
|
||||
if (!executors[executor]) {
|
||||
revert Dispatcher__UnapprovedExecutor();
|
||||
|
||||
@@ -502,26 +502,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
||||
require(msg.sender.code.length != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Called by UniswapV3 pool when swapping on it.
|
||||
* See in IUniswapV3SwapCallback for documentation.
|
||||
*/
|
||||
function uniswapV3SwapCallback(
|
||||
int256, /* amount0Delta */
|
||||
int256, /* amount1Delta */
|
||||
bytes calldata data
|
||||
) external {
|
||||
if (data.length < 24) revert TychoRouter__InvalidDataLength();
|
||||
// We are taking advantage of the fact that the data we need is already encoded in the correct format inside msg.data
|
||||
// This way we preserve the bytes calldata (and don't need to convert it to bytes memory)
|
||||
uint256 dataOffset = 4 + 32 + 32 + 32; // Skip selector + 2 ints + data_offset
|
||||
uint256 dataLength =
|
||||
uint256(bytes32(msg.data[dataOffset:dataOffset + 32]));
|
||||
|
||||
bytes calldata fullData = msg.data[4:dataOffset + 32 + dataLength];
|
||||
_handleCallback(fullData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Called by PancakeV3 pool when swapping on it.
|
||||
*/
|
||||
|
||||
@@ -80,6 +80,7 @@ contract UniswapV3Executor is IExecutor, ICallback {
|
||||
returns (bytes memory result)
|
||||
{
|
||||
// The data has the following layout:
|
||||
// - selector (4 bytes)
|
||||
// - amount0Delta (32 bytes)
|
||||
// - amount1Delta (32 bytes)
|
||||
// - dataOffset (32 bytes)
|
||||
@@ -87,11 +88,11 @@ contract UniswapV3Executor is IExecutor, ICallback {
|
||||
// - protocolData (variable length)
|
||||
|
||||
(int256 amount0Delta, int256 amount1Delta) =
|
||||
abi.decode(msgData[:64], (int256, int256));
|
||||
abi.decode(msgData[4:68], (int256, int256));
|
||||
|
||||
address tokenIn = address(bytes20(msgData[128:148]));
|
||||
address tokenIn = address(bytes20(msgData[132:152]));
|
||||
|
||||
verifyCallback(msgData[128:]);
|
||||
verifyCallback(msgData[132:]);
|
||||
|
||||
uint256 amountOwed =
|
||||
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
|
||||
@@ -147,10 +148,10 @@ contract UniswapV3Executor is IExecutor, ICallback {
|
||||
|
||||
function _makeV3CallbackData(address tokenIn, address tokenOut, uint24 fee)
|
||||
internal
|
||||
view
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodePacked(tokenIn, tokenOut, fee, self);
|
||||
return abi.encodePacked(tokenIn, tokenOut, fee);
|
||||
}
|
||||
|
||||
function _verifyPairAddress(
|
||||
|
||||
@@ -41,7 +41,6 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool zeroForOne,
|
||||
address callbackExecutor,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||
) = _decodeData(data);
|
||||
|
||||
@@ -107,14 +106,13 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
params[2] = abi.encode(Currency.wrap(tokenOut), uint256(0));
|
||||
swapData = abi.encode(actions, params);
|
||||
}
|
||||
bytes memory fullData = abi.encodePacked(swapData, callbackExecutor);
|
||||
uint256 tokenOutBalanceBefore;
|
||||
|
||||
tokenOutBalanceBefore = tokenOut == address(0)
|
||||
? address(this).balance
|
||||
: IERC20(tokenOut).balanceOf(address(this));
|
||||
|
||||
executeActions(fullData);
|
||||
executeActions(swapData);
|
||||
|
||||
uint256 tokenOutBalanceAfter;
|
||||
|
||||
@@ -140,22 +138,20 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback {
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool zeroForOne,
|
||||
address callbackExecutor,
|
||||
UniswapV4Pool[] memory pools
|
||||
)
|
||||
{
|
||||
if (data.length < 87) {
|
||||
if (data.length < 67) {
|
||||
revert UniswapV4Executor__InvalidDataLength();
|
||||
}
|
||||
|
||||
tokenIn = address(bytes20(data[0:20]));
|
||||
tokenOut = address(bytes20(data[20:40]));
|
||||
zeroForOne = (data[40] != 0);
|
||||
callbackExecutor = address(bytes20(data[41:61]));
|
||||
|
||||
uint256 poolsLength = (data.length - 61) / 26; // 26 bytes per pool object
|
||||
uint256 poolsLength = (data.length - 41) / 26; // 26 bytes per pool object
|
||||
pools = new UniswapV4Pool[](poolsLength);
|
||||
bytes memory poolsData = data[61:];
|
||||
bytes memory poolsData = data[41:];
|
||||
uint256 offset = 0;
|
||||
for (uint256 i = 0; i < poolsLength; i++) {
|
||||
address intermediaryToken;
|
||||
|
||||
Reference in New Issue
Block a user