Developer notes on @rmrk-team/evm-contracts v2.0.0
Say hello to the second major version of our package! 🥳
Say hello to the second major version of our package! 🥳
The theme of this update is size reduction and simplification. We reduced the sizes of some contracts while keeping all of the functionality and reduced the inheritance depth of the smart contracts. Let's examine how we managed to achieve this!
We reduced the size of the implementation smart contracts by merging multiple smart contracts that all provided essential information about collections. The smart contracts we combined were RMRKCollectionMetadata, RMRKMintingUtils, and parts of RMRKCore. The new smart contract containing these is RMRKImplementationBase.
The implementation smart contracts are opinionated to the extent of being compatible with most wallets and marketplaces; this is why we feel comfortable including the above data.
You might be familiar with our InitData struct that we used to avoid Solidity language's limitation of the number of input variables for a function. Previous packages relied on a single InitData struct which sometimes contained unused data. This meant the redundant variables defined within a struct were using up smart contract's size without bringing any value to the source code.
The redesigned implementation smart contracts now only accept the necessary initialization parameters, which saves a bit of valuable smart contract code space.
Even core non-opinionated implementation sizes were reduced by removing the name and symbol parameters. This makes them even more implementation-agnostic and applicable to more use cases.
All of the ready-to-use implementations received their soulbound, non-transferable counterparts. If you know that you want to build a non-transferable collection, it's now even easier to get started.
Collection data can now be retrieved in batches using the CollectionUtils smart contract. Much like the previous utility smart contracts, this one only needs to be deployed once per network and can be used by anyone with any collection they desire. With its introduction, the getPaginatedMintedIds method was moved from RenderUtils to the CollectionUtils.
The RenderUtils were expanded with two new methods. The getTotalDecendants method retrieves the number of direct child tokens and the ones they own recursively down to every last one. In addition to the total number of children, this method also returns the information on whether the token being checked has more than one level of nesting.
The second new method is hasMoreThanOneLevelOfNesting. It is used to retrieve information about whether any of the given token's child tokens have their own.
A new public-good repository was added to the package. The TokenPropertiesRepository allows for token attributes storage on chain. This allows smart contracts to make decisions based on the token's attributes without relying on off-chain data. The repository contains elaborate access control that can be managed by the collection smart contract owner and allows for either collection issuer, collaborator, token's owner, or a dedicated address to manage specific attributes. Yes, your read that right; the access control can be managed on a per-attribute level.
Another exciting addition to the RMRK smart contract stack is the TokenHolder. This ERC-721 extension allows the token to hold any of the tokens floating around in the chain; ERC-20, ERC-721, and ERC-1155. The tokens held by the TokenHolder don't need to be specially configured to support such a relationship.
The TokenHolder can be utilized for Over-the-Counter trades of tokens or for selling bundles of tokens. A single TokenHolder-powered token can hold any combination of ERC-20, ERC-721, or ERC-1155 tokens. Like the other extensions and standards we published, this one is compatible with all the wallets and marketplaces supporting the ERC-721 standard.
We added network configurations for Sepolia, Polygon Mumbai, Polygon, and Ethereum Mainnet networks to the Harhdat config to keep up to date with the ecosystem and simplify using more networks. As the network is deprecated, we removed the Görli testnet from it.
As mentioned above, the implementation smart contracts have been redesigned to reduce their size and inheritance depth.
Equippable RMRK lego implementations are the ones that consume the most smart contract space. Our MinifiedEquippable is designed to comply with Equippable ERC but doesn't inherit Nestable or MultiAsset; it instead implements all of the functionality on its own. This package version further reduces its size without impacting functionality or security.
The premint implementation smart contracts now require you to pass the token URI when minting tokens. The lazy mint versions rely on enumerable token URIs. The decision was made because you, as the owner, should know the token's metadata before preminting the token, but if the users are minting the tokens, it is easier and safer to use enumerable token URIs which append token ID to the base token URI.
The tokenURI extension has been split into two so that, whether you are using enumerable token URIs or per-token URIs, you shouldn't have wasted smart contract size due to the other implementation.
Split Equippable RMRK lego composite was our effort to provide Equippable capabilities with more space for custom business logic in your smart contracts. With the MinifiedEquippable smart contract significantly reducing the smart contract size, it didn't make sense for us to keep maintaining the additional implementation of the same composite.
During the course of development, we relied on mock smart contracts to be able to test our stack thoroughly. Changes to the package left some of these mocks orphaned. We removed them to reduce the size of the package itself.
We have been using an errors library for a while. It contains all of the errors for our smart contracts. A few of the errors were still defined within the smart contracts. With this update, all remaining errors have been moved into the library.
Implementation smart contracts now support IERC721Metadata. This ensures the proper display of the tokens no matter which wallet you use.
Having released the EmotesRepository public-good repository, we've sunsetted the Emotes extension. Having reactions to tokens gathered in one repository is much more beneficial and valuable than having every collection keep its own records. We realize that specific use cases might need to keep their reaction records separate from the ones granted within marketplaces and emote interfaces. The new version of EmotesRepository allows for this by supporting string-type reactions, meaning you could prepend the Unicode emoji with your collection's symbol or anything else you desire.
You can try out the freely-accessible EmotesRepository at https://emotes.app. The repository has been deployed to Sepolia, Moonbase, Moonriver, and Mumbai testnets and Ethereum Mainnet, Polygon, and Moonbeam main networks.
In addition to the above, the package's dependencies have been updated, the Solidity version has been upgraded to v0.8.21, and test coverage has been increased:
Fixes included in this update improve user experience and eliminate inconsistencies within the smart contracts.
Nested tokens sometimes emitted the root owner in the Transfer event instead of the direct one. This could have been confusing for legacy marketplaces, so the behavior has been modified to conform to their expectations. Now all of the Transfer events emit the direct owner's address.
The owner's balance was reduced before the _beforeTransfer hook was called in some of the smart contracts. This is inconsistent with the hook's expectation because it assumes that the smart contract's state hasn't been updated before it is called. All occurrences of this inconsistency have been remedied in this version.
The previous version of EmotesRespository relied on bytes4 type to store the encoded emoji. While this might have sufficed years ago, it was too small of a storage space to store them now. The addition of skin tones and variations of emojis have bloated their Unicode representation. We updated the emoji storage type to string to be compatible with all current and future Unicode emojis.