Omniscia Steadefi Audit
LendingPoolConfig Manual Review Findings
LendingPoolConfig Manual Review Findings
LPC-01M: Arbitrary Adjustment of Interest Rate Model
Type | Severity | Location |
---|---|---|
Centralization Concern | LendingPoolConfig.sol:L113-L125 |
Description:
The LendingPoolConfig::updateInterestRateModel
function permits the contract's owner to arbitrarily set the contract's various interest rate variables.
Example:
105/**106 * Updates lending pool interest rate model variables, callable only by owner107 @param _baseRate // Base interest rate when utilization rate is 0 in 1e18108 @param _multiplier // Multiplier of utilization rate that gives the slope of the interest rate in 1e18109 @param _jumpMultiplier // Multiplier after hitting a specified utilization point (kink2) in 1e18110 @param _kink1 // Utilization point at which the interest rate is fixed in 1e18111 @param _kink2 // Utilization point at which the jump multiplier is applied in 1e18112*/113function updateInterestRateModel(114 uint256 _baseRate,115 uint256 _multiplier,116 uint256 _jumpMultiplier,117 uint256 _kink1,118 uint256 _kink2119) external onlyOwner {120 baseRate = _baseRate;121 multiplier = _multiplier;122 jumpMultiplier = _jumpMultiplier;123 kink1 = _kink1;124 kink2 = _kink2;125}
Recommendation:
We advise the function to be solely invoke-able by a governance entity, preferably sitting behind a timelock function to permit existing users of the system to adequately react to a lending pool configurational change by i.e. repaying their loans while interest rates are low etc.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The Steadefi team has stated that they will implement a path to decentralization for all the deployed contracts within their ecosystem by utilizing a combination of a governance mechanism and a Timelock
contract. As such, we consider this exhibit alleviated based on the fact that the Steadefi team will implement a decentralization path in the near future.
LPC-02M: Inexplicable Per Second Interest Rate Calculation
Type | Severity | Location |
---|---|---|
Mathematical Operations | LendingPoolConfig.sol:L67 |
Description:
The LendingPoolConfig::interestRatePerSecond
function offsets the calculated interest rate by the SAFE_MULTIPLIER
(1e18
) prior to dividing by the SECONDS_PER_YEAR
.
Impact:
The interestRatePerSecond
is solely utilized in LendingPool::_pendingInterest
where the "double" 1e18
offset is in fact normalized via two divisions with SAFE_MULTIPLIER
. As such, this does not represent an active error in the mathematical calculations of the protocol. In any case, we advise this to be corrected optimizing the code's gas cost and minimizing its error surface.
Example:
60/**61 * Return the interest rate based on the utilization rate, per second62 * @param _debt Total borrowed amount63 * @param _floating Total available liquidity64 * @return ratePerSecond Current interest rate per second in 1e1865*/66function interestRatePerSecond(uint256 _debt, uint256 _floating) external view returns (uint256) {67 return _calculateInterestRate(_debt, _floating) * SAFE_MULTIPLIER / SECONDS_PER_YEAR;68}
Recommendation:
We advise the multiplication to be omitted as it represents an incorrect accuracy increase. An interest rate defined w/ 18
decimal places will retain the same accuracy when divided by the SECONDS_PER_YEAR
, meaning that multiplication by SAFE_MULTIPLIER
is incorrect.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The multiplication by SAFE_MULTIPLIER
has been removed as advised, ensuring that the LendingPoolConfig::interestRatePerSecond
function yields a value with an expectable precision.
LPC-03M: Inexistent Sanitization of Pool Configuration
Type | Severity | Location |
---|---|---|
Input Sanitization | LendingPoolConfig.sol:L34-L46, L113-L125 |
Description:
The lending pool's constructor
and configuration function (LendingPoolConfig::updateInterestRateModel
) do not apply any sanitization to their input arguments.
Impact:
A misconfigured lending pool configuration can lead to undesirable arbitrage attacks, ultimately hurting the pool's and consequently protocol's health.
Example:
27/**28 * @param _baseRate // Base interest rate when utilization rate is 0 in 1e1829 * @param _multiplier // Multiplier of utilization rate that gives the slope of the interest rate in 1e1830 * @param _jumpMultiplier // Multiplier after hitting a specified utilization point (kink2) in 1e1831 * @param _kink1 // Utilization point at which the interest rate is fixed in 1e1832 * @param _kink2 // Utilization point at which the jump multiplier is applied in 1e1833*/34constructor(35 uint256 _baseRate,36 uint256 _multiplier,37 uint256 _jumpMultiplier,38 uint256 _kink1,39 uint256 _kink240) {41 baseRate = _baseRate;42 multiplier = _multiplier;43 jumpMultiplier = _jumpMultiplier;44 kink1 = _kink1;45 kink2 = _kink2;46}
Recommendation:
We advise the input values to be sanitized per the business requirements of Steadefi. Some example sanitizations include mandating that _jumpMultiplier
is greater-than _multiplier
(based on LendingPoolConfig::_calculateInterestRate
), ensuring that the _kink1
/ _kink2
thresholds are at most equal to 1e18
(100% utilization) as well as the _baseRate
to be greater-than-or-equal-to a "zero" interest rate (i.e. 1e18
).
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The constructor
of the LendingPoolConfig
contract now makes use of the LendingPoolConfig::updateInterestRateModel
function which has had multiple require
checks introduced meant to sanitize incoming interest rate model adjustments. As a result, we consider this exhibit fully alleviated given that all interest rate model adjustments are sanitized as being valid.
LPC-04M: Incorrect Interest Rate Calculation
Type | Severity | Location |
---|---|---|
Logical Fault | LendingPoolConfig.sol:L89, L95, L100 |
Description:
The LendingPoolConfig::_calculateInterestRate
function will yield an incorrectly calculated interest rate if the utilizationRate
is exactly equal to kink2
which will be much higher than expected.
Impact:
The interest rate calculated when utilizationRate
is equivalent to kink2
is significantly higher than the expected one based on the calculations of the utilizationRate > kink2
conditional. Given that the utilization rate can be arbitrarily adjusted by a party using flash-loans, they can exploit this interest rate within the system to create artificial arbitrage opportunities.
Example:
72/**73 * Return the interest rate based on the utilization rate74 * @param _debt Total borrowed amount75 * @param _floating Total available liquidity76 * @return rate Current interest rate in 1e1877*/78function _calculateInterestRate(uint256 _debt, uint256 _floating) internal view returns (uint256) {79 if (_debt == 0 && _floating == 0) return 0;80
81 uint256 total = _debt + _floating;82 uint256 utilizationRate = _debt * SAFE_MULTIPLIER / total;83
84 // calculate borrow rate for slope up to kink 185 uint256 rate = baseRate + (utilizationRate * multiplier / SAFE_MULTIPLIER);86
87 // If utilization above kink2, return a higher interest rate88 // (base + rate + excess utilization above kink 2 * jumpMultiplier)89 if (utilizationRate > kink2) {90 return baseRate + (kink1 * multiplier / SAFE_MULTIPLIER)91 + ((utilizationRate - kink2) * jumpMultiplier / SAFE_MULTIPLIER);92 }93
94 // If utilization between kink1 and kink2, rates are flat95 if (kink1 < utilizationRate && utilizationRate < kink2) {96 return baseRate + (kink1 * multiplier / SAFE_MULTIPLIER);97 }98
99 // If utilization below kink1, return rate100 return rate;101}
Recommendation:
We advise the conditional of L95 to become inclusive in its comparison between utilizationRate
and kink2
, ensuring that the edge case of utilizationRate == kink2
is adequately handled by the function.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The LendingPoolConfig::_calculateInterestRate
function's referenced conditional was corrected to be inclusive with kink2
on the lower bound, optimizing its execution cost and correcting the rate's abnormal momentary increase that was present.