Closed c4-bot-2 closed 10 months ago
141345 marked the issue as sufficient quality report
0 amount transfer due to rounding. edge case with revert on 0 amount transfer token.
141345 marked the issue as primary issue
Alec1017 (sponsor) confirmed
Per ERC20 spec -
Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.
0xean marked the issue as unsatisfactory: Insufficient quality
Lines of code
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L159-L179 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L132-L146 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100-L118
Vulnerability details
Impact
The
PaymentEscrow
contract is responsible for escrowing rental payments while rentals are active. When rentals are stopped, this module determines payouts to all parties. ForPAY
orders that are stopped before the rental period has ended, the_settlePaymentProRata()
function in this contract calculates the pro-rata payment for the renter and lender and transfers the respective amounts.However, there is a potential issue with the implementation of this function. It first calls the internal function
_calculatePaymentProRata()
to calculate the renter and lender amounts. Due to the precision loss in the calculation, there can be scenarios where either therenterAmount
or thelenderAmount
becomes zero, both close to the beginning and the end of the total duration of the rental.The problem arises when these zero amounts are passed on to the
_safeTransfer()
function. Some ERC20 tokens revert when attempting to transfer 0 tokens. If such a token is used as payment in aPAY
order, the transaction will revert at certain periods, preventing the renter or lender from stopping the rental. In an (unrealistic) worst-case scenario, this locks the funds until the rental period is over.Proof of Concept
Let's consider the extreme case of a
PAY
order withamount = 1
andtotalTime = 1000
. The calculation ofrenterAmount
in the original code is as follows:$$ \begin{aligned} \text{numerator} &= (\text{amount} \times \text{elapsedTime}) \times 1000, \ \text{renterAmount} &= \left(\frac{\text{numerator}}{\text{totalTime}} + 500\right) / 1000. \end{aligned} $$
Substituting
amount = 1
andtotalTime = 1000
, we get:$$ \begin{aligned} \text{numerator} &= (1 \times \text{elapsedTime}) \times 1000, \ \text{renterAmount} &= \left(\frac{\text{elapsedTime} \times 1000}{1000} + 500\right) / 1000 \ &= \frac{\text{elapsedTime} + 500}{1000}. \end{aligned} $$
For
elapsedTime < 500
, therenterAmount
becomes less than 1, as the addition of500
and division by1000
results in a value less than 1. SincerenterAmount
must be an integer, it will be rounded down to 0.Conversely, for
elapsedTime ≥ 500
,renterAmount
will be 1, as the sum ofelapsedTime
and500
will be 1000 or more, making the division result 1. This leaves no value forlenderAmount
, effectively making it 0.To summarize, for
elapsedTime
< 500,renterAmount
will be zero, and forelapsedTime
>= 500,lenderAmount
will be 0. If the ERC20 token used in thisPAY
order reverts on zero value transfers, it would not be possible to stop the rental at any point until it is over.While this is an extreme scenario, the issue does also arise for less extremes values of
amount
andtotalTime
and leads to periods during which an order cannot be stopped.Tools Used
Manual review
Recommended Mitigation Steps
A simple fix would be to add a check for zero value amounts in the
_safeTransfer()
function and skip the transfer if the amount is zero. This would prevent the transaction from reverting and allow the rental to be stopped as expected.Assessed type
ERC20