Paladin - Warden Pledges contest - indijanc's results

A governance lending protocol transforming users voting power into a new money lego.

General Information

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

Paladin

Findings Distribution

Researcher Performance

Rank: 7/96

Findings: 2

Award: $1,241.67

Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: 0x52

Also found by: indijanc, pashov

Labels

bug
2 (Med Risk)
satisfactory
sponsor confirmed
duplicate-161

Awards

1117.8296 USDC - $1,117.83

External Links

Lines of code

https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L18

Vulnerability details

Impact

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.

Proof of Concept

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 () => {

Tools Used

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)

#1 - Kogaroshi

2022-10-31T19:23:20Z

Fixed in PR 2, commit

#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

Gas optimizations

Redundant variables

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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgecreatePledge28440634210432729785-
WardenPledge--26958379 %-
After:
WardenPledgecreatePledge28439034208832728185-
WardenPledge--26947579 %-
Diff:
WardenPledgecreatePledge-16-16-16-
WardenPledge---1080-

creator in retrievePledgeRewards() can be removed

WardenPledge.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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgeretrievePledgeRewards35659745135325412-
WardenPledge--26958379 %-
After:
WardenPledgeretrievePledgeRewards35654745085324912-
WardenPledge--26932579 %-
Diff:
WardenPledgeretrievePledgeRewards-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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgeclosePledge52205756045911115-
WardenPledge--26958379 %-
After:
WardenPledgeclosePledge52200755995910615-
WardenPledge--26954299 %-
Diff:
WardenPledgeclosePledge-5-5-5-
WardenPledge---408-

Redundant variables oldMinTarget and oldfee in update methods

WardenPledge.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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgeupdateMinTargetVotes--301212-
WardenPledgeupdatePlatformFee--300372-
WardenPledge--26958379 %-
After:
WardenPledgeupdateMinTargetVotes--301072-
WardenPledgeupdatePlatformFee--300122-
WardenPledge--26969299 %-
Diff:
WardenPledgeupdateMinTargetVotes---14-
WardenPledgeupdatePlatformFee---25-
WardenPledge--+1092-

Redundant operations

Redundant add operation in 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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgecreatePledge28440634210432729785-
WardenPledge--26958379 %-
After:
WardenPledgecreatePledge28431234201032720385-
WardenPledge--26893629 %-
Diff:
WardenPledgecreatePledge-94-94-94-
WardenPledge---6475-

Redundant return statement in 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:

ContractMethodMinMaxAvg# callseur (avg)
WardenPledgerecoverERC20--396541-
WardenPledge--26958379 %-
After:
WardenPledgerecoverERC20--395791-
WardenPledge--26939059 %-
Diff:
WardenPledgerecoverERC20---75-
WardenPledge---1932-

#0 - c4-judge

2022-11-12T00:56:36Z

kirk-baird marked the issue as grade-a

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter