DEV Community

Discussion on: Deploying Regenerative NFTs on Polygon

Collapse
 
benm4nn profile image
benm4nn

Nice tutorial Archie, for me I was focused on the meta-transactions piece as I've build my own NFT collectable generation program.

I'm stuck with Opensea unable to pull my metadata... I'm using Pinata for storage and a custom API on Heruko to serve up the metadata (this is all working fine).

Looking at how you're calling MintItem with the token ID as well, I think that might be the issue... Is this supposed to be the base URI for my token API, or does it expect the token number too?

For example I'm using my base URL in mintItem:
krazyphaces.herokuapp.com/api/token
But to serve each token's metadata requires the token ID too e.g.
krazyphaces.herokuapp.com/api/toke...

  try {
        for (var i = START_NUM; i < (START_NUM + NUM_ITEMS); i++) {
              await gameItem.mintItem(OWNER_ADDRESS, **BASE_URL**);
              console.log("Minting NFT " + i + " of " + NUM_ITEMS);
        }
Enter fullscreen mode Exit fullscreen mode

My verified contract code is here...
mumbai.polygonscan.com/address/0xb...

Cheers!

Collapse
 
benm4nn profile image
benm4nn

Oh and the collection is here: testnets.opensea.io/collection/kra...

Collapse
 
benm4nn profile image
benm4nn

After a bit more digging on Polygonscan it looks like the token URI isn't returning the token id when queried...
It seems the tokens ID counter isn't setting a newItemId here in _setTokenURI...?

function mintItem(address player, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}

Collapse
 
yournewempire profile image
Archie Smyth • Edited

Hi Ben. I have looked at your APIs and they look fine as you say. So let's get down to it.

In the last post, I did not set a base uri, but just minted the item, passing in the full uri of the metadata - no prefix base. I did the same again here in this example. I have not overriden the virtual function _baseURI, this is where you would override it in the GameItem contract and add your base uri in the return statement. Although, there is more to it than that:

An important part I did not mention, is that I picked inheritance/import of ERC721URIStorage.sol over ERC721.sol. This is because ERC721URIStorage.sol inherits ERC721, so that one can override the tokenURI function to get URIs and add a new function, setTokenURI for setting the URIs (hence the name ERC721URIStorage).

So when you are minting with the full URI, setTokenURI from ERC721URIStorage will expect a full working URI and not use a baseURI at all. Notice that this function is scoped as 'virtual', meaning it can be overridden to perhaps concatenate the baseURI with the ID you want to pass in when minting.

With the above in mind, you can review the changes of the overridden tokenURI view (getter) function.

The function now checks if the baseURI has been changed from the original empty 0 string. If the _baseURI() call returns that empty string, then tokenURI returns the URI from _tokenURIs mapping, set when you call my specific mintItem function with the full URI, otherwise tokenURI will concatenate the base uri with the token ID from _tokenURIs mapping.

So if you do override the _baseURI function to fit your base , when you mint, keep in mind that you can pass in the number of your token id (to complete with the base uri) but it will be stored as a string. Which leads me to your mint.js script. In case you did not know, in JS you can pass expressions into strings when using the backtick ` character. If your base URI is set like so: krazyphaces.herokuapp.com/api/token/

then your js script could look like this

`
const hre = require("hardhat");
const NUM_ITEMS = 5;
const START_NUM = 0
const OWNER_ADDRESS = "";

async function main() {

  const GameItem = await hre.ethers.getContractFactory("GameItem");
  const gameItem = await GameItem.attach('yourcontractaddress')

  for (var i = START_NUM; i <= (START_NUM + NUM_ITEMS); i++) {
        await gameItem.mintItem(OWNER_ADDRESS, `${i}`);
  }
Enter fullscreen mode Exit fullscreen mode

}

... the main() call
`

Notice that i have added <= before (START_NUM + NUM_ITEMS); to include the final number of NUM_ITEMS, in this case 5.

To clarify, I forgot to mention that I was using the ERC721URIStorage for URI storage and also not using a base.

To conclude this, I would encourage scanning through openzeppelins contracts that I linked above, noticing the scopes of some of these functions that you may be using.

I hope I have layed out the information you need, looking forward to your progress.

Collapse
 
benm4nn profile image
benm4nn

Excellent explanation as always Archie!
My workaround was to create my own counter and pass this each time I mint. Now the tokenIds are being returned correctly and the metadata is displaying...

My next challenge (and a suggestion for your next tutorial) is
1) Using the OpenSea SDK to sell the NFTs... When I try to sell using the OpenSea Creatures example sell script (github.com/ProjectOpenSea/opensea-...) I get this error:

return constants.DEPLOYED[network].WyvernExchange;

TypeError: Cannot read property 'WyvernExchange' of undefined

My theory right now is that the script (designed for mainnet, not matic/mumbai is expecting the WyvernExchange and I'm not sure if this is provided by matic/mumbai... but this is the first time I learned of the WyvernExchange protocol....

2) I tried to write a node.js script to force updates of my metadata by calling the URL of each NFT followed by ?force_update=true, however OpenSea's cloudflare didn't like the requests and blocked with a 403 error... Wish there was a way to force a metadata update for a whole collection without having to use a browser to open each page...

Keep up the great work, there's a NFT with your name on it (literally) once I'm done!

Thread Thread
 
benm4nn profile image
benm4nn

I found this one liner on why the OpenSea Creatures sell script fails on Polygon with TypeError: Cannot read property 'WyvernExchange' of undefined...
"OpenSea uses different order matching for ethereum and polygon. On Ethereum, Wyvern is used, but on Polygon, 0x v3 is used."
(ethereum.stackexchange.com/questio...)

So I guess I need a sell script that uses 0x v3 protocol instead, but can't see how on earth to do that!

Thread Thread
 
yournewempire profile image
Archie Smyth

Hey Ben, thankful for the great response.

I will be honest, I have never used the sell script, therefore never looked into it that much. With your introduction of it, I shall definitely have a look, seems interesting.

This is a bit of a problem when there are no OpenSea examples for PolygonMatic sell scripts. I can already tell by looking at the script on lines 36 and 49 that you would be relying on OpenSeaPort to have compatibility with a Matic.

So I was snooping around in the Opesea.js GitHub and OpenSeaPort docs and cant find 'Polygon' or 'Matic' anywhere. I will continue to search around.

Thanks, Archie

Thread Thread
 
benm4nn profile image
benm4nn

Thanks Archie, After a few hours of poking around, here's what I'm trying... It overcame the error I had before but there's very little out there about the sell.js and Polygon/Matic except a few comments suggesting that OpenSea's v1 API doesn't support it yet....

So I tried this... network name as Network.Matic....
const seaport = new OpenSeaPort(
providerEngine,
{
networkName: Network.Mumbai //,
// apiKey: API_KEY, /* i dont have (opensea) API_KEY */
},
(arg) => console.log(arg)
);

This got me past the previous error... But now I'm stuck with this new error...

FetchError: request to api.opensea.io/api/v1/asset/0xdbb6...? failed, reason: certificate has expired

Obviously I've checked all my keys (I'm using Alchemy with a key defined specifically for Polygon Mumbai)....
And I don't have the optional OpenSea API but have applied for one...

Ever onward! But really don't want to have to put thousands of Polygon NFTs up for sale individually by hand!

Thread Thread
 
yournewempire profile image
Archie Smyth

Hey Ben, I just got a response from opensea discord:

You should go join OpenSea, Polygon discords if you have not already.

Thanks, Archie