Search code examples
soliditynftremix

The SetApprovalForAll is not working when I try to transferFrom from a marketplace


I built a NFT smart contract called AliveNatureERC721 and I also built a marketplace contract called AliveNatureMarketplaceV1.

Both contracts are below. I'm using Remix. I got an error

Fail with error 'Only the owner of NFT can transfer or burn it'

on the ERCNFT.transferFrom( msg.sender, address(this), _tokenId) even when I went to the deployed NFT smart contract on Remix and executed the function setApprovalForAll("marketplace smart contract address", true) before I called the function listNFT(ERC721 _nft, uint256 _tokenId, uint256 _price, address _coin).

I also tried to call the setApprovalForAll function inside the marketplace smart contract and didn't work either.

Here is the call I'm doing:

Marketplace address: 0x3f46f42900DDFE85FC03B80D934608a8C2522Dc
NFT address: 0x2CF055487A80014a10d75D4EC86de2b0Ac1C9B0e

setApprovalForAll("0x3f46f42900DDFE85FC03B80D934608a8C2522Dc", true)

after I call ERCNFT.transferFrom( msg.sender, address(this), _tokenId); where address(this) is 0x3f46f42900DDFE85FC03B80D934608a8C2522Dc

I don't know what I'm doing wrong in this case.

Marketplace smart contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";


contract AliveNatureMarketplaceV1 is Ownable, Pausable,  ReentrancyGuard  {
    IERC20 public ERC20;
    IERC721 public ERCNFT;

    using Counters for Counters.Counter;
    
    Counters.Counter private numOfListing;

    uint256  commissionPercentage = 250;

struct NFTListing {  
  ERC721 nft;
  uint tokenId;
  uint price;
  address coin;
  address seller;
  bool forSale;
}
  
  event NftBuy(address _buyer, uint256 _tokenId, uint256 _price);
  event NftList(address _seller, IERC721 _nft, uint256 _tokenId);
 
  mapping(uint256 => NFTListing) public listings;
   
   modifier onlyNftOwner(uint _Id) {
        require(msg.sender == listings[_Id].seller);
        _;
    }


    function pauseMarketplace() public onlyOwner {
        _pause();
    }

    function unpauseMarketplace() public onlyOwner {
        _unpause();
    }

  
// this function will list and sell an NFT into the marketplace
  function listNFT(ERC721 _nft, uint256 _tokenId, uint256 _price, address _coin) public  {
    ERCNFT = IERC721(_nft);
    require (ERCNFT.ownerOf(_tokenId) == msg.sender, "You are not the owner");
    require(_price > 0, "NFTMarket: price must be greater than 0");
    numOfListing.increment();
    listings[numOfListing.current()] = NFTListing(
       _nft,
       _tokenId,
       _price,
       _coin,
       payable(msg.sender), 
       false
       );
    NFTListing storage listing = listings[_tokenId];
   //ERCNFT.approve(address(this) , _tokenId);
  // listing.nft.setApprovalForAll(ERCNFT, true);
    ERCNFT.transferFrom( msg.sender, address(this), _tokenId);
    listing.forSale = true;
     emit NftList(msg.sender, ERCNFT, _tokenId);

  } 



// this function will cancel the listing. it also has checks to make sure only the owner of the listing can cancel the listing from the market place

  function cancel(uint _Id) public payable onlyNftOwner(_Id){
     NFTListing storage listing = listings[_Id];
     require(listing.seller == msg.sender);
     require(listing.forSale == true);
     listing.nft.transferFrom(address(this), msg.sender, _Id);
     listing.forSale = false;
  }



// this function will facilitate the purchasing of a listing
  function buyNFT(uint _Id) public payable whenNotPaused nonReentrant {
        NFTListing storage listing = listings[_Id];
        require(_Id > 0 && _Id <= numOfListing.current(), "item doesn't exist");
        require(msg.value >= listing.price,"not enough balance for this transaction");
        require(listing.forSale != false, "item is not for sell");
        require(listing.seller != msg.sender, "You cannot buy your own nft");

        uint256 comissionAmount = SafeMath.mul(listing.price, commissionPercentage);
        comissionAmount = SafeMath.div(comissionAmount, 10000);
        uint256 sellerAmount = SafeMath.sub(listing.price, comissionAmount);


        if (listing.coin == 0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9){
        require(sellerAmount <= address(this).balance, "Insufficient funds.");
        payable(address(this)).transfer(comissionAmount);
        payable(listing.seller).transfer(sellerAmount);
        } else {
          ERC20 = IERC20(listing.coin);
          require(sellerAmount <= ERC20.balanceOf(address(this)), "Insufficient funds.");
          ERC20.transfer(address(this), comissionAmount);
          ERC20.transfer(listing.seller, sellerAmount);

        }
        listing.nft.transferFrom(address(this), msg.sender, listing.tokenId);
        listing.seller = msg.sender;
        listing.forSale = false;
        emit NftBuy( msg.sender, listing.tokenId, sellerAmount);
    }

     // Function to transfer or withdraw the funds
    function transferOrWithdraw (bool _isCelo, uint _amount, address _ERC20Address) public whenNotPaused nonReentrant onlyOwner {
        require(_amount != 0, "Amount cannot be zero");
       if (_isCelo) {
            require(_amount <= address(this).balance, "Insufficient funds.");
            payable(msg.sender).transfer(_amount);
        } else {
            require(_ERC20Address != address(0), "ERC20 address cannot be zero address");
            ERC20 = IERC20(_ERC20Address);
            require(_amount <= ERC20.balanceOf(address(this)), "Insufficient funds.");
            ERC20.transfer(msg.sender , _amount);
        }
    }

// this function will get the listings in the market place
    function getNFTListing(uint _Id) public view returns (NFTListing memory) {
        return listings[_Id];
    }

    
    // get list of items
    function getListinglength() public view returns (uint) {
        return numOfListing.current();
    }   


  

}

NFT SMART CONTRACT

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";


contract AliveNatureERC721 is ERC721, ERC721Enumerable, ERC721URIStorage,Ownable ,ERC2981,  Pausable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;


    //Base URI
    string private url;
    // percentage split for commission 
    uint96 public commissionPercentage;
    // address of the commission recipient
    address public commissionRecipient;

    struct ProjectData {
        string name;
        uint256 projectTokenId;
        string methodology;
        string area;
        string region;
        string emissionType;
        string uri;
        address creator;
        uint256 timeStamp;
    }

    struct RetireData {
        uint256 retireTokenId;
        address beneficiary;
        string retirementMessage;
        uint256 timeStamp;
        uint256 amount;
    }

    mapping (uint256 => ProjectData) private _projectData;
    mapping (uint256 => RetireData) private _retireData;




    constructor( string memory _MyToken, string memory _Symbol) ERC721(_MyToken, _Symbol) {
        commissionPercentage = 100;
        commissionRecipient = 0xE3506A38C80D8bA1ef219ADF55E31E18FB88EbF4;
        _setDefaultRoyalty(commissionRecipient, commissionPercentage);
    }

    function _baseURI() internal view override returns (string memory) {
        return url;
    }

    function pause() external onlyOwner  {
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }


    function safeMint(address _to, string memory _uri, string memory _name,
    string memory _methodology, string memory _area, string memory _region,  string memory _emissionType, uint256 _tokenId) public whenNotPaused onlyOwner {
        uint256 tokenId = _tokenId;
        _safeMint(_to, tokenId);
        _setTokenURI(tokenId, _uri);
        
        // Create a new ProjectData struct and store it in the contract's storage
        _projectData[tokenId] = ProjectData({
        projectTokenId : tokenId,
        uri : _uri,
        name : _name,
        methodology : _methodology,
        area : _area,
        region : _region,
        emissionType : _emissionType,
        creator : msg.sender,
        timeStamp : block.timestamp
        });
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
        internal
        whenNotPaused
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
            if (from != address(0)) {
            address owner = ownerOf(tokenId);
            require(owner == msg.sender, "Only the owner of NFT can transfer or burn it");
        }
    }

    function _burn(uint256 tokenId) internal whenNotPaused override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function burnToken(uint256 tokenId, string memory _retirementMessage, uint256 _amount) public whenNotPaused {
        address owner = ownerOf(tokenId);
        require(owner == msg.sender, "Only the owner of NFT can burn it");
        _burn(tokenId);

        // Create a new ProjectData struct and store it in the contract's storage
        _retireData[tokenId] = RetireData({
        retireTokenId : tokenId,
        beneficiary : msg.sender,
        retirementMessage : _retirementMessage,
        timeStamp : block.timestamp,
        amount : _amount
        });
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    // The following functions are overrides required by Solidity.

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC2981,  ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }



    function ownerOf(uint256 tokenId) public view virtual override(ERC721, IERC721) returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    function getProjectData(uint256 tokenId) public view returns (ProjectData memory) {
        return _projectData[tokenId];
    }

    function getRetireData(uint256 tokenId) public view returns (RetireData memory) {
        return _retireData[tokenId];
    }

    function getCommissionPercentage() public view returns (uint96) {
        return commissionPercentage;
    }

    function getCommissionRecipient() public view returns (address) {
        return commissionRecipient;
    }

}




I tried to follow many examples that call that I need to first executa setApprovalforAll but even that still don't work


Solution

  • require(owner == msg.sender, "Only the owner of NFT can transfer or burn it")

    The error tells it all, the owner is not the msg.sender.

    When calling ERCNFT.transferFrom, the msg.sender will be the address of the Marketplace contract. Your logic inside _beforeTokenTransfer restricts every address except the token owner to do the transfer, even if that address got approved.