简体   繁体   中英

How to transfer an ERC721 token

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);

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM