/* ==================================================================== */
/* Copyright (c) 2018 The TokenTycoon Project. All rights reserved.
/*
/* https://tokentycoon.io
/*
/* authors rickhunter.shen@gmail.com
/* ssesunding@gmail.com
/* ==================================================================== */
pragma solidity ^0.4.23;
/// @title ERC-165 Standard Interface Detection
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
interface ERC165 {
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
contract ERC721 is ERC165 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
function transferFrom(address _from, address _to, uint256 _tokenId) external;
function approve(address _approved, uint256 _tokenId) public;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
/// @title ERC-721 Non-Fungible Token Standard
interface ERC721TokenReceiver {
function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
interface ERC721Metadata /* is ERC721 */ {
function name() external pure returns (string _name);
function symbol() external pure returns (string _symbol);
function tokenURI(uint256 _tokenId) external view returns (string);
}
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
interface ERC721Enumerable /* is ERC721 */ {
function totalSupply() external view returns (uint256);
function tokenByIndex(uint256 _index) external view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
interface ERC721MetadataProvider {
function tokenURI(uint256 _tokenId) external view returns (string);
}
contract AccessAdmin {
bool public isPaused = false;
address public addrAdmin;
event AdminTransferred(address indexed preAdmin, address indexed newAdmin);
constructor() public {
addrAdmin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == addrAdmin);
_;
}
modifier whenNotPaused() {
require(!isPaused);
_;
}
modifier whenPaused {
require(isPaused);
_;
}
function setAdmin(address _newAdmin) external onlyAdmin {
require(_newAdmin != address(0));
emit AdminTransferred(addrAdmin, _newAdmin);
addrAdmin = _newAdmin;
}
function doPause() external onlyAdmin whenNotPaused {
isPaused = true;
}
function doUnpause() external onlyAdmin whenPaused {
isPaused = false;
}
}
interface TokenRecipient {
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external;
}
contract ManagerToken is ERC721, ERC721Metadata, ERC721Enumerable, AccessAdmin {
/// @dev All manangers array(tokenId => gene)
uint256[] public managerArray;
/// @dev Mananger tokenId vs owner address
mapping (uint256 => address) tokenIdToOwner;
/// @dev Manangers owner by the owner (array)
mapping (address => uint256[]) ownerToManagerArray;
/// @dev Mananger token ID search in owner array
mapping (uint256 => uint256) tokenIdToOwnerIndex;
/// @dev The authorized address for each TTM
mapping (uint256 => address) tokenIdToApprovals;
/// @dev The authorized operators for each address
mapping (address => mapping (address => bool)) operatorToApprovals;
/// @dev Trust contract
mapping (address => bool) safeContracts;
/// @dev Metadata provider
ERC721MetadataProvider public providerContract;
/// @dev This emits when the approved address for an TTM is changed or reaffirmed.
event Approval
(
address indexed _owner,
address indexed _approved,
uint256 _tokenId
);
/// @dev This emits when an operator is enabled or disabled for an owner.
event ApprovalForAll
(
address indexed _owner,
address indexed _operator,
bool _approved
);
/// @dev This emits when the equipment ownership changed
event Transfer
(
address indexed from,
address indexed to,
uint256 tokenId
);
constructor() public {
addrAdmin = msg.sender;
managerArray.length += 1;
}
// modifier
/// @dev Check if token ID is valid
modifier isValidToken(uint256 _tokenId) {
require(_tokenId >= 1 && _tokenId <= managerArray.length, "TokenId out of range");
require(tokenIdToOwner[_tokenId] != address(0), "Token have no owner");
_;
}
modifier canTransfer(uint256 _tokenId) {
address owner = tokenIdToOwner[_tokenId];
require(msg.sender == owner || msg.sender == tokenIdToApprovals[_tokenId] || operatorToApprovals[owner][msg.sender], "Can not transfer");
_;
}
// ERC721
function supportsInterface(bytes4 _interfaceId) external view returns(bool) {
// ERC165 || ERC721 || ERC165^ERC721
return (_interfaceId == 0x01ffc9a7 || _interfaceId == 0x80ac58cd || _interfaceId == 0x8153916a) && (_interfaceId != 0xffffffff);
}
function name() public pure returns(string) {
return "Token Tycoon Managers";
}
function symbol() public pure returns(string) {
return "TTM";
}
function tokenURI(uint256 _tokenId) external view returns (string) {
if (address(providerContract) == address(0)) {
return "";
}
return providerContract.tokenURI(_tokenId);
}
/// @dev Search for token quantity address
/// @param _owner Address that needs to be searched
/// @return Returns token quantity
function balanceOf(address _owner) external view returns(uint256) {
require(_owner != address(0), "Owner is 0");
return ownerToManagerArray[_owner].length;
}
/// @dev Find the owner of an TTM
/// @param _tokenId The tokenId of TTM
/// @return Give The address of the owner of this TTM
function ownerOf(uint256 _tokenId) external view returns (address owner) {
return tokenIdToOwner[_tokenId];
}
/// @dev Transfers the ownership of an TTM from one address to another address
/// @param _from The current owner of the TTM
/// @param _to The new owner
/// @param _tokenId The TTM to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)
external
whenNotPaused
{
_safeTransferFrom(_from, _to, _tokenId, data);
}
/// @dev Transfers the ownership of an TTM from one address to another address
/// @param _from The current owner of the TTM
/// @param _to The new owner
/// @param _tokenId The TTM to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId)
external
whenNotPaused
{
_safeTransferFrom(_from, _to, _tokenId, "");
}
/// @dev Transfer ownership of an TTM, '_to' must be a vaild address, or the TTM will lost
/// @param _from The current owner of the TTM
/// @param _to The new owner
/// @param _tokenId The TTM to transfer
function transferFrom(address _from, address _to, uint256 _tokenId)
external
whenNotPaused
isValidToken(_tokenId)
canTransfer(_tokenId)
{
address owner = tokenIdToOwner[_tokenId];
require(owner != address(0), "Owner is 0");
require(_to != address(0), "Transfer target address is 0");
require(owner == _from, "Transfer to self");
_transfer(_from, _to, _tokenId);
}
/// @dev Set or reaffirm the approved address for an TTM
/// @param _approved The new approved TTM controller
/// @param _tokenId The TTM to approve
function approve(address _approved, uint256 _tokenId) public whenNotPaused {
address owner = tokenIdToOwner[_tokenId];
require(owner != address(0));
require(msg.sender == owner || operatorToApprovals[owner][msg.sender]);
tokenIdToApprovals[_tokenId] = _approved;
emit Approval(owner, _approved, _tokenId);
}
/// @dev Enable or disable approval for a third party ("operator") to manage all your asset.
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True if the operators is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved)
external
whenNotPaused
{
operatorToApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/// @dev Get the approved address for a single TTM
/// @param _tokenId The TTM to find the approved address for
/// @return The approved address for this TTM, or the zero address if there is none
function getApproved(uint256 _tokenId)
external
view
isValidToken(_tokenId)
returns (address)
{
return tokenIdToApprovals[_tokenId];
}
/// @dev Query if an address is an authorized operator for another address
/// @param _owner The address that owns the TTMs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return operatorToApprovals[_owner][_operator];
}
/// @dev Count TTMs tracked by this contract
/// @return A count of valid TTMs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256) {
return managerArray.length - 1;
}
/// @dev Enumerate valid TTMs
/// @param _index A counter less than totalSupply
/// @return The token identifier for the `_index`th TTM,
function tokenByIndex(uint256 _index)
external
view
returns (uint256)
{
require(_index < managerArray.length);
return _index;
}
/// @notice Enumerate TTMs assigned to an owner
/// @param _owner Token owner address
/// @param _index A counter less than balanceOf(_owner)
/// @return The TTM tokenId
function tokenOfOwnerByIndex(address _owner, uint256 _index)
external
view
returns (uint256)
{
require(_owner != address(0));
require(_index < ownerToManagerArray[_owner].length);
return ownerToManagerArray[_owner][_index];
}
/// @dev Do the real transfer with out any condition checking
/// @param _from The old owner of this TTM(If created: 0x0)
/// @param _to The new owner of this TTM
/// @param _tokenId The tokenId of the TTM
function _transfer(address _from, address _to, uint256 _tokenId) internal {
if (_from != address(0)) {
uint256 indexFrom = tokenIdToOwnerIndex[_tokenId];
uint256[] storage ttmArray = ownerToManagerArray[_from];
require(ttmArray[indexFrom] == _tokenId);
if (indexFrom != ttmArray.length - 1) {
uint256 lastTokenId = ttmArray[ttmArray.length - 1];
ttmArray[indexFrom] = lastTokenId;
tokenIdToOwnerIndex[lastTokenId] = indexFrom;
}
ttmArray.length -= 1;
if (tokenIdToApprovals[_tokenId] != address(0)) {
delete tokenIdToApprovals[_tokenId];
}
}
tokenIdToOwner[_tokenId] = _to;
ownerToManagerArray[_to].push(_tokenId);
tokenIdToOwnerIndex[_tokenId] = ownerToManagerArray[_to].length - 1;
emit Transfer(_from != address(0) ? _from : this, _to, _tokenId);
}
/// @dev Actually perform the safeTransferFrom
function _safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)
internal
isValidToken(_tokenId)
canTransfer(_tokenId)
{
address owner = tokenIdToOwner[_tokenId];
require(owner != address(0));
require(_to != address(0));
require(owner == _from);
_transfer(_from, _to, _tokenId);
// Do the callback after everything is done to avoid reentrancy attack
uint256 codeSize;
assembly { codeSize := extcodesize(_to) }
if (codeSize == 0) {
return;
}
bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(_from, _tokenId, data);
// bytes4(keccak256("onERC721Received(address,uint256,bytes)")) = 0xf0b9e5ba;
require(retval == 0xf0b9e5ba);
}
function setSafeContract(address _actionAddr, bool _useful) external onlyAdmin {
safeContracts[_actionAddr] = _useful;
}
function getSafeContract(address _actionAddr) external view onlyAdmin returns(bool) {
return safeContracts[_actionAddr];
}
function setMetadataProvider(address _provider) external onlyAdmin {
providerContract = ERC721MetadataProvider(_provider);
}
function getOwnTokens(address _owner) external view returns(uint256[]) {
require(_owner != address(0));
return ownerToManagerArray[_owner];
}
function safeGiveByContract(uint256 _tokenId, address _to)
external
whenNotPaused
{
require(safeContracts[msg.sender]);
// Only the token's owner is this can use this function
require(tokenIdToOwner[_tokenId] == address(this));
require(_to != address(0));
_transfer(address(this), _to, _tokenId);
}
/// @dev Safe transfer by trust contracts
function safeTransferByContract(uint256 _tokenId, address _to)
external
whenNotPaused
{
require(safeContracts[msg.sender]);
require(_tokenId >= 1 && _tokenId <= managerArray.length);
address owner = tokenIdToOwner[_tokenId];
require(owner != address(0));
require(_to != address(0));
require(owner != _to);
_transfer(owner, _to, _tokenId);
}
function initManager(uint256 _gene, uint256 _count) external {
require(safeContracts[msg.sender] || msg.sender == addrAdmin);
require(_gene > 0 && _count <= 128);
address owner = address(this);
uint256[] storage ttmArray = ownerToManagerArray[owner];
uint256 newTokenId;
for (uint256 i = 0; i < _count; ++i) {
newTokenId = managerArray.length;
managerArray.push(_gene);
tokenIdToOwner[newTokenId] = owner;
tokenIdToOwnerIndex[newTokenId] = ttmArray.length;
ttmArray.push(newTokenId);
emit Transfer(address(0), owner, newTokenId);
}
}
function approveAndCall(address _spender, uint256 _tokenId, bytes _extraData)
external
whenNotPaused
returns (bool success)
{
TokenRecipient spender = TokenRecipient(_spender);
approve(_spender, _tokenId);
spender.receiveApproval(msg.sender, _tokenId, this, _extraData);
return true;
}
function getProtoIdByTokenId(uint256 _tokenId)
external
view
returns(uint256 protoId)
{
if (_tokenId > 0 && _tokenId < managerArray.length) {
return managerArray[_tokenId];
}
}
function getOwnerTokens(address _owner)
external
view
returns(uint256[] tokenIdArray, uint256[] protoIdArray)
{
uint256[] storage ownTokens = ownerToManagerArray[_owner];
uint256 count = ownTokens.length;
tokenIdArray = new uint256[](count);
protoIdArray = new uint256[](count);
for (uint256 i = 0; i < count; ++i) {
tokenIdArray[i] = ownTokens[i];
protoIdArray[i] = managerArray[tokenIdArray[i]];
}
}
}
{
"compilationTarget": {
"ManagerToken.sol": "ManagerToken"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_approved","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_to","type":"address"}],"name":"safeTransferByContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"doUnpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"getOwnTokens","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_actionAddr","type":"address"}],"name":"getSafeContract","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_provider","type":"address"}],"name":"setMetadataProvider","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"doPause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"managerArray","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"providerContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_operator","type":"address"},{"name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_gene","type":"uint256"},{"name":"_count","type":"uint256"}],"name":"initManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_to","type":"address"}],"name":"safeGiveByContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isPaused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"},{"name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"addrAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"getProtoIdByTokenId","outputs":[{"name":"protoId","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_tokenId","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_actionAddr","type":"address"},{"name":"_useful","type":"bool"}],"name":"setSafeContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"getOwnerTokens","outputs":[{"name":"tokenIdArray","type":"uint256[]"},{"name":"protoIdArray","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_approved","type":"address"},{"indexed":false,"name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_operator","type":"address"},{"indexed":false,"name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"preAdmin","type":"address"},{"indexed":true,"name":"newAdmin","type":"address"}],"name":"AdminTransferred","type":"event"}]