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: 34/114
Findings: 1
Award: $304.58
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
304.5822 USDC - $304.58
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/Funding.sol#L21
address public immutable ajnaTokenAddress = 0x9a96ec9B57Fb64FbC60B423d1f4da7691Bd35079;
if(block.number < _getChallengeStageEndBlock(currentDistribution.endBlock))`
_getChallengeStageEndBlock
should be included as it is everywhere else
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L179-L180
toBucket.lpsAtStakeTime = uint128(positionManager.getLP(tokenId_, toIndex)); toBucket.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(toIndex));
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L235-L241
// record the number of lps in bucket at the time of staking bucketState.lpsAtStakeTime = uint128(positionManager.getLP( tokenId_, bucketId )); // record the bucket exchange rate at the time of staking bucketState.rateAtStakeTime = uint128(IPool(ajnaPool).bucketExchangeRate(bucketId));
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L262-L333
In moveLiquidity
there is a check if the owner attempts to move liquidity after they've already done so
if (vars.depositTime == 0) revert RemovePositionFailed();
which means the expected behaviour is to set fromPosition.depositTime
to 0 at the end of the function when the liquidity is transferred to toPosition
. This never happens and the check will pass every time. memorializePositions
and reedemPositions
will also have unexpected behaviour because they also have this validation(and others that include depositTime
).
IERC20(ajnaTokenAddress).safeTransfer(msg.sender, rewardClaimed_);
safeTransfer
will always be called, even when not needed(when rewardClaimed_
is 0)
Check that rewardClaimed_
!= 0 before safeTransfer
.
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L6
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L54-L55
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L166
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L258
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L348
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/PositionManager.sol#L225 (Transfer
event is also emitted)
New stake position with valid tokenId and with 0 buckets (positionIndexes length is zero) can be opened in RewardsManager contract. For loop will be skipped and transfering of the nft will be successful. The contract will receive nft with zero bucket which is fully unusuable in the future.
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-core/src/RewardsManager.sol#L207
math.wad - getMinimumThresholdPercentage()
can revert if _extraordinaryFundingProposals
has length around 191 and more. This will make function proposeExtraordinary
unusuable in the future.
My recommendation is to have maximum limit of minimum threshold percentage. For example, if length of _extraordinaryFundingProposals
is too big to return specifci defined constant for minimum threshold percentage
code
#0 - c4-judge
2023-05-18T18:37:44Z
Picodes marked the issue as grade-a