fix: Support Euler in swapExactInput

Do the token transfers before calling swap on the pool manager

Took 13 minutes


Took 10 seconds
This commit is contained in:
Diana Carvalho
2025-10-09 11:34:26 +02:00
parent d13b4d8586
commit b83f7c41f6
2 changed files with 78 additions and 21 deletions

View File

@@ -332,6 +332,7 @@ contract UniswapV4Executor is
uint128 amountOut = 0; uint128 amountOut = 0;
Currency swapCurrencyIn = currencyIn; Currency swapCurrencyIn = currencyIn;
uint256 swapAmountIn = amountIn; uint256 swapAmountIn = amountIn;
_settle(currencyIn, amountIn, transferType);
unchecked { unchecked {
uint256 pathLength = path.length; uint256 pathLength = path.length;
PathKey calldata pathKey; PathKey calldata pathKey;
@@ -353,12 +354,6 @@ contract UniswapV4Executor is
} }
} }
uint256 amount = _getFullDebt(currencyIn);
if (amount > amountIn) {
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
}
_settle(currencyIn, amount, transferType);
_take( _take(
swapCurrencyIn, // at the end of the loop this is actually currency out swapCurrencyIn, // at the end of the loop this is actually currency out
receiver, receiver,
@@ -409,21 +404,6 @@ contract UniswapV4Executor is
amount = uint256(_amount); amount = uint256(_amount);
} }
/// @notice Obtain the full amount owed by this contract (negative delta)
/// @param currency Currency to get the delta for
/// @return amount The amount owed by this contract as a uint256
function _getFullDebt(Currency currency)
internal
view
returns (uint256 amount)
{
int256 _amount = poolManager.currencyDelta(address(this), currency);
// If the amount is positive, it should be taken not settled.
if (_amount > 0) revert UniswapV4Executor__DeltaNotNegative(currency);
// Casting is safe due to limits on the total supply of a pool
amount = uint256(-_amount);
}
/** /**
* @notice Pays and settles a currency to the pool manager. * @notice Pays and settles a currency to the pool manager.
* @dev The implementing contract must ensure that the `payer` is a secure address. * @dev The implementing contract must ensure that the `payer` is a secure address.

View File

@@ -282,6 +282,7 @@ contract UniswapV4ExecutorTestForEuler is Constants, TestUtils {
UniswapV4ExecutorExposed uniswapV4Exposed; UniswapV4ExecutorExposed uniswapV4Exposed;
IERC20 USDT = IERC20(USDT_ADDR); IERC20 USDT = IERC20(USDT_ADDR);
IERC20 RLUSD = IERC20(RLUSD_ADDR); IERC20 RLUSD = IERC20(RLUSD_ADDR);
IERC20 WBTC = IERC20(WBTC_ADDR);
function setUp() public { function setUp() public {
uint256 forkBlock = 23535338; uint256 forkBlock = 23535338;
@@ -322,6 +323,82 @@ contract UniswapV4ExecutorTestForEuler is Constants, TestUtils {
); );
assertTrue(USDT.balanceOf(ALICE) == amountOut); assertTrue(USDT.balanceOf(ALICE) == amountOut);
} }
function testMultipleSwapEulerLowBalance() public {
// RLUSD -(euler)-> USDT -> WBTC
uint256 amountIn = 134187695711754971245517404;
deal(RLUSD_ADDR, address(uniswapV4Exposed), amountIn);
address eulerProxy = 0xe1Ce9AF672f8854845E5474400B6ddC7AE458a10;
uint256 rlusdEulerBalanceBefore = RLUSD.balanceOf(eulerProxy);
UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](2);
pools[0] = UniswapV4Executor.UniswapV4Pool({
intermediaryToken: USDT_ADDR,
fee: uint24(50),
tickSpacing: int24(1),
hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
hookData: bytes("")
});
pools[1] = UniswapV4Executor.UniswapV4Pool({
intermediaryToken: WBTC_ADDR,
fee: uint24(3000),
tickSpacing: int24(60),
hook: address(0),
hookData: bytes("")
});
bytes memory data = UniswapV4Utils.encodeExactInput(
RLUSD_ADDR,
WBTC_ADDR,
true,
RestrictTransferFrom.TransferType.Transfer,
ALICE,
pools
);
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
assertEq(
RLUSD.balanceOf(eulerProxy), rlusdEulerBalanceBefore + amountIn
);
assertTrue(WBTC.balanceOf(ALICE) == amountOut);
}
function testMultipleSwapLastSwapEuler() public {
// USDC -> RLUSD -(euler)- > USDT
// Sanity check to see if a grouped swap with Euler in the last hop works
uint256 amountIn = 134187695711754971245517404;
deal(USDC_ADDR, address(uniswapV4Exposed), amountIn);
UniswapV4Executor.UniswapV4Pool[] memory pools =
new UniswapV4Executor.UniswapV4Pool[](2);
pools[0] = UniswapV4Executor.UniswapV4Pool({
intermediaryToken: RLUSD_ADDR,
fee: uint24(500),
tickSpacing: int24(10),
hook: address(0),
hookData: bytes("")
});
pools[1] = UniswapV4Executor.UniswapV4Pool({
intermediaryToken: USDT_ADDR,
fee: uint24(50),
tickSpacing: int24(1),
hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
hookData: bytes("")
});
bytes memory data = UniswapV4Utils.encodeExactInput(
USDC_ADDR,
USDT_ADDR,
false,
RestrictTransferFrom.TransferType.Transfer,
ALICE,
pools
);
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
assertTrue(USDT.balanceOf(ALICE) == amountOut);
}
} }
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup { contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {