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: 61/114
Findings: 2
Award: $70.26
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xTheC0der
Also found by: DadeKuma, Haipls, SpicyMeatball, ToonVH, aviggiano, azhar, evmboi32, juancito, kodyvim, ro1sharkm, rvierdiiev, sakshamguruji
34.0183 USDC - $34.02
Judge has assessed an item in Issue #467 as 2 risk. The relevant finding follows:
[L-03] Anyone can memorialize other users' position if the owner approves PositionManager There isn't a check to ensure that the caller is the actual owner of the position, so anyone can memorialize a position if the original owner approves PositionManager.
Modify ajna-core/tests/forge/unit/PositionManager.t.sol with the following and the test will not fail:
File: ajna-core\tests\forge\unit\PositionManager.t.sol
File: ajna-grants/src/grants/base/StandardFunding.sol
170: /** 171: * @notice Get the block number at which this distribution period's challenge stage ends. 172: * @param endBlock_ The end block of quarterly distribution to get the challenge stage end block for. 173: * @return The block number at which this distribution period's challenge stage ends. 174: / 175: function getChallengeStageEndBlock( 176: uint256 endBlock 177: ) internal pure returns (uint256) { 178: return endBlock_ + CHALLENGE_PERIOD_LENGTH; 179: } 180: 181: /* 182: * @notice Get the block number at which this distribution period's screening stage ends. 183: * @param endBlock_ The end block of quarterly distribution to get the screening stage end block for. 184: * @return The block number at which this distribution period's screening stage ends. 185: / 186: function getScreeningStageEndBlock( 187: uint256 endBlock 188: ) internal pure returns (uint256) { 189: return endBlock_ - FUNDING_PERIOD_LENGTH; 190: } 191: 192: /* 193: * @notice Updates Treasury with surplus funds from distribution. 194: * @dev Counters incremented in an unchecked block due to being bounded by array length of at most 10. 195: * @param distributionId_ distribution Id of updating distribution 196: */ 197: function updateTreasury( 198: uint24 distributionId 199: ) private { 200: bytes32 fundedSlateHash = distributions[distributionId].fundedSlateHash; 201: uint256 fundsAvailable = distributions[distributionId].fundsAvailable; 202: 203: uint256[] memory fundingProposalIds = _fundedProposalSlates[fundedSlateHash]; 204: 205: uint256 totalTokensRequested; 206: uint256 numFundedProposals = fundingProposalIds.length; 207: 208: for (uint i = 0; i < numFundedProposals; ) { 209: Proposal memory proposal = _standardFundingProposals[fundingProposalIds[i]]; 210: 211: totalTokensRequested += proposal.tokensRequested; 212: 213: unchecked { ++i; } 214: } 215: 216: // readd non distributed tokens to the treasury
ajna-grants/src/grants/base/StandardFunding.sol#L170-L216
#0 - c4-judge
2023-05-18T18:33:39Z
Picodes marked the issue as duplicate of #356
#1 - c4-judge
2023-05-30T21:47:16Z
Picodes marked the issue as duplicate of #488
#2 - c4-judge
2023-05-30T21:47:49Z
Picodes marked the issue as satisfactory
#3 - c4-judge
2023-05-30T21:47:53Z
Picodes marked the issue as partial-50
#4 - c4-judge
2023-05-30T21:48:18Z
Picodes changed the severity to 3 (High Risk)
🌟 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
Id | Title | Instances |
---|---|---|
[L-01] | Revert by undeflow when extraordinary proposals exceeds the maximum amount | 1 |
[L-02] | Wasteful/redundant operations when calculating the votes squared | 1 |
[L-03] | Anyone can memorialize other users' position if the owner approves PositionManager | 1 |
Total: 3 instances over 3 issues.
Id | Title | Instances |
---|---|---|
[NC-01] | newTopSlate_ condition can be simplified | 1 |
Total: 1 instances over 1 issues.
Add a custom revert when proposals exceed the maximum, as it's possible to send only 9 proposals, to avoid an underflow error. Check if _getMinimumThresholdPercentage() >= Maths.WAD
, and revert if that's the case.
There is 1 instance of this issue.
File: ajna-grants/src/grants/base/ExtraordinaryFunding.sol 105: if (uint256(totalTokensRequested) > _getSliceOfTreasury(Maths.WAD - _getMinimumThresholdPercentage())) revert InvalidProposal();
ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L105
The power of n
with an exponent of 2 is always positive, regardless of n
(it can be either positive or negative), so calculating the absolute value is wasteful (Maths.abs
)
There is 1 instance of this issue.
File: ajna-grants/src/grants/base/StandardFunding.sol 849: votesCastSumSquared_ += Maths.wpow(SafeCast.toUint256(Maths.abs(votesCast_[i].votesUsed)), 2);
ajna-grants/src/grants/base/StandardFunding.sol#L849
memorialize
other users' position if the owner approves PositionManager
There isn't a check to ensure that the caller is the actual owner of the position, so anyone can memorialize
a position if the original owner approves PositionManager
.
Modify ajna-core/tests/forge/unit/PositionManager.t.sol
with the following and the test will not fail:
File: ajna-core\tests\forge\unit\PositionManager.t.sol - 176: vm.expectEmit(true, true, true, true); - 177: emit MemorializePosition(testAddress, tokenId, indexes); + 176: vm.stopPrank(); + 177: vm.prank(address(123456)); //not owner 178: _positionManager.memorializePositions(memorializeParams);
There is 1 instance of this issue.
File: ajna-grants/src/grants/base/StandardFunding.sol 170: /** 171: * @notice Get the block number at which this distribution period's challenge stage ends. 172: * @param endBlock_ The end block of quarterly distribution to get the challenge stage end block for. 173: * @return The block number at which this distribution period's challenge stage ends. 174: */ 175: function _getChallengeStageEndBlock( 176: uint256 endBlock_ 177: ) internal pure returns (uint256) { 178: return endBlock_ + CHALLENGE_PERIOD_LENGTH; 179: } 180: 181: /** 182: * @notice Get the block number at which this distribution period's screening stage ends. 183: * @param endBlock_ The end block of quarterly distribution to get the screening stage end block for. 184: * @return The block number at which this distribution period's screening stage ends. 185: */ 186: function _getScreeningStageEndBlock( 187: uint256 endBlock_ 188: ) internal pure returns (uint256) { 189: return endBlock_ - FUNDING_PERIOD_LENGTH; 190: } 191: 192: /** 193: * @notice Updates Treasury with surplus funds from distribution. 194: * @dev Counters incremented in an unchecked block due to being bounded by array length of at most 10. 195: * @param distributionId_ distribution Id of updating distribution 196: */ 197: function _updateTreasury( 198: uint24 distributionId_ 199: ) private { 200: bytes32 fundedSlateHash = _distributions[distributionId_].fundedSlateHash; 201: uint256 fundsAvailable = _distributions[distributionId_].fundsAvailable; 202: 203: uint256[] memory fundingProposalIds = _fundedProposalSlates[fundedSlateHash]; 204: 205: uint256 totalTokensRequested; 206: uint256 numFundedProposals = fundingProposalIds.length; 207: 208: for (uint i = 0; i < numFundedProposals; ) { 209: Proposal memory proposal = _standardFundingProposals[fundingProposalIds[i]]; 210: 211: totalTokensRequested += proposal.tokensRequested; 212: 213: unchecked { ++i; } 214: } 215: 216: // readd non distributed tokens to the treasury
ajna-grants/src/grants/base/StandardFunding.sol#L170-L216
newTopSlate_
condition can be simplifiedcurrentSlateHash!= 0
can be safely removed in the second part of the OR condition, as the condition would short circuit on the first expression (currentSlateHash == 0
) if that isn't the case.
There is 1 instance of this issue.
File: ajna-grants/src/grants/base/StandardFunding.sol 317: newTopSlate_ = currentSlateHash == 0 || 318: (currentSlateHash!= 0 && sum > _sumProposalFundingVotes(_fundedProposalSlates[currentSlateHash]));
ajna-grants/src/grants/base/StandardFunding.sol#L317-L318
#0 - DadeKuma
2023-05-11T21:25:05Z
Whops, I copied the wrong code for L-03, this is the correct one:
File: ajna-core/src/PositionManager.sol 170: function memorializePositions( 171: MemorializePositionsParams calldata params_ 172: ) external override { 173: EnumerableSet.UintSet storage positionIndex = positionIndexes[params_.tokenId]; 174: 175: IPool pool = IPool(poolKey[params_.tokenId]); 176: address owner = ownerOf(params_.tokenId); 177: 178: uint256 indexesLength = params_.indexes.length; 179: uint256 index; 180: 181: for (uint256 i = 0; i < indexesLength; ) { 182: index = params_.indexes[i]; 183: 184: // record bucket index at which a position has added liquidity 185: // slither-disable-next-line unused-return 186: positionIndex.add(index); 187: 188: (uint256 lpBalance, uint256 depositTime) = pool.lenderInfo(index, owner); 189: 190: Position memory position = positions[params_.tokenId][index]; 191: 192: // check for previous deposits 193: if (position.depositTime != 0) { 194: // check that bucket didn't go bankrupt after prior memorialization 195: if (_bucketBankruptAfterDeposit(pool, index, position.depositTime)) { 196: // if bucket did go bankrupt, zero out the LP tracked by position manager 197: position.lps = 0; 198: } 199: } 200: 201: // update token position LP 202: position.lps += lpBalance; 203: // set token's position deposit time to the original lender's deposit time 204: position.depositTime = depositTime; 205: 206: // save position in storage 207: positions[params_.tokenId][index] = position; 208: 209: unchecked { ++i; } 210: } 211: 212: // update pool LP accounting and transfer ownership of LP to PositionManager contract 213: pool.transferLP(owner, address(this), params_.indexes); 214: 215: emit MemorializePosition(owner, params_.tokenId, params_.indexes); 216: }
#1 - c4-judge
2023-05-18T18:33:12Z
Picodes marked the issue as grade-b