148 lines
4.3 KiB
Solidity
148 lines
4.3 KiB
Solidity
|
// 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;
|
||
|
}
|
||
|
}
|