The perils of developing NFT for OpenSea Marketplace

Hagen Hübel
8 min readSep 25, 2021

While creating NFTs is a breeze for me as a developer purely programmatically thanks to the OpenZeppelin ERC721 and ERC1155 implementations, this is no longer the case when integrating with the OpenSea Marketplace. I have faced so many issues over recent months that I think it is worth it to write about and list them in a blog post.

Just to be clear: using OpenSea itself as minting tool without any custom code and custom integration around works perfectly. The problem starts when we want to list our own contracts on OpenSea because OpenSea ignores the standards a bit or use them on their own interpretation.

If someone has already solved the one or the other issue, pls let me know in the comments.

1. “Unidentified contract”

When we want to list NFTs based on our own ERC-1155 contract, they will be listed as “unidentified contract”, which looks less trustful on a market place.

Example: https://forum.openzeppelin.com/t/create-an-erc1155/4433

To find out, why OpenSea’s own ERC-1155 based SmartContracts are displayed correctly without this “unidenftified contract” issue even there, I compared the official sample repositories with OpenZeppelin’s implementation.

One particular difference is this line: https://github.com/ProjectOpenSea/opensea-erc1155/blob/master/contracts/ERC1155Tradable.sol#L28

Have you seen line #10? As soon as we place a public string variable called “name” into our own SmartContract, its value will be listed as the collection name on OpenSea!

Adding this line in our own contract and giving it a value in the constructor will make OpenSea listing this value as collection name on our item (x123123 in the following example):

But actually this is a huge problem! This way, all token minted in our SmartContract will be listed under the same collection. But isn’t ERC-1155 called a Multi-Token-Standard? Yes, it is officially per EIP.

What if we want to handle multiple collections in one contract instance each having its own title?

2. Poor Quality on orphaned OpenSea code examples

In order to find a proper solution for the issue listed above at §1, I was taking a deep dive into their documentation and source code of OpenSea custom contract code. At the time of writing, you will find them here and here.

First of all, it is noticeable that the code is already very old: 2 years at the time of writing. That feels like half an eternity in the young NFT universe. They even got in stuck with Solidity at Version 0.5, which is outdated since the very first days of 2020 (although all contracts that was deployed with 0.5 still works thanks to backward compatibility, while they can’t get compiled any more today which I will point out below in the next section)! Today, we have 0.8 which makes a huge difference compared with the old days 2 years ago and should be used as a working template for new projects.

Second, contributions from the community was not approved although they were good explained and easy to understand. All in all: the code examples are orphaned!

Third, the code feels little messed up. It might reflect the code quality on 2 to 3 year old Solidity projects we have seen back then very well, but should not be referenced any more as a good working example, nor are the contracts easy to understand and absolutely not easy to adapt for new projects.

However, we can’t even bring them to work in Summer 2021 as setting up the project already fails at “npm install”:

I spent many hours trying to get it to work somehow and did the following, among other things:

  • updating dependencies
  • fixing code here and there
  • trying older node versions and newer ones
  • changing operating systems between Intel based Macs and ARM based Macs and even on a blank Ubuntu node

Long story short: there is no way any more to bring these examples into life without further investigation and big(!) refactoring. It would be much faster if we start from scratch based on the OpenZeppelin framework.

And here we go.

As I mentioned above, when we import an ERC-721 based contract, it will be listed under a collection-name that was provided by the Name-property of the contract:

However, this means that one deployment of a SmartContract is required per collection, resulting in higher gas costs.

For ERC-1155, we have to add the Name-property by ourself as mentioned above, which ruins the idea of a Multi-Token-Contract totally!

3. The gap between creating a collection on-site on OpenSea and via importing from a contract

For some reasons, OpenSea is distinguishing between those Collections, that was created off-chain on their site directly without any on-chain-operation and between those, that will be imported from an already deployed SmartContract. This occurs regardless of whether the contract is an ERC-721 or an ERC-1155.

As we can see in the screenshot below, after navigating to the menu item “My Collections” on OpenSea store front only this special test collection will be shown that I have created manually on-site. Where are all my other collections that have been imported on-chain from contracts?

