Open code423n4 opened 1 year ago
Picodes marked the issue as duplicate of #263
Picodes marked the issue as duplicate of #263
Picodes changed the severity to 3 (High Risk)
Picodes marked the issue as satisfactory
Picodes marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/StandardFunding.sol#L236-L265 https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/base/StandardFunding.sol#L216-L217
Vulnerability details
Impact
Each period reserves a reward for granting up to 3% (GBC: Global Budget Constraint). The GBC is split into two parts:
Voters who have participated can claim their reward after the period has ended via
claimDelegateReward()
. However, the claim function does not account for the claimed reward towards treasury granting. As a result, the treasury technically reserves up to 90% in each period while actually granting 100%.Consider this example:
1000 AJNA
. 3% is reserved for this period, resulting in a GBC of30 AJNA
. The treasury is updated to1000 - 30 = 970 AJNA
.27 AJNA
) and 10% is for voters (3 AJNA
).27 AJNA
are fully granted among winning proposals.0.3 AJNA
, totaling3 AJNA
.27 AJNA + 3 AJNA
, leaving an actual balance of970 AJNA
.970 += (30 - 27)
=973
.973 AJNA
while having only970 AJNA
in actuality.More detailed analysis
When the current period has ended and before starting a new one, the treasury will re-account its amount in case the last period did not utilize all the reserved reward. For example, if the last period granted only 80% of the GBC among winning proposals, the remaining 10% will be re-added to the treasury.
In the code block above,
fundsAvailable
represents 100% of the GBC andtotalTokensRequested
represents up to 90% of the GBC. As a result, the treasury always adds 10% of the reserve back to its accounting.Proof of Concept
The following PoC code is quite long because it must go through all stages. Please append and run this function in the file
ajna-grants/test/unit/StandardFunding.t.sol
. The test should pass without errors.Tools Used
Recommended Mitigation Steps
If it is safe to assume that all periods will always have 10% for delegation rewards, the contract should calculate only 90% of
fundsAvailable
when updating the treasury.Remark
The
claimDelegateReward()
function usesMaths.wmul()
, which automatically rounds the multiplication result up or down. For example,Maths.wmul(1, 0.5 * 1e18) = 1
(rounding up) whileMaths.wmul(1, 0.49 * 1e18) = 0
(rounding down). As a result,rewardClaimed_
can lose precision for small decimal amounts and token holders typically have small fractions of tokens down to1 wei
. It is uncertain, but the total actual paid rewards could be more than 10% if rounded up, resulting in an insignificant loss of precision in the treasury. However, ifrewardClaimed_
is deducted fromfundsAvailable
, it could lead to an integer underflow revert iffundsAvailable - totalClaimed - totalTokensRequested = 100% - 10.xx% - 90%
, which exceeds 100%.Assessed type
Math