Platform: Code4rena
Start Date: 27/10/2022
Pot Size: $33,500 USDC
Total HM: 8
Participants: 96
Period: 3 days
Judge: kirk-baird
Total Solo HM: 1
Id: 176
League: ETH
Rank: 1/96
Findings: 5
Award: $5,767.74
🌟 Selected for report: 2
🚀 Solo Findings: 0
🌟 Selected for report: ladboy233
Also found by: 0x52, 0xDecorativePineapple, 0xhunter, Aymen0909, Bnke0x0, Dravee, JTJabba, Jeiwan, Lambda, Nyx, Picodes, Trust, cccz, cryptonue, csanuragjain, dic0de, hansfriese, horsefacts, imare, minhtrng, pashov, peritoflores, rbserver, rvierdiiev, wagmi, yixxas
9.9073 USDC - $9.91
Owner can rug all pledges
function removeRewardToken(address token) external onlyOwner { if(token == address(0)) revert Errors.ZeroAddress(); if(minAmountRewardToken[token] == 0) revert Errors.NotAllowedToken(); minAmountRewardToken[token] = 0; emit RemoveRewardToken(token); } function recoverERC20(address token) external onlyOwner returns(bool) { if(minAmountRewardToken[token] != 0) revert Errors.CannotRecoverToken(); uint256 amount = IERC20(token).balanceOf(address(this)); if(amount == 0) revert Errors.NullValue(); IERC20(token).safeTransfer(owner(), amount); return true; }
Owner can rug all pledge creators by delisting reward token to remove it's protection then calling recoverERC20 to take the entire balance. In the event that the owner wallet is compromised all user funds would be stolen.
Manual Review
Delisted assets should be added to a mapping. When calling recoverERC20, it should revert if mapping returns true for that asset. This protects users funds even after a reward token is delisted.
#0 - Kogaroshi
2022-10-31T00:06:11Z
Duplicate of #17
#1 - c4-judge
2022-11-10T07:11:48Z
kirk-baird marked the issue as not a duplicate
#2 - c4-judge
2022-11-10T07:11:55Z
kirk-baird marked the issue as duplicate
#3 - c4-judge
2022-11-10T21:21:49Z
kirk-baird marked the issue as satisfactory
#4 - c4-judge
2022-11-10T21:21:55Z
kirk-baird marked the issue as not a duplicate
#5 - c4-judge
2022-11-10T21:22:01Z
kirk-baird marked the issue as duplicate of #17
#6 - c4-judge
2022-12-06T17:32:42Z
Simon-Busch marked the issue as duplicate of #68
2421.9641 USDC - $2,421.96
Delisted reward tokens can continue to be use by extending current pledges that already use it
if(pledgeId >= pledgesIndex()) revert Errors.InvalidPledgeID(); address creator = pledgeOwner[pledgeId]; if(msg.sender != creator) revert Errors.NotPledgeCreator(); Pledge storage pledgeParams = pledges[pledgeId]; if(pledgeParams.closed) revert Errors.PledgeClosed(); if(pledgeParams.endTimestamp <= block.timestamp) revert Errors.ExpiredPledge(); if(newEndTimestamp == 0) revert Errors.NullEndTimestamp(); uint256 oldEndTimestamp = pledgeParams.endTimestamp; if(newEndTimestamp != _getRoundedTimestamp(newEndTimestamp) || newEndTimestamp < oldEndTimestamp) revert Errors.InvalidEndTimestamp(); uint256 addedDuration = newEndTimestamp - oldEndTimestamp; if(addedDuration < minDelegationTime) revert Errors.DurationTooShort(); uint256 totalRewardAmount = (pledgeParams.rewardPerVote * pledgeParams.votesDifference * addedDuration) / UNIT; uint256 feeAmount = (totalRewardAmount * protocalFeeRatio) / MAX_PCT ; if(totalRewardAmount > maxTotalRewardAmount) revert Errors.IncorrectMaxTotalRewardAmount(); if(feeAmount > maxFeeAmount) revert Errors.IncorrectMaxFeeAmount();
During the input validation checks, it's never checked that reward token of the pledge being extended is still a valid reward token. This would allow creators using delisted tokens to continue using them as long as they wanted, by simply extending their currently active pledges.
Manual Review
Add the following check during the input validation block:
+ if(minAmountRewardToken[rewardToken] == 0) revert Errors.TokenNotWhitelisted();
#1 - c4-judge
2022-11-11T07:35:00Z
kirk-baird marked the issue as primary issue
#2 - kirk-baird
2022-11-11T07:37:14Z
I consider this a valid medium risk as pledges can be extended indefinitely. It bypasses the whitelisting which may be damaging if a token is found to be malicious and removed.
#3 - c4-judge
2022-11-11T07:37:22Z
kirk-baird marked the issue as satisfactory
#4 - c4-judge
2022-11-11T07:59:07Z
kirk-baird marked the issue as selected for report
1453.1785 USDC - $1,453.18
Owner may accidentally transfer ownership to inoperable address due to perceived safeguard that doesn't exist
contract WardenPledge is Ownable, Pausable, ReentrancyGuard {
WardenPledge inherits from Ownable rather than Owner, which is the intended contract. Owner overwrites the critical Ownable#transferOwnership function to make the ownership transfer process a two step process. This adds important safeguards because in the event that the target is unable to accept for any reason (input typo, incompatible multisig/contract, etc.) the ownership transfer process will fail because the pending owner will not be able to accept the transfer. To make matters worse, since it only overwrites the trasnferOwnership function the WardenPledge contract will otherwise function as intended just without this safeguard. It is likely that the owner won't even realize until too late and the safeguard has failed. A perceived safeguard where there isn't one is more damaging than not having any safeguard at all.
Manual Review
- contract WardenPledge is Ownable, Pausable, ReentrancyGuard { + contract WardenPledge is Owner, Pausable, ReentrancyGuard {
#0 - trust1995
2022-10-30T21:26:35Z
nice, well spotted! Interested what judge will decide, since on one hand it requires admin error and traditionally no two-step is a QA find, but on the other hand you did argue nicely that they could be trigger-happy on the transferOwnership because of perceived two step.
Still think transferOwnership is such a rare event that the team should be careful enough in the first place.
#1 - Kogaroshi
2022-10-30T21:31:43Z
Duplicate of #180
#2 - c4-judge
2022-11-12T00:12:43Z
kirk-baird marked the issue as not a duplicate
#3 - c4-judge
2022-11-12T00:12:48Z
kirk-baird marked the issue as primary issue
#4 - c4-judge
2022-11-12T00:12:51Z
kirk-baird marked the issue as satisfactory
#5 - c4-judge
2022-11-16T12:58:33Z
HickupHH3 marked the issue as selected for report
1863.0493 USDC - $1,863.05
Pledge creator is overcharged fees
vars.duration = endTimestamp - block.timestamp; if(vars.duration < minDelegationTime) revert Errors.DurationTooShort(); vars.votesDifference = targetVotes - votingEscrow.balanceOf(receiver); vars.totalRewardAmount = (rewardPerVote * vars.votesDifference * vars.duration) / UNIT; vars.feeAmount = (vars.totalRewardAmount * protocalFeeRatio) / MAX_PCT ;
The amount of voting power added by a boost decays rapidly and is calculated by the second, making it highly unlikely that the entire reward amount will be used up. To make the issue worse, current boost aren't counted when calculating the total boost amount needed, meaning that if the target of the pledge already has a boost, the creator is overcharged to a greater extent. Since the remaining amount is returned to the user after the pledge is complete, taking the full reward amount is fine. The fee however is taken in full based on the optimistic amount calculated which is almost certainly too much, which means the creator is overcharged.
Manual Review
The full amount of the pledge fee should be sent directly to the pledge contract. The pledge fee should be calculated and applied when each boost is received and the amount should be claimable. After a pledge is over, the pledge creator should receive their refund amount along with all unused fees.
#0 - Kogaroshi
2022-10-31T19:50:12Z
Duplicate of #235
#1 - c4-judge
2022-11-11T21:47:58Z
kirk-baird marked the issue as not a duplicate
#2 - c4-judge
2022-11-11T21:48:06Z
kirk-baird marked the issue as duplicate of #235
#3 - c4-judge
2022-11-11T21:48:13Z
kirk-baird marked the issue as satisfactory
#4 - c4-judge
2022-11-11T21:48:20Z
kirk-baird marked the issue as not a duplicate
#5 - c4-judge
2022-11-11T21:48:26Z
kirk-baird marked the issue as duplicate of #235
🌟 Selected for report: robee
Also found by: 0x007, 0x1f8b, 0x52, 0xDjango, 0xNazgul, 0xSmartContract, 8olidity, Awesome, B2, Bnke0x0, Chom, Diana, Dravee, JTJabba, Jeiwan, Josiah, Lambda, Mathieu, Picodes, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Rolezn, Ruhum, Sm4rty, Tricko, Trust, Waze, __141345__, a12jmx, adriro, ajtra, brgltd, c3phas, carlitox477, cccz, ch0bu, chaduke, chrisdior4, corerouter, cryptonue, csanuragjain, ctf_sec, cylzxje, delfin454000, dic0de, djxploit, horsefacts, imare, jayphbee, jwood, ktg, ladboy233, leosathya, lukris02, minhtrng, neko_nyaa, oyc_109, pashov, peritoflores, rbserver, rvierdiiev, shark, tnevler, yixxas
19.6449 USDC - $19.64
https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L299-L358 https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L368-L404
Setting protocol fee to 0 will break support for reward tokens that don't support 0 transfers
IERC20(rewardToken).safeTransferFrom(creator, address(this), totalRewardAmount); IERC20(rewardToken).safeTransferFrom(creator, chestAddress, feeAmount);
WardenPledge#increasePledgeRewardPerVote, extendPledge and createPledge will all attempt to send a protocol fee even if there is not protocol fee to collect. This is problematic as some ERC20 tokens revert when transferring an amount of 0.
function updatePlatformFee(uint256 newFee) external onlyOwner { if(newFee > 500) revert Errors.InvalidValue(); uint256 oldfee = protocalFeeRatio; protocalFeeRatio = newFee; emit PlatformFeeUpdated(oldfee, newFee); }
updatePlatformFee allows owner to set protocol fee to 0. In this situation pledges that use affected reward tokens can no longer be created, extended or have their rewards increased.
Manual Review
IERC20(rewardToken).safeTransferFrom(creator, address(this), totalRewardAmount); + if(feeAmount != 0) { + IERC20(rewardToken).safeTransferFrom(creator, chestAddress, feeAmount); + }
#0 - peritoflores
2022-10-30T21:08:30Z
Dupe of #266
#1 - Kogaroshi
2022-10-31T00:10:48Z
Duplicate of #266
#2 - c4-judge
2022-11-11T08:00:22Z
kirk-baird changed the severity to QA (Quality Assurance)
#3 - c4-judge
2022-11-12T00:35:25Z
kirk-baird marked the issue as grade-b