You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 27, 2024. It is now read-only.
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
OCC_Modular::amountOwed() calculates lateFee incorrectly if more than 1 payment was missed
Summary
loans[id].APRLateFee is the annualized late fee accrued on the outstanding principal of a loan. However, the code overcalculates the late fee, leading to significantly higher fees in case of late payments delayed by more than paymentInterval since the first payment due time (essentially missing more than 1 payment).
Vulnerability Detail
loans[id].APRLateFee is, as stated in the docs 1, 2, respectively.
The APR charged on the outstanding principal if payment is late.
In the event of a missed payment, late fee interest begins to accrue on the outstanding principal.
However, if more than one payment is missed, the fee is not applied on the outstanding principal, but on n times this amount, where n is the number of missed payments for bullet loans; or outstandingPrincipal/n + outstandingPrincipal*(n-1)/n + ...) for amortization loans. Thus, for example, if a borrower misses 2 payments, in bullet loans (simpler calculations), and the apr is 10%, the actual late fees will be close to 20% (sligthly less as block.timestamp - loans[id].paymentDueBy is lower for the second payment).
The reason is that OCC_Modular::amountOwed() always calculates the late fee to be applied on block.timestamp - loans[id].paymentDueBy, and payments always have to be payed in sequence, that is, if a borrower misses 2 payments, it has to call OCC_Modular::makePayment() twice.
This issue is also confirmed by the fact that OCC_Modular::callLoan() only charges late fees on the first missed payment and then closes the loan, which is not what would happen in OCC_Modular::makePayment(), as described above.
Add the following test toTest_OCC_Modular.sol (forked at block 19718909). As can be seen, the fee paid is almost double the expected.
function amountOwed(uint256id) publicviewreturns (
uint256principal, uint256interest, uint256lateFee, uint256total
) {
// 0 == Bullet.if (loans[id].paymentSchedule ==0) {
if (loans[id].paymentsRemaining ==1) { principal = loans[id].principalOwed; }
}
// 1 == Amortization (only two options, use else here).else { principal = loans[id].principalOwed / loans[id].paymentsRemaining; }
// Add late fee if past loans[id].paymentDueBy.if (block.timestamp> loans[id].paymentDueBy && loans[id].state == LoanState.Active) {
lateFee = loans[id].principalOwed * (block.timestamp- loans[id].paymentDueBy) *
loans[id].APRLateFee / (86400*365* BIPS);
}
interest = loans[id].principalOwed * loans[id].paymentInterval * loans[id].APR / (86400*365* BIPS);
total = principal + interest + lateFee;
}
Tool used
Manual Review
Vscode
Foundry
Recommendation
Late fees should only apply on the first payment when several payments are missing so the APR is correctly calculated. To do this, a variable can be added to struct loan, lastPayment, which tracks the latest payment and applies the late fee only on block.timestamp - loans[id].lastPayment. For example:
1 comment(s) were left on this issue during the judging contest.
panprog commented:
medium, dup of #97. This one assumes that makePayment is incorrect, comparing it to callLoan (which is assumed correct). Per my understanding, it's callLoan that is incorrect. Since this issue brings up the callLoan difference from makePayment, I consider this the same core reason.
sherlock-admin2
changed the title
Curved Marmalade Cobra - OCC_Modular::amountOwed() calculates lateFee incorrectly if more than 1 payment was missed
0x73696d616f - OCC_Modular::amountOwed() calculates lateFee incorrectly if more than 1 payment was missed
May 11, 2024
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
0x73696d616f
high
OCC_Modular::amountOwed()
calculateslateFee
incorrectly if more than 1 payment was missedSummary
loans[id].APRLateFee
is the annualized late fee accrued on the outstanding principal of a loan. However, the code overcalculates the late fee, leading to significantly higher fees in case of late payments delayed by more thanpaymentInterval
since the first payment due time (essentially missing more than 1 payment).Vulnerability Detail
loans[id].APRLateFee
is, as stated in the docs 1, 2, respectively.However, if more than one payment is missed, the fee is not applied on the outstanding principal, but on
n
times this amount, wheren
is the number of missed payments for bullet loans; oroutstandingPrincipal/n + outstandingPrincipal*(n-1)/n + ...)
for amortization loans. Thus, for example, if a borrower misses 2 payments, in bullet loans (simpler calculations), and the apr is10%
, the actual late fees will be close to20%
(sligthly less asblock.timestamp - loans[id].paymentDueBy
is lower for the second payment).The reason is that
OCC_Modular::amountOwed()
always calculates the late fee to be applied onblock.timestamp - loans[id].paymentDueBy
, and payments always have to be payed in sequence, that is, if a borrower misses 2 payments, it has to callOCC_Modular::makePayment()
twice.This issue is also confirmed by the fact that
OCC_Modular::callLoan()
only charges late fees on the first missed payment and then closes the loan, which is not what would happen inOCC_Modular::makePayment()
, as described above.Add the following test to
Test_OCC_Modular.sol
(forked at block19718909
). As can be seen, the fee paid is almost double the expected.Impact
Loan late fee is incorrect and will punish borrowers more than supposed by a large amount.
Code Snippet
OCC_Modular::amountOwed()
Tool used
Manual Review
Vscode
Foundry
Recommendation
Late fees should only apply on the first payment when several payments are missing so the
APR
is correctly calculated. To do this, a variable can be added to structloan
,lastPayment
, which tracks the latest payment and applies the late fee only onblock.timestamp - loans[id].lastPayment
. For example:Duplicate of #97
The text was updated successfully, but these errors were encountered: