Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporates all of the fixes for the December 2024 sherlock audit #86

Open
wants to merge 45 commits into
base: merge-train-r5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
40f76bd
disable revoke using v r s
ethereumdegen Dec 9, 2024
05bc06a
fix aftertokentransfer
ethereumdegen Dec 11, 2024
f173d6b
use safe approve
ethereumdegen Dec 11, 2024
e430554
use call instead of try catch
ethereumdegen Dec 12, 2024
83b5ac8
return 0 if sub would underflow
ethereumdegen Dec 12, 2024
6a04ffc
prevent re-entrancy
ethereumdegen Dec 17, 2024
8c081e3
fix
ethereumdegen Dec 17, 2024
425d703
callback even if paused
ethereumdegen Dec 17, 2024
a5ef0a3
Merge branch 'fix/issue-46' into fix/issue-43
ethereumdegen Dec 18, 2024
34c6826
add test
ethereumdegen Dec 18, 2024
95373b1
improve test
ethereumdegen Dec 18, 2024
f2b4c0e
add ot test
ethereumdegen Dec 20, 2024
d483a75
trying to test tokens repaid
ethereumdegen Dec 20, 2024
2d2b182
repaid amt
ethereumdegen Dec 20, 2024
2006714
trying to fix test
ethereumdegen Dec 20, 2024
5202b7d
adding tests for liquidate
ethereumdegen Dec 20, 2024
d1ef805
add
ethereumdegen Dec 20, 2024
84c8288
more test
ethereumdegen Dec 20, 2024
47c7255
trying to fix math in liq
ethereumdegen Dec 20, 2024
211c2d6
trying to fix shortfall math
ethereumdegen Dec 20, 2024
3d1434f
add to tests
ethereumdegen Dec 23, 2024
b600657
fixing logic using tests
ethereumdegen Dec 23, 2024
d888e89
fix loan scenarios using TDD
ethereumdegen Dec 23, 2024
b2e8297
all tests pass
ethereumdegen Dec 23, 2024
b6729dd
Merge branch 'fix/revoke-delegation' into merge-train-r6
ethereumdegen Dec 26, 2024
9b5f743
Merge branch 'fix/issue-29' into merge-train-r6
ethereumdegen Dec 26, 2024
0f0c37c
Merge branch 'fix/issue-39' into merge-train-r6
ethereumdegen Dec 26, 2024
106a9fc
Merge branch 'fix/issue-46' into merge-train-r6
ethereumdegen Dec 26, 2024
f529eb0
Merge branch 'fix/issue-42' into merge-train-r6
ethereumdegen Dec 26, 2024
55559c6
Merge branch 'fix/issue-51' into merge-train-r6
ethereumdegen Dec 26, 2024
b25f8ec
Merge branch 'fix/issue-54' into merge-train-r6
ethereumdegen Dec 26, 2024
0dce11a
Merge branch 'fix/issue-43' into merge-train-r6
ethereumdegen Dec 26, 2024
ae415b6
Merge branch 'fix/issue-43' into merge-train-r6
ethereumdegen Dec 26, 2024
38f4c75
remove console log
ethereumdegen Dec 30, 2024
5ce67ae
add gas req
ethereumdegen Dec 31, 2024
4918966
change event emit
ethereumdegen Jan 2, 2025
834a082
remove console log
ethereumdegen Jan 2, 2025
a7c18db
merge 43 update
ethereumdegen Jan 2, 2025
c13ae3e
adding tests for excess repayment accounting
ethereumdegen Jan 2, 2025
4d43bfc
enw tests
ethereumdegen Jan 2, 2025
171c798
all tests pass now
ethereumdegen Jan 2, 2025
2cc47a9
fix amount due remaining
ethereumdegen Jan 2, 2025
79b1743
re-order for clarity
ethereumdegen Jan 2, 2025
6473ad1
Merge branch 'fix/repay-callback-gas' into merge-train-r6
ethereumdegen Jan 3, 2025
53c51f9
Merge branch 'test-excess-repay' into merge-train-r6
ethereumdegen Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ contract LenderCommitmentGroupShares is ERC20, Ownable {
uint256 amount
) internal override {

if (amount > 0) {
//reset prepared
poolSharesPreparedToWithdrawForLender[from] = 0;
poolSharesPreparedTimestamp[from] = block.timestamp;

//reset prepared
poolSharesPreparedToWithdrawForLender[from] = 0;
poolSharesPreparedTimestamp[from] = block.timestamp;
}


}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;


