Skip to content

Commit

Permalink
fix euler withdraw when balance underlying token non null (#65)
Browse files Browse the repository at this point in the history
* fix euler withdraw when balance underlying token non null

* full test withdraw with restricting available liquidity

* decrease optimizer run for AaveFraxStaker
  • Loading branch information
GuillaumeNervoXS authored Mar 3, 2023
1 parent d6f01f3 commit adcd1f3
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ contract GenericEuler is GenericLenderBaseUpgradeable {
uint256 toUnstake;
// We can take all
if (toWithdraw <= availableLiquidity)
toUnstake = toWithdraw > (balanceUnderlying + looseBalance)
? toWithdraw - (balanceUnderlying + looseBalance)
: 0;
toUnstake = toWithdraw > balanceUnderlying ? toWithdraw - balanceUnderlying : 0;
else {
// Take all we can
toUnstake = availableLiquidity > balanceUnderlying ? availableLiquidity - balanceUnderlying : 0;
Expand Down
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const config: HardhatUserConfig = {
viaIR: false,
optimizer: {
enabled: true,
runs: 20000,
runs: 150000,
},
},
},
Expand Down
139 changes: 130 additions & 9 deletions test/foundry/optimizerAPR/euler/GenericEulerStakerTest.test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.12;
import "../../BaseTest.test.sol";
import { PoolManager } from "../../../../contracts/mock/MockPoolManager2.sol";
import { OptimizerAPRStrategy } from "../../../../contracts/strategies/OptimizerAPR/OptimizerAPRStrategy.sol";
import { GenericEulerStaker, IERC20, IEulerStakingRewards, IEuler, IEulerExec, IEulerEToken, IEulerDToken, IGenericLender, AggregatorV3Interface } from "../../../../contracts/strategies/OptimizerAPR/genericLender/euler/GenericEulerStaker.sol";
import { GenericEulerStaker, IERC20, IEulerStakingRewards, IEuler, IEulerExec, IEulerEToken, IEulerDToken, IEulerMarkets, IGenericLender, AggregatorV3Interface } from "../../../../contracts/strategies/OptimizerAPR/genericLender/euler/GenericEulerStaker.sol";

interface IMinimalLiquidityGauge {
// solhint-disable-next-line
Expand All @@ -21,8 +21,11 @@ contract GenericEulerStakerTest is BaseTest {
IEulerExec private constant _EXEC = IEulerExec(0x59828FdF7ee634AaaD3f58B19fDBa3b03E2D9d80);
IEulerStakingRewards internal constant _STAKER = IEulerStakingRewards(0xE5aFE81e63f0A52a3a03B922b30f73B8ce74D570);
IEuler private constant _euler = IEuler(0x27182842E098f60e3D576794A5bFFb0777E025d3);
IEulerMarkets private constant _eulerMarkets = IEulerMarkets(0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3);
IEulerEToken internal constant _eUSDC = IEulerEToken(0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716);
IEulerDToken internal constant _dUSDC = IEulerDToken(0x84721A3dB22EB852233AEAE74f9bC8477F8bcc42);
IEulerEToken internal constant _eDAI = IEulerEToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);
IERC20 internal constant _DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
AggregatorV3Interface private constant _CHAINLINK =
AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);

Expand Down Expand Up @@ -127,24 +130,142 @@ contract GenericEulerStakerTest is BaseTest {

// ================================== WITHDRAW =================================

function testWithdawSuccess(uint256 amount, uint256 propWithdraw) public {
function testWithdawSuccess(
uint256 amount,
uint256 amountAirDropToken,
uint256 amountAirDropEToken,
uint256 propWithdraw
) public {
amount = bound(amount, minTokenAmount, maxTokenAmount);
amountAirDropToken = bound(amountAirDropToken, 0, maxTokenAmount);
amountAirDropEToken = bound(amountAirDropEToken, minTokenAmount, maxTokenAmount);
propWithdraw = bound(propWithdraw, 1, BASE_PARAMS);
uint256 toWithdraw = (amount * propWithdraw) / BASE_PARAMS;
uint256 toWithdraw = ((amount + amountAirDropToken + amountAirDropEToken) * propWithdraw) / BASE_PARAMS;
if (toWithdraw < minTokenAmount) toWithdraw = minTokenAmount;
deal(address(_TOKEN), address(lender), amount);
vm.prank(_KEEPER);
lender.deposit();

// make the want balance non null
deal(address(_TOKEN), address(lender), amountAirDropToken);
vm.startPrank(_KEEPER);
deal(address(_TOKEN), address(_KEEPER), amountAirDropEToken);
_TOKEN.approve(address(_euler), amountAirDropEToken);
_eUSDC.deposit(0, amountAirDropEToken);
IERC20(address(_eUSDC)).transfer(address(lender), _eUSDC.balanceOf(address(_KEEPER)));
vm.stopPrank();

vm.prank(_KEEPER);
lender.withdraw(toWithdraw);
assertEq(_TOKEN.balanceOf(address(lender)), 0);
assertApproxEqAbs(_eUSDC.balanceOf(address(lender)), 0, 10**(18 - 6));
uint256 balanceInUnderlying = _eUSDC.convertBalanceToUnderlying(
IERC20(address(_STAKER)).balanceOf(address(lender))
IERC20(address(_STAKER)).balanceOf(address(lender)) + IERC20(address(_eUSDC)).balanceOf(address(lender))
);
assertApproxEqAbs(_TOKEN.balanceOf(address(strat)), toWithdraw, 1 wei);
assertApproxEqAbs(balanceInUnderlying, amount - toWithdraw, 1 wei);
assertApproxEqAbs(lender.underlyingBalanceStored(), amount - toWithdraw, 1 wei);
// 2 wei because approx on amount and amountAirDropEToken
assertApproxEqAbs(_TOKEN.balanceOf(address(strat)), toWithdraw, 2 wei);
assertApproxEqAbs(lender.nav(), amount + amountAirDropEToken + amountAirDropToken - toWithdraw, 2 wei);
if (toWithdraw > amountAirDropToken) {
assertApproxEqAbs(
balanceInUnderlying,
amount + amountAirDropEToken - (toWithdraw - amountAirDropToken),
2 wei
);
assertApproxEqAbs(
lender.underlyingBalanceStored(),
amount + amountAirDropEToken - (toWithdraw - amountAirDropToken),
2 wei
);
assertEq(_TOKEN.balanceOf(address(lender)), 0);
if (amountAirDropEToken > toWithdraw - amountAirDropToken)
assertApproxEqAbs(
_eUSDC.balanceOfUnderlying((address(lender))),
amountAirDropEToken - (toWithdraw - amountAirDropToken),
10**(18 - 6)
);
else assertApproxEqAbs(_eUSDC.balanceOfUnderlying((address(lender))), 0, 1 wei);
} else {
assertApproxEqAbs(balanceInUnderlying, amount + amountAirDropEToken, 2 wei);
assertApproxEqAbs(lender.underlyingBalanceStored(), amount + amountAirDropEToken, 2 wei);
assertEq(_TOKEN.balanceOf(address(lender)), amountAirDropToken - toWithdraw);
assertApproxEqAbs(_eUSDC.balanceOfUnderlying((address(lender))), amountAirDropEToken, 1 wei);
}
}

function testWithdawPartialLiquiditySuccess(
uint256 amount,
uint256 availableLiquidity,
uint256 amountAirDropToken,
uint256 amountAirDropEToken,
uint256 propWithdraw
) public {
amount = bound(amount, minTokenAmount, maxTokenAmount);
amountAirDropToken = bound(amountAirDropToken, 0, maxTokenAmount);
amountAirDropEToken = bound(amountAirDropEToken, minTokenAmount, maxTokenAmount);
propWithdraw = bound(propWithdraw, 1, BASE_PARAMS);
uint256 toWithdraw = ((amount + amountAirDropToken + amountAirDropEToken) * propWithdraw) / BASE_PARAMS;
if (toWithdraw < minTokenAmount) toWithdraw = minTokenAmount;
deal(address(_TOKEN), address(lender), amount);
vm.prank(_KEEPER);
lender.deposit();

// make the want balance non null
deal(address(_TOKEN), address(lender), amountAirDropToken);
vm.startPrank(_KEEPER);
deal(address(_TOKEN), address(_KEEPER), amountAirDropEToken);
_TOKEN.approve(address(_euler), amountAirDropEToken);
_eUSDC.deposit(0, amountAirDropEToken);
IERC20(address(_eUSDC)).transfer(address(lender), _eUSDC.balanceOf(address(_KEEPER)));
vm.stopPrank();

// artificially decrease Euler balance
vm.startPrank(_BOB);
{
uint256 curBalanceEuler = _TOKEN.balanceOf(address(_euler));
availableLiquidity = bound(availableLiquidity, 0, curBalanceEuler);
uint256 depositAmount = 10 * (curBalanceEuler - availableLiquidity) * 10**12;
deal(address(_DAI), _BOB, depositAmount);
_DAI.approve(address(_euler), depositAmount);
_eulerMarkets.enterMarket(0, address(_DAI));
_eDAI.deposit(0, depositAmount);
_dUSDC.borrow(0, curBalanceEuler - availableLiquidity);
}
vm.stopPrank();

vm.prank(_KEEPER);
lender.withdraw(toWithdraw);
uint256 balanceInUnderlying = _eUSDC.convertBalanceToUnderlying(
IERC20(address(_STAKER)).balanceOf(address(lender)) + IERC20(address(_eUSDC)).balanceOf(address(lender))
);
uint256 withdrawnAmount = availableLiquidity + amountAirDropToken < toWithdraw
? availableLiquidity + amountAirDropToken
: toWithdraw;
// 2 wei because approx on amount and amountAirDropEToken
assertApproxEqAbs(_TOKEN.balanceOf(address(strat)), withdrawnAmount, 2 wei);
assertApproxEqAbs(lender.nav(), amount + amountAirDropEToken + amountAirDropToken - withdrawnAmount, 2 wei);
if (withdrawnAmount > amountAirDropToken) {
assertApproxEqAbs(
balanceInUnderlying,
amount + amountAirDropEToken + amountAirDropToken - withdrawnAmount,
2 wei
);
assertApproxEqAbs(
lender.underlyingBalanceStored(),
amount + amountAirDropEToken + amountAirDropToken - withdrawnAmount,
2 wei
);
assertEq(_TOKEN.balanceOf(address(lender)), 0);
if (amountAirDropEToken > withdrawnAmount - amountAirDropToken)
assertApproxEqAbs(
_eUSDC.balanceOfUnderlying((address(lender))),
amountAirDropEToken - (withdrawnAmount - amountAirDropToken),
10**(18 - 6)
);
else assertApproxEqAbs(_eUSDC.balanceOfUnderlying((address(lender))), 0, 1 wei);
} else {
assertApproxEqAbs(balanceInUnderlying, amount + amountAirDropEToken, 2 wei);
assertApproxEqAbs(lender.underlyingBalanceStored(), amount + amountAirDropEToken, 2 wei);
assertEq(_TOKEN.balanceOf(address(lender)), amountAirDropToken - withdrawnAmount);
assertApproxEqAbs(_eUSDC.balanceOfUnderlying((address(lender))), amountAirDropEToken, 1 wei);
}
}

function testWithdawAllSuccess(uint256 amount) public {
Expand Down

0 comments on commit adcd1f3

Please sign in to comment.