Moonwell

Moonwell
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: low
Invalid

Users can be liquidated at a low cost.

Summary

Users can be liquidated at a low cost.

Vulnerability Details

principal comes from borrowBalanceStored(), fetch user's current borrow balance.
liquidated comes from accountTokens[user], get current amount for a user that will transfer to the liquidator.
borrowBalanceStored() is obtained through borrowBalanceStoredInternal() in MToken.sol.

function borrowBalanceStored(address account) public view returns (uint) {
(MathError err, uint result) = borrowBalanceStoredInternal(account);
require(err == MathError.NO_ERROR, "borrowBalanceStored: borrowBalanceStoredInternal failed");
return result;
}

The borrow balance of the account is calculated using the value of accountBorrows[user], and then returned.
liquidated is taken from accountTokens[user], only check liquidated != 0.

if (liquidated != 0)

In MToken.sol, accountTokens[user] is affected by functions like mintFresh, redeemFresh, transferTokens, etc.
Consider this scenario :
a user is about to be liquidated by an admin using fixUser().
User can front run and uses transfer() to decrease the value of accountTokens[user] in their account.
transfer > transferTokens

function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) {
/* Fail if transfer not allowed */
uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed);
}
/* Do not allow self-transfers */
if (src == dst) {
return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED);
}
/* ... */
(mathErr, srcTokensNew) = subUInt(accountTokens[src], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH);
}
(mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srcTokensNew;
accountTokens[dst] = dstTokensNew;
/* ... */
return uint(Error.NO_ERROR);
}

Impact

Users can eliminate their current borrow balance with minimal liquidation.
Over time, this will devalue the principal more and more.

Tools Used

Recommendations

Updates

Lead Judging Commences

0xnevi Lead Judge
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-front-run-fixUser

icebear Submitter
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
Assigned finding tags:

finding-front-run-fixUser

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.