Skip to content

Commit a3ab52d

Browse files
wisdantPlayJok3r
authored andcommitted
Cleanup, add comments
1 parent 177e167 commit a3ab52d

File tree

5 files changed

+63
-91
lines changed

5 files changed

+63
-91
lines changed

contracts/BaseCreditPool.sol

Lines changed: 56 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -603,40 +603,29 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
603603
// several cycles.
604604
cr = _updateDueInfo(borrower, false, true);
605605
}
606-
console.log("After updateDueInfo, cr.unbilledPrincipal=", cr.unbilledPrincipal);
607-
608-
// Computes payoff amount, including correction
609-
// Since only payback generates generative correction, and all other actions (e.g. drawdown)
610-
// will only increase totalDue, unbilledPrincipal, and move correction to the positive
611-
// side, if abs(cr.correction) is larger than the sum of due and principal, the credit line
612-
// would have been paid off at the last payment when the big negative cr.correction was
613-
// generated. This statement is recursively true. Thus the assertion below.
606+
607+
// Computes the final payoff amount. Needs to consider the correction associated with
608+
// all outstanding principals.
614609
uint256 payoffCorrection = _feeManager.calcCorrection(
615610
cr.dueDate,
616611
_creditRecordStaticMapping[borrower].aprInBps,
617612
cr.unbilledPrincipal + cr.totalDue - cr.feesAndInterestDue
618613
);
619614

620-
bool paidOff = false;
621615
uint256 payoffAmount = uint256(
622616
int256(int96(cr.totalDue + cr.unbilledPrincipal)) + int256(cr.correction)
623617
) - payoffCorrection;
624618

625-
console.log("In payoffAmount calculation, cr.unbilledPrincipal=", cr.unbilledPrincipal);
626-
console.log("cr.totalDue=", cr.totalDue);
627-
console.logInt(cr.correction);
628-
console.log("payoffCorrection=", payoffCorrection);
629-
console.log("payoffAmount=", payoffAmount);
630-
631-
// The amount to be applied towards principal
632-
uint256 principalPayment = 0;
619+
bool paidOff = false;
633620

634621
// The amount to be collected from the borrower. When _amount is more than what is needed
635622
// for payoff, only the payoff amount will be transferred
636623
uint256 amountToCollect;
637624

625+
// The amount to be applied towards principal
626+
uint256 principalPayment = 0;
627+
638628
if (amount < cr.totalDue) {
639-
console.log("Below totalDue path");
640629
amountToCollect = amount;
641630
cr.totalDue = uint96(cr.totalDue - amount);
642631

@@ -646,19 +635,17 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
646635
principalPayment = amount - cr.feesAndInterestDue;
647636
cr.feesAndInterestDue = 0;
648637
}
638+
if (cr.state == BS.CreditState.Defaulted)
639+
_recoverDefaultedAmount(borrower, amountToCollect);
649640
} else if (amount < payoffAmount) {
650-
console.log("below payoff path");
651641
amountToCollect = amount;
652642

653643
// Apply extra payments towards principal, reduce unbilledPrincipal amount
654644
cr.unbilledPrincipal -= uint96(amount - cr.totalDue);
655645

656646
principalPayment = amount - cr.feesAndInterestDue;
657-
console.log("principalPayment=", principalPayment);
658647
if (principalPayment > 0) {
659648
// If there is principal payment, calcuate new correction
660-
console.log("Before updating, cr.correction=");
661-
console.logInt(cr.correction);
662649
cr.correction -= int96(
663650
uint96(
664651
_feeManager.calcCorrection(
@@ -668,57 +655,36 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
668655
)
669656
)
670657
);
671-
console.log("After updating.");
672-
console.logInt(cr.correction);
673658
}
674659
cr.feesAndInterestDue = 0;
675660
cr.totalDue = 0;
676661
cr.missedPeriods = 0;
662+
663+
// Moves account to GoodStanding if it was delayed.
677664
if (cr.state == BS.CreditState.Delayed) cr.state = BS.CreditState.GoodStanding;
665+
666+
// Recovers funds to the pool if the account is Defaulted.
667+
// Only moves it to GoodStanding only after payoff, handled in the payoff branch
668+
if (cr.state == BS.CreditState.Defaulted)
669+
_recoverDefaultedAmount(borrower, amountToCollect);
678670
} else {
671+
// Payoff logic
679672
paidOff = true;
680673
principalPayment = cr.unbilledPrincipal + cr.totalDue - cr.feesAndInterestDue;
681674
amountToCollect = payoffAmount;
682-
// Distribut or reverse income to consume outstanding correction.
683-
// Book income with positive correction, i.e., user had drawdown in the past cycle.
684-
// Reverse income with negative correction, i.e., interest for the entire final pay
685-
// period has been booked and distributed, but the user paid off early, thus negative
686-
// correction and income reverse.
687-
cr.correction = cr.correction - int96(int256(payoffCorrection));
688-
}
689675

