#46819 [SC-Critical] direct theft of users funds when expired loan get liquidated
Description
Brief/Intro
Vulnerability Details
function createSettlement(
string memory _settlementId,
SettleInfo calldata _settleInfo,
string[] memory _loanIds,
LoanInfo[] calldata _loans,
bytes calldata _signature
) external nonReentrant {
//PART 1: Verify signature
bytes32 digest = _getSettlementHash(_settlementId, _settleInfo, _loanIds, _loans);
if (!SignatureChecker.isValidSignatureNow(_operator, digest, _signature)) {
revert InvalidSignature();
}
//PART 2: Create sanity checks
bytes32 settlementId = _settlementId.toBytes32();
if (settlements[settlementId].taker != address(0)) {
revert SettlementAlreadyExists(settlementId);
} // if the settlement already exists, revert
if (_settleInfo.expiryTime < block.timestamp) {
revert SettlementExpired(block.timestamp, _settleInfo.expiryTime);
} // if the settlement is in past(it can be equal)
if (_settleInfo.taker != msg.sender) {
revert TakerNotMatched(settlementId, _settleInfo.taker, msg.sender);
} // taker must be the caller
if (_loans.length == 0) {
revert EmptyLoan();
}
// PART 3: Create settlement and loans
uint256 totalCollateralAmt;
uint256 totalDebtAmt;
address debtToken = _loans[0].debtTokenAddr; // debt token addr for first loan(USDC)
address collateralToken = _loans[0].collateralTokenAddr; // collateral token addr for first loan(ETH)
for (uint256 i = 0; i < _loanIds.length; i++) {
bytes32 loanId = _loanIds[i].toBytes32();
LoanInfo memory loan = _loans[i];
if (loans[loanId].maker != address(0)) {
revert LoanAlreadyExisted(loanId);
} // if the loan created before, revert
loan.settlementId = settlementId; //set the settlement id for each loan
loan.settled = false;
loans[loanId] = loan; //UPDATE storage
totalCollateralAmt += loan.debtData.collateralAmt;
totalDebtAmt += loan.debtData.debtAmt;
if (_settleInfo.takerType == TakerType.BORROW && loan.collateralTokenAddr != collateralToken) {
revert TakerNotMatched(loanId, collateralToken, loan.collateralTokenAddr);
} //if the taker is borrowing then check if the all loan have same collateral token
else if (_settleInfo.takerType == TakerType.LEND && loan.debtTokenAddr != debtToken) {
revert TakerNotMatched(loanId, debtToken, loan.debtTokenAddr);
} //if taker is lending then check if the debt token is same for all loans
}
if (_settleInfo.takerType == TakerType.LEND) {
uint256 allowance = IERC20(collateralToken).allowance(msg.sender, address(this));
if (allowance < Constants.ALLOWANCE_SCALE * totalCollateralAmt) {
revert TokenAllowanceInsufficient(allowance, Constants.ALLOWANCE_SCALE * totalCollateralAmt);
} // lender must approve this contract before doing anything
allowance = IERC20(debtToken).allowance(msg.sender, address(this));
if (allowance < totalDebtAmt) {
revert TokenAllowanceInsufficient(allowance, totalDebtAmt);
} // lender must approve this contract before doing anything(for debt token)
} else {
uint256 allowance = IERC20(collateralToken).allowance(msg.sender, address(this));
if (allowance < totalCollateralAmt) {
revert TokenAllowanceInsufficient(allowance, totalCollateralAmt);
}
}
settlements[settlementId] = _settleInfo;
emit SettlementCreated(msg.sender, settlementId);
}
Impact Details
Recommend
Proof of Concept
Proof of Concept
PreviousTerm Structure Institutional | IOPNext#46608 [SC-Medium] Any call to the repay function can potentially be front-run by a malicious actor, lead to prevent users from repaying on time.
Was this helpful?