Can someone explain to me how I can set approval for ERC721 contract ?
1- At first, the token once minted belongs to the marketplace
2- with buy, I need to transfer ownership of the token to the caller
I keep getting these errors
X Fail with error 'ERC721: approve caller is not token owner nor approved for all'
x Fail with error 'ERC721: approve to caller'
This is my smart contract : the mint buy function
//The first time a token is created, it is listed here
function createToken(string memory tokenURI, string memory name, uint256 price) public payable returns (uint) {
uint256 currentTokenId = _tokenIds.current();
//Mint the NFT with tokenId newTokenId to the address who called createToken
_safeMint(msg.sender, currentTokenId);
//Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)
_setTokenURI(currentTokenId, tokenURI);
//Helper function to update Global variables and emit an event
_create(currentTokenId, tokenURI, name, listPrice);
setApprovalForAll(address(this), true);
//Increment the tokenId counter, which is keeping track of the number of minted NFTs
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
return newTokenId;
}
function _create(uint256 tokenId, string memory tokenURI, string memory name, uint256 price) private {
//Make sure the sender sent enough ETH to pay for listing
require(msg.value == listPrice, "Hopefully sending the correct price");
//Just sanity check
require(price > 0, "Make sure the price isn't negative");
//Update the mapping of tokenId's to Token details, useful for retrieval functions
idToToken[tokenId] = Token(
tokenId,
tokenURI,
name,
payable(address(this)),
price,
true
);
_transfer(msg.sender, address(this), tokenId);
//Emit the event for successful transfer. The frontend parses this message and updates the end user
emit TokenListedSuccess(
tokenId,
address(this),
price,
true
);
}
function buyNFT(uint256 tokenId) public payable {
require(msg.value > 0, "You need to send some ether");
require(msg.value == idToToken[tokenId].price, "Please submit the asking price in order to complete the purchase");
approve(msg.sender, tokenId);
setApprovalForAll(msg.sender, true);
transferFrom(address(this), msg.sender,tokenId);
// safeTransferFrom(address(this), msg.sender, tokenId);
payable(idToToken[tokenId].owner).transfer(msg.value);
//update the details of the token
idToToken[tokenId].owner = payable(msg.sender);
idToToken[tokenId].isForSale = false;
_itemsSold.increment();
//Transfer the proceeds from the sale to the seller of the NFT
payable(address(this)).transfer(msg.value);
// emit Event
}
this is my client app :
const buyToken = (...args) => {
const [tokenId] = args
return new Promise(async resolve => {
try {
let transaction = await contract.buyNFT(tokenId, {
gasLimit: 5500000,
value: price.toString()
});
await transaction.wait();
resolve()
} catch (e) {
console.error(e)
}
})
}
I think the issue is with
transferFrom(address(this), msg.sender,tokenId);
You are transferring from the contract address to the msg.sender but it should be from the nft owner or token id owner.
The approve() function grants a third party the ability to transfer a
single token id. setApprovalForAll() will grant a third party the
ability to transfer all of the tokens you own for a given collection.
When we mint the token we call setApprovalForAll() to authorize the marketplace to transfer our token. This is the trasferFrom
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
Before transferring it is checking if the msg.sender is already authorized for this transaction or from address is the "owner" of token. To solve the issue get the owner of the tokenId. Since you are passing the "tokenIdto thebuyNFT`
address owner=ERC721.ownerOf(tokenId);
transferFrom(owner, msg.sender,tokenId);
Related
I am completely new to this blockchain world so I want to try learn it. While I am following a tutorial to create a simple ERC-1155 contract to mint a token and set its price. When I am trying to run the buyToken function from another address, it keeps saying "Not Enough Fund". When I debug the msg.value value is 0, even though I have 100 ethers balance on each of my address. When I change the amount of token to 0, it works successfully. What should I need to do to be able to do the transaction?
Log
This is my contract that I have modified on:
// Contract based on https://docs.openzeppelin.com/contracts/4.x/erc1155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC1155/ERC1155.sol";
/**
* #title NFTMinter
* #dev NFT Contract Minter
* #custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract NFTMinter is ERC1155 {
constructor() ERC1155("https://raw.githubusercontent.com/noopmood/TutorialNFTInGo/main/metadata/{id}.json") payable {}
// Define the mapping of addresses to balances
mapping(address => mapping(uint256 => uint256)) public _balances;
// Define the mapping of address to tokenIds owned
mapping(address => uint256[]) public _tokenIds;
// Define the mapping of tokenId to price
mapping(uint256 => uint256) public tokenPrice;
// Define the sender to address payable type
address payable public sender;
struct Token {
uint256 tokenId;
uint256 balance;
}
function mintCaller(uint256 tokenId, uint256 amount) public {
_mint(msg.sender, tokenId, amount, "");
}
// Mints new tokens and sets the price for each token.
function mintAddress(uint256 tokenId, uint256 amount, address addr, uint256 price) public{
_mint(addr, tokenId, amount, "");
// Update the balance of the recipient
_balances[addr][tokenId] += amount;
// Add the tokenId to the address
_tokenIds[addr].push(tokenId);
// Set the price of the token
tokenPrice[tokenId] = price;
}
// Get all tokenIds from its owner address
function getTokenIdsByAddress(address addr) public view returns (uint[] memory) {
return _tokenIds[addr];
}
// Get the balance / amount of the tokenId
function getTokenByIdAndAddress(address addr, uint256 tokenId) public view returns (Token memory) {
Token memory result;
result.tokenId = tokenId;
result.balance = _balances[addr][tokenId];
return result;
}
// Get the tokenIds along with its corresponding balances/amount
function getAllTokenByAddress(address holder) public view returns (Token[] memory) {
Token[] memory result = new Token[](_tokenIds[holder].length);
for (uint i = 0; i < _tokenIds[holder].length; i++) {
result[i].tokenId = _tokenIds[holder][i];
result[i].balance = _balances[holder][_tokenIds[holder][i]];
}
return result;
}
// Transfers the tokens from one address to another.
function transfer(address addr, uint256 tokenId, uint256 amount) public {
require(_balances[msg.sender][tokenId] >= amount, "Not enough balance");
// Transfer the token
_safeTransferFrom(msg.sender, addr, tokenId, amount, "");
// Update the sender's balance
_balances[msg.sender][tokenId] -= amount;
// Update the recipient's balance
_balances[addr][tokenId] += amount;
}
// Allows a buyer to purchase a token by sending the required amount to the contract and updating the balance of the buyer.
function buyToken(uint256 tokenId, uint256 amount) public payable {
require(msg.value >= amount * tokenPrice[tokenId], "Not enough funds");
// Deduct the amount from the buyer
sender = payable(msg.sender);
sender.transfer(amount * tokenPrice[tokenId]);
// Transfer the token to the buyer
_safeTransferFrom(address(0), msg.sender, tokenId, amount, "");
// Update the buyer's balance
_balances[msg.sender][tokenId] += amount;
}
}
On what the step to reproduce this:
Deploy the contract with value, for me I pass 20 Ether
Run the mintAddress function by passing, tokenId: 1, amount: 10, price: 10, address: the address an account
Run the transfer function by passing address: the address of account you want to transfer the token into, tokenId: 1, amount: 10
Change the account in upper part to the address you use in transfer function
Run buyToken function, tokenId: 1, amount: 10
It will said Not Enough Fund
buyToken
What I have tried so far:
I read in another post, that I should deploy the contract with the value in it. But it still doesn't work
I have checked that the balance of my account in remix have 100 ethers, but when I try to doing the buy token with it, it still said 'Not Enough Fund'
What I am expecting:
To be able to buy the token with the price that has been set when minting the token.
Your error is failing from here:
// require is like if statement. if condition does not satisfied, it shows the passed message
require(msg.value >= amount * tokenPrice[tokenId], "Not enough funds")
When a user sends funds alongside the transaction, not as an function argument, solidity will catch it with msg.value. Looks like you are not sending any value alongside the transaction or not sending enough funds. in Remix IDE
you need to pass the necessary value in the above red borderd box
I have deployed today a smart contract it's working fine even I have received the total supply in my wallet but when I open contract at bscscan.com it's showing nothing about Name, Symbol, TotalSupply etc here is the contract link and code
https://testnet.bscscan.com/address/0x4333f52f6c84a507cdbb6d8f511c11653be6ecac
// This is the contract for the ABC token
contract ABCToken {
// The name of the token
string public name;
// The symbol of the token
string public symbol;
// The number of decimals of the token
uint8 public decimals;
// The total supply of the token
uint256 public totalSupply;
// The balance of the token for each account
mapping(address => uint256) public balanceOf;
// The contract owner
address public owner;
// The constructor function is called when the contract is deployed
constructor() public {
// Set the name of the token
name = "ABC";
// Set the symbol of the token
symbol = "XYZ";
// Set the number of decimals of the token
decimals = 2;
// Set the total supply of the token
totalSupply = 100000;
// Set the contract owner to the address that deployed the contract
owner = msg.sender;
// Set the balance of the contract owner to the total supply
balanceOf[owner] = totalSupply;
}
// This function allows the contract owner to issue more tokens
function issueTokens(uint256 _amount) public {
// Only the contract owner can issue more tokens
require(msg.sender == owner, "Only the owner can issue more tokens");
// Increase the total supply and the balance of the contract owner
totalSupply += _amount;
balanceOf[owner] += _amount;
}
// This function allows the contract owner to transfer tokens to another account
function transfer(address _to, uint256 _amount) public {
// Only the contract owner can transfer tokens
require(msg.sender == owner, "Only the owner can transfer tokens");
// Check that the contract owner has enough tokens to transfer
require(balanceOf[owner] >= _amount, "Insufficient balance");
// Transfer the tokens and update the balances
balanceOf[owner] -= _amount;
balanceOf[_to] += _amount;
}
}
i have try to change type of strings but it's not works for me
It does show. you might be looking at wrong places:
I am building a NFT Marketplace
I use 2 smart contracts to :
first, mint the token
then, call setApprovalForAll() to authorize the marketplace contract to transfer the token
strangely, the owner is address(0) when it should be the msg.sender when I created the token last
" The transaction has been reverted to the initial state.
Reason provided by the contract: "ERC721: invalid token ID". "
This the source code of the 2 smart contracts below :
smart contract : NFT
smart contract : NFTMarketplace
Thanks for the help
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./#openzeppelin/contracts/utils/Counters.sol";
import "./#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./#openzeppelin/contracts/security/ReentrancyGuard.sol";
contract NFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
address contractAddress;
constructor(address marketplaceAddress) ERC721("NFTMarketplace", "NFTM") {
contractAddress = marketplaceAddress;
}
event TokenMinted (
uint256 indexed tokenId,
string tokenURI
);
function createToken(string memory tokenURI) public returns (uint) {
uint256 currentTokenId = _tokenIds.current();
_safeMint(msg.sender, currentTokenId);
_setTokenURI(currentTokenId, tokenURI);
setApprovalForAll(contractAddress, true);
_tokenIds.increment();
emit TokenMinted(currentTokenId, tokenURI);
return currentTokenId;
}
function getCurrentToken() public view returns (uint256) {
return _tokenIds.current();
}
}
contract NFTMarketplace is ReentrancyGuard, ERC721 {
using Counters for Counters.Counter;
//_tokenIds variable has the most recent minted tokenId
//Keeps track of the number of items sold on the marketplace
Counters.Counter private _itemsSold;
//owner is the contract address that created the smart contract
address payable owner;
//The fee charged by the marketplace to be allowed to list an NFT
uint256 listPrice = 0.01 ether;
constructor() ERC721("NFTMarketplace", "NFTM") {}
//The structure to store info about a listed token
struct Token {
uint256 tokenId;
string tokenURI;
address nftContract;
string name;
address payable owner;
uint256 price;
bool isListed;
}
//the event emitted when a token is successfully listed
event TokenListedSuccess (
uint256 indexed tokenId,
address nftContract,
address owner,
uint256 price,
bool isListed
);
//This mapping maps tokenId to token info and is helpful when retrieving details about a tokenId
mapping(uint256 => Token) private idToToken;
function updateListPrice(uint256 _listPrice) public payable {
require(owner == msg.sender, "Only owner can update listing price");
listPrice = _listPrice;
}
//The first time a token is created, it is listed here
// make it payable with money -- add require
function listToken(address nftContract, uint256 currentTokenId, string memory tokenURI, string memory name, uint256 price) public payable {
require(msg.value > 0, "Price must be at least 1 wei");
require(msg.value == listPrice, "Price must be equal to listing price");
idToToken[currentTokenId] = Token(
currentTokenId,
tokenURI,
nftContract,
name,
payable(address(this)),
listPrice,
true
);
emit TokenListedSuccess(
currentTokenId,
nftContract,
msg.sender,
listPrice,
true
);
}
function buyNFT(address nftContract, uint256 itemId) public payable nonReentrant {
uint price = idToToken[itemId].price;
uint tokenId = idToToken[itemId].tokenId;
address seller = ERC721.ownerOf(tokenId);
address buyer = msg.sender;
require(msg.value > 0, "You need to send some ether");
require(buyer != seller,"You already own this nft");
require(msg.value == price, "Please submit the asking price in order to complete the purchase");
idToToken[itemId].isListed = false;
idToToken[itemId].owner = payable(buyer);
payable(seller).transfer(price);
_itemsSold.increment();
IERC721(nftContract).transferFrom(seller, buyer, tokenId);
}
/* ...rest of smart contract */
}
I think you forget to assign "owner" value.
Typically, we set it in constructor.
like
constructor() {
i_owner = msg.sender;
}
I have the following smart contract:
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./MyCoinSupply.sol";
contract MyCoinDEX
{
IERC20 public token;
event Bought(uint256 amount);
event Sold(uint256 amount);
constructor() public
{
token = new MyCoinSupply();
}
function getSenderAddress() public view returns (address) // for debugging purposes
{
return (msg.sender);
}
function getAddress() public view returns (address)
{
return address(this);
}
function getTokenAddress() public view returns (address)
{
return address(token);
}
function buy() payable public // send ether and get tokens in exchange; 1 token == 1 ether
{
uint256 amountTobuy = msg.value;
uint256 dexBalance = token.balanceOf(address(this));
require(amountTobuy > 0, "You need to send some ether");
require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
token.transfer(msg.sender, amountTobuy);
emit Bought(amountTobuy);
}
function sell(uint256 amount) public // send tokens to get ether back
{
require(amount > 0, "You need to sell at least some tokens");
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
// https://stackoverflow.com/questions/67341914/error-send-and-transfer-are-only-available-for-objects-of-type-address-payable
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
}
if I call the buy() method from truffle console, it executes without any exceptions:
truffle(development)> MyCoinDEX.buy({value: 1})
I verified that the account calling the buy() method receives the token. However, the balance of Ether in Ganache for the account calling the buy() method doesn't decrease. So essentially, the account is getting tokens for free.
What's going on here? How do I fix it?
I am not sure but it would be necessary to investigate about the balance of the contract account. The gas for token.transfer might be paid by the contract account balance, since the contract account is the transaction sender for token contract.
Or the balance decrement would be unnoticeable because it is too small.
I would be happy to know about the answer if you have found out.
I'm writing an NFT smart contract using the OpenZeppelin ERC721Full contract. I'm able to mint NFTs, but I want to have a button that enables them to be bought. I'm trying writing this 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);
}
This does not work because function approve must be called by the owner or an already approved address. I have no clue on how a buy function should be built. I know that I must use some requirements but first I want the function to work on tests and then I'll write the requirements.
How should a buy function be coded? Because the only solution I have found is to overwrite the approve function and omit the require of who can call this function. But it looks like it isn't the way it should be done.
Thank you!
You can use just the _transfer() function, see my buy() function for an example of implementation.
The approvals for sale can be done using a custom mapping - in my example tokenIdToPrice. If the value is non-zero, the token ID (mapping key) is for sale.
This is a basic code that allows selling an NTF. Feel free to expand on my code to allow "give away for free", "whitelist buyers" or any other feature.
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);
}
}
How to simulate the sale:
The contract deployer (msg.sender) gets token ID 1.
Execute allowBuy(1, 2) that will allow anyone to buy token ID 1 for 2 wei.
From a second address, execute buy(1) sending along 2 wei, to buy the token ID 1.
Call (the parent ERC721) function ownerOf(1) to validate that the owner is now the second address.
If you let anyone call the approve function, it would allow anyone to approve themselves to take NFTs! The purpose of approve is to give the owner of an asset the ability to give someone else permission to transfer that asset as if it was theirs.
The basic premise of any sale is that you want to make sure that you get paid, and that the buyer receives the goods in return for the sale. Petr Hedja's solution takes care of this by having the buy function not only transfer the NFT, but also include the logic for sending the price of the token. I'd like to recommend a similar structure with a few changes. One is so that the function will also work with ERC20 tokens, the other is to prevent an edge case where if gas runs out during execution, the buyer could end up with their NFT for free. This is building on his answer, though, and freely uses some of the code in that answer for architecture.
Ether can still be set as the accepted currency by inputting the zero address (address(0)) as the contract address of the token.
If the sale is in an ERC20 token, the buyer will need to approve the NFT contract to spend the amount of the sale since the contract will be pulling the funds from the buyer's account directly.
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);
}
this is Nft struct
struct NftItem{
uint tokenId;
uint price;
// creator and owner are not same. creator someone who minted. creator does not change
address creator;
bool isListed;
}