Skip to content

Commit

Permalink
added manual leverage adjustment
Browse files Browse the repository at this point in the history
  • Loading branch information
RedVeil committed Oct 21, 2024
1 parent 8a4bab0 commit 2dceff6
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 45 deletions.
83 changes: 60 additions & 23 deletions src/strategies/BaseAaveLeverageStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,15 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
revert();
}

/// @notice The token rewarded if the aave liquidity mining is active
function rewardTokens() external view override returns (address[] memory) {
return aaveIncentives.getRewardsByAsset(asset());
}

/*//////////////////////////////////////////////////////////////
MANAGEMENT LOGIC
LEVERAGE LOGIC
//////////////////////////////////////////////////////////////*/

function adjustLeverage() public {
// get vault current leverage : debt/collateral
(
Expand All @@ -212,8 +218,7 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
)
)).mulDiv(1e18, (1e18 - targetLTV), Math.Rounding.Ceil);

// flash loan debt asset to repay part of the debt
_flashLoan(borrowAmount, 0, 0, 0, false, slippage);
_leverDown(borrowAmount, slippage);
} else {
uint256 depositAmount = (targetLTV.mulDiv(
currentCollateral,
Expand All @@ -225,30 +230,33 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
Math.Rounding.Ceil
);

uint256 dustBalance = address(this).balance;
_leverUp(depositAmount);
}

if (dustBalance < depositAmount) {
// flashloan but use eventual collateral dust remained in the contract as well
uint256 borrowAmount = depositAmount - dustBalance;
// reverts if LTV got above max
_assertHealthyLTV();
}

// flash loan debt asset from lending protocol and add to cdp - slippage not used in this case, pass 0
_flashLoan(borrowAmount, depositAmount, 0, 2, false, 0);
} else {
// deposit the dust as collateral- borrow amount is zero
// leverage naturally decreases
_redepositAsset(0, dustBalance, asset());
}
}
function leverUp(uint256 depositAmount) public onlyKeeperOrOwner {
_leverUp(depositAmount);

// reverts if LTV got above max
_assertHealthyLTV();
}

/// @notice The token rewarded if the aave liquidity mining is active
function rewardTokens() external view override returns (address[] memory) {
return aaveIncentives.getRewardsByAsset(asset());
function leverDown(
uint256 borrowAmount,
uint256 slippage
) public onlyKeeperOrOwner {
_leverDown(borrowAmount, slippage);
// reverts if LTV got above max
_assertHealthyLTV();
}

/*//////////////////////////////////////////////////////////////
HARVEST LOGIC
//////////////////////////////////////////////////////////////*/

/// @notice Claim additional rewards given that it's active.
function claim() internal override returns (bool success) {
if (address(aaveIncentives) == address(0)) return false;
Expand Down Expand Up @@ -286,6 +294,10 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
emit Harvested();
}

/*//////////////////////////////////////////////////////////////
MANAGEMENT LOGIC
//////////////////////////////////////////////////////////////*/

function setHarvestValues(bytes memory harvestValues) external onlyOwner {
_setHarvestValues(harvestValues);
}
Expand Down Expand Up @@ -323,10 +335,6 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
initCollateral = true;
}

function withdrawDust(address recipient) public onlyOwner {
_withdrawDust(recipient);
}

/*//////////////////////////////////////////////////////////////
FLASH LOAN LOGIC
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -427,6 +435,14 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
);
}

/*//////////////////////////////////////////////////////////////
OTHER LOGIC
//////////////////////////////////////////////////////////////*/

function withdrawDust(address recipient) public onlyOwner {
_withdrawDust(recipient);
}

/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -503,6 +519,27 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
_assertHealthyLTV();
}

function _leverUp(uint256 depositAmount) internal {
uint256 dustBalance = address(this).balance;

if (dustBalance < depositAmount) {
// flashloan but use eventual collateral dust remained in the contract as well
uint256 borrowAmount = depositAmount - dustBalance;

// flash loan debt asset from lending protocol and add to cdp - slippage not used in this case, pass 0
_flashLoan(borrowAmount, depositAmount, 0, 2, false, 0);
} else {
// deposit the dust as collateral- borrow amount is zero
// leverage naturally decreases
_redepositAsset(0, dustBalance, asset());
}
}

