From 978ae66cc0160831b0d48217f9df92af3aac3806 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Thu, 11 Nov 2021 15:16:43 -0300 Subject: [PATCH 1/3] feat: fixed audit findings --- contracts/DebtLocker.sol | 17 ++++++++++----- contracts/test/DebtLocker.t.sol | 38 ++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/contracts/DebtLocker.sol b/contracts/DebtLocker.sol index a47fec2..912893b 100644 --- a/contracts/DebtLocker.sol +++ b/contracts/DebtLocker.sol @@ -53,6 +53,8 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied { require(amount_ == uint256(0) || ERC20Helper.transfer(loan_.fundsAsset(), address(_loan), amount_)); loan_.acceptNewTerms(refinancer_, calls_, uint256(0)); + + _principalRemainingAtLastClaim = loan_.principal(); } function setFundsToCapture(uint256 amount_) override external { @@ -209,19 +211,24 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied { require(!_isLiquidationActive(), "DL:HCOR:LIQ_NOT_FINISHED"); address fundsAsset = IMapleLoanLike(_loan).fundsAsset(); - uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)); // Funds recovered from liquidation and any unclaimed previous payment amounts - uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation + uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation + uint256 fundsCaptured = _fundsToCapture; + + // Funds recovered from liquidation and any unclaimed previous payment amounts + uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)) - fundsCaptured; // If `recoveredFunds` is greater than `principalToCover`, the remaining amount is treated as interest in the context of the pool. // If `recoveredFunds` is less than `principalToCover`, the difference is registered as a shortfall. - details_[0] = recoveredFunds; + details_[0] = recoveredFunds + fundsCaptured; details_[1] = recoveredFunds > principalToCover ? recoveredFunds - principalToCover : 0; + details_[2] = fundsCaptured; details_[5] = recoveredFunds > principalToCover ? principalToCover : recoveredFunds; details_[6] = principalToCover > recoveredFunds ? principalToCover - recoveredFunds : 0; - require(ERC20Helper.transfer(fundsAsset, _pool, recoveredFunds), "DL:HCOR:TRANSFER"); + require(ERC20Helper.transfer(fundsAsset, _pool, recoveredFunds + fundsCaptured), "DL:HCOR:TRANSFER"); - _repossessed = false; + _fundsToCapture = uint256(0); + _repossessed = false; } function _handleClaim() internal returns (uint256[7] memory details_) { diff --git a/contracts/test/DebtLocker.t.sol b/contracts/test/DebtLocker.t.sol index 3a20cf3..5c7c1ad 100644 --- a/contracts/test/DebtLocker.t.sol +++ b/contracts/test/DebtLocker.t.sol @@ -532,7 +532,8 @@ contract DebtLockerTest is TestUtils { uint256 principalAfter = loan.principal(); - assertEq(principalBefore + principalIncrease_, principalAfter); + assertEq(principalBefore + principalIncrease_, principalAfter); + assertEq(debtLocker.principalRemainingAtLastClaim(), principalAfter); } function test_fundsToCaptureForNextClaim() public { @@ -590,4 +591,39 @@ contract DebtLockerTest is TestUtils { pool.claim(address(debtLocker)); } + function test_fundsToCaptureWhileInDefault() public { + ( loan, debtLocker ) = _createFundAndDrawdownLoan(1_000_000); + + // Prepare additional amount to be captured + fundsAsset.mint(address(debtLocker), 500_000); + + assertEq(fundsAsset.balanceOf(address(debtLocker)), 500_000); + assertEq(fundsAsset.balanceOf(address(pool)), 0); + assertEq(debtLocker.principalRemainingAtLastClaim(), loan.principalRequested()); + assertEq(debtLocker.fundsToCapture(), 0); + + //trigger default + hevm.warp(loan.nextPaymentDueDate() + loan.gracePeriod() + 1); + + pool.triggerDefault(address(debtLocker)); + + //After triggering default, set funds to capture + poolDelegate.debtLocker_setFundsToCapture(address(debtLocker), 500_000); + + //claim + uint256[7] memory details = pool.claim(address(debtLocker)); + + assertEq(fundsAsset.balanceOf(address(debtLocker)), 0); + assertEq(fundsAsset.balanceOf(address(pool)), 500_000); + assertEq(debtLocker.fundsToCapture(), 0); + + assertEq(details[0], 500_000); + assertEq(details[1], 0); + assertEq(details[2], 500_000); + assertEq(details[3], 0); + assertEq(details[4], 0); + assertEq(details[5], 0); + assertEq(details[6], loan.principalRequested()); // No principal was recovered + } + } From 31e97c981c3fda7c6d51053894d69127dd4a81e9 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Thu, 11 Nov 2021 18:27:26 -0300 Subject: [PATCH 2/3] fix: PR comments --- contracts/DebtLocker.sol | 4 ++-- contracts/test/DebtLocker.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/DebtLocker.sol b/contracts/DebtLocker.sol index 912893b..1da2dd6 100644 --- a/contracts/DebtLocker.sol +++ b/contracts/DebtLocker.sol @@ -211,11 +211,11 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxied { require(!_isLiquidationActive(), "DL:HCOR:LIQ_NOT_FINISHED"); address fundsAsset = IMapleLoanLike(_loan).fundsAsset(); - uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation + uint256 principalToCover = _principalRemainingAtLastClaim; // Principal remaining at time of liquidation uint256 fundsCaptured = _fundsToCapture; // Funds recovered from liquidation and any unclaimed previous payment amounts - uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)) - fundsCaptured; + uint256 recoveredFunds = IERC20Like(fundsAsset).balanceOf(address(this)) - fundsCaptured; // If `recoveredFunds` is greater than `principalToCover`, the remaining amount is treated as interest in the context of the pool. // If `recoveredFunds` is less than `principalToCover`, the difference is registered as a shortfall. diff --git a/contracts/test/DebtLocker.t.sol b/contracts/test/DebtLocker.t.sol index 5c7c1ad..d5006b7 100644 --- a/contracts/test/DebtLocker.t.sol +++ b/contracts/test/DebtLocker.t.sol @@ -602,7 +602,7 @@ contract DebtLockerTest is TestUtils { assertEq(debtLocker.principalRemainingAtLastClaim(), loan.principalRequested()); assertEq(debtLocker.fundsToCapture(), 0); - //trigger default + // Trigger default hevm.warp(loan.nextPaymentDueDate() + loan.gracePeriod() + 1); pool.triggerDefault(address(debtLocker)); From de50762b288e039253df28843a60928b2a459739 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 11 Nov 2021 16:42:23 -0500 Subject: [PATCH 3/3] formatting: update comments --- contracts/test/DebtLocker.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/test/DebtLocker.t.sol b/contracts/test/DebtLocker.t.sol index d5006b7..f106187 100644 --- a/contracts/test/DebtLocker.t.sol +++ b/contracts/test/DebtLocker.t.sol @@ -607,10 +607,10 @@ contract DebtLockerTest is TestUtils { pool.triggerDefault(address(debtLocker)); - //After triggering default, set funds to capture + // After triggering default, set funds to capture poolDelegate.debtLocker_setFundsToCapture(address(debtLocker), 500_000); - //claim + // Claim uint256[7] memory details = pool.claim(address(debtLocker)); assertEq(fundsAsset.balanceOf(address(debtLocker)), 0);