This post briefly summarizes the Eco Governance Proposal submitted on Nov 13th, 2022 and enacted on Nov 17th, 2022 during the second Eco Generation. Due to the details of the vulnerabilities, this post was published after the proposal had already been enacted.
Two bugs were discovered through Immunefi bug bounty submissions:
- When using
delegateAmounton an attacker address who has (or later sets) a primary delegate, the attacker could self transfer to cause the voting power given by
delegateAmountto be transferred to the primary delegate. This not only violated the principles of delegation, but disallowed the victim from calling
undelegatein any form, leaving the amount untransferable. The offending code was in
_balances[from] + amountto calculate the pre-transfer balance of
This issue was fixed by an early exit of
_afterTokenTransfer on self transfers as there would be no need to re-calculate delegation. We acknowledge that this would allow a user to self transfer funds that would not be transferrable to any other address (in the case of them being locked by using
delegateAmount), but would have no further effect on the system.
- If an attacker is able to atomically call the
incrementGenerationcall, they would have access to the new clone of
PolicyProposals.solfor that generation in a block that was equal to
blockNumberwhere the voting power is measured. This would allow the user to submit a proposal, support it, transfer tokens to another address, and then support it again with the same tokens. This process could be extended as far as gas and contract size limits would allow for the sandwich transaction. This attack is detailed in the test contract
This issue was fixed by restricting the voting calculation. Two offending functions created this vulnerability:
getPastTotalSupply. These functions are used in
VotingPower.sol to determine voting power during the community governance process. They were changed to respect their respective names and require the block submitted to be strictly before the current block. This means that any attempts to perform the exploit above will fail in the calculation of the addresses’s voting power. If someone did want to see voting power for the current block, they could use
totalSupply. Note that
getPastLinearInflation was not changed in this update, as it did not have a corresponding function for the current block and therefore is used in a number of places to access the amount of linear inflation in the current block.
Additionally the ECOxStaking contract was proxied to allow for easy upgradeability.
These issues were addressed in generation 1002 via the proposal
VoteCheckpointsUpgrade.propo.sol whose deployment can be found here . The PR for updating the codebase with this fix can be found here. The proposal can also be seen in Eco Community Governance Interface linked here.