xanoutas/solidity-security-findings huggingface dataset:
function flashLoan is vulnerable to overflow/underflow and maxFlashLoan is not used
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

paulius.eth


# Vulnerability details

## Impact
function flashLoan is vulnerable to overflow/underflow when the fee is not 0. Although currently the fee is set to 0, there is a comment: "By default there is no fee, but this can be changed by overriding {flashFee}" As these contracts are upgradeable, I cannot assume that this fee will always stay 0, thus I want you to be aware of this possible issue. function flashLoan does not use SafeMath when doing the calculations and accepts an arbitrary value for the amount parameter. When the fee is above 0, it is possible to pass such a value for an amount that (amount + fee) will overflow/underflow. For example, if the fee is set to 1 and I invoke a flashLoan with an amount of max_uint, I will be minted max_uint of tokens and will need to return (burn) 0 tokens. 
Also, there is a function maxFlashLoan which returns the maximum amount of tokens available for a loan, however, this function is never used. I assume the intention was to limit the amount you can borrow but currently it has no effect.

## Recommended Mitigation Steps
Either use SafeMath here or make sure to never introduce a fee > 0. Also, make use of maxFlashLoan function.
function swapTo doesn't have a re-entrancy modifier
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

paulius.eth


# Vulnerability details

## Impact
function swap has a nonReentrant modifier but function swapTo doesn't. swapTo is a public function so it can be invoked directly.

## Recommended Mitigation Steps
I guess it was meant to be the opposite as swap just invokes swapTo so it could automatically inherit nonReentrant from swapTo. Move nonReentrant modifier from swap to swapTo.
erc1155 are redeemed one by one
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

paulius.eth


# Vulnerability details

## Impact
When depositing erc1155s amounts array is used and tokens are sent in bulk (safeBatchTransferFrom), however, when redeeming it iterates over the amount and redeems it one by one. It is not convenient when the amount is large. Let's say I deposited 1000 arrows (erc1155), the deposit is done in one tx one function (mintTo) and is relatively cheap. To later redeem it, I need to call redeemTo hundreds of times to redeem in small batches as every unit consumes extra gas. I think that is not an appropriate handle of erc1155s as its primary difference between erc721 is that it can be transferred in batches. The example was given with 1000 items but imagine what happens when the amount >100k, I expect such items would be left in the vault forever as redeeming it one by one was infeasible.

## Recommended Mitigation Steps
I would suggest somehow combine redeem logic with balanceOf function to avoid this issue.
getRandomTokenIdFromFund not really random
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

gpersoon


# Vulnerability details

## Impact
The function getRandomTokenIdFromFund of NFTXVaultUpgradeable.sol is not really random, as noted in the name of the function getPseudoRand.
The value of the blockhash(block.number - 1) is fully determined for al the transactions in the same block.
The result is that the retrieval of NFS's (via redeemTo, swapTo, withdrawNFTsTo, while enableDirectRedeem==false) can be manipulated.
You can make a smart contract that tries to call redeemTo or swapTo, checks the resulting NFTs and reverts if it has NFTs that are unwanted.

## Proof of Concept
    function getPseudoRand(uint256 modulus) internal virtual returns (uint256) {
        randNonce += 1;
        return
            uint256(
                keccak256(
                    abi.encodePacked(blockhash(block.number - 1), randNonce)
                )
            ) %
            modulus;
    }

## Tools Used
Editor

## Recommended Mitigation Steps
If the value or desirability of the NFT's are different then it's important to use other ways to randomize, for example via a random oracle or a commit/reveal schema.
Missing allValidNFTs and afterRedeemHook with swapTo?
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

gpersoon


# Vulnerability details

## Impact
The function swapTo of NFTXVaultUpgradeable.sol is kind of a combination of mintTo and redeemTo (the code looks very similar to a combination of mintTo and redeemTo).
Before receiveNFTs I would expect a call to allValidNFTs, like in mintTo.
This is to make sure only eligible NFTs are transferred. Without this check any NFT could be transferred.

After withdrawNFTsTo I would expect a call to afterRedeemHook, like in redeemTo.
The afterRedeemHook fixes the administration for the eligability. Without this the eligibility administrations would be flawed.

This way swapTo would circumvent the checks of mintTo and redeemTo and would allow any NFT to be transferred (even the one's that are not eligible)

## Proof of Concept
NFTXVaultUpgradeable.sol

function mintTo(..)
  ...
        require(allValidNFTs(tokenIds), "NFTXVault: not eligible");
        uint256 count = receiveNFTs(tokenIds, amounts);

 function redeemTo(uint256 amount, uint256[] memory specificIds, address to)
   ....
        uint256[] memory redeemedIds = withdrawNFTsTo(amount, specificIds, to);
        afterRedeemHook(redeemedIds);

 function swapTo(..)
        ...
        uint256 count = receiveNFTs(tokenIds, amounts);
        ...
        uint256[] memory ids = withdrawNFTsTo(count, specificIds, to);
      

## Tools Used
Editor

## Recommended Mitigation Steps
Check if it necessary to also add the following statements in swapTo:
-  require(allValidNFTs(tokenIds), "NFTXVault: not eligible");
-  afterRedeemHook(redeemedIds);
EIP-721 / EIP-1155 Re-Entrancy Vulnerability
Audit: 2021-05-nftx-findings
Severity: HIGH

# Handle

0xsomeone


# Vulnerability details

## Impact

The impact of this finding is difficult to estimate as the contract system within scope is limited in how the various components are meant to be utilized. 

A definitive side-effect of this re-entrancy is the delayed application of the `afterRedeemHook` which, in some implementations, renders NFTs illegible which would not be the case during the re-entrancy's execution. Another side-effect is that the `quantity1155` or `holdings` would be out-of-sync and would indicate the NFT / EIP-1155 token amount to still be "in the system" when it is not.

## Proof of Concept

The `safeTransferFrom` implementations of both `ERC1155` and `ERC721` in `withdrawNFTsTo` contain a callback hook on the recipient of the transfer in case they are a contract as the standard dictates that smart contract recipients should be aware of the transfer.

While re-entrancies are prevented via the `nonReentrant` modifier for most system functions, they are not done so for `swapTo` (and consequently `swap`) invocations meaning that it is still possible to re-enter the system at this stage. Additionally, re-entrancy is still possible in other segments of the codebase i.e. ones that rely on the eligibility contracts.

## Tools Used

Manual Review.

## Recommended Mitigation Steps

The `afterRedeemHook` paradigm should be changed to a `beforeRedeemHook` paradigm to ensure that all state changes are applied prior to external calls according to the Checks-Effects-Interactions pattern. Additionally, the state changes within `withdrawNFTsTo` should occur prior to the `safeTransferFrom` invocations.
Delegated transfer of owner fails
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

cmichel


# Vulnerability details


## Vulnerability Details

The `Visor.delegatedTransferERC20` function skips the approval check if `msg.sender == _getOwner()`, however, it will still try to reduce the approval in that case.
As it is implemented that the owner does not need an approval for this function, a previous approve action was most likely never sent and the transaction will fail when trying to reduce the `erc20Approvals` field by the `amount` due to the usage of SafeMath underflow checks.

## Impact

Owners cannot transfer using this function.

## Recommended Mitigation Steps

Move the approval subtraction to the `if` path.
Unchecked return value of `transfer` in `NFTXFeeDistributor`
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

shw


# Vulnerability details

## Impact

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.

## Proof of Concept

Referenced code:
[NFTXFeeDistributor.sol#L143](https://github.com/code-423n4/2021-05-nftx/blob/main/nftx-protocol-v2/contracts/solidity/NFTXFeeDistributor.sol#L143)

## Tools Used

None

## Recommended Mitigation Steps

Use the `safeTransfer` or `safeTransferFrom` of the [`SafeERC20` implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol) from OpenZeppelin.
mintRequests can remain 0 when the token is erc721
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

paulius.eth


# Vulnerability details

## Impact
Contract NFTXMintRequestEligibility function requestMint sets mintRequests to the amount that was minted, however, it does not check that amount[i] > 0, so it is possible that when the token is not erc1155, the amount has a value of 0 but the token is still transferred from the sender to the contract and because mintRequests remains 0, this token cannot later be redeemed as all these claim functions have a condition: require(amount > 0).

## Recommended Mitigation Steps
Either always require that amount[i] > 0 or explicitly set mintRequests to 1 when erc721 is deposited.
Use safeTransfer/safeTransferFrom consistently instead of transfer/transferFrom
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.

While most places use a require or safeTransfer/safeTransferFrom, there are three missing cases in the withdrawal of staking token and rescue of arbitrary tokens sent to the FeeDistributor contract.

Reference this similar medium-severity finding from Consensys Diligence Audit of Fei Protocol: https://consensys.net/diligence/audits/2021/01/fei-protocol/#unchecked-return-value-for-iweth-transfer-call


## Proof of Concept

https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXLPStaking.sol#L188

https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXFeeDistributor.sol#L45

https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXFeeDistributor.sol#L143


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Consider using safeTransfer/safeTransferFrom or require() consistently.
Randomization of NFTs returned in redeem/swap operations can be brute-forced
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

If we assume that certain NFTs in a vault over time will have different market demand/price then the users will try to redeem those specific NFTs. Even if direct redeems are disabled to prevent such a scenario to default to returning randomized NFTs, a user can brute-forced this on-chain randomization (using nonce + blockhash) by repeatedly trying to redeem/swap from a contract, checking the NFT IDs returned from the function and reverting the transaction if those are not the NFT IDs of specific interest.

The impact will be a subversion of the randomization goal to return random NFTs which cannot be specified by the user.

A similar exploit happened recently with Meebit NFTs https://twitter.com/sillytuna/status/1391013965170454540


## Proof of Concept

https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L376

https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L413-L427


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Consider onlyEOA for redeem/swap operations to prevent brute-forcing via contracts. Alternatively, make the user commit to pseudo-random IDs before revealing them.
NFTs initially of similar price may change significantly in value leading to unfair ownership changes
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

Vaults might start off as holding NFTs of similar price but over time some of them might be revealed as having unique/hidden features (or older ones with lower IDs) leading to increased demand (e.g. certain Hashmasks or Cryptopunks) which might significantly increase their value compared to others in the vault. The protocol specification/design treats all of them as the same allowing a user to redeem potentially higher-valued NFT(s) (using directRedeem/swap) later having minted lower-valued one(s) earlier.

The impact could be significant to users based on the value of the NFTs minted/redeemed. Owners of NFTs whose value increases significantly at a later time may lose “ownership" of their earlier minted ones to someone else who specifically redeems their NFTs anticipating/knowing the increased valuation. This leads to perceived ownership/value loss.

## Proof of Concept


https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L207-L223


https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L233-L257


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Given that this indexing is fundamental to the protocol offering, design and assumptions, not sure if there is a specific mitigation. Removing the ability to directRedeem could perhaps make redeeming entirely pseudo-random to everyone and seemingly more fair.
The direct redeem fee can be circumvented
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

janbro


# Vulnerability details

## Summary
The direct redeem fee can be circumvented

## Risk Rating
Medium

## Vulnerability Details
Since the random NFT is determined in the same transaction a payment or swap is being executed, a malicious actor can revert a transaction if they did not get the NFT they wanted. Combined with utilizing Flashbots miners which do not publish transactions which revert with FlashbotsCheckAndSend, there would be no cost to constantly attempting this every block or after the nonce is updated from getPseudoRand().

NFTXVaultUpgradeable.sol
```
Line 374: uint256 tokenId = i < specificIds.length
                ? specificIds[i]
                : getRandomTokenIdFromFund();
```

## Impact
The directReedemFee can be avoided and users lose on potential earnings.

## Proof of Concept
Transfer ownership of ERC20 tokens to attack contract
```
function revertIfNotSpecifiedID(uint256 targetTokenID) public {
    NFTXVaultUpgradeable vault = NFTXVaultUpgradeable(_vault);
    uint256[] resultID = vault.redeem(1,[]);
    require(resultID[0] == targetTokenID);
}
```

## Tools Used
Manual Code Review

## Recommended Mitigation Steps
Use a commit-reveal pattern for NFT swaps and redemptions
A malicious receiver can cause another receiver to lose out on distributed fees by returning `false` for `tokensReceived` when receiveRewards is called on their receiver contract.
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

janbro


# Vulnerability details

## Summary
A malicious receiver can cause another receiver to lose out on distributed fees by returning `false` for `tokensReceived` when receiveRewards is called on their receiver contract.

## Risk Rating
Medium

## Vulnerability Details
A malicious receiver can cause another receiver to lose out on distributed fees by returning `false` for `tokensReceived` when receiveRewards is called on their receiver contract. This causes the fee distributor to double spend the `amountToSend` because the contract incorrectly assumes the returned data is truthful.

NFTXFeeDistributor.sol
```
Line 163: (bool success, bytes memory returnData) = address(_receiver.receiver).call(payload);
```

## Impact
Any subsequent receivers not receiving funds

## Tools Used
Manual Code Review

## Recommended Mitigation Steps
Don't trust return data from externally called contracts. Only utilize whether the transaction succeeds to determine if the treasury fallback should be called.
```
Line 165: if (!success) {
```
Insecure randomness  in getPseudoRand(uint256 modulus){}  function
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

JMukesh


# Vulnerability details

## Impact
insecure randomness due to a modulo on block.timestamp, now or blockhash. These can be influenced by miners to some extent so they should be avoided

## Proof of Concept

  https://github.com/code-423n4/2021-05-nftx/blob/main/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L418

## Tools Used
 slither

## Recommended Mitigation Steps
use chainlink vrf
Tokens can get stuck in `NFTXMintRequestEligibility`
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

@cmichelio


# Vulnerability details


## Vulnerability Details

When dealing with ERC721 (instead of 1155) the amounts array is ignored, which leads to an issue.

User can call `NFTXMintRequestEligibility.requestMint` for an ERC721 with `amounts[i] = 0`.
The `ERC721.transferFrom` is still executed but user cannot `reclaimRequestedMint` later and the NFT is stuck as it checks (`amounts[i] > 0`).


## Impact

Tokens can get stuck.
Also, subscribers to `Request` event could be tricked by specifying `amounts[i] > 1` in the ERC721 case, as only one token was transferred but the amount multiple quantities get logged.

## Recommended Mitigation Steps

`requestMint`: Check `amounts[i] == 1` in ERC721 case, `amounts[i] > 0` in 1155 case.
`getPseudoRand` can be predicted
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

@cmichelio


# Vulnerability details


## Vulnerability Details

The `NFTXVaultUpgradeable.getPseudoRand` is not really random and can be predicted.
It's also easy to make sure that one gets the correct token by having a smart contract simulate the randomness logic before the call to redeem / swap.


## Impact

When redeeming one should only be able to specify the NFTs when `enableDirectRedeem` is `true` which allows specifying `specificIds`.
With randomness that is resolved in the same transaction, one can circumvent this restriction and get the same behaviour as if `enableDirectRedeem` is true even though it's false.
A smart contract can redeem and check the result of the redeem and if it's not the desired result, the transaction is reverted and retried.


## Recommended Mitigation Steps

This predictability can only be circumvented with a two-step process that doesn't resolve the randomness in the same step as the tokens are transferred.
Manager can grief with fees
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

@cmichelio


# Vulnerability details


## Vulnerability Details

The fees in `NFTXVaultUpgradeable` can be set arbitrarily high (no restriction in `setFees`).

## Impact

The manager can frontrun mints and set a huge fee (for example `fee = base`) which transfers user's NFTs to the vault but doesn't mint any pool share tokens in return for the user.

Similar griefing attacks are also possible with other functions besides `mint`.


## Recommended Mitigation Steps

Check for a max fee as a percentage of `base` (like 10%) whenever setting fees.
Unbounded iteration in `NFTXEligiblityManager.distribute` over `_feeReceivers`
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

@cmichelio


# Vulnerability details


## Vulnerability Details

`NFTXEligiblityManager.distribute` iterates over all `_feeReceivers`.

## Impact

If the number of `_feeReceivers` gets too big, the transaction's gas cost could exceed the block gas limit and make it impossible to call `distribute` at all.

## Recommended Mitigation Steps

Keep the number of `_feeReceivers` small.
function receiveNFTs does not check if amount > 0
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

paulius.eth


# Vulnerability details

## Impact
When is1155 is true, function receiveNFTs iterates over all the tokens and updates holdings and quantity1155. If the quantity1155 is 0 for that token, it adds this token to the holdings set. However, it does not check that the amount is greater than 0, thus it is possible to push the same token to the hodlings multiple times as quantity1155 still remains 0.

## Recommended Mitigation Steps
Solution: check that amount > 0 if is1155 is true. Also, it would be a good practice to check that the amounts array is empty when it is erc721 as amounts are ignored for ERC721 vaults.
Fee Distribution Re-Entrancy
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

0xsomeone


# Vulnerability details

## Impact

The `distribute` function of `NFTXFeeDistributor` has no access control and will invoke a fallback on the fee receivers, meaning that a fee receiver can re-enter via this function to acquire their allocation repeatedly potentially draining the full balance and sending zero amounts to the rest of the recipients.

## Proof of Concept

A smart contract with a malicious `receiveRewards` function can re-enter the `distribute` function with the same vault ID thereby causing the exploit.

## Tools Used

Manual review.

## Recommended Mitigation Steps

Re-entrancy protection should be incorporated into the `distribute` function. I should note that a seemingly innocuous contract can cause this re-entrancy by simply asking the owners of the project to include an upgrade-able contract that is then replaced for a malicious implementation.
This is a test submission and can be deleted
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

adamavenir


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps
EIP-721 / EIP-1155 Re-Entrancy Vulnerability (Resubmission)
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

0xsomeone


# Vulnerability details

This submission acts as additional material to the original submission. I would like to note that the "Areas of Review" chapter of the contest explicitly states that "Vaults should always maintain 1:1 ratio..." which the re-entrancy breaks.
Weak PRNG 
Audit: 2021-05-nftx-findings
Severity: MEDIUM

# Handle

maplesyrup


# Vulnerability details

## Impact

Using blockhash/blocknumber and randNone are subject to attack, particularly by malicious miners:

https://medium.com/coinmonks/attack-on-pseudo-random-number-generator-prng-used-in-1000-guess-an-ethereum-lottery-game-7b76655f953d

This could be used to the behavior of getRandomTokenIdFromFund to cause a preferential TokenId to be returned.  It allows for gaming of the system by miners or a savvy attacker.

## Proof of Concept

Code is at - https://github.com/code-423n4/2021-05-nftx/blob/f6d793c136d110774de259d9f3b25d003c4f8098/nftx-protocol-v2/contracts/solidity/NFTXVaultUpgradeable.sol#L418


## Tools Used

Slither

## Recommended Mitigation Steps

Use of an on-chain oracle for true randomness:

ChainLink- https://blog.chain.link/verifiable-random-functions-vrf-random-number-generation-rng-feature/
RanDAO - https://github.com/randao/randao
Provable - https://provable.xyz/