Arbitrum Security Council Election System - 0xbepresent's results

A suite of scaling solutions providing environments with high-throughput, low-cost smart contracts, backed by industry-leading proving technology rooted in Ethereum.

General Information

Platform: Code4rena

Start Date: 03/08/2023

Pot Size: $90,500 USDC

Total HM: 6

Participants: 36

Period: 7 days

Judge: 0xean

Total Solo HM: 1

Id: 273

League: ETH

Arbitrum Foundation

Findings Distribution

Researcher Performance

Rank: 32/36

Findings: 1

Award: $36.16

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
grade-b
QA (Quality Assurance)
Q-07

Awards

36.1616 USDC - $36.16

External Links

1 - Contender and Nominee can vote to themselves

In the SecurityCouncilNomineeElectionGovernorCountingUpgradeable:_countVote() there is not any validation that the contenders can vote to themselves.

Additionally the nominees can vote to themselves since there is not any restriction in the SecurityCouncilMemberElectionGovernorCountingUpgradeable._countVote() function.

Add a restriction that contender or nominee can not vote to themselves.

2 - Validates only the votes number that will be used

The SecurityCouncilNomineeElectionGovernorCountingUpgradeable::_countVote() allows the users to count the votes for multiple contenders in order to nominee them.

The next code block helps to calculate the actualVotes variable which is the exact number that helps the contenteder to be a nominee, therefore the voter's votes are not wasted and it is only used the necessary amount:

File: SecurityCouncilNomineeElectionGovernorCountingUpgradeable.sol
093: 
094:         uint256 prevVotesReceived = election.votesReceived[contender];
095:         uint256 votesThreshold = quorum(proposalSnapshot(proposalId));
096: 
097:         uint256 actualVotes = votes;
098:         if (prevVotesReceived + votes >= votesThreshold) {
099:             // we pushed the contender over the line
100:             // we should only give the contender enough votes to get to the line so that we don't waste votes
101:             actualVotes = votesThreshold - prevVotesReceived;
102: 
103:             // push the contender to the nominees
104:             _addNominee(proposalId, contender);
105:         }
106: 
107:         election.votesUsed[account] = prevVotesUsed + actualVotes;
108:         election.votesReceived[contender] = prevVotesReceived + actualVotes;

The problem is in the next code block helps to ensure the voter has enough avaliabale vote weight. Since not all the voter's votes will be used, it is not necessary to validates all the votes number:

File: SecurityCouncilNomineeElectionGovernorCountingUpgradeable.sol
87:         NomineeElectionCountingInfo storage election = _elections[proposalId];
88:         uint256 prevVotesUsed = election.votesUsed[account];
89: 
90:         if (votes + prevVotesUsed > weight) {
91:             revert InsufficientTokens(votes, prevVotesUsed, weight);
92:         }

Consider validates only the votes that will be necessary to cover the contender threshold.

--      if (votes + prevVotesUsed > weight) {
++      if (actualVotes + prevVotesUsed > weight) {
            revert InsufficientTokens(votes, prevVotesUsed, weight);
        }

3 - The relay() functions don't have the payable modifier

The SecurityCouncilMemberElectionGovernor.relay() and SecurityCouncilNomineeElectionGovernor.relay() functions don't have the payable modifier.

The requirements of the AddressUpgradeable.functionCallWithValue() are:

  • the calling contract must have an ETH balance of at least value.
  • the called Solidity function must be payable.

Consider adding the payable modifier in the relay() functions.

#0 - c4-judge

2023-08-18T23:27:45Z

0xean marked the issue as grade-c

#1 - c4-judge

2023-08-18T23:59:21Z

0xean marked the issue as grade-b

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