// Contracts
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
Expand Down Expand Up @@ -123,6 +124,7 @@ contract LenderCommitmentGroup_Smart is

uint256 public totalPrincipalTokensLended;
uint256 public totalPrincipalTokensRepaid; //subtract this and the above to find total principal tokens outstanding for loans
uint256 public excessivePrincipalTokensRepaid;

uint256 public totalInterestCollected;

Expand All @@ -142,6 +144,7 @@ contract LenderCommitmentGroup_Smart is

//mapping(address => uint256) public principalTokensCommittedByLender;
mapping(uint256 => bool) public activeBids;
mapping(uint256 => uint256) public activeBidsAmountDueRemaining;

//this excludes interest
// maybe it is possible to get rid of this storage slot and calculate it from totalPrincipalTokensRepaid, totalPrincipalTokensLended
Expand Down Expand Up @@ -434,8 +437,15 @@ contract LenderCommitmentGroup_Smart is
{

int256 poolTotalEstimatedValueSigned = int256(totalPrincipalTokensCommitted)
//+ int256( totalPrincipalTokensRepaid ) //cant really incorporate because needs totalPrincipalTokensLended to help balance it

+ int256(totalInterestCollected) + int256(tokenDifferenceFromLiquidations)
- int256(totalPrincipalTokensWithdrawn);
+ int256( excessivePrincipalTokensRepaid )
- int256( totalPrincipalTokensWithdrawn )
//- int256( totalPrincipalTokensLended ) //amount borrowed -- should not be incorporated as it does not really affect net value
;



//if the poolTotalEstimatedValue_ is less than 0, we treat it as 0.
poolTotalEstimatedValue_ = poolTotalEstimatedValueSigned > int256(0)
Expand Down Expand Up @@ -553,14 +563,15 @@ contract LenderCommitmentGroup_Smart is
"Insufficient Borrower Collateral"
);

principalToken.approve(address(TELLER_V2), _principalAmount);
principalToken.safeApprove(address(TELLER_V2), _principalAmount);

//do not have to override msg.sender as this contract is the lender !
_acceptBidWithRepaymentListener(_bidId);

totalPrincipalTokensLended += _principalAmount;

activeBids[_bidId] = true; //bool for now
activeBidsAmountDueRemaining[_bidId] = _principalAmount;


emit BorrowerAcceptedFunds(
Expand Down Expand Up @@ -661,7 +672,11 @@ contract LenderCommitmentGroup_Smart is

//use original principal amount as amountDue

uint256 amountDue = _getAmountOwedForBid(_bidId);
uint256 loanTotalPrincipalAmount = _getLoanTotalPrincipalAmount(_bidId); //only used for the auction delta amount

(uint256 principalDue,uint256 interestDue) = _getAmountOwedForBid(_bidId); //this is the base amount that must be repaid by the liquidator

// uint256 principalAmountAlreadyRepaid = loanTotalPrincipalAmount - principalDue;


uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2)
Expand All @@ -673,10 +688,11 @@ contract LenderCommitmentGroup_Smart is
);

int256 minAmountDifference = getMinimumAmountDifferenceToCloseDefaultedLoan(
amountDue,
loanTotalPrincipalAmount,
loanDefaultedOrUnpausedAtTimeStamp
);



