Platform: Code4rena
Start Date: 21/02/2024
Pot Size: $200,000 USDC
Total HM: 22
Participants: 36
Period: 19 days
Judge: Trust
Total Solo HM: 12
Id: 330
League: ETH
Rank: 19/36
Findings: 1
Award: $940.15
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: t0x1c
Also found by: DanielArmstrong
940.1462 USDC - $940.15
https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L127 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L44
User cannot repay all debt because at least 1 wei
of borrowShare
remains.
When repaying all debt with AaveHub.sol#paybackExactAmountETH
, 1 wei
of borrowShare
remains.
AaveHub.sol#paybackExactAmountETH
function which repays debt is as follows.
function paybackExactAmountETH( uint256 _nftId ) external payable nonReentrant returns (uint256) { _checkPositionLocked( _nftId ); address aaveWrappedETH = aaveTokenAddress[ WETH_ADDRESS ]; 498 uint256 userBorrowShares = WISE_LENDING.getPositionBorrowShares( _nftId, aaveWrappedETH ); WISE_LENDING.syncManually( aaveWrappedETH ); 507 uint256 maxPaybackAmount = WISE_LENDING.paybackAmount( aaveWrappedETH, userBorrowShares ); ( uint256 paybackAmount, uint256 ethRefundAmount ) = _getInfoPayback( msg.value, maxPaybackAmount ); _wrapETH( paybackAmount ); uint256 actualAmountDeposit = _wrapAaveReturnValueDeposit( WETH_ADDRESS, paybackAmount, address(this) ); 531 uint256 borrowSharesReduction = WISE_LENDING.paybackExactAmount( _nftId, aaveWrappedETH, actualAmountDeposit ); if (ethRefundAmount > 0) { _sendValue( msg.sender, ethRefundAmount ); } emit IsPaybackAave( _nftId, block.timestamp ); return borrowSharesReduction; }
On L498, we say that userBorrowShare
is 10
.
And on 507 the equation which calculates paybackAmount
from borrowShare
is userBorrowShare * pseudoTotalBorrowAmount / totalBorrowShares + 1
.
Here if we say pseudoTotalBorrowAmount
= 100, totalBorrowShares
= 50, maxPaybackAmount
becomes 20 + 1
.
And we say actualAmountDeposit = paybackAmount = maxPaybackAmount
.
And the equation which calculates borrowShares
from paybackAmount
is maxPaybackAmount * totalBorrowShares / pseudoTotalBorrowAmount - 1
.
So borrowSharesReduction
becomes (20 + 1) * 50 / 100 - 1 = 10 - 1
.
Therefore, userBorrowShares
cannot be removed and 1 wei
of borrowShare remains.
Manual Review
We have to modify all processes of rounding up or down correctly. For example, mitigation steps for this problem are as follows.
MainHelper.sol#paybackAmount
function as follows.function paybackAmount( address _poolToken, uint256 _shares ) public view returns (uint256) { uint256 product = _shares * borrowPoolData[_poolToken].pseudoTotalBorrowAmount; uint256 totalBorrowShares = borrowPoolData[_poolToken].totalBorrowShares; - return product / totalBorrowShares + 1; + returnn (product + totalBorrowShares - 1) / totalBorrowShares; }
MainHelper.sol#_calculateShares
function as follows.function _calculateShares( uint256 _product, uint256 _pseudo, bool _maxSharePrice ) private pure returns (uint256) { return _maxSharePrice == true - ? _product / _pseudo + 1 - : _product / _pseudo - 1; + ? (_product + _pseudo - 1) / _pseudo + : _product / _pseudo; }
Math
#0 - c4-pre-sort
2024-03-17T15:48:14Z
GalloDaSballo marked the issue as duplicate of #27
#1 - c4-pre-sort
2024-03-18T16:31:06Z
GalloDaSballo marked the issue as sufficient quality report
#2 - trust1995
2024-03-26T19:06:50Z
A much weaker version of the selected report.
#3 - c4-judge
2024-03-26T19:07:01Z
trust1995 marked the issue as partial-50