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
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.