DCIPs/assets/eip-5006/contracts/ERC5006.sol

148 lines
4.3 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./IERC5006.sol";
contract ERC5006 is ERC1155, ERC1155Receiver, IERC5006 {
using EnumerableSet for EnumerableSet.UintSet;
mapping(uint256 => mapping(address => uint256)) private _frozens;
mapping(uint256 => UserRecord) private _records;
mapping(uint256 => mapping(address => EnumerableSet.UintSet))
private _userRecordIds;
uint256 _curRecordId;
uint256 recordLimit;
constructor(string memory uri_, uint256 recordLimit_) ERC1155(uri_) {
recordLimit = recordLimit_;
}
function isOwnerOrApproved(address owner) public view returns (bool) {
require(
owner == msg.sender || isApprovedForAll(owner, msg.sender),
"only owner or approved"
);
return true;
}
function usableBalanceOf(address account, uint256 tokenId)
public
view
override
returns (uint256 amount)
{
uint256[] memory recordIds = _userRecordIds[tokenId][account].values();
for (uint256 i = 0; i < recordIds.length; i++) {
if (block.timestamp <= _records[recordIds[i]].expiry) {
amount += _records[recordIds[i]].amount;
}
}
}
function frozenBalanceOf(address account, uint256 tokenId)
public
view
override
returns (uint256)
{
return _frozens[tokenId][account];
}
function userRecordOf(uint256 recordId)
public
view
override
returns (UserRecord memory)
{
return _records[recordId];
}
function createUserRecord(
address owner,
address user,
uint256 tokenId,
uint64 amount,
uint64 expiry
) public override returns (uint256) {
require(isOwnerOrApproved(owner));
require(user != address(0), "user cannot be the zero address");
require(amount > 0, "amount must be greater than 0");
require(expiry > block.timestamp, "expiry must after the block timestamp");
require(
_userRecordIds[tokenId][user].length() < recordLimit,
"user cannot have more records"
);
_safeTransferFrom(owner, address(this), tokenId, amount, "");
_frozens[tokenId][owner] += amount;
_curRecordId++;
_records[_curRecordId] = UserRecord(
tokenId,
owner,
amount,
user,
expiry
);
_userRecordIds[tokenId][user].add(_curRecordId);
emit CreateUserRecord(
_curRecordId,
tokenId,
amount,
owner,
user,
expiry
);
return _curRecordId;
}
function deleteUserRecord(uint256 recordId) public override {
UserRecord storage _record = _records[recordId];
require(isOwnerOrApproved(_record.owner));
_safeTransferFrom(
address(this),
_record.owner,
_record.tokenId,
_record.amount,
""
);
_frozens[_record.tokenId][_record.owner] -= _record.amount;
_userRecordIds[_record.tokenId][_record.user].remove(recordId);
delete _records[recordId];
emit DeleteUserRecord(recordId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC1155, ERC1155Receiver)
returns (bool)
{
return
interfaceId == type(IERC5006).interfaceId ||
ERC1155.supportsInterface(interfaceId) ||
ERC1155Receiver.supportsInterface(interfaceId);
}
function onERC1155Received(
address operator,
address from,
uint256 tokenId,
uint256 value,
bytes calldata data
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155BatchReceived.selector;
}
}