Как перенести NFT с одной учетной записи на другую с помощью ERC721?
Я пишу смарт-контракт и NFT, используя контракт OpenZeppelin ERC721Full. Я могу чеканить NFT, но мне нужна кнопка, позволяющая их покупать. Я пытаюсь написать эту функцию:
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);
}
Это не работает, потому что функция утверждения должна вызываться владельцем или уже утвержденным адресом. Я понятия не имею, как должна быть построена функция покупки. Я знаю, что должен использовать некоторые требования, но сначала я хочу, чтобы функция работала над тестами, а затем я напишу требования.
Как следует кодировать функцию покупки?Потому что единственное решение, которое я нашел, - это перезаписать функцию утверждения и опустить требование о том, кто может вызывать эту функцию. Но похоже, что это не так.
Спасибо!
3 ответа
Вы можете использовать только функцию _transfer() , см. Мой
buy()
функция для примера реализации.
Утверждения для продажи могут быть выполнены с использованием настраиваемого сопоставления - в моем примере
tokenIdToPrice
. Если значение не равно нулю, идентификатор токена (ключ сопоставления) продается.
Это базовый код, позволяющий продавать 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);
}
}
Как смоделировать продажу:
- Развертыватель контракта (
msg.sender
) получает идентификатор токена 1. - Выполнять
allowBuy(1, 2)
что позволит любому купить токен ID 1 за 2 wei. - Со второго адреса выполнить
buy(1)
отправка 2 wei, чтобы купить токен ID 1. - Вызов (родительской ERC721) функции
ownerOf(1)
чтобы подтвердить, что владелец теперь является вторым адресом.
Если вы позволите кому-либо вызвать эту функцию, это позволит каждому одобрить использование NFT! Цель
Основная предпосылка любой продажи заключается в том, что вы хотите быть уверены, что вам заплатят, и что покупатель получит товары в обмен на продажу. Решение Petr Hedja позаботится об этом, имея
Эфир по-прежнему можно установить в качестве принятой валюты, введя нулевой адрес (
Если продажа осуществляется с помощью токена 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;
}