require(
_tokenAmountDifference >= minAmountDifference,
"Insufficient tokenAmountDifference"
Expand All @@ -687,7 +703,8 @@ contract LenderCommitmentGroup_Smart is
//this is used when the collateral value is higher than the principal (rare)
//the loan will be completely made whole and our contract gets extra funds too
uint256 tokensToTakeFromSender = abs(minAmountDifference);




uint256 liquidationProtocolFee = Math.mulDiv(
tokensToTakeFromSender ,
Expand All @@ -699,18 +716,22 @@ contract LenderCommitmentGroup_Smart is
IERC20(principalToken).safeTransferFrom(
msg.sender,
address(this),
amountDue + tokensToTakeFromSender - liquidationProtocolFee
principalDue + tokensToTakeFromSender - liquidationProtocolFee
);

address protocolFeeRecipient = ITellerV2(address(TELLER_V2)).getProtocolFeeRecipient();

IERC20(principalToken).safeTransferFrom(
msg.sender,
address(protocolFeeRecipient),
liquidationProtocolFee
);
if (liquidationProtocolFee > 0) {
IERC20(principalToken).safeTransferFrom(
msg.sender,
address(protocolFeeRecipient),
liquidationProtocolFee
);
}

totalPrincipalTokensRepaid += amountDue;
totalPrincipalTokensRepaid += principalDue;

// tokenDifferenceFromLiquidations += int256(principalAmountAlreadyRepaid); //this helps us more correctly calculate the shortfall
tokenDifferenceFromLiquidations += int256(tokensToTakeFromSender - liquidationProtocolFee );


Expand All @@ -719,33 +740,56 @@ contract LenderCommitmentGroup_Smart is

uint256 tokensToGiveToSender = abs(minAmountDifference);


IERC20(principalToken).safeTransferFrom(
msg.sender,
address(this),
amountDue - tokensToGiveToSender
);

//dont stipend/refund more than principalDue base
if (tokensToGiveToSender > principalDue) {
tokensToGiveToSender = principalDue;
}

totalPrincipalTokensRepaid += amountDue;
uint256 netAmountDue = principalDue - tokensToGiveToSender ;


if (netAmountDue > 0) {
IERC20(principalToken).safeTransferFrom(
msg.sender,
address(this),
netAmountDue //principalDue - tokensToGiveToSender
);
}

totalPrincipalTokensRepaid += principalDue;

//this will make tokenDifference go more negative
tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender);

//this is the shortfall
// tokenDifferenceFromLiquidations += int256(principalAmountAlreadyRepaid);//this helps us more correctly calculate the shortfall
// tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender);

// uint256 shortfallNet = principalDue < tokensToGiveToSender ? tokensToGiveToSender - principalDue : 0;

tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender);



}

//this will effectively 'forfeit' tokens from this contract equal to ... the amount (principal) that has not been repaid ! principalDue


//this will give collateral to the caller
ITellerV2(TELLER_V2).lenderCloseLoanWithRecipient(_bidId, msg.sender);


emit DefaultedLoanLiquidated(
_bidId,
msg.sender,
amountDue,
principalDue,
_tokenAmountDifference
);
}



function getLastUnpausedAt()
public view
Expand All @@ -766,18 +810,29 @@ contract LenderCommitmentGroup_Smart is
lastUnpausedAt = block.timestamp;
}

function _getLoanTotalPrincipalAmount(uint256 _bidId )
internal
view
virtual
returns (uint256 principalAmount)
{
(,,,, principalAmount, , , )
= ITellerV2(TELLER_V2).getLoanSummary(_bidId);


}



function _getAmountOwedForBid(uint256 _bidId )
internal
view
virtual
returns (uint256 amountDue)
returns (uint256 principal,uint256 interest)
{
(,,,, amountDue, , , )
= ITellerV2(TELLER_V2).getLoanSummary(_bidId);
Payment memory owed = ITellerV2(TELLER_V2).calculateAmountOwed(_bidId, block.timestamp );


return (owed.principal, owed.interest) ;
}


Expand Down Expand Up @@ -919,21 +974,39 @@ contract LenderCommitmentGroup_Smart is


