Platform: Code4rena
Start Date: 03/05/2023
Pot Size: $60,500 USDC
Total HM: 25
Participants: 114
Period: 8 days
Judge: Picodes
Total Solo HM: 6
Id: 234
League: ETH
Rank: 46/114
Findings: 2
Award: $223.47
🌟 Selected for report: 0
🚀 Solo Findings: 0
187.2333 USDC - $187.23
The treasury
variable has accounting problems when the delegators claim their rewards of previous distribution(s) after a new distribution period has started, leading to a number of issues with the Ajna system.
The treasury is a critical component of the Ajna ecosystem, as it is used to fund grants, pay for development and operational expenses. The accounting problems with the treasury variable results in inaccurate reporting of funds available for grants or other expenses. This could lead to overcommitment of funds or insufficient funding for critical projects. Additionally, if there are discrepancies between the reported treasury balance and the actual balance of funds held in reserve, this could erode trust in the Ajna system and damage its reputation. The incorrect handling of the treasury variable has serious implications for the overall stability and functionality of the protocol.
The accurate management of the treasury balance is of utmost importance in the precise calculation of token distribution within each quarter, adhering to the guidelines stipulated by the Global Budgetary Constraint (GBC) outlined on page 33 of the whitepaper. Furthermore, it serves as a crucial factor in assessing whether a proposal meets the minimum withdrawal threshold, as described on page 35.
Given the vital role the treasury balance plays in key protocol functions, such as funds allocation, proposal thresholds, and extraordinary funding mechanisms, any vulnerability in this area poses a significant concern.
Gist link to the PoC test file: https://gist.github.com/0x3agle/cc2e90f498bedbbec632a2f460b5cd20
Note: Save this file to "2023-05-ajna/ajna-grants/test/unit". Run the PoC with "forge test --match-contract TreasuryAccounting -vvv"
The process of a distribution period in Ajna's grant coordination mechanism:
[+] Intended behaviour of the treasury funds ------------------ Distribution ID 0 started Current Funds: 500000000000000000000000000 Distribution ID 0 ended ------------------ Distribution ID 1 started Current Funds: 485000000000000000000000000 Distribution ID 1 ended ------------------ Distribution ID 2 started Current Funds: 470450000000000000000000000 Previous voter[1] balance 431846446912702651 User claims delegate reward New voter[1] balance 47706036425531786009850 Current Funds: 470402294395420915126692801 //Notice the change 47045 -> 47040 Distribution ID 2 ended ------------------
But, here's what actually happens:
Distribution 1
in Distribution 2
, the treasury
variable is not being updated.[+] Actual behaviour of the treasury funds Distribution ID 1 started Current Funds: 500000000000000000000000000 Distribution ID 1 ended ------------------ Treasury balance before distribution ID 2 started: 485000000000000000000000000 Distribution ID 2 started Treasury balance after distribution ID 2 started: 481120000000000000000000000 ------------------ Previous voter[1] balance 431846446912702651 User claims delegate reward New voter[1] balance 47706036425531786009850 ------------------ Treasury balance after user claims reward: 481120000000000000000000000 //No change ------------------
Conclusion: This issue persists for every delegate in every distribution, therefore it can be classified as a high severity bug in Ajna Grants due to incorrect accounting in
treasury
variable.
Manual Review and Foundry
mapping (address delegate => mapping(uint distributionId => uint reward)) rewards
Math
#0 - c4-judge
2023-05-18T09:57:10Z
Picodes marked the issue as duplicate of #263
#1 - c4-judge
2023-05-30T18:09:17Z
Picodes marked the issue as duplicate of #263
#2 - c4-judge
2023-05-30T18:14:06Z
Picodes marked the issue as satisfactory
🌟 Selected for report: rbserver
Also found by: 0xnev, ABAIKUNANBAEV, Audit_Avengers, Aymen0909, BGSecurity, BRONZEDISC, Bason, DadeKuma, GG_Security, Jerry0x, Jorgect, MohammedRizwan, REACH, Sathish9098, Shogoki, T1MOH, UniversalCrypto, aviggiano, ayden, berlin-101, bytes032, codeslide, descharre, fatherOfBlocks, hals, kaveyjoe, kodyvim, lfzkoala, lukris02, nadin, naman1778, patitonar, pontifex, sakshamguruji, squeaky_cactus, teawaterwire, wonjun, yjrwkk
36.2377 USDC - $36.24
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L227-L241
Within PositionManager.sol
, the mint function does not check for liquidity pool positions. This could lead to future vulnerabilities if improvements are made without this in mind.
We recommend checking for liquidity prior to minting an NFT.
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L207-L245
Within RewardsManager.sol
, an empty NFT can be staked. Rewards cannot be distributed, thus we are marking this as a low. However, similar to L-01, care must be taken here with future upgrades.
Mitigation from L-01 will suffice.
The blocks/minute is variable within Ethereum, and may even change drastically with forks.
As it stands, the average block is mined in 12.11 seconds today, down from 13.56 a year ago.
If you want the block count to remain constant, then you should update the documentation to reflect that. If you want the time(days/seconds/etc.) to remain constant, then you should be able to change the block numbers in period lengths. Adding setter functions and removing the constant
modifier would suffice.
E.g., At 15 seconds per block (4 per minute), within StandardFunding.sol
, the FUNDING_PERIOD_LENGTH should be 57600 for ~10 days. At 12.11 seconds, it should be 70965 blocks. It is set to 72000.
GLOBAL_BUDGET_CONSTRAINT
does not match documentationFrom the docs pertaining to the Primary Funding Mechanism:
"Each quarter (90 days), up to a 2% of the treasury can be distributed to projects that win a competitive bidding process..."
However in PrimaryFundingMechanism.sol,
/** * @notice Maximum percentage of tokens that can be distributed by the treasury in a quarter. * @dev Stored as a Wad percentage. */ uint256 internal constant GLOBAL_BUDGET_CONSTRAINT = 0.03 * 1e18;
3% is set. We recommend changing this to 2%, or updating the documentation.
#0 - c4-judge
2023-05-18T18:35:11Z
Picodes marked the issue as grade-b