690-
// For account in default, record the recovered principal for the pool.
691-
// Note: correction only impacts interest amount, thus no impact on recovered principal
692-
if (cr.state == BS.CreditState.Defaulted) {
693-
console.log("\nIn default recovery flow, principalPayment=", principalPayment);
694-
console.log("_totalPoolValue=", _totalPoolValue);
695-
console.log("defaultAmount=", _creditRecordStaticMapping[borrower].defaultAmount);
696-
697-
uint96 _defaultAmount = _creditRecordStaticMapping[borrower].defaultAmount;
698-
699-
if (_defaultAmount > 0) {
700-
uint256 recoveredPrincipal;
701-
if (_defaultAmount >= amountToCollect) {
702-
recoveredPrincipal = amountToCollect;
703-
} else {
704-
recoveredPrincipal = _defaultAmount;
705-
distributeIncome(amountToCollect - recoveredPrincipal);
706-
}
707-
_totalPoolValue += recoveredPrincipal;
708-
_defaultAmount -= uint96(recoveredPrincipal);
709-
_creditRecordStaticMapping[borrower].defaultAmount = _defaultAmount;
710-
console.log("\nAfter adjusting, recoveredPrincipal=", recoveredPrincipal);
676+
if (cr.state == BS.CreditState.Defaulted) {
677+
_recoverDefaultedAmount(borrower, amountToCollect);
711678
} else {
712-
distributeIncome(amountToCollect);
713-
}
714-
715-
console.log("After default logic, _totalPoolValue=", _totalPoolValue);
716-
}
717-
718-
if (paidOff) {
719-
console.log("In payoff flow, before distributeIncome, cr.correction=");
720-
console.logInt(cr.correction);
721-
if (cr.state != BS.CreditState.Defaulted) {
679+
// Distribut or reverse income to consume outstanding correction.
680+
// Positive correction is generated becasue of a drawdown within this period,
681+
// it is not booked or distributed yet, needs to be distributed.
682+
// Negative correction is generated because of a payment including principal
683+
// within this period, the extra interest paid is not accounted for yet, thus
684+
// a reversal.
685+
// Note: For defaulted account, we do not distributed fees and interests
686+
// until they are paid. It is handled in _recoverDefaultedAmount().
687+
cr.correction = cr.correction - int96(int256(payoffCorrection));
722688
if (cr.correction > 0) distributeIncome(uint256(uint96(cr.correction)));
723689
else if (cr.correction < 0) reverseIncome(uint256(uint96(0 - cr.correction)));
724690
}
@@ -727,6 +693,7 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
727693
cr.unbilledPrincipal = 0;
728694
cr.feesAndInterestDue = 0;
729695
cr.totalDue = 0;
696+
730697
// Closes the credit line if it is in the final period
731698
if (cr.remainingPeriods == 0) {
732699
cr.state = BS.CreditState.Deleted;
@@ -735,8 +702,6 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
735702
}
736703

737704
_creditRecordMapping[borrower] = cr;
738-
console.log("\nAfter processing. cr.correction=");
739-
console.logInt(cr.correction);
740705

741706
if (amountToCollect > 0 && isPaymentReceived == false) {
742707
// Transfer assets from the _borrower to pool locker
@@ -749,10 +714,36 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
749714
msg.sender
750715
);
751716
}
752-
console.log("amountToCollect=", amountToCollect);
753717
return (amountToCollect, paidOff);
754718
}
755719

