Contract Source Code:
File 1 of 1 : KydyCore
pragma solidity ^0.4.24;
/// @title New Child Kydy's Genes
contract GeneSynthesisInterface {
/// @dev boolean to check this is the contract we expect to be
function isGeneSynthesis() public pure returns (bool);
/**
* @dev Synthesizes the genes of yin and yang Kydy, and returns the result as the child's genes.
* @param gene1 genes of yin Kydy
* @param gene2 genes of yang Kydy
* @return the genes of the child
*/
function synthGenes(uint256 gene1, uint256 gene2) public returns (uint256);
}
/**
* @title Part of KydyCore that manages special access controls.
* @author VREX Lab Co., Ltd
* @dev See the KydyCore contract documentation to understand how the various contracts are arranged.
*/
contract KydyAccessControl {
/**
* This contract defines access control for the following important roles of the Dyverse:
*
* - The CEO: The CEO can assign roles and change the addresses of the smart contracts.
* It can also solely unpause the smart contract.
*
* - The CFO: The CFO can withdraw funds from the KydyCore and the auction contracts.
*
* - The COO: The COO can release Generation 0 Kydys and create promotional-type Kydys.
*
*/
/// @dev Used when contract is upgraded.
event ContractUpgrade(address newContract);
// The assigned addresses of each role, as defined in this contract.
address public ceoAddress;
address public cfoAddress;
address public cooAddress;
/// @dev Checks if the contract is paused. When paused, most of the functions of this contract will also be stopped.
bool public paused = false;
/// @dev Access modifier for CEO-only
modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}
/// @dev Access modifier for CFO-only
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
/// @dev Access modifier for COO-only
modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}
/// @dev Access modifier for CEO, CFO, COO
modifier onlyCLevel() {
require(
msg.sender == ceoAddress ||
msg.sender == cfoAddress ||
msg.sender == cooAddress
);
_;
}
/**
* @dev Assigns a new address to the CEO. Only the current CEO has the authority.
* @param _newCEO The address of the new CEO
*/
function setCEO(address _newCEO) external onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
/**
* @dev Assigns a new address to the CFO. Only the current CEO has the authority.
* @param _newCFO The address of the new CFO
*/
function setCFO(address _newCFO) external onlyCEO {
require(_newCFO != address(0));
cfoAddress = _newCFO;
}
/**
* @dev Assigns a new address to the COO. Only the current CEO has the authority.
* @param _newCOO The address of the new COO
*/
function setCOO(address _newCOO) external onlyCEO {
require(_newCOO != address(0));
cooAddress = _newCOO;
}
/*** Pausable functionality adapted from OpenZeppelin ***/
/// @dev Modifier to allow actions only when the contract IS NOT paused
modifier whenNotPaused() {
require(!paused);
_;
}
/// @dev Modifier to allow actions only when the contract IS paused
modifier whenPaused {
require(paused);
_;
}
/**
* @dev Called by any "C-level" role to pause the contract. Used only when
* a bug or exploit is detected to limit the damage.
*/
function pause() external onlyCLevel whenNotPaused {
paused = true;
}
/**
* @dev Unpauses the smart contract. Can only be called by the CEO, since
* one reason we may pause the contract is when CFO or COO accounts are
* compromised.
* @notice This is public rather than external so it can be called by
* derived contracts.
*/
function unpause() public onlyCEO whenPaused {
// can't unpause if contract was upgraded
paused = false;
}
}
contract ERC165Interface {
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
* @return `true` if the contract implements `interfaceID` and
* `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
contract ERC165 is ERC165Interface {
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev internal method for registering an interface
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
_supportedInterfaces[interfaceId] = true;
}
}
// Every ERC-721 compliant contract must implement the ERC721 and ERC165 interfaces.
/**
* @title ERC-721 Non-Fungible Token Standard
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
* Note: the ERC-165 identifier for this interface is 0x80ac58cd.
*/
contract ERC721Basic is ERC165 {
// Below is MUST
/**
* @dev This emits when ownership of any NFT changes by any mechanism.
* This event emits when NFTs are created (`from` == 0) and destroyed
* (`to` == 0). Exception: during contract creation, any number of NFTs
* may be created and assigned without emitting Transfer. At the time of
* any transfer, the approved address for that NFT (if any) is reset to none.
*/
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
/**
* @dev This emits when the approved address for an NFT is changed or
* reaffirmed. The zero address indicates there is no approved address.
* When a Transfer event emits, this also indicates that the approved
* address for that NFT (if any) is reset to none.
*/
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
/**
* @dev This emits when an operator is enabled or disabled for an owner.
* The operator can manage all NFTs of the owner.
*/
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/**
* @notice Count all NFTs assigned to an owner
* @dev NFTs assigned to the zero address are considered invalid, and this
* function throws for queries about the zero address.
* @param _owner An address for whom to query the balance
* @return The number of NFTs owned by `_owner`, possibly zero
*/
function balanceOf(address _owner) public view returns (uint256);
/**
* @notice Find the owner of an NFT
* @dev NFTs assigned to zero address are considered invalid, and queries
* about them do throw.
* @param _tokenId The identifier for an NFT
* @return The address of the owner of the NFT
*/
function ownerOf(uint256 _tokenId) public view returns (address);
/**
* @notice Transfers the ownership of an NFT from one address to another address
* @dev Throws unless `msg.sender` is the current owner, an authorized
* operator, or the approved address for this NFT. Throws if `_from` is
* not the current owner. Throws if `_to` is the zero address. Throws if
* `_tokenId` is not a valid NFT. When transfer is complete, this function
* checks if `_to` is a smart contract (code size > 0). If so, it calls
* `onERC721Received` on `_to` and throws if the return value is not
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
* @param _from The current owner of the NFT
* @param _to The new owner
* @param _tokenId The NFT 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) public;
/**
* @notice Transfers the ownership of an NFT from one address to another address
* @dev This works identically to the other function with an extra data parameter,
* except this function just sets data to "".
* @param _from The current owner of the NFT
* @param _to The new owner
* @param _tokenId The NFT to transfer
*/
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;
/**
* @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
* TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
* THEY MAY BE PERMANENTLY LOST
* @dev Throws unless `msg.sender` is the current owner, an authorized
* operator, or the approved address for this NFT. Throws if `_from` is
* not the current owner. Throws if `_to` is the zero address. Throws if
* `_tokenId` is not a valid NFT.
* @param _from The current owner of the NFT
* @param _to The new owner
* @param _tokenId The NFT to transfer
*/
function transferFrom(address _from, address _to, uint256 _tokenId) public;
/**
* @notice Change or reaffirm the approved address for an NFT
* @dev The zero address indicates there is no approved address.
* Throws unless `msg.sender` is the current NFT owner, or an authorized
* operator of the current owner.
* @param _approved The new approved NFT controller
* @param _tokenId The NFT to approve
*/
function approve(address _approved, uint256 _tokenId) external;
/**
* @notice Enable or disable approval for a third party ("operator") to manage
* all of `msg.sender`'s assets
* @dev Emits the ApprovalForAll event. The contract MUST allow
* multiple operators per owner.
* @param _operator Address to add to the set of authorized operators
* @param _approved True if the operator is approved, false to revoke approval
*/
function setApprovalForAll(address _operator, bool _approved) external;
/**
* @notice Get the approved address for a single NFT
* @dev Throws if `_tokenId` is not a valid NFT.
* @param _tokenId The NFT to find the approved address for
* @return The approved address for this NFT, or the zero address if there is none
*/
function getApproved(uint256 _tokenId) public view returns (address);
/**
* @notice Query if an address is an authorized operator for another address
* @param _owner The address that owns the NFTs
* @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) public view returns (bool);
// Below is OPTIONAL
// ERC721Metadata
// The metadata extension is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent.
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
* Note: the ERC-165 identifier for this interface is 0x5b5e139f.
*/
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string _symbol);
/**
* @notice A distinct Uniform Resource Identifier (URI) for a given asset.
* @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
* 3986. The URI may point to a JSON file that conforms to the "ERC721
* Metadata JSON Schema".
*/
function tokenURI(uint256 _tokenId) external view returns (string);
// ERC721Enumerable
// The enumeration extension is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your contract to publish its full list of NFTs and make them discoverable.
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
* Note: the ERC-165 identifier for this interface is 0x780e9d63.
*/
/**
* @notice Count NFTs tracked by this contract
* @return A count of valid NFTs tracked by this contract, where each one of
* them has an assigned and queryable owner not equal to the zero address
*/
function totalSupply() public view returns (uint256);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
/**
* @title The base contract of Dyverse. ERC-721 compliant.
* @author VREX Lab Co., Ltd
* @dev See the KydyCore contract for more info on details.
*/
contract KydyBase is KydyAccessControl, ERC721Basic {
using SafeMath for uint256;
using Address for address;
/*** EVENT ***/
/**
* @dev The Creation event takes place whenever a new Kydy is created via Synthesis or minted by the COO.
*/
event Created(address indexed owner, uint256 kydyId, uint256 yinId, uint256 yangId, uint256 genes);
/*** DATA TYPES ***/
/**
* @dev Every Kydy in the Dyverse is a copy of this structure.
*/
struct Kydy {
// The Kydy's genetic code is stored into 256-bits and never changes.
uint256 genes;
// The timestamp of the block when this Kydy was created
uint64 createdTime;
// The timestamp of when this Kydy can synthesize again.
uint64 rechargeEndBlock;
// The ID of the parents (Yin, female, and Yang, male). It is 0 for Generation 0 Kydys.
uint32 yinId;
uint32 yangId;
// The ID of the yang Kydy that the yin Kydy is creating with.
uint32 synthesizingWithId;
// The recharge index that represents the duration of the recharge for this Kydy.
// After each synthesis, this increases by one for both yin and yang Kydys of the synthesis.
uint16 rechargeIndex;
// The generation index of this Kydy. The newly created Kydy takes the generation index of the parent
// with a larger generation index.
uint16 generation;
}
/*** CONSTANTS ***/
/**
* @dev An array table of the recharge duration. Referred to as "creation time" for yin
* and "synthesis recharge" for yang Kydys. Maximum duration is 4 days.
*/
uint32[14] public recharges = [
uint32(1 minutes),
uint32(2 minutes),
uint32(5 minutes),
uint32(10 minutes),
uint32(30 minutes),
uint32(1 hours),
uint32(2 hours),
uint32(4 hours),
uint32(8 hours),
uint32(16 hours),
uint32(1 days),
uint32(2 days),
uint32(4 days)
];
// An approximation of seconds between blocks.
uint256 public secondsPerBlock = 15;
/*** STORAGE ***/
/**
* @dev This array contains the ID of every Kydy as an index.
*/
Kydy[] kydys;
/**
* @dev This maps each Kydy ID to the address of the owner. Every Kydy must have an owner, even Gen 0 Kydys.
* You can view this mapping via `ownerOf()`.
*/
mapping (uint256 => address) internal kydyIndexToOwner;
/**
* @dev This maps the owner's address to the number of Kydys that the address owns.
* You can view this mapping via `balanceOf()`.
*/
mapping (address => uint256) internal ownershipTokenCount;
/**
* @dev This maps transferring Kydy IDs to the the approved address to call safeTransferFrom().
* You can view this mapping via `getApproved()`.
*/
mapping (uint256 => address) internal kydyIndexToApproved;
/**
* @dev This maps KydyIDs to the address approved to synthesize via synthesizeWithAuto().
* You can view this mapping via `getSynthesizeApproved()`.
*/
mapping (uint256 => address) internal synthesizeAllowedToAddress;
/**
* @dev This maps the owner to operator approvals, for the usage of setApprovalForAll().
*/
mapping (address => mapping (address => bool)) private _operatorApprovals;
/**
* @dev Returns the owner of the given Kydy ID. Required for ERC-721 compliance.
* @param _tokenId uint256 ID of the Kydy in query
* @return the address of the owner of the given Kydy ID
*/
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = kydyIndexToOwner[_tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Returns the approved address of the receiving owner for a Kydy ID. Required for ERC-721 compliance.
* @param tokenId uint256 ID of the Kydy in query
* @return the address of the approved, receiving owner for the given Kydy ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
return kydyIndexToApproved[tokenId];
}
/**
* @dev Returns the synthesize approved address of the Kydy ID.
* @param tokenId uint256 ID of the Kydy in query
* @return the address of the synthesizing approved of the given Kydy ID
*/
function getSynthesizeApproved(uint256 tokenId) external view returns (address) {
require(_exists(tokenId));
return synthesizeAllowedToAddress[tokenId];
}
/**
* @dev Returns whether an operator is approved by the owner. Required for ERC-721 compliance.
* @param owner owner address to check whether it is approved
* @param operator operator address to check whether it is approved
* @return bool whether the operator is approved or not
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Sets or unsets the approval of the operator. Required for ERC-721 compliance.
* @param to operator address to set the approval
* @param approved the status to be set
*/
function setApprovalForAll(address to, bool approved) external {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/// @dev Assigns ownership of this Kydy to an address.
function _transfer(address _from, address _to, uint256 _tokenId) internal {
ownershipTokenCount[_to] = ownershipTokenCount[_to].add(1);
// Transfers the ownership of this Kydy.
kydyIndexToOwner[_tokenId] = _to;
ownershipTokenCount[_from] = ownershipTokenCount[_from].sub(1);
// After a transfer, synthesis allowance is also reset.
delete synthesizeAllowedToAddress[_tokenId];
// Clears any previously approved transfer.
delete kydyIndexToApproved[_tokenId];
// Emit the transfer event.
emit Transfer(_from, _to, _tokenId);
}
/**
* @dev Returns whether the given Kydy ID exists
* @param _tokenId uint256 ID of the Kydy in query
* @return whether the Kydy exists
*/
function _exists(uint256 _tokenId) internal view returns (bool) {
address owner = kydyIndexToOwner[_tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer the Kydy ID
* @param _spender address of the spender to query
* @param _tokenId uint256 ID of the Kydy to be transferred
* @return bool whether the msg.sender is approved
*/
function _isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) {
address owner = ownerOf(_tokenId);
// Disable solium check because of
// https://github.com/duaraghav8/Solium/issues/175
// solium-disable-next-line operator-whitespace
return (_spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender));
}
/**
* @dev Internal function to add a Kydy ID to the new owner's list.
* @param _to address the new owner's address
* @param _tokenId uint256 ID of the transferred Kydy
*/
function _addTokenTo(address _to, uint256 _tokenId) internal {
// Checks if the owner of the Kydy is 0x0 before the transfer.
require(kydyIndexToOwner[_tokenId] == address(0));
// Transfers the ownership to the new owner.
kydyIndexToOwner[_tokenId] = _to;
// Increases the total Kydy count of the new owner.
ownershipTokenCount[_to] = ownershipTokenCount[_to].add(1);
}
/**
* @dev Internal function to remove a Kydy ID from the previous owner's list.
* @param _from address the previous owner's address
* @param _tokenId uint256 ID of the transferred Kydy
*/
function _removeTokenFrom(address _from, uint256 _tokenId) internal {
// Checks the current owner of the Kydy is '_from'.
require(ownerOf(_tokenId) == _from);
// Reduces the total Kydy count of the previous owner.
ownershipTokenCount[_from] = ownershipTokenCount[_from].sub(1);
// Deletes the transferred Kydy from the current owner's list.
kydyIndexToOwner[_tokenId] = address(0);
}
/**
* @dev Internal function to mint a new Kydy.
* @param _to The address that owns the newly minted Kydy
* @param _tokenId uint256 ID of the newly minted Kydy
*/
function _mint(address _to, uint256 _tokenId) internal {
require(!_exists(_tokenId));
_addTokenTo(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
}
/**
* @dev Internal function to clear current approvals of a given Kydy ID.
* @param _owner owner of the Kydy
* @param _tokenId uint256 ID of the Kydy to be transferred
*/
function _clearApproval(address _owner, uint256 _tokenId) internal {
require(ownerOf(_tokenId) == _owner);
if (kydyIndexToApproved[_tokenId] != address(0)) {
kydyIndexToApproved[_tokenId] = address(0);
}
if (synthesizeAllowedToAddress[_tokenId] != address(0)) {
synthesizeAllowedToAddress[_tokenId] = address(0);
}
}
/**
* @dev Internal function that creates a new Kydy and stores it.
* @param _yinId The ID of the yin Kydy (zero for Generation 0 Kydy)
* @param _yangId The ID of the yang Kydy (zero for Generation 0 Kydy)
* @param _generation The generation number of the new Kydy.
* @param _genes The Kydy's gene code
* @param _owner The owner of this Kydy, must be non-zero (except for the ID 0)
*/
function _createKydy(
uint256 _yinId,
uint256 _yangId,
uint256 _generation,
uint256 _genes,
address _owner
)
internal
returns (uint)
{
require(_yinId == uint256(uint32(_yinId)));
require(_yangId == uint256(uint32(_yangId)));
require(_generation == uint256(uint16(_generation)));
// New Kydy's recharge index is its generation / 2.
uint16 rechargeIndex = uint16(_generation / 2);
if (rechargeIndex > 13) {
rechargeIndex = 13;
}
Kydy memory _kyd = Kydy({
genes: _genes,
createdTime: uint64(now),
rechargeEndBlock: 0,
yinId: uint32(_yinId),
yangId: uint32(_yangId),
synthesizingWithId: 0,
rechargeIndex: rechargeIndex,
generation: uint16(_generation)
});
uint256 newbabyKydyId = kydys.push(_kyd) - 1;
// Just in case.
require(newbabyKydyId == uint256(uint32(newbabyKydyId)));
// Emits the Created event.
emit Created(
_owner,
newbabyKydyId,
uint256(_kyd.yinId),
uint256(_kyd.yangId),
_kyd.genes
);
// Here grants ownership, and also emits the Transfer event.
_mint(_owner, newbabyKydyId);
return newbabyKydyId;
}
// Any C-level roles can change the seconds per block
function setSecondsPerBlock(uint256 secs) external onlyCLevel {
require(secs < recharges[0]);
secondsPerBlock = secs;
}
}
/**
* @notice This is MUST to be implemented.
* A wallet/broker/auction application MUST implement the wallet interface if it will accept safe transfers.
* @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
*/
contract ERC721TokenReceiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `transfer`. This function MAY throw to revert and reject the
* transfer. Return of other than the magic value MUST result in the
* transaction being reverted.
* Note: the contract address is always the message sender.
* @param _operator The address which called `safeTransferFrom` function
* @param _from The address which previously owned the token
* @param _tokenId The NFT identifier which is being transferred
* @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
* unless throwing
*/
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) public returns (bytes4);
}
// File: contracts/lib/Strings.sol
library Strings {
// via https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
function strConcat(string _a, string _b, string _c, string _d, string _e) internal pure returns (string) {
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
bytes memory _bc = bytes(_c);
bytes memory _bd = bytes(_d);
bytes memory _be = bytes(_e);
string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
bytes memory babcde = bytes(abcde);
uint k = 0;
for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
return string(babcde);
}
function strConcat(string _a, string _b, string _c, string _d) internal pure returns (string) {
return strConcat(_a, _b, _c, _d, "");
}
function strConcat(string _a, string _b, string _c) internal pure returns (string) {
return strConcat(_a, _b, _c, "", "");
}
function strConcat(string _a, string _b) internal pure returns (string) {
return strConcat(_a, _b, "", "", "");
}
function uint2str(uint i) internal pure returns (string) {
if (i == 0) return "0";
uint j = i;
uint len;
while (j != 0){
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (i != 0){
bstr[k--] = byte(48 + i % 10);
i /= 10;
}
return string(bstr);
}
}
/**
* @title Part of the KydyCore contract that manages ownership, ERC-721 compliant.
* @author VREX Lab Co., Ltd
* @dev Ref: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
* See the KydyCore contract documentation to understand how the various contracts are arranged.
*/
contract KydyOwnership is KydyBase {
using Strings for string;
/// @notice Name and symbol of the non fungible token, as defined in ERC721.
string public constant _name = "Dyverse";
string public constant _symbol = "KYDY";
// Base Server Address for Token MetaData URI
string internal tokenURIBase = "http://testapi.dyver.se/api/KydyMetadata/";
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `ERC721TokenReceiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
bytes4 private constant _InterfaceId_ERC165 = 0x01ffc9a7;
/**
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
bytes4 private constant _InterfaceId_ERC721Metadata = 0x5b5e139f;
/**
* 0x5b5e139f ===
* bytes4(keccak256('name()')) ^
* bytes4(keccak256('symbol()')) ^
* bytes4(keccak256('tokenURI(uint256)'))
*/
constructor() public {
_registerInterface(_InterfaceId_ERC165);
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_InterfaceId_ERC721);
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_InterfaceId_ERC721Metadata);
}
/**
* @dev Checks if a given address is the current owner of this Kydy.
* @param _claimant the address which we want to query the ownership of the Kydy ID.
* @param _tokenId Kydy id, only valid when > 0
*/
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kydyIndexToOwner[_tokenId] == _claimant;
}
/**
* @dev Grants an approval to the given address for safeTransferFrom(), overwriting any
* previous approval. Setting _approved to address(0) clears all transfer approval.
* Note that _approve() does NOT emit the Approval event. This is intentional because
* _approve() and safeTransferFrom() are used together when putting Kydys to the auction,
* and there is no need to spam the log with Approval events in that case.
*/
function _approve(uint256 _tokenId, address _approved) internal {
kydyIndexToApproved[_tokenId] = _approved;
}
/**
* @dev Transfers a Kydy owned by this contract to the specified address.
* Used to rescue lost Kydys. (There is no "proper" flow where this contract
* should be the owner of any Kydy. This function exists for us to reassign
* the ownership of Kydys that users may have accidentally sent to our address.)
* @param _kydyId ID of the lost Kydy
* @param _recipient address to send the Kydy to
*/
function rescueLostKydy(uint256 _kydyId, address _recipient) external onlyCOO whenNotPaused {
require(_owns(this, _kydyId));
_transfer(this, _recipient, _kydyId);
}
/**
* @dev Gets the number of Kydys owned by the given address.
* Required for ERC-721 compliance.
* @param _owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address _owner) public view returns (uint256) {
require(_owner != address(0));
return ownershipTokenCount[_owner];
}
/**
* @dev Approves another address to transfer the given Kydy ID.
* The zero address indicates that there is no approved address.
* There can only be one approved address per Kydy at a given time.
* Can only be called by the Kydy owner or an approved operator.
* Required for ERC-721 compliance.
* @param to address to be approved for the given Kydy ID
* @param tokenId uint256 ID of the Kydy to be approved
*/
function approve(address to, uint256 tokenId) external whenNotPaused {
address owner = ownerOf(tokenId);
require(to != owner);
// Owner or approved operator by owner can approve the another address
// to transfer the Kydy.
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
// Approves the given address.
_approve(tokenId, to);
// Emits the Approval event.
emit Approval(owner, to, tokenId);
}
/**
* @dev Transfers the ownership of the Kydy to another address.
* Usage of this function is discouraged, use `safeTransferFrom` whenever possible.
* Requires the msg sender to be the owner, approved, or operator.
* Required for ERC-721 compliance.
* @param from current owner of the Kydy
* @param to address to receive the ownership of the given Kydy ID
* @param tokenId uint256 ID of the Kydy to be transferred
*/
function transferFrom(address from, address to, uint256 tokenId) public whenNotPaused {
// Checks the caller is the owner or approved one or an operator.
require(_isApprovedOrOwner(msg.sender, tokenId));
// Safety check to prevent from transferring Kydy to 0x0 address.
require(to != address(0));
// Clears approval from current owner.
_clearApproval(from, tokenId);
// Resets the ownership of this Kydy from current owner and sets it to 0x0.
_removeTokenFrom(from, tokenId);
// Grants the ownership of this Kydy to new owner.
_addTokenTo(to, tokenId);
// Emits the Transfer event.
emit Transfer(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given Kydy to another address.
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
* Otherwise, the transfer is reverted.
* Requires the msg sender to be the owner, approved, or operator.
* Required for ERC-721 compliance.
* @param from current owner of the Kydy
* @param to address to receive the ownership of the given Kydy ID
* @param tokenId uint256 ID of the Kydy to be transferred
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
// solium-disable-next-line arg-overflow
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given Kydy to another address.
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
* Otherwise, the transfer is reverted.
* Requires the msg sender to be the owner, approved, or operator.
* Required for ERC-721 compliance.
* @param from current owner of the Kydy
* @param to address to receive the ownership of the given Kydy ID
* @param tokenId uint256 ID of the Kydy to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes _data) public {
transferFrom(from, to, tokenId);
// solium-disable-next-line arg-overflow
require(_checkOnERC721Received(from, to, tokenId, _data));
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address.
* This function is not executed if the target address is not a contract.
* @param _from address representing the previous owner of the given Kydy ID
* @param _to target address that will receive the Kydys
* @param _tokenId uint256 ID of the Kydy to be transferred
* @param _data bytes optional data to send along with the call
* @return whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address _from, address _to, uint256 _tokenId, bytes _data) internal returns (bool) {
if (!_to.isContract()) {
return true;
}
bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Gets the token name.
* Required for ERC721Metadata compliance.
* @return string representing the token name
*/
function name() external view returns (string) {
return _name;
}
/**
* @dev Gets the token symbol.
* Required for ERC721Metadata compliance.
* @return string representing the token symbol
*/
function symbol() external view returns (string) {
return _symbol;
}
/**
* @dev Returns an URI for a given Kydy ID.
* Throws if the token ID does not exist. May return an empty string.
* Required for ERC721Metadata compliance.
* @param tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 tokenId) external view returns (string) {
require(_exists(tokenId));
return Strings.strConcat(
tokenURIBase,
Strings.uint2str(tokenId)
);
}
/**
* @dev Gets the total amount of Kydys stored in the contract
* @return uint256 representing the total amount of Kydys
*/
function totalSupply() public view returns (uint256) {
return kydys.length - 1;
}
/**
* @notice Returns a list of all Kydy IDs assigned to an address.
* @param _owner The owner whose Kydys we are interested in.
* @dev This function MUST NEVER be called by smart contract code. It's pretty
* expensive (it looks into the entire Kydy array looking for Kydys belonging to owner),
* and it also returns a dynamic array, which is only supported for web3 calls, and
* not contract-to-contract calls.
*/
function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
uint256 tokenCount = balanceOf(_owner);
if (tokenCount == 0) {
// Return an empty array
return new uint256[](0);
} else {
uint256[] memory result = new uint256[](tokenCount);
uint256 totalKydys = totalSupply();
uint256 resultIndex = 0;
// All Kydys have IDs starting at 1 and increasing sequentially up to the totalKydy count.
uint256 kydyId;
for (kydyId = 1; kydyId <= totalKydys; kydyId++) {
if (kydyIndexToOwner[kydyId] == _owner) {
result[resultIndex] = kydyId;
resultIndex++;
}
}
return result;
}
}
}
/**
* @title This manages synthesis and creation of Kydys.
* @author VREX Lab Co., Ltd
* @dev Please reference the KydyCore contract for details.
*/
contract KydySynthesis is KydyOwnership {
/**
* @dev The Creating event is emitted when two Kydys synthesize and the creation
* timer begins by the yin.
*/
event Creating(address owner, uint256 yinId, uint256 yangId, uint256 rechargeEndBlock);
/**
* @notice The minimum payment required for synthesizeWithAuto(). This fee is for
* the gas cost paid by whoever calls bringKydyHome(), and can be updated by the COO address.
*/
uint256 public autoCreationFee = 14 finney;
// Number of the Kydys that are creating a new Kydy.
uint256 public creatingKydys;
/**
* @dev The address of the sibling contract that mixes and combines genes of the two parent Kydys.
*/
GeneSynthesisInterface public geneSynthesis;
/**
* @dev Updates the address of the genetic contract. Only CEO may call this function.
* @param _address An address of the new GeneSynthesis contract instance.
*/
function setGeneSynthesisAddress(address _address) external onlyCEO {
GeneSynthesisInterface candidateContract = GeneSynthesisInterface(_address);
// Verifies that the contract is valid.
require(candidateContract.isGeneSynthesis());
// Sets the new GeneSynthesis contract address.
geneSynthesis = candidateContract;
}
/**
* @dev Checks that the Kydy is able to synthesize.
*/
function _isReadyToSynthesize(Kydy _kyd) internal view returns (bool) {
// Double-checking if there is any pending creation event.
return (_kyd.synthesizingWithId == 0) && (_kyd.rechargeEndBlock <= uint64(block.number));
}
/**
* @dev Checks if a yang Kydy has been approved to synthesize with this yin Kydy.
*/
function _isSynthesizingAllowed(uint256 _yangId, uint256 _yinId) internal view returns (bool) {
address yinOwner = kydyIndexToOwner[_yinId];
address yangOwner = kydyIndexToOwner[_yangId];
return (yinOwner == yangOwner || synthesizeAllowedToAddress[_yangId] == yinOwner);
}
/**
* @dev Sets the rechargeEndTime for the given Kydy, based on its current rechargeIndex.
* The rechargeIndex increases until it hits the cap.
* @param _kyd A reference to the Kydy that needs its timer to be started.
*/
function _triggerRecharge(Kydy storage _kyd) internal {
// Computes the approximation of the end of recharge time in blocks (based on current rechargeIndex).
_kyd.rechargeEndBlock = uint64((recharges[_kyd.rechargeIndex] / secondsPerBlock) + block.number);
// Increases this Kydy's synthesizing count, and the cap is fixed at 12.
if (_kyd.rechargeIndex < 12) {
_kyd.rechargeIndex += 1;
}
}
/**
* @notice Grants approval to another user to synthesize with one of your Kydys.
* @param _address The approved address of the yin Kydy that can synthesize with your yang Kydy.
* @param _yangId Your kydy that _address can now synthesize with.
*/
function approveSynthesizing(address _address, uint256 _yangId)
external
whenNotPaused
{
require(_owns(msg.sender, _yangId));
synthesizeAllowedToAddress[_yangId] = _address;
}
/**
* @dev Updates the minimum payment required for calling bringKydyHome(). Only COO
* can call this function.
*/
function setAutoCreationFee(uint256 value) external onlyCOO {
autoCreationFee = value;
}
/// @dev Checks if this Kydy is creating and if the creation period is complete.
function _isReadyToBringKydyHome(Kydy _yin) private view returns (bool) {
return (_yin.synthesizingWithId != 0) && (_yin.rechargeEndBlock <= uint64(block.number));
}
/**
* @notice Checks if this Kydy is able to synthesize
* @param _kydyId reference the ID of the Kydy
*/
function isReadyToSynthesize(uint256 _kydyId)
public
view
returns (bool)
{
require(_kydyId > 0);
Kydy storage kyd = kydys[_kydyId];
return _isReadyToSynthesize(kyd);
}
/**
* @dev Checks if the Kydy is currently creating.
* @param _kydyId reference the ID of the Kydy
*/
function isCreating(uint256 _kydyId)
public
view
returns (bool)
{
require(_kydyId > 0);
return kydys[_kydyId].synthesizingWithId != 0;
}
/**
* @dev Internal check to see if these yang and yin are a valid couple.
* @param _yin A reference to the Kydy struct of the potential yin.
* @param _yinId The yin's ID.
* @param _yang A reference to the Kydy struct of the potential yang.
* @param _yangId The yang's ID
*/
function _isValidCouple(
Kydy storage _yin,
uint256 _yinId,
Kydy storage _yang,
uint256 _yangId
)
private
view
returns(bool)
{
// Kydy can't synthesize with itself.
if (_yinId == _yangId) {
return false;
}
// Kydys can't synthesize with their parents.
if (_yin.yinId == _yangId || _yin.yangId == _yangId) {
return false;
}
if (_yang.yinId == _yinId || _yang.yangId == _yinId) {
return false;
}
// Skip sibling check for Gen 0
if (_yang.yinId == 0 || _yin.yinId == 0) {
return true;
}
// Kydys can't synthesize with full or half siblings.
if (_yang.yinId == _yin.yinId || _yang.yinId == _yin.yangId) {
return false;
}
if (_yang.yangId == _yin.yinId || _yang.yangId == _yin.yangId) {
return false;
}
return true;
}
/**
* @dev Internal check to see if these yang and yin Kydys, connected via market, are a valid couple for synthesis.
*/
function _canSynthesizeWithViaAuction(uint256 _yinId, uint256 _yangId)
internal
view
returns (bool)
{
Kydy storage yin = kydys[_yinId];
Kydy storage yang = kydys[_yangId];
return _isValidCouple(yin, _yinId, yang, _yangId);
}
/**
* @dev Checks if the two Kydys can synthesize together, including checks for ownership and synthesizing approvals.
* @param _yinId ID of the yin Kydy
* @param _yangId ID of the yang Kydy
*/
function canSynthesizeWith(uint256 _yinId, uint256 _yangId)
external
view
returns(bool)
{
require(_yinId > 0);
require(_yangId > 0);
Kydy storage yin = kydys[_yinId];
Kydy storage yang = kydys[_yangId];
return _isValidCouple(yin, _yinId, yang, _yangId) &&
_isSynthesizingAllowed(_yangId, _yinId);
}
/**
* @dev Internal function to start synthesizing, when all the conditions are met
*/
function _synthesizeWith(uint256 _yinId, uint256 _yangId) internal {
Kydy storage yang = kydys[_yangId];
Kydy storage yin = kydys[_yinId];
// Marks this yin as creating, and make note of who the yang Kydy is.
yin.synthesizingWithId = uint32(_yangId);
// Triggers the recharge for both parents.
_triggerRecharge(yang);
_triggerRecharge(yin);
// Clears synthesizing permission for both parents, just in case.
delete synthesizeAllowedToAddress[_yinId];
delete synthesizeAllowedToAddress[_yangId];
// When a Kydy starts creating, this number is increased.
creatingKydys++;
// Emits the Creating event.
emit Creating(kydyIndexToOwner[_yinId], _yinId, _yangId, yin.rechargeEndBlock);
}
/**
* @dev Synthesis between two approved Kydys. Requires a pre-payment of the fee to the first caller of bringKydyHome().
* @param _yinId ID of the Kydy which will be a yin (will start creation if successful)
* @param _yangId ID of the Kydy which will be a yang (will begin its synthesizing cooldown if successful)
*/
function synthesizeWithAuto(uint256 _yinId, uint256 _yangId)
external
payable
whenNotPaused
{
// Checks for pre-payment.
require(msg.value >= autoCreationFee);
// Caller must be the yin's owner.
require(_owns(msg.sender, _yinId));
// Checks if the caller has valid authority for this synthesis
require(_isSynthesizingAllowed(_yangId, _yinId));
// Gets a reference of the potential yin.
Kydy storage yin = kydys[_yinId];
// Checks that the potential yin is ready to synthesize
require(_isReadyToSynthesize(yin));
// Gets a reference of the potential yang.
Kydy storage yang = kydys[_yangId];
// Checks that the potential yang is ready to synthesize
require(_isReadyToSynthesize(yang));
// Checks that these Kydys are a valid couple.
require(_isValidCouple(
yin,
_yinId,
yang,
_yangId
));
// All checks passed! Yin Kydy starts creating.
_synthesizeWith(_yinId, _yangId);
}
/**
* @notice Let's bring the new Kydy to it's home!
* @param _yinId A Kydy which is ready to bring the newly created Kydy to home.
* @return The Kydy ID of the newly created Kydy.
* @dev The newly created Kydy is transferred to the owner of the yin Kydy. Anyone is welcome to call this function.
*/
function bringKydyHome(uint256 _yinId)
external
whenNotPaused
returns(uint256)
{
// Gets a reference of the yin from storage.
Kydy storage yin = kydys[_yinId];
// Checks that the yin is a valid Kydy.
require(yin.createdTime != 0);
// Checks that the yin is in creation mode, and the creating period is over.
require(_isReadyToBringKydyHome(yin));
// Gets a reference of the yang from storage.
uint256 yangId = yin.synthesizingWithId;
Kydy storage yang = kydys[yangId];
// Ascertains which has the higher generation number between the two parents.
uint16 parentGen = yin.generation;
if (yang.generation > yin.generation) {
parentGen = yang.generation;
}
// The baby Kydy receives its genes
uint256 childGenes = geneSynthesis.synthGenes(yin.genes, yang.genes);
// The baby Kydy is now on blockchain
address owner = kydyIndexToOwner[_yinId];
uint256 kydyId = _createKydy(_yinId, yin.synthesizingWithId, parentGen + 1, childGenes, owner);
// Clears the synthesis status of the parents
delete yin.synthesizingWithId;
// When a baby Kydy is created, this number is decreased back.
creatingKydys--;
// Sends the fee to the person who called this.
msg.sender.transfer(autoCreationFee);
// Returns the new Kydy's ID.
return kydyId;
}
}
contract ERC721Holder is ERC721TokenReceiver {
function onERC721Received(address, address, uint256, bytes) public returns (bytes4) {
return this.onERC721Received.selector;
}
}
/**
* @title Base auction contract of the Dyverse
* @author VREX Lab Co., Ltd
* @dev Contains necessary functions and variables for the auction.
* Inherits `ERC721Holder` contract which is the implementation of the `ERC721TokenReceiver`.
* This is to accept safe transfers.
*/
contract AuctionBase is ERC721Holder {
using SafeMath for uint256;
// Represents an auction on an NFT
struct Auction {
// Current owner of NFT
address seller;
// Price (in wei) of NFT
uint128 price;
// Time when the auction started
// NOTE: 0 if this auction has been concluded
uint64 startedAt;
}
// Reference to contract tracking NFT ownership
ERC721Basic public nonFungibleContract;
// The amount owner takes from the sale, (in basis points, which are 1/100 of a percent).
uint256 public ownerCut;
// Maps token ID to it's corresponding auction.
mapping (uint256 => Auction) tokenIdToAuction;
event AuctionCreated(uint256 tokenId, uint256 price);
event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address bidder);
event AuctionCanceled(uint256 tokenId);
/// @dev Disables sending funds to this contract.
function() external {}
/// @dev A modifier to check if the given value can fit in 64-bits.
modifier canBeStoredWith64Bits(uint256 _value) {
require(_value <= (2**64 - 1));
_;
}
/// @dev A modifier to check if the given value can fit in 128-bits.
modifier canBeStoredWith128Bits(uint256 _value) {
require(_value <= (2**128 - 1));
_;
}
/**
* @dev Returns true if the claimant owns the token.
* @param _claimant An address which to query the ownership of the token.
* @param _tokenId ID of the token to query the owner of.
*/
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
}
/**
* @dev Escrows the NFT. Grants the ownership of the NFT to this contract safely.
* Throws if the escrow fails.
* @param _owner Current owner of the token.
* @param _tokenId ID of the token to escrow.
*/
function _escrow(address _owner, uint256 _tokenId) internal {
nonFungibleContract.safeTransferFrom(_owner, this, _tokenId);
}
/**
* @dev Transfers an NFT owned by this contract to another address safely.
* @param _receiver The receiving address of NFT.
* @param _tokenId ID of the token to transfer.
*/
function _transfer(address _receiver, uint256 _tokenId) internal {
nonFungibleContract.safeTransferFrom(this, _receiver, _tokenId);
}
/**
* @dev Adds an auction to the list of open auctions.
* @param _tokenId ID of the token to be put on auction.
* @param _auction Auction information of this token to open.
*/
function _addAuction(uint256 _tokenId, Auction _auction) internal {
tokenIdToAuction[_tokenId] = _auction;
emit AuctionCreated(
uint256(_tokenId),
uint256(_auction.price)
);
}
/// @dev Cancels the auction which the _seller wants.
function _cancelAuction(uint256 _tokenId, address _seller) internal {
_removeAuction(_tokenId);
_transfer(_seller, _tokenId);
emit AuctionCanceled(_tokenId);
}
/**
* @dev Computes the price and sends it to the seller.
* Note that this does NOT transfer the ownership of the token.
*/
function _bid(uint256 _tokenId, uint256 _bidAmount)
internal
returns (uint256)
{
// Gets a reference of the token from auction storage.
Auction storage auction = tokenIdToAuction[_tokenId];
// Checks that this auction is currently open
require(_isOnAuction(auction));
// Checks that the bid is greater than or equal to the current token price.
uint256 price = _currentPrice(auction);
require(_bidAmount >= price);
// Gets a reference of the seller before the auction gets deleted.
address seller = auction.seller;
// Removes the auction before sending the proceeds to the sender
_removeAuction(_tokenId);
// Transfers proceeds to the seller.
if (price > 0) {
uint256 auctioneerCut = _computeCut(price);
uint256 sellerProceeds = price.sub(auctioneerCut);
seller.transfer(sellerProceeds);
}
// Computes the excess funds included with the bid and transfers it back to bidder.
uint256 bidExcess = _bidAmount - price;
// Returns the exceeded funds.
msg.sender.transfer(bidExcess);
// Emits the AuctionSuccessful event.
emit AuctionSuccessful(_tokenId, price, msg.sender);
return price;
}
/**
* @dev Removes an auction from the list of open auctions.
* @param _tokenId ID of the NFT on auction to be removed.
*/
function _removeAuction(uint256 _tokenId) internal {
delete tokenIdToAuction[_tokenId];
}
/**
* @dev Returns true if the NFT is on auction.
* @param _auction An auction to check if it exists.
*/
function _isOnAuction(Auction storage _auction) internal view returns (bool) {
return (_auction.startedAt > 0);
}
/// @dev Returns the current price of an NFT on auction.
function _currentPrice(Auction storage _auction)
internal
view
returns (uint256)
{
return _auction.price;
}
/**
* @dev Computes the owner's receiving amount from the sale.
* @param _price Sale price of the NFT.
*/
function _computeCut(uint256 _price) internal view returns (uint256) {
return _price * ownerCut / 10000;
}
}
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused() {
require(paused);
_;
}
function pause() onlyOwner whenNotPaused public {
paused = true;
emit Pause();
}
function unpause() onlyOwner whenPaused public {
paused = false;
emit Unpause();
}
}
/**
* @title Auction for NFT.
* @author VREX Lab Co., Ltd
*/
contract Auction is Pausable, AuctionBase {
/**
* @dev Removes all Ether from the contract to the NFT contract.
*/
function withdrawBalance() external {
address nftAddress = address(nonFungibleContract);
require(
msg.sender == owner ||
msg.sender == nftAddress
);
nftAddress.transfer(address(this).balance);
}
/**
* @dev Creates and begins a new auction.
* @param _tokenId ID of the token to creat an auction, caller must be it's owner.
* @param _price Price of the token (in wei).
* @param _seller Seller of this token.
*/
function createAuction(
uint256 _tokenId,
uint256 _price,
address _seller
)
external
whenNotPaused
canBeStoredWith128Bits(_price)
{
require(_owns(msg.sender, _tokenId));
_escrow(msg.sender, _tokenId);
Auction memory auction = Auction(
_seller,
uint128(_price),
uint64(now)
);
_addAuction(_tokenId, auction);
}
/**
* @dev Bids on an open auction, completing the auction and transferring
* ownership of the NFT if enough Ether is supplied.
* @param _tokenId - ID of token to bid on.
*/
function bid(uint256 _tokenId)
external
payable
whenNotPaused
{
_bid(_tokenId, msg.value);
_transfer(msg.sender, _tokenId);
}
/**
* @dev Cancels an auction and returns the NFT to the current owner.
* @param _tokenId ID of the token on auction to cancel.
* @param _seller The seller's address.
*/
function cancelAuction(uint256 _tokenId, address _seller)
external
{
// Requires that this function should only be called from the
// `cancelSaleAuction()` of NFT ownership contract. This function gets
// the _seller directly from it's arguments, so if this check doesn't
// exist, then anyone can cancel the auction! OMG!
require(msg.sender == address(nonFungibleContract));
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
address seller = auction.seller;
require(_seller == seller);
_cancelAuction(_tokenId, seller);
}
/**
* @dev Cancels an auction when the contract is paused.
* Only the owner may do this, and NFTs are returned to the seller.
* @param _tokenId ID of the token on auction to cancel.
*/
function cancelAuctionWhenPaused(uint256 _tokenId)
external
whenPaused
onlyOwner
{
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
_cancelAuction(_tokenId, auction.seller);
}
/**
* @dev Returns the auction information for an NFT
* @param _tokenId ID of the NFT on auction
*/
function getAuction(uint256 _tokenId)
external
view
returns
(
address seller,
uint256 price,
uint256 startedAt
) {
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
return (
auction.seller,
auction.price,
auction.startedAt
);
}
/**
* @dev Returns the current price of the token on auction.
* @param _tokenId ID of the token
*/
function getCurrentPrice(uint256 _tokenId)
external
view
returns (uint256)
{
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
return _currentPrice(auction);
}
}
/**
* @title Auction for synthesizing
* @author VREX Lab Co., Ltd
* @notice Reset fallback function to prevent accidental fund sending to this contract.
*/
contract SynthesizingAuction is Auction {
/**
* @dev Sanity check that allows us to ensure that we are pointing to the
* right auction in our `setSynthesizingAuctionAddress()` call.
*/
bool public isSynthesizingAuction = true;
/**
* @dev Creates a reference to the NFT ownership contract and checks the owner cut is valid
* @param _nftAddress Address of a deployed NFT interface contract
* @param _cut Percent cut which the owner takes on each auction, between 0-10,000.
*/
constructor(address _nftAddress, uint256 _cut) public {
require(_cut <= 10000);
ownerCut = _cut;
ERC721Basic candidateContract = ERC721Basic(_nftAddress);
nonFungibleContract = candidateContract;
}
/**
* @dev Creates and begins a new auction. Since this function is wrapped,
* requires the caller to be KydyCore contract.
* @param _tokenId ID of token to auction, sender must be it's owner.
* @param _price Price of the token (in wei).
* @param _seller Seller of this token.
*/
function createAuction(
uint256 _tokenId,
uint256 _price,
address _seller
)
external
canBeStoredWith128Bits(_price)
{
require(msg.sender == address(nonFungibleContract));
_escrow(_seller, _tokenId);
Auction memory auction = Auction(
_seller,
uint128(_price),
uint64(now)
);
_addAuction(_tokenId, auction);
}
/**
* @dev Places a bid for synthesizing. Requires the caller
* is the KydyCore contract because all bid functions
* should be wrapped. Also returns the Kydy to the
* seller rather than the bidder.
*/
function bid(uint256 _tokenId)
external
payable
{
require(msg.sender == address(nonFungibleContract));
address seller = tokenIdToAuction[_tokenId].seller;
// _bid() checks that the token ID is valid and will throw if bid fails
_bid(_tokenId, msg.value);
// Transfers the Kydy back to the seller, and the bidder will get
// the baby Kydy.
_transfer(seller, _tokenId);
}
}
/**
* @title Auction for sale of Kydys.
* @author VREX Lab Co., Ltd
*/
contract SaleAuction is Auction {
/**
* @dev To make sure we are addressing to the right auction.
*/
bool public isSaleAuction = true;
// Last 5 sale price of Generation 0 Kydys.
uint256[5] public lastGen0SalePrices;
// Total number of Generation 0 Kydys sold.
uint256 public gen0SaleCount;
/**
* @dev Creates a reference to the NFT ownership contract and checks the owner cut is valid
* @param _nftAddress Address of a deployed NFT interface contract
* @param _cut Percent cut which the owner takes on each auction, between 0-10,000.
*/
constructor(address _nftAddress, uint256 _cut) public {
require(_cut <= 10000);
ownerCut = _cut;
ERC721Basic candidateContract = ERC721Basic(_nftAddress);
nonFungibleContract = candidateContract;
}
/**
* @dev Creates and begins a new auction.
* @param _tokenId ID of token to auction, sender must be it's owner.
* @param _price Price of the token (in wei).
* @param _seller Seller of this token.
*/
function createAuction(
uint256 _tokenId,
uint256 _price,
address _seller
)
external
canBeStoredWith128Bits(_price)
{
require(msg.sender == address(nonFungibleContract));
_escrow(_seller, _tokenId);
Auction memory auction = Auction(
_seller,
uint128(_price),
uint64(now)
);
_addAuction(_tokenId, auction);
}
/**
* @dev Updates lastSalePrice only if the seller is nonFungibleContract.
*/
function bid(uint256 _tokenId)
external
payable
{
// _bid verifies token ID
address seller = tokenIdToAuction[_tokenId].seller;
uint256 price = _bid(_tokenId, msg.value);
_transfer(msg.sender, _tokenId);
// If the last sale was not Generation 0 Kydy's, the lastSalePrice doesn't change.
if (seller == address(nonFungibleContract)) {
// Tracks gen0's latest sale prices.
lastGen0SalePrices[gen0SaleCount % 5] = price;
gen0SaleCount++;
}
}
/// @dev Gives the new average Generation 0 sale price after each Generation 0 Kydy sale.
function averageGen0SalePrice() external view returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 5; i++) {
sum = sum.add(lastGen0SalePrices[i]);
}
return sum / 5;
}
}
/**
* @title This contract defines how sales and synthesis auctions for Kydys are created.
* @author VREX Lab Co., Ltd
*/
contract KydyAuction is KydySynthesis {
/**
* @dev The address of the Auction contract which handles ALL sales of Kydys, both user-generated and Generation 0.
*/
SaleAuction public saleAuction;
/**
* @dev The address of another Auction contract which handles synthesis auctions.
*/
SynthesizingAuction public synthesizingAuction;
/**
* @dev Sets the address for the sales auction. Only CEO may call this function.
* @param _address The address of the sale contract.
*/
function setSaleAuctionAddress(address _address) external onlyCEO {
SaleAuction candidateContract = SaleAuction(_address);
// Verifies that the contract is correct
require(candidateContract.isSaleAuction());
// Sets the new sale auction contract address.
saleAuction = candidateContract;
}
/**
* @dev Sets the address to the synthesis auction. Only CEO may call this function.
* @param _address The address of the synthesis contract.
*/
function setSynthesizingAuctionAddress(address _address) external onlyCEO {
SynthesizingAuction candidateContract = SynthesizingAuction(_address);
require(candidateContract.isSynthesizingAuction());
synthesizingAuction = candidateContract;
}
/**
* @dev Creates a Kydy sale.
*/
function createSaleAuction(
uint256 _kydyId,
uint256 _price
)
external
whenNotPaused
{
require(_owns(msg.sender, _kydyId));
require(!isCreating(_kydyId));
_approve(_kydyId, saleAuction);
saleAuction.createAuction(
_kydyId,
_price,
msg.sender
);
}
/**
* @dev Creates a synthesis auction.
*/
function createSynthesizingAuction(
uint256 _kydyId,
uint256 _price
)
external
whenNotPaused
{
require(_owns(msg.sender, _kydyId));
require(isReadyToSynthesize(_kydyId));
_approve(_kydyId, synthesizingAuction);
synthesizingAuction.createAuction(
_kydyId,
_price,
msg.sender
);
}
/**
* @dev After bidding for a synthesis auction is accepted, this starts the actual synthesis process.
* @param _yangId ID of the yang Kydy on the synthesis auction.
* @param _yinId ID of the yin Kydy owned by the bidder.
*/
function bidOnSynthesizingAuction(
uint256 _yangId,
uint256 _yinId
)
external
payable
whenNotPaused
{
require(_owns(msg.sender, _yinId));
require(isReadyToSynthesize(_yinId));
require(_canSynthesizeWithViaAuction(_yinId, _yangId));
uint256 currentPrice = synthesizingAuction.getCurrentPrice(_yangId);
require (msg.value >= currentPrice + autoCreationFee);
synthesizingAuction.bid.value(msg.value - autoCreationFee)(_yangId);
_synthesizeWith(uint32(_yinId), uint32(_yangId));
}
/**
* @dev Cancels a sale and returns the Kydy back to the owner.
* @param _kydyId ID of the Kydy on sale that the owner wishes to cancel.
*/
function cancelSaleAuction(
uint256 _kydyId
)
external
whenNotPaused
{
// Checks if the Kydy is in auction.
require(_owns(saleAuction, _kydyId));
// Gets the seller of the Kydy.
(address seller,,) = saleAuction.getAuction(_kydyId);
// Checks that the caller is the real seller.
require(msg.sender == seller);
// Cancels the sale auction of this kydy by it's seller's request.
saleAuction.cancelAuction(_kydyId, msg.sender);
}
/**
* @dev Cancels an synthesis auction.
* @param _kydyId ID of the Kydy on the synthesis auction.
*/
function cancelSynthesizingAuction(
uint256 _kydyId
)
external
whenNotPaused
{
require(_owns(synthesizingAuction, _kydyId));
(address seller,,) = synthesizingAuction.getAuction(_kydyId);
require(msg.sender == seller);
synthesizingAuction.cancelAuction(_kydyId, msg.sender);
}
/**
* @dev Transfers the balance.
*/
function withdrawAuctionBalances() external onlyCLevel {
saleAuction.withdrawBalance();
synthesizingAuction.withdrawBalance();
}
}
/**
* @title All functions related to creating Kydys
* @author VREX Lab Co., Ltd
*/
contract KydyMinting is KydyAuction {
// Limits of the number of Kydys that COO can create.
uint256 public constant promoCreationLimit = 888;
uint256 public constant gen0CreationLimit = 8888;
uint256 public constant gen0StartingPrice = 10 finney;
// Counts the number of Kydys that COO has created.
uint256 public promoCreatedCount;
uint256 public gen0CreatedCount;
/**
* @dev Creates promo Kydys, up to a limit. Only COO can call this function.
* @param _genes Encoded genes of the Kydy to be created.
* @param _owner Future owner of the created Kydys. COO is the default owner.
*/
function createPromoKydy(uint256 _genes, address _owner) external onlyCOO {
address kydyOwner = _owner;
if (kydyOwner == address(0)) {
kydyOwner = cooAddress;
}
require(promoCreatedCount < promoCreationLimit);
promoCreatedCount++;
_createKydy(0, 0, 0, _genes, kydyOwner);
}
/**
* @dev Creates a new gen0 Kydy with the given genes and
* creates an sale auction of it.
*/
function createGen0Auction(uint256 _genes) external onlyCOO {
require(gen0CreatedCount < gen0CreationLimit);
uint256 kydyId = _createKydy(0, 0, 0, _genes, address(this));
_approve(kydyId, saleAuction);
saleAuction.createAuction(
kydyId,
_computeNextGen0Price(),
address(this)
);
gen0CreatedCount++;
}
/**
* @dev Computes the next gen0 auction price. It will be
* the average of the past 5 prices + 50%.
*/
function _computeNextGen0Price() internal view returns (uint256) {
uint256 averagePrice = saleAuction.averageGen0SalePrice();
// Sanity check to ensure not to overflow arithmetic.
require(averagePrice == uint256(uint128(averagePrice)));
uint256 nextPrice = averagePrice.add(averagePrice / 2);
// New gen0 auction price will not be less than the
// starting price always.
if (nextPrice < gen0StartingPrice) {
nextPrice = gen0StartingPrice;
}
return nextPrice;
}
}
contract KydyTravelInterface {
function balanceOfUnclaimedTT(address _user) public view returns(uint256);
function transferTTProduction(address _from, address _to, uint256 _kydyId) public;
function getProductionOf(address _user) public view returns (uint256);
}
/**
* @title The Dyverse : A decentralized universe of Kydys, the unique 3D characters and avatars on the Blockchain.
* @author VREX Lab Co., Ltd
* @dev This is the main KydyCore contract. It keeps track of the kydys over the blockchain, and manages
* general operation of the contracts, metadata and important addresses, including defining who can withdraw
* the balance from the contract.
*/
contract KydyCore is KydyMinting {
// This is the main Kydy contract. To keep the code upgradable and secure, we broke up the code in two different ways.
// First, we separated auction and gene combination functions into several sibling contracts. This allows us to securely
// fix bugs and upgrade contracts, if necessary. Please note that while we try to make most code open source,
// some code regarding gene combination is not open-source to make it more intriguing for users.
// However, as always, advanced users will be able to figure out how it works.
//
// We also break the core function into a few files, having one contract for each of the major functionalities of the Dyverse.
// The breakdown is as follows:
//
// - KydyBase: This contract defines the most fundamental core functionalities, including data storage and management.
//
// - KydyAccessControl: This contract manages the roles, addresses and constraints for CEO, CFO and COO.
//
// - KydyOwnership: This contract provides the methods required for basic non-fungible token transactions.
//
// - KydySynthesis: This contract contains how new baby Kydy is created via a process called the Synthesis.
//
// - KydyAuction: This contract manages auction creation and bidding.
//
// - KydyMinting: This contract defines how we create new Generation 0 Kydys. There is a limit of 8,888 Gen 0 Kydys.
// Upgraded version of the core contract.
// Should be used when the core contract is broken and an upgrade is required.
address public newContractAddress;
/// @notice Creates the main Kydy smart contract instance.
constructor() public {
// Starts with the contract is paused.
paused = true;
// The creator of the contract is the initial CEO
ceoAddress = msg.sender;
// Starts with the Kydy ID 0 which is invalid one.
// So we don't have generation-0 parent issues.
_createKydy(0, 0, 0, uint256(-1), address(0));
}
/**
* @dev Used to mark the smart contract as upgraded when an upgrade happens.
* @param _v2Address Upgraded version of the core contract.
*/
function setNewAddress(address _v2Address) external onlyCEO whenPaused {
// We'll announce if the upgrade is needed.
newContractAddress = _v2Address;
emit ContractUpgrade(_v2Address);
}
/**
* @dev Rejects all Ether being sent from unregistered addresses, so that users don't accidentally end us Ether.
*/
function() external payable {
require(
msg.sender == address(saleAuction) ||
msg.sender == address(synthesizingAuction)
);
}
/**
* @notice Returns all info about a given Kydy.
* @param _id ID of the Kydy you are enquiring about.
*/
function getKydy(uint256 _id)
external
view
returns (
bool isCreating,
bool isReady,
uint256 rechargeIndex,
uint256 nextActionAt,
uint256 synthesizingWithId,
uint256 createdTime,
uint256 yinId,
uint256 yangId,
uint256 generation,
uint256 genes
) {
Kydy storage kyd = kydys[_id];
// If this is setted to 0 then it's not at creating mode.
isCreating = (kyd.synthesizingWithId != 0);
isReady = (kyd.rechargeEndBlock <= block.number);
rechargeIndex = uint256(kyd.rechargeIndex);
nextActionAt = uint256(kyd.rechargeEndBlock);
synthesizingWithId = uint256(kyd.synthesizingWithId);
createdTime = uint256(kyd.createdTime);
yinId = uint256(kyd.yinId);
yangId = uint256(kyd.yangId);
generation = uint256(kyd.generation);
genes = kyd.genes;
}
/**
* @dev Overrides unpause() to make sure that all external contract addresses are set before unpause.
* @notice This should be public rather than external.
*/
function unpause() public onlyCEO whenPaused {
require(saleAuction != address(0));
require(synthesizingAuction != address(0));
require(geneSynthesis != address(0));
require(newContractAddress == address(0));
// Now the contract actually unpauses.
super.unpause();
}
/// @dev CFO can withdraw the balance available from the contract.
function withdrawBalance() external onlyCFO {
uint256 balance = address(this).balance;
// Subtracts all creation fees needed to be given to the bringKydyHome() callers,
// and plus 1 of margin.
uint256 subtractFees = (creatingKydys + 1) * autoCreationFee;
if (balance > subtractFees) {
cfoAddress.transfer(balance - subtractFees);
}
}
/// @dev Sets new tokenURI API for token metadata.
function setNewTokenURI(string _newTokenURI) external onlyCLevel {
tokenURIBase = _newTokenURI;
}
// An address of Kydy Travel Plugin.
KydyTravelInterface public travelCore;
/**
* @dev Adds the Kydy Travel Plugin contract to the Kydy Core contract.
* @notice We have a plan to add some fun features to the Dyverse.
* Your Kydy will travel all over our world while you carry on with your life.
* During their travel, they will earn some valuable coins which will then be given to you.
* Please stay tuned!
*/
function setTravelCore(address _newTravelCore) external onlyCEO whenPaused {
travelCore = KydyTravelInterface(_newTravelCore);
}
}