Omniscia Kanpeki Finance Audit

RewardManager Manual Review Findings

RewardManager Manual Review Findings

RMR-01M: Ineffectual Cap Increase

TypeSeverityLocation
Logical FaultMediumRewardManager.sol:L197, L265, L286, L291

Description:

The _calcReward incentivizes discounted borrowers to make large debts by increasing the total reward they are able to receive, however, the double reward cap is only utilized for the assignment of the reward the user is meant to receive and is not actually utilized for the reward payout meaning that a user will never receive the fully eligible payout they initially calculated they will receive.

Example:

contracts/managers/RewardManager.sol
245/**
246 * calculates the KAE reward for a Debt
247 *
248 * reward calculation is basically: Math.min((multiplier * debtInUSD), reward cap)
249 * -> multiplier = (interestRate - 1)%
250 * -> reward cap = currentRewardCap
251 *
252 * if (isBorrowerDiscounted && debtInUSD > $50K)
253 * -> rewardCap = currentRewardCap * 2
254 *
255 * @param interestRate Debt's interest rate in basis point but must be converted to 1e18
256 */
257function _calcReward (uint256 debtInUSD, uint256 interestRate, bool isDiscountedBorrower) internal view returns (uint256)
258{
259 uint256 reward = _calcScaledPercentOf(debtInUSD, _calcPriceAdjustedMultiplier(interestRate - 100));
260
261 // 50_000e18 = $50K in DAI
262 if (isDiscountedBorrower && debtInUSD > 50_000e18)
263 {
264 // 50 = 0.5% in basis point; first converts basis point to 1e18; 0.5% of Debt
265 return Math.min(reward + _calcScaledPercentOf(debtInUSD, _calcPriceAdjustedMultiplier(50)), _config.currentRewardCap * 2);
266 }
267 else
268 {
269 return Math.min(reward, _config.currentRewardCap);
270 }
271}
272
273// front-end
274function calcReward (address debtToken, uint256 debt, uint256 interestRate, bool isDiscountedBorrower) external view returns (uint256)
275{
276 return _calcReward(IOracle(_ADDRESSES.oracle()).convertToUSD(debtToken, debt), interestRate, isDiscountedBorrower);
277}
278
279// @param interestRate Debt interest in basis point but must be converted to 1e18
280function registerDebt (bytes32 debtID, uint256 debtInUSD, uint256 interestRate, bool isDiscountedBorrower) external override
281{
282 require(hasRole(REWARDER_ROLE, msg.sender), "!rewarder");
283 require(_reward[debtID] == 0, "reg'd");
284
285
286 uint256 reward = _calcReward(debtInUSD, interestRate, isDiscountedBorrower);
287
288 require(reward > 0 && reward < type(uint80).max, "bad reward");
289
290
291 _reward[debtID] = uint80(reward);
292}

Recommendation:

We strongly recommend rewards that contain a double cap multiplier to be properly tracked and rewarded with the new cap in mind to avoid discrepancies between what the users perceive on the application and what they actually receive.

Alleviation:

The codebase was significantly revamped no longer rendering this exhibit relevant.