Skip to content

Commit

Permalink
add leverage calculation tests
Browse files Browse the repository at this point in the history
  • Loading branch information
daopunk committed Jul 10, 2024
1 parent 81b9a5d commit f2cf1d9
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 31 deletions.
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ src = "src"
out = "out"
libs = ["lib"]
ffi = true
fuzz_runs = 128
optimizer_runs = 5_000

[rpc_endpoints]
mainnet = "${ARB_MAINNET_RPC}"
Expand Down
7 changes: 6 additions & 1 deletion src/leverage/ParaswapSellAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
/// @dev approve address(this) as safeHandler and request to borrow asset on Aave
function requestFlashloan(
SellParams memory _sellParams,
uint256 _initCollateral,
uint256 _collateralLoan,
uint256 _minDstAmount,
uint256 _safeId,
Expand All @@ -115,7 +116,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
address(collateralJoinFactory.collateralJoins(_cType)),
coinJoin,
_safeId,
_collateralLoan - PREMIUM,
_initCollateral + _collateralLoan,
_sellParams.sellAmount
);

Expand All @@ -141,12 +142,15 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
abi.decode(params, (uint256, SellParams, bytes));

emit log_named_uint('RETH BAL AQUIRE LOAN', IERC20Metadata(_sellParams.toToken).balanceOf(address(this)));
emit log_named_uint('OD BAL BEFORE LOCK', IERC20Metadata(_sellParams.fromToken).balanceOf(address(this)));

uint256 _beforebalance = IERC20Metadata(_sellParams.fromToken).balanceOf(address(this));
uint256 _sellAmount = _sellParams.sellAmount;

_executeFromProxy(_payload);

emit log_named_uint('OD BAL AFTER LOCK', IERC20Metadata(_sellParams.fromToken).balanceOf(address(this)));

// todo add error msg
// if (_sellAmount != OD.balanceOf(address(this)) - _beforebalance) revert();

Expand All @@ -160,6 +164,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
_minDstAmount
);
emit log_named_uint('RETH BAL POST SWAP', IERC20Metadata(_sellParams.toToken).balanceOf(address(this)));
emit log_named_uint('OD BAL AFTER SWAP', IERC20Metadata(_sellParams.fromToken).balanceOf(address(this)));

