ERC721 Tokens with Metadata and Enumerable with Openzeppelin v4.1.0 - ethereum

My question has two parts,
I am trying to create an ERC721 token using the Openzeppelin contracts with metadata and that is enumerable. My understanding is after openzeppelin v4.0.0 they removed the ERC721Full.sol contract which included metadata and enummerable. I want to use solidity 0.8.0 and so those old contracts wont work, right? When importing and inheriting ERC721Enumerable.sol into the ERC721.sol contract, I get TypeError: Definition of base has to precede definition of derived contract
I tried just importing ERC721Enumerable.sol in my own contract, but still errors. I also tried importing the older ERC721Full.sol contract and changing all the pragma 0.5.0 to pragma 0.8.0, but it inherits like a dozen other contracts and changing all of them doesn't seem wise. I tried the same with IERC721Enumerable.sol, still errors. Any ideas? Any help would be amazing!
Second part. What's the difference between ERC__ and IERC__? What's the purpose of IERC contracts?
Thanks!!
Here's my contract (I'm following a tutorial). I import the regular ERC721 contract, inherit it. it gives me an error when I test and call the totalSupply function because there is no totalSupply function:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Color is ERC721 {
string[] public colors;
mapping(string => bool) _colorExists;
constructor() ERC721("Color", "COLOR") {
}
function mint(string memory _color) public {
colors.push(_color);
uint _id = colors.length;
_mint(msg.sender, _id);
_colorExists[_color] = true;
}
}
my test script:
const Color = artifacts.require('./Color.sol')
require('chai')
.use(require('chai-as-promised'))
.should()
contract('Color', (accounts) => {
let contract
before(async () => {
contract = await Color.deployed()
})
describe('deployment', async () => {
it('deploys successfully', async () => {
contract = await Color.deployed()
const address = contract.address
console.log(address)
assert.notEqual(address, 0x0)
assert.notEqual(address,'')
assert.notEqual(address, null)
assert.notEqual(address, undefined)
})
it('has a name', async () => {
const name = await contract.name()
assert.equal(name, 'Color')
})
it('has a symbol', async () => {
const symbol = await contract.symbol()
assert.equal(symbol, 'COLOR')
})
})
describe('minting', async () => {
it('creates a new token', async () => {
const result = await contract.mint('#00CD22')
const totalSupply = await contract.totalSupply()
// SUCCESS
asert.equal(totalSupply, 1)
})
})
})
this is my error without the enumerable contract/without totalSupply
I can paste the openzeppelin contracts if you like, or link them here
I also tried this, importing ERC721Enumerable
And got this:
let me know fi you need anymore info!
thanks in advance

For the first part
An ERC721 by default does not have a totalSupply method, and that's the reason you're getting the error. The totalSupply method comes from the IERC721Enumerable, wich is an optional extension of the standard ERC721 as the documentation states. If you want your ERC721 to be enumerable just import the enumerable extension from the openzeppelin implementations for your derived contract, like so:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Color is ERC721Enumerable {
string[] public colors;
mapping(string => bool) _colorExists;
constructor() ERC721("Color", "COLOR") {
}
function mint(string memory _color) public {
colors.push(_color);
uint _id = colors.length;
_mint(msg.sender, _id);
_colorExists[_color] = true;
}
}
The reason the compiler gave you the error when trying to import ERC721Enumerable is that you were tryning to import it in the Openzeppelin ERC721 implementation, but that contract had to exists prior to the ERC721Enumberable. In other words the inheritance chain is
ERC721 <-- ERC721Enumerable
What you were trying to do was
ERC721 <-- ERC721Enumerable
|_____________↑
Wich creates a circular dependency that cannot be satisfied.
For the second part
ERC contracts are like abstracts classes in every OOP programming languages (first that come to my mind and maybe most relatable are Java and C++), while IERC are interfaces; this means that while both cannot be instanciated directly (they both need the children to implement something) ERC contracts provide standard implementations for the corresponding IERC methods. This is the reason often you see contracts implement the ERC contracts and not the IERC ones.

