Mundus Security Technical Blog

Top 10 NFT Smart Contract Vulnerabilities You Need to Know

Smart Contract Security Tips
Top 10 NFT Vulnerabilities
This article analyzes the widespread NFT flaws found by thorough Solidity-based NFT security audits. Although not all potential problems are addressed, this presents a wide range of frequently occurring NFT vulnerabilities.

Check out our website and please join our community!

Twitter

Telegram

Website

Looking for audit, let's talk

An Introduction to Non-Fungible Tokens

Non-Fungible Tokens, or NFTs, represent unique digital assets that stand as indivisible and distinct tokens of ownership for specific items or content, all tracked via blockchain technology. Unlike fungible cryptocurrencies like Bitcoin or Ethereum, which allow for one-for-one exchanges, each NFT boasts unique attributes, setting them apart from their counterparts. This aspect of uniqueness and verifiable ownership renders NFTs ideal for digital art, collectibles, etc.

Common Attack Vectors

NFT smart contract codes, despite their unique nature, can share vulnerabilities with other types of smart contracts. These vulnerabilities are typically related to NFT operations and the NFT contracts themselves. Below is an encompassing list of potential issues that could impact NFT contracts during their deployment and lifecycle.

  • Smart contract vulnerabilities: In audits of NFT projects, two frequently encountered complications are reentrancy vulnerabilities and improper access validations for crucial functions. Many contracts incorporate callback verification procedures to confirm that the recipient contracts are authorized to accept NFT transfers prior to processing them. These procedures create multiple instances for external contracts to exploit these checks, thereby executing malicious code. Furthermore, the minting of NFTs is a sensitive operation typically overseen by one or more privileged accounts. This control mechanism is established to preserve the rarity and intrinsic value of each individual NFT. Thus, it is of utmost importance to ensure robust user verification mechanisms are in place to permit only the correct users the ability to mint tokens. Any flaws in this execution may lead to unintentional restrictions on access to the NFT minting process.
  • Private Key Compromises: The integrity of an NFT is as strong as the private key that safeguards it. Therefore, users and projects need to be extremely cautious in managing their private keys. It's crucial to avoid sharing these keys, and users should always stay alert for possible vulnerabilities stemming from third-party tools such as Profanity. As part of the NFT smart contract audit process, assessing the robustness of key protection methods is a critical task.
  • Phishing Attacks: In the world of NFTs, phishing is a common attack technique. Scammers use sophisticated strategies to coax users into interacting with malicious contracts or revealing their private keys. They might mimic trusted entities or create a false sense of urgency. An essential part of any NFT deployment strategy should involve user education on phishing schemes and the implementation of mechanisms to safeguard against them.
  • Unrestricted Token Approval: This is a prevalent vulnerability in NFT operations where granting unlimited token approval to a malicious contract enables the operator of that contract to empty the compromised wallets of all their digital assets. In the context of NFT audits, this is a red flag and something developers need to address to ensure a secure environment for users.
  • Social Media Platform Breaches: Social media has become a hotspot for NFT trading and discussion. However, it's also a platform that attackers exploit. They target projects' Discord or Twitter accounts to spread harmful disinformation or misleading instructions, tricking users into making wrong decisions or exposing their sensitive information. Part of a thorough NFT audit involves verifying the project's social media security protocols.

Significant NFT Hacks Prior to July 2023: A Chronological Overview

During last 18 months more than $35mln was stolen in different NFT hacks. You could find the chronological list below.

Q1 2022:

  1. Lympo ($18.5M lost, January 2022): The Sports NFT platform Lympo suffered a data breach in its hot wallet, leading to a loss of $18.5M across 10 wallets.
  2. OpenSea Low-Price Exploit (January 2022): Cybercriminals took advantage of a back-end vulnerability, purchasing NFTs at significantly reduced prices and subsequently reselling them for a profit of more than 300 ETH (over $700K). The old listing remained accessible via OpenSea's API.
  3. Full Send Metacard Phishing (January 2022): Malefactors compromised the official Discord server, spamming a scam link that led to several users' wallets being emptied.
  4. LooksRare DDoS Attack (January 2022): Shortly after its launch, the NFT marketplace fell victim to a denial-of-service attack, leaving users grappling with connection issues even after the site's restoration.
  5. Dego Finance ($15.4M lost, February 2022): This project suffered a significant financial setback due to a attack on private keys and smart contracts.

