Reputation: 4023
I'm trying to transfer an ERC721 token, but I'm getting the error ERC721: transfer caller is not owner nor approved
for the transferToken
method.
Main.sol
import "./ERC721.sol";
import "./Counters.sol";
contract Main is ERC721 {
using Counters for Counters.Counter;
Counters.Counter internal _tokenIds;
address payable internal admin;
constructor() ERC721("MyToken", "TOKEN") {
admin = payable(msg.sender);
}
}
Auction.sol
import "./Main.sol";
contract Auction is Main {
struct AuctionInfo {
uint256 tokenId;
address highestBidder;
uint highestBid;
}
mapping(string => AuctionInfo) private _auctionInfo;
function createAuction(string memory id) public {
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
_mint(msg.sender, newTokenId);
_auctionInfo[id].tokenId = newTokenId;
}
function transferToken(string memory id) public {
require(msg.sender == _auctionInfo[id].highestBidder, "You are not the highest bidder");
safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
}
// other methods...
}
The minting contract is this
and the owner of the token is the msg.sender
of the minting method if I'm not mistaken. Am I to use the approve
(or setApprovalForAll
) for this
each time before transferring? I've tried this
, payable(this)
, and address(this)
for the safeTransferFrom
method, but none seem to be working.
For example, I tried the following, but get the same revert message:
approve(address(this), _auctionInfo[id].tokenId);
this.safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
Upvotes: 0
Views: 5979
Reputation: 43481
Because of the internal
visibility of the ERC721
._approve()
function, you can effectively perform the approval for the user.
Then you'll be able to execute the safeTransferFrom(tokenOwner, receiver, tokenId)
from your contract, because your contract address is approved to operate this specific token even though it belongs to the tokenOwner
.
This snippet mints the token, assigning the ownership to the msg.sender
. But then it also calls the _approve()
function that doesn't contain any validations and simply assigns the approval of the token to the Auction
address.
pragma solidity ^0.8;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
contract Auction is ERC721 {
constructor() ERC721("CollectionName", "Symbol") {}
function createAuction() public {
uint256 newTokenId = 1;
_mint(msg.sender, newTokenId);
_approve(address(this), newTokenId);
}
}
You can see from the screenshot that the owner is 0x5B...
(the user address) and that the token is approved for 0xd9...
(the contract address).
Note: The _approve()
function is internal
- it can be called from the ERC721
contract and contracts deriving from it (in your case Main
and Auction
), but it can't be called from external contracts or end user addresses.
Upvotes: 0
Reputation: 664
The main principle behind any Blockchain is that nobody on the blockchain network should be trusted, and still the transactions should happen fool proof, with no possibility of any cheating being done (barring of course of some hacking).
If you invoke the approve method from the Auction
contract, then the msg.sender
for the approve function in the ERC721 token contract is your auction contract address. So, in other words, your Auction Contract is trying to approve itself to sell someone else's NFTs, which is not very trustworthy.
What should really happen is that owner of the NFT should invoke the approve
method of the ERC721 contract - i.e. the transaction that you send for the approve
function call, should be signed by the NFT owner wallet address. This way, the msg.sender
for the approve
function in the ERC721 contract will be the owner of the NFT. As per the ERC721 standards, the owner of the NFT can approve anyone they want, to sell their NFT(s), as the no-trust in the network is still maintained (At least I should be able to trust myself). The approve
method should be invoked from within your DAPP, before the transferToken
function is invoked from the DAPP.
Hope that explains why you are unable to transfer your ERC721 tokens.
Upvotes: 1