Platform: Code4rena
Start Date: 06/05/2021
Pot Size: $66,000 USDC
Total HM: 16
Participants: 11
Period: 6 days
Judge: cemozer
Total Solo HM: 9
Id: 8
League: ETH
Rank: 4/11
Findings: 5
Award: $8,535.85
🌟 Selected for report: 6
🚀 Solo Findings: 0
999.994 USDC - $999.99
shw
The swapTo
function in the NFTXVaultUpgradeable
contract should use the modifier nonReentrant
to prevent reentrancy, which could happen when a user receives an NFT and calls the swapTo
function again in the onERC721Received
or onERC1155Received
functions he implemented.
Both the mintTo
, redeemTo
includes a nonReentrant
modifier to prevent reentrancy, while the function swapTo
does not. Currently, the modifier is applied in the swap
function instead of swapTo
.
Referenced code: NFTXVaultUpgradeable.sol#L259-L289
None
Move the nonReentrant
modifier from swap
to swapTo
.
#0 - cemozerr
2021-05-25T23:25:59Z
1028.8004 USDC - $1,028.80
shw
The function rescueTokens
in the contract NFTXFeeDistributor
does not check the return value of transfer
. It is possible that the transferred assets are not ERC20-compliant (e.g., returns a false
when a transfer fails), leading to unexpected results.
Referenced code: NFTXFeeDistributor.sol#L143
None
Use the safeTransfer
or safeTransferFrom
of the SafeERC20
implementation from OpenZeppelin.
#0 - cemozerr
2021-05-25T23:43:50Z
shw
The getPseudoRand
function uses a weak PRNG, based on blockhash(block.number - 1)
, which can be calculated on-chain by any smart contract beforehand. With the assist of Flashbots, users can revert transactions without paying gas fees to the miner. Thus, before interacting with the Vault, a user can calculate the result of randomness first to see if it is satisfying. If not, he reverts the transactions and retries multiple times as his desire. In short, the output randomness is predictable and potentially controllable (especially when the variable modulus
is not large enough) without the user paying gas fees but only a tip to the miner.
In both redeemTo
and swapTo
functions, the directRedeemFee
and redeemFee
seem to be different. Assuming that directRedeemFee
is greater than redeemFee
, a user could exploit the potentially controllable output of randomness to redeem or swap specific chosen NFTs but avoid paying directRedeemFee
at the same time.
Referenced code: NFTXVaultUpgradeable.sol#L413-L427 NFTXVaultUpgradeable.sol#L245-L247 NFTXVaultUpgradeable.sol#L280-L282
None
Use Chainlink VRF to generate randomness.
205.7601 USDC - $205.76
shw
In the contract NFTXFeeDistributor
, the SafeMath
library is not used when performing arithmetic operations, which could potentially integer overflow/underflow and cause unexpected results.
Referenced code: NFTXFeeDistributor.sol#L64-L65 NFTXFeeDistributor.sol#L91-L93 NFTXFeeDistributor.sol#L108 NFTXFeeDistributor.sol#L147 NFTXFeeDistributor.sol#L154
None
Use SafeMath
to prevent integer overflow/underflow.
#0 - cemozerr
2021-05-25T23:42:51Z
762.0744 USDC - $762.07
shw
The variable eligibilityManager
in the contract NFTXVaultFactoryUpgradeable
is not set or used. However, this variable is referenced in the function deployEligibilityStorage
of NFTXVaultUpgradeable
, which should be initialized.
Referenced code: NFTXVaultFactoryUpgradeable.sol#L24 NFTXVaultUpgradeable.sol#L163-L177
None
Add functions in NFTXVaultFactoryUpgradeable
to set the variable eligibilityManager
.
🌟 Selected for report: shw
762.0744 USDC - $762.07
shw
The contract NFTXDeferEligibility
includes a constructor
; however, according to the documentation from Openzeppelin, it shouldn't:
Due to a requirement of the proxy-based upgradeability system, no constructors can be used in upgradeable contracts.
If we deploy the NFTXDeferEligibility
through the upgrades.deployProxy
function (as written in the tests), we will get an error:
contracts/solidity/eligibility/NFTXDeferEligibility.sol:23: Contract `NFTXDeferEligibility` has a constructor Define an initializer instead
Referenced code: NFTXDeferEligibility.sol#L23-L25
None
Remove the constructor
in the contract.
🌟 Selected for report: shw
shw
During initialization, the contract NFTXDeferEligibility
does not check whether the provided first parameter (i.e., _deferAddress
) is non-zero. Besides, the contract NFTXUniqueEligibility
does not check that the provided first and second parameters are non-zero (_owner
, _vault
) either. If any of these parameters are provided as zero accidentally, there is no chance to change the corresponding state variables, and the contracts should be redeployed.
Referenced code: NFTXDeferEligibility.sol#L44
None
Add non-zero address checks, e.g., require(_vault != address(0))
.
🌟 Selected for report: shw
shw
In the NFTXMintRequestEligibility
contract, the approveMintRequests
and claimUnminted
functions contain out-of-bound index access. Transactions calling these two functions will fail.
The dynamic array is not initialized with a given length and will be 0-length by default. Therefore, assigning the 0th element (at line 159, 160, 182, 183) of the array is considered out-of-bound.
Referenced code: NFTXMintRequestEligibility.sol#L157-L160 NFTXMintRequestEligibility.sol#L180-L183
None
In both functions, change uint256[] memory _tokenIds;
to uint256[] memory _tokenIds = new uint256[](1);
, and uint256[] memory _amounts;
to uint256[] memory _amounts = new uint256[](1);
.
🌟 Selected for report: shw
shw
In the contract ERC1155HolderUpgradeable
, the function supportsInterface
is not fully implemented, leading to errors for other contracts/Dapps that call this function. See the ERC165 specification for details.
Referenced code: ERC1155HolderUpgradeable.sol#L29-L34
None
Implement the function by using a mapping to store the supported interfaces, as shown in the ERC165Storage.sol implementation from OpenZeppelin.
#0 - 0xKiwi
2021-05-22T08:29:35Z
Added
🌟 Selected for report: shw
shw
In the contract NFTXLPStaking
, the return values of transfer
and transferFrom
are sometimes checked to be true
and sometimes not checked.
Referenced code: NFTXLPStaking.sol#L107 NFTXLPStaking.sol#L118 NFTXLPStaking.sol#L188
The following three lines in the code involve transfers of the rewardToken
or stakingToken
, but only one of them (at line 118) checks the result with a require
statement. If stakingToken
is not ERC20-compliant, then line 188 needs a require
statement. Or, if stakingToken
is sure to be ERC20-compliant, the require
at line 118 is not necessary.
IERC20Upgradeable(pool.rewardToken).transferFrom(msg.sender, address(rewardDistToken), amount); // line 107 require(IERC20Upgradeable(pool.stakingToken).transferFrom(msg.sender, address(this), amount)); // line 118 IERC20Upgradeable(pool.stakingToken).transfer(account, amount); // line 188
None
A more general and safer way is to use the safeTransfer
or safeTransferFrom
of the SafeERC20
implementation from OpenZeppelin.
🌟 Selected for report: shw
shw
During initialization, if the first parameter uniLikeExchange
is provided as 0 accidentally, there is no chance to update the corresponding state variable, and the contract should be redeployed.
Referenced code: StakingTokenProvider.sol#L25
None
Add a non-zero address check, i.e., require(uniLikeExchange != address(0))
.
59.0156 USDC - $59.02
shw
In the contract NFTXMintRequestEligibility
, the state variable manager
is not used. The state variable reverseEligOnRedeem
is assigned to a value during initialization but not used afterwards. There are also unused events, CanApproveMintRequestsSet
, Reject
, and Approve
.
Referenced code: NFTXMintRequestEligibility.sol#L28 NFTXMintRequestEligibility.sol#L31 NFTXMintRequestEligibility.sol#L44 NFTXMintRequestEligibility.sol#L50-L51
None
Consider removing these variables and events to save gas.
#0 - 0xKiwi
2021-05-22T08:15:35Z
Implemented usage of the above missing items.
#1 - cemozerr
2021-05-25T23:40:49Z
🌟 Selected for report: 0xRajeev
Also found by: maplesyrup, shw
145.7175 USDC - $145.72
shw
The function __NFTXEligibility_init_bytes
in the contract NFTXEligibility
can be declared external to save gas. Contracts that inherit NFTXEligibility
and override __NFTXEligibility_init_bytes
, i.e., the NFTX*Eligibility
ones, can also be declared external. Please refer to the following links as details.
Referenced code: NFTXEligibility.sol#L13 NFTXDeferEligibility.sol#L32-L37 NFTXListEligibility.sol#L28-L33 NFTXMintRequestEligibility.sol#L58-L68 NFTXRangeEligibility.sol#L44-L54 (NFTXUniqueEligibility.sol#L47-L57)[https://github.com/code-423n4/2021-05-nftx/blob/main/nftx-protocol-v2/contracts/solidity/eligibility/NFTXUniqueEligibility.sol#L47-L57]
Please refer to the files 500.js
, 500-original.png
, and 500-changed.png
in the following link.
PoC: Link to PoC
None
Change public
to external
and bytes memory
to bytes calldata
.
#0 - cemozerr
2021-05-25T23:42:06Z