❗️ ERC-7409 supersedes ERC-6381. ❗️
The Public Non-Fungible Tokens Emote Repository standard provides an enhanced interactive utility for ERC-721 and ERC-1155 by allowing NFTs to be emoted at.
This proposal introduces the ability to react to NFTs using Unicode standardized emoji in a public non-gated repository smart contract that is accessible at the same address in all of the networks.
With NFTs being a widespread form of tokens in the Ethereum ecosystem and being used for a variety of use cases, it is time to standardize additional utility for them. Having the ability for anyone to interact with an NFT introduces an interactive aspect to owning an NFT and unlocks feedback-based NFT mechanics.
This ERC introduces new utilities for ERC-721 based tokens in the following areas:
This proposal fixes the compatibility issue in the ERC-6381 interface specification, where emojis are represented using bytes4
values. The introduction of variation flags and emoji skin tones has rendered the bytes4
namespace insufficient for representing all possible emojis, so the new standard used string
instead. Apart from this fix, this proposal is functionally equivalent to ERC-6381.
The ability to emote on an NFT introduces the aspect of interactivity to owning an NFT. This can either reflect the admiration for the emoter (person emoting to an NFT) or can be a result of a certain action performed by the token's owner. Accumulating emotes on a token can increase its uniqueness and/or value.
Standardized on-chain reactions to NFTs allow for feedback based evolution.
Current solutions are either proprietary or off-chain and therefore subject to manipulation and distrust. Having the ability to track the interaction on-chain allows for trust and objective evaluation of a given token. Designing the tokens to evolve when certain emote thresholds are met incentivizes interaction with the token collection.
Current NFT market heavily relies on previous values the token has been sold for, the lowest price of the listed token and the scarcity data provided by the marketplace. There is no real time indication of admiration or desirability of a specific token. Having the ability for users to emote to the tokens adds the possibility of potential buyers and sellers gauging the value of the token based on the impressions the token has collected.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
/// @title ERC-7409 Emotable Extension for Non-Fungible Tokens
/// @dev See https://eips.ethereum.org/EIPS/eip-7409
/// @dev Note: the ERC-165 identifier for this interface is 0x1b3327ab.
pragma solidity ^0.8.16;
interface IERC7409 /*is IERC165*/ {
/**
* @notice Used to notify listeners that the token with the specified ID has been emoted to or that the reaction has been revoked.
* @dev The event MUST only be emitted if the state of the emote is changed.
* @param emoter Address of the account that emoted or revoked the reaction to the token
* @param collection Address of the collection smart contract containing the token being emoted to or having the reaction revoked
* @param tokenId ID of the token
* @param emoji Unicode identifier of the emoji
* @param on Boolean value signifying whether the token was emoted to (`true`) or if the reaction has been revoked (`false`)
*/
event Emoted(
address indexed emoter,
address indexed collection,
uint256 indexed tokenId,
string emoji,
bool on
);
/**
* @notice Used to get the number of emotes for a specific emoji on a token.
* @param collection Address of the collection containing the token being checked for emoji count
* @param tokenId ID of the token to check for emoji count
* @param emoji Unicode identifier of the emoji
* @return Number of emotes with the emoji on the token
*/
function emoteCountOf(
address collection,
uint256 tokenId,
string memory emoji
) external view returns (uint256);
/**
* @notice Used to get the number of emotes for a specific emoji on a set of tokens.
* @param collections An array of addresses of the collections containing the tokens being checked for emoji count
* @param tokenIds An array of IDs of the tokens to check for emoji count
* @param emojis An array of unicode identifiers of the emojis
* @return An array of numbers of emotes with the emoji on the tokens
*/
function bulkEmoteCountOf(
address[] memory collections,
uint256[] memory tokenIds,
string[] memory emojis
) external view returns (uint256[] memory);
/**
* @notice Used to get the information on whether the specified address has used a specific emoji on a specific
* token.
* @param emoter Address of the account we are checking for a reaction to a token
* @param collection Address of the collection smart contract containing the token being checked for emoji reaction
* @param tokenId ID of the token being checked for emoji reaction
* @param emoji The ASCII emoji code being checked for reaction
* @return A boolean value indicating whether the `emoter` has used the `emoji` on the token (`true`) or not
* (`false`)
*/
function hasEmoterUsedEmote(
address emoter,
address collection,
uint256 tokenId,
string memory emoji
) external view returns (bool);
/**
* @notice Used to get the information on whether the specified addresses have used specific emojis on specific
* tokens.
* @param emoters An array of addresses of the accounts we are checking for reactions to tokens
* @param collections An array of addresses of the collection smart contracts containing the tokens being checked
* for emoji reactions
* @param tokenIds An array of IDs of the tokens being checked for emoji reactions
* @param emojis An array of the ASCII emoji codes being checked for reactions
* @return An array of boolean values indicating whether the `emoter`s has used the `emoji`s on the tokens (`true`)
* or not (`false`)
*/
function haveEmotersUsedEmotes(
address[] memory emoters,
address[] memory collections,
uint256[] memory tokenIds,
string[] memory emojis
) external view returns (bool[] memory);
/**
* @notice Used to get the message to be signed by the `emoter` in order for the reaction to be submitted by someone
* else.
* @param collection The address of the collection smart contract containing the token being emoted at
* @param tokenId ID of the token being emoted
* @param emoji Unicode identifier of the emoji
* @param state Boolean value signifying whether to emote (`true`) or undo (`false`) emote
* @param deadline UNIX timestamp of the deadline for the signature to be submitted
* @return The message to be signed by the `emoter` in order for the reaction to be submitted by someone else
*/
function prepareMessageToPresignEmote(
address collection,
uint256 tokenId,
string memory emoji,
bool state,
uint256 deadline
) external view returns (bytes32);
/**
* @notice Used to get multiple messages to be signed by the `emoter` in order for the reaction to be submitted by someone
* else.
* @param collections An array of addresses of the collection smart contracts containing the tokens being emoted at
* @param tokenIds An array of IDs of the tokens being emoted
* @param emojis An array of unicode identifiers of the emojis
* @param states An array of boolean values signifying whether to emote (`true`) or undo (`false`) emote
* @param deadlines An array of UNIX timestamps of the deadlines for the signatures to be submitted
* @return The array of messages to be signed by the `emoter` in order for the reaction to be submitted by someone else
*/
function bulkPrepareMessagesToPresignEmote(
address[] memory collections,
uint256[] memory tokenIds,
string[] memory emojis,
bool[] memory states,
uint256[] memory deadlines
) external view returns (bytes32[] memory);
/**
* @notice Used to emote or undo an emote on a token.
* @dev Does nothing if attempting to set a pre-existent state.
* @dev MUST emit the `Emoted` event is the state of the emote is changed.
* @param collection Address of the collection containing the token being emoted at
* @param tokenId ID of the token being emoted
* @param emoji Unicode identifier of the emoji
* @param state Boolean value signifying whether to emote (`true`) or undo (`false`) emote
*/
function emote(
address collection,
uint256 tokenId,
string memory emoji,
bool state
) external;
/**
* @notice Used to emote or undo an emote on multiple tokens.
* @dev Does nothing if attempting to set a pre-existent state.
* @dev MUST emit the `Emoted` event is the state of the emote is changed.
* @dev MUST revert if the lengths of the `collections`, `tokenIds`, `emojis` and `states` arrays are not equal.
* @param collections An array of addresses of the collections containing the tokens being emoted at
* @param tokenIds An array of IDs of the tokens being emoted
* @param emojis An array of unicode identifiers of the emojis
* @param states An array of boolean values signifying whether to emote (`true`) or undo (`false`) emote
*/
function bulkEmote(
address[] memory collections,
uint256[] memory tokenIds,
string[] memory emojis,
bool[] memory states
) external;
/**
* @notice Used to emote or undo an emote on someone else's behalf.
* @dev Does nothing if attempting to set a pre-existent state.
* @dev MUST emit the `Emoted` event is the state of the emote is changed.
* @dev MUST revert if the lengths of the `collections`, `tokenIds`, `emojis` and `states` arrays are not equal.
* @dev MUST revert if the `deadline` has passed.
* @dev MUST revert if the recovered address is the zero address.
* @param emoter The address that presigned the emote
* @param collection The address of the collection smart contract containing the token being emoted at
* @param tokenId IDs of the token being emoted
* @param emoji Unicode identifier of the emoji
* @param state Boolean value signifying whether to emote (`true`) or undo (`false`) emote
* @param deadline UNIX timestamp of the deadline for the signature to be submitted
* @param v `v` value of an ECDSA signature of the message obtained via `prepareMessageToPresignEmote`
* @param r `r` value of an ECDSA signature of the message obtained via `prepareMessageToPresignEmote`
* @param s `s` value of an ECDSA signature of the message obtained via `prepareMessageToPresignEmote`
*/
function presignedEmote(
address emoter,
address collection,
uint256 tokenId,
string memory emoji,
bool state,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Used to bulk emote or undo an emote on someone else's behalf.
* @dev Does nothing if attempting to set a pre-existent state.
* @dev MUST emit the `Emoted` event is the state of the emote is changed.
* @dev MUST revert if the lengths of the `collections`, `tokenIds`, `emojis` and `states` arrays are not equal.
* @dev MUST revert if the `deadline` has passed.
* @dev MUST revert if the recovered address is the zero address.
* @param emoters An array of addresses of the accounts that presigned the emotes
* @param collections An array of addresses of the collections containing the tokens being emoted at
* @param tokenIds An array of IDs of the tokens being emoted
* @param emojis An array of unicode identifiers of the emojis
* @param states An array of boolean values signifying whether to emote (`true`) or undo (`false`) emote
* @param deadlines UNIX timestamp of the deadline for the signature to be submitted
* @param v An array of `v` values of an ECDSA signatures of the messages obtained via `prepareMessageToPresignEmote`
* @param r An array of `r` values of an ECDSA signatures of the messages obtained via `prepareMessageToPresignEmote`
* @param s An array of `s` values of an ECDSA signatures of the messages obtained via `prepareMessageToPresignEmote`
*/
function bulkPresignedEmote(
address[] memory emoters,
address[] memory collections,
uint256[] memory tokenIds,
string[] memory emojis,
bool[] memory states,
uint256[] memory deadlines,
uint8[] memory v,
bytes32[] memory r,
bytes32[] memory s
) external;
}
The message to be signed by the emoter
in order for the reaction to be submitted by someone else is formatted as follows:
keccak256(
abi.encode(
DOMAIN_SEPARATOR,
collection,
tokenId,
emoji,
state,
deadline
)
);
The values passed when generating the message to be signed are:
DOMAIN_SEPARATOR
- The domain separator of the Emotable repository smart contractcollection
- Address of the collection containing the token being emoted attokenId
- ID of the token being emotedemoji
- Unicode identifier of the emojistate
- Boolean value signifying whether to emote (true
) or undo (false
) emotedeadline
- UNIX timestamp of the deadline for the signature to be submittedThe DOMAIN_SEPARATOR
is generated as follows:
keccak256(
abi.encode(
"ERC-7409: Public Non-Fungible Token Emote Repository",
"1",
block.chainid,
address(this)
)
);
Each chain, that the Emotable repository smart contract is deployed on, will have a different DOMAIN_SEPARATOR
value due to chain IDs being different.
The address of the Emotable repository smart contract is designed to resemble the function it serves. It starts with 0x3110735
which is the abstract representation of EMOTES
. The address is:
0x3110735F0b8e71455bAe1356a33e428843bCb9A1
Designing the proposal, we considered the following questions:
string
value. This means that while we encourage implementers to add the reactions using standardized emojis, the values not covered by the Unicode standard can be used for custom emotes. The only drawback being that the interface displaying the reactions will have to know what kind of image to render and such additions will probably be limited to the interface or marketplace in which they were made.bytes4
or strings
to represent emotes?\
Initially the proposal used bytes4
. This was due to the assumption that all of the emojis use UTF-4 encoding, which is not the case.\
The emojis usually use up to 8 bytes, but the personalized emojis with skin tones use much more. This is why we decided to use strings
to represent the emotes. This allows the repository to be forward compatible with any future emojis that might be added to the Unicode standard.\
This is how this proposal fixes the forward compatibility issue of the ERC-6381.The Emote repository standard is fully compatible with ERC-721 and with the robust tooling available for implementations of ERC-721 as well as with the existing ERC-721 infrastructure.
Tests are included in emotableRepository.ts
.
To run them in terminal, you can use the following commands:
cd ../static/assets/eip-7409
npm install
npx hardhat test
The proposal does not envision handling any form of assets from the user, so the assets should not be at risk when interacting with an Emote repository.
The ability to use ECDSA signatures to emote on someone else's behalf introduces the risk of a replay attack, which the format of the message to be signed guards against. The DOMAIN_SEPARATOR
used in the message to be signed is unique to the repository smart contract of the chain it is deployed on. This means that the signature is invalid on any other chain and the Emote repositories deployed on them should revert the operation if a replay attack is attempted.
Another thing to consider is the ability of presigned message reuse. Since the message includes the signature validity deadline, the message can be reused any number of times before the deadline is reached. The proposal only allows for a single reaction with a given emoji to a specific token to be active, so the presigned message can not be abused to increase the reaction count on the token. However, if the service using the repository relies on the ability to revoke the reaction after certain actions, a valid presigned message can be used to re-react to the token. We suggest that the services using the repository in conjunction with presigned messages use deadlines that invalidate presigned messages after a reasonably short period of time.
Caution is advised when dealing with non-audited contracts.
Copyright and related rights waived via CC0.