/*
This callback occurs when a TellerV2 repayment happens or when a TellerV2 liquidate happens

lenderCloseLoan does not trigger a repayLoanCallback

It is important that only teller loans FOR THIS POOL can call this !
*/
@dev This callback occurs when a TellerV2 repayment happens or when a TellerV2 liquidate happens
@dev lenderCloseLoan does not trigger a repayLoanCallback
@dev It is important that only teller loans for this specific pool can call this
@dev It is important that this function does not revert even if paused since repayments can occur in this case
*/
function repayLoanCallback(
uint256 _bidId,
address repayer,
uint256 principalAmount,
uint256 interestAmount
) external onlyTellerV2 whenForwarderNotPaused whenNotPaused bidIsActiveForGroup(_bidId) {
totalPrincipalTokensRepaid += principalAmount;
) external onlyTellerV2 bidIsActiveForGroup(_bidId) {

uint256 amountDueRemaining = activeBidsAmountDueRemaining[_bidId];


uint256 principalAmountAppliedToAmountDueRemaining = principalAmount < amountDueRemaining ?
principalAmount : amountDueRemaining;


//should never fail due to the above .
activeBidsAmountDueRemaining[_bidId] -= principalAmountAppliedToAmountDueRemaining;


totalPrincipalTokensRepaid += principalAmountAppliedToAmountDueRemaining;
totalInterestCollected += interestAmount;


uint256 excessiveRepaymentAmount = principalAmount < amountDueRemaining ?
0 : (principalAmount - amountDueRemaining);

excessivePrincipalTokensRepaid += excessiveRepaymentAmount;


emit LoanRepaid(
_bidId,
repayer,
Expand Down Expand Up @@ -966,7 +1039,11 @@ contract LenderCommitmentGroup_Smart is
public
view
returns (uint256)
{
{
if (totalPrincipalTokensRepaid > totalPrincipalTokensLended) {
return 0;
}

return totalPrincipalTokensLended - totalPrincipalTokensRepaid;
}

Expand Down
22 changes: 7 additions & 15 deletions packages/contracts/contracts/MarketRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ contract MarketRegistry is
* @notice Removes a borrower from a market via delegated revocation.
* @dev See {_revokeStakeholderViaDelegation}.
*/
function revokeLender(
/* function revokeLender(
uint256 _marketId,
address _lenderAddress,
uint8 _v,
Expand All @@ -344,7 +344,7 @@ contract MarketRegistry is
_r,
_s
);
}
} */

/**
* @notice Allows a lender to voluntarily leave a market.
Expand Down Expand Up @@ -409,7 +409,7 @@ contract MarketRegistry is
* @notice Removes a borrower from a market via delegated revocation.
* @dev See {_revokeStakeholderViaDelegation}.
*/
function revokeBorrower(
/* function revokeBorrower(
uint256 _marketId,
address _borrowerAddress,
uint8 _v,
Expand All @@ -424,7 +424,7 @@ contract MarketRegistry is
_r,
_s
);
}
}*/

/**
* @notice Allows a borrower to voluntarily leave a market.
Expand Down Expand Up @@ -1180,16 +1180,8 @@ contract MarketRegistry is
// tellerAS.revoke(uuid);
}

/**
* @notice Removes a stakeholder from an market via delegated revocation.
* @param _marketId The market ID to remove the borrower from.
* @param _stakeholderAddress The address of the borrower to remove from the market.
* @param _isLender Boolean indicating if the stakeholder is a lender. Otherwise it is a borrower.
* @param _v Signature value
* @param _r Signature value
* @param _s Signature value
*/
function _revokeStakeholderViaDelegation(

/* function _revokeStakeholderViaDelegation(
uint256 _marketId,
address _stakeholderAddress,
bool _isLender,
Expand All @@ -1205,7 +1197,7 @@ contract MarketRegistry is
// NOTE: Disabling the call to revoke the attestation on EAS contracts
// address attestor = markets[_marketId].owner;
// tellerAS.revokeByDelegation(uuid, attestor, _v, _r, _s);
}
} */

/**
* @notice Removes a stakeholder (borrower/lender) from a market.
Expand Down
Loading
Loading