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: 7/96
Findings: 2
Award: $1,241.67
🌟 Selected for report: 0
🚀 Solo Findings: 0
1117.8296 USDC - $1,117.83
https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L18
2 step ownership change is a more secure way of changing contract owners where the next owner must accept ownership. A mistake on ownership change would render the contract unable to add/remove reward tokens, change chest contract and doing other upgrades.
This is normally not a high issue and depends on the individual project and use case, but I consider it a medium in this case because the documentation mentions 2-step ownership change, it is also implemented and prepared in Owner.sol
but is never used.
This simple failing test will showcase that WardenPledge.sol
is not using 2-step ownership change:
diff --git a/test/wardenPledge.test.ts b/test/wardenPledge.test.ts index 5167d0d..44b49a5 100644 --- a/test/wardenPledge.test.ts +++ b/test/wardenPledge.test.ts @@ -29,6 +29,7 @@ import { HOLDERS, AMOUNTS } from "./utils/constant" +import exp from "constants"; chai.use(solidity); const { expect } = chai; @@ -230,6 +231,12 @@ describe('Warden Pledge contract tests', () => { }); + it(' should use 2-step fow WardenPledge ownership change', async () => { + await wardenPledge.connect(admin).transferOwnership(delegator1.address) + // 2 step ownershipe change should keep old owner until new owner accepts + await expect(await wardenPledge.owner()).to.be.eq(admin.address) + }); + }); describe('addMultipleRewardToken', async () => {
Manual review
I recommend using the implemented and already prepared Owner.sol
to use 2-step ownerrship change. Derive WardenPledge
from Owner
and you will find the previously failing test to succeed:
diff --git a/contracts/WardenPledge.sol b/contracts/WardenPledge.sol index beb990d..dab9ac6 100644 --- a/contracts/WardenPledge.sol +++ b/contracts/WardenPledge.sol @@ -15,7 +15,7 @@ import "./utils/Errors.sol"; /* Delegation market (Pledge version) based on Curve Boost V2 contract */ -contract WardenPledge is Ownable, Pausable, ReentrancyGuard { +contract WardenPledge is Owner, Pausable, ReentrancyGuard { using SafeERC20 for IERC20; // Constants :
#0 - Kogaroshi
2022-10-31T19:18:59Z
(the same issue was reported in some QA reports too, so not sure it should considered as Medium or QA)
#2 - kirk-baird
2022-11-10T23:17:10Z
Agreed that this is considered QA as two stage ownership transfers are a LOW rated issue.
#3 - c4-judge
2022-11-10T23:17:17Z
kirk-baird changed the severity to QA (Quality Assurance)
#4 - kirk-baird
2022-11-11T21:05:18Z
After consulting with other judges I've decided Medium is more appropriate here. Although the 2-stage pattern is considered a QA issue, it was implemented and intended to be used and that justifies raising it to a medium.
Any wardens who mentioned that two stage ownership pattern was created and not implemented in their QA will be marked as a duplicate of this issue. If the warden does not mentioned that the two stage ownership was created and not implemented it will remain a QA issue.
#5 - Simon-Busch
2022-11-13T06:13:36Z
Change severity back to Medium as requested by @kirk-baird
#6 - c4-judge
2022-11-13T22:07:15Z
kirk-baird marked the issue as satisfactory
#7 - c4-judge
2022-11-13T22:07:33Z
kirk-baird marked the issue as duplicate of #161
🌟 Selected for report: c3phas
Also found by: 0x1f8b, 0xNazgul, 0xRoxas, 0xSmartContract, 0xbepresent, Amithuddar, Awesome, B2, Bnke0x0, Dravee, KoKo, Mathieu, Picodes, RaymondFam, RedOneN, ReyAdmirado, RockingMiles, Ruhum, SadBase, SooYa, Waze, __141345__, adriro, ajtra, ballx, carlitox477, ch0bu, cylzxje, djxploit, durianSausage, emrekocak, erictee, gogo, halden, horsefacts, imare, indijanc, karanctf, leosathya, lukris02, neko_nyaa, oyc_109, peiw, sakman, shark, skyle, tnevler
123.8403 USDC - $123.84
creator
in createPledge()
can be replaced with msg.sender
WardenPledge.sol L308
Redundant variable creator
can be removed - use msg.sender
instead. This slightly reduces contract size and interaction cost on createPledge()
Using the project tests we see the following gas improvement after removal:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | createPledge | 284406 | 342104 | 327297 | 85 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | createPledge | 284390 | 342088 | 327281 | 85 | - |
WardenPledge | - | - | 2694757 | 9 % | - | |
Diff: | ||||||
WardenPledge | createPledge | -16 | -16 | -16 | - | |
WardenPledge | - | - | -1080 | - |
creator
in retrievePledgeRewards()
can be removedWardenPledge.sol L458
Redundant variable creator
can be removed. This slightly reduces contract size and interaction cost on retrievePledgeRewards()
. Use pledgeOwner[pledgeId]
in the if statement instead.
Using the project tests we see the following improvement after removal:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | retrievePledgeRewards | 35659 | 74513 | 53254 | 12 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | retrievePledgeRewards | 35654 | 74508 | 53249 | 12 | - |
WardenPledge | - | - | 2693257 | 9 % | - | |
Diff: | ||||||
WardenPledge | retrievePledgeRewards | -5 | -5 | -5 | - | |
WardenPledge | - | - | -2580 | - |
creator
in closePledge()
can be replaced with msg.sender
WardenPledge.sol L490
Redundant variable creator
can be removed - use msg.sender
instead. This slightly reduces contract size and interaction cost on closePledge()
Using the project tests we see the following gas improvement after removal:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | closePledge | 52205 | 75604 | 59111 | 15 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | closePledge | 52200 | 75599 | 59106 | 15 | - |
WardenPledge | - | - | 2695429 | 9 % | - | |
Diff: | ||||||
WardenPledge | closePledge | -5 | -5 | -5 | - | |
WardenPledge | - | - | -408 | - |
oldMinTarget
and oldfee
in update methodsWardenPledge.sol L614 WardenPledge.sol L627 By emitting before assignment you can remove the redundant variable. There is a slight increase in deployment cost however. Using the project tests we see the following gas improvement after removal of both variables:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | updateMinTargetVotes | - | - | 30121 | 2 | - |
WardenPledge | updatePlatformFee | - | - | 30037 | 2 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | updateMinTargetVotes | - | - | 30107 | 2 | - |
WardenPledge | updatePlatformFee | - | - | 30012 | 2 | - |
WardenPledge | - | - | 2696929 | 9 % | - | |
Diff: | ||||||
WardenPledge | updateMinTargetVotes | - | - | -14 | - | |
WardenPledge | updatePlatformFee | - | - | -25 | - | |
WardenPledge | - | - | +1092 | - |
createPledge()
WardenPledge.sol L340
Redundant addition costs more gas than assignment. pledgeAvailableRewardAmounts
of the newly created pledge will always start with 0, so the add sign doesn't make much sense and increases gos cast on each pledge creation.
Using the project tests we see the following improvement by removing the addition:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | createPledge | 284406 | 342104 | 327297 | 85 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | createPledge | 284312 | 342010 | 327203 | 85 | - |
WardenPledge | - | - | 2689362 | 9 % | - | |
Diff: | ||||||
WardenPledge | createPledge | -94 | -94 | -94 | - | |
WardenPledge | - | - | -6475 | - |
recoverERC20()
WardenPledge.sol L660
The return statement in recoverERC20()
always returns true. Seems redundant, the function and transfer of funds will revert on failure, or pass otherwise. Removing return statement saves some gas.
Using the project tests here's the gas improvement after removing the return statement:
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
WardenPledge | recoverERC20 | - | - | 39654 | 1 | - |
WardenPledge | - | - | 2695837 | 9 % | - | |
After: | ||||||
WardenPledge | recoverERC20 | - | - | 39579 | 1 | - |
WardenPledge | - | - | 2693905 | 9 % | - | |
Diff: | ||||||
WardenPledge | recoverERC20 | - | - | -75 | - | |
WardenPledge | - | - | -1932 | - |
#0 - c4-judge
2022-11-12T00:56:36Z
kirk-baird marked the issue as grade-a