Q2 2022:

  1. Bored Ape Yacht Club ($13M lost, April 2022): One of the largest phishing scams took place through a fake airdrop, tricking victims into transferring NFTs worth $13M.
  2. Rikkei Finance ($1.15M lost, April 2022): A considerable amount was lost in this project due to exploiting the ability to change the oracle through the public function setOracleData(). Rikket Finance relied on the SimplePriceOracle in the Cointroller to determine pricing, but unfortunately, the function setOracleData() was not limited and could be controlled by any user..
  3. Tales of Elleria (April 2023): Co-founder Wayne reported an exploit on the bridge contract of their NFT game, causing a loss of more than $280,000 due to unregulated ELM token extraction.
  4. OMNI ($1.4M lost, July 2022): Users suffered a significant financial hit during a massive reentrancy attack on the Ethereum-based NFT finance protocol. The attacker utilized a flash loan to amplify their buying power.

Q3 and Q4 2022:

  1. n00dleswap Reentrancy (October 2022): A hacker exploited a reentrancy vulnerability in the NFT DEX's smart contract, which was deployed on Ethereum.

Q1 2023:

  1. Azuki’s Twitter Hack (January 2023): An attacker breached a Twitter account belonging to a popular anime NFT collection, spreading a phishing link among followers. It marked the second instance since April 2022 when hackers exploited poor access control to Azuki’s social media platforms.
  2. OMNI Real Estate Exploit (January 2023): The Omni Real Estate token, deployed on BNB smart chain, suffered from several code weaknesses related to integer overflow/underflow and improper argument validation.
  3. @HideYoApes (February 2023): The owner of several high-value NFTs from Yuga Labs fell victim to an attack, with the malefactor selling all NFTs for a profit of 127.3 wETH (~$208,000). The user reported installing the MetaMask wallet extension from the official website.

Q2 2023:

  1. Astaria, the NFT lending platform (June 2023): Astaria identified a vulnerability in BeaconProxy.sol that permitted an attacker to exploit the beacon and trigger the self-destruct feature. However, no funds or NFTs were lost, and the platform successfully executed a white hat recovery script to protect all ERC20 and ERC721 assets of all LPs and borrowers. Astaria suspended new loans and communicated a future plan for next steps.

Check out our website and please join our community!

Twitter

Telegram

Website

Looking for audit, let's talk

List of Typical NFT vulnerabilities

Here we collect the most interesting examples of NFT related vulnerabilities.

1. Reentrancy Exploits via Unsafe Callbacks

Popular NFT standards, such as ERC-721 and ERC-1155, have corresponding template contracts developed by OpenZeppelin, often used within the community. These contracts use functions like _safeTransfer, _safeMint, _mint, _safeTransferFrom, _mintBatch, and _safeBatchTransferFrom to invoke the recipient contract via callback functions (onERC721Received, onERC1155Received, onERC1155BatchReceived) to ensure successful token receipt. Yet, this could potentially expose a security loophole where attackers can exploit these callbacks through reentrancy attacks.
Consider the following code from a vulnerable NFT contract that allows only whitelisted users to mint one NFT:
    function mintNFT(bytes memory _signature) public payable {
        require(mintActive, 'Not active');
        require(mintPrice <= msg.value, "Insufficient payable value");
        require(totalSupply().add(1).add(partnerMintAmount) <= TOTAL_NFT, "Can't mint more than 10000");
        require(whitelist[msg.sender], "Not whitelisted User")
        require(!addressMinted[msg.sender], "Address has minted");

        _safeMint(msg.sender, totalSupply() + 1);

        addressMinted[msg.sender] = true;
    }

