Platform: Code4rena
Start Date: 16/10/2023
Pot Size: $60,500 USDC
Total HM: 16
Participants: 131
Period: 10 days
Judge: 0xTheC0der
Total Solo HM: 3
Id: 296
League: ETH
Rank: 50/131
Findings: 2
Award: $91.30
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xpiken
Also found by: 0xCiphky, 0xComfyCat, 0xStalin, 0xhegel, 0xkazim, 3docSec, AM, Aymen0909, CaeraDenoir, DeFiHackLabs, Drynooo, Eigenvectors, Fulum, HALITUS, HChang26, Jiamin, Juntao, LokiThe5th, Mike_Bello90, MiloTruck, QiuhaoLi, Silvermist, SovaSlava, SpicyMeatball, T1MOH, Toshii, TrungOre, TuringConsulting, Vagner, Yanchuan, ZdravkoHr, _nd_koo, almurhasan, audityourcontracts, ayden, cartlex_, circlelooper, crunch, cu5t0mpeo, deth, erictee, ggg_ttt_hhh, gizzy, gumgumzum, hash, jasonxiale, josephdara, ke1caM, kodyvim, lanrebayode77, marqymarq10, max10afternoon, nirlin, nonseodion, osmanozdemir1, peter, radev_sw, rvierdiiev, said, serial-coder, sl1, smiling_heretic, squeaky_cactus, stackachu, tallo, trachev, zaevlad
0.0606 USDC - $0.06
Market can not be closed.
The closeMarket method in WildcatMarket.sol sets the market APR to 0% and marks market as closed.
function closeMarket() external onlyController nonReentrant { MarketState memory state = _getUpdatedState(); state.annualInterestBips = 0; state.isClosed = true; state.reserveRatioBips = 0; if (_withdrawalData.unpaidBatches.length() > 0) { revert CloseMarketWithUnpaidWithdrawals(); } uint256 currentlyHeld = totalAssets(); uint256 totalDebts = state.totalDebts(); if (currentlyHeld < totalDebts) { // Transfer remaining debts from borrower asset.safeTransferFrom(borrower, address(this), totalDebts - currentlyHeld); } else if (currentlyHeld > totalDebts) { // Transfer excess assets to borrower asset.safeTransfer(borrower, currentlyHeld - totalDebts); } _writeState(state); emit MarketClosed(block.timestamp); }
However, this method can not be called directly, there is modifier onlyController so it can only be called by controller:
modifier onlyController() { if (msg.sender != controller) revert NotController(); _; }
This controller is WildcatMarketController:
/// @dev Address of the Market Controller. address public immutable controller;
and is initialized when the market is deployed by WildcatMarketController:
controller = parameters.controller; `` The problem is that closeMarket method is not called in WildcatMarketController, so there is no way to close a market. ## Tools Used Manual Review ## Recommended Mitigation Steps To mitigate this issue, an method should be defined to call closeMarket method from WildcatMarketController. ## Assessed type Access Control
#0 - c4-pre-sort
2023-10-27T07:25:53Z
minhquanym marked the issue as duplicate of #147
#1 - c4-judge
2023-11-07T13:53:21Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-07T14:06:05Z
MarioPoneder marked the issue as partial-50
#3 - c4-judge
2023-11-07T14:16:53Z
MarioPoneder changed the severity to 3 (High Risk)
🌟 Selected for report: osmanozdemir1
Also found by: 0xCiphky, 0xStalin, HChang26, Infect3d, Jiamin, Juntao, QiuhaoLi, circlelooper, crunch, rvierdiiev
91.2409 USDC - $91.24
Should remove account's scaledBalance from market's scaledTotalSupply when an account is blocked, or borrower will pay unnecessary interest for the blocked account.
_blockAccount method is used to block an account:
function _blockAccount(MarketState memory state, address accountAddress) internal { Account memory account = _accounts[accountAddress]; if (account.approval != AuthRole.Blocked) { uint104 scaledBalance = account.scaledBalance; account.approval = AuthRole.Blocked; emit AuthorizationStatusUpdated(accountAddress, AuthRole.Blocked); if (scaledBalance > 0) { account.scaledBalance = 0; address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow( accountAddress, borrower, address(this) ); emit Transfer(accountAddress, escrow, state.normalizeAmount(scaledBalance)); _accounts[escrow].scaledBalance += scaledBalance; emit SanctionedAccountAssetsSentToEscrow( accountAddress, escrow, state.normalizeAmount(scaledBalance) ); } _accounts[accountAddress] = account; } }
If the account's scaledBalance is not 0, then the account's scaledBalance will be set to 0. As the blocked account's supply is 0, no interest should be accrued. The problem is that market state's scaledTotalSupply is not updated accordingly, and the interest a borrower needs to pay is calculated based on scaledTotalSupply, as wen can see from totalDebts method, that means interest still accrues unnecessarily even if an account is blocked:
function totalDebts(MarketState memory state) internal pure returns (uint256) { return state.normalizeAmount(state.scaledTotalSupply) + state.normalizedUnclaimedWithdrawals + state.accruedProtocolFees; }
Manual Review
To mitigate this issue, remove account's scaledBalance from market's scaledTotalSupply when an account is blocked
Math
#0 - c4-pre-sort
2023-10-28T14:37:53Z
minhquanym marked the issue as duplicate of #123
#1 - c4-judge
2023-11-07T18:14:26Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-07T18:18:08Z
MarioPoneder marked the issue as satisfactory