Contract Source Code:
File 1 of 1 : WarToken
/* ==================================================================== */
/* Copyright (c) 2018 The ether.online Project. All rights reserved.
/*
/* https://ether.online The first RPG game of blockchain
/* ==================================================================== */
pragma solidity ^0.4.20;
/// @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) external;
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);
}
contract AccessAdmin {
bool public isPaused = false;
address public addrAdmin;
event AdminTransferred(address indexed preAdmin, address indexed newAdmin);
function AccessAdmin() 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));
AdminTransferred(addrAdmin, _newAdmin);
addrAdmin = _newAdmin;
}
function doPause() external onlyAdmin whenNotPaused {
isPaused = true;
}
function doUnpause() external onlyAdmin whenPaused {
isPaused = false;
}
}
contract WarToken is ERC721, AccessAdmin {
/// @dev The equipment info
struct Fashion {
uint16 protoId; // 0 Equipment ID
uint16 quality; // 1 Rarity: 1 Coarse/2 Good/3 Rare/4 Epic/5 Legendary
uint16 pos; // 2 Slots: 1 Weapon/2 Hat/3 Cloth/4 Pant/5 Shoes/9 Pets
uint16 health; // 3 Health
uint16 atkMin; // 4 Min attack
uint16 atkMax; // 5 Max attack
uint16 defence; // 6 Defennse
uint16 crit; // 7 Critical rate
uint16 isPercent; // 8 Attr value type
uint16 attrExt1; // 9 future stat 1
uint16 attrExt2; // 10 future stat 2
uint16 attrExt3; // 11 future stat 3
}
/// @dev All equipments tokenArray (not exceeding 2^32-1)
Fashion[] public fashionArray;
/// @dev Amount of tokens destroyed
uint256 destroyFashionCount;
/// @dev Equipment token ID vs owner address
mapping (uint256 => address) fashionIdToOwner;
/// @dev Equipments owner by the owner (array)
mapping (address => uint256[]) ownerToFashionArray;
/// @dev Equipment token ID search in owner array
mapping (uint256 => uint256) fashionIdToOwnerIndex;
/// @dev The authorized address for each WAR
mapping (uint256 => address) fashionIdToApprovals;
/// @dev The authorized operators for each address
mapping (address => mapping (address => bool)) operatorToApprovals;
/// @dev Trust contract
mapping (address => bool) actionContracts;
function setActionContract(address _actionAddr, bool _useful) external onlyAdmin {
actionContracts[_actionAddr] = _useful;
}
function getActionContract(address _actionAddr) external view onlyAdmin returns(bool) {
return actionContracts[_actionAddr];
}
/// @dev This emits when the approved address for an WAR 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);
/// @dev This emits when the equipment created
event CreateFashion(address indexed owner, uint256 tokenId, uint16 protoId, uint16 quality, uint16 pos, uint16 createType);
/// @dev This emits when the equipment's attributes changed
event ChangeFashion(address indexed owner, uint256 tokenId, uint16 changeType);
/// @dev This emits when the equipment destroyed
event DeleteFashion(address indexed owner, uint256 tokenId, uint16 deleteType);
function WarToken() public {
addrAdmin = msg.sender;
fashionArray.length += 1;
}
// modifier
/// @dev Check if token ID is valid
modifier isValidToken(uint256 _tokenId) {
require(_tokenId >= 1 && _tokenId <= fashionArray.length);
require(fashionIdToOwner[_tokenId] != address(0));
_;
}
modifier canTransfer(uint256 _tokenId) {
address owner = fashionIdToOwner[_tokenId];
require(msg.sender == owner || msg.sender == fashionIdToApprovals[_tokenId] || operatorToApprovals[owner][msg.sender]);
_;
}
// 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 "WAR Token";
}
function symbol() public pure returns(string) {
return "WAR";
}
/// @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));
return ownerToFashionArray[_owner].length;
}
/// @dev Find the owner of an WAR
/// @param _tokenId The tokenId of WAR
/// @return Give The address of the owner of this WAR
function ownerOf(uint256 _tokenId) external view /*isValidToken(_tokenId)*/ returns (address owner) {
return fashionIdToOwner[_tokenId];
}
/// @dev Transfers the ownership of an WAR from one address to another address
/// @param _from The current owner of the WAR
/// @param _to The new owner
/// @param _tokenId The WAR 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 WAR from one address to another address
/// @param _from The current owner of the WAR
/// @param _to The new owner
/// @param _tokenId The WAR to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId)
external
whenNotPaused
{
_safeTransferFrom(_from, _to, _tokenId, "");
}
/// @dev Transfer ownership of an WAR, '_to' must be a vaild address, or the WAR will lost
/// @param _from The current owner of the WAR
/// @param _to The new owner
/// @param _tokenId The WAR to transfer
function transferFrom(address _from, address _to, uint256 _tokenId)
external
whenNotPaused
isValidToken(_tokenId)
canTransfer(_tokenId)
{
address owner = fashionIdToOwner[_tokenId];
require(owner != address(0));
require(_to != address(0));
require(owner == _from);
_transfer(_from, _to, _tokenId);
}
/// @dev Set or reaffirm the approved address for an WAR
/// @param _approved The new approved WAR controller
/// @param _tokenId The WAR to approve
function approve(address _approved, uint256 _tokenId)
external
whenNotPaused
{
address owner = fashionIdToOwner[_tokenId];
require(owner != address(0));
require(msg.sender == owner || operatorToApprovals[owner][msg.sender]);
fashionIdToApprovals[_tokenId] = _approved;
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;
ApprovalForAll(msg.sender, _operator, _approved);
}
/// @dev Get the approved address for a single WAR
/// @param _tokenId The WAR to find the approved address for
/// @return The approved address for this WAR, or the zero address if there is none
function getApproved(uint256 _tokenId) external view isValidToken(_tokenId) returns (address) {
return fashionIdToApprovals[_tokenId];
}
/// @dev Query if an address is an authorized operator for another address
/// @param _owner The address that owns the WARs
/// @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 WARs tracked by this contract
/// @return A count of valid WARs 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 fashionArray.length - destroyFashionCount - 1;
}
/// @dev Do the real transfer with out any condition checking
/// @param _from The old owner of this WAR(If created: 0x0)
/// @param _to The new owner of this WAR
/// @param _tokenId The tokenId of the WAR
function _transfer(address _from, address _to, uint256 _tokenId) internal {
if (_from != address(0)) {
uint256 indexFrom = fashionIdToOwnerIndex[_tokenId];
uint256[] storage fsArray = ownerToFashionArray[_from];
require(fsArray[indexFrom] == _tokenId);
// If the WAR is not the element of array, change it to with the last
if (indexFrom != fsArray.length - 1) {
uint256 lastTokenId = fsArray[fsArray.length - 1];
fsArray[indexFrom] = lastTokenId;
fashionIdToOwnerIndex[lastTokenId] = indexFrom;
}
fsArray.length -= 1;
if (fashionIdToApprovals[_tokenId] != address(0)) {
delete fashionIdToApprovals[_tokenId];
}
}
// Give the WAR to '_to'
fashionIdToOwner[_tokenId] = _to;
ownerToFashionArray[_to].push(_tokenId);
fashionIdToOwnerIndex[_tokenId] = ownerToFashionArray[_to].length - 1;
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 = fashionIdToOwner[_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);
}
//----------------------------------------------------------------------------------------------------------
/// @dev Equipment creation
/// @param _owner Owner of the equipment created
/// @param _attrs Attributes of the equipment created
/// @return Token ID of the equipment created
function createFashion(address _owner, uint16[9] _attrs, uint16 _createType)
external
whenNotPaused
returns(uint256)
{
require(actionContracts[msg.sender]);
require(_owner != address(0));
uint256 newFashionId = fashionArray.length;
require(newFashionId < 4294967296);
fashionArray.length += 1;
Fashion storage fs = fashionArray[newFashionId];
fs.protoId = _attrs[0];
fs.quality = _attrs[1];
fs.pos = _attrs[2];
if (_attrs[3] != 0) {
fs.health = _attrs[3];
}
if (_attrs[4] != 0) {
fs.atkMin = _attrs[4];
fs.atkMax = _attrs[5];
}
if (_attrs[6] != 0) {
fs.defence = _attrs[6];
}
if (_attrs[7] != 0) {
fs.crit = _attrs[7];
}
if (_attrs[8] != 0) {
fs.isPercent = _attrs[8];
}
_transfer(0, _owner, newFashionId);
CreateFashion(_owner, newFashionId, _attrs[0], _attrs[1], _attrs[2], _createType);
return newFashionId;
}
/// @dev One specific attribute of the equipment modified
function _changeAttrByIndex(Fashion storage _fs, uint16 _index, uint16 _val) internal {
if (_index == 3) {
_fs.health = _val;
} else if(_index == 4) {
_fs.atkMin = _val;
} else if(_index == 5) {
_fs.atkMax = _val;
} else if(_index == 6) {
_fs.defence = _val;
} else if(_index == 7) {
_fs.crit = _val;
} else if(_index == 9) {
_fs.attrExt1 = _val;
} else if(_index == 10) {
_fs.attrExt2 = _val;
} else if(_index == 11) {
_fs.attrExt3 = _val;
}
}
/// @dev Equiment attributes modified (max 4 stats modified)
/// @param _tokenId Equipment Token ID
/// @param _idxArray Stats order that must be modified
/// @param _params Stat value that must be modified
/// @param _changeType Modification type such as enhance, socket, etc.
function changeFashionAttr(uint256 _tokenId, uint16[4] _idxArray, uint16[4] _params, uint16 _changeType)
external
whenNotPaused
isValidToken(_tokenId)
{
require(actionContracts[msg.sender]);
Fashion storage fs = fashionArray[_tokenId];
if (_idxArray[0] > 0) {
_changeAttrByIndex(fs, _idxArray[0], _params[0]);
}
if (_idxArray[1] > 0) {
_changeAttrByIndex(fs, _idxArray[1], _params[1]);
}
if (_idxArray[2] > 0) {
_changeAttrByIndex(fs, _idxArray[2], _params[2]);
}
if (_idxArray[3] > 0) {
_changeAttrByIndex(fs, _idxArray[3], _params[3]);
}
ChangeFashion(fashionIdToOwner[_tokenId], _tokenId, _changeType);
}
/// @dev Equipment destruction
/// @param _tokenId Equipment Token ID
/// @param _deleteType Destruction type, such as craft
function destroyFashion(uint256 _tokenId, uint16 _deleteType)
external
whenNotPaused
isValidToken(_tokenId)
{
require(actionContracts[msg.sender]);
address _from = fashionIdToOwner[_tokenId];
uint256 indexFrom = fashionIdToOwnerIndex[_tokenId];
uint256[] storage fsArray = ownerToFashionArray[_from];
require(fsArray[indexFrom] == _tokenId);
if (indexFrom != fsArray.length - 1) {
uint256 lastTokenId = fsArray[fsArray.length - 1];
fsArray[indexFrom] = lastTokenId;
fashionIdToOwnerIndex[lastTokenId] = indexFrom;
}
fsArray.length -= 1;
fashionIdToOwner[_tokenId] = address(0);
delete fashionIdToOwnerIndex[_tokenId];
destroyFashionCount += 1;
Transfer(_from, 0, _tokenId);
DeleteFashion(_from, _tokenId, _deleteType);
}
/// @dev Safe transfer by trust contracts
function safeTransferByContract(uint256 _tokenId, address _to)
external
whenNotPaused
{
require(actionContracts[msg.sender]);
require(_tokenId >= 1 && _tokenId <= fashionArray.length);
address owner = fashionIdToOwner[_tokenId];
require(owner != address(0));
require(_to != address(0));
require(owner != _to);
_transfer(owner, _to, _tokenId);
}
//----------------------------------------------------------------------------------------------------------
/// @dev Get fashion attrs by tokenId
function getFashion(uint256 _tokenId) external view isValidToken(_tokenId) returns (uint16[12] datas) {
Fashion storage fs = fashionArray[_tokenId];
datas[0] = fs.protoId;
datas[1] = fs.quality;
datas[2] = fs.pos;
datas[3] = fs.health;
datas[4] = fs.atkMin;
datas[5] = fs.atkMax;
datas[6] = fs.defence;
datas[7] = fs.crit;
datas[8] = fs.isPercent;
datas[9] = fs.attrExt1;
datas[10] = fs.attrExt2;
datas[11] = fs.attrExt3;
}
/// @dev Get tokenIds and flags by owner
function getOwnFashions(address _owner) external view returns(uint256[] tokens, uint32[] flags) {
require(_owner != address(0));
uint256[] storage fsArray = ownerToFashionArray[_owner];
uint256 length = fsArray.length;
tokens = new uint256[](length);
flags = new uint32[](length);
for (uint256 i = 0; i < length; ++i) {
tokens[i] = fsArray[i];
Fashion storage fs = fashionArray[fsArray[i]];
flags[i] = uint32(uint32(fs.protoId) * 100 + uint32(fs.quality) * 10 + fs.pos);
}
}
/// @dev WAR token info returned based on Token ID transfered (64 at most)
function getFashionsAttrs(uint256[] _tokens) external view returns(uint16[] attrs) {
uint256 length = _tokens.length;
require(length <= 64);
attrs = new uint16[](length * 11);
uint256 tokenId;
uint256 index;
for (uint256 i = 0; i < length; ++i) {
tokenId = _tokens[i];
if (fashionIdToOwner[tokenId] != address(0)) {
index = i * 11;
Fashion storage fs = fashionArray[tokenId];
attrs[index] = fs.health;
attrs[index + 1] = fs.atkMin;
attrs[index + 2] = fs.atkMax;
attrs[index + 3] = fs.defence;
attrs[index + 4] = fs.crit;
attrs[index + 5] = fs.isPercent;
attrs[index + 6] = fs.attrExt1;
attrs[index + 7] = fs.attrExt2;
attrs[index + 8] = fs.attrExt3;
}
}
}
}