ERC-6381 Public NFT emote repository (Public Non-Fungible Token Emote Repository)
Final · NFTs & multi-token · created 2023-01-22
A common-good repository contract standard where anyone can leave or withdraw a reaction to any NFT using a Unicode emoji. It is reachable at the same address on every network.
Why it is needed
Today's NFT market relies heavily on the price a token sold for in the past, the floor price, and the rarity information a marketplace provides. There is no real-time indicator of how much affection and attention a particular token is getting right now. Even where reaction features such as likes exist, they are either a proprietary system of a specific vendor or handled off-chain, so they can be manipulated and are hard to trust. When reaction records are scattered across services, it is also hard to build reliable feedback-based features, such as a token that changes once its reactions pass a certain count. This standard provides a common-good repository contract that anyone can use, at the same address on every network, so that anyone can leave a reaction to any NFT with a Unicode emoji and the records can be queried transparently on-chain.
In depth
A common-good repository that uses the same address on every chain
This standard is designed not as an extension attached to individual token contracts but as a single common-good repository that anyone uses. As a result, reactions can be left even on ERC-721 and ERC-1155 tokens issued before the standard. The repository address is computed deterministically and is the same on every chain: 0x31107354b61A0412E722455A771bC462901668eA. The leading part of the address, 0x311073, is meant to spell EMOTE. Integrators do not need to look up an address for each chain and can query the same address.
Emojis are represented by a bytes4 Unicode identifier
A reaction is recorded not as a string or a number but as the bytes4 identifier of a Unicode emoji, because emojis are an established way to convey emotions and impressions. Values that are not in the Unicode standard can also be used, which allows custom emotes, but in that case the display side must know which image to draw, so such a reaction is likely to be recognized only inside the interface or marketplace that created it.
Behavior rules of emote and bulkEmote
emote leaves a reaction when state is true and withdraws it when false. Trying to set the same state again does nothing, and the Emoted event MUST be emitted only when the state actually changes. bulkEmote MUST revert if the collections, tokenIds, emojis, and states arrays have different lengths. Both a single and a bulk function exist so that users can pick the more economical one depending on network gas costs and the number of tokens.
Submitting on someone's behalf with a pre-signature
The message returned by prepareMessageToPresignEmote is built as keccak256(abi.encode(DOMAIN_SEPARATOR, collection, tokenId, emoji, state, deadline)). DOMAIN_SEPARATOR is the keccak256 hash of the abi.encode of "ERC-6381: Public Non-Fungible Token Emote Repository", "1", block.chainid, and address(this), so it differs per chain. Once the party reacting signs this message with ECDSA, someone else can submit it on their behalf through presignedEmote. It MUST revert if the deadline has passed or if the address recovered from the signature is the zero address. Signatures were chosen over a delegate list to reduce complexity, and the feature is intended for uses such as recording on someone's behalf a reaction earned through off-chain activity.
Query functions and the interface identifier
emoteCountOf returns the count of a specific emoji reaction for one token, and bulkEmoteCountOf returns multiple combinations as an array. hasEmoterUsedEmote and haveEmotersUsedEmotes check, for a single case and for multiple cases respectively, whether a specific account has used the given emoji on a specific token. The ERC-165 identifier of this interface is 0xd9fac55a.
Why chain ID is not a parameter
There was a proposal to add a chain ID parameter so that a reaction on one chain could target a token on another chain, but it was not adopted. It would raise the cost of every reaction transaction for a rarely used feature, and marketplaces and wallets would have to query every chain where the repository is deployed to aggregate reactions. The repository does not verify that the target token exists, so reactions can be left on tokens that do not exist yet, and since it is very unlikely that a different collection exists at the same address, a workaround of recording a reaction with a collection address from another chain is also possible.
Things to watch out for
- It does not handle user assets: This proposal is designed not to handle user assets in any form, so assets should not be put at risk when interacting with the repository.
- Defense against signature replay attacks: The DOMAIN_SEPARATOR included in the signed message differs for each chain and repository contract it is deployed to, so a signature made on one chain is invalid on another chain, and the repository MUST revert on an attempted reuse.
- Reuse of pre-signed messages: A signature can be submitted again any number of times until the deadline. Only one reaction with the same emoji is valid per token, so this cannot be used to inflate the reaction count, but for a service that depends on reaction withdrawal, a valid signature could bring a reaction back. The original text recommends keeping the signature validity period reasonably short.
- Beware of unaudited contracts: Take care when dealing with contracts that have not been audited.