我已经实现了一个基本合约,可以铸造代币、将代币转移到其他人的账户并购买 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 合约结合?
任何建议或示例将不胜感激。