Skip to content

Feat - Quantstamp Report #443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721Enumer
*/
abstract contract NFTAccessControlUpgradeable is Initializable, ContextUpgradeable, INFTAccessControlUpgradeable, ERC165Upgradeable {
function __NFTAccessControl_init(address registry) internal initializer {
require(registry != address(0), "NFTAccessControlUpgradeable: Invalid registry address.");
__Context_init_unchained();
__ERC165_init_unchained();
__NFTAccessControl_init_unchained();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeab
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
Expand All @@ -28,6 +29,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
Initializable,
ContextUpgradeable,
AccessControlEnumerableUpgradeable,
ReentrancyGuardUpgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable,
OwnableUpgradeable,
Expand Down Expand Up @@ -285,7 +287,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
/// @param label a unique label to attach to the token
/// @param registrationData data to store in the tokenURI
/// @param tokenId unique uint256 identifier for the newly created token
function registerByToken(address to, string calldata label, string calldata registrationData, uint256 tokenId) external virtual {
function registerByToken(address to, string calldata label, string calldata registrationData, uint256 tokenId) external virtual nonReentrant {
require(registrationToken != address(0), "NonFungibleRegistry: registration by token not enabled.");
require(!_mappingExists(label), "NonFungibleRegistry: label is already registered.");
// user must approve the registry to collect the registration fee from their wallet
Expand All @@ -294,7 +296,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
_createLabeledToken(to, label, registrationData, tokenId);

uint256 burnAmount = registrationFee * burnFee / 10000;
IERC20Upgradeable(registrationToken).transfer(burnAddress, burnAmount);
require(IERC20Upgradeable(registrationToken).transfer(burnAddress, burnAmount), "NonFungibleRegistry: token transfer failed.");
// the fee stays with the token, not the token owner
identityStakes[tokenId] = Fee(registrationToken, registrationFee-burnAmount);
}
Expand Down Expand Up @@ -340,6 +342,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
require(_isApprovedOrOwner(_msgSender(), tokenId), "NonFungibleRegistry: caller is not owner nor approved nor registrar.");
require(!_mappingExists(label), "NonFungibleRegistry: label is already registered.");

delete registryMap[reverseRegistryMap[tokenId]];
registryMap[label] = tokenId;
reverseRegistryMap[tokenId] = label;
emit LabelUpdated(tokenId, label);
Expand Down Expand Up @@ -381,7 +384,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
/// @notice burn removes a token from the registry enumeration and refunds registration fee to burner
/// @dev only callable by the owner, approved caller when allowTransfers is true or REGISTRAR_ROLE
/// @param tokenId unique id to refence the target token
function burn(uint256 tokenId) public virtual {
function burn(uint256 tokenId) public virtual nonReentrant {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "NonFungibleRegistry: caller is not owner nor approved nor registrar.");
_burn(tokenId);
Expand All @@ -390,7 +393,7 @@ contract NonFungibleRegistryEnumerableUpgradeable is
if (identityStakes[tokenId].amount != 0) {
// send the registration fee to the token burner
// don't set a registration token you do not control/trust, otherwise, this could be used for re-entrancy attack
require(IERC20Upgradeable(identityStakes[tokenId].token).transfer(_msgSender(), identityStakes[tokenId].amount), "NonFungibleRegistry: token tansfer failed.");
require(IERC20Upgradeable(identityStakes[tokenId].token).transfer(_msgSender(), identityStakes[tokenId].amount), "NonFungibleRegistry: token transfer failed.");
delete identityStakes[tokenId];
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity ^0.8.4;
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
Expand All @@ -28,6 +29,7 @@ contract NonFungibleRegistryUpgradeable is
ContextUpgradeable,
AccessControlEnumerableUpgradeable,
ERC721URIStorageUpgradeable,
ReentrancyGuardUpgradeable,
OwnableUpgradeable,
RoyaltiesV2Impl
{
Expand Down Expand Up @@ -158,6 +160,10 @@ contract NonFungibleRegistryUpgradeable is
address _admin
)
public initializer {
require(address(_primaryRegistry) != address(0), "NonFungibleRegistry: Invalid primaryRegistry address.");
require(address(_registrar) != address(0), "NonFungibleRegistry: Invalid registrar address.");
require(address(_admin) != address(0), "NonFungibleRegistry: Invalid admin address.");

__Context_init();
__AccessControlEnumerable_init();
__ERC721URIStorage_init();
Expand Down Expand Up @@ -242,7 +248,7 @@ contract NonFungibleRegistryUpgradeable is
/// @param _primaryRegistry address to set as the primary registry
function setPrimaryRegistry(address _primaryRegistry) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "NonFungibleRegistry: must be admin.");
// allow this feature to be disablled by setting to 0 address
// allow this feature to be disabled by setting to 0 address
if (address(_primaryRegistry) == address(0)) {
primaryRegistry = address(0);
} else {
Expand Down Expand Up @@ -282,7 +288,7 @@ contract NonFungibleRegistryUpgradeable is
/// @param label a unique label to attach to the token
/// @param registrationData data to store in the tokenURI
/// @param tokenId unique uint256 identifier for the newly created token
function registerByToken(address to, string calldata label, string calldata registrationData, uint256 tokenId) external virtual {
function registerByToken(address to, string calldata label, string calldata registrationData, uint256 tokenId) external virtual nonReentrant {
require(registrationToken != address(0), "NonFungibleRegistry: registration by token not enabled.");
require(!_mappingExists(label), "NonFungibleRegistry: label is already registered.");
// user must approve the registry to collect the registration fee from their wallet
Expand All @@ -291,7 +297,7 @@ contract NonFungibleRegistryUpgradeable is
_createLabeledToken(to, label, registrationData, tokenId);

uint256 burnAmount = registrationFee * burnFee / 10000;
IERC20Upgradeable(registrationToken).transfer(burnAddress, burnAmount);
require(IERC20Upgradeable(registrationToken).transfer(burnAddress, burnAmount), "NonFungibleRegistry: token transfer failed.");
// the fee stays with the token, not the token owner
identityStakes[tokenId] = Fee(registrationToken, registrationFee-burnAmount);
}
Expand Down Expand Up @@ -337,6 +343,7 @@ contract NonFungibleRegistryUpgradeable is
require(_isApprovedOrOwner(_msgSender(), tokenId), "NonFungibleRegistry: caller is not owner nor approved nor registrar.");
require(!_mappingExists(label), "NonFungibleRegistry: label is already registered.");

delete registryMap[reverseRegistryMap[tokenId]];
registryMap[label] = tokenId;
reverseRegistryMap[tokenId] = label;
emit LabelUpdated(tokenId, label);
Expand Down Expand Up @@ -378,7 +385,7 @@ contract NonFungibleRegistryUpgradeable is
/// @notice burn removes a token from the registry enumeration and refunds registration fee to burner
/// @dev only callable by the owner, approved caller when allowTransfers is true or REGISTRAR_ROLE
/// @param tokenId unique id to refence the target token
function burn(uint256 tokenId) public virtual {
function burn(uint256 tokenId) public virtual nonReentrant {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "NonFungibleRegistry: caller is not owner nor approved nor registrar.");
_burn(tokenId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./NonFungibleRegistryUpgradeable.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
* @title Hypernet Protocol Non Fungible Registry Factory
Expand All @@ -21,7 +22,7 @@ import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
* See unit tests for example usage:
* https://github.com/GoHypernet/hypernet-protocol/blob/dev/packages/contracts/test/upgradeable-factory-test.js
*/
contract UpgradeableRegistryFactory is AccessControlEnumerable {
contract UpgradeableRegistryFactory is AccessControlEnumerable, ReentrancyGuard {

/// @dev address of our upgradeble registry with enumeration proxy beacon
address public enumerableRegistryBeacon;
Expand Down Expand Up @@ -76,6 +77,9 @@ contract UpgradeableRegistryFactory is AccessControlEnumerable {
address _registrationToken) {
require(_names.length == _symbols.length, "RegistryFactory: Initializer arrays must be equal length.");
require(_symbols.length == _registrars.length, "RegistryFactory: Initializer arrays must be equal length.");
require(address(_enumerableRegistry) != address(0), "RegistryFactory: Invalid enumerableRegistry address.");
require(address(_registry) != address(0), "RegistryFactory: Invalid registry address.");
require(address(_registrationToken) != address(0), "RegistryFactory: Invalid registrationToken address.");

// set the administrator of the registry factory
_setupRole(DEFAULT_ADMIN_ROLE, _admin);
Expand Down Expand Up @@ -119,33 +123,31 @@ contract UpgradeableRegistryFactory is AccessControlEnumerable {
/// @notice setProfileRegistryAddress change the address of the profile registry contract
/// @dev can only be called by the DEFAULT_ADMIN_ROLE
/// @param _hypernetProfileRegistry address of ERC721 token to use as profile contract
function setProfileRegistryAddress(address _hypernetProfileRegistry) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "RegistryFactory: must have admin role to set parameters");
function setProfileRegistryAddress(address _hypernetProfileRegistry) external isAdmin {
require(address(_hypernetProfileRegistry) != address(0), "NonFungibleRegistry: Invalid hypernetProfileRegistry address.");
hypernetProfileRegistry = _hypernetProfileRegistry;
}

/// @notice setRegistrationToken setter function for configuring which ERC20 token is burned when adding new apps
/// @dev can only be called by the DEFAULT_ADMIN_ROLE
/// @param _registrationToken address of ERC20 token burned during registration
function setRegistrationToken(address _registrationToken) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "RegistryFactory: must have admin role to set parameters");
function setRegistrationToken(address _registrationToken) external isAdmin {
require(_registrationToken != address(0), """RegistryFactory: Invalid registrationToken address");
registrationToken = _registrationToken;
}

/// @notice setRegistrationFee setter function for configuring how much token is burned when adding new apps
/// @dev can only be called by the DEFAULT_ADMIN_ROLE
/// @param _registrationFee burn fee amount
function setRegistrationFee(uint256 _registrationFee) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "RegistryFactory: must have admin role to set parameters");
require(registrationFee >= 0, "RegistryFactory: Registration fee must be nonnegative.");
function setRegistrationFee(uint256 _registrationFee) external isAdmin {
require(_registrationFee >= 0, "RegistryFactory: Registration fee must be nonnegative.");
registrationFee = _registrationFee;
}

/// @notice setBurnAddress setter function for configuring where tokens are sent when calling createRegistryByToken
/// @dev can only be called by the DEFAULT_ADMIN_ROLE
/// @param _burnAddress address where creation fee is to be sent
function setBurnAddress(address _burnAddress) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "RegistryFactory: must have admin role to set parameters");
function setBurnAddress(address _burnAddress) external isAdmin {
burnAddress = _burnAddress;
}

Expand Down Expand Up @@ -182,7 +184,7 @@ contract UpgradeableRegistryFactory is AccessControlEnumerable {
}
}

function _createEnumerableRegistry(string memory _name, string memory _symbol, address _registrar) private {
function _createEnumerableRegistry(string memory _name, string memory _symbol, address _registrar) private nonReentrant {
require(_registrar != address(0), "RegistryFactory: Registrar address must not be 0.");
require(!_registryExists(_name), "RegistryFactory: Registry by that name exists.");

Expand All @@ -193,7 +195,7 @@ contract UpgradeableRegistryFactory is AccessControlEnumerable {
emit RegistryCreated(address(proxy));
}

function _createRegistry(string memory _name, string memory _symbol, address _registrar) private {
function _createRegistry(string memory _name, string memory _symbol, address _registrar) private nonReentrant {
require(_registrar != address(0), "RegistryFactory: Registrar address must not be 0.");
require(!_registryExists(_name), "RegistryFactory: Registry by that name exists.");

Expand All @@ -214,4 +216,9 @@ contract UpgradeableRegistryFactory is AccessControlEnumerable {
// does the recipient have a non-zero balance.
return ((hypernetProfileRegistry == address(0)) || (IERC721Upgradeable(hypernetProfileRegistry).balanceOf(owner) > 0));
}

modifier isAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "RegistryFactory: must have admin role to set parameters");
_;
}
}
15 changes: 15 additions & 0 deletions packages/contracts/contracts/interfaces/INfr.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @dev a minimal interface for interacting with Hypernet Protocol NFRs
interface INfr {
function ownerOf(uint256 tokenId) external view returns (address);
function hasRole(bytes32 role, address account) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function REGISTRAR_ROLE() external view returns (bytes32);
function registrationFee() external view returns (uint256);
function registrationToken() external view returns (address);
function burnAddress() external view returns (address);
function register(address to, string calldata label, string calldata registrationData, uint256 tokenId) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
8 changes: 1 addition & 7 deletions packages/contracts/contracts/modules/BatchModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Context.sol";
import "../interfaces/INfr.sol";

/**
* @title Hypernet Protocol Batch Minting Module for NFRs
Expand Down Expand Up @@ -53,10 +54,3 @@ contract BatchModule is Context {
}
}
}

/// @dev a minimal interface for interacting with Hypernet Protocol NFRs
interface INfr {
function register(address to, string calldata label, string calldata registrationData, uint256 tokenId) external;
function hasRole(bytes32 role, address account) external view returns (bool);
function REGISTRAR_ROLE() external view returns (bytes32);
}
8 changes: 1 addition & 7 deletions packages/contracts/contracts/modules/BulkTransferModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Context.sol";
import "../interfaces/INfr.sol";

/**
* @title Hypernet Protocol Bulk Transfer Module for NFRs
Expand Down Expand Up @@ -49,10 +50,3 @@ contract BulkTransferModule is Context {
}
}
}

/// @dev a minimal interface for interacting with Hypernet Protocol NFRs
interface INfr {
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function REGISTRAR_ROLE() external view returns (bytes32);
}
13 changes: 2 additions & 11 deletions packages/contracts/contracts/modules/BuyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/INfr.sol";

/**
* @title Hypernet Protocol Buy Module for NFRs
Expand Down Expand Up @@ -40,6 +41,7 @@ contract BuyModule is Context {
external
virtual
{
require(registry != address(0), "BuyModule: Invalid registry address.");
address seller = INfr(registry).ownerOf(tokenId);
uint256 price = INfr(registry).registrationFee();
// the current owner of the tokenid must be an account in the REGISTRAR_ROLE
Expand All @@ -53,14 +55,3 @@ contract BuyModule is Context {
require(INfr(registry).ownerOf(tokenId) == _msgSender(), "BuyModule: NFI purchase transfer failed");
}
}

/// @dev a minimal interface for interacting with Hypernet Protocol NFRs
interface INfr {
function ownerOf(uint256 tokenId) external view returns (address);
function hasRole(bytes32 role, address account) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function REGISTRAR_ROLE() external view returns (bytes32);
function registrationFee() external view returns (uint256);
function registrationToken() external view returns (address);
function burnAddress() external view returns (address);
}
Loading