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.