The getRoundPhase() function in the BuyerAgent contract incorrectly accumulates the total number of rounds by adding an unnecessary +1 in each iteration, without checking if the the result got rounded down, because of Solidity decimal handling. This miscalculation inflates the total round count, leading to incorrect determination of the current round and phase. Consequently, this can cause the contract to behave unexpectedly, potentially disrupting the protocol's operation and affecting users' interactions.
The function aims to determine the current round, phase, and the time until the next phase based on elapsed time and market parameters. The issue arises from the way the total rounds are accumulated:
Incorrect Accumulation Logic:
The innerRound variable already represents the number of complete rounds within a given time interval. Adding +1 unnecessarily increments the total round count, leading to an inflated number. This addition seems to be required if we didn't have whole numbers (i.e 10) and the result has a decimal part (i.e. 10,5), which will get truncated. Since we want next/new round has round the +1 offset makes sense in order to point to the correct number of rounds. However, if the result didn't have a decimal part (i.e. was exactly 10), we add an +1 offset that results in miscalculations of rounds.
Let's consider a concrete example to demonstrate how this incorrect accumulation affects the round calculation.
Time Intervals:
t0 (createdAt) = 0
t1 = 1000
t2 = 2000
t3 (block.timestamp) = 3000
Market Parameters (marketParams):
marketParams[0]: Active from t0 to t1 with cycleTime0
marketParams[1]: Active from t1 to t2 with cycleTime1
marketParams[2]: Active from t2 onwards with cycleTime2
Cycle Times:
Under marketParams[0]: cycleTime0 = 100
Under marketParams[1]: cycleTime1 = 200
Under marketParams[2]: cycleTime2 = 100
Rounds under marketParams[0]:
Rounds under marketParams[1]:
Rounds under marketParams[2]:
Total Expected Rounds:
Using the incorrect accumulation logic from the code:
First Iteration:
Accumulating Rounds with '+1':
First Accumulation:
Second Accumulation:
Total Rounds Calculated:
Inflated Total Rounds:
Expected Total Rounds: 25
Calculated Total Rounds: 27
The extra +1 in each accumulation step adds unnecessary rounds, causing the total round count to be higher than it should be.
The incorrect addition of +1 when accumulating rounds is the root cause of the miscalculation:
The innerRound variable already accounts for the complete rounds within the elapsed time. Adding +1 effectively counts an extra round that doesn't exist.
The issue leads to the BuyerAgent completely skipping some rounds and participate in later rounds. Furthermore, the rounds that are returned don't correlate to the correct round.
Manual Review
The +1 offset appears to be indeed required in scenarios where the decimal precision is lost in Solidity. However, when the result doesn't get rounded down and doesn't have a decimal part, the offset is not needed and leads to wrong round calculations.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.