Skip to content

Commit

Permalink
Merge pull request #30 from Ion-Protocol/hrikb/L-08
Browse files Browse the repository at this point in the history
L-08, M-02 (OZ Audit)
  • Loading branch information
HrikB committed Jan 17, 2024
2 parents baee2f0 + 3675011 commit b1ee5d5
Show file tree
Hide file tree
Showing 17 changed files with 20,218 additions and 328 deletions.
20,000 changes: 20,000 additions & 0 deletions fuzz_out.txt

Large diffs are not rendered by default.

147 changes: 65 additions & 82 deletions src/IonPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import { SpotOracle } from "src/oracles/spot/SpotOracle.sol";
import { RewardModule } from "src/reward/RewardModule.sol";
import { InterestRate } from "src/InterestRate.sol";
import { WadRayMath, RAY } from "src/libraries/math/WadRayMath.sol";
import { IonPausableUpgradeable } from "src/admin/IonPausableUpgradeable.sol";

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";

contract IonPool is IonPausableUpgradeable, RewardModule {
import { safeconsole as console } from "forge-std/safeconsole.sol";

contract IonPool is PausableUpgradeable, RewardModule {
using SafeERC20 for IERC20;
using SafeCast for *;
using WadRayMath for *;
Expand Down Expand Up @@ -303,52 +305,20 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
}

/**
* @dev Pause actions that put the protocol into a further unsafe state.
* These are actions that take liquidity out of the system (e.g. borrowing,
* withdrawing base)
*/
function pauseUnsafeActions() external onlyRole(ION) {
_pause(Pauses.UNSAFE);
}

/**
* @dev Unpause actions that put the protocol into a further unsafe state.
*/
function unpauseUnsafeActions() external onlyRole(ION) {
_unpause(Pauses.UNSAFE);
}

/**
* @dev Pause actions that put the protocol into a further safe state.
* These are actions that put liquidity into the system (e.g. repaying,
* depositing base)
*
* This will also pause UNSAFE actions.
*
* Pausing accrual is also necessary with this since disabling repaying
* should not continue to accrue interest.
*
* Also accrues interest before the pause to update all debt at the time the
* pause takes place.
* @dev Pause actions but accrue interest as well.
*/
function pauseSafeActions() external onlyRole(ION) {
function pause() external onlyRole(ION) {
_accrueInterest();
_pause(Pauses.SAFE);
_pause(Pauses.UNSAFE);
_pause();
}

/**
* @dev Unpause actions that put the protocol into a further safe state.
*
* This will also unpause UNSAFE actions.
*
* Will also update the `lastRateUpdate` to the unpause transaction
* timestamp. This essentially allows for a pausing and unpausing of the
* accrual of interest.
* @dev Unpause actions but this will also update the `lastRateUpdate` to
* the unpause transaction timestamp. This essentially allows for a pausing
* and unpausing of the accrual of interest.
*/
function unpauseSafeActions() external onlyRole(ION) {
_unpause(Pauses.SAFE);
_unpause(Pauses.UNSAFE);
function unpause() external onlyRole(ION) {
_unpause();
IonPoolStorage storage $ = _getIonPoolStorage();

uint256 ilksLength = $.ilks.length;
Expand All @@ -367,20 +337,14 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* @dev Updates accumulators for all `ilk`s based on current interest rates.
* @return newTotalDebt the new total debt after interest accrual
*/
function accrueInterest() external whenNotPaused(Pauses.SAFE) returns (uint256 newTotalDebt) {
function accrueInterest() external whenNotPaused returns (uint256 newTotalDebt) {
return _accrueInterest();
}

function _accrueInterest() internal returns (uint256 newTotalDebt) {
IonPoolStorage storage $ = _getIonPoolStorage();

// Safe actions should really only be paused in conjunction with unsafe
// actions. However, if for some reason only safe actions were paused,
// it would still be possible to accrue interest by withdrawing and/or
// borrowing... so we prevent this outcome; but without reverting the tx
// altogether.
if (paused(Pauses.SAFE)) return ($.debt);
uint256 totalEthSupply = totalSupply();
uint256 totalEthSupply = totalSupplyUnaccrued();

uint256 totalSupplyFactorIncrease;
uint256 totalTreasuryMintAmount;
Expand Down Expand Up @@ -412,19 +376,30 @@ contract IonPool is IonPausableUpgradeable, RewardModule {

newTotalDebt = $.debt + totalDebtIncrease;
$.debt = newTotalDebt;
_setSupplyFactor(supplyFactor() + totalSupplyFactorIncrease);
_setSupplyFactor(supplyFactorUnaccrued() + totalSupplyFactorIncrease);
_mintToTreasury(totalTreasuryMintAmount);
}

function calculateRewardAndDebtDistribution() public view returns (uint256 totalSupplyFactorIncrease, uint256 totalTreasuryMintAmount, uint104[] memory rateIncreases, uint256 totalDebtIncrease, uint48[] memory timestampIncreases) {
function calculateRewardAndDebtDistribution()
public
view
override
returns (
uint256 totalSupplyFactorIncrease,
uint256 totalTreasuryMintAmount,
uint104[] memory rateIncreases,
uint256 totalDebtIncrease,
uint48[] memory timestampIncreases
)
{
IonPoolStorage storage $ = _getIonPoolStorage();

uint256 ilksLength = $.ilks.length;

rateIncreases = new uint104[](ilksLength);
timestampIncreases = new uint48[](ilksLength);

uint256 totalEthSupply = totalSupply();
uint256 totalEthSupply = totalSupplyUnaccrued();

for (uint8 i = 0; i < ilksLength;) {
(
Expand Down Expand Up @@ -456,14 +431,13 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* @return newRateIncrease the rate increase for the ilk.
* @return timestampIncrease the timestamp increase for the ilk.
*/
function calculateRewardAndDebtDistributionForIlk(uint8 ilkIndex) external view returns (uint104 newRateIncrease, uint48 timestampIncrease) {
(
,
,
newRateIncrease,
,
timestampIncrease
) = _calculateRewardAndDebtDistributionForIlk(ilkIndex, totalSupply());
function calculateRewardAndDebtDistributionForIlk(uint8 ilkIndex)
public
view
returns (uint104 newRateIncrease, uint48 timestampIncrease)
{
(,, newRateIncrease,, timestampIncrease) =
_calculateRewardAndDebtDistributionForIlk(ilkIndex, totalSupplyUnaccrued());
}

function _calculateRewardAndDebtDistributionForIlk(
Expand Down Expand Up @@ -515,7 +489,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
newDebtIncrease = _totalNormalizedDebt * newRateIncrease; // [RAD]

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

// If there is no supply, then nothing is being lent out.
supplyFactorIncrease = _normalizedTotalSupply == 0
Expand All @@ -536,7 +510,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* asset should be sent to.
* @param amount of underlying to reedeem for.
*/
function withdraw(address receiverOfUnderlying, uint256 amount) external whenNotPaused(Pauses.UNSAFE) {
function withdraw(address receiverOfUnderlying, uint256 amount) external whenNotPaused {
uint256 newTotalDebt = _accrueInterest();
IonPoolStorage storage $ = _getIonPoolStorage();

Expand All @@ -561,7 +535,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
bytes32[] calldata proof
)
external
whenNotPaused(Pauses.SAFE)
whenNotPaused
onlyWhitelistedLenders(proof)
{
uint256 newTotalDebt = _accrueInterest();
Expand Down Expand Up @@ -595,7 +569,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
bytes32[] calldata proof
)
external
whenNotPaused(Pauses.UNSAFE)
whenNotPaused
onlyWhitelistedBorrowers(ilkIndex, proof)
{
_accrueInterest();
Expand All @@ -619,7 +593,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
uint256 amountOfNormalizedDebt
)
external
whenNotPaused(Pauses.SAFE)
whenNotPaused
{
_accrueInterest();
(uint104 ilkRate, uint256 newDebt) =
Expand All @@ -642,7 +616,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
uint256 amount
)
external
whenNotPaused(Pauses.UNSAFE)
whenNotPaused
{
_accrueInterest();
_modifyPosition(ilkIndex, user, recipient, address(0), -(amount.toInt256()), 0);
Expand All @@ -666,7 +640,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
bytes32[] calldata proof
)
external
whenNotPaused(Pauses.SAFE)
whenNotPaused
onlyWhitelistedBorrowers(ilkIndex, proof)
{
_accrueInterest();
Expand Down Expand Up @@ -766,7 +740,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* @param user the address that owns the bad debt being paid off
* @param rad amount of debt to be repaid (45 decimals)
*/
function repayBadDebt(address user, uint256 rad) external whenNotPaused(Pauses.SAFE) {
function repayBadDebt(address user, uint256 rad) external whenNotPaused {
IonPoolStorage storage $ = _getIonPoolStorage();

$.unbackedDebt[user] -= rad;
Expand Down Expand Up @@ -828,7 +802,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
int256 changeInNormalizedDebt
)
external
whenNotPaused(Pauses.UNSAFE)
whenNotPaused
onlyRole(LIQUIDATOR_ROLE)
{
_accrueInterest();
Expand Down Expand Up @@ -862,14 +836,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* @param usr user
* @param wad amount to add or remove
*/
function mintAndBurnGem(
uint8 ilkIndex,
address usr,
int256 wad
)
external
onlyRole(GEM_JOIN_ROLE)
{
function mintAndBurnGem(uint8 ilkIndex, address usr, int256 wad) external onlyRole(GEM_JOIN_ROLE) {
IonPoolStorage storage $ = _getIonPoolStorage();

$.gem[ilkIndex][usr] = _add($.gem[ilkIndex][usr], wad);
Expand All @@ -884,7 +851,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
* @param dst destination of the gem
* @param wad amount of gem
*/
function transferGem(uint8 ilkIndex, address src, address dst, uint256 wad) external whenNotPaused(Pauses.UNSAFE) {
function transferGem(uint8 ilkIndex, address src, address dst, uint256 wad) external whenNotPaused {
if (!isAllowed(src, _msgSender())) revert GemTransferWithoutConsent(ilkIndex, src, _msgSender());

IonPoolStorage storage $ = _getIonPoolStorage();
Expand Down Expand Up @@ -949,12 +916,20 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
return $.ilks[ilkIndex].totalNormalizedDebt;
}

function rateUnaccrued(uint8 ilkIndex) external view returns (uint256) {
IonPoolStorage storage $ = _getIonPoolStorage();
return $.ilks[ilkIndex].rate;
}

/**
* @return The rate (debt accumulator) for collateral with index `ilkIndex`.
*/
function rate(uint8 ilkIndex) external view returns (uint256) {
IonPoolStorage storage $ = _getIonPoolStorage();
return $.ilks[ilkIndex].rate;

(uint256 newRateIncrease,) = calculateRewardAndDebtDistributionForIlk(ilkIndex);

return $.ilks[ilkIndex].rate + newRateIncrease;
}

/**
Expand Down Expand Up @@ -1048,13 +1023,21 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
return either(user == operator, $.isOperator[user][operator] == 1);
}

function debtUnaccrued() external view returns (uint256) {
IonPoolStorage storage $ = _getIonPoolStorage();
return $.debt;
}

/**
* @dev This includes unbacked debt.
* @return The total amount of debt.
*/
function debt() external view returns (uint256) {
IonPoolStorage storage $ = _getIonPoolStorage();
return $.debt;

(,,, uint256 totalDebtIncrease,) = calculateRewardAndDebtDistribution();

return $.debt + totalDebtIncrease;
}

/**
Expand Down Expand Up @@ -1095,7 +1078,7 @@ contract IonPool is IonPausableUpgradeable, RewardModule {
function getCurrentBorrowRate(uint8 ilkIndex) public view returns (uint256 borrowRate, uint256 reserveFactor) {
IonPoolStorage storage $ = _getIonPoolStorage();

uint256 totalEthSupply = totalSupply();
uint256 totalEthSupply = totalSupplyUnaccrued();
uint256 _totalNormalizedDebt = $.ilks[ilkIndex].totalNormalizedDebt;
uint256 _rate = $.ilks[ilkIndex].rate;

Expand Down

0 comments on commit b1ee5d5

Please sign in to comment.