To use the ERC271Enumerable extension you need to implement it and override some functions of ERC271, _beforeTokenTransfer and supportsInterface.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract Color is ERC721, ERC721Enumerable{
string[] public colors;
mapping(string => bool) _colorExists;
constructor () ERC721("Color", "COLORS") {}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) {
return super.supportsInterface(interfaceId);
}
function mint(string memory _color) public{
colors.push(_color);
uint _id = colors.length;
_mint(msg.sender, _id);
_colorExists[_color] = true;
}
}
What's the difference between ERC__ and IERC__? What's the purpose of IERC contracts?
IERC is the Interface for the token contract.
ERC is the implementation of the token contract.
The importance is to make sure a Contract implementation has the right methods, with the right visibility, parameters, and return values.

Related

My Solidity program work on Ganache but not on testnet (Goerli)

I want to get all reserve in UniswapV2 pool in one request to the EVM. Here is the code getReserve :
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract IUniswapV2Pair {
function getReserves() public view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) {}
}
contract getReserve {
function get(address[] memory _add) external view returns (uint[] memory) {
uint n = _add.length;
uint[] memory liste = new uint[](n*2);
// Define variables to store the returned values
uint112 reserve0;
uint112 reserve1;
uint32 blockTimestampLast;
for (uint i=0; i<n; i++) {
// Call the getReserves function in the other contract and store the returned values
(reserve0, reserve1, blockTimestampLast) = IUniswapV2Pair(_add[i]).getReserves();
liste[i*2] = reserve0;
liste[i*2+1] = reserve1;
}
return liste;
}
function getOnlyOne(address _add) external view returns (uint112, uint112, uint32) {
return IUniswapV2Pair(_add).getReserves();
}
}
To test that this program work well on my Ganache EVM I have created this program IUniswapV2Pair:
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract IUniswapV2Pair {
function getReserves() external pure returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) {
reserve0 = 11;
reserve1 = 12;
blockTimestampLast = 13;
return (reserve0, reserve1, blockTimestampLast);
}
}
Like I said, it is working well on my Ganache EVM. But when I deploy it on the GOERLI testnet it is not working, here is the addresss :
getReserve : 0x993305D7d90675656857c7Cd69f1CF57242D79D5
IUniswapV2Pair : 0x33e57A530F90aB2A5572E2a877161Ca644e8FC95
My Problem is to make getOnlyOne("0x33e57A530F90aB2A5572E2a877161Ca644e8FC95") working.
1 Testing code on Goerli
I have tried to connect to GOERLI via Python, here is the code :
from web3 import Web3
import json, web3
server = "https://eth-goerli.public.blastapi.io"
w3 = Web3(Web3.HTTPProvider(server))
w3.isConnected()
# Contrat intermédiaire
address = "0x993305D7d90675656857c7Cd69f1CF57242D79D5"
abi = [{"inputs":[{"internalType":"address[]","name":"_add","type":"address[]"}],"name":"get","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function","constant":true},{"inputs":[{"internalType":"address","name":"_add","type":"address"}],"name":"getOnlyOne","outputs":[{"internalType":"uint112","name":"","type":"uint112"},{"internalType":"uint112","name":"","type":"uint112"},{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function","constant":true}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.get(["0x33e57A530F90aB2A5572E2a877161Ca644e8FC95"]).call()
contract.functions.getOnlyOne("0x33e57A530F90aB2A5572E2a877161Ca644e8FC95").call()
I have this error web3.exceptions.ContractLogicError: execution reverted, which I don't have when I do it on Ganache.
And to make sure that it is not a problem from IUniswapV2Pair I have this code:
address = "0x33e57A530F90aB2A5572E2a877161Ca644e8FC95"
abi = [{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"reserve0","type":"uint112"},{"internalType":"uint112","name":"reserve1","type":"uint112"},{"internalType":"uint32","name":"blockTimestampLast","type":"uint32"}],"stateMutability":"pure","type":"function","constant":true}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.getReserves().call()
Which return me : [11, 12, 13]
2 Testing connection between contracts
I have created 2 others easier smart contract which have been working well here it is :
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract FavoriteNumber {
uint favoriteNumber;
function getFavoriteNumber() external view returns(uint) {
return favoriteNumber;
}
function setFavoriteNumber(uint _favoriteNumber) external {
favoriteNumber = _favoriteNumber;
}
}
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract FavoriteNumber {
function getFavoriteNumber() public view returns(uint) {}
function setFavoriteNumber(uint _favoriteNumber) public {}
}
contract ExistingNoSet {
function getA(address _t) public view returns (uint result) {
return FavoriteNumber(_t).getFavoriteNumber();
}
function setA(address _t, uint _val) public {
FavoriteNumber(_t).setFavoriteNumber(_val);
}
}
Here is the address :
FavoriteNumber : 0x14c89b4F462C11961Bb48aD6B2008f64617CF62a
ExistingNoSet : 0x97BdDaff1a971580f99C1DB850dE5EcF4982251a
And to test the code, here is the Python program, be careful I give a private key here (don't use it outside of a testnet) :
from web3 import Web3
import json, web3
server = "https://eth-goerli.public.blastapi.io"
private_key = "df49b58fbc863c5e60fe4e64829a853c46a8a12c3310404bc2a03bfefb89f68a"
public_add = "0xb4311ad11530F735ecE2d652Cbd56D1FB8D6Efeb"
w3 = Web3(Web3.HTTPProvider(server))
w3.isConnected()
# Contrat intermédiaire
address = "0x97BdDaff1a971580f99C1DB850dE5EcF4982251a"
abi = [{"inputs":[{"internalType":"address","name":"_t","type":"address"}],"name":"getA","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function","constant":true},{"inputs":[{"internalType":"address","name":"_t","type":"address"},{"internalType":"uint256","name":"_val","type":"uint256"}],"name":"setA","outputs":[],"stateMutability":"nonpayable","type":"function"}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.getA("0x14c89b4F462C11961Bb48aD6B2008f64617CF62a").call() # It should return 15
txn = contract.functions.setA(
"0x14c89b4F462C11961Bb48aD6B2008f64617CF62a", 3 # You set this new number
).build_transaction({
'nonce': w3.eth.get_transaction_count(public_add),
'gas': 200000,
'gasPrice': w3.toWei('20', 'gwei')
})
signed_txn = w3.eth.account.sign_transaction(txn, private_key=private_key)
w3.eth.send_raw_transaction(signed_txn.rawTransaction)
# You have to time sleep a little bit like 1min
contract.functions.getA("0x14c89b4F462C11961Bb48aD6B2008f64617CF62a").call() # It should return 3
So this program works well.
Thank you very to have read my post until here, I hope you will be able to help me.

How to create many ERC721 tokens sharing the same metadata tokenURI but different TokenId?

I'm actually trying to create an NFT Marketplace, where a user can upload his NFT (create/mint new NFT) and other users can give some Ether and mint the same NFT. Trying to achieve this with the ERC721 standard. Maybe ERC1155 will be a better choice but I have to use ERC721.
So, I think how I can achieve this using ERC721 by minting NFTs with different tokenIDs but with pointing to the same NFT metadata or TokenURI.
I know this is possible as mentioned in this post: https://ethereum.stackexchange.com/questions/59765/two-erc721-tokens-that-refer-to-the-same-metadata
But I can't fo it programatically.
This is how I'm trying: (It's not working, even 1 NFT is not showing on he opensea testnet)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract navich is ERC721URIStorage, Ownable {
using Strings for uint256;
using Counters for Counters.Counter;
Counters.Counter private _tokenId;
string public baseURI = "ipfs://QmRyezUtChrpvH4i4wKEoPngwTFKHuu4YYjZrSg89wvqVq/";
constructor() ERC721("Wagmi", "Wagmi") {}
function mintNFT() public payable {
uint256 dynamic = 1;
_tokenId.increment();
_mint(msg.sender, _tokenId.current());
_setTokenURI(dynamic, baseURI);
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
return
string(
abi.encodePacked(
baseURI,
tokenId.toString(),
".json"
)
);
}
// function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
// require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
// return "ipfs://QmRyezUtChrpvH4i4wKEoPngwTFKHuu4YYjZrSg89wvqVq";
// }
}
Incidentally, this is not the full Marketplace code.

How to call a method from a contract that was instantiated from another contract via truffle console?

I'm trying to call a method from a contract that was instantiated from another contract. I use truffle console.
Here are the details: (I think you can gloss over the code, since this question is aimed at using truffle console, not fixing any bugs)
I have a contract called MyCoinSupply.sol:
pragma solidity ^0.8.0;
import "./MyCoin.sol";
contract MyCoinSupply is MyCoin("MyCoin", "MYC") // MyCoin is ERC20
{
constructor() public // gives 1000 tokens to the owner
{
_mint(msg.sender, 1000);
}
function getCoinbase() public returns (address) // for debugging purposes
{
return block.coinbase;
}
function _mintMinerReward() internal // gives 20 tokens to the miner
{
// _mint(block.coinbase, 20); // for some reason, block.coinbase is address 0
}
function _transfer(address from, address to, uint256 value) override internal
{
_mintMinerReward();
super._transfer(from, to, value);
}
}
I instantiate MyCoinSupply.sol within MyCoinDEX.sol:
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 showSender() public view returns (address) // for debugging purposes
{
return (msg.sender);
}
function buy(uint256 amountTobuy) payable public // send ether and get tokens in exchange; 1 token == 1 ether
{
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);
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
}
Here's my migration for deploying contracts:
const MyCoinSupply = artifacts.require("MyCoinSupply");
const MyCoinDEX = artifacts.require("MyCoinDEX");
module.exports = function(deployer) {
deployer.deploy(MyCoinSupply);
deployer.deploy(MyCoinDEX);
};
Here's my problem:
When I try to call functions that belong to MyCoinDEX, all works well:
truffle(development)> let instance = await MyCoinDEX.deployed()
undefined
truffle(development)> instance.buy(1)
All is good here.
However, when I try the following I get an error:
truffle(development)> instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
evalmachine.<anonymous>:0
instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
^
Uncaught TypeError: instance.token.balanceOf is not a function
I Googled this error (I read this solution and this solution), but I didn't really stumble upon a solution. Can someone tell me how can I call token's methods from MyCoinDEX?
Thank you in advance!

Solidity function implementing `public onlyOwner` cannot be called even by the owner

I am following along the documentation here: https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft/how-to-mint-a-nft. And have a smart contract of form:
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract NFTA is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter public _tokenIds;
mapping (uint256 => string) public _tokenURIs;
mapping(string => uint8) public hashes;
constructor() public ERC721("NFTA", "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
/**
* #dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
When I attempt to estimate the gas cost of minting with this:
const MY_PUBLIC_KEY = '..'
const MY_PRIVATE_KEY = '..'
const ALCHEMY = {
http: '',
websocket:'',
}
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(ALCHEMY.http);
const NFTA = require("../artifacts/contracts/OpenSea.sol/NFTA.json");
const address_a = '0x...';
const nft_A = new web3.eth.Contract(NFTA.abi, address_a);
async function mint({ tokenURI, run }){
const nonce = await web3.eth.getTransactionCount(MY_PUBLIC_KEY, 'latest');
const fn = nft_A.methods.mintNFT(MY_PUBLIC_KEY, '')
console.log( 'fn: ', fn.estimateGas() )
}
mint({ tokenURI: '', run: true })
I receive error:
(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner
Presumably because mintNFT is public onlyOwner. However, when I check Etherscan, the From field is the same as MY_PUBLIC_KEY, and I'm not sure what else can be done to sign the transaction as from MY_PUBLIC_KEY. The easy way to solve this is to remove the onlyOwner from function mintNFT, and everything runs as expected. But suppose we want to keep onlyOwner, how would I sign the transaction beyond what is already written above.
Note I'm using hardHat to compile the contracts and deploying them. That is:
npx hardhat compile
npx hardhat run scripts/deploy.js
=============================================
addendum
The exact code given by alchemy to deploy the mint is:
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
Note in the transaction the from field is PUBLIC_KEY, the same PUBLIC_KEY that deployed the contract, and in this case the nftContract has public onlyOwner specified. This is exactly what I have done. So conceptually who owns this NFT code? On etherscan is it the to address ( the contract address ), or the from address, which is my public key, the address that deployed the contract, and the one that is calling mint, which is now failing with caller is not the owner error.
Search the internet, I see others have encountered this problem here: https://ethereum.stackexchange.com/questions/94114/erc721-testing-transferfrom, for Truffle you can specify the caller with extra field:
await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })
Extra parameters is not an option here because I'm using hardhat.
OpenZeppelin's Ownable.sol defines the default owner value as the contract deployer. You can later change it by calling transferOwnership() or renounce the owner (i.e. set to 0x0) by calling renounceOwnership().
The onlyOwner modifier reverts the transaction if it's not sent by the current owner. (see the code)
So you need to call the mintNFT() function from the same address that deployed the contract, because that's the current owner. Or you can change the owner first by calling transferOwnership() (from the current owner address).
Removing the onlyOwner modifier from the mintNFT() function would allow anyone to call the function.
Answering this for anyone else who stumbles across the issue while using the Alchemy tutorial:
In the tutorial, it says to init the contract in your mint method like this:
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
However, if you attempt to call estimateGas() or encodeABI() it will fail with the onlyOwner error.
The solution is to change the third line to:
const nftContract = new web3.eth.Contract(contract.abi, contractAddress, {
from: PUBLIC_KEY
});
This will set a default value of "From" so that when you call estimateGas() on a mint function marked onlyOwner, it will be able to use that from field to see that its the owner calling the estimateGas.
Took forever to figure this out.
I finally figured it out, the contract does not initialize the way I deploy it. So you have to initialize it after deployment.

How to test a contract which is generated by another deployed contract?

Currently, I want to test a contract called B which is generated from another contract Registry by using truffle. For example:
Contract Registry
pragma solidity >=0.4.17;
import "./B.sol";
contract Registry is Owned {
function createContract(
address userAddress)
public
returns(
bool res
)
{
// create a contract B which the owner is the user.
B b = new B(userAddress);
return true;
}
Contract B
pragma solidity >=0.4.17;
contract B {
address private owner;
constructor (
address _owner
)
public
{
owner = _owner;
}
function logData(
bytes32 data,
uint timestamp
)
public
returns(bool res)
{
...
}
...
testing script B.js in truffle/test:
const Registry = artifacts.require('./Registry.sol');
const B = artifacts.require('./B.sol');
const truffleAssert = require('truffle-assertions');
const assert = require('assert');
contract('Registry', (accounts) => {
let registry;
let B;
beforeEach(async () => {
// create a reportSummary contract from registry contract.
registry = await Registry.deployed();
// How can I retrieve the contract B's instance?
});
Because I am a newbie in solidity, thus I cannot find any way to test the function logData or other functions in contract B which depends on the contract Registry. Is there any suitable way or should I change the contract designed? Thanks.
You can check my this answer it has example too about accessing the function from imported contract
Solidity: Can a Parent contract see data updates from a Child contract?
To access the function through web3, just use the instance of Registry contract and you can call the function of Contract B