164 lines
4.4 KiB
Solidity
164 lines
4.4 KiB
Solidity
|
// SPDX-License-Identifier: CC0-1.0
|
||
|
pragma solidity ^0.8.0;
|
||
|
import "./ERC5007.sol";
|
||
|
import "./IERC5007Composable.sol";
|
||
|
|
||
|
abstract contract ERC5007Composable is ERC5007, IERC5007Composable {
|
||
|
mapping(uint256 => uint256) internal _rootIdMapping;
|
||
|
|
||
|
/**
|
||
|
* @dev See {IERC5007Composable-rootTokenId}.
|
||
|
*/
|
||
|
function rootTokenId(uint256 tokenId)
|
||
|
public
|
||
|
view
|
||
|
override
|
||
|
returns (uint256 rootId)
|
||
|
{
|
||
|
require(_exists(tokenId), "ERC5007: invalid tokenId");
|
||
|
rootId = _rootIdMapping[tokenId];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev See {IERC5007Composable-split}.
|
||
|
*/
|
||
|
function split(
|
||
|
uint256 oldTokenId,
|
||
|
uint256 newTokenId,
|
||
|
address newTokenOwner,
|
||
|
int64 newTokenStartTime
|
||
|
) public virtual override {
|
||
|
require(
|
||
|
_isApprovedOrOwner(_msgSender(), oldTokenId),
|
||
|
"ERC5007: caller is not owner nor approved"
|
||
|
);
|
||
|
|
||
|
int64 oldTokenStartTime = _timeNftMapping[oldTokenId].startTime;
|
||
|
int64 oldTokenEndTime = _timeNftMapping[oldTokenId].endTime;
|
||
|
require(
|
||
|
oldTokenStartTime < newTokenStartTime &&
|
||
|
newTokenStartTime <= oldTokenEndTime,
|
||
|
"ERC5007: invalid newTokenStartTime"
|
||
|
);
|
||
|
|
||
|
_timeNftMapping[oldTokenId].endTime = newTokenStartTime - 1;
|
||
|
int64 newTokenEndTime = oldTokenEndTime;
|
||
|
|
||
|
_mintTimeNftWithRootId(
|
||
|
newTokenOwner,
|
||
|
newTokenId,
|
||
|
_rootIdMapping[oldTokenId],
|
||
|
newTokenStartTime,
|
||
|
newTokenEndTime
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev See {IERC5007Composable-merge}.
|
||
|
*/
|
||
|
function merge(
|
||
|
uint256 firstTokenId,
|
||
|
uint256 secondTokenId,
|
||
|
address newTokenOwner,
|
||
|
uint256 newTokenId
|
||
|
) public virtual {
|
||
|
require(
|
||
|
_isApprovedOrOwner(_msgSender(), firstTokenId) &&
|
||
|
_isApprovedOrOwner(_msgSender(), secondTokenId),
|
||
|
"ERC5007: caller is not owner nor approved"
|
||
|
);
|
||
|
|
||
|
TimeNftInfo memory firstToken = _timeNftMapping[firstTokenId];
|
||
|
TimeNftInfo memory secondToken = _timeNftMapping[secondTokenId];
|
||
|
require(
|
||
|
_rootIdMapping[firstTokenId] == _rootIdMapping[secondTokenId] &&
|
||
|
firstToken.startTime <= firstToken.endTime &&
|
||
|
(firstToken.endTime + 1) == secondToken.startTime &&
|
||
|
secondToken.startTime <= secondToken.endTime,
|
||
|
"ERC5007: invalid input data"
|
||
|
);
|
||
|
|
||
|
|
||
|
_mintTimeNftWithRootId(
|
||
|
newTokenOwner,
|
||
|
newTokenId,
|
||
|
_rootIdMapping[firstTokenId],
|
||
|
firstToken.startTime,
|
||
|
secondToken.endTime
|
||
|
);
|
||
|
|
||
|
_burn(firstTokenId);
|
||
|
_burn(secondTokenId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev mint a new common time NFT
|
||
|
*
|
||
|
* Requirements:
|
||
|
*
|
||
|
* - `to_` cannot be the zero address.
|
||
|
* - `tokenId_` must not exist.
|
||
|
* - `rootId_` must exist.
|
||
|
* - `endTime_` should be equal or greater than `startTime_`
|
||
|
*/
|
||
|
function _mintTimeNftWithRootId(
|
||
|
address to_,
|
||
|
uint256 tokenId_,
|
||
|
uint256 rootId_,
|
||
|
int64 startTime_,
|
||
|
int64 endTime_
|
||
|
) internal virtual {
|
||
|
require(_exists(rootId_), "ERC5007: invalid rootId_");
|
||
|
super._mintTimeNft(to_, tokenId_, startTime_, endTime_);
|
||
|
_rootIdMapping[tokenId_] = rootId_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev mint a new common time NFT
|
||
|
*
|
||
|
* Requirements:
|
||
|
*
|
||
|
* - `to_` cannot be the zero address.
|
||
|
* - `tokenId_` must not exist.
|
||
|
* - `endTime_` should be equal or greater than `startTime_`
|
||
|
*/
|
||
|
function _mintTimeNft(
|
||
|
address to_,
|
||
|
uint256 tokenId_,
|
||
|
int64 startTime_,
|
||
|
int64 endTime_
|
||
|
) internal virtual override {
|
||
|
super._mintTimeNft(to_, tokenId_, startTime_, endTime_);
|
||
|
|
||
|
_rootIdMapping[tokenId_] = tokenId_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev Destroys `tokenId`.
|
||
|
*
|
||
|
* Requirements:
|
||
|
*
|
||
|
* - `tokenId` must exist.
|
||
|
*
|
||
|
*/
|
||
|
function _burn(uint256 tokenId) internal virtual override {
|
||
|
super._burn(tokenId);
|
||
|
delete _rootIdMapping[tokenId];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dev See {IERC165-supportsInterface}.
|
||
|
*/
|
||
|
function supportsInterface(bytes4 interfaceId)
|
||
|
public
|
||
|
view
|
||
|
virtual
|
||
|
override
|
||
|
returns (bool)
|
||
|
{
|
||
|
return
|
||
|
interfaceId == type(IERC5007Composable).interfaceId ||
|
||
|
super.supportsInterface(interfaceId);
|
||
|
}
|
||
|
}
|