forked from DecentralizedClimateFoundation/DCIPs
128 lines
4.8 KiB
Solidity
128 lines
4.8 KiB
Solidity
|
//SPDX-License-Identifier: CC0-1.0
|
||
|
pragma solidity ^0.8.0;
|
||
|
|
||
|
import "./IERC5489.sol";
|
||
|
|
||
|
contract ERC5489 is IERC5489, ERC721Enumerable, Ownable {
|
||
|
using EnumerableSet for EnumerableSet.AddressSet;
|
||
|
|
||
|
mapping(uint256 => EnumerableSet.AddressSet) tokenId2AuthroizedAddresses;
|
||
|
mapping(uint256 => mapping(address=> string)) tokenId2Address2Value;
|
||
|
mapping(uint256 => string) tokenId2ImageUri;
|
||
|
|
||
|
string private _imageURI;
|
||
|
string private _name;
|
||
|
|
||
|
constructor() ERC721("Hyperlink NFT Collection", "HNFT") {}
|
||
|
|
||
|
modifier onlyTokenOwner(uint256 tokenId) {
|
||
|
require(_msgSender() == ownerOf(tokenId), "should be the token owner");
|
||
|
_;
|
||
|
}
|
||
|
|
||
|
modifier onlySlotManager(uint256 tokenId) {
|
||
|
require(_msgSender() == ownerOf(tokenId) || tokenId2AuthroizedAddresses[tokenId].contains(_msgSender()), "address should be authorized");
|
||
|
_;
|
||
|
}
|
||
|
|
||
|
function setSlotUri(uint256 tokenId, string calldata value) override external onlySlotManager(tokenId) {
|
||
|
tokenId2Address2Value[tokenId][_msgSender()] = value;
|
||
|
|
||
|
emit SlotUriUpdated(tokenId, _msgSender(), value);
|
||
|
}
|
||
|
|
||
|
function getSlotUri(uint256 tokenId, address slotManagerAddr) override external view returns (string memory) {
|
||
|
return tokenId2Address2Value[tokenId][slotManagerAddr];
|
||
|
}
|
||
|
|
||
|
function authorizeSlotTo(uint256 tokenId, address slotManagerAddr) override external onlyTokenOwner(tokenId) {
|
||
|
require(!tokenId2AuthroizedAddresses[tokenId].contains(slotManagerAddr), "address already authorized");
|
||
|
|
||
|
_authorizeSlotTo(tokenId, slotManagerAddr);
|
||
|
}
|
||
|
|
||
|
function _authorizeSlotTo(uint256 tokenId, address slotManagerAddr) private {
|
||
|
tokenId2AuthroizedAddresses[tokenId].add(slotManagerAddr);
|
||
|
emit SlotAuthorizationCreated(tokenId, slotManagerAddr);
|
||
|
}
|
||
|
|
||
|
function revokeAuthorization(uint256 tokenId, address slotManagerAddr) override external onlyTokenOwner(tokenId) {
|
||
|
tokenId2AuthroizedAddresses[tokenId].remove(slotManagerAddr);
|
||
|
delete tokenId2Address2Value[tokenId][slotManagerAddr];
|
||
|
|
||
|
emit SlotAuthorizationRevoked(tokenId, slotManagerAddr);
|
||
|
}
|
||
|
|
||
|
function revokeAllAuthorizations(uint256 tokenId) override external onlyTokenOwner(tokenId) {
|
||
|
for (uint256 i = tokenId2AuthroizedAddresses[tokenId].length() - 1;i > 0; i--) {
|
||
|
address addr = tokenId2AuthroizedAddresses[tokenId].at(i);
|
||
|
tokenId2AuthroizedAddresses[tokenId].remove(addr);
|
||
|
delete tokenId2Address2Value[tokenId][addr];
|
||
|
|
||
|
emit SlotAuthorizationRevoked(tokenId, addr);
|
||
|
}
|
||
|
|
||
|
if (tokenId2AuthroizedAddresses[tokenId].length() > 0) {
|
||
|
address addr = tokenId2AuthroizedAddresses[tokenId].at(0);
|
||
|
tokenId2AuthroizedAddresses[tokenId].remove(addr);
|
||
|
delete tokenId2Address2Value[tokenId][addr];
|
||
|
|
||
|
emit SlotAuthorizationRevoked(tokenId, addr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isSlotManager(uint256 tokenId, address addr) public view returns (bool) {
|
||
|
return tokenId2AuthroizedAddresses[tokenId].contains(addr);
|
||
|
}
|
||
|
|
||
|
// !!expensive, should call only when no gas is needed;
|
||
|
function getSlotManagers(uint256 tokenId) external view returns (address[] memory) {
|
||
|
return tokenId2AuthroizedAddresses[tokenId].values();
|
||
|
}
|
||
|
|
||
|
function _mintToken(uint256 tokenId, string calldata imageUri) private {
|
||
|
_safeMint(msg.sender, tokenId);
|
||
|
tokenId2ImageUri[tokenId] = imageUri;
|
||
|
}
|
||
|
|
||
|
function mint(string calldata imageUri) external {
|
||
|
uint256 tokenId = totalSupply() + 1;
|
||
|
_mintToken(tokenId, imageUri);
|
||
|
}
|
||
|
|
||
|
function mintAndAuthorizeTo(string calldata imageUri, address slotManagerAddr) external {
|
||
|
uint256 tokenId = totalSupply() + 1;
|
||
|
_mintToken(tokenId, imageUri);
|
||
|
_authorizeSlotTo(tokenId, slotManagerAddr);
|
||
|
}
|
||
|
|
||
|
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
|
||
|
require(
|
||
|
_exists(_tokenId),
|
||
|
"URI query for nonexistent token"
|
||
|
);
|
||
|
|
||
|
return
|
||
|
string(
|
||
|
abi.encodePacked(
|
||
|
"data:application/json;base64,",
|
||
|
Base64.encode(
|
||
|
bytes(
|
||
|
abi.encodePacked(
|
||
|
'{"name":"',
|
||
|
abi.encodePacked(
|
||
|
_name,
|
||
|
" # ",
|
||
|
Strings.toString(_tokenId)
|
||
|
),
|
||
|
'",',
|
||
|
'"description":"Hyperlink NFT collection created with Parami Foundation"',
|
||
|
'}'
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|