Table of contents
NFT Marketplace is probably one those projects which every beginner makes. It is the Todo App equivalent of Web3. An NFT marketplace encompasses a whole of topics which prove grasp over the development of Dapps - from designing custom ERC20 contract to act as currency to Cross-contract invocations. It has become one of the unspoken standard in Web3 interviews now-a-days.
Earlier this year we saw the rise of pixelated images with no intrinsic value and we saw millionaires being made out of them. NFTs certainly have become a household name. Who's who of IT firms and franchises considering launching their own NFT collections representing their Brand.
Amidst all these there is one of those small things which has gone unnoticed by many developers and that's the introduction of Royalty standards. At the time of writing NFT Royalty Standard is almost 2 years old.
Most of the time you might find people using Opensea or even building their own marketplace by forking Seaport. But once in a while, there comes one of those hardcore developers who likes to do it from scratch. This article is supposed to help Web3 developers (both budding and unaware veterans) get to know about it. It is no way advocating the inclusion of ERC-2981 in interview questions, lol.
The Premise
If you think about it, NFT Royalty Standard ain't any cutting edge opinionated idea like fractional NFTs and NFT liquidity. It is rather a simple argument - if you are designing a Marketplace for others, what incentive would they have to sell their NFTs on it?
I mean sure, they could sell it off initially and that's that. But what happens if that token appreciates in value ridiculously and the original creator gets to see none of the big bucks? The NFT Royalty Standard is, thus, like a tribute to the content creators and helps them get a piece of the action every time their work changes hands. It, in a way, becomes a source of passive income. Now something of that sort is a feature that would make NFT marketplaces really lucrative.
Itsy-bitsy code...
At the time of writing, Openzeppelin already has a solid implementation of ERC-2981 though it might not be listed on the Documentation directly. It provides the service as an extension though. You can browse the code for the Openzeppelin implementation of NFT Royalty Standard on Github.
Funnily enough a standard like only introduces one method - royaltyInfo(uint256 tokenId, uint256 salePrice)
. This is meant to give the amount of fungible tokens that need to be transferred to the original creator as a royalty.
You can try to have your own implementation of ERC-2981 by using just the ERC-2981 Interface provided by Openzeppelin. But it is always wiser to use the wheel to build a car than to rediscover the wheel.
The idea presented by the code is simple - you maintain a mapping from an NFT's Id and the address to send the royalty to along with the fraction of the original price that constitutes the royalty. That is what the code below from the ERC2981 contract does.
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo private _defaultRoyaltyInfo;
mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
Keep in mind that the above code is part of an abstract contract. So you will have to implement any missing methods on your own (or you could go for the ERC721 extension).
Everytime the royaltyInfo(uint256 _tokenId, uint256 _salePrice)
function gets called, it just performs a lookup in the _tokenRoyaltyInfo
mapping and calculates what fraction of _salePrice
will be sent to the address recorded as the receiver in the struct inside the above codeblock.
The _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
function can be used to manually set the royalty fraction for a specific token.
If you want to have a fixed royalty fraction for all tokens, then just don't touch this function. Instead just set it up using the _setDefaultRoyalty(address receiver, uint96 feeNumerator)
. Keep in mind that the receiver
argument essentially does not matter here. Every solidity developer should know that any uninitialized address variable will have a default value of Zero address (0x0
). The royaltyInfo(uint256 _tokenId, uint256 _salePrice)
function in the contract checks if there is any address other than the Zero address specified as the receiver for the given tokenId
. In case there is no address, it automatically uses the default fraction value to calculate the NFT's Royalty.
Keep in mind that this contract only calculates the royalty. It does not send the tokens to the content creator/receiver. You need to handle that logic manually in your contract during the NFT transfer.
Conclusion
Easy, right? ERC-2981 ain't that tough. But the idea it introduced in the ecosystem is what differentiates common NFT marketplaces from the good ones and is one of those dark horses which determine who leads the adoption race. Next time you find yourself implementing a Marketplace, consider adding this recipe to it.
That's it for this article. If you think I missed something, feel free to point it out in the comments below. If you think there is something that warrants discussion, leave comments. If you liked reading the article, consider leaving a reaction and subscribing to the newsletter for more such content. Until next time, keep building awesome stuff on Web3 and WAGMI!