In this code, the _safeMint call happens before setting the addressMinted[msg.sender] state to true. An attacker could use a reentrancy exploit on the mintNFT function, bypassing the require(!addressMinted[msg.sender], "Address has minted") check, minting multiple tokens. The mitigation strategy is to follow the check-effect-interaction coding pattern or implement reentrancy guardrails like nonReentrant modifiers.

2. Use safe functions instead of mint, transferFrom for ERC721

Some smart contracts don't support the ERC721 standard, meaning that using transferFrom() or mint() could result in NFTs being sent to such contracts. As stated in the EIP-721 documentation:
A wallet/broker/auction application MUST implement the wallet interface if it will accept safe transfers.
We suggest conducting a thorough review of the _stakeToken() function, paying particular attention to the transferFrom() and _mint() functions. Implementing additional safeguards to validate the contract compatibility before NFT transfers can help mitigate potential issues. For example, _safeMint и safeTransferFrom functions.

3. Inadequate Signature Validation in Token Minting

In another NFT contract, there's a bug in the minting process that can be exploited by attackers to mint excessive tokens.
Let’s take as an example hypothetical NFT project, uniqueness of NFT tokens is maintained by setting restrictions based on signature verification. Users can call the mintToken() function to pay ETH and mint NFTs, but the minting transaction needs initial approval by the "minter role". This is shown in the _verifyVoucher function's verification implementation
Note: The following code is simplified for better illustration:
  function mintTokens(uint256 tokenCount_, VoucherParams memory params_) external payable whenNotPaused {
    require(tokenCount_ * params_.pricePerToken == msg.value, "Not enough ETH Received");

    require(
      _verifyVoucher(
        abi.encode(
          keccak256(
            "Voucher(address buyer,uint256 issueTime,uint256 expirationDuration,uint256 pricePerToken,uint256 nonce,string functionABI)"
          ),
          _msgSender(),
          params_.issueTime,
          params_.expirationDuration,
          params_.pricePerToken,
          params_.nonce,
          keccak256(bytes("mintTokens(uint256,VoucherParams)"))
        ),
        params_,
        VOUCHER_SIGNER_ROLE
      ),
      "Verification Failed"
    );

    for (uint256 i = 1; i <= tokenCount_; i++) {
      _safeMint(_msgSender(), _tokenIdCounter.current());
      _tokenIdCounter.increment();
    }
  }

  function _verifyVoucher(
    bytes memory stuff_,
    VoucherParams memory params_,
    bytes32 role_
  ) internal returns (bool) {
    require(!_voucherNonces[params_.nonce], "The nonce has been used");
    _voucherNonces[params_.nonce] = true;
    require(
      params_.issueTime <= block.timestamp && block.timestamp <= (params_.issueTime + params_.expirationDuration),
      "Not correct time to mint"
    );

    //  Verify Signer
    require(hasRole(role_, ECDSA.recover(_hashTypedDataV4(keccak256(stuff_)), params_.signature)), "Not minter roles");

    return true;
  }

Although the signature has been verified by the ECDSA.recover function, the tokenCount_ parameter isn't included in the verification process. This means that once the VoucherParams verification passes, users can mint any number of tokens they want.

4. onERC721Received reenterancy

When observing the transfer of an NFT, ensure the code adheres to the checks-effects-interactions pattern or possesses a reentrancy guard. Absence of these protective measures might expose the project to a reentrancy exploit via the NFT's onERC721Received hook.
  function mint(
        MintTrade memory _mintTrade
    ) external onlyMinter {
        uint newTokenID = _tokenIds.current();

        Trade storage newTrade = _trades[newTokenID];
        newTrade.margin = _mintTrade.margin;
        newTrade.leverage = _mintTrade.leverage;
        newTrade.asset = _mintTrade.asset;
        newTrade.direction = _mintTrade.direction;
        newTrade.price = _mintTrade.price;
        newTrade.tpPrice = _mintTrade.tp;
        newTrade.slPrice = _mintTrade.sl;
        newTrade.orderType = _mintTrade.orderType;
        newTrade.id = newTokenID;
        newTrade.tigAsset = _mintTrade.tigAsset;

        _safeMint(_mintTrade.account, newTokenID);   // make external call because of safeMint() usage
        if (_mintTrade.orderType > 0) { // update the values of some storage functions
            _limitOrders[_mintTrade.asset].push(newTokenID);
            _limitOrderIndexes[_mintTrade.asset][newTokenID] = _limitOrders[_mintTrade.asset].length-1;
        } else {
            initId[newTokenID] = accInterestPerOi[_mintTrade.asset][_mintTrade.tigAsset][_mintTrade.direction]*int256(_mintTrade.margin*_mintTrade.leverage/1e18)/1e18;
            _openPositions.push(newTokenID);
            _openPositionsIndexes[newTokenID] = _openPositions.length-1;

            _assetOpenPositions[_mintTrade.asset].push(newTokenID);
            _assetOpenPositionsIndexes[_mintTrade.asset][newTokenID] = _assetOpenPositions[_mintTrade.asset].length-1;
        }
        _tokenIds.increment();
    }