function _leverDown(uint256 borrowAmount, uint256 slippage) internal {
// flash loan debt asset to repay part of the debt
_flashLoan(borrowAmount, 0, 0, 0, false, slippage);
}

///@notice called after a flash loan to repay cdp
function _reduceLeverage(
bool isFullWithdraw,
Expand Down Expand Up @@ -607,7 +644,7 @@ abstract contract BaseAaveLeverageStrategy is BaseStrategy, IFlashLoanReceiver {
}

/*//////////////////////////////////////////////////////////////
TO OVERRIDE IN IMPLEMENTATION
TO OVERRIDE IN IMPLEMENTATION
//////////////////////////////////////////////////////////////*/

// must provide conversion from debt asset to vault (collateral) asset
Expand Down
147 changes: 125 additions & 22 deletions test/strategies/stader/EthXLooper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@

pragma solidity ^0.8.25;

import {
ETHXLooper,
BaseAaveLeverageStrategy,
LooperBaseValues,
LooperValues,
IERC20,
IETHxStaking
} from "src/strategies/stader/ETHxLooper.sol";
import {ETHXLooper, BaseAaveLeverageStrategy, LooperBaseValues, LooperValues, IERC20, IETHxStaking} from "src/strategies/stader/ETHxLooper.sol";
import {IERC20Metadata, ILendingPool, IProtocolDataProvider, Math} from "src/strategies/BaseAaveLeverageStrategy.sol";

import {BaseStrategyTest, IBaseStrategy, TestConfig, stdJson, Math} from "../BaseStrategyTest.sol";
Expand Down Expand Up @@ -58,7 +51,12 @@ contract ETHXLooperTest is BaseStrategyTest {
// Deploy Strategy
ETHXLooper strategy = new ETHXLooper();

strategy.initialize(testConfig_.asset, address(this), true, abi.encode(baseValues, looperInitValues));
strategy.initialize(
testConfig_.asset,
address(this),
true,
abi.encode(baseValues, looperInitValues)
);

strategyContract = ETHXLooper(payable(strategy));

Expand Down Expand Up @@ -105,23 +103,24 @@ contract ETHXLooperTest is BaseStrategyTest {

function test__initialization() public override {
LooperBaseValues memory baseValues = abi.decode(
json.parseRaw(
string.concat(".configs[0].specific.base")
),
json.parseRaw(string.concat(".configs[0].specific.base")),
(LooperBaseValues)
);

LooperValues memory looperInitValues = abi.decode(
json.parseRaw(
string.concat(".configs[0].specific.init")
),
json.parseRaw(string.concat(".configs[0].specific.init")),
(LooperValues)
);

// Deploy Strategy
ETHXLooper strategy = new ETHXLooper();

strategy.initialize(testConfig.asset, address(this), true, abi.encode(baseValues, looperInitValues));
strategy.initialize(
testConfig.asset,
address(this),
true,
abi.encode(baseValues, looperInitValues)
);

verify_adapterInit();
}
Expand Down Expand Up @@ -215,8 +214,14 @@ contract ETHXLooperTest is BaseStrategyTest {
address asset = strategy.asset();

strategyContract.setHarvestValues(abi.encode(newPool));
uint256 oldAllowance = IERC20(asset).allowance(address(strategy), oldPool);
uint256 newAllowance = IERC20(asset).allowance(address(strategy), newPool);
uint256 oldAllowance = IERC20(asset).allowance(
address(strategy),
oldPool
);
uint256 newAllowance = IERC20(asset).allowance(
address(strategy),
newPool
);

assertEq(address(strategyContract.stableSwapPool()), newPool);
assertEq(oldAllowance, 0);
Expand Down Expand Up @@ -531,13 +536,23 @@ contract ETHXLooperTest is BaseStrategyTest {
function test__setLeverageValues_invalidInputs() public {
// protocolLTV < targetLTV < maxLTV
vm.expectRevert(
abi.encodeWithSelector(BaseAaveLeverageStrategy.InvalidLTV.selector, 3e18, 4e18, strategyContract.protocolMaxLTV())
abi.encodeWithSelector(
BaseAaveLeverageStrategy.InvalidLTV.selector,
3e18,
4e18,
strategyContract.protocolMaxLTV()
)
);
strategyContract.setLeverageValues(3e18, 4e18);

// maxLTV < targetLTV < protocolLTV
vm.expectRevert(
abi.encodeWithSelector(BaseAaveLeverageStrategy.InvalidLTV.selector, 4e17, 3e17, strategyContract.protocolMaxLTV())
abi.encodeWithSelector(
BaseAaveLeverageStrategy.InvalidLTV.selector,
4e17,
3e17,
strategyContract.protocolMaxLTV()
)
);
strategyContract.setLeverageValues(4e17, 3e17);
}
Expand All @@ -554,7 +569,13 @@ contract ETHXLooperTest is BaseStrategyTest {
function test__setSlippage_invalidValue() public {
uint256 newSlippage = 1e18; // 100%

vm.expectRevert(abi.encodeWithSelector(BaseAaveLeverageStrategy.InvalidSlippage.selector, newSlippage, 2e17));
vm.expectRevert(
abi.encodeWithSelector(
BaseAaveLeverageStrategy.InvalidSlippage.selector,
newSlippage,
2e17
)
);
strategyContract.setSlippage(newSlippage);
}

Expand Down Expand Up @@ -601,6 +622,84 @@ contract ETHXLooperTest is BaseStrategyTest {
// assertApproxEqAbs(strategyContract.targetLTV(), strategyContract.getLTV(), _delta_, string.concat("ltv != expected"));
// }

function test__leverUp() public {
uint256 amountDeposit = 1e18;
deal(address(ethX), bob, amountDeposit);

vm.startPrank(bob);
ethX.approve(address(strategy), amountDeposit);
strategy.deposit(amountDeposit, bob);
vm.stopPrank();

uint256 initialABalance = aEthX.balanceOf(address(strategy));
uint256 initialLTV = strategyContract.getLTV();
uint256 depositAmount = 0.5e18; // Example deposit amount

// Call leverUp with a specific deposit amount
strategyContract.leverUp(depositAmount);

uint256 finalABalance = aEthX.balanceOf(address(strategy));
uint256 finalLTV = strategyContract.getLTV();

// Check that the aToken balance has increased
assertGt(
finalABalance,
initialABalance,
"aToken balance should increase after leverUp"
);

// Check that the LTV has increased
assertGt(finalLTV, initialLTV, "LTV should increase after leverUp");

// Check that the final LTV is not above the max LTV
assertLe(
finalLTV,
strategyContract.maxLTV(),
"Final LTV should not exceed max LTV"
);
}

function test__leverDown() public {
uint256 amountDeposit = 1e18;
deal(address(ethX), bob, amountDeposit);

vm.startPrank(bob);
ethX.approve(address(strategy), amountDeposit);
strategy.deposit(amountDeposit, bob);
vm.stopPrank();

// First, lever up to create some debt
strategyContract.leverUp(0.5e18);

uint256 initialABalance = aEthX.balanceOf(address(strategy));
uint256 initialLTV = strategyContract.getLTV();
uint256 borrowAmount = 0.2e18; // Example borrow amount to reduce
uint256 slippage = 1e16; // 1% slippage

// Now call leverDown
strategyContract.leverDown(borrowAmount, slippage);

uint256 finalABalance = aEthX.balanceOf(address(strategy));
uint256 finalLTV = strategyContract.getLTV();

// Check that the aToken balance has decreased
assertLt(
finalABalance,
initialABalance,
"aToken balance should decrease after leverDown"
);

// Check that the LTV has decreased
assertLt(finalLTV, initialLTV, "LTV should decrease after leverDown");

// Check that the final LTV is not above the max LTV
assertLe(
finalLTV,
strategyContract.maxLTV(),
"Final LTV should not exceed max LTV"
);
}

/*//////////////////////////////////////////////////////////////
INITIALIZATION
//////////////////////////////////////////////////////////////*/
Expand All @@ -609,7 +708,11 @@ contract ETHXLooperTest is BaseStrategyTest {
assertEq(strategy.asset(), address(ethX), "asset");
assertEq(
IERC20Metadata(address(strategy)).name(),
string.concat("VaultCraft Leveraged ", IERC20Metadata(address(ethX)).name(), " Strategy"),
string.concat(
"VaultCraft Leveraged ",
IERC20Metadata(address(ethX)).name(),
" Strategy"
),
"name"
);
assertEq(
Expand Down
Loading

0 comments on commit 2dceff6

Please sign in to comment.