ERC-6381: Public NFT Emote Repository
ERC-6381 standardises an on-chain emoji-reaction primitive: any user can emote/un-emote on any NFT (across any contract), and the repository tracks the count per (collection, tokenId, emoji). Used for:
- Social NFT discovery — hot NFTs surfaced by reaction count.
- Decentralised "likes" — censorship-resistant reactions outside platform-specific systems.
- Community curation — collective reactions weight indexer ranking algorithms.
- Reputation signals — patterns of emoji usage over a wallet's history form a reputation profile.
Critically, the repository is a separate singleton contract — not part of any NFT contract. NFT collections don't need to opt in; users emote via the repository, which works against any collection / tokenId.
Required Interface
interface IERC6381 {
event Emoted(address indexed emoter, address indexed collection, uint256 indexed tokenId,
string emoji, bool on);
function emote(address collection, uint256 tokenId, string calldata emoji, bool on) external;
function emoteCountOf(address collection, uint256 tokenId, string calldata emoji)
external view returns (uint256);
function hasEmoterUsedEmote(address collection, uint256 tokenId, string calldata emoji,
address emoter) external view returns (bool);
}The emoji is a UTF-8 string (e.g. "👍", "🔥", "❤️"); the on flag toggles emote/un-emote. Counts are aggregated per (collection, tokenId, emoji).
Neo Equivalent: Standalone Repository Contract
The Neo port is a single contract handling reactions across any NEP-11. No coupling to the NFT contracts themselves.
public static void Emote(UInt160 collection, ByteString tokenId, string emoji, bool on)
{
var emoter = Runtime.CallingScriptHash;
if (!Runtime.CheckWitness(emoter)) throw new Exception("EMOTE:NoAuth");
var userKey = UserEmoteKey(collection, tokenId, emoji, emoter);
var countKey = CountKey(collection, tokenId, emoji);
var alreadyEmoted = Storage.Get(Storage.CurrentContext, userKey) is not null;
if (on && !alreadyEmoted)
{
Storage.Put(Storage.CurrentContext, userKey, 1);
var count = (BigInteger)(Storage.Get(Storage.CurrentContext, countKey) ?? ByteString.Empty);
Storage.Put(Storage.CurrentContext, countKey, count + 1);
OnEmoted(emoter, collection, tokenId, emoji, true);
}
else if (!on && alreadyEmoted)
{
Storage.Delete(Storage.CurrentContext, userKey);
var count = (BigInteger)(Storage.Get(Storage.CurrentContext, countKey) ?? ByteString.Empty);
Storage.Put(Storage.CurrentContext, countKey, count - 1);
OnEmoted(emoter, collection, tokenId, emoji, false);
}
}
public static BigInteger EmoteCountOf(UInt160 collection, ByteString tokenId, string emoji)
=> (BigInteger)(Storage.Get(Storage.CurrentContext, CountKey(collection, tokenId, emoji)) ?? ByteString.Empty);| ERC-6381 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
emote(collection, tokenId, emoji, on) | Emote(...) emoter-witness-checked | Toggle pattern |
emoteCountOf(...) | EmoteCountOf(...) view | Direct port |
hasEmoterUsedEmote(...) | HasEmoterUsedEmote(...) view | Direct port |
| Singleton repository | Single deployed contract works for all NEP-11 collections | Same architecture |
Composition
- ERC-7053 — media indexing. Reactions inform indexer-side ranking weights.
- ERC-5023 — shareable NFT. Reactions on shared NFTs aggregate across all holders.
- ERC-5489 — NFT hyperlinks. Hot reactions surface to the top of NFT detail pages.
Migration Notes
For social-NFT platforms:
- Deploy the repository contract once; it serves all NEP-11 NFTs.
- dApps integrate by calling
Emote(...)from the user's wallet and queryingEmoteCountOffor display. - Reaction counts are global — any indexer or marketplace can read them without per-NFT-contract integration.
The on-chain footprint is small per emote (~50 GAS); for high- traffic NFTs (millions of reactions) consider sharding by emoji or introducing a per-NFT subkey rotation strategy.