As visible in the code, an external call is made to the attacker-specified address (sendTo) by collateral.addr.safeTransferFrom(address(this), sendTo, [collateral.id]). This invokes the sendTo address's onERC721Received() function. Subsequently, the code verifies that the debt is below the maximum allowed level, and retrieves the updated debt post-call, granting the user an opportunity to offset some of the debt within the hook function.

5. Owner control NFT after transferring, burning etc

Sometimes, developers forget that the owner of an NFT can still utilize the NFT's power even after transferring it. Similar issues can arise when OpenSea fails to cancel sale offers upon NFT transfer. If the NFT is eventually transferred back to the original owner, all previous approvals remain intact, which may come as an unexpected outcome for the owner. Let's examine the following code. In this case, there is a football club (represented as an NFT) that can have multiple players (also represented as NFTs) and funds (ERC-20 tokens). The owner has the ability to set the football club's players and funds to themselves using these functions.
function setApprovalForERC20(
    IERC20 erc20Contract,
    address to,
    uint256 amount
) external onlyClubOwner {
    erc20Contract.approve(to, amount);
}

/**
 * @notice Sets approval for ERC721 tokens.
 * @param erc721Contract ERC721 contract address.
 * @param to The address of token spender.
 * @param approved Boolean flag indicating whether approved or not.
 * @dev only the club owner address allowed.
 */
function setApprovalForERC721(
    IERC721 erc721Contract,
    address to,
    bool approved
) external onlyClubOwner {
    erc721Contract.setApprovalForAll(to, approved);
}

However, there's no mechanism in place to revoke these approvals upon transfer. As a result, the previous owner retains the ability to transfer the club's players and funds back to themselves even after selling the club. Some recommendations below:
  1. Implement a mechanism to revoke the prior approvals automatically upon transfer. This can be done within the transfer function to ensure that when an NFT is transferred, all prior approvals are invalidated.
  2. Introduce access controls to limit who can call the approval functions. This would restrict the ability to modify these approvals only to the current owner.

6. Replay Attacks Due to Weak Signature Verification

The next vulnerability also resides in the signature verification process, potentially leading to replay attacks. In this scenario, an attacker could reuse the same signature multiple times for personal gain.
In the code snippet below, the createToken() function allows users to create NFT tokens, given the NFT calldata has been signed by the project owner. However, there is no validation to check if the signature has been used or has expired. Therefore, an attacker can repeat the same calldata to create unlimited tokens.
    function createToken(NFTMint calldata nft) external
    {
        require( _owner = _verifyNFT(nft), "Created NFT is not verified by owner");
        uint256 tokenID = tokenCounter;
        tokenCounter++;
        _mint(nft.issuer, tokenID, nft.totalSupply, ""); // Assign fractional tokens to the issuer
        _setTokenURI(
            tokenID,
            nft.deedNo,
            nft.assetID,
            nft.issuerID,
            nft.projectID
        );
        details[tokenID].tokenID = tokenID;
        details[tokenID].issuer = nft.issuer;
        details[tokenID].tokens = nft.totalSupply;
        emit CreatedNFT(tokenID, nft.issuer);
    }

    function _verifyNFT(NFTMint calldata nft)
        public
        view
        returns (address)
    {
        bytes32 digest = _hashNFT(nft);
        return ECDSA.recover(digest, nft.signature);
    }

