Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WILL NOT MERGE -- Revert to rebasing #87

Merged
merged 2 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/IonPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ contract IonPool is PausableUpgradeable, RewardToken {
function _accrueInterest() internal returns (uint256 newTotalDebt) {
IonPoolStorage storage $ = _getIonPoolStorage();

uint256 totalEthSupply = getTotalUnderlyingClaimsUnaccrued();
uint256 totalEthSupply = totalSupplyUnaccrued();

uint256 totalSupplyFactorIncrease;
uint256 totalTreasuryMintAmount;
Expand Down Expand Up @@ -419,7 +419,7 @@ contract IonPool is PausableUpgradeable, RewardToken {
rateIncreases = new uint104[](ilksLength);
timestampIncreases = new uint48[](ilksLength);

uint256 totalEthSupply = getTotalUnderlyingClaimsUnaccrued();
uint256 totalEthSupply = totalSupplyUnaccrued();

for (uint8 i = 0; i < ilksLength;) {
(
Expand Down Expand Up @@ -457,7 +457,7 @@ contract IonPool is PausableUpgradeable, RewardToken {
returns (uint104 newRateIncrease, uint48 timestampIncrease)
{
(,, newRateIncrease,, timestampIncrease) =
_calculateRewardAndDebtDistributionForIlk(ilkIndex, getTotalUnderlyingClaimsUnaccrued());
_calculateRewardAndDebtDistributionForIlk(ilkIndex, totalSupplyUnaccrued());
}

function _calculateRewardAndDebtDistributionForIlk(
Expand Down Expand Up @@ -512,7 +512,7 @@ contract IonPool is PausableUpgradeable, RewardToken {
newDebtIncrease = _totalNormalizedDebt * newRateIncrease; // [RAD]

// Income distribution
uint256 _normalizedTotalSupply = totalSupplyUnaccrued(); // [WAD]
uint256 _normalizedTotalSupply = normalizedTotalSupplyUnaccrued(); // [WAD]

// If there is no supply, then nothing is being lent out.
supplyFactorIncrease = _normalizedTotalSupply == 0
Expand Down Expand Up @@ -570,7 +570,7 @@ contract IonPool is PausableUpgradeable, RewardToken {

uint256 _supplyCap = $.supplyCap;

if (getTotalUnderlyingClaims() > _supplyCap) revert DepositSurpassesSupplyCap(amount, _supplyCap);
if (totalSupply() > _supplyCap) revert DepositSurpassesSupplyCap(amount, _supplyCap);

emit Supply(user, _msgSender(), amount, _supplyFactor, newTotalDebt);
}
Expand Down Expand Up @@ -954,7 +954,7 @@ contract IonPool is PausableUpgradeable, RewardToken {
function getCurrentBorrowRate(uint8 ilkIndex) external view returns (uint256 borrowRate, uint256 reserveFactor) {
IonPoolStorage storage $ = _getIonPoolStorage();

uint256 totalEthSupply = getTotalUnderlyingClaimsUnaccrued();
uint256 totalEthSupply = totalSupplyUnaccrued();

uint256 _totalNormalizedDebt = $.ilks[ilkIndex].totalNormalizedDebt;
uint256 _rate = $.ilks[ilkIndex].rate;
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/IIonPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,5 @@ interface IIonPool {
function getTotalUnderlyingClaims() external view returns (uint256);
function getUnderlyingClaimOf(address user) external view returns (uint256);
function extsload(bytes32 slot) external view returns (bytes32);
function balanceOfUnaccrued(address user) external view returns (uint256);
}
33 changes: 20 additions & 13 deletions src/token/RewardToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,6 @@ abstract contract RewardToken is
$._normalizedBalances[from] = oldSenderBalance - amountNormalized;
}
$._normalizedBalances[to] += amountNormalized;

emit Transfer(from, to, amountNormalized);
}

/**
Expand Down Expand Up @@ -450,22 +448,30 @@ abstract contract RewardToken is
}

/**
* @dev Current token balance
* @dev Current claim of the underlying token inclusive of interest to be accrued.
* @param user to get balance of
*/
function getUnderlyingClaimOf(address user) public view returns (uint256) {
function balanceOf(address user) public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();

(uint256 totalSupplyFactorIncrease,,,,) = calculateRewardAndDebtDistribution();

return $._normalizedBalances[user].rayMulDown($.supplyFactor + totalSupplyFactorIncrease);
}

/**
* @dev Current claim of the underlying token without accounting for interest to be accrued.
*/
function balanceOfUnaccrued(address user) public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();
return $._normalizedBalances[user].rayMulDown($.supplyFactor);
}

/**
* @dev Accounting is done in normalized balances
* @param user to get normalized balance of
*/
function balanceOf(address user) external view returns (uint256) {
function normalizedBalanceOf(address user) external view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();
return $._normalizedBalances[user];
}
Expand Down Expand Up @@ -494,7 +500,10 @@ abstract contract RewardToken is
return $.treasury;
}

function getTotalUnderlyingClaimsUnaccrued() public view returns (uint256) {
/**
* @dev Total claim of the underlying asset belonging to lenders not inclusive of the new interest to be accrued.
*/
function totalSupplyUnaccrued() public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();

uint256 _normalizedTotalSupply = $.normalizedTotalSupply;
Expand All @@ -507,9 +516,9 @@ abstract contract RewardToken is
}

/**
* @dev Total claim of the underlying asset belonging to lenders.
* @dev Total claim of the underlying asset belonging to lender inclusive of the new interest to be accrued.
*/
function getTotalUnderlyingClaims() public view returns (uint256) {
function totalSupply() public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();

uint256 _normalizedTotalSupply = $.normalizedTotalSupply;
Expand All @@ -523,17 +532,15 @@ abstract contract RewardToken is
return _normalizedTotalSupply.rayMulDown($.supplyFactor + totalSupplyFactorIncrease);
}

function totalSupplyUnaccrued() public view returns (uint256) {
function normalizedTotalSupplyUnaccrued() public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();
return $.normalizedTotalSupply;
}

/**
* @dev Current total supply
*
* Normalized total supply and total supply are same in non-rebasing token.
* @dev Normalized total supply.
*/
function totalSupply() public view returns (uint256) {
function normalizedTotalSupply() public view returns (uint256) {
RewardTokenStorage storage $ = _getRewardTokenStorage();

(uint256 totalSupplyFactorIncrease, uint256 totalTreasuryMintAmount,,,) = calculateRewardAndDebtDistribution();
Expand Down
38 changes: 25 additions & 13 deletions src/vault/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { ReentrancyGuard } from "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
import { AccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";

/**
* @title Ion Lending Vault
* @author Molecular Labs
Expand All @@ -28,6 +27,7 @@ import { AccessControlDefaultAdminRules } from
*
* @custom:security-contact [email protected]
*/

contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, ReentrancyGuard {
using EnumerableSet for EnumerableSet.AddressSet;
using Math for uint256;
Expand Down Expand Up @@ -115,6 +115,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
*/
function updateFeePercentage(uint256 _feePercentage) external onlyRole(OWNER_ROLE) {
if (_feePercentage > RAY) revert InvalidFeePercentage();
_accrueFee();
feePercentage = _feePercentage;
}

Expand Down Expand Up @@ -197,10 +198,10 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
if (pool == IDLE) {
if (BASE_ASSET.balanceOf(address(this)) != 0) revert InvalidIdleMarketRemovalNonZeroBalance();
} else {
// Checks `balanceOf` as it may be possible that
// `getUnderlyingClaimOf` returns zero even though the
// Checks `normalizedBalanceOf` as it may be possible that
// `balanceOf` returns zero even though the
// `normalizedBalance` is zero.
if (pool.balanceOf(address(this)) != 0) revert InvalidMarketRemovalNonZeroSupply(pool);
if (pool.normalizedBalanceOf(address(this)) != 0) revert InvalidMarketRemovalNonZeroSupply(pool);
BASE_ASSET.approve(address(pool), 0);
}

Expand Down Expand Up @@ -323,7 +324,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
MarketAllocation calldata allocation = allocations[i];
IIonPool pool = allocation.pool;

uint256 currentSupplied = pool == IDLE ? currentIdleDeposits : pool.getUnderlyingClaimOf(address(this));
uint256 currentSupplied = pool == IDLE ? currentIdleDeposits : pool.balanceOf(address(this));
int256 assets = allocation.assets; // to deposit or withdraw

// if `assets` is `type(int256).min`, this means fully withdraw from the market.
Expand All @@ -343,6 +344,8 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
// to the user from the previous function scope.
if (pool != IDLE) {
pool.withdraw(address(this), transferAmt);
} else {
currentIdleDeposits -= transferAmt;
}

totalWithdrawn += transferAmt;
Expand Down Expand Up @@ -372,6 +375,8 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
// contract.
if (pool != IDLE) {
pool.supply(address(this), transferAmt, new bytes32[](0));
} else {
currentIdleDeposits += transferAmt;
}

totalSupplied += transferAmt;
Expand Down Expand Up @@ -645,7 +650,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy

/**
* @notice Returns the total claim that the vault has across all supported IonPools.
* @dev `IonPool.getUnderlyingClaimOf` returns the rebasing balance of the
* @dev `IonPool.balanceOf` returns the rebasing balance of the
* lender receipt token that is pegged 1:1 to the underlying supplied asset.
* @return assets The total assets held on the contract and inside the underlying
* pools by this vault.
Expand All @@ -655,8 +660,16 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
for (uint256 i; i != _supportedMarketsLength;) {
IIonPool pool = IIonPool(supportedMarkets.at(i));

uint256 assetsInPool =
pool == IDLE ? BASE_ASSET.balanceOf(address(this)) : pool.getUnderlyingClaimOf(address(this));
uint256 assetsInPool;
if (pool == IDLE) {
assetsInPool = BASE_ASSET.balanceOf(address(this));
} else {
if (pool.paused()) {
assetsInPool = pool.balanceOfUnaccrued(address(this));
} else {
assetsInPool = pool.balanceOf(address(this));
}
}

assets += assetsInPool;

Expand Down Expand Up @@ -763,7 +776,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
(feeShares, newTotalAssets) = _accruedFeeShares();
if (feeShares != 0) _mint(feeRecipient, feeShares);

lastTotalAssets = newTotalAssets; // This update happens outside of this function in Metamorpho.
lastTotalAssets = newTotalAssets;

emit FeeAccrued(feeShares, newTotalAssets);
}
Expand Down Expand Up @@ -897,7 +910,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
* @return The max amount of assets withdrawable from this IonPool.
*/
function _withdrawable(IIonPool pool) internal view returns (uint256) {
uint256 currentSupplied = pool.getUnderlyingClaimOf(address(this));
uint256 currentSupplied = pool.balanceOf(address(this));
uint256 availableLiquidity = uint256(pool.extsload(ION_POOL_LIQUIDITY_SLOT));

return Math.min(currentSupplied, availableLiquidity);
Expand All @@ -910,9 +923,8 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
* @return The max amount of assets depositable to this IonPool.
*/
function _depositable(IIonPool pool) internal view returns (uint256) {
uint256 allocationCapDiff = _zeroFloorSub(caps[pool], pool.getUnderlyingClaimOf(address(this)));
uint256 supplyCapDiff =
_zeroFloorSub(uint256(pool.extsload(ION_POOL_SUPPLY_CAP_SLOT)), pool.getTotalUnderlyingClaims());
uint256 allocationCapDiff = _zeroFloorSub(caps[pool], pool.balanceOf(address(this)));
uint256 supplyCapDiff = _zeroFloorSub(uint256(pool.extsload(ION_POOL_SUPPLY_CAP_SLOT)), pool.totalSupply());

return Math.min(allocationCapDiff, supplyCapDiff);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fork/fuzz/lrt/EtherFiLibrary.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ contract EtherFiLibrary_FuzzTest is Test {

function testForkFuzz_GetLstAmountOutForEthAmountIn(uint256 ethAmount) external {
vm.assume(ethAmount != 0);
vm.assume(ethAmount < type(uint128).max);
vm.assume(ethAmount < type(uint96).max);

uint256 lrtAmountOut = EtherFiLibrary.getLstAmountOutForEthAmountIn(WEETH_ADDRESS, ethAmount);

Expand Down
11 changes: 4 additions & 7 deletions test/integration/concrete/WeEthIonPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ contract WeEthIonPool_IntegrationTest is WeEthIonPoolSharedSetup {
ionPool.supply(lenderA, lenderAFirstSupplyAmount, lenderProofs[0]);
vm.stopPrank();

assertEq(ionPool.getUnderlyingClaimOf(lenderA), lenderAFirstSupplyAmount, "lender balance after 1st supply");
assertEq(ionPool.balanceOf(lenderA), lenderAFirstSupplyAmount, "lender balance after 1st supply");
assertEq(lens.liquidity(iIonPool), lenderAFirstSupplyAmount, "liquidity after 1st supply");

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down Expand Up @@ -172,10 +172,7 @@ contract WeEthIonPool_IntegrationTest is WeEthIonPoolSharedSetup {
uint256 roundingError = ionPool.supplyFactor() / 1e27;

assertApproxEqAbs(
ionPool.getUnderlyingClaimOf(lenderB),
lenderBFirstSupplyAmount,
roundingError,
"lenderB balance after 1st supply"
ionPool.balanceOf(lenderB), lenderBFirstSupplyAmount, roundingError, "lenderB balance after 1st supply"
);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down Expand Up @@ -219,14 +216,14 @@ contract WeEthIonPool_IntegrationTest is WeEthIonPoolSharedSetup {
vm.startPrank(lenderA);
ionPool.withdraw(lenderA, lender1WithdrawAmountFail);

uint256 lenderABalanceBefore = ionPool.getUnderlyingClaimOf(lenderA);
uint256 lenderABalanceBefore = ionPool.balanceOf(lenderA);

uint256 lender1WithdrawAmount = 10e18;
ionPool.withdraw(lenderA, lender1WithdrawAmount);
vm.stopPrank();

assertEq(
ionPool.getUnderlyingClaimOf(lenderA),
ionPool.balanceOf(lenderA),
lenderABalanceBefore - lender1WithdrawAmount,
"lenderA balance after 1st withdrawal"
);
Expand Down
11 changes: 7 additions & 4 deletions test/invariant/IonPool/ActorManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ contract IonPool_InvariantTest is IonPoolSharedSetup {

function invariant_LenderDepositsAddToBalance() external returns (bool) {
for (uint256 i = 0; i < lenders.length; i++) {
assertEq(lenders[i].totalHoldingsNormalized(), ionPool.balanceOf(address(lenders[i])));
assertEq(lenders[i].totalHoldingsNormalized(), ionPool.normalizedBalanceOf(address(lenders[i])));
}

return !failed();
Expand All @@ -236,9 +236,12 @@ contract IonPool_InvariantTest is IonPoolSharedSetup {
function invariant_LenderBalancesPlusTreasuryAddToTotalSupply() external returns (bool) {
uint256 totalLenderNormalizedBalances;
for (uint256 i = 0; i < lenders.length; i++) {
totalLenderNormalizedBalances += ionPool.balanceOf(address(lenders[i]));
totalLenderNormalizedBalances += ionPool.normalizedBalanceOf(address(lenders[i]));
}
assertEq(totalLenderNormalizedBalances + ionPool.balanceOf(TREASURY), ionPool.totalSupplyUnaccrued());
assertEq(
totalLenderNormalizedBalances + ionPool.normalizedBalanceOf(TREASURY),
ionPool.normalizedTotalSupplyUnaccrued()
);

return !failed();
}
Expand All @@ -256,7 +259,7 @@ contract IonPool_InvariantTest is IonPoolSharedSetup {
assertGe(lens.liquidity(iIonPool) + totalDebt, ionPool.totalSupplyUnaccrued());
assertGe(
lens.liquidity(iIonPool).scaleUpToRad(18) + lens.debtUnaccrued(iIonPool),
ionPool.totalSupplyUnaccrued() * ionPool.supplyFactorUnaccrued()
ionPool.normalizedTotalSupplyUnaccrued() * ionPool.supplyFactorUnaccrued()
);

return !failed();
Expand Down
8 changes: 4 additions & 4 deletions test/invariant/RewardToken/ActorManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,20 @@ contract RewardToken_InvariantTest is RewardTokenSharedSetup {
uint256 totalSupplyByBalances;
for (uint256 i = 0; i < userHandlers.length; i++) {
UserHandler user = userHandlers[i];
totalSupplyByBalances += rewardModule.balanceOf(address(user));
totalSupplyByBalances += rewardModule.normalizedBalanceOf(address(user));
}

underlying.balanceOf(address(rewardModule)); // update underlying balance
rewardModule.totalSupply();

assertEq(rewardModule.totalSupply(), totalSupplyByBalances);
assertEq(rewardModule.normalizedTotalSupply(), totalSupplyByBalances);
}

function invariant_lenderClaimAlwaysBacked() external {
uint256 lenderClaim = rewardModule.getTotalUnderlyingClaims();
uint256 totalSupply = rewardModule.totalSupply();

uint256 underlyingBalance = underlying.balanceOf(address(rewardModule));

assertGe(underlyingBalance, lenderClaim);
assertGe(underlyingBalance, totalSupply);
}
}
Loading
Loading