DCIPs/assets/eip-3525/contracts/ERC3525.sol

224 lines
8.5 KiB
Solidity

//SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "ERC721Enumerable.sol";
import "./interface/IERC3525.sol";
import "./interface/IERC3525Metadata.sol";
import "./interface/IERC3525Receiver.sol";
contract ERC3525 is IERC3525, IERC3525Metadata, ERC721Enumerable {
using Address for address;
using Strings for uint256;
struct ApproveData {
address[] approvals;
mapping(address => uint256) allowances;
}
/// @dev tokenId => values
mapping(uint256 => uint256) internal _values;
/// @dev tokenId => operator => units
mapping(uint256 => ApproveData) private _approvedValues;
/// @dev tokenId => slot
mapping(uint256 => uint256) internal _slots;
string private _name;
string private _symbol;
uint8 private _decimals;
constructor( string memory name_, string memory symbol_, uint8 decimals_) ERC721(name_, symbol_) {
_decimals = decimals_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721Enumerable) returns (bool)
{
return
interfaceId == type(IERC3525).interfaceId ||
interfaceId == type(IERC3525Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function valueDecimals() public view virtual override returns (uint8) {
return _decimals;
}
function balanceOf(uint256 tokenId_) public view virtual override returns (uint256)
{
require( _exists(tokenId_), "ERC3525: balance query for nonexistent token");
return _values[tokenId_];
}
function slotOf(uint256 tokenId_) public view virtual override returns (uint256)
{
require(_exists(tokenId_), "ERC3525: slot query for nonexistent token");
return _slots[tokenId_];
}
function contractURI() public view virtual override returns (string memory)
{
string memory baseURI = _baseURI();
return
bytes(baseURI).length > 0
? string( abi.encodePacked( baseURI, "contract/", Strings.toHexString(uint256(uint160(address(this)))))) : "";
}
function slotURI(uint256 slot_) public view virtual override returns (string memory)
{
string memory baseURI = _baseURI();
return
bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, "slot/", slot_.toString())) : "";
}
function approve( uint256 tokenId_, address to_, uint256 value_) external payable virtual override {
address owner = ERC721.ownerOf(tokenId_);
require(to_ != owner, "ERC3525: approval to current owner");
require(
ERC721._isApprovedOrOwner(_msgSender(), tokenId_),
"ERC3525: approve caller is not owner nor approved for all"
);
_approveValue(tokenId_, to_, value_);
}
function allowance(uint256 tokenId_, address operator_) public view virtual override returns (uint256)
{
return _approvedValues[tokenId_].allowances[operator_];
}
function transferFrom( uint256 fromTokenId_, address to_, uint256 value_) public payable virtual override returns (uint256) {
_spendAllowance(_msgSender(), fromTokenId_, value_);
uint256 newTokenId = _getNewTokenId(fromTokenId_);
_mint(to_, newTokenId, _slots[fromTokenId_]);
_transfer(fromTokenId_, newTokenId, value_);
return newTokenId;
}
function transferFrom( uint256 fromTokenId_, uint256 toTokenId_, uint256 value_) public payable virtual override {
_spendAllowance(_msgSender(), fromTokenId_, value_);
_transfer(fromTokenId_, toTokenId_, value_);
}
function _mint( address to_, uint256 tokenId_, uint256 slot_) private {
ERC721._mint(to_, tokenId_);
_slots[tokenId_] = slot_;
emit SlotChanged(tokenId_, 0, slot_);
}
function _mintValue( address to_, uint256 tokenId_, uint256 slot_, uint256 value_) internal virtual {
require(to_ != address(0), "ERC3525: mint to the zero address");
require(tokenId_ != 0, "ERC3525: cannot mint zero tokenId");
require(!_exists(tokenId_), "ERC3525: token already minted");
_mint(to_, tokenId_, slot_);
_beforeValueTransfer(address(0), to_, 0, tokenId_, slot_, value_);
_values[tokenId_] = value_;
_afterValueTransfer(address(0), to_, 0, tokenId_, slot_, value_);
emit TransferValue(0, tokenId_, value_);
}
function _burn(uint256 tokenId_) internal virtual override {
address owner = ERC721.ownerOf(tokenId_);
ERC721._burn(tokenId_);
uint256 slot = _slots[tokenId_];
uint256 value = _values[tokenId_];
_beforeValueTransfer(owner, address(0), tokenId_, 0, slot, value);
delete _slots[tokenId_];
delete _values[tokenId_];
_afterValueTransfer(owner, address(0), tokenId_, 0, slot, value);
emit TransferValue(tokenId_, 0, value);
emit SlotChanged(tokenId_, slot, 0);
}
function _transfer( uint256 fromTokenId_, uint256 toTokenId_, uint256 value_) internal virtual {
require( _exists(fromTokenId_),
"ERC35255: transfer from nonexistent token");
require(_exists(toTokenId_), "ERC35255: transfer to nonexistent token");
require( _values[fromTokenId_] >= value_,
"ERC3525: transfer amount exceeds balance");
require( _slots[fromTokenId_] == _slots[toTokenId_],
"ERC3535: transfer to token with different slot");
address from = ERC721.ownerOf(fromTokenId_);
address to = ERC721.ownerOf(toTokenId_);
_beforeValueTransfer(from, to, fromTokenId_, toTokenId_, _slots[fromTokenId_], value_);
_values[fromTokenId_] -= value_;
_values[toTokenId_] += value_;
_afterValueTransfer(from, to, fromTokenId_, toTokenId_, _slots[fromTokenId_], value_);
emit TransferValue(fromTokenId_, toTokenId_, value_);
}
function _spendAllowance( address operator_, uint256 tokenId_, uint256 value_) internal virtual {
uint256 currentAllowance = ERC3525.allowance(tokenId_, operator_);
if ( !_isApprovedOrOwner(operator_, tokenId_) && currentAllowance != type(uint256).max) {
require( currentAllowance >= value_, "ERC3525: insufficient allowance");
_approveValue(tokenId_, operator_, currentAllowance - value_);
}
}
function _approveValue( uint256 tokenId_, address to_, uint256 value_) internal virtual {
ApproveData storage approveData = _approvedValues[tokenId_];
approveData.approvals.push(to_);
approveData.allowances[to_] = value_;
emit ApprovalValue(tokenId_, to_, value_);
}
function _getNewTokenId(uint256 fromTokenId_) internal virtual returns (uint256)
{
return ERC721Enumerable.totalSupply() + 1;
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
//clear approve data
uint256 length = _approvedValues[tokenId].approvals.length;
for (uint256 i = 0; i < length; i++) {
address approval = _approvedValues[tokenId].approvals[i];
delete _approvedValues[tokenId].allowances[approval];
}
delete _approvedValues[tokenId].approvals;
}
function _afterTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {}
function _checkOnERC3525Received( uint256 fromTokenId_, uint256 toTokenId_, uint256 value_, bytes memory data_) private returns (bool) {
address to = ERC721.ownerOf((toTokenId_));
if (to.isContract() && IERC165(to).supportsInterface(type(IERC3525Receiver).interfaceId)) {
try
IERC3525Receiver(to).onERC3525Received( _msgSender(), fromTokenId_, toTokenId_, value_, data_)
returns (bytes4 retval) {
return retval == IERC3525Receiver.onERC3525Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert( "ERC3525: transfer to non ERC3525Receiver implementer");
} else {
// solhint-disable-next-line
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _beforeValueTransfer( address from_, address to_, uint256 fromTokenId_, uint256 toTokenId_, uint256 slot_, uint256 value_) internal virtual {}
function _afterValueTransfer( address from_, address to_, uint256 fromTokenId_, uint256 toTokenId_, uint256 slot_, uint256 value_) internal virtual {}
}