Although there are already some collections listed under my account due to imported contracts, they won’t be listed in this section. They will first be listed after we navigate directly into an item of these collections:

4. “There was a problem processing your request”

If nothing else goes wrong, this problem could occur:

This issue has been mentioned several times in Discord and even on GitHub, and after our very first launch of an ERC1155 on Polygon, it affected us as well.

It has something to do how OpenSea is performing gasless transactions and how it is triggering a transfer of a token to a buyer. There are two situations when this issue can happen:

  1. Use menu item “Transfer”
  2. List an item and someone else wants to buy your item

The bad thing is that even if the first case doesn’t happen, the second could be a problem and vice versa. This basically means that you ALWAYS need to test both cases so that you can be sure that everything will work as expected!

The solution is actually simple. We have to override the function isApprovedForAll with the right address to identify OpenSea’s proxy. For ERC721 on Polygon MainNet, this will be at the time of writing: 0x58807baD0B376efc12F5AD86aAc70E78ed67deaE, while for ERC1155 it needs to be 0x207Fa8Df3a17D96Ca7EA4f2893fcdCb78a304101. If you make this wrong, or use OpenSea-Boilerplate-Code from their official ERC1155-Example, it will cause the error mentioned above.

/**
* Override isApprovedForAll to auto-approve OS's proxy contract
*/
function isApprovedForAll(address _owner, address _operator) public view override returns (bool isOperator) {
// if OpenSea's ERC721 Proxy Address is detected, auto-return true
if (_operator == address(0x58807baD0B376efc12F5AD86aAc70E78ed67deaE)) {
return true;
}

// otherwise, use the default ERC721.isApprovedForAll()
return ERC721.isApprovedForAll(_owner, _operator);
}

5. The Multi-Token-Standard without being a Multi-Token-Standard

This section applies not only to OpenSea, but also to all other Ethereum-based NFT marketplaces: they prefer an own Contract instance per listed collection maybe due to technical limitations of the NFT-related ERC-standards.

Even Rarible is following the same approach like OpenSea in order to put a “global” instance variable for the name of the collection on top of an ERC-1155 contract (which should not be there!).

Their source code can be found on GitHub:

Rarible offers an ERC1155Factory contract that enables users to create a custom ERC1155 contract instance for each listed collection. While it’s fine to do this from a technical standpoint, it defeats the purpose of having a scaffold that is supposed to contain multiple tokens and even multiple token types.

Shouldn’t be the name of a token collection be part of the ERC1155-Standard in order to make this standard really workable for NFT marketplaces and to make it really a “Multi-Token-Standard”?

One approach could be to have the collection name as part of the Meta Data. Another approach would be to have a function on the Smart contract that returns the collection name for a particular token or — much better —to offer a separate mapping between token-ID and a Collection-ID that could be configured with its name somewhere else.

Conclusion:

OpenSea works perfectly if one is using their onsite capabilities to mint token on their own platform without any further customisation and without bringing custom contracts into their system.

OpenSea does not play well with imported contracts and handles them as a second class citizen without being listed next to collections that was created directly on-site.

OpenSea does not follow the actual Multi-Token-Standard (ERC-1155) as it requires a global name-property that is not part of the ERC but will be required and will represent the collection name for ALL tokens that are handled by a particular ERC-1155 contract!

OpenSea ignores the fact that ERC-1155 is capable of storing different token types which could represent different collections with own attributes as well.

OpenSea still lacks on good documentation in order to support the ecosystem of developers and agencies around its marketplace. And their official ERC1155-example contains an error which causes transfer and Buying reverting smart-contract-wise (see §4 above).

OpenSea refers everywhere to their Discord server. However, most parts of the Discord server are a haven of impatient “When moon my NFT?” people, while the lonely “Developer” channel is a very frequented place for developers who encounter the same problems and questions I listed in this post, without any support from developers and OpenSea staff themselves.

Recently, OpenSea got funding, right? If I were OpenSea, I would use the money to hire some people to spruce up the code sample repositories and especially to complete the documentation. For the benefit of the ecosystem and for the people who have brought OpenSea to where it is today.

Thanks for reading. Please leave a message with your own experiences in the comments.

--

--