DCIPs/assets/eip-5496/contracts/ERC5496.sol

91 lines
3.3 KiB
Solidity

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./IERC5496.sol";
contract ERC5496 is ERC721, IERC5496 {
struct PrivilegeRecord {
address user;
uint256 expiresAt;
}
struct PrivilegeStorage {
uint lastExpiresAt;
// privId => PrivilegeRecord
mapping(uint => PrivilegeRecord) privilegeEntry;
}
uint public privilegeTotal;
// tokenId => PrivilegeStorage
mapping(uint => PrivilegeStorage) public privilegeBook;
mapping(address => mapping(address => bool)) private privilegeDelegator;
constructor(string memory name_, string memory symbol_)
ERC721(name_,symbol_)
{
}
function setPrivilege(
uint tokenId,
uint privId,
address user,
uint64 expires
) external virtual {
require((hasPrivilege(tokenId, privId, ownerOf(tokenId)) && _isApprovedOrOwner(msg.sender, tokenId)) || _isDelegatorOrHolder(msg.sender, tokenId, privId), "ERC721: transfer caller is not owner nor approved");
require(expires < block.timestamp + 30 days, "expire time invalid");
require(privId < privilegeTotal, "invalid privilege id");
privilegeBook[tokenId].privilegeEntry[privId].user = user;
if (_isApprovedOrOwner(msg.sender, tokenId)) {
privilegeBook[tokenId].privilegeEntry[privId].expiresAt = expires;
if (privilegeBook[tokenId].lastExpiresAt < expires) {
privilegeBook[tokenId].lastExpiresAt = expires;
}
}
emit PrivilegeAssigned(tokenId, privId, user, uint64(privilegeBook[tokenId].privilegeEntry[privId].expiresAt));
}
function hasPrivilege(
uint256 tokenId,
uint256 privId,
address user
) public virtual view returns(bool) {
if ( privilegeBook[tokenId].privilegeEntry[privId].expiresAt >= block.timestamp ){
return privilegeBook[tokenId].privilegeEntry[privId].user == user;
}
return ownerOf(tokenId) == user;
}
function privilegeExpires(
uint256 tokenId,
uint256 privId
) public virtual view returns(uint256){
return privilegeBook[tokenId].privilegeEntry[privId].expiresAt;
}
function _setPrivilegeTotal(
uint total
) internal {
emit PrivilegeTotalChanged(total, privilegeTotal);
privilegeTotal = total;
}
function getPrivilegeInfo(uint tokenId, uint privId) external view returns(address user, uint256 expiresAt) {
return (privilegeBook[tokenId].privilegeEntry[privId].user, privilegeBook[tokenId].privilegeEntry[privId].expiresAt);
}
function setDelegator(address delegator, bool enabled) external {
privilegeDelegator[msg.sender][delegator] = enabled;
}
function _isDelegatorOrHolder(address delegator, uint256 tokenId, uint privId) internal virtual view returns (bool) {
address holder = privilegeBook[tokenId].privilegeEntry[privId].user;
return (delegator == holder || privilegeDelegator[holder][delegator]);
}
function supportsInterface(bytes4 interfaceId) public override virtual view returns (bool) {
return interfaceId == type(IERC5496).interfaceId || super.supportsInterface(interfaceId);
}
}