簡體   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