Signatures could also be susceptible to cross-chain replay attacks. Without distinction between different supported blockchains, a transaction intended for one network could be executed on another network via a replay attack. Implementing EIP712 to identify and place checks on the chainId can prevent cross-chain replay attacks by ensuring signatures are valid only for a specific network.
NFT marketplaces, where NFTs are bought and sold, are crucial for user interaction with NFTs as most transactions related to NFTs occur here. Since they play a significant role in determining the price and value of these NFTs, marketplaces are prime targets for hackers. Hence, it's paramount to ensure the security of smart contract code associated with them

7. Potential for Reentrancy Attacks within the Bidding Process

Marketplaces often facilitate users to bid on NFTs and trade them for other tokens. They usually provide services for users to bid on specified NFTs and allow NFT owners to transfer ownership. However, a potential pitfall is the reentrancy attack vector embedded within certain marketplace implementations, which can be exploited to drain assets from the contract.
Here, a vulnerable marketplace contract has designed specific functions to facilitate NFT trading:
  • enterBid(): Allows bidders to place bids
  • acceptBid(): Enables NFT owners to accept the bid and exchange NFT for ETH
  • withdrawBid(): Grants buyers the ability to revoke a bid and offers refunds to bidders
  • withdrawPendingFunds(): Allows withdrawal of pending ETH from an NFT transaction
Note: The code below has been simplified for clarity:
  function enterBid(uint _id) external payable {
    require(NFT.ownerOf(_id) != address(0x0), "Cannot bid on NFT assigned to the 0x0 address.");
    require(msg.value > 0, "Must offer a nonzero amount for bidding.");

    Bid memory existingBid = bids[_id];
    require(msg.value > existingBid.value, "A higher bid has already been made for this NFT.");

    // Refund the existing bid to the original bidder and overwrite with the higher bid.
    if (existingBid.value > 0) {
      pendingWithdrawals[existingBid.bidder] += existingBid.value;
    }
    bids[_id] = Bid(msg.sender, msg.value);
  }

  function acceptBid(uint _id, uint _minPrice) external onlyNFTOwner(_id) {
    Bid memory existingBid = bids[_id];
    require(existingBid.value > 0, "Cannot accept a 0 bid.");
    require(existingBid.value >= _minPrice, "Existing bid is lower than the specified _minPrice.");

    NFT.safeTransferFrom(msg.sender, existingBid.bidder, _id);

    delete bids[_id];
    pendingWithdrawals[msg.sender] += existingBid.value;
  }

  function withdrawBid(uint _id) external nonReentrant {
    require(bids[_id].bidder == msg.sender, "Cannot withdraw a bid not made by the sender.");

    uint amount = bids[_id].value;
    Address.sendValue(payable(msg.sender), amount);
    delete bids[_id];
  }

  function withdrawPendingFunds() external nonReentrant {
    Address.sendValue(payable(msg.sender), pendingWithdrawals[msg.sender]);
    delete pendingWithdrawals[msg.sender];
  }

The problem arises as the bids[] record gets deleted subsequent to the external calls of the safeTransferFrom() and Address.sendValue() functions. This vulnerability enables an attacker to execute a reentrancy attack, using the onERC721Received() callback or native token transfer fallback, thereby draining funds from the contract.
The attacker can activate acceptBid() and withdrawBid() in a single transaction. This maneuver results in non-payment while simultaneously allowing the extraction of assets from the contract. The specific attack flow is as follows:
  1. The attacker first acquires an NFT token (either through purchase or loan)
  2. The attacker then places a high bid for the NFT
  3. Since the attacker is also the token owner, they can invoke acceptBid(), triggering the safeTransferFrom() function in the NFT contract
  4. The safeTransferFrom() function in turn initiates the onERC721Received() callback on the attacker's contract, where withdrawBid() is called
  5. Since the bid record hasn't been deleted yet, withdrawBid() executes successfully and transfers ETH to the attacker
  6. Finally, once the acceptBid() function is completed, the contract increments the pendingWithdrawals records of the attacker, enabling them to withdraw additional ETH later
The flaw lies in the reentrancy loophole, where the attacker can trade an NFT with themselves without incurring any cost, but misleading the contract to transfer assets to them.
This type of vulnerability is common during auditing. It's recommended that projects follow the check-effects-interaction pattern to avert reentrancy attacks. Furthermore, it's important to note that a single nonReentrant modifier on the withdrawBid() function is insufficient to prevent a "cross-function" reentrancy attack. Therefore, it's advisable to apply the nonReentrant modifier to all interconnected functions as needed.

8. Flawed Price Calculation Logic

Proper price calculation for NFTs is a significant factor that demands attention. The following example is derived from an actual exploit that led to the theft of more than 100 NFTs. This incident arose due to a simple design flaw in the price calculation mechanism, which inadvertently allowed users to purchase ERC721 NFTs without any payment. Below are the excerpts of the flawed code:
The Buyer Contract:
    function buyItem(
        address _nftAddress,
        uint256 _tokenId,
        address _owner,
        uint256 _quantity,
        uint256 _pricePerItem
    ) external {
        (, uint256 pricePerItem,) = marketplace.listings(_nftAddress, _tokenId, _owner);

        require(pricePerItem == _pricePerItem, "pricePerItem changed!");

        uint256 totalPrice = _pricePerItem * _quantity;
        IERC20(marketplace.paymentToken()).safeTransferFrom(msg.sender, address(this), totalPrice);
        IERC20(marketplace.paymentToken()).safeApprove(address(marketplace), totalPrice);

        ...
  }

The Marketplace Contract:
    function buyItem(
        address _nftAddress,
        uint256 _tokenId,
        address _owner,
        uint256 _quantity
    )
        external
        nonReentrant
        isListed(_nftAddress, _tokenId, _owner)
        validListing(_nftAddress, _tokenId, _owner)
    {
        require(_msgSender() != _owner, "Cannot buy your own item");

        Listing memory listedItem = listings[_nftAddress][_tokenId][_owner];
        require(listedItem.quantity >= _quantity, "not enough quantity");

        // Transfer NFT to buyer
        if (IERC165(_nftAddress).supportsInterface(INTERFACE_ID_ERC721)) {
            IERC721(_nftAddress).safeTransferFrom(_owner, _msgSender(), _tokenId);
        } else {
            IERC1155(_nftAddress).safeTransferFrom(_owner, _msgSender(), _tokenId, _quantity, bytes(""));
        }

        ...
    }

In this setup, the buyItem() function in the Buyer contract calculates fees based on the quantity and calls the buyItem() function in the Marketplace contract to handle the NFT distribution.
The buyItem() function in the Marketplace contract facilitates the purchase of NFT tokens conforming to both the ERC721 and ERC1155 standards. However, due to a logical flaw in its implementation, users can acquire ERC721 tokens without any cost by inputting the value 0 for the quantity parameter.
Despite this being a glaring error, it also serves as a cautionary tale emphasizing the importance of meticulous design and case-specific handling to prevent bugs that may arise due to protocol incompatibilities.

9. Incorrect Tracking/Updating of NFT Counter

Consistency across different functions is crucial, particularly for the paired mint and burn functions. Ideally, a burn function should reverse all state changes made by a mint function. Any disparities in these paired functions, such as neglecting to unset a field or subtract from a value, can lead to the inadvertent assignment or overwriting of existing NFTs to other users. Let's delve into an instance of this issue:
    function register(uint256 _cidNFTID) external {
         if (ERC721(cidNFT).ownerOf(_cidNFTID) != msg.sender)
             // We only guarantee that a CID NFT is owned by the user at the time of registration
             // ownerOf reverts if non-existing ID is provided
             revert NFTNotOwnedByUser(_cidNFTID, msg.sender);
         cidNFTs[msg.sender] = _cidNFTID;
         emit CIDNFTAdded(msg.sender, _cidNFTID);
     }

The current implementation allows the same CID NFT to be registered under multiple accounts simultaneously. This occurs because the registration function doesn't check whether the CID NFT has been previously registered, nor does it reset any prior association. This could lead to a scenario where the same CID NFT is registered to multiple accounts at the same time, given that a CID NFT can be registered, transferred, re-registered, and this sequence could be repeated indefinitely.
Recommendations:
  1. Introduce a mechanism to track the registration status of each NFT. This could be done using a mapping where the key is the NFT ID, and the value indicates whether it has been registered or not.
  2. Before registering an NFT, check if it's already been registered. If it has, either reject the operation or ensure to reset any prior association.
  3. Emit an event when an NFT is transferred or unregistered, to provide better visibility into state changes.

10. Lack of Ownership Verification When Function Arguments Include Arbitrary NFT ID

In situations where an arbitrary NFT ID is part of function arguments, it is essential to verify that the caller indeed owns the NFT on which the function is being executed. Neglecting this check can leave the contract vulnerable to exploits. Let's take a look at an example below:
function withdrawFromGauge(uint256 _NFTId, address[] memory _tokens)  public  {
    uint256 amount = depositReceipt.pooledTokens(_NFTId);
    depositReceipt.burn(_NFTId);
    gauge.getReward(address(this), _tokens);
    gauge.withdraw(amount);
    //AMMToken adheres to ERC20 spec meaning it reverts on failure, no need to check return
    //slither-disable-next-line unchecked-transfer
    AMMToken.transfer(msg.sender, amount);
}

This function is designed to withdraw funds from the gauge by burning the deposit receipt NFT. A potential threat arises when, after the NFT is transferred or approved, a malicious user can exploit this function to illegitimately withdraw the NFT for themselves.
Recommendations:
  1. Implement ownership checks in functions that handle NFT transactions. A call to the ownerOf() function should be made at the beginning of such functions to verify that the msg.sender is indeed the owner of the _NFTId. If the check fails, the function should revert.
  2. Use function modifiers such as onlyOwner() to restrict the execution of certain critical functions only to the owners of specific NFTs.

The Vital Role of Smart Contract Audits in NFT Projects

Smart contract audits play a crucial role in identifying potential weaknesses within the code that could be manipulated, leading to reputation damage or asset loss. Additionally, crypto audits enhance system efficiency and performance by identifying and eliminating errors and optimizing the underlying code.

The Solution – Conducting an NFT Smart Contract Audit

Throughout the smart contract auditing process, auditors meticulously scrutinize the code for potential vulnerabilities, such as susceptibility to denial of service attacks, gas limit issues, reentrancy attacks, insecure random number generation, overflow and underflow errors, and logical inconsistencies. This rigorous vulnerability analysis assigns a severity level to each identified security issue. Consequently, the final audit report serves as a definitive guide on the remedial actions needed to enhance security.

How Can Users Mitigate Potential Risks?

Here are some basic guidelines for ensuring secure management of your digital artwork and in-game collectibles:
  1. Opt for reputable NFT marketplaces and secure wallets for storage.
  2. Stay alert for scams and verify the legitimacy of any offers before executing transactions involving funds or NFTs.
  3. Implement multi-factor authentication whenever available.
  4. Thoroughly review the specifics of each NFT transaction before authorizing it through your wallet.
What Does an NFT Audit Entail?
An NFT audit is a thorough code review aimed at confirming the technical soundness and safety of a token, smart contract, and NFT trading platform, thereby reducing the potential for cyber threats.
Is an Audit Necessary for NFTs?
Just like any other token, NFTs also require auditing. This is essential to minimize the risk of these digital assets being compromised or stolen by hackers.
What is Smart Contract Auditing?
Smart contract auditing is a rigorous security measure where a qualified external auditor examines the smart contract code, typically written in languages like Solidity, Move, Vyper, or Rust. This process is designed to uncover any vulnerabilities that could jeopardize the system's operation. For more details, feel free to reach out to the Mundus Security Team!

Check out our website and please join our community!

Twitter

Telegram

Website

Looking for audit, let's talk