Merge branch 'main' into router/hr/ENG-4237-refactor-usv3-callback
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "../../src/executors/UniswapV4Executor.sol";
|
||||
import "./UniswapV4Utils.sol";
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
import {Constants} from "../Constants.sol";
|
||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||
import {console} from "forge-std/console.sol";
|
||||
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||
|
||||
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||
constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {}
|
||||
@@ -16,8 +17,11 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||
returns (
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool isExactInput,
|
||||
uint256 amount
|
||||
uint256 amountOutMin,
|
||||
bool zeroForOne,
|
||||
address callbackExecutor,
|
||||
bytes4 callbackSelector,
|
||||
UniswapV4Pool[] memory pools
|
||||
)
|
||||
{
|
||||
return _decodeData(data);
|
||||
@@ -40,31 +44,84 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
||||
}
|
||||
|
||||
function testDecodeParams() public view {
|
||||
uint24 expectedPoolFee = 500;
|
||||
uint128 expectedAmount = 100;
|
||||
uint256 minAmountOut = 100;
|
||||
bool zeroForOne = true;
|
||||
uint24 pool1Fee = 500;
|
||||
int24 tickSpacing1 = 60;
|
||||
uint24 pool2Fee = 1000;
|
||||
int24 tickSpacing2 = -10;
|
||||
|
||||
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
||||
USDE_ADDR, USDT_ADDR, expectedPoolFee, false, 1, expectedAmount
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: pool1Fee,
|
||||
tickSpacing: tickSpacing1
|
||||
});
|
||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDE_ADDR,
|
||||
fee: pool2Fee,
|
||||
tickSpacing: tickSpacing2
|
||||
});
|
||||
|
||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
minAmountOut,
|
||||
zeroForOne,
|
||||
address(uniswapV4Exposed),
|
||||
SafeCallback.unlockCallback.selector,
|
||||
pools
|
||||
);
|
||||
|
||||
(address tokenIn, address tokenOut, bool isExactInput, uint256 amount) =
|
||||
uniswapV4Exposed.decodeData(data);
|
||||
(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountOutMin,
|
||||
bool zeroForOneDecoded,
|
||||
address callbackExecutor,
|
||||
bytes4 callbackSelector,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
||||
) = uniswapV4Exposed.decodeData(data);
|
||||
|
||||
assertEq(tokenIn, USDE_ADDR);
|
||||
assertEq(tokenOut, USDT_ADDR);
|
||||
assertTrue(isExactInput);
|
||||
assertEq(amount, expectedAmount);
|
||||
assertEq(amountOutMin, minAmountOut);
|
||||
assertEq(zeroForOneDecoded, zeroForOne);
|
||||
assertEq(callbackExecutor, address(uniswapV4Exposed));
|
||||
assertEq(callbackSelector, SafeCallback.unlockCallback.selector);
|
||||
assertEq(decodedPools.length, 2);
|
||||
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
|
||||
assertEq(decodedPools[0].fee, pool1Fee);
|
||||
assertEq(decodedPools[0].tickSpacing, tickSpacing1);
|
||||
assertEq(decodedPools[1].intermediaryToken, USDE_ADDR);
|
||||
assertEq(decodedPools[1].fee, pool2Fee);
|
||||
assertEq(decodedPools[1].tickSpacing, tickSpacing2);
|
||||
}
|
||||
|
||||
function testSwap() public {
|
||||
function testSingleSwap() public {
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
||||
uint256 usdeBalanceBeforeSwapExecutor =
|
||||
USDE.balanceOf(address(uniswapV4Exposed));
|
||||
|
||||
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
||||
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
|
||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
USDT_ADDR,
|
||||
uint256(1),
|
||||
true,
|
||||
address(uniswapV4Exposed),
|
||||
SafeCallback.unlockCallback.selector,
|
||||
pools
|
||||
);
|
||||
|
||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||
@@ -75,4 +132,46 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
||||
);
|
||||
assertTrue(USDT.balanceOf(address(uniswapV4Exposed)) == amountOut);
|
||||
}
|
||||
|
||||
function testMultipleSwap() public {
|
||||
// USDE -> USDT -> WBTC
|
||||
uint256 amountIn = 100 ether;
|
||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
||||
uint256 usdeBalanceBeforeSwapExecutor =
|
||||
USDE.balanceOf(address(uniswapV4Exposed));
|
||||
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: USDT_ADDR,
|
||||
fee: uint24(100),
|
||||
tickSpacing: int24(1)
|
||||
});
|
||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||
intermediaryToken: WBTC_ADDR,
|
||||
fee: uint24(3000),
|
||||
tickSpacing: int24(60)
|
||||
});
|
||||
|
||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||
USDE_ADDR,
|
||||
WBTC_ADDR,
|
||||
uint256(1),
|
||||
true,
|
||||
address(uniswapV4Exposed),
|
||||
SafeCallback.unlockCallback.selector,
|
||||
pools
|
||||
);
|
||||
|
||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
||||
assertEq(
|
||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||
usdeBalanceBeforeSwapExecutor - amountIn
|
||||
);
|
||||
assertTrue(
|
||||
IERC20(WBTC_ADDR).balanceOf(address(uniswapV4Exposed)) == amountOut
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,43 +4,34 @@ pragma solidity ^0.8.26;
|
||||
import "@src/executors/UniswapV4Executor.sol";
|
||||
|
||||
library UniswapV4Utils {
|
||||
function encodeExactInputSingle(
|
||||
function encodeExactInput(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint24 fee,
|
||||
uint256 amountOutMin,
|
||||
bool zeroForOne,
|
||||
uint24 tickSpacing,
|
||||
uint128 amountIn
|
||||
address callbackExecutor,
|
||||
bytes4 callbackSelector,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||
) public pure returns (bytes memory) {
|
||||
PoolKey memory key = PoolKey({
|
||||
currency0: Currency.wrap(zeroForOne ? tokenIn : tokenOut),
|
||||
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
||||
fee: fee,
|
||||
tickSpacing: int24(tickSpacing),
|
||||
hooks: IHooks(address(0))
|
||||
});
|
||||
bytes memory encodedPools;
|
||||
|
||||
bytes memory actions = abi.encodePacked(
|
||||
uint8(Actions.SWAP_EXACT_IN_SINGLE),
|
||||
uint8(Actions.SETTLE_ALL),
|
||||
uint8(Actions.TAKE_ALL)
|
||||
for (uint256 i = 0; i < pools.length; i++) {
|
||||
encodedPools = abi.encodePacked(
|
||||
encodedPools,
|
||||
pools[i].intermediaryToken,
|
||||
bytes3(pools[i].fee),
|
||||
pools[i].tickSpacing
|
||||
);
|
||||
}
|
||||
|
||||
return abi.encodePacked(
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
amountOutMin,
|
||||
zeroForOne,
|
||||
callbackExecutor,
|
||||
bytes4(callbackSelector),
|
||||
encodedPools
|
||||
);
|
||||
|
||||
bytes[] memory params = new bytes[](3);
|
||||
|
||||
params[0] = abi.encode(
|
||||
IV4Router.ExactInputSingleParams({
|
||||
poolKey: key,
|
||||
zeroForOne: zeroForOne,
|
||||
amountIn: amountIn,
|
||||
amountOutMinimum: 0,
|
||||
hookData: bytes("")
|
||||
})
|
||||
);
|
||||
|
||||
params[1] = abi.encode(key.currency0, amountIn);
|
||||
params[2] = abi.encode(key.currency1, 0);
|
||||
|
||||
return abi.encode(actions, params);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user