繁体   English   中英

如何使用 ERC721 将 NFT 从一个账户转移到另一个账户?

[英]How to transfer a NFT from one account to another using ERC721?

我正在使用 OpenZeppelin ERC721Full 合约编写 NFT 智能合约。 我可以铸造 NFT,但我想要一个按钮来购买它们。 我正在尝试写这个 function:

function buyNFT(uint _id) public payable{
    //Get NFT owner address
    address payable _seller = ownerOf(_id);

    // aprove nft sell
    approve(_seller, _id);
    setApprovalForAll(msg.sender, true);

    //transfer NFT
    transferFrom(_seller, msg.sender, _id);

    // transfer price in ETH
    address(_seller).transfer(msg.value);

    emit NftBought(_seller, msg.sender, msg.value);

  }

这不起作用,因为 function 批准必须由所有者或已批准的地址调用。 我不知道应该如何构建 buy function。 我知道我必须使用一些要求,但首先我希望 function 能够进行测试,然后我将编写要求。

购买function应该如何编码? 因为我找到的唯一解决方案是覆盖 approve function 并省略 who can call this function 的要求。但看起来这不是应该完成的方式。

谢谢!

您可以只使用_transfer() function,请参阅我的buy() function 以获取实现示例。

可以使用自定义映射来完成销售批准 - 在我的示例中tokenIdToPrice 如果该值非零,则令牌 ID(映射键)正在出售。

这是允许销售 NTF 的基本代码。 随意扩展我的代码以允许“免费赠送”、“白名单买家”或任何其他功能。

pragma solidity ^0.8.4;

import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol';

contract MyToken is ERC721 {
    event NftBought(address _seller, address _buyer, uint256 _price);

    mapping (uint256 => uint256) public tokenIdToPrice;

    constructor() ERC721('MyToken', 'MyT') {
        _mint(msg.sender, 1);
    }

    function allowBuy(uint256 _tokenId, uint256 _price) external {
        require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
        require(_price > 0, 'Price zero');
        tokenIdToPrice[_tokenId] = _price;
    }

    function disallowBuy(uint256 _tokenId) external {
        require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
        tokenIdToPrice[_tokenId] = 0;
    }
    
    function buy(uint256 _tokenId) external payable {
        uint256 price = tokenIdToPrice[_tokenId];
        require(price > 0, 'This token is not for sale');
        require(msg.value == price, 'Incorrect value');
        
        address seller = ownerOf(_tokenId);
        _transfer(seller, msg.sender, _tokenId);
        tokenIdToPrice[_tokenId] = 0; // not for sale anymore
        payable(seller).transfer(msg.value); // send the ETH to the seller

        emit NftBought(seller, msg.sender, msg.value);
    }
}

如何模拟销售:

  1. 合约部署者 ( msg.sender ) 获得令牌 ID 1。
  2. 执行allowBuy(1, 2)将允许任何人以 2 wei 的价格购买代币 ID 1。
  3. 从第二个地址执行buy(1)发送 2 wei,购买 ID 为 1 的代币。
  4. 调用(父 ERC721)function ownerOf(1)以验证所有者现在是第二个地址。

如果您让任何人调用approve function,它将允许任何人批准自己接受 NFT! approve的目的是让资产的所有者能够允许其他人转移该资产,就好像它是他们的一样。

任何销售的基本前提是您要确保您获得付款,并且买方收到货物以换取销售。 Petr Hedja 的解决方案通过让buy function 不仅转移 NFT,还包括发送代币价格的逻辑来解决这个问题。 我想推荐一个类似的结构,但有一些变化。 一个是为了让 function 也可以与 ERC20 代币一起使用,另一个是为了防止在执行过程中如果 gas 用完,买家最终可以免费获得 NFT。 不过,这是建立在他的答案之上的,并且可以自由地使用该答案中的一些代码用于架构。

通过输入零地址( address(0) )作为代币的合约地址,以太币仍然可以设置为接受的货币。

如果以 ERC20 代币进行销售,则买方将需要批准 NFT 合约才能花费销售金额,因为该合约将直接从买方账户中提取资金。

pragma solidity ^0.8.4;

import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';

contract MyToken is ERC721 {
    event NftBought(address _seller, address _buyer, uint256 _price);

    mapping (uint256 => uint256) public tokenIdToPrice;
    mapping (uint256 => address) public tokenIdToTokenAddress;

    constructor() ERC721('MyToken', 'MyT') {
        _mint(msg.sender, 1);
    }

    function setPrice(uint256 _tokenId, uint256 _price, address _tokenAddress) external {
        require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
        tokenIdToPrice[_tokenId] = _price;
        tokenIdToTokenAddress[_tokenId] = _tokenAddress;
    }

    function allowBuy(uint256 _tokenId, uint256 _price) external {
        require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
        require(_price > 0, 'Price zero');
        tokenIdToPrice[_tokenId] = _price;
    }

    function disallowBuy(uint256 _tokenId) external {
        require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
        tokenIdToPrice[_tokenId] = 0;
    }
    
    function buy(uint256 _tokenId) external payable {
        uint256 price = tokenIdToPrice[_tokenId];
        require(price > 0, 'This token is not for sale');
        require(msg.value == price, 'Incorrect value');
        address seller = ownerOf(_tokenId);
        address tokenAddress = tokenIdToTokenAddress[_tokenId];
        if(address != address(0){
            IERC20 tokenContract = IERC20(tokenAddress);
            require(tokenContract.transferFrom(msg.sender, address(this), price),
                "buy: payment failed");
        } else {
            payable(seller).transfer(msg.value);
        }
        _transfer(seller, msg.sender, _tokenId);
        tokenIdToPrice[_tokenId] = 0;
        

        emit NftBought(seller, msg.sender, msg.value);
    }
}
// mapping is for fast lookup. the longer operation, the more gas
mapping(uint => NftItem) private _idToNftItem;

function buyNft(uint tokenId) public payable{
    uint price=_idToNftItem[tokenId].price;
    // this is set in erc721 contract
    // Since contracts are inheriting, I want to make sure I use this method in ERC721
    address owner=ERC721.ownerOf(tokenId);
    require(msg.sender!=owner,"You already own this nft");
    require(msg.value==price,"Please submit the asking price");
    // since this is purchased, it is not for sale anymore 
    _idToNftItem[tokenId].isListed=false;
    _listedItems.decrement();
    // this is defined in ERC721
    // this already sets owner _owners[tokenId] = msg.sender;
    _transfer(owner,msg.sender,tokenId);
    payable(owner).transfer(msg.value);
  }

这是 Nft 结构

struct NftItem{
    uint tokenId;
    uint price;
    // creator and owner are not same. creator someone who minted. creator does not change
    address creator;
    bool isListed;
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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