QA Report #388
Labels
bug
Something isn't working
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
valid
Overview
Table of Contents
@openzeppelin/contracts
version_safeMint()
should be used rather than_mint()
wherever possible__gap[50]
storage variable to allow for new storage variables in later versionsinitialize()
functions are front-runnable in the solutionnonReentrant
modifier
should occur before all other modifiersbytes.concat()
return
statement when the function defines a named return variable, is redundant1. Low Risk Issues
1.1. Known vulnerabilities exist in currently used
@openzeppelin/contracts
versionAs some known vulnerabilities exist in the current
@openzeppelin/contracts
version, consider updatingpackage.json
with at least@openzeppelin/[email protected]
here:While vulnerabilities are known, the current scope isn't affected (this might not hold true for the whole solution)
1.2. Add constructor initializers
As per OpenZeppelin’s (OZ) recommendation, “The guidelines are now to make it impossible for anyone to run
initialize
on an implementation contract, by adding an empty constructor with theinitializer
modifier. So the implementation contract gets initialized automatically upon deployment.”Note that this behaviour is also incorporated the OZ Wizard since the UUPS vulnerability discovery: “Additionally, we modified the code generated by the Wizard 19 to include a constructor that automatically initializes the implementation when deployed.”
Furthermore, this thwarts any attempts to frontrun the initialization tx of these contracts:
Note that this is already mitigated here:
1.3. Uninitialized Upgradeable contract
Similar issue in the past: here
Upgradeable dependencies should be initialized.
While not causing any harm at the moment, suppose OZ someday decide to upgrade this contract and the sponsor uses the new version: it is possible that the contract will not work anymore.
Consider calling the missing
__ReentrancyGuard_init()
here (ERC2771ContextUpgradeable
doesn't have an init function):File: Community.sol 21: contract Community is 22: ICommunity, 23: PausableUpgradeable, 24: ReentrancyGuardUpgradeable, 25: ERC2771ContextUpgradeable ... 102: function initialize(address _homeFi) ... 108: // Initialize pausable. Set pause to false; 109: __Pausable_init(); + 110: __ReentrancyGuard_init(); //@audit ERC2771ContextUpgradeable doesn't have an init function
1.4. Fees should be upper-bounded
There should be an upper limit to reasonable fees
1.5.
_safeMint()
should be used rather than_mint()
wherever possible_mint()
is discouraged in favor of_safeMint()
which ensures that the recipient is either an EOA or implementsIERC721Receiver
, see the WARNING L278:Both open OpenZeppelin and solmate have versions of this function so that NFTs aren't lost if they're minted to contracts that cannot transfer them back out.
Consider replacing with
_safeMint()
here:Be careful however to respect the CEI pattern or add a re-entrancy guard as
_safeMint
adds a callback-check (_checkOnERC721Received
) and a maliciousonERC721Received
could be exploited if not careful.Reading material:
1.6. Upgradeable contract is missing a
__gap[50]
storage variable to allow for new storage variables in later versionsSee this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.
1.7. Multiple
initialize()
functions are front-runnable in the solutionConsider adding some access control to them or deploying atomically or using constructor
initializer
:Only
Project.sol
is protected.1.8. Use a 2-step ownership transfer pattern
Contracts inheriting from OpenZeppelin's libraries have the default
transferOwnership()
function (a one-step process). It's possible that theonlyOwner
role mistakenly transfers ownership to a wrong address, resulting in a loss of theonlyOwner
role.Consider overriding the default
transferOwnership()
function to first nominate an address as thependingOwner
and implementing anacceptOwnership()
function which is called by thependingOwner
to confirm the transfer.2. Non-Critical Issues
2.1. It's better to emit after all processing is done
2.2. The
nonReentrant
modifier
should occur before all other modifiersThis is a best-practice to protect against re-entrancy in other modifiers
2.3. Use
bytes.concat()
Solidity version 0.8.4 introduces
bytes.concat()
(vsabi.encodePacked(<bytes>,<bytes>)
)2.4. Adding a
return
statement when the function defines a named return variable, is redundantWhile not consuming more gas with the Optimizer enabled: using both named returns and a return statement isn't necessary. Removing one of those can improve code clarity.
Affected code:
The text was updated successfully, but these errors were encountered: