I have deployed a ERC-1155 based contract (based on OpenZeppelin) and minted some NFTs on this contract successfully. But when I want to use these NFTs in OpenSea, it always says "Unidentified contract".
Example: https://testnets.opensea.io/assets/0xc7d3e4a5A0c3e14ba8C68ea1b8a99a9dBf3ca76F/2
API-Example: https://testnets-api.opensea.io/api/v1/asset/0xc7d3e4a5A0c3e14ba8C68ea1b8a99a9dBf3ca76F/2/?force_update=true
Following their official Tutorial repository (which does not compile any more because of outdated dependencies and other issues) I have added some (maybe) opensea-specific functions and data that might required for OpenSea in order to work properly. However, OpenSea is able to grab all required data to display an NFT, but as long as they say "Unidentified contract", this all makes no sense so far.
My question has:
has someone already managed to deploy a ERC-1155 and used it with OpenSea properly without this issue? Is there anything we have to "register" somehow contracts that are not based on ERC-721?
🔢 Code to reproduce
import "#openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "#openzeppelin/contracts/access/AccessControl.sol";
import "#openzeppelin/contracts/security/Pausable.sol";
import "#openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract OwnableDelegateProxy { }
contract ProxyRegistry {
mapping(address => OwnableDelegateProxy) public proxies;
}
contract MetaCoin is ERC1155, AccessControl, Pausable, ERC1155Burnable {
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
address proxyRegistryAddress;
constructor(address _proxyRegistryAddress) ERC1155("https://abcoathup.github.io/SampleERC1155/api/token/{id}.json") {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(URI_SETTER_ROLE, msg.sender);
_setupRole(PAUSER_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
proxyRegistryAddress = _proxyRegistryAddress;
}
function setURI(string memory newuri) public onlyRole(URI_SETTER_ROLE) {
_setURI(newuri);
}
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC1155, AccessControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function mint(address account, uint256 id, uint256 amount, bytes memory data)
public
onlyRole(MINTER_ROLE)
{
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyRole(MINTER_ROLE)
{
_mintBatch(to, ids, amounts, data);
}
function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
internal
whenNotPaused
override
{
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
/**
* Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-free listings.
*/
function isApprovedForAll(
address _owner,
address _operator
) public override view returns (bool isOperator) {
// Whitelist OpenSea proxy contract for easy trading.
ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
if (address(proxyRegistry.proxies(_owner)) == _operator) {
return true;
}
return ERC1155.isApprovedForAll(_owner, _operator);
}
}
💻 Environment
node: v16.7.0
deps:
"#openzeppelin/contracts": "^4.3.0",
"#nomiclabs/buidler": "^1.4.8",
"#nomiclabs/hardhat-ethers": "^2.0.2",
"#nomiclabs/hardhat-etherscan": "^2.1.1",
"#nomiclabs/hardhat-waffle": "^2.0.1",
"#openzeppelin/hardhat-upgrades": "^1.9.0",
"#typechain/ethers-v5": "^6.0.5",
"#typechain/hardhat": "^1.0.1",
"#types/chai": "^4.2.15",
"#types/chai-as-promised": "^7.1.3",
"#types/mocha": "^8.2.2",
"#types/node": "^14.14.37",
"chai": "^4.3.3",
"chai-as-promised": "^7.1.1",
"chai-datetime": "^1.8.0",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.4.5",
"hardhat": "^2.6.1",
"hardhat-typechain": "^0.3.5",
"ts-generator": "^0.1.1",
"ts-node": "^9.1.1",
"typechain": "^4.0.3",
"typescript": "^4.2.4"
This is taken from name in your contract.
For ERC-1155 tokens, add a public variable name
string public name = "My Collection Name";
I finally found the root cause! OpenSea expects a public property called name in order to display the proper Name of the Collection instead of a static label Unidentified contract.
I came across this while looking at their reference code (which depends on a now 3-year-old MultiToken-Contract implementation and needs all in all some downgrades of Node and other tools in order to get it build [a downgrade to Node 10 worked best for me today] ).
Related
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'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.
So I'm working on an NFT marketplace smart contract and one of the features in this marketplace allows users to deploy their own collection (this is basically a smart contract that extends the ERC721 specification). For this, I'm leveraging the create2 opcode. It deploys well while testing on the testnet (except for the insanely huge gas requirement) but the problem is, I can only call a function once in this contract. This is the contract that extends ERC721:
pragma solidity ^0.8.0;
import '#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';
import '#openzeppelin/contracts/token/ERC721/IERC721.sol';
import '#openzeppelin/contracts/security/ReentrancyGuard.sol';
import '#openzeppelin/contracts/utils/Counters.sol';
import './interfaces/IDeployableCollection.sol';
contract DeployableCollection is IDeployableCollection, ERC721URIStorage, ReentrancyGuard {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
address public _collectionOwner;
bytes32 public _category;
address payable public _paymentReceiver;
string public _collectionURI;
mapping(address => uint256) public lastMintedForIDs;
constructor(
string memory name_,
string memory symbol_,
address collectionOwner_,
string memory category_,
address paymentReceiver_,
string memory collectionURI_
) ERC721(name_, symbol_) {
_collectionOwner = collectionOwner_;
_category = keccak256(abi.encode(category_));
_paymentReceiver = payable(paymentReceiver_);
_collectionURI = collectionURI_;
}
function mintFor(string memory _tokenURI, address to) external nonReentrant returns (uint256 _tokenId) {
_tokenIds.increment();
_tokenId = _tokenIds.current();
_mint(to, _tokenId);
_setTokenURI(_tokenId, _tokenURI);
lastMintedForIDs[to] = _tokenId;
}
}
This is the line that deploys the contract:
function deployCollection(
string memory name_,
string memory symbol_,
string memory category_,
address paymentReceiver_,
string memory _collectionURI
) external payable nonReentrant {
uint256 _fee = _utilityToken != address(0) && IERC20(_utilityToken).balanceOf(_msgSender()) >= _requiredHold
? _collectionDeployFeeInEther.sub((uint256(_percentageDiscount).mul(_collectionDeployFeeInEther)).div(100))
: _collectionDeployFeeInEther;
require(msg.value >= _fee, 'FEE_TOO_LOW');
bytes memory _byteCode = abi.encodePacked(
type(DeployableCollection).creationCode,
abi.encode(name_, symbol_, _msgSender(), category_, paymentReceiver_, _collectionURI)
);
bytes32 _salt = keccak256(abi.encode(name_, _msgSender()));
address _collection;
assembly {
_collection := create2(0, add(_byteCode, 32), mload(_byteCode), _salt)
}
emit CollectionDeployed(_collection, _msgSender(), block.timestamp, name_, category_, symbol_);
}
Now this works fine, except once I call mintFor and an NFT gets minted, the contract stops working. It shows a total supply of zero (0) on the block explorer even though an NFT has been created (I'm only able to create an NFT with an ID of 1). I also am not able to call any function again as it raises an exception (the cause of which is unknown). This is the line in the factory contract that does the actual minting:
function mintNFT(
address collection,
string memory tokenURI_,
address _for
) external payable nonReentrant returns (bool) {
uint256 _fee = _utilityToken != address(0) && IERC20(_utilityToken).balanceOf(_msgSender()) >= _requiredHold
? _mintFeeInEther.sub((uint256(_percentageDiscount).mul(_mintFeeInEther)).div(100))
: _mintFeeInEther;
require(msg.value >= _fee, 'FEE_TOO_LOW');
address _paymentReceiver = IDeployableCollection(collection)._paymentReceiver();
uint256 _feeForOwner = (uint256(_percentageForCollectionOwners).mul(_fee)).div(100);
_safeMintFor(collection, tokenURI_, _for);
_safeTransferETH(_paymentReceiver, _feeForOwner);
uint256 _tokenId = IDeployableCollection(collection).lastMintedForIDs(_msgSender());
emit Mint(collection, _tokenId, block.timestamp, tokenURI_, _msgSender());
return true;
}
I'm guessing an optimization with a runs of 1 is applied upon deployment using create2 (forgive me if this is stupid) but I also think it isn't likely as I'm also watching for events on the smart contract with a Nodejs back-end and I'm able to call '_collectionURI()' when the event data is propagated to my back-end app and this is before calling mintFor. I'm confused! Please help!
This is a link to the contract info on the explorer: https://testnet.bscscan.com/token/0x6dd5bd0072cdc5e8c24f262a9631c175bc2356a0
I'm trying to get estimated eth needed to perform a swap on ropsten.
interface IUniswap {
function WETH() external pure returns (address);
function getAmountsOut(
uint amountIn,
address[] calldata path)
external
view
returns (uint[] memory amounts);
}
contract UniswapController {
address internal constant UNISWAP_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
IUniswap public uniswapRouter;
address private UNISWAP_DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
constructor() public {
uniswapRouter = IUniswap(UNISWAP_ROUTER_ADDRESS);
}
function getEstimatedETHforToken(uint daiAmount) public view returns (uint[] memory) {
return uniswapRouter.getAmountsOut(daiAmount, getPathForETHtoDAI());
}
function getWETH() public view returns(address) {
return uniswapRouter.WETH();
}
function getPathForETHtoDAI() public view returns (address[] memory) {
address[] memory path = new address[](2);
path[0] = uniswapRouter.WETH();
path[1] = UNISWAP_DAI_ADDRESS;
return path;
}
}
When I run getEstimatedETHforToken(amount) on remix ide, I get the error:
execution reverted: UniswapV2Library: INSUFFICIENT_LIQUIDITY
I suspect I have the wrong DAI stablecoin address, but this is the address on uniswap's site, which one should I use?
There doesn't seem to be good tokens use on testnets. Just forked the mainnet using ganache-cli and infura with this tutorial and it worked just fine.
I am using the openzeppelin for creating the ICO contract. I am concerned about the MinterRole here. After I develop Token contract, I added the sale contract as a minter using "addMinter" function.
Question
After the sale is over
I do not see a way to remove the contract address as a minter. I see that "renounceMinter" will remove only the person's who is calling this method.
I understand even If I remove (or) not remove the contract address as a minter at that point (i.e., after the sale is over) the Sale contract will b defined in such a way that it will not work at all. is it safe even if we leave the contract address in the minter list ?
Why openzeppelin has designed the "MinterRole" contract with the "renounceMinter" function that can remove only the person who is calling that contract but the function "addMinter" can do this for others also ?
Code
SampleCrowdsale
contract SampleCrowdsale is FinalizableCrowdsale, MintedCrowdsale
MintedCrowdsale
contract MintedCrowdsale is Crowdsale {
constructor() internal {}
function _deliverTokens(
address beneficiary,
uint256 tokenAmount
)
internal
{
// Potentially dangerous assumption about the type of the token.
require(
ERC20Mintable(address(token())).mint(beneficiary, tokenAmount));
}
}
ERC20Mintable
contract ERC20Mintable is ERC20, MinterRole
MinterRole
pragma solidity ^0.4.24;
import "../Roles.sol";
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private minters;
constructor() internal {
_addMinter(msg.sender);
}
modifier onlyMinter() {
require(isMinter(msg.sender));
_;
}
function isMinter(address account) public view returns (bool) {
return minters.has(account);
}
function addMinter(address account) public onlyMinter {
_addMinter(account);
}
function renounceMinter() public {
_removeMinter(msg.sender);
}
function _addMinter(address account) internal {
minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
minters.remove(account);
emit MinterRemoved(account);
}
}