feat: Support Euler low balance single swaps (univ4)
Took 1 hour 34 minutes Took 4 minutes
This commit is contained in:
@@ -275,17 +275,12 @@ contract UniswapV4Executor is
|
|||||||
address receiver,
|
address receiver,
|
||||||
bytes calldata hookData
|
bytes calldata hookData
|
||||||
) external returns (uint128) {
|
) external returns (uint128) {
|
||||||
|
Currency currencyIn = zeroForOne ? poolKey.currency0 : poolKey.currency1;
|
||||||
|
_settle(currencyIn, amountIn, transferType);
|
||||||
uint128 amountOut = _swap(
|
uint128 amountOut = _swap(
|
||||||
poolKey, zeroForOne, -int256(uint256(amountIn)), hookData
|
poolKey, zeroForOne, -int256(uint256(amountIn)), hookData
|
||||||
).toUint128();
|
).toUint128();
|
||||||
|
|
||||||
Currency currencyIn = zeroForOne ? poolKey.currency0 : poolKey.currency1;
|
|
||||||
uint256 amount = _getFullDebt(currencyIn);
|
|
||||||
if (amount > amountIn) {
|
|
||||||
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
|
|
||||||
}
|
|
||||||
_settle(currencyIn, amount, transferType);
|
|
||||||
|
|
||||||
Currency currencyOut =
|
Currency currencyOut =
|
||||||
zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
||||||
_take(currencyOut, receiver, _mapTakeAmount(amountOut, currencyOut));
|
_take(currencyOut, receiver, _mapTakeAmount(amountOut, currencyOut));
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ contract Constants is Test, BaseConstants {
|
|||||||
address BSGG_ADDR = address(0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3);
|
address BSGG_ADDR = address(0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3);
|
||||||
address GHO_ADDR = address(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f);
|
address GHO_ADDR = address(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f);
|
||||||
address ONDO_ADDR = address(0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3);
|
address ONDO_ADDR = address(0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3);
|
||||||
|
address RLUSD_ADDR = address(0x8292Bb45bf1Ee4d140127049757C2E0fF06317eD);
|
||||||
|
|
||||||
// Maverick v2
|
// Maverick v2
|
||||||
address MAVERICK_V2_FACTORY = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e;
|
address MAVERICK_V2_FACTORY = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e;
|
||||||
@@ -146,6 +147,9 @@ contract Constants is Test, BaseConstants {
|
|||||||
// Curve meta registry
|
// Curve meta registry
|
||||||
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
||||||
|
|
||||||
|
// Uniswap v4 pool manager
|
||||||
|
address POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Deploys a dummy contract with non-empty bytecode
|
* @dev Deploys a dummy contract with non-empty bytecode
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
|||||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||||
|
|
||||||
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||||
constructor(IPoolManager _poolManager, address _permit2)
|
constructor(IPoolManager _POOL_MANAGER, address _permit2)
|
||||||
UniswapV4Executor(_poolManager, _permit2)
|
UniswapV4Executor(_POOL_MANAGER, _permit2)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
function decodeData(bytes calldata data)
|
function decodeData(bytes calldata data)
|
||||||
@@ -41,13 +41,11 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
IERC20 USDT = IERC20(USDT_ADDR);
|
IERC20 USDT = IERC20(USDT_ADDR);
|
||||||
IERC20 USDC = IERC20(USDC_ADDR);
|
IERC20 USDC = IERC20(USDC_ADDR);
|
||||||
|
|
||||||
address poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 22689128;
|
uint256 forkBlock = 22689128;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
||||||
IPoolManager(poolManager), PERMIT2_ADDRESS
|
IPoolManager(POOL_MANAGER), PERMIT2_ADDRESS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +112,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
function testSingleSwap() public {
|
function testSingleSwap() public {
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
@@ -138,7 +136,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
);
|
);
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -152,12 +150,12 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
loadCallDataFromFile("test_encode_uniswap_v4_simple_swap");
|
loadCallDataFromFile("test_encode_uniswap_v4_simple_swap");
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(ALICE), usdeBalanceBeforeSwapExecutor - amountIn
|
USDE.balanceOf(ALICE), usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
);
|
);
|
||||||
@@ -168,7 +166,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
// USDE -> USDT -> WBTC
|
// USDE -> USDT -> WBTC
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
@@ -197,7 +195,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
);
|
);
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -212,12 +210,12 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
|
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -267,6 +265,66 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contract UniswapV4ExecutorTestForEuler is Constants, TestUtils {
|
||||||
|
/* These tests are necessary because Euler works a little differently from general UniswapV4 logic.
|
||||||
|
In the previous version of the UniswapV4Executor we are only sending the user's tokens into the Pool Manager
|
||||||
|
after we call swap on it. This is ok because the Pool Manager tracks the debts and accepts everything as long
|
||||||
|
as the tokens are transfers inside of the unlock callback. However, Euler expects the funds to already be
|
||||||
|
in the Pool Manager when beforeSwap is called. This is not a problem for tokens that the Pool Manager has a
|
||||||
|
lot of, but for tokens with low balances this makes the tx fail. We need to transfer the tokens into
|
||||||
|
the Pool Manager before we call swap on it.
|
||||||
|
The only risk here is that we are assuming that the amount_in will never change. In the previous version, we
|
||||||
|
were confirming this amount with the currencyDelta of the Pool Manager. Now we pray.
|
||||||
|
*/
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
UniswapV4ExecutorExposed uniswapV4Exposed;
|
||||||
|
IERC20 USDT = IERC20(USDT_ADDR);
|
||||||
|
IERC20 RLUSD = IERC20(RLUSD_ADDR);
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
uint256 forkBlock = 23535338;
|
||||||
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
||||||
|
IPoolManager(POOL_MANAGER), PERMIT2_ADDRESS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSingleSwapEulerLowBalance() public {
|
||||||
|
uint256 amountIn = 134187695711754971245517404;
|
||||||
|
deal(RLUSD_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
|
address eulerProxy = 0xe1Ce9AF672f8854845E5474400B6ddC7AE458a10;
|
||||||
|
uint256 rlusdEulerBalanceBefore = RLUSD.balanceOf(eulerProxy);
|
||||||
|
uint256 rlusdBalanceBeforeSwapExecutor =
|
||||||
|
RLUSD.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: uint24(50),
|
||||||
|
tickSpacing: int24(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
|
RLUSD_ADDR,
|
||||||
|
USDT_ADDR,
|
||||||
|
true,
|
||||||
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
|
ALICE,
|
||||||
|
address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
|
||||||
|
bytes(""),
|
||||||
|
pools
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
|
assertEq(
|
||||||
|
RLUSD.balanceOf(eulerProxy), rlusdEulerBalanceBefore + amountIn
|
||||||
|
);
|
||||||
|
assertTrue(USDT.balanceOf(ALICE) == amountOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||||
function testSingleSwapUSV4CallbackPermit2() public {
|
function testSingleSwapUSV4CallbackPermit2() public {
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
|
|||||||
Reference in New Issue
Block a user