Here’s an untested implementation:
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;
interface IERC5114 {
event Mint(uint256 indexed badgeId, address indexed nftAddress, uint256 indexed nftTokenId);
function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId);
function collectionUri() external pure returns (string memory collectionUri);
function badgeUri(uint256 badgeId) external view returns (string memory badgeUri);
function metadataFormat() external pure returns (string memory format);
}
contract Reactions is IERC5114 {
event Reacted(
address indexed emoter,
address indexed nftAddress,
uint256 indexed nftTokenId,
bytes4 emoji,
bool enabled
);
struct Reaction {
address operator;
address nftAddress;
uint256 nftTokenId;
bytes4 codepoint;
bool enabled;
}
mapping (uint256 => Reaction) private _reactions;
// (nftAddress => (nftTokenId => (codepoint => count )))
mapping (address => mapping (uint256 => mapping(bytes4 => uint256))) private _counts;
/*
* IERC5114 Implementation
*/
string constant public metadataFormat = "TODO";
string constant public collectionUri = "TODO";
function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId) {
Reaction storage reaction = _reactions[badgeId];
require(address(0) != reaction.operator);
return (reaction.nftAddress, reaction.nftTokenId);
}
function badgeUri(uint256 badgeId) external view returns (string memory) {
require(address(0) != _reactions[badgeId].operator);
return "TODO";
}
/*
* IToggleable Implementation
*/
function isEnabled(uint256 badgeId) external view returns (bool) {
Reaction storage reaction = _reactions[badgeId];
require(address(0) != reaction.operator);
return reaction.enabled;
}
/*
* Reactions Implementation
*/
function id(
address operator,
address nftAddress,
uint256 nftTokenId,
bytes4 codepoint
) public pure returns (uint256) {
return uint256(keccak256(abi.encode(operator, nftAddress, nftTokenId, codepoint)));
}
function react(
address nftAddress,
uint256 nftTokenId,
bytes4 codepoint,
bool enabled
) external returns (uint256) {
uint256 badgeId = id(msg.sender, nftAddress, nftTokenId, codepoint);
Reaction storage reaction = _reactions[badgeId];
if (reaction.enabled == enabled) {
return badgeId;
}
if (address(0) == reaction.operator) {
reaction.operator = msg.sender;
reaction.nftAddress = nftAddress;
reaction.nftTokenId = nftTokenId;
reaction.codepoint = codepoint;
emit Mint(badgeId, nftAddress, nftTokenId);
}
if (enabled) {
_counts[nftAddress][nftTokenId][codepoint] += 1;
} else {
_counts[nftAddress][nftTokenId][codepoint] -= 1;
}
reaction.enabled = enabled;
emit Reacted(
msg.sender,
nftAddress,
nftTokenId,
codepoint,
enabled
);
return badgeId;
}
function reactionCountOf(
address nftAddress,
uint256 nftTokenId,
bytes4 codepoint
) public view returns (uint256) {
return _counts[nftAddress][nftTokenId][codepoint];
}
}