Platform: Code4rena
Start Date: 18/10/2023
Pot Size: $36,500 USDC
Total HM: 17
Participants: 77
Period: 7 days
Judge: MiloTruck
Total Solo HM: 5
Id: 297
League: ETH
Rank: 27/77
Findings: 2
Award: $188.94
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: falconhoof
Also found by: 0x6d6164616e, Baki, Beosin, Krace, T1MOH, bitsurfer, immeas, pep7siup, tnquanghuy0512, twicek
180.637 USDC - $180.64
IAccountingEngine
defines WAD
as equivalent to 100%, as indicated by the comment:
/// @notice Throws when surplus surplusTransferPercentage is great than WAD (100%) error AccEng_surplusTransferPercentOverLimit();
However, within the function auctionSurplus
, the code uses the constant ONE_HUNDRED_WAD
to calculate the _amountToSell
:
uint256 internal constant ONE_HUNDRED_WAD = 100 * WAD;
This misuse of WAD
and ONE_HUNDRED_WAD
enforces a requirement that the _amountToSell
must be greater than 9900% of _params.surplusAmount
, thereby directly impacting the normal contract logic.
auctionSurplus
stipulates that _params.surplusTransferPercentage
must be less than or equal to WAD
.
function auctionSurplus() external returns (uint256 _id) { if(_params.surplusTransferPercentage > WAD) revert AccEng_surplusTransferPercentOverLimit();
However, during the calculation of _amountToSell
, the code employs ONE_HUNDRED_WAD
to compute the percentage. Because _params.surplusTransferPercentage
is less than or equal to WAD
, the result of ONE_HUNDRED_WAD - _params.surplusTransferPercentage
must be greater than or equal to 99 * WAD
. Given that WAD
is defined as 100%, this outcome exceeds 9900%.
// auction surplus percentage if (_params.surplusTransferPercentage < ONE_HUNDRED_WAD) { _id = surplusAuctionHouse.startAuction({ _amountToSell: //@audit: 100WAD - surplusTransferPercentage >= 99WAD _params.surplusAmount.wmul(ONE_HUNDRED_WAD - _params.surplusTransferPercentage), _initialBid: 0 }); lastSurplusTime = block.timestamp; emit AuctionSurplus(_id, 0, _params.surplusAmount.wmul(ONE_HUNDRED_WAD - _params.surplusTransferPercentage)); }
function wmul(uint256 _x, uint256 _y) internal pure returns (uint256 _wmul) { return (_x * _y) / WAD; }
vscode
To address this issue, it is advisable to utilize ONE_HUNDRED_WAD
as equivalent to 100% both for verifying the surplusTransferPercentage
and calculating the _amountToSell
.
Math
#0 - c4-pre-sort
2023-10-26T01:28:21Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-26T01:28:42Z
raymondfam marked the issue as duplicate of #26
#2 - c4-judge
2023-11-03T14:07:02Z
MiloTruck changed the severity to 3 (High Risk)
#3 - c4-judge
2023-11-03T14:08:36Z
MiloTruck marked the issue as satisfactory
🌟 Selected for report: MrPotatoMagic
Also found by: 0xMosh, 0xPsuedoPandit, 0xhacksmithh, 8olidity, Al-Qa-qa, Baki, Bughunter101, Krace, Stormreckson, T1MOH, Tendency, eeshenggoh, fibonacci, hals, immeas, kutugu, lsaudit, m4k2, mrudenko, okolicodes, phoenixV110, spark, twicek, xAriextz
8.3007 USDC - $8.30
Total Low Risk Issues | 2 |
---|
Total Non-Critical Issues | 2 |
---|
auctionSurplus()
will calculate the _rad
by: surplusAmount
* surplusTransferPercentage
/ WAD
. But there is no checks to ensure that the result of multiplication is bigger than WAD
, so precision loss may occur when the owner wrongly sets the _params
.
if (_params.surplusTransferPercentage > 0) { if (extraSurplusReceiver == address(0)) revert AccEng_NullSurplusReceiver(); safeEngine.transferInternalCoins({ _source: address(this), _destination: extraSurplusReceiver, _rad: _params.surplusAmount.wmul(_params.surplusTransferPercentage) // @audit if surplusTransferPercentage * surplusAmount is less than WAD, precisionl will loss }); lastSurplusTime = block.timestamp; emit TransferSurplus(extraSurplusReceiver, _params.surplusAmount.wmul(_params.surplusTransferPercentage)); }
function wmul(uint256 _x, uint256 _y) internal pure returns (uint256 _wmul) { return (_x * _y) / WAD; }
initializeManager
and initializeRenderer
can be invoked by anyone when the address is not set.
Attack could frontrun this function to set the corresponding address.
/** * @dev initializes SafeManager contract */ function initializeManager() external { if (address(safeManager) == address(0)) _setSafeManager(msg.sender); }
/** * @dev initializes NFTRenderer contract */ function initializeRenderer() external { if (address(nftRenderer) == address(0)) _setNftRenderer(msg.sender); }
auctionSurplus()
has a redundant checks at line 201 and line 225. And the second check is useless.
function auctionSurplus() external returns (uint256 _id) { if(_params.surplusTransferPercentage > WAD) revert AccEng_surplusTransferPercentOverLimit(); if (_params.surplusAmount == 0) revert AccEng_NullAmount(); if (extraSurplusReceiver == address(0)) revert AccEng_NullSurplusReceiver(); //@audit redundant [....] // transfer surplus percentage if (_params.surplusTransferPercentage > 0) { if (extraSurplusReceiver == address(0)) revert AccEng_NullSurplusReceiver(); //@audit redundant
This check is useless because there is a more strict check at the begin of function auctionSurplus
.
function auctionSurplus() external returns (uint256 _id) { if(_params.surplusTransferPercentage > WAD) revert AccEng_surplusTransferPercentOverLimit(); //@audit this requires that surplusTransferPercentage <= WAD if (_params.surplusAmount == 0) revert AccEng_NullAmount(); if (extraSurplusReceiver == address(0)) revert AccEng_NullSurplusReceiver(); if (block.timestamp < lastSurplusTime + _params.surplusDelay) revert AccEng_SurplusCooldown(); uint256 _coinBalance = safeEngine.coinBalance(address(this)); uint256 _debtBalance = safeEngine.debtBalance(address(this)); (_coinBalance, _debtBalance) = _settleDebt(_coinBalance, _debtBalance, _unqueuedUnauctionedDebt(_debtBalance)); if (_coinBalance < _debtBalance + _params.surplusAmount + _params.surplusBuffer) { revert AccEng_InsufficientSurplus(); } // auction surplus percentage if (_params.surplusTransferPercentage < ONE_HUNDRED_WAD) { //@audit this is useless because surplusTransferPercentage is strictly less than WAD, ONE_HUNDRED_WAD is 100*WAD _id = surplusAuctionHouse.startAuction({ _amountToSell: _params.surplusAmount.wmul(ONE_HUNDRED_WAD - _params.surplusTransferPercentage), _initialBid: 0 }); lastSurplusTime = block.timestamp; emit AuctionSurplus(_id, 0, _params.surplusAmount.wmul(ONE_HUNDRED_WAD - _params.surplusTransferPercentage)); }
#0 - c4-pre-sort
2023-10-27T01:28:45Z
raymondfam marked the issue as low quality report
#1 - c4-judge
2023-11-03T16:56:47Z
MiloTruck marked the issue as grade-b