Summary

Type: Governance
Timeline: September 22, 2025 → September 23, 2025
Languages: Solidity

Findings
Total issues: 4 (3 resolved)
Critical: 0 (0 resolved) · High: 0 (0 resolved) · Medium: 0 (0 resolved) · Low: 0 (0 resolved)

Notes & Additional Information
4 notes raised (3 resolved)

Scope

OpenZeppelin audited pull request #10 of the AcronymFoundation/anvil-contracts repository at commit 606d5de. As part of this audit, the implementation of the new Anvil governance token and its integration with the governance system were reviewed. Previously, in May 2024, an audit of the AcronymFoundation/anvil-contracts repository was performed at commit be6bd02.

In scope were the following files:

 contracts
└── governance
    ├── Anvil.sol
    └── AnvilGovernorDelegate.sol

System Overview

The new Anvil (ANVL) token has been designed to follow the standard ERC20Votes pattern, providing a more streamlined and broadly compatible voting mechanism. This is in contrast to the legacy Anvil token, which had custom logic to account for the delegated voting power of unclaimed airdropped tokens. By adopting the conventional ERC20Votes approach, the new design aligns the token with established governance standards. The total supply of the new Anvil token is capped at 100 billion, and all tokens are minted during deployment to a specified destinationAddress provided in the constructor.

In parallel, the AnvilGovernorDelegate contract will be upgraded, to introduce the reinitializeGovernanceToken function, which allows the governor contract to be connected to the new Anvil token. Protected by the onlyGovernance modifier, this function can only be invoked through a governance proposal.

Trust Assumptions

During the audit, the following trust assumptions were made:

  • During the construction of the Anvil governance token, the token's total supply is immediately minted to a specified destinationAddress. It is assumed that this amount will be fairly distributed to the existing token holders during the funds migration process.

  • The migration from the legacy ANVL token contract to the new token contract will be carried out through an airdrop, based on a snapshot of current token holder balances. The migration logic itself was not part of this audit. It is assumed that the airdrop mechanism will be correctly implemented and executed, and that it will accurately reflect the intended balances of existing token holders. Both the legacy and new Anvil tokens will remain active in circulation. However, only the new token will be recognized by the governor contract for voting purposes.

  • As a result of token migration, token holders will need to re-delegate their new tokens in order for their voting power to be reflected, even if they had already delegated in the legacy system. This redelegation requirement represents a functional change in governance behavior and is assumed to be clearly communicated to token holders as part of the migration process to avoid confusion or unintended gaps in voting power.

  • It is assumed that users will be properly informed about the existence of two tokens in circulation. While both the legacy and new tokens may continue to be held or traded, only the new token will be valid for governance.

Notes & Additional Information

Non-Explicit Imports

The use of non-explicit imports in the codebase can decrease code clarity and may create naming conflicts between locally defined and imported variables. This is particularly relevant when multiple contracts exist within the same Solidity file or when inheritance chains are long.

In the Anvil and AnvilGovernorDelegate contracts, global imports are being used. For example, import "@openzeppelin/contracts/token/ERC20/ERC20.sol" is used in line 4 of the Anvil contract.

Following the principle that clearer code is better code, consider using the named import syntax (import {A, B, C} from "X") to explicitly declare which contracts are being imported, such as, import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol".

Update: Resolved in pull request #11 at commit ed882f4.

Use Custom Errors

Since Solidity version 0.8.4, custom errors provide a cleaner and more cost-efficient way to explain to users why an operation failed.

The AnvilGovernorDelegate contract currently contains one revert("Cannot upgrade the timelock") statement.

For conciseness, consider replacing revert message with custom errors.

Update: Resoved in pull request #12 at commit d5bcd0b.

EIP-712 Version Consistency in New ANVL Token Deployment

A new version of the ANVL token is planned for deployment at a new contract address. Since the verifyingContract field in the EIP-712 domain separator will change, signature replay from the previous deployment is not possible. However, keeping the same EIP-712 version value may cause confusion when distinguishing between the legacy and the new contract. For clarity and stronger semantic versioning, it is advisable to increment the EIP-712 version in the new deployment.

Consider updating the EIP-712 version field in the newly deployed ANVL token to explicitly indicate the new release and avoid ambiguity in off-chain integrations.

Update: Resolved in pull request #13 at commit 172522a.

Incomplete Docstring

Within AnvilGovernorDelegate.sol, the initialize function has an incomplete docstring. For example, the timelock_, governanceToken_, votingPeriod_, votingDelay_, proposalThreshold_ parameters are not documented.

Consider thoroughly documenting all functions/events (and their parameters or return values) that are part of a contract's public API. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).

Update: Resolved in pull request #14 at commit a0661e7.

Conclusion

The audited code changes introduce a new version of the Anvil (ANVL) governance token. The legacy implementation relied on custom extensions to OpenZeppelin’s ERC20Votes contract to support airdrop claims and delegated voting during vesting. In contrast, the new version is a simplified implementation that directly extends the ERC20Votes contract, with all existing funds to be migrated to token holders, treating all tokens as fully vested.

Throughout the engagement, the Anvil Team has been highly responsive and cooperative, providing the audit team with clear explanations and valuable context regarding the planned migration process.