720+
/**
721+
* @notice Recovers amount when a payment is paid towards a defaulted account.
722+
* @dev For any payment after a default, it is applied towards principal losses first.
723+
* Only after the principal is fully recovered, it is applied towards fees & interest.
724+
*/
725+
function _recoverDefaultedAmount(address borrower, uint256 amountToCollect) internal {
726+
uint96 _defaultAmount = _creditRecordStaticMapping[borrower].defaultAmount;
727+
728+
if (_defaultAmount > 0) {
729+
uint256 recoveredPrincipal;
730+
if (_defaultAmount >= amountToCollect) {
731+
recoveredPrincipal = amountToCollect;
732+
} else {
733+
recoveredPrincipal = _defaultAmount;
734+
distributeIncome(amountToCollect - recoveredPrincipal);
735+
}
736+
_totalPoolValue += recoveredPrincipal;
737+
_defaultAmount -= uint96(recoveredPrincipal);
738+
_creditRecordStaticMapping[borrower].defaultAmount = _defaultAmount;
739+
} else {
740+
// note The account is moved out of Defaulted state only if the entire due
741+
// including principals, fees&Interest are paid off. It is possible for
742+
// the account to owe fees&Interest after _defaultAmount becomes zero.
743+
distributeIncome(amountToCollect);
744+
}
745+
}
746+
756747
/// Checks if the given amount is higher than what is allowed by the pool
757748
function _maxCreditLineCheck(uint256 amount) internal view {
758749
if (amount > _poolConfig.maxCreditLine()) {
@@ -796,8 +787,6 @@ contract BaseCreditPool is BasePool, BaseCreditPoolStorage, ICredit {
796787

797788
if (periodsPassed > 0) {
798789
// Distribute income
799-
console.log("before distributeIncome, newCharges=", newCharges);
800-
console.log("distributeChargesForLastCycle=", distributeChargesForLastCycle);
801790
if (cr.state != BS.CreditState.Defaulted) {
802791
if (distributeChargesForLastCycle) distributeIncome(newCharges);
803792
else distributeIncome(newCharges - cr.feesAndInterestDue);

contracts/BaseFeeManager.sol

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@ contract BaseFeeManager is IFeeManager, Ownable {
112112
// rounding to days
113113
uint256 remainingTime = dueDate - block.timestamp;
114114

115-
console.log("\nIn calcCorrection, amount=", amount);
116-
console.log("remainingTime=", remainingTime);
117-
118115
return (amount * aprInBps * remainingTime) / SECONDS_IN_A_YEAR / HUNDRED_PERCENT_IN_BPS;
119116
}
120117

contracts/BasePool.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,6 @@ abstract contract BasePool is Initializable, BasePoolStorage, ILiquidityProvider
138138
*/
139139
function distributeIncome(uint256 value) internal virtual {
140140
uint256 poolIncome = _poolConfig.distributeIncome(value);
141-
console.log("distributeIncome, value=", value);
142-
console.log("poolIncome=", poolIncome);
143141
_totalPoolValue += poolIncome;
144142
}
145143

@@ -154,7 +152,6 @@ abstract contract BasePool is Initializable, BasePoolStorage, ILiquidityProvider
154152
* poolOwner and EA do not participate in losses, but they participate in income reverse.
155153
*/
156154
function distributeLosses(uint256 value) internal virtual {
157-
console.log("distributeLosses called, value=", value);
158155
if (_totalPoolValue > value) _totalPoolValue -= value;
159156
else _totalPoolValue = 0;
160157
emit LossesDistributed(value, _totalPoolValue);
@@ -171,7 +168,6 @@ abstract contract BasePool is Initializable, BasePoolStorage, ILiquidityProvider
171168
*/
172169
function reverseIncome(uint256 value) internal virtual {
173170
uint256 poolIncome = _poolConfig.reverseIncome(value);
174-
console.log("reverseIncome called. value=", value);
175171
_totalPoolValue -= poolIncome;
176172
}
177173

test/BaseCreditPoolTest.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const getLoanContractFromAddress = async function (address, signer) {
2121
//
2222
//
2323
// Numbers in Google Sheet: more detail: (shorturl.at/dfqrT)
24-
describe.only("Base Credit Pool", function () {
24+
describe("Base Credit Pool", function () {
2525
let poolContract;
2626
let poolConfigContract;
2727
let hdtContract;
@@ -148,32 +148,24 @@ describe.only("Base Credit Pool", function () {
148148

149149
await testTokenContract.mint(borrower.address, 1080);
150150
await testTokenContract.connect(borrower).approve(poolContract.address, 4040);
151-
console.log("zero");
152151
await poolContract.connect(borrower).makePayment(borrower.address, 4040);
153152
// Note since there is no time passed, the interest charged will be offset at the payoff
154153
record = await poolContract.creditRecordMapping(borrower.address);
155-
console.log("1");
156154
expect(record.totalDue).to.equal(0);
157-
console.log("2");
158155
expect(record.unbilledPrincipal).to.equal(0);
159-
console.log("3");
160156

161157
await poolContract.connect(eaServiceAccount).changeCreditLine(borrower.address, 0);
162-
console.log("4");
163158
result = await poolContract.creditRecordStaticMapping(borrower.address);
164159
expect(result.creditLimit).to.equal(0);
165-
console.log("5");
166160
record = await poolContract.creditRecordMapping(borrower.address);
167161
expect(record.state).to.equal(0);
168-
console.log("6");
169162

170163
// remove the extra tokens in the borrower's account to return to clean account status
171164
await testTokenContract.burn(
172165
borrower.address,
173166
await testTokenContract.balanceOf(borrower.address)
174167
);
175168
expect(await testTokenContract.balanceOf(borrower.address)).to.equal(0);
176-
console.log("7");
177169
});
178170
});
179171

@@ -665,7 +657,7 @@ describe.only("Base Credit Pool", function () {
665657
});
666658
});
667659

668-
describe.only("Quick large amount payback (for getDueInfo overflow)", function () {
660+
describe("Quick large amount payback (for getDueInfo overflow)", function () {
669661
it("Quick follow-up borrowing", async function () {
670662
let blockNumBefore = await ethers.provider.getBlockNumber();
671663
let blockBefore = await ethers.provider.getBlock(blockNumBefore);
@@ -879,7 +871,6 @@ describe.only("Base Credit Pool", function () {
879871
advanceClock(40);
880872
dueDate += 2592000;
881873

882-
console.log("\n*** First payment ***");
883874
await testTokenContract.connect(borrower).approve(poolContract.address, 25_000);
884875

885876
await poolContract.connect(borrower).makePayment(borrower.address, 25_000);
@@ -918,7 +909,6 @@ describe.only("Base Credit Pool", function () {
918909
expect(await testTokenContract.balanceOf(poolContract.address)).to.equal(4_036_000);
919910

920911
// // Stage 3: pay off
921-
console.log("\n*** Second payment ***");
922912
advanceClock(10);
923913
await testTokenContract.connect(borrower).approve(poolContract.address, 1_077_000);
924914

test/InvoiceFactoringTest.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -817,13 +817,13 @@ describe("Invoice Factoring", function () {
817817
ethers.utils.formatBytes32String("1")
818818
);
819819

820-
expect(await hdtContract.withdrawableFundsOf(poolOwner.address)).to.equal(998_411);
821-
expect(await hdtContract.withdrawableFundsOf(lender.address)).to.equal(1_996_822);
820+
expect(await hdtContract.withdrawableFundsOf(poolOwner.address)).to.equal(999_390);
821+
expect(await hdtContract.withdrawableFundsOf(lender.address)).to.equal(1_998_780);
822822

823823
accruedIncome = await poolConfigContract.accruedIncome();
824-
expect(accruedIncome.protocolIncome).to.equal(9472);
825-
expect(accruedIncome.eaIncome).to.equal(7104);
826-
expect(accruedIncome.poolOwnerIncome).to.equal(2368);
824+
expect(accruedIncome.protocolIncome).to.equal(7024);
825+
expect(accruedIncome.eaIncome).to.equal(5268);
826+
expect(accruedIncome.poolOwnerIncome).to.equal(1756);
827827

828828
expect(await testTokenContract.balanceOf(poolContract.address)).to.equal(
829829
5_011_000

0 commit comments

Comments
 (0)