0

我已经实现了一个基本合约,可以铸造代币、将代币转移到其他人的账户并购买 NFT。这是在 Remix 上测试的完全实现的代码。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

import '@openzeppelin/contracts/utils/Counters.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';

contract ArtWorkMinter is ERC721URIStorage{

  mapping(uint256 => address) idToOwner;
  mapping(address => uint256[]) userTokenList;
  mapping(uint256 => uint256) tokenPriceList;

  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;
  constructor() ERC721("MyNFT", "MNFT") {}

    function mint_art_work(address receiver, string memory tokenURI, uint price) external {
    _tokenIds.increment();

    uint256 newNftTokenId = _tokenIds.current();
    _mint(receiver, newNftTokenId);
    _setTokenURI(newNftTokenId, tokenURI);

    userTokenList[receiver].push(newNftTokenId);
    idToOwner[newNftTokenId] = receiver;
    tokenPriceList[newNftTokenId] = price;
  }

  function my_total_tokens(address receiver) external view returns (uint256){
    return userTokenList[receiver].length;
  }

  function get_my_nft_tokens(address receiver) external view returns (uint256[] memory){
    return userTokenList[receiver];
  }

  function get_token_details(uint token_id) external view returns (string memory){
    return tokenURI(token_id);
  }

  function _transfer_token(address from_address, address to_address, uint token_id) internal{
    // Update contract variables --
    idToOwner[token_id] = to_address;
    // Move the last element into the place of token id, delete the last item in array --
    for(uint i = 0; i<userTokenList[from_address].length-1; i++){
      if(userTokenList[from_address][i] == token_id){
        userTokenList[from_address][i] = userTokenList[from_address][userTokenList[from_address].length - 1];
        userTokenList[from_address].pop();
        break;
      }
    }
    // Add nft token to to address mapping array --
    userTokenList[to_address].push(token_id);
  }

  function transfer_token(address from_address, address to_address, uint token_id) external{
    // Check if the caller is the owner of the token id --
    require(ownerOf(token_id) == msg.sender, "NOT OWNER");
    // approve the target address on which we are going to transfer the token
    approve(to_address, token_id);
    // transfer the token --
    safeTransferFrom(from_address, to_address, token_id);

    _transfer_token(from_address, to_address, token_id);
  }

  function buy_token(uint token_id) external payable{
    require(msg.value >= tokenPriceList[token_id]);
    // approve the target address on which we are going to transfer the token
    approve(msg.sender, token_id);
    // transfer the token --
    address token_owner = ownerOf(token_id);
    safeTransferFrom(token_owner, msg.sender, token_id);
    _transfer_token(token_owner, msg.sender, token_id);
    // send the ETH to the seller
    payable(token_owner).transfer(msg.value);
  }
}

合约可以成功铸造代币和转移代币。但是,每当我执行功能 buy_token 时,都会出现以下错误:

transact to ArtWorkMinter.buy_token errored: VM error: revert.

revert
    The transaction has been reverted to the initial state.
Reason provided by the contract: "ERC721: transfer caller is not owner nor approved".
Debug the transaction to get more information. 

我想我理解错误,问题出在哪里,调用方用户(消费方)必须在 NFT 令牌从所有者账户转移到消费方账户之前获得批准。因为代币是由所有者创建的,并且所有者不会购买自己的代币,所以其他用户会这样做。但其他用户(Spender)不能调用transferFrom批准,因为他不是令牌的所有者。 那么,我们如何解决这个问题呢?解决这个问题的标准方法是什么?

经过一番研究,我知道应该有一个额外的合同,称为“运营商合同”,它将代表所有者批准和转移代币。

如何实现该 Operator 合约,以及该合约如何与主 NFT 合约结合?

任何建议或示例将不胜感激。

4

0 回答 0