Skip to content
This repository has been archived by the owner on Aug 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #33 from ionicprotocol/feat/net-apr-user
Browse files Browse the repository at this point in the history
Net apr user
  • Loading branch information
carlomazzaferro authored Sep 13, 2023
2 parents 7fde050 + 826ce83 commit 38b45d4
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 78 deletions.
4 changes: 4 additions & 0 deletions contracts/ionic/levered/LeveredPositionsLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ contract LeveredPositionsLens is Initializable {
factory = _factory;
}

function reinitialize(ILeveredPositionFactory _factory) external reinitializer(2) {
factory = _factory;
}

/// @notice this is a lens fn, it is not intended to be used on-chain
/// @dev returns lists of the market addresses, names and symbols of the underlying assets of those collateral markets that are whitelisted
function getCollateralMarkets()
Expand Down
97 changes: 97 additions & 0 deletions contracts/ionic/strategies/flywheel/IonicFlywheelLensRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,103 @@ contract IonicFlywheelLensRouter {
return apr;
}

function getRewardsAprForMarket(ICErc20 market) internal returns (int256 totalMarketRewardsApr) {
IonicComptroller comptroller = market.comptroller();
BasePriceOracle oracle = comptroller.oracle();
uint256 underlyingPrice = oracle.getUnderlyingPrice(market);

address[] memory flywheels = comptroller.getAccruingFlywheels();
for (uint256 j = 0; j < flywheels.length; j++) {
IonicFlywheelCore flywheel = IonicFlywheelCore(flywheels[j]);
ERC20 rewardToken = flywheel.rewardToken();

uint256 rewardSpeedPerSecondPerToken = getRewardSpeedPerSecondPerToken(
flywheel,
market,
uint256(rewardToken.decimals())
);

uint256 marketApr = getApr(
rewardSpeedPerSecondPerToken,
oracle.price(address(rewardToken)),
underlyingPrice,
market.exchangeRateCurrent()
);

totalMarketRewardsApr += int256(marketApr);
}
}

function getUserNetValueDeltaForMarket(
address user,
ICErc20 market,
int256 offchainApr,
int256 blocksPerYear
) internal returns (int256) {
IonicComptroller comptroller = market.comptroller();
BasePriceOracle oracle = comptroller.oracle();
int256 netApr = getRewardsAprForMarket(market) +
getUserInterestAprForMarket(user, market, blocksPerYear) +
offchainApr;
return (netApr * int256(market.balanceOfUnderlying(user)) * int256(oracle.getUnderlyingPrice(market))) / 1e36;
}

function getUserInterestAprForMarket(
address user,
ICErc20 market,
int256 blocksPerYear
) internal returns (int256) {
return (int256(market.supplyRatePerBlock()) - int256(market.borrowRatePerBlock())) * blocksPerYear;
}

struct AdjustedUserNetAprVars {
int256 userNetAssetsValue;
int256 userNetValueDelta;
BasePriceOracle oracle;
ICErc20[] markets;
IonicComptroller pool;
}

function getAdjustedUserNetApr(
address user,
int256 blocksPerYear,
address[] memory offchainRewardsAprMarkets,
int256[] memory offchainRewardsAprs
) public returns (int256) {
AdjustedUserNetAprVars memory vars;

(, PoolDirectory.Pool[] memory pools) = fpd.getActivePools();
for (uint256 i = 0; i < pools.length; i++) {
IonicComptroller pool = IonicComptroller(pools[i].comptroller);
vars.oracle = pool.oracle();
vars.markets = pool.getAllMarkets();
for (uint256 j = 0; j < vars.markets.length; j++) {
int256 offchainRewardsApr = 0;
for (uint256 k = 0; k < offchainRewardsAprMarkets.length; k++) {
if (offchainRewardsAprMarkets[k] == address(vars.markets[j])) offchainRewardsApr = offchainRewardsAprs[k];
}
vars.userNetAssetsValue +=
int256(vars.markets[j].balanceOfUnderlying(user) * vars.oracle.getUnderlyingPrice(vars.markets[j])) /
1e18;
vars.userNetValueDelta += getUserNetValueDeltaForMarket(
user,
vars.markets[j],
offchainRewardsApr,
blocksPerYear
);
}
}

if (vars.userNetAssetsValue == 0) return 0;
else return (vars.userNetValueDelta * 1e18) / vars.userNetAssetsValue;
}

function getUserNetApr(address user, int256 blocksPerYear) external returns (int256) {
address[] memory emptyAddrArray = new address[](0);
int256[] memory emptyIntArray = new int256[](0);
return getAdjustedUserNetApr(user, blocksPerYear, emptyAddrArray, emptyIntArray);
}

function getAllRewardTokens() public view returns (address[] memory uniqueRewardTokens) {
(, PoolDirectory.Pool[] memory pools) = fpd.getActivePools();

Expand Down
14 changes: 14 additions & 0 deletions contracts/test/FLRTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,18 @@ contract FLRTest is BaseTest {
IonicFlywheelLensRouter router = IonicFlywheelLensRouter(0x3391ed1C5203168337Fa827cB5Ac8BB8B60D93B7);
router.getPoolMarketRewardsInfo(IonicComptroller(0x044c436b2f3EF29D30f89c121f9240cf0a08Ca4b));
}

function testNetAprPolygon() public fork(POLYGON_MAINNET) {
address user = 0x8982aa50bb919E42e9204f12e5b59D053Eb2A602;
int256 blocks = 26 * 24 * 365 * 60;
int256 apr = lensRouter.getUserNetApr(user, blocks);
emit log_named_int("apr", apr);
}

function testNetAprChapel() public fork(BSC_CHAPEL) {
address user = 0x8982aa50bb919E42e9204f12e5b59D053Eb2A602;
int256 blocks = 26 * 24 * 365 * 60;
int256 apr = lensRouter.getUserNetApr(user, blocks);
emit log_named_int("apr", apr);
}
}
90 changes: 12 additions & 78 deletions contracts/test/LeveredPositionTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ contract LeveredPositionLensTest is BaseTest {
emit log("");
}
}

function testPrintLeveredPositions() public fork(POLYGON_MAINNET) {
address[] memory markets = factory.getWhitelistedCollateralMarkets();

emit log_named_array("markets", markets);

for (uint256 j = 0; j < markets.length; j++) {
address[] memory borrowable = factory.getBorrowableMarketsByCollateral(ICErc20(markets[j]));
emit log_named_array("borrowable", borrowable);
}
}
}

contract LeveredPositionFactoryTest is BaseTest {
Expand All @@ -105,24 +116,6 @@ contract LeveredPositionFactoryTest is BaseTest {
abi.encode(borrowRate / factory.blocksPerYear())
);

{
// upgrade the factory
LeveredPositionFactoryFirstExtension newExt1 = new LeveredPositionFactoryFirstExtension();
LeveredPositionFactorySecondExtension newExt2 = new LeveredPositionFactorySecondExtension();

vm.startPrank(factory.owner());
DiamondBase asBase = DiamondBase(address(factory));
address[] memory oldExts = asBase._listExtensions();
if (oldExts.length == 1) {
asBase._registerExtension(newExt1, DiamondExtension(oldExts[0]));
asBase._registerExtension(newExt2, DiamondExtension(address(0)));
} else if (oldExts.length == 2) {
asBase._registerExtension(newExt1, DiamondExtension(oldExts[0]));
asBase._registerExtension(newExt2, DiamondExtension(oldExts[1]));
}
vm.stopPrank();
}

uint256 _borrowRate = _stableMarket.borrowRatePerBlock() * factory.blocksPerYear();
emit log_named_uint("_borrowRate", _borrowRate);

Expand Down Expand Up @@ -525,65 +518,6 @@ contract Jbrl2BrlLeveredPositionTest is LeveredPositionTest {
}
}

contract Par2EurLeveredPositionTest is LeveredPositionTest {
function setUp() public fork(POLYGON_MAINNET) {}

function afterForkSetUp() internal override {
super.afterForkSetUp();

uint256 depositAmount = 2000e18;

address twoEurMarket = 0x1944FA4a490f85Ed99e2c6fF9234F94DE16fdbde;
address parMarket = 0xCA1A940B02E15FF71C128f877b29bdb739785299;
address twoEurWhale = address(888);
address balancer = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
address parWhale = 0xFa22D298E3b0bc1752E5ef2849cEc1149d596674; // uniswap pool

IERC20Upgradeable twoEur = IERC20Upgradeable(ICErc20(twoEurMarket).underlying());
vm.prank(balancer);
twoEur.transfer(twoEurWhale, 80 * depositAmount);

_configurePair(twoEurMarket, parMarket);
_fundMarketAndSelf(ICErc20(twoEurMarket), twoEurWhale);
_fundMarketAndSelf(ICErc20(parMarket), parWhale);

(position, maxLevRatio, minLevRatio) = _openLeveredPosition(address(this), depositAmount);
}
}

contract MaticXMaticXBbaWMaticLeveredPositionTest is LeveredPositionTest {
function setUp() public fork(POLYGON_MAINNET) {}

function afterForkSetUp() internal override {
super.afterForkSetUp();

uint256 depositAmount = 1000e18;

address maticXBbaWMaticMarket = 0x13e763D25D78c3Fd6FEA534231BdaEBE7Fa52945;
address maticXMarket = 0x0db51E5255E44751b376738d8979D969AD70bff6;
address maticXBbaWMaticWhale = 0xB0B28d7A74e62DF5F6F9E0d9Ae0f4e7982De9585;
address maticXWhale = 0x72f0275444F2aF8dBf13F78D54A8D3aD7b6E68db;

IonicComptroller pool = IonicComptroller(ICErc20(maticXBbaWMaticMarket).comptroller());
_configurePairAndLiquidator(maticXBbaWMaticMarket, maticXMarket, new BalancerSwapLiquidator());

{
vm.prank(pool.admin());
pool._supplyCapWhitelist(address(maticXBbaWMaticMarket), maticXBbaWMaticWhale, true);
}

_fundMarketAndSelf(ICErc20(maticXBbaWMaticMarket), maticXBbaWMaticWhale);
_fundMarketAndSelf(ICErc20(maticXMarket), maticXWhale);

(position, maxLevRatio, minLevRatio) = _openLeveredPosition(address(this), depositAmount);

{
vm.prank(pool.admin());
pool._supplyCapWhitelist(address(maticXBbaWMaticMarket), address(position), true);
}
}
}

contract BombTDaiLeveredPositionTest is LeveredPositionTest {
uint256 depositAmount = 100e18;
address whale = 0xe7B7dF67C1fe053f1C6B965826d3bFF19603c482;
Expand Down Expand Up @@ -964,7 +898,7 @@ contract RetroCashAUsdcWethLeveredPositionTest is LeveredPositionTest {
function afterForkSetUp() internal override {
super.afterForkSetUp();

uint256 depositAmount = 8e18;
uint256 depositAmount = 2e18;

// LP token underlying xUSDC-WETH05
address lpTokenMarket = 0xC7cA03A0bE1dBAc350E5BfE5050fC5af6406490E;
Expand Down

0 comments on commit 38b45d4

Please sign in to comment.