I am creating NFT minter and NFT marketplace contracts in minter. I am passing NFT market contract address to save as default NFT marketplace.
contract ERC721NFTMinter is Ownable, ERC721URIStorage{
//#dev: NFT Marketplace address
address marketPlaceAddress;
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor(address _marketplaceAddress) ERC721("Metaverse NFTs", "META-NFT"){
// nextTokenId is initialized to 1, since starting at 0 leads to higher gas cost for the first minter
//TOKENS IDS starts from 1(not 0)
_tokenIds.increment();
marketPlaceAddress = _marketplaceAddress;
}
//#dev: Main Minter function that mints ERC721 nfts
// #requere: _tokenURI; A URI string that contrains the token metadata.
// #returns: Newly minted token id
function mintNFT(string memory _tokenURI) public returns(uint256){
require(msg.sender != address(0), "ERC721NFTMinter#mintNFT: ZERO_ADDRESS");
// To automatically increment the token id so that if the biginign token
uint256 newItemId = _tokenIds.current();
_tokenIds.increment();
// mint function to mint the tokens for the sender
_safeMint(address(msg.sender), newItemId);
// sent the token URI for the metadata
_setTokenURI(newItemId, _tokenURI);
// Set permission for tokens
setApprovalForAll( marketPlaceAddress, true);
return newItemId;
}
}
This compiled nicely but when I was creating a new token it seems setApprovalForAll function is not working
setApprovalForAll( marketPlaceAddress, true);
I am getting below error with brownie testing:
> assert erc721_nft_minter.getApproved(id) == marketplace_address, 'NOT APPROVED FOR LISTING'
E AssertionError: NOT APPROVED FOR LISTING
E assert '0x0000000000...0000000000000' == '0x3194cBDC3d...5c3394048Cc87'
E - 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
E + 0x0000000000000000000000000000000000000000
I also tried to do something like this below to override that method but giving error for that.
function setApprovalForAll(address _operator, bool _approved) public override{
_setApprovalForAll(msg.sender, _operator, _approved);
}
the error
CompilerError: solc returned the following errors:
DeclarationError: Undeclared identifier. Did you mean "setApprovalForAll"?
--> contracts/NFT/ERC721/NFTMinter_ERC721.sol:57:6:
|
57 | _setApprovalForAll(msg.sender, _operator, _approved);
| ^^^^^^^^^^^^^^^^^^
ERROR: Unable to load project
How can I solve this?
Related
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:
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);
I'm new to smart contract and Solidity programming and I've been trying to deploy an NFT contract on goerli testnet. The contract deploys without any problem but when I try to call the "createCollectible" function, I get the following error:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
{
"originalError": {
"code": 3,
"data": "0xf0019fe60000000000000000000000000000000000000000000000000000000000000c6a000000000000000000000000b54644506388a04187d943dbbbd3edbb3ee53094",
"message": "execution reverted"
}
}
Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
/**
* #title The AdvandedCollectible contract
* #notice A contract that mints an NFT
*/
contract AdvancedCollectible is ERC721, VRFConsumerBaseV2 {
VRFCoordinatorV2Interface immutable COORDINATOR;
LinkTokenInterface immutable LINKTOKEN;
uint256 public tokenCounter;
uint64 immutable s_subscriptionId;
// The gas lane to use, which specifies the maximum gas price to bump to.
// For a list of available gas lanes on each network,
// see https://docs.chain.link/docs/vrf-contracts/#configurations
bytes32 immutable s_keyhash;
uint32 immutable s_numWords = 1;
uint16 immutable s_requestConfirmations = 3;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
// so 100,000 is a safe default for this example contract. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 immutable s_callbackGasLimit = 100000;
uint256 public s_requestId;
uint256 public s_randomWords;
address s_owner;
enum Breed {
PUG,
SHIBA_INU,
ST_BERNARD
}
mapping(uint256 => Breed) public tokenIdToBreed;
mapping(uint256 => address) public requestIdToSender;
event requestCollectible(uint256 indexed requestId, address requester);
event breedAssigned(uint256 tokenId, Breed breed);
constructor(
address vrfCoordinator,
address link,
bytes32 keyhash,
uint64 subscriptionId
) VRFConsumerBaseV2(vrfCoordinator) ERC721("Dogie", "DOG") {
tokenCounter = 0;
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
LINKTOKEN = LinkTokenInterface(link);
s_keyhash = keyhash;
s_owner = msg.sender;
s_subscriptionId = subscriptionId;
}
modifier onlyOwner() {
require(msg.sender == s_owner, "You are not the owner");
_;
}
function createCollectible() public returns (uint256) {
s_requestId = COORDINATOR.requestRandomWords(
s_keyhash,
s_subscriptionId,
s_requestConfirmations,
s_callbackGasLimit,
s_numWords
);
requestIdToSender[s_requestId] = msg.sender;
emit requestCollectible(s_requestId, msg.sender);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)
internal
override
{
s_randomWords = randomWords[0];
Breed breed = Breed(s_randomWords % 3);
uint256 newTokenId = tokenCounter;
tokenIdToBreed[newTokenId] = breed;
emit breedAssigned(newTokenId, breed);
address owner = requestIdToSender[requestId];
_safeMint(owner, newTokenId);
tokenCounter++;
}
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
require(
_isApprovedOrOwner(_msgSender(), tokenId),
"ERC721: Caller is not owner nor approved."
);
setTokenURI(tokenId, _tokenURI);
}
}
Does anybody have an idea what I'm doing wrong here?
So I realized that I'm missing one small yet important step, and that is adding a consumer to my subscription.
Using the Chainlink VRF V2, you need to create a subscription, add funds to that subscription and add the address where your contract is deployed at as a consumer to the subscription. I wasn't doing that last step and that's why my transaction's execution kept reverting.
I am newbie to this field, and just learning some courses online about solidity and creating a simple contract, I would like to ask about a simple project that I try but not successful yet.
I want to create a swap function that can swap between 2 tokens that I created from ERC20, WITHOUT using uniswap or something like that, just dev a new function that swap between token A and token B, for example with the rate of 1 token A = 3 token B, the rate was ask to save in a mapping and got config at first, but I dont really know how to setting up the rate for swapping.
Could anyone help me out with this, many thanks guys.
Existed Code of Swap contract, just temporary :
// SPDX-License-Identifier: MIT
pragma solidity >= 0.7.0 <0.9.0;
import "./Token.sol";
import "./safemath.sol";
contract SwapToken {
using SafeMath for uint;
Token public token1;
address public owner1;
Token public token2;
address public owner2;
uint public amount1;
uint public amount2;
uint public swapRate;
constructor(
address _token1,
address _owner1,
address _token2,
address _owner2,
uint _amount1
) {
token1 = Token(_token1);
owner1 = _owner1;
token2 = Token(_token2);
owner2 = _owner2;
swapRate = 3;
amount1 = _amount1;
amount2 = _amount1.mul(swapRate);
}
function swap() public {
require(msg.sender == owner1 || msg.sender == owner2, "Not authorized from owners");
require(
token1.allowance(owner1, address(this)) >= amount1,
"Token 1 allowance too low"
);
require(
token2.allowance(owner2, address(this)) >= amount2,
"Token 2 allowance too low"
);
_safeTransferFrom(token1, owner1, owner2, amount1);
_safeTransferFrom(token2, owner2, owner1, amount2);
}
function _safeTransferFrom(
Token token,
address sender,
address recipient,
uint amount
) private {
bool sent = token.transferFrom(sender, recipient, amount);
require(sent, "Token transfer failed");
}
}
Let's start with a general answer and we'll build on it as we understand the question better.
I think you are not trying to create a swapping solution with a liquidity pool, but instead, want to match 2 people(?) that want to swap tokens.
A very simplistic approach to it could be
// have a mapping, as your tutor said. This will store information about how a token (token's contract address) and another token (again, contract address) rate against each other.
mapping(address => mapping (address => uint256)) rates;
function setRating(address memory token1, address memory token2, uint256 rate) public {
rates[token1][token2] = rate;
}
Then, while swapping, check the rate and calculate accordingly.
// you need to pass which token to swap and how much swap
function swap(address tokenIn, uint amountIn) public {
// you want only either of two owners call
require(
msg.sender == owner1 || msg.sender == owner2,
"Not authorized from owners"
);
require(
token1.allowance(owner1, address(this)) >= amount1,
"Token 1 allowance too low"
);
require(
token2.allowance(owner2, address(this)) >= amount2,
"Token 2 allowance too low"
);
// you need to require tokenIn is only token1 or token2 that you store in storage
require(
tokenIn == address(token1) || tokenIn == address(token2),
"Invalid token"
);
// make sure you pass positive amount
require(_amountIn > 0, "invalid amount");
// define token1 and token2 based on function arg tokenIn
bool isToken1 = tokenIn == address(token1);
( // you are using Token as type instead of IERC20
Token tokenIn,
Token tokenOut,
// reserve1 and reserve2 how many each tokens holds. you should be setting in state
uint reserve1,
uint reserve2
) = isToken1
? (token1, token2, reserve1, reserve2)
: (token2, token1, reserve2, reserve1);
// instead token1, tokenIn should be transferred
// I believe this is what it does
_safeTransferFrom(tokenIn, owner1, owner2, amount1);
// calculate the tokenOut including the fee=0.3%. user who wants to swap has to pay for fee
uint amountInAfterFee = (_amountIn * 997) / 1000;
// here you need to calculate how much y token will be taken out
// this is the formula dy=(y*dx)/(x+dx) y and x are reserves, dx is amountInAfterFee
amountOut =
(reserve2 * amountInAfterFee) /
(reserve1 + amountInAfterFee);
// now you need to send this amountOut to tokenOut or token2 in your contract
token2.transfer(msg.sender,amountOut)
// here update the reserves
// I dont think that you need this
// _safeTransferFrom(token2, owner2, owner1, amount2);
}
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;
}