uint256 _payBack = amount + premium;
IERC20Metadata(asset).approve(address(POOL), _payBack);
Expand Down
1 change: 1 addition & 0 deletions src/leverage/interfaces/IParaswapSellAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ interface IParaswapSellAdapter {
*/
function requestFlashloan(
SellParams memory _sellParams,
uint256 _initCollateral,
uint256 _collateralLoan,
uint256 _minDstAmount,
uint256 _safeId,
Expand Down
134 changes: 104 additions & 30 deletions test/e2e/E2ESwapExit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import '@script/Registry.s.sol';
import {IERC20Metadata} from '@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol';
import {IVault721} from '@opendollar/interfaces/proxies/IVault721.sol';
import {IDenominatedOracle} from '@opendollar/interfaces/oracles/IDenominatedOracle.sol';
import {DelayedOracleForTest} from '@opendollar/test/mocks/DelayedOracleForTest.sol';
import {AugustusRegistry} from '@aave-debt-swap/dependencies/paraswap/AugustusRegistry.sol';
import {ParaswapSellAdapter, IParaswapSellAdapter} from 'src/leverage/ParaswapSellAdapter.sol';
import {CommonTest} from 'test/e2e/common/CommonTest.t.sol';
Expand All @@ -14,6 +13,7 @@ import {Math} from '@opendollar/libraries/Math.sol';
contract E2ESwapExit is CommonTest {
using Math for uint256;

uint256 public constant MAX_SLIPPAGE_PERCENT = 0.3e4;
uint256 public constant PREMIUM = 500_000_000_000;
uint256 public constant INTEREST_RATE_MODE = 0;
uint16 public constant REF_CODE = 0;
Expand All @@ -32,7 +32,7 @@ contract E2ESwapExit is CommonTest {
super.setUp();
rethOracle = IDenominatedOracle(MAINNET_DENOMINATED_RETH_USD_ORACLE);
(rethUsdPrice,) = rethOracle.getResultWithValidity();
setCTypePrice(RETH, rethUsdPrice);
_setCTypePrice(RETH, rethUsdPrice);

userProxy = _deployOrFind(USER);
label(USER, 'USER-WALLET');
Expand Down Expand Up @@ -67,46 +67,120 @@ contract E2ESwapExit is CommonTest {
vm.stopPrank();
}

/**
* todo: test with initial deposit of collateral
* initial deposit + loan to overcome the over-collateralization "loss"
* in being able to returning to loan in full + premium
*/
function testRequestFlashloan() public {
assertEq(readCTypePrice(RETH), rethUsdPrice);
uint256 _collateralLoan = 1 ether;
uint256 _sellAmount = (_collateralLoan.wmul(rethUsdPrice) - PREMIUM) * 2 / 3;
/// @dev example of locking collateral at same time of leveraging
function testRequestFlashloan0() public {
uint256 _initCapital = 0.1 ether;

/// @notice locked 30% collateral independently from capital allocated to leverage (swap loss?)
uint256 _additionalCapital = _initCapital * 30 / 100;

_testRequestFlashLoan(_initCapital, _additionalCapital);
}

function testRequestFlashloan1() public {
uint256 _initCapital = 0.5 ether;

/// @notice locked 35% collateral independently from capital allocated to leverage (swap loss?)
uint256 _additionalCapital = _initCapital * 35 / 100;

_testRequestFlashLoan(_initCapital, _additionalCapital);
}

function testRequestFlashloan2() public {
uint256 _initCapital = 1 ether;

/// @notice locked 45% collateral independently from capital allocated to leverage (swap loss?)
uint256 _additionalCapital = _initCapital * 45 / 100;

_testRequestFlashLoan(_initCapital, _additionalCapital);
}

function testRequestFlashloan3() public {
uint256 _initCapital = 5 ether;

/// @notice locked 55% collateral independently from capital allocated to leverage (swap loss?)
uint256 _additionalCapital = _initCapital * 55 / 100;

_testRequestFlashLoan(_initCapital, _additionalCapital);
}

function testRequestFlashloan4() public {
uint256 _initCapital = 10 ether;

/// @notice locked 55% collateral independently from capital allocated to leverage (swap loss?)
uint256 _additionalCapital = _initCapital * 60 / 100;

_testRequestFlashLoan(_initCapital, _additionalCapital);
}

function testMath() public {
// intial reth
uint256 initialCollateral = 0.5 ether;

// 100 / 1.35% = 74
uint256 _percentMaxDebt = uint256(10_000) / uint256(135);
emit log_named_uint('_percentMaxDebt ', _percentMaxDebt);

// 100 - 74 = 26
uint256 _percentMakeUp = 100 - _percentMaxDebt;
emit log_named_uint('_percentMakeUp ', _percentMakeUp);

uint256 _maxCollateral = initialCollateral * 100 / _percentMakeUp;
emit log_named_uint('_maxCollateral ', _maxCollateral);

uint256 _maxTotalLoan = _maxCollateral - initialCollateral;
emit log_named_uint('_maxTotalLoan ', _maxTotalLoan);

uint256 _maxLoan = _maxTotalLoan - (PREMIUM + MAX_SLIPPAGE_PERCENT);
emit log_named_uint('_maxLoan ', _maxLoan);
}

// Helper Functions
function _calculateMaxLeverage(uint256 _initialCollateral, uint256 _safetyRatio) public returns (uint256) {
emit log_named_uint('_currentRethPrice ', rethUsdPrice);

uint256 _percentMaxDebt = uint256(10_000) / _safetyRatio;
uint256 _percentMakeUp = 100 - _percentMaxDebt;

uint256 _maxCollateral = _initialCollateral * 100 / _percentMakeUp;
emit log_named_uint('_maxCollateral ', _maxCollateral);

uint256 _maxTotalLoan = _maxCollateral - _initialCollateral;
emit log_named_uint('_maxTotalLoan ', _maxTotalLoan);

return _maxTotalLoan;
}

function _testRequestFlashLoan(uint256 _initCapital, uint256 _additionalCapital) internal {
uint256 _deposit = _initCapital + _additionalCapital;
deal(RETH_ADDR, USER, _deposit);

uint256 _maxLoan = _calculateMaxLeverage(_initCapital, 135);
uint256 _sellAmount = _maxLoan.wmul(rethUsdPrice);
emit log_named_uint('DEBT SELL AMOUNT', _sellAmount);

(uint256 _dstAmount, IParaswapSellAdapter.SellParams memory _sellParams) =
_getFullUserInputWithAmount(OD_ADDR, RETH_ADDR, _sellAmount);

// todo: lock initial capital in safe
uint256 _initCapital = _collateralLoan / 2;
deal(RETH_ADDR, USER, _initCapital);

vm.prank(userProxy);
safeManager.allowSAFE(vaults[userProxy], sellAdapterProxy, true);

vm.startPrank(USER);
IERC20Metadata(RETH_ADDR).approve(SELL_ADAPTER, _initCapital);
sellAdapter.deposit(RETH_ADDR, _initCapital);

// assertEq(IERC20Metadata(RETH_ADDR).balanceOf(SELL_ADAPTER), _initCapital);
// assertEq(IERC20Metadata(OD_ADDR).balanceOf(SELL_ADAPTER), 0);

sellAdapter.requestFlashloan(_sellParams, _collateralLoan, _dstAmount, vaults[userProxy], RETH);
// assertEq(IERC20Metadata(RETH_ADDR).balanceOf(SELL_ADAPTER), 0);

IERC20Metadata(RETH_ADDR).approve(SELL_ADAPTER, _deposit);
sellAdapter.deposit(RETH_ADDR, _deposit);
sellAdapter.requestFlashloan(_sellParams, _initCapital, _maxLoan, _dstAmount, vaults[userProxy], RETH);
vm.stopPrank();
}

function setCTypePrice(bytes32 _cType, uint256 _price) public {
DelayedOracleForTest(address(delayedOracle[_cType])).setPriceAndValidity(_price, true);
oracleRelayer.updateCollateralPrice(_cType);
_logFinalValues(_deposit);
}

function readCTypePrice(bytes32 _cType) public returns (uint256 _price) {
_price = delayedOracle[_cType].read();
function _logFinalValues(uint256 _deposit) internal {
(uint256 _c, uint256 _d) = _getSAFE(RETH, userNFV.safeHandler);
emit log_named_uint('ORIGINAL DEBT ', 0);
emit log_named_uint('FINAL DEBT ', _d);
emit log_named_uint('--------------------', 0);
emit log_named_uint('ORIGINAL COLLATERAL ', _deposit);
emit log_named_uint('FINAL COLLATERAL ', _c);
emit log_named_uint('LEVERAGE PERCENTAGE ', _c * 100 / _deposit);
}
}
10 changes: 10 additions & 0 deletions test/e2e/common/CommonTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Math} from '@opendollar/libraries/Math.sol';
import {ODProxy} from '@opendollar/contracts/proxies/ODProxy.sol';
import {IVault721} from '@opendollar/interfaces/proxies/IVault721.sol';
import {ISAFEEngine} from '@opendollar/interfaces/ISAFEEngine.sol';
import {DelayedOracleForTest} from '@opendollar/test/mocks/DelayedOracleForTest.sol';
import {ExitActions} from 'src/leverage/ExitActions.sol';
import {LeverageCalculator} from 'src/leverage/LeverageCalculator.sol';
import {BaseTest} from 'test/e2e/common/BaseTest.t.sol';
Expand Down Expand Up @@ -38,6 +39,15 @@ contract CommonTest is Common, BaseTest {
token = address(collateral[TKN]);
}

function _setCTypePrice(bytes32 _cType, uint256 _price) internal {
DelayedOracleForTest(address(delayedOracle[_cType])).setPriceAndValidity(_price, true);
oracleRelayer.updateCollateralPrice(_cType);
}

function _readCTypePrice(bytes32 _cType) internal returns (uint256 _price) {
_price = delayedOracle[_cType].read();
}

function _deployOrFind(address _owner) internal returns (address _proxy) {
_proxy = vault721.getProxy(_owner);
if (_proxy == address(0)) {
Expand Down

0 comments on commit f2cf1d9

Please sign in to comment.