Juicebox contest - csanuragjain's results

The decentralized fundraising and treasury protocol.

General Information

Platform: Code4rena

Start Date: 18/10/2022

Pot Size: $50,000 USDC

Total HM: 13

Participants: 67

Period: 5 days

Judge: Picodes

Total Solo HM: 7

Id: 172

League: ETH

Juicebox

Findings Distribution

Researcher Performance

Rank: 49/67

Findings: 1

Award: $37.88

QA:
grade-b

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

37.8829 USDC - $37.88

Labels

bug
disagree with severity
downgraded by judge
QA (Quality Assurance)
satisfactory
grade-b
Q-02

External Links

Lines of code

https://github.com/jbx-protocol/juice-nft-rewards/blob/main/contracts/JB721TieredGovernance.sol#L177

Vulnerability details

Impact

The _totalTierCheckpoints does not get updated if user has delegated its votes to address(0). This becomes a problem for Governance since for a tier, the total votes would become more than existing vote. In case if a significant portion of vote has been delegated to address 0, then the voting system will get impacted

Proof of Concept

  1. User A delegates his voting units to address(0) using setTierDelegate function
function setTierDelegate(address _delegatee, uint256 _tierId) public virtual override { _delegateTier(msg.sender, _delegatee, _tierId); }
  1. This internally calls _delegateTier function which then calls _moveTierDelegateVotes function with voting units of User A
function _delegateTier( address _account, address _delegatee, uint256 _tierId ) internal virtual { // Get the current delegatee address _oldDelegate = _tierDelegation[_account][_tierId]; // Store the new delegatee _tierDelegation[_account][_tierId] = _delegatee; emit DelegateChanged(_account, _oldDelegate, _delegatee); // Move the votes. _moveTierDelegateVotes( _oldDelegate, _delegatee, _tierId, _getTierVotingUnits(_account, _tierId) ); }
  1. Observe there is no check to verify that new _delegatee is not address 0

  2. Now _moveTierDelegateVotes function is called which updates the _delegateTierCheckpoints for this user on the mentioned tier

function _moveTierDelegateVotes( address _from, address _to, uint256 _tierId, uint256 _amount ) internal { // Nothing to do if moving to the same account, or no amount is being moved. if (_from == _to || _amount == 0) return; // If not moving from the zero address, update the checkpoints to subtract the amount. if (_from != address(0)) { (uint256 _oldValue, uint256 _newValue) = _delegateTierCheckpoints[_from][_tierId].push( _subtract, _amount ); emit TierDelegateVotesChanged(_from, _oldValue, _newValue, _tierId, msg.sender); } // If not moving to the zero address, update the checkpoints to add the amount. if (_to != address(0)) { (uint256 _oldValue, uint256 _newValue) = _delegateTierCheckpoints[_to][_tierId].push( _add, _amount ); emit TierDelegateVotesChanged(_to, _tierId, _oldValue, _newValue, msg.sender); } }
  1. Since _to is address 0 (user is delegating to 0 address) so _delegateTierCheckpoints[_to][_tierId] will not be updated and only _delegateTierCheckpoints[_from][_tierId] will be updated which is correct

  2. The problem here is that now this tier has in a way burned all the voting units for User A which means this tier has now lesser number of voting units. This should have been updated in _totalTierCheckpoints (which tracks total voting units in tier) but it is not done within this flow

  3. This means after this transaction, total voting units in this checkpoint will be including the non existing votes (from user A delegation to 0 address)

  4. So lets say in tier id1

total votes : 1000 user A voting units: 600 User A delegates to address 0 total votes : 1000 user A voting units: 0 (since _delegateTierCheckpoints[_from][_tierId].push( _subtract, _amount );) Governance voting starts Only 1000-600=400 votes can be placed If governance required atleast 50% consensus from total votes or required 50% participation of overall votes then this would never reach

Do not allow User to delegate to 0 address if they have some voting units. If required then kindly update _totalTierCheckpoints before delegating to 0 address

#0 - trust1995

2022-10-23T22:55:54Z

I believe it to be a non issue. The reasoning is that any voter who delegates to 0 can be considered a malicious voter. If the voter is malicious, it can just as well use the majority to do whatever it wishes, instead of crippling the voting. If the voter is not malicious, then sending to the 0 address is a user mistake, which should be checked but can't be more than LOW severity as it depends on big user error.

#1 - csanuragjain

2022-10-24T05:18:04Z

I believe that this issue is highlighting an accounting issue which if not fixed will always show higher number of total votes per tier than actually present in system. This will become a problem for a voting consensus which is derived based on total votes per tier

I do understand your point that user can use those votes directly instead of crippling the system. Although user error can happen, which could lead to this issue. Previously issue requiring pre-requisite conditions have been marked Medium but would leave this for the judge's decision :)

#2 - drgorillamd

2022-10-24T11:41:41Z

  • this is a vote delegation (not a token transfer), if this is an user mistake, one can always fix it by redelegating to the correct address.

Duplicate #228 Mis-tagged this one, transferring to 0 is not an issue per se, but the total weight has an accounting issue

  • not from this (but for us to fix): transfering voting power to 0 is a one-way only @mejango

#3 - mejango

2022-10-24T17:28:36Z

@drgorillamd down to prevent delegation to 0 address.

#4 - Picodes

2022-11-04T09:57:09Z

The warden showed how delegation to address(0) is not handled correctly as:

  • the overall accounting of total votes would be impacted

Mitigation could be to prevent delegation to address(0) or to correct the accounting

However it's very unlikely that this impact the system globally as highlighted by @trust1995, and at the user level we're down to a case of a finding conditional to a user mistake

#5 - c4-judge

2022-11-04T09:57:21Z

Picodes marked the issue as change severity

#6 - c4-judge

2022-11-04T09:57:35Z

Picodes marked the issue as satisfactory

#7 - c4-judge

2022-11-18T18:14:21Z

Picodes changed the severity to QA (Quality Assurance)

#8 - Picodes

2022-11-22T09:32:15Z

@csanuragjain apologizes for the back and forth in my decisions. When reviewing this with a senior judge, I decided to downgrade this to Low as the error can be corrected easily if it is one, and if the voter is malicious this is like delegating to an EOA that will never vote

#9 - c4-judge

2022-12-03T18:57:05Z

Picodes 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