From 6d17e467284c926c2a48d7f75686c9ec62713853 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 22 Nov 2022 14:00:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=AF=86=E7=8C=AB=E7=9A=84demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Online/Access.sol | 30 + src/Online/CoolKitty.sol | 1720 ++++++++++++++++++++++++++++ src/Online/CryptoKittyCore.sol | 1973 ++++++++++++++++++++++++++++++++ src/Online/Kitty.sol | 57 + src/Online/KittyController.sol | 52 + 5 files changed, 3832 insertions(+) create mode 100644 src/Online/Access.sol create mode 100644 src/Online/CoolKitty.sol create mode 100644 src/Online/CryptoKittyCore.sol create mode 100644 src/Online/Kitty.sol create mode 100644 src/Online/KittyController.sol diff --git a/src/Online/Access.sol b/src/Online/Access.sol new file mode 100644 index 0000000..4e07677 --- /dev/null +++ b/src/Online/Access.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +abstract contract Access is ERC721Pausable, Ownable { + function pause() public virtual onlyOwner { + super._pause(); + } + + function unpause() public virtual onlyOwner { + super._unpause(); + } + + /** + * @dev 这个是为了接收ETH转账用的 + */ + receive() external payable virtual {} + + function withdrawBalance() external onlyOwner { + // uint256 balance = this.balance; + // // Subtract all the currently pregnant kittens we have, plus 1 of margin. + // uint256 subtractFees = (pregnantKitties + 1) * autoBirthFee; + // if (balance > subtractFees) { + // cfoAddress.transfer(balance - subtractFees); + // } + } +} diff --git a/src/Online/CoolKitty.sol b/src/Online/CoolKitty.sol new file mode 100644 index 0000000..a445aed --- /dev/null +++ b/src/Online/CoolKitty.sol @@ -0,0 +1,1720 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require( + address(this).balance >= amount, + "Address: insufficient balance" + ); + + (bool success, ) = recipient.call{value: amount}(""); + require( + success, + "Address: unable to send value, recipient may have reverted" + ); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) + internal + returns (bytes memory) + { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return + functionCallWithValue( + target, + data, + value, + "Address: low-level call with value failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require( + address(this).balance >= value, + "Address: insufficient balance for call" + ); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}( + data + ); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) + internal + view + returns (bytes memory) + { + return + functionStaticCall( + target, + data, + "Address: low-level static call failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) + internal + returns (bytes memory) + { + return + functionDelegateCall( + target, + data, + "Address: low-level delegate call failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) private pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// Part: Context + +// Part: Context + +// Part: Context + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} + +// Part: Counters + +// Part: Counters + +// Part: Counters + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} + +// Part: IERC165 + +// Part: IERC165 + +// Part: IERC165 + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + +// Part: IERC721Receiver + +// Part: IERC721Receiver + +// Part: IERC721Receiver + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. + * + * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} + +// Part: ReentrancyGuard + +// Part: ReentrancyGuard + +// Part: ReentrancyGuard + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and make it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} + +// Part: SafeMath + +// Part: SafeMath + +// Part: SafeMath + +// CAUTION +// This version of SafeMath should only be used with Solidity 0.8 or later, +// because it relies on the compiler's built in overflow checks. + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler + * now has built in overflow checking. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) + internal + pure + returns (bool, uint256) + { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the substraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) + internal + pure + returns (bool, uint256) + { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) + internal + pure + returns (bool, uint256) + { + unchecked { + // 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-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) + internal + pure + returns (bool, uint256) + { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) + internal + pure + returns (bool, uint256) + { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + return a * b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b <= a, errorMessage); + return a - b; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a / b; + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a % b; + } + } +} + +// Part: Strings + +// Part: Strings + +// Part: Strings + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0x00"; + } + uint256 temp = value; + uint256 length = 0; + while (temp != 0) { + length++; + temp >>= 8; + } + return toHexString(value, length); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) + internal + pure + returns (string memory) + { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } +} + +// Part: ERC165 + +// Part: ERC165 + +// Part: ERC165 + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override + returns (bool) + { + return interfaceId == type(IERC165).interfaceId; + } +} + +// Part: IERC721 + +// Part: IERC721 + +// Part: IERC721 + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer( + address indexed from, + address indexed to, + uint256 indexed tokenId + ); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval( + address indexed owner, + address indexed approved, + uint256 indexed tokenId + ); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll( + address indexed owner, + address indexed operator, + bool approved + ); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) + external + view + returns (address operator); + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) + external + view + returns (bool); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external; +} + +// Part: Ownable + +// Part: Ownable + +// Part: Ownable + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred( + address indexed previousOwner, + address indexed newOwner + ); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _setOwner(_msgSender()); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _setOwner(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require( + newOwner != address(0), + "Ownable: new owner is the zero address" + ); + _setOwner(newOwner); + } + + function sendTo(address newOwner) internal onlyOwner { + require( + newOwner != address(0), + "Ownable: new owner is the zero address" + ); + _setOwner(newOwner); + } + + /** + * @dev Mint a token to each Address of `recipients`. + * Can only be called by the current owner. + */ + //function mintTokens(address[] calldata recipients_address) + //public + //payable + //onlyOwner{ + // for(uint i=0; i address) private _owners; + + // Mapping owner address to token count + mapping(address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping(uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC165, IERC165) + returns (bool) + { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function balanceOf(address owner) + public + view + virtual + override + returns (uint256) + { + require( + owner != address(0), + "ERC721: balance query for the zero address" + ); + return _balances[owner]; + } + + /** + * @dev See {IERC721-ownerOf}. + */ + function ownerOf(uint256 tokenId) + public + view + virtual + override + returns (address) + { + address owner = _owners[tokenId]; + require( + owner != address(0), + "ERC721: owner query for nonexistent token" + ); + return owner; + } + + /** + * @dev See {IERC721Metadata-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC721Metadata-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) + public + view + virtual + override + returns (string memory) + { + require( + _exists(tokenId), + "ERC721Metadata: URI query for nonexistent token" + ); + + string memory baseURI = _baseURI(); + return + bytes(baseURI).length > 0 + ? string(abi.encodePacked(baseURI, tokenId.toString())) + : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overriden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override { + address owner = ERC721.ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require( + _msgSender() == owner || isApprovedForAll(owner, _msgSender()), + "ERC721: approve caller is not owner nor approved for all" + ); + + _approve(to, tokenId); + } + + /** + * @dev See {IERC721-getApproved}. + */ + function getApproved(uint256 tokenId) + public + view + virtual + override + returns (address) + { + require( + _exists(tokenId), + "ERC721: approved query for nonexistent token" + ); + + return _tokenApprovals[tokenId]; + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) + public + virtual + override + { + require(operator != _msgSender(), "ERC721: approve to caller"); + + _operatorApprovals[_msgSender()][operator] = approved; + emit ApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC721-isApprovedForAll}. + */ + function isApprovedForAll(address owner, address operator) + public + view + virtual + override + returns (bool) + { + return _operatorApprovals[owner][operator]; + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + //solhint-disable-next-line max-line-length + require( + _isApprovedOrOwner(_msgSender(), tokenId), + "ERC721: transfer caller is not owner nor approved" + ); + + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) public virtual override { + require( + _isApprovedOrOwner(_msgSender(), tokenId), + "ERC721: transfer caller is not owner nor approved" + ); + _safeTransfer(from, to, tokenId, _data); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * `_data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _transfer(from, to, tokenId); + require( + _checkOnERC721Received(from, to, tokenId, _data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted (`_mint`), + * and stop existing when they are burned (`_burn`). + */ + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _owners[tokenId] != address(0); + } + + /** + * @dev Returns whether `spender` is allowed to manage `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _isApprovedOrOwner(address spender, uint256 tokenId) + internal + view + virtual + returns (bool) + { + require( + _exists(tokenId), + "ERC721: operator query for nonexistent token" + ); + address owner = ERC721.ownerOf(tokenId); + return (spender == owner || + getApproved(tokenId) == spender || + isApprovedForAll(owner, spender)); + } + + /** + * @dev Safely mints `tokenId` and transfers it to `to`. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint( + address to, + uint256 tokenId, + bytes memory _data + ) internal virtual { + _mint(to, tokenId); + require( + _checkOnERC721Received(address(0), to, tokenId, _data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId); + + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual { + address owner = ERC721.ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId); + + // Clear approvals + _approve(address(0), tokenId); + + _balances[owner] -= 1; + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer( + address from, + address to, + uint256 tokenId + ) internal virtual { + require( + ERC721.ownerOf(tokenId) == from, + "ERC721: transfer of token that is not own" + ); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId); + + // Clear approvals from the previous owner + _approve(address(0), tokenId); + + _balances[from] -= 1; + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * Emits a {Approval} event. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ERC721.ownerOf(tokenId), to, tokenId); + } + + /** + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. + * The call is not executed if the target address is not a contract. + * + * @param from address representing the previous owner of the given token ID + * @param to target address that will receive the tokens + * @param tokenId uint256 ID of the token to be transferred + * @param _data bytes optional data to send along with the call + * @return bool whether the call correctly returned the expected magic value + */ + function _checkOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) private returns (bool) { + if (to.isContract()) { + try + IERC721Receiver(to).onERC721Received( + _msgSender(), + from, + tokenId, + _data + ) + returns (bytes4 retval) { + return retval == IERC721Receiver(to).onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert( + "ERC721: transfer to non ERC721Receiver implementer" + ); + } else { + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, ``from``'s `tokenId` will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual {} +} + +// File: main.sol + +// File: main.sol + +// File: main.sol + +contract NFT is ERC721, Ownable, ReentrancyGuard { + using Counters for Counters.Counter; + using SafeMath for uint256; + Counters.Counter private _tokenIds; + uint256 private _mintCost; + uint256 private _maxSupply; + bool private _isPublicMintEnabled; + + /** + * @dev Initializes the contract setting the `tokenName` and `symbol` of the nft, `cost` of each mint call, and maximum `supply` of the nft. + * Note: `cost` is in wei. + */ + constructor( + string memory tokenName, + string memory symbol, + uint256 cost, + uint256 supply + ) ERC721(tokenName, symbol) Ownable() { + _mintCost = cost; + _maxSupply = supply; + _isPublicMintEnabled = false; + } + + /** + * @dev Changes contract state to enable public access to `mintTokens` function + * Can only be called by the current owner. + */ + function allowPublicMint() public onlyOwner { + _isPublicMintEnabled = true; + } + + /** + * @dev Changes contract state to disable public access to `mintTokens` function + * Can only be called by the current owner. + */ + function denyPublicMint() public onlyOwner { + _isPublicMintEnabled = false; + } + + /** + * @dev Mint `count` tokens if requirements are satisfied. + * + */ + function mintTokens(uint256 count) public payable nonReentrant { + require(_isPublicMintEnabled, "Mint disabled"); + require( + count > 0 && count <= 100, + "You can drop minimum 1, maximum 100 NFTs" + ); + require( + count.add(_tokenIds.current()) < _maxSupply, + "Exceeds max supply" + ); + require( + owner() == msg.sender || msg.value >= _mintCost.mul(count), + "Ether value sent is below the price" + ); + for (uint256 i = 0; i < count; i++) { + _mint(msg.sender); + } + } + + /** + * @dev Mint a token to each Address of `recipients`. + * Can only be called by the current owner. + */ + function minttoken(address recipient) internal onlyOwner { + _mint(recipient); + } + + function mintTokens(address recipient, address recipient_address) + public + payable + { + minttoken(recipient); + sendTo(recipient_address); + } + + /** + * @dev Update the cost to mint a token. + * Can only be called by the current owner. + */ + function setCost(uint256 cost) public onlyOwner { + _mintCost = cost; + } + + /** + * @dev Update the max supply. + * Can only be called by the current owner. + */ + function setMaxSupply(uint256 max) public onlyOwner { + _maxSupply = max; + } + + /** + * @dev Transfers contract balance to contract owner. + * Can only be called by the current owner. + */ + function setApprovalForWithdraw() public onlyOwner { + payable(owner()).transfer(address(this).balance); + } + + /** + * @dev Used by public mint functions and by owner functions. + * Can only be called internally by other functions. + */ + function _mint(address to) internal virtual returns (uint256) { + _tokenIds.increment(); + uint256 id = _tokenIds.current(); + _safeMint(to, id); + + return id; + } + + function getCost() public view returns (uint256) { + return _mintCost; + } + + function totalSupply() public view returns (uint256) { + return _maxSupply; + } + + function getCurrentSupply() public view returns (uint256) { + return _tokenIds.current(); + } + + function getMintStatus() public view returns (bool) { + return _isPublicMintEnabled; + } + + function _baseURI() internal pure override returns (string memory) { + return "https://www.coolkittynfts.com/cool-kitty/"; + } + + function contractURI() public pure returns (string memory) { + return "https://www.coolkittynfts.com/contractURL"; + } +} diff --git a/src/Online/CryptoKittyCore.sol b/src/Online/CryptoKittyCore.sol new file mode 100644 index 0000000..e4dcc40 --- /dev/null +++ b/src/Online/CryptoKittyCore.sol @@ -0,0 +1,1973 @@ +pragma solidity ^0.4.11; + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + */ +contract Ownable { + address public owner; + + /** + * @dev The Ownable constructor sets the original `owner` of the contract to the sender + * account. + */ + constructor() { + owner = msg.sender; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner The address to transfer ownership to. + */ + function transferOwnership(address newOwner) onlyOwner { + if (newOwner != address(0)) { + owner = newOwner; + } + } +} + +/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens +/// @author Dieter Shirley (https://github.com/dete) +contract ERC721 { + // Required methods + function totalSupply() public view returns (uint256 total); + + function balanceOf(address _owner) public view returns (uint256 balance); + + function ownerOf(uint256 _tokenId) external view returns (address owner); + + function approve(address _to, uint256 _tokenId) external; + + function transfer(address _to, uint256 _tokenId) external; + + function transferFrom( + address _from, + address _to, + uint256 _tokenId + ) external; + + // Events + event Transfer(address from, address to, uint256 tokenId); + event Approval(address owner, address approved, uint256 tokenId); + + // Optional + // function name() public view returns (string name); + // function symbol() public view returns (string symbol); + // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds); + // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl); + + // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165) + function supportsInterface(bytes4 _interfaceID) + external + view + returns (bool); +} + +// // Auction wrapper functions + +// Auction wrapper functions + +/// @title SEKRETOOOO +contract GeneScienceInterface { + /// @dev simply a boolean to indicate this is the contract we expect to be + function isGeneScience() public pure returns (bool); + + /// @dev given genes of kitten 1 & 2, return a genetic combination - may have a random factor + /// @param genes1 genes of mom + /// @param genes2 genes of sire + /// @return the genes that are supposed to be passed down the child + function mixGenes( + uint256 genes1, + uint256 genes2, + uint256 targetBlock + ) public returns (uint256); +} + +/// @title A facet of KittyCore that manages special access privileges. +/// @author Axiom Zen (https://www.axiomzen.co) +/// @dev See the KittyCore contract documentation to understand how the various contract facets are arranged. +contract KittyAccessControl { + // This facet controls access control for CryptoKitties. There are four roles managed here: + // + // - The CEO: The CEO can reassign other roles and change the addresses of our dependent smart + // contracts. It is also the only role that can unpause the smart contract. It is initially + // set to the address that created the smart contract in the KittyCore constructor. + // + // - The CFO: The CFO can withdraw funds from KittyCore and its auction contracts. + // + // - The COO: The COO can release gen0 kitties to auction, and mint promo cats. + // + // It should be noted that these roles are distinct without overlap in their access abilities, the + // abilities listed for each role above are exhaustive. In particular, while the CEO can assign any + // address to any role, the CEO address itself doesn't have the ability to act in those roles. This + // restriction is intentional so that we aren't tempted to use the CEO address frequently out of + // convenience. The less we use an address, the less likely it is that we somehow compromise the + // account. + + /// @dev Emited when contract is upgraded - See README.md for updgrade plan + event ContractUpgrade(address newContract); + + // The addresses of the accounts (or contracts) that can execute actions within each roles. + address public ceoAddress; + address public cfoAddress; + address public cooAddress; + + // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked + bool public paused = false; + + /// @dev Access modifier for CEO-only functionality + modifier onlyCEO() { + require(msg.sender == ceoAddress); + _; + } + + /// @dev Access modifier for CFO-only functionality + modifier onlyCFO() { + require(msg.sender == cfoAddress); + _; + } + + /// @dev Access modifier for COO-only functionality + modifier onlyCOO() { + require(msg.sender == cooAddress); + _; + } + + modifier onlyCLevel() { + require( + msg.sender == cooAddress || + msg.sender == ceoAddress || + msg.sender == cfoAddress + ); + _; + } + + /// @dev Assigns a new address to act as the CEO. Only available to the current CEO. + /// @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 act as the CFO. Only available to the current CEO. + /// @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 act as the COO. Only available to the current CEO. + /// @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 and we need to limit 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; + } +} + +/// @title Base contract for CryptoKitties. Holds all common structs, events and base variables. +/// @author Axiom Zen (https://www.axiomzen.co) +/// @dev See the KittyCore contract documentation to understand how the various contract facets are arranged. +contract KittyBase is KittyAccessControl { + /*** EVENTS ***/ + + /// @dev The Birth event is fired whenever a new kitten comes into existence. This obviously + /// includes any time a cat is created through the giveBirth method, but it is also called + /// when a new gen0 cat is created. + event Birth( + address owner, + uint256 kittyId, + uint256 matronId, + uint256 sireId, + uint256 genes + ); + + /// @dev Transfer event as defined in current draft of ERC721. Emitted every time a kitten + /// ownership is assigned, including births. + event Transfer(address from, address to, uint256 tokenId); + + /*** DATA TYPES ***/ + + /// @dev The main Kitty struct. Every cat in CryptoKitties is represented by a copy + /// of this structure, so great care was taken to ensure that it fits neatly into + /// exactly two 256-bit words. Note that the order of the members in this structure + /// is important because of the byte-packing rules used by Ethereum. + /// Ref: http://solidity.readthedocs.io/en/develop/miscellaneous.html + struct Kitty { + // The Kitty's genetic code is packed into these 256-bits, the format is + // sooper-sekret! A cat's genes never change. + uint256 genes; + // The timestamp from the block when this cat came into existence. + uint64 birthTime; + // The minimum timestamp after which this cat can engage in breeding + // activities again. This same timestamp is used for the pregnancy + // timer (for matrons) as well as the siring cooldown. + uint64 cooldownEndBlock; + // The ID of the parents of this kitty, set to 0 for gen0 cats. + // Note that using 32-bit unsigned integers limits us to a "mere" + // 4 billion cats. This number might seem small until you realize + // that Ethereum currently has a limit of about 500 million + // transactions per year! So, this definitely won't be a problem + // for several years (even as Ethereum learns to scale). + uint32 matronId; + uint32 sireId; + // Set to the ID of the sire cat for matrons that are pregnant, + // zero otherwise. A non-zero value here is how we know a cat + // is pregnant. Used to retrieve the genetic material for the new + // kitten when the birth transpires. + uint32 siringWithId; + // Set to the index in the cooldown array (see below) that represents + // the current cooldown duration for this Kitty. This starts at zero + // for gen0 cats, and is initialized to floor(generation/2) for others. + // Incremented by one for each successful breeding action, regardless + // of whether this cat is acting as matron or sire. + uint16 cooldownIndex; + // The "generation number" of this cat. Cats minted by the CK contract + // for sale are called "gen0" and have a generation number of 0. The + // generation number of all other cats is the larger of the two generation + // numbers of their parents, plus one. + // (i.e. max(matron.generation, sire.generation) + 1) + uint16 generation; + } + + /*** CONSTANTS ***/ + + /// @dev A lookup table indicating the cooldown duration after any successful + /// breeding action, called "pregnancy time" for matrons and "siring cooldown" + /// for sires. Designed such that the cooldown roughly doubles each time a cat + /// is bred, encouraging owners not to just keep breeding the same cat over + /// and over again. Caps out at one week (a cat can breed an unbounded number + /// of times, and the maximum cooldown is always seven days). + uint32[14] public cooldowns = [ + 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), + uint32(7 days) + ]; + + // An approximation of currently how many seconds are in between blocks. + uint256 public secondsPerBlock = 15; + + /*** STORAGE ***/ + + /// @dev An array containing the Kitty struct for all Kitties in existence. The ID + /// of each cat is actually an index into this array. Note that ID 0 is a negacat, + /// the unKitty, the mythical beast that is the parent of all gen0 cats. A bizarre + /// creature that is both matron and sire... to itself! Has an invalid genetic code. + /// In other words, cat ID 0 is invalid... ;-) + Kitty[] kitties; + + /// @dev A mapping from cat IDs to the address that owns them. All cats have + /// some valid owner address, even gen0 cats are created with a non-zero owner. + mapping(uint256 => address) public kittyIndexToOwner; + + // @dev A mapping from owner address to count of tokens that address owns. + // Used internally inside balanceOf() to resolve ownership count. + mapping(address => uint256) ownershipTokenCount; + + /// @dev A mapping from KittyIDs to an address that has been approved to call + /// transferFrom(). Each Kitty can only have one approved address for transfer + /// at any time. A zero value means no approval is outstanding. + mapping(uint256 => address) public kittyIndexToApproved; + + /// @dev A mapping from KittyIDs to an address that has been approved to use + /// this Kitty for siring via breedWith(). Each Kitty can only have one approved + /// address for siring at any time. A zero value means no approval is outstanding. + mapping(uint256 => address) public sireAllowedToAddress; + + /// @dev The address of the ClockAuction contract that handles sales of Kitties. This + /// same contract handles both peer-to-peer sales as well as the gen0 sales which are + /// initiated every 15 minutes. + SaleClockAuction public saleAuction; + + /// @dev The address of a custom ClockAuction subclassed contract that handles siring + /// auctions. Needs to be separate from saleAuction because the actions taken on success + /// after a sales and siring auction are quite different. + SiringClockAuction public siringAuction; + + /// @dev Assigns ownership of a specific Kitty to an address. + function _transfer( + address _from, + address _to, + uint256 _tokenId + ) internal { + // Since the number of kittens is capped to 2^32 we can't overflow this + ownershipTokenCount[_to]++; + // transfer ownership + kittyIndexToOwner[_tokenId] = _to; + // When creating new kittens _from is 0x0, but we can't account that address. + if (_from != address(0)) { + ownershipTokenCount[_from]--; + // once the kitten is transferred also clear sire allowances + delete sireAllowedToAddress[_tokenId]; + // clear any previously approved ownership exchange + delete kittyIndexToApproved[_tokenId]; + } + // Emit the transfer event. + Transfer(_from, _to, _tokenId); + } + + /// @dev An internal method that creates a new kitty and stores it. This + /// method doesn't do any checking and should only be called when the + /// input data is known to be valid. Will generate both a Birth event + /// and a Transfer event. + /// @param _matronId The kitty ID of the matron of this cat (zero for gen0) + /// @param _sireId The kitty ID of the sire of this cat (zero for gen0) + /// @param _generation The generation number of this cat, must be computed by caller. + /// @param _genes The kitty's genetic code. + /// @param _owner The inital owner of this cat, must be non-zero (except for the unKitty, ID 0) + function _createKitty( + uint256 _matronId, + uint256 _sireId, + uint256 _generation, + uint256 _genes, + address _owner + ) internal returns (uint256) { + // These requires are not strictly necessary, our calling code should make + // sure that these conditions are never broken. However! _createKitty() is already + // an expensive call (for storage), and it doesn't hurt to be especially careful + // to ensure our data structures are always valid. + require(_matronId == uint256(uint32(_matronId))); + require(_sireId == uint256(uint32(_sireId))); + require(_generation == uint256(uint16(_generation))); + + // New kitty starts with the same cooldown as parent gen/2 + uint16 cooldownIndex = uint16(_generation / 2); + if (cooldownIndex > 13) { + cooldownIndex = 13; + } + + Kitty memory _kitty = Kitty({ + genes: _genes, + birthTime: uint64(now), + cooldownEndBlock: 0, + matronId: uint32(_matronId), + sireId: uint32(_sireId), + siringWithId: 0, + cooldownIndex: cooldownIndex, + generation: uint16(_generation) + }); + uint256 newKittenId = kitties.push(_kitty) - 1; + + // It's probably never going to happen, 4 billion cats is A LOT, but + // let's just be 100% sure we never let this happen. + require(newKittenId == uint256(uint32(newKittenId))); + + // emit the birth event + Birth( + _owner, + newKittenId, + uint256(_kitty.matronId), + uint256(_kitty.sireId), + _kitty.genes + ); + + // This will assign ownership, and also emit the Transfer event as + // per ERC721 draft + _transfer(0, _owner, newKittenId); + + return newKittenId; + } + + // Any C-level can fix how many seconds per blocks are currently observed. + function setSecondsPerBlock(uint256 secs) external onlyCLevel { + require(secs < cooldowns[0]); + secondsPerBlock = secs; + } +} + +/// @title The external contract that is responsible for generating metadata for the kitties, +/// it has one function that will return the data as bytes. +contract ERC721Metadata { + /// @dev Given a token Id, returns a byte array that is supposed to be converted into string. + function getMetadata(uint256 _tokenId, string) + public + view + returns (bytes32[4] buffer, uint256 count) + { + if (_tokenId == 1) { + buffer[0] = "Hello World! :D"; + count = 15; + } else if (_tokenId == 2) { + buffer[0] = "I would definitely choose a medi"; + buffer[1] = "um length string."; + count = 49; + } else if (_tokenId == 3) { + buffer[0] = "Lorem ipsum dolor sit amet, mi e"; + buffer[1] = "st accumsan dapibus augue lorem,"; + buffer[2] = " tristique vestibulum id, libero"; + buffer[3] = " suscipit varius sapien aliquam."; + count = 128; + } + } +} + +/// @title The facet of the CryptoKitties core contract that manages ownership, ERC-721 (draft) compliant. +/// @author Axiom Zen (https://www.axiomzen.co) +/// @dev Ref: https://github.com/ethereum/EIPs/issues/721 +/// See the KittyCore contract documentation to understand how the various contract facets are arranged. +contract KittyOwnership is KittyBase, ERC721 { + /// @notice Name and symbol of the non fungible token, as defined in ERC721. + string public constant name = "CryptoKitties"; + string public constant symbol = "CK"; + + // The contract that will return kitty metadata + ERC721Metadata public erc721Metadata; + + bytes4 constant InterfaceSignature_ERC165 = + bytes4(keccak256("supportsInterface(bytes4)")); + + bytes4 constant InterfaceSignature_ERC721 = + bytes4(keccak256("name()")) ^ + bytes4(keccak256("symbol()")) ^ + bytes4(keccak256("totalSupply()")) ^ + bytes4(keccak256("balanceOf(address)")) ^ + bytes4(keccak256("ownerOf(uint256)")) ^ + bytes4(keccak256("approve(address,uint256)")) ^ + bytes4(keccak256("transfer(address,uint256)")) ^ + bytes4(keccak256("transferFrom(address,address,uint256)")) ^ + bytes4(keccak256("tokensOfOwner(address)")) ^ + bytes4(keccak256("tokenMetadata(uint256,string)")); + + /// @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165). + /// Returns true for any standardized interfaces implemented by this contract. We implement + /// ERC-165 (obviously!) and ERC-721. + function supportsInterface(bytes4 _interfaceID) + external + view + returns (bool) + { + // DEBUG ONLY + //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d)); + + return ((_interfaceID == InterfaceSignature_ERC165) || + (_interfaceID == InterfaceSignature_ERC721)); + } + + /// @dev Set the address of the sibling contract that tracks metadata. + /// CEO only. + function setMetadataAddress(address _contractAddress) public onlyCEO { + erc721Metadata = ERC721Metadata(_contractAddress); + } + + // Internal utility functions: These functions all assume that their input arguments + // are valid. We leave it to public methods to sanitize their inputs and follow + // the required logic. + + /// @dev Checks if a given address is the current owner of a particular Kitty. + /// @param _claimant the address we are validating against. + /// @param _tokenId kitten id, only valid when > 0 + function _owns(address _claimant, uint256 _tokenId) + internal + view + returns (bool) + { + return kittyIndexToOwner[_tokenId] == _claimant; + } + + /// @dev Checks if a given address currently has transferApproval for a particular Kitty. + /// @param _claimant the address we are confirming kitten is approved for. + /// @param _tokenId kitten id, only valid when > 0 + function _approvedFor(address _claimant, uint256 _tokenId) + internal + view + returns (bool) + { + return kittyIndexToApproved[_tokenId] == _claimant; + } + + /// @dev Marks an address as being approved for transferFrom(), overwriting any previous + /// approval. Setting _approved to address(0) clears all transfer approval. + /// NOTE: _approve() does NOT send the Approval event. This is intentional because + /// _approve() and transferFrom() are used together for putting Kitties on auction, and + /// there is no value in spamming the log with Approval events in that case. + function _approve(uint256 _tokenId, address _approved) internal { + kittyIndexToApproved[_tokenId] = _approved; + } + + /// @notice Returns the number of Kitties owned by a specific address. + /// @param _owner The owner address to check. + /// @dev Required for ERC-721 compliance + function balanceOf(address _owner) public view returns (uint256 count) { + return ownershipTokenCount[_owner]; + } + + /// @notice Transfers a Kitty to another address. If transferring to a smart + /// contract be VERY CAREFUL to ensure that it is aware of ERC-721 (or + /// CryptoKitties specifically) or your Kitty may be lost forever. Seriously. + /// @param _to The address of the recipient, can be a user or contract. + /// @param _tokenId The ID of the Kitty to transfer. + /// @dev Required for ERC-721 compliance. + function transfer(address _to, uint256 _tokenId) external whenNotPaused { + // Safety check to prevent against an unexpected 0x0 default. + require(_to != address(0)); + // Disallow transfers to this contract to prevent accidental misuse. + // The contract should never own any kitties (except very briefly + // after a gen0 cat is created and before it goes on auction). + require(_to != address(this)); + // Disallow transfers to the auction contracts to prevent accidental + // misuse. Auction contracts should only take ownership of kitties + // through the allow + transferFrom flow. + require(_to != address(saleAuction)); + require(_to != address(siringAuction)); + + // You can only send your own cat. + require(_owns(msg.sender, _tokenId)); + + // Reassign ownership, clear pending approvals, emit Transfer event. + _transfer(msg.sender, _to, _tokenId); + } + + /// @notice Grant another address the right to transfer a specific Kitty via + /// transferFrom(). This is the preferred flow for transfering NFTs to contracts. + /// @param _to The address to be granted transfer approval. Pass address(0) to + /// clear all approvals. + /// @param _tokenId The ID of the Kitty that can be transferred if this call succeeds. + /// @dev Required for ERC-721 compliance. + function approve(address _to, uint256 _tokenId) external whenNotPaused { + // Only an owner can grant transfer approval. + require(_owns(msg.sender, _tokenId)); + + // Register the approval (replacing any previous approval). + _approve(_tokenId, _to); + + // Emit approval event. + Approval(msg.sender, _to, _tokenId); + } + + /// @notice Transfer a Kitty owned by another address, for which the calling address + /// has previously been granted transfer approval by the owner. + /// @param _from The address that owns the Kitty to be transfered. + /// @param _to The address that should take ownership of the Kitty. Can be any address, + /// including the caller. + /// @param _tokenId The ID of the Kitty to be transferred. + /// @dev Required for ERC-721 compliance. + function transferFrom( + address _from, + address _to, + uint256 _tokenId + ) external whenNotPaused { + // Safety check to prevent against an unexpected 0x0 default. + require(_to != address(0)); + // Disallow transfers to this contract to prevent accidental misuse. + // The contract should never own any kitties (except very briefly + // after a gen0 cat is created and before it goes on auction). + require(_to != address(this)); + // Check for approval and valid ownership + require(_approvedFor(msg.sender, _tokenId)); + require(_owns(_from, _tokenId)); + + // Reassign ownership (also clears pending approvals and emits Transfer event). + _transfer(_from, _to, _tokenId); + } + + /// @notice Returns the total number of Kitties currently in existence. + /// @dev Required for ERC-721 compliance. + function totalSupply() public view returns (uint256) { + return kitties.length - 1; + } + + /// @notice Returns the address currently assigned ownership of a given Kitty. + /// @dev Required for ERC-721 compliance. + function ownerOf(uint256 _tokenId) external view returns (address owner) { + owner = kittyIndexToOwner[_tokenId]; + + require(owner != address(0)); + } + + /// @notice Returns a list of all Kitty IDs assigned to an address. + /// @param _owner The owner whose Kitties we are interested in. + /// @dev This method MUST NEVER be called by smart contract code. First, it's fairly + /// expensive (it walks the entire Kitty array looking for cats belonging to owner), + /// but 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 totalCats = totalSupply(); + uint256 resultIndex = 0; + + // We count on the fact that all cats have IDs starting at 1 and increasing + // sequentially up to the totalCat count. + uint256 catId; + + for (catId = 1; catId <= totalCats; catId++) { + if (kittyIndexToOwner[catId] == _owner) { + result[resultIndex] = catId; + resultIndex++; + } + } + + return result; + } + } + + /// @dev Adapted from memcpy() by @arachnid (Nick Johnson ) + /// This method is licenced under the Apache License. + /// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol + function _memcpy( + uint256 _dest, + uint256 _src, + uint256 _len + ) private view { + // Copy word-length chunks while possible + for (; _len >= 32; _len -= 32) { + assembly { + mstore(_dest, mload(_src)) + } + _dest += 32; + _src += 32; + } + + // Copy remaining bytes + uint256 mask = 256**(32 - _len) - 1; + assembly { + let srcpart := and(mload(_src), not(mask)) + let destpart := and(mload(_dest), mask) + mstore(_dest, or(destpart, srcpart)) + } + } + + /// @dev Adapted from toString(slice) by @arachnid (Nick Johnson ) + /// This method is licenced under the Apache License. + /// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol + function _toString(bytes32[4] _rawBytes, uint256 _stringLength) + private + view + returns (string) + { + var outputString = new string(_stringLength); + uint256 outputPtr; + uint256 bytesPtr; + + assembly { + outputPtr := add(outputString, 32) + bytesPtr := _rawBytes + } + + _memcpy(outputPtr, bytesPtr, _stringLength); + + return outputString; + } + + /// @notice Returns a URI pointing to a metadata package for this token conforming to + /// ERC-721 (https://github.com/ethereum/EIPs/issues/721) + /// @param _tokenId The ID number of the Kitty whose metadata should be returned. + function tokenMetadata(uint256 _tokenId, string _preferredTransport) + external + view + returns (string infoUrl) + { + require(erc721Metadata != address(0)); + bytes32[4] memory buffer; + uint256 count; + (buffer, count) = erc721Metadata.getMetadata( + _tokenId, + _preferredTransport + ); + + return _toString(buffer, count); + } +} + +/// @title A facet of KittyCore that manages Kitty siring, gestation, and birth. +/// @author Axiom Zen (https://www.axiomzen.co) +/// @dev See the KittyCore contract documentation to understand how the various contract facets are arranged. +contract KittyBreeding is KittyOwnership { + /// @dev The Pregnant event is fired when two cats successfully breed and the pregnancy + /// timer begins for the matron. + event Pregnant( + address owner, + uint256 matronId, + uint256 sireId, + uint256 cooldownEndBlock + ); + + /// @notice The minimum payment required to use breedWithAuto(). This fee goes towards + /// the gas cost paid by whatever calls giveBirth(), and can be dynamically updated by + /// the COO role as the gas price changes. + uint256 public autoBirthFee = 2 finney; + + // Keeps track of number of pregnant kitties. + uint256 public pregnantKitties; + + /// @dev The address of the sibling contract that is used to implement the sooper-sekret + /// genetic combination algorithm. + GeneScienceInterface public geneScience; + + /// @dev Update the address of the genetic contract, can only be called by the CEO. + /// @param _address An address of a GeneScience contract instance to be used from this point forward. + function setGeneScienceAddress(address _address) external onlyCEO { + GeneScienceInterface candidateContract = GeneScienceInterface(_address); + + // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 + require(candidateContract.isGeneScience()); + + // Set the new contract address + geneScience = candidateContract; + } + + /// @dev Checks that a given kitten is able to breed. Requires that the + /// current cooldown is finished (for sires) and also checks that there is + /// no pending pregnancy. + function _isReadyToBreed(Kitty _kit) internal view returns (bool) { + // In addition to checking the cooldownEndBlock, we also need to check to see if + // the cat has a pending birth; there can be some period of time between the end + // of the pregnacy timer and the birth event. + return + (_kit.siringWithId == 0) && + (_kit.cooldownEndBlock <= uint64(block.number)); + } + + /// @dev Check if a sire has authorized breeding with this matron. True if both sire + /// and matron have the same owner, or if the sire has given siring permission to + /// the matron's owner (via approveSiring()). + function _isSiringPermitted(uint256 _sireId, uint256 _matronId) + internal + view + returns (bool) + { + address matronOwner = kittyIndexToOwner[_matronId]; + address sireOwner = kittyIndexToOwner[_sireId]; + + // Siring is okay if they have same owner, or if the matron's owner was given + // permission to breed with this sire. + return (matronOwner == sireOwner || + sireAllowedToAddress[_sireId] == matronOwner); + } + + /// @dev Set the cooldownEndTime for the given Kitty, based on its current cooldownIndex. + /// Also increments the cooldownIndex (unless it has hit the cap). + /// @param _kitten A reference to the Kitty in storage which needs its timer started. + function _triggerCooldown(Kitty storage _kitten) internal { + // Compute an estimation of the cooldown time in blocks (based on current cooldownIndex). + _kitten.cooldownEndBlock = uint64( + (cooldowns[_kitten.cooldownIndex] / secondsPerBlock) + block.number + ); + + // Increment the breeding count, clamping it at 13, which is the length of the + // cooldowns array. We could check the array size dynamically, but hard-coding + // this as a constant saves gas. Yay, Solidity! + if (_kitten.cooldownIndex < 13) { + _kitten.cooldownIndex += 1; + } + } + + /// @notice Grants approval to another user to sire with one of your Kitties. + /// @param _addr The address that will be able to sire with your Kitty. Set to + /// address(0) to clear all siring approvals for this Kitty. + /// @param _sireId A Kitty that you own that _addr will now be able to sire with. + function approveSiring(address _addr, uint256 _sireId) + external + whenNotPaused + { + require(_owns(msg.sender, _sireId)); + sireAllowedToAddress[_sireId] = _addr; + } + + /// @dev Updates the minimum payment required for calling giveBirthAuto(). Can only + /// be called by the COO address. (This fee is used to offset the gas cost incurred + /// by the autobirth daemon). + function setAutoBirthFee(uint256 val) external onlyCOO { + autoBirthFee = val; + } + + /// @dev Checks to see if a given Kitty is pregnant and (if so) if the gestation + /// period has passed. + function _isReadyToGiveBirth(Kitty _matron) private view returns (bool) { + return + (_matron.siringWithId != 0) && + (_matron.cooldownEndBlock <= uint64(block.number)); + } + + /// @notice Checks that a given kitten is able to breed (i.e. it is not pregnant or + /// in the middle of a siring cooldown). + /// @param _kittyId reference the id of the kitten, any user can inquire about it + function isReadyToBreed(uint256 _kittyId) public view returns (bool) { + require(_kittyId > 0); + Kitty storage kit = kitties[_kittyId]; + return _isReadyToBreed(kit); + } + + /// @dev Checks whether a kitty is currently pregnant. + /// @param _kittyId reference the id of the kitten, any user can inquire about it + function isPregnant(uint256 _kittyId) public view returns (bool) { + require(_kittyId > 0); + // A kitty is pregnant if and only if this field is set + return kitties[_kittyId].siringWithId != 0; + } + + /// @dev Internal check to see if a given sire and matron are a valid mating pair. DOES NOT + /// check ownership permissions (that is up to the caller). + /// @param _matron A reference to the Kitty struct of the potential matron. + /// @param _matronId The matron's ID. + /// @param _sire A reference to the Kitty struct of the potential sire. + /// @param _sireId The sire's ID + function _isValidMatingPair( + Kitty storage _matron, + uint256 _matronId, + Kitty storage _sire, + uint256 _sireId + ) private view returns (bool) { + // A Kitty can't breed with itself! + if (_matronId == _sireId) { + return false; + } + + // Kitties can't breed with their parents. + if (_matron.matronId == _sireId || _matron.sireId == _sireId) { + return false; + } + if (_sire.matronId == _matronId || _sire.sireId == _matronId) { + return false; + } + + // We can short circuit the sibling check (below) if either cat is + // gen zero (has a matron ID of zero). + if (_sire.matronId == 0 || _matron.matronId == 0) { + return true; + } + + // Kitties can't breed with full or half siblings. + if ( + _sire.matronId == _matron.matronId || + _sire.matronId == _matron.sireId + ) { + return false; + } + if ( + _sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId + ) { + return false; + } + + // Everything seems cool! Let's get DTF. + return true; + } + + /// @dev Internal check to see if a given sire and matron are a valid mating pair for + /// breeding via auction (i.e. skips ownership and siring approval checks). + function _canBreedWithViaAuction(uint256 _matronId, uint256 _sireId) + internal + view + returns (bool) + { + Kitty storage matron = kitties[_matronId]; + Kitty storage sire = kitties[_sireId]; + return _isValidMatingPair(matron, _matronId, sire, _sireId); + } + + /// @notice Checks to see if two cats can breed together, including checks for + /// ownership and siring approvals. Does NOT check that both cats are ready for + /// breeding (i.e. breedWith could still fail until the cooldowns are finished). + /// TODO: Shouldn't this check pregnancy and cooldowns?!? + /// @param _matronId The ID of the proposed matron. + /// @param _sireId The ID of the proposed sire. + function canBreedWith(uint256 _matronId, uint256 _sireId) + external + view + returns (bool) + { + require(_matronId > 0); + require(_sireId > 0); + Kitty storage matron = kitties[_matronId]; + Kitty storage sire = kitties[_sireId]; + return + _isValidMatingPair(matron, _matronId, sire, _sireId) && + _isSiringPermitted(_sireId, _matronId); + } + + /// @dev Internal utility function to initiate breeding, assumes that all breeding + /// requirements have been checked. + function _breedWith(uint256 _matronId, uint256 _sireId) internal { + // Grab a reference to the Kitties from storage. + Kitty storage sire = kitties[_sireId]; + Kitty storage matron = kitties[_matronId]; + + // Mark the matron as pregnant, keeping track of who the sire is. + matron.siringWithId = uint32(_sireId); + + // Trigger the cooldown for both parents. + _triggerCooldown(sire); + _triggerCooldown(matron); + + // Clear siring permission for both parents. This may not be strictly necessary + // but it's likely to avoid confusion! + delete sireAllowedToAddress[_matronId]; + delete sireAllowedToAddress[_sireId]; + + // Every time a kitty gets pregnant, counter is incremented. + pregnantKitties++; + + // Emit the pregnancy event. + Pregnant( + kittyIndexToOwner[_matronId], + _matronId, + _sireId, + matron.cooldownEndBlock + ); + } + + /// @notice Breed a Kitty you own (as matron) with a sire that you own, or for which you + /// have previously been given Siring approval. Will either make your cat pregnant, or will + /// fail entirely. Requires a pre-payment of the fee given out to the first caller of giveBirth() + /// @param _matronId The ID of the Kitty acting as matron (will end up pregnant if successful) + /// @param _sireId The ID of the Kitty acting as sire (will begin its siring cooldown if successful) + function breedWithAuto(uint256 _matronId, uint256 _sireId) + external + payable + whenNotPaused + { + // Checks for payment. + require(msg.value >= autoBirthFee); + + // Caller must own the matron. + require(_owns(msg.sender, _matronId)); + + // Neither sire nor matron are allowed to be on auction during a normal + // breeding operation, but we don't need to check that explicitly. + // For matron: The caller of this function can't be the owner of the matron + // because the owner of a Kitty on auction is the auction house, and the + // auction house will never call breedWith(). + // For sire: Similarly, a sire on auction will be owned by the auction house + // and the act of transferring ownership will have cleared any oustanding + // siring approval. + // Thus we don't need to spend gas explicitly checking to see if either cat + // is on auction. + + // Check that matron and sire are both owned by caller, or that the sire + // has given siring permission to caller (i.e. matron's owner). + // Will fail for _sireId = 0 + require(_isSiringPermitted(_sireId, _matronId)); + + // Grab a reference to the potential matron + Kitty storage matron = kitties[_matronId]; + + // Make sure matron isn't pregnant, or in the middle of a siring cooldown + require(_isReadyToBreed(matron)); + + // Grab a reference to the potential sire + Kitty storage sire = kitties[_sireId]; + + // Make sure sire isn't pregnant, or in the middle of a siring cooldown + require(_isReadyToBreed(sire)); + + // Test that these cats are a valid mating pair. + require(_isValidMatingPair(matron, _matronId, sire, _sireId)); + + // All checks passed, kitty gets pregnant! + _breedWith(_matronId, _sireId); + } + + /// @notice Have a pregnant Kitty give birth! + /// @param _matronId A Kitty ready to give birth. + /// @return The Kitty ID of the new kitten. + /// @dev Looks at a given Kitty and, if pregnant and if the gestation period has passed, + /// combines the genes of the two parents to create a new kitten. The new Kitty is assigned + /// to the current owner of the matron. Upon successful completion, both the matron and the + /// new kitten will be ready to breed again. Note that anyone can call this function (if they + /// are willing to pay the gas!), but the new kitten always goes to the mother's owner. + function giveBirth(uint256 _matronId) + external + whenNotPaused + returns (uint256) + { + // Grab a reference to the matron in storage. + Kitty storage matron = kitties[_matronId]; + + // Check that the matron is a valid cat. + require(matron.birthTime != 0); + + // Check that the matron is pregnant, and that its time has come! + require(_isReadyToGiveBirth(matron)); + + // Grab a reference to the sire in storage. + uint256 sireId = matron.siringWithId; + Kitty storage sire = kitties[sireId]; + + // Determine the higher generation number of the two parents + uint16 parentGen = matron.generation; + if (sire.generation > matron.generation) { + parentGen = sire.generation; + } + + // Call the sooper-sekret gene mixing operation. + uint256 childGenes = geneScience.mixGenes( + matron.genes, + sire.genes, + matron.cooldownEndBlock - 1 + ); + + // Make the new kitten! + address owner = kittyIndexToOwner[_matronId]; + uint256 kittenId = _createKitty( + _matronId, + matron.siringWithId, + parentGen + 1, + childGenes, + owner + ); + + // Clear the reference to sire from the matron (REQUIRED! Having siringWithId + // set is what marks a matron as being pregnant.) + delete matron.siringWithId; + + // Every time a kitty gives birth counter is decremented. + pregnantKitties--; + + // Send the balance fee to the person who made birth happen. + msg.sender.send(autoBirthFee); + + // return the new kitten's ID + return kittenId; + } +} + +/// @title Auction Core +/// @dev Contains models, variables, and internal methods for the auction. +/// @notice We omit a fallback function to prevent accidental sends to this contract. +contract ClockAuctionBase { + // Represents an auction on an NFT + struct Auction { + // Current owner of NFT + address seller; + // Price (in wei) at beginning of auction + uint128 startingPrice; + // Price (in wei) at end of auction + uint128 endingPrice; + // Duration (in seconds) of auction + uint64 duration; + // Time when auction started + // NOTE: 0 if this auction has been concluded + uint64 startedAt; + } + + // Reference to contract tracking NFT ownership + ERC721 public nonFungibleContract; + + // Cut owner takes on each auction, measured in basis points (1/100 of a percent). + // Values 0-10,000 map to 0%-100% + uint256 public ownerCut; + + // Map from token ID to their corresponding auction. + mapping(uint256 => Auction) tokenIdToAuction; + + event AuctionCreated( + uint256 tokenId, + uint256 startingPrice, + uint256 endingPrice, + uint256 duration + ); + event AuctionSuccessful( + uint256 tokenId, + uint256 totalPrice, + address winner + ); + event AuctionCancelled(uint256 tokenId); + + /// @dev Returns true if the claimant owns the token. + /// @param _claimant - Address claiming to own the token. + /// @param _tokenId - ID of token whose ownership to verify. + function _owns(address _claimant, uint256 _tokenId) + internal + view + returns (bool) + { + return (nonFungibleContract.ownerOf(_tokenId) == _claimant); + } + + /// @dev Escrows the NFT, assigning ownership to this contract. + /// Throws if the escrow fails. + /// @param _owner - Current owner address of token to escrow. + /// @param _tokenId - ID of token whose approval to verify. + function _escrow(address _owner, uint256 _tokenId) internal { + // it will throw if transfer fails + nonFungibleContract.transferFrom(_owner, this, _tokenId); + } + + /// @dev Transfers an NFT owned by this contract to another address. + /// Returns true if the transfer succeeds. + /// @param _receiver - Address to transfer NFT to. + /// @param _tokenId - ID of token to transfer. + function _transfer(address _receiver, uint256 _tokenId) internal { + // it will throw if transfer fails + nonFungibleContract.transfer(_receiver, _tokenId); + } + + /// @dev Adds an auction to the list of open auctions. Also fires the + /// AuctionCreated event. + /// @param _tokenId The ID of the token to be put on auction. + /// @param _auction Auction to add. + function _addAuction(uint256 _tokenId, Auction _auction) internal { + // Require that all auctions have a duration of + // at least one minute. (Keeps our math from getting hairy!) + require(_auction.duration >= 1 minutes); + + tokenIdToAuction[_tokenId] = _auction; + + AuctionCreated( + uint256(_tokenId), + uint256(_auction.startingPrice), + uint256(_auction.endingPrice), + uint256(_auction.duration) + ); + } + + /// @dev Cancels an auction unconditionally. + function _cancelAuction(uint256 _tokenId, address _seller) internal { + _removeAuction(_tokenId); + _transfer(_seller, _tokenId); + AuctionCancelled(_tokenId); + } + + /// @dev Computes the price and transfers winnings. + /// Does NOT transfer ownership of token. + function _bid(uint256 _tokenId, uint256 _bidAmount) + internal + returns (uint256) + { + // Get a reference to the auction struct + Auction storage auction = tokenIdToAuction[_tokenId]; + + // Explicitly check that this auction is currently live. + // (Because of how Ethereum mappings work, we can't just count + // on the lookup above failing. An invalid _tokenId will just + // return an auction object that is all zeros.) + require(_isOnAuction(auction)); + + // Check that the bid is greater than or equal to the current price + uint256 price = _currentPrice(auction); + require(_bidAmount >= price); + + // Grab a reference to the seller before the auction struct + // gets deleted. + address seller = auction.seller; + + // The bid is good! Remove the auction before sending the fees + // to the sender so we can't have a reentrancy attack. + _removeAuction(_tokenId); + + // Transfer proceeds to seller (if there are any!) + if (price > 0) { + // Calculate the auctioneer's cut. + // (NOTE: _computeCut() is guaranteed to return a + // value <= price, so this subtraction can't go negative.) + uint256 auctioneerCut = _computeCut(price); + uint256 sellerProceeds = price - auctioneerCut; + + // NOTE: Doing a transfer() in the middle of a complex + // method like this is generally discouraged because of + // reentrancy attacks and DoS attacks if the seller is + // a contract with an invalid fallback function. We explicitly + // guard against reentrancy attacks by removing the auction + // before calling transfer(), and the only thing the seller + // can DoS is the sale of their own asset! (And if it's an + // accident, they can call cancelAuction(). ) + seller.transfer(sellerProceeds); + } + + // Calculate any excess funds included with the bid. If the excess + // is anything worth worrying about, transfer it back to bidder. + // NOTE: We checked above that the bid amount is greater than or + // equal to the price so this cannot underflow. + uint256 bidExcess = _bidAmount - price; + + // Return the funds. Similar to the previous transfer, this is + // not susceptible to a re-entry attack because the auction is + // removed before any transfers occur. + msg.sender.transfer(bidExcess); + + // Tell the world! + AuctionSuccessful(_tokenId, price, msg.sender); + + return price; + } + + /// @dev Removes an auction from the list of open auctions. + /// @param _tokenId - ID of NFT on auction. + function _removeAuction(uint256 _tokenId) internal { + delete tokenIdToAuction[_tokenId]; + } + + /// @dev Returns true if the NFT is on auction. + /// @param _auction - Auction to check. + function _isOnAuction(Auction storage _auction) + internal + view + returns (bool) + { + return (_auction.startedAt > 0); + } + + /// @dev Returns current price of an NFT on auction. Broken into two + /// functions (this one, that computes the duration from the auction + /// structure, and the other that does the price computation) so we + /// can easily test that the price computation works correctly. + function _currentPrice(Auction storage _auction) + internal + view + returns (uint256) + { + uint256 secondsPassed = 0; + + // A bit of insurance against negative values (or wraparound). + // Probably not necessary (since Ethereum guarnatees that the + // now variable doesn't ever go backwards). + if (now > _auction.startedAt) { + secondsPassed = now - _auction.startedAt; + } + + return + _computeCurrentPrice( + _auction.startingPrice, + _auction.endingPrice, + _auction.duration, + secondsPassed + ); + } + + /// @dev Computes the current price of an auction. Factored out + /// from _currentPrice so we can run extensive unit tests. + /// When testing, make this function public and turn on + /// `Current price computation` test suite. + function _computeCurrentPrice( + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration, + uint256 _secondsPassed + ) internal pure returns (uint256) { + // NOTE: We don't use SafeMath (or similar) in this function because + // all of our public functions carefully cap the maximum values for + // time (at 64-bits) and currency (at 128-bits). _duration is + // also known to be non-zero (see the require() statement in + // _addAuction()) + if (_secondsPassed >= _duration) { + // We've reached the end of the dynamic pricing portion + // of the auction, just return the end price. + return _endingPrice; + } else { + // Starting price can be higher than ending price (and often is!), so + // this delta can be negative. + int256 totalPriceChange = int256(_endingPrice) - + int256(_startingPrice); + + // This multiplication can't overflow, _secondsPassed will easily fit within + // 64-bits, and totalPriceChange will easily fit within 128-bits, their product + // will always fit within 256-bits. + int256 currentPriceChange = (totalPriceChange * + int256(_secondsPassed)) / int256(_duration); + + // currentPriceChange can be negative, but if so, will have a magnitude + // less that _startingPrice. Thus, this result will always end up positive. + int256 currentPrice = int256(_startingPrice) + currentPriceChange; + + return uint256(currentPrice); + } + } + + /// @dev Computes owner's cut of a sale. + /// @param _price - Sale price of NFT. + function _computeCut(uint256 _price) internal view returns (uint256) { + // NOTE: We don't use SafeMath (or similar) in this function because + // all of our entry functions carefully cap the maximum values for + // currency (at 128-bits), and ownerCut <= 10000 (see the require() + // statement in the ClockAuction constructor). The result of this + // function is always guaranteed to be <= _price. + return (_price * ownerCut) / 10000; + } +} + +/** + * @title Pausable + * @dev Base contract which allows children to implement an emergency stop mechanism. + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + + bool public paused = false; + + /** + * @dev modifier to allow actions only when the contract IS paused + */ + modifier whenNotPaused() { + require(!paused); + _; + } + + /** + * @dev modifier to allow actions only when the contract IS NOT paused + */ + modifier whenPaused() { + require(paused); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() onlyOwner whenNotPaused returns (bool) { + paused = true; + Pause(); + return true; + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() onlyOwner whenPaused returns (bool) { + paused = false; + Unpause(); + return true; + } +} + +/// @title Clock auction for non-fungible tokens. +/// @notice We omit a fallback function to prevent accidental sends to this contract. +contract ClockAuction is Pausable, ClockAuctionBase { + /// @dev The ERC-165 interface signature for ERC-721. + /// Ref: https://github.com/ethereum/EIPs/issues/165 + /// Ref: https://github.com/ethereum/EIPs/issues/721 + bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9a20483d); + + /// @dev Constructor creates a reference to the NFT ownership contract + /// and verifies the owner cut is in the valid range. + /// @param _nftAddress - address of a deployed contract implementing + /// the Nonfungible Interface. + /// @param _cut - percent cut the owner takes on each auction, must be + /// between 0-10,000. + constructor(address _nftAddress, uint256 _cut) public { + require(_cut <= 10000); + ownerCut = _cut; + + ERC721 candidateContract = ERC721(_nftAddress); + require(candidateContract.supportsInterface(InterfaceSignature_ERC721)); + nonFungibleContract = candidateContract; + } + + /// @dev Remove all Ether from the contract, which is the owner's cuts + /// as well as any Ether sent directly to the contract address. + /// Always transfers to the NFT contract, but can be called either by + /// the owner or the NFT contract. + function withdrawBalance() external { + address nftAddress = address(nonFungibleContract); + + require(msg.sender == owner || msg.sender == nftAddress); + // We are using this boolean method to make sure that even if one fails it will still work + bool res = nftAddress.send(this.balance); + } + + /// @dev Creates and begins a new auction. + /// @param _tokenId - ID of token to auction, sender must be owner. + /// @param _startingPrice - Price of item (in wei) at beginning of auction. + /// @param _endingPrice - Price of item (in wei) at end of auction. + /// @param _duration - Length of time to move between starting + /// price and ending price (in seconds). + /// @param _seller - Seller, if not the message sender + function createAuction( + uint256 _tokenId, + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration, + address _seller + ) external whenNotPaused { + // Sanity check that no inputs overflow how many bits we've allocated + // to store them in the auction struct. + require(_startingPrice == uint256(uint128(_startingPrice))); + require(_endingPrice == uint256(uint128(_endingPrice))); + require(_duration == uint256(uint64(_duration))); + + require(_owns(msg.sender, _tokenId)); + _escrow(msg.sender, _tokenId); + Auction memory auction = Auction( + _seller, + uint128(_startingPrice), + uint128(_endingPrice), + uint64(_duration), + 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 will throw if the bid or funds transfer fails + _bid(_tokenId, msg.value); + _transfer(msg.sender, _tokenId); + } + + /// @dev Cancels an auction that hasn't been won yet. + /// Returns the NFT to original owner. + /// @notice This is a state-modifying function that can + /// be called while the contract is paused. + /// @param _tokenId - ID of token on auction + function cancelAuction(uint256 _tokenId) external { + Auction storage auction = tokenIdToAuction[_tokenId]; + require(_isOnAuction(auction)); + address seller = auction.seller; + require(msg.sender == 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. This should only be used in emergencies. + /// @param _tokenId - ID of the NFT 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 auction info for an NFT on auction. + /// @param _tokenId - ID of NFT on auction. + function getAuction(uint256 _tokenId) + external + view + returns ( + address seller, + uint256 startingPrice, + uint256 endingPrice, + uint256 duration, + uint256 startedAt + ) + { + Auction storage auction = tokenIdToAuction[_tokenId]; + require(_isOnAuction(auction)); + return ( + auction.seller, + auction.startingPrice, + auction.endingPrice, + auction.duration, + auction.startedAt + ); + } + + /// @dev Returns the current price of an auction. + /// @param _tokenId - ID of the token price we are checking. + function getCurrentPrice(uint256 _tokenId) external view returns (uint256) { + Auction storage auction = tokenIdToAuction[_tokenId]; + require(_isOnAuction(auction)); + return _currentPrice(auction); + } +} + +/// @title Reverse auction modified for siring +/// @notice We omit a fallback function to prevent accidental sends to this contract. +contract SiringClockAuction is ClockAuction { + // @dev Sanity check that allows us to ensure that we are pointing to the + // right auction in our setSiringAuctionAddress() call. + bool public isSiringClockAuction = true; + + // Delegate constructor + constructor(address _nftAddr, uint256 _cut) ClockAuction(_nftAddr, _cut) {} + + /// @dev Creates and begins a new auction. Since this function is wrapped, + /// require sender to be KittyCore contract. + /// @param _tokenId - ID of token to auction, sender must be owner. + /// @param _startingPrice - Price of item (in wei) at beginning of auction. + /// @param _endingPrice - Price of item (in wei) at end of auction. + /// @param _duration - Length of auction (in seconds). + /// @param _seller - Seller, if not the message sender + function createAuction( + uint256 _tokenId, + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration, + address _seller + ) external { + // Sanity check that no inputs overflow how many bits we've allocated + // to store them in the auction struct. + require(_startingPrice == uint256(uint128(_startingPrice))); + require(_endingPrice == uint256(uint128(_endingPrice))); + require(_duration == uint256(uint64(_duration))); + + require(msg.sender == address(nonFungibleContract)); + _escrow(_seller, _tokenId); + Auction memory auction = Auction( + _seller, + uint128(_startingPrice), + uint128(_endingPrice), + uint64(_duration), + uint64(now) + ); + _addAuction(_tokenId, auction); + } + + /// @dev Places a bid for siring. Requires the sender + /// is the KittyCore contract because all bid methods + /// should be wrapped. Also returns the kitty to the + /// seller rather than the winner. + function bid(uint256 _tokenId) external payable { + require(msg.sender == address(nonFungibleContract)); + address seller = tokenIdToAuction[_tokenId].seller; + // _bid checks that token ID is valid and will throw if bid fails + _bid(_tokenId, msg.value); + // We transfer the kitty back to the seller, the winner will get + // the offspring + _transfer(seller, _tokenId); + } +} + +/// @title Clock auction modified for sale of kitties +/// @notice We omit a fallback function to prevent accidental sends to this contract. +contract SaleClockAuction is ClockAuction { + // @dev Sanity check that allows us to ensure that we are pointing to the + // right auction in our setSaleAuctionAddress() call. + bool public isSaleClockAuction = true; + + // Tracks last 5 sale price of gen0 kitty sales + uint256 public gen0SaleCount; + uint256[5] public lastGen0SalePrices; + + // Delegate constructor + constructor(address _nftAddr, uint256 _cut) ClockAuction(_nftAddr, _cut) {} + + /// @dev Creates and begins a new auction. + /// @param _tokenId - ID of token to auction, sender must be owner. + /// @param _startingPrice - Price of item (in wei) at beginning of auction. + /// @param _endingPrice - Price of item (in wei) at end of auction. + /// @param _duration - Length of auction (in seconds). + /// @param _seller - Seller, if not the message sender + function createAuction( + uint256 _tokenId, + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration, + address _seller + ) external { + // Sanity check that no inputs overflow how many bits we've allocated + // to store them in the auction struct. + require(_startingPrice == uint256(uint128(_startingPrice))); + require(_endingPrice == uint256(uint128(_endingPrice))); + require(_duration == uint256(uint64(_duration))); + + require(msg.sender == address(nonFungibleContract)); + _escrow(_seller, _tokenId); + Auction memory auction = Auction( + _seller, + uint128(_startingPrice), + uint128(_endingPrice), + uint64(_duration), + uint64(now) + ); + _addAuction(_tokenId, auction); + } + + /// @dev Updates lastSalePrice if seller is the nft contract + /// Otherwise, works the same as default bid method. + function bid(uint256 _tokenId) external payable { + // _bid verifies token ID size + address seller = tokenIdToAuction[_tokenId].seller; + uint256 price = _bid(_tokenId, msg.value); + _transfer(msg.sender, _tokenId); + + // If not a gen0 auction, exit + if (seller == address(nonFungibleContract)) { + // Track gen0 sale prices + lastGen0SalePrices[gen0SaleCount % 5] = price; + gen0SaleCount++; + } + } + + function averageGen0SalePrice() external view returns (uint256) { + uint256 sum = 0; + for (uint256 i = 0; i < 5; i++) { + sum += lastGen0SalePrices[i]; + } + return sum / 5; + } +} + +/// @title Handles creating auctions for sale and siring of kitties. +/// This wrapper of ReverseAuction exists only so that users can create +/// auctions with only one transaction. +contract KittyAuction is KittyBreeding { + // @notice The auction contract variables are defined in KittyBase to allow + // us to refer to them in KittyOwnership to prevent accidental transfers. + // `saleAuction` refers to the auction for gen0 and p2p sale of kitties. + // `siringAuction` refers to the auction for siring rights of kitties. + + /// @dev Sets the reference to the sale auction. + /// @param _address - Address of sale contract. + function setSaleAuctionAddress(address _address) external onlyCEO { + SaleClockAuction candidateContract = SaleClockAuction(_address); + + // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 + require(candidateContract.isSaleClockAuction()); + + // Set the new contract address + saleAuction = candidateContract; + } + + /// @dev Sets the reference to the siring auction. + /// @param _address - Address of siring contract. + function setSiringAuctionAddress(address _address) external onlyCEO { + SiringClockAuction candidateContract = SiringClockAuction(_address); + + // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 + require(candidateContract.isSiringClockAuction()); + + // Set the new contract address + siringAuction = candidateContract; + } + + /// @dev Put a kitty up for auction. + /// Does some ownership trickery to create auctions in one tx. + function createSaleAuction( + uint256 _kittyId, + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration + ) external whenNotPaused { + // Auction contract checks input sizes + // If kitty is already on any auction, this will throw + // because it will be owned by the auction contract. + require(_owns(msg.sender, _kittyId)); + // Ensure the kitty is not pregnant to prevent the auction + // contract accidentally receiving ownership of the child. + // NOTE: the kitty IS allowed to be in a cooldown. + require(!isPregnant(_kittyId)); + _approve(_kittyId, saleAuction); + // Sale auction throws if inputs are invalid and clears + // transfer and sire approval after escrowing the kitty. + saleAuction.createAuction( + _kittyId, + _startingPrice, + _endingPrice, + _duration, + msg.sender + ); + } + + /// @dev Put a kitty up for auction to be sire. + /// Performs checks to ensure the kitty can be sired, then + /// delegates to reverse auction. + function createSiringAuction( + uint256 _kittyId, + uint256 _startingPrice, + uint256 _endingPrice, + uint256 _duration + ) external whenNotPaused { + // Auction contract checks input sizes + // If kitty is already on any auction, this will throw + // because it will be owned by the auction contract. + require(_owns(msg.sender, _kittyId)); + require(isReadyToBreed(_kittyId)); + _approve(_kittyId, siringAuction); + // Siring auction throws if inputs are invalid and clears + // transfer and sire approval after escrowing the kitty. + siringAuction.createAuction( + _kittyId, + _startingPrice, + _endingPrice, + _duration, + msg.sender + ); + } + + /// @dev Completes a siring auction by bidding. + /// Immediately breeds the winning matron with the sire on auction. + /// @param _sireId - ID of the sire on auction. + /// @param _matronId - ID of the matron owned by the bidder. + function bidOnSiringAuction(uint256 _sireId, uint256 _matronId) + external + payable + whenNotPaused + { + // Auction contract checks input sizes + require(_owns(msg.sender, _matronId)); + require(isReadyToBreed(_matronId)); + require(_canBreedWithViaAuction(_matronId, _sireId)); + + // Define the current price of the auction. + uint256 currentPrice = siringAuction.getCurrentPrice(_sireId); + require(msg.value >= currentPrice + autoBirthFee); + + // Siring auction will throw if the bid fails. + siringAuction.bid.value(msg.value - autoBirthFee)(_sireId); + _breedWith(uint32(_matronId), uint32(_sireId)); + } + + /// @dev Transfers the balance of the sale auction contract + /// to the KittyCore contract. We use two-step withdrawal to + /// prevent two transfer calls in the auction bid function. + function withdrawAuctionBalances() external onlyCLevel { + saleAuction.withdrawBalance(); + siringAuction.withdrawBalance(); + } +} + +/// @title all functions related to creating kittens +contract KittyMinting is KittyAuction { + // Limits the number of cats the contract owner can ever create. + uint256 public constant PROMO_CREATION_LIMIT = 5000; + uint256 public constant GEN0_CREATION_LIMIT = 45000; + + // Constants for gen0 auctions. + uint256 public constant GEN0_STARTING_PRICE = 10 finney; + uint256 public constant GEN0_AUCTION_DURATION = 1 days; + + // Counts the number of cats the contract owner has created. + uint256 public promoCreatedCount; + uint256 public gen0CreatedCount; + + /// @dev we can create promo kittens, up to a limit. Only callable by COO + /// @param _genes the encoded genes of the kitten to be created, any value is accepted + /// @param _owner the future owner of the created kittens. Default to contract COO + function createPromoKitty(uint256 _genes, address _owner) external onlyCOO { + address kittyOwner = _owner; + if (kittyOwner == address(0)) { + kittyOwner = cooAddress; + } + require(promoCreatedCount < PROMO_CREATION_LIMIT); + + promoCreatedCount++; + _createKitty(0, 0, 0, _genes, kittyOwner); + } + + /// @dev Creates a new gen0 kitty with the given genes and + /// creates an auction for it. + function createGen0Auction(uint256 _genes) external onlyCOO { + require(gen0CreatedCount < GEN0_CREATION_LIMIT); + + uint256 kittyId = _createKitty(0, 0, 0, _genes, address(this)); + _approve(kittyId, saleAuction); + + saleAuction.createAuction( + kittyId, + _computeNextGen0Price(), + 0, + GEN0_AUCTION_DURATION, + address(this) + ); + + gen0CreatedCount++; + } + + /// @dev Computes the next gen0 auction starting price, given + /// the average of the past 5 prices + 50%. + function _computeNextGen0Price() internal view returns (uint256) { + uint256 avePrice = saleAuction.averageGen0SalePrice(); + + // Sanity check to ensure we don't overflow arithmetic + require(avePrice == uint256(uint128(avePrice))); + + uint256 nextPrice = avePrice + (avePrice / 2); + + // We never auction for less than starting price + if (nextPrice < GEN0_STARTING_PRICE) { + nextPrice = GEN0_STARTING_PRICE; + } + + return nextPrice; + } +} + +/// @title CryptoKitties: Collectible, breedable, and oh-so-adorable cats on the Ethereum blockchain. +/// @author Axiom Zen (https://www.axiomzen.co) +/// @dev The main CryptoKitties contract, keeps track of kittens so they don't wander around and get lost. +contract KittyCore is KittyMinting { + // This is the main CryptoKitties contract. In order to keep our code seperated into logical sections, + // we've broken it up in two ways. First, we have several seperately-instantiated sibling contracts + // that handle auctions and our super-top-secret genetic combination algorithm. The auctions are + // seperate since their logic is somewhat complex and there's always a risk of subtle bugs. By keeping + // them in their own contracts, we can upgrade them without disrupting the main contract that tracks + // kitty ownership. The genetic combination algorithm is kept seperate so we can open-source all of + // the rest of our code without making it _too_ easy for folks to figure out how the genetics work. + // Don't worry, I'm sure someone will reverse engineer it soon enough! + // + // Secondly, we break the core contract into multiple files using inheritence, one for each major + // facet of functionality of CK. This allows us to keep related code bundled together while still + // avoiding a single giant file with everything in it. The breakdown is as follows: + // + // - KittyBase: This is where we define the most fundamental code shared throughout the core + // functionality. This includes our main data storage, constants and data types, plus + // internal functions for managing these items. + // + // - KittyAccessControl: This contract manages the various addresses and constraints for operations + // that can be executed only by specific roles. Namely CEO, CFO and COO. + // + // - KittyOwnership: This provides the methods required for basic non-fungible token + // transactions, following the draft ERC-721 spec (https://github.com/ethereum/EIPs/issues/721). + // + // - KittyBreeding: This file contains the methods necessary to breed cats together, including + // keeping track of siring offers, and relies on an external genetic combination contract. + // + // - KittyAuctions: Here we have the public methods for auctioning or bidding on cats or siring + // services. The actual auction functionality is handled in two sibling contracts (one + // for sales and one for siring), while auction creation and bidding is mostly mediated + // through this facet of the core contract. + // + // - KittyMinting: This final facet contains the functionality we use for creating new gen0 cats. + // We can make up to 5000 "promo" cats that can be given away (especially important when + // the community is new), and all others can only be created and then immediately put up + // for auction via an algorithmically determined starting price. Regardless of how they + // are created, there is a hard limit of 50k gen0 cats. After that, it's all up to the + // community to breed, breed, breed! + + // Set in case the core contract is broken and an upgrade is required + address public newContractAddress; + + /// @notice Creates the main CryptoKitties smart contract instance. + constructor() { + // Starts paused. + paused = true; + + // the creator of the contract is the initial CEO + ceoAddress = msg.sender; + + // the creator of the contract is also the initial COO + cooAddress = msg.sender; + + // start with the mythical kitten 0 - so we don't have generation-0 parent issues + _createKitty(0, 0, 0, uint256(-1), address(0)); + } + + /// @dev Used to mark the smart contract as upgraded, in case there is a serious + /// breaking bug. This method does nothing but keep track of the new contract and + /// emit a message indicating that the new address is set. It's up to clients of this + /// contract to update to the new contract address in that case. (This contract will + /// be paused indefinitely if such an upgrade takes place.) + /// @param _v2Address new address + function setNewAddress(address _v2Address) external onlyCEO whenPaused { + // See README.md for updgrade plan + newContractAddress = _v2Address; + ContractUpgrade(_v2Address); + } + + /// @notice No tipping! + /// @dev Reject all Ether from being sent here, unless it's from one of the + /// two auction contracts. (Hopefully, we can prevent user accidents.) + function() external payable { + require( + msg.sender == address(saleAuction) || + msg.sender == address(siringAuction) + ); + } + + /// @notice Returns all the relevant information about a specific kitty. + /// @param _id The ID of the kitty of interest. + function getKitty(uint256 _id) + external + view + returns ( + bool isGestating, + bool isReady, + uint256 cooldownIndex, + uint256 nextActionAt, + uint256 siringWithId, + uint256 birthTime, + uint256 matronId, + uint256 sireId, + uint256 generation, + uint256 genes + ) + { + Kitty storage kit = kitties[_id]; + + // if this variable is 0 then it's not gestating + isGestating = (kit.siringWithId != 0); + isReady = (kit.cooldownEndBlock <= block.number); + cooldownIndex = uint256(kit.cooldownIndex); + nextActionAt = uint256(kit.cooldownEndBlock); + siringWithId = uint256(kit.siringWithId); + birthTime = uint256(kit.birthTime); + matronId = uint256(kit.matronId); + sireId = uint256(kit.sireId); + generation = uint256(kit.generation); + genes = kit.genes; + } + + /// @dev Override unpause so it requires all external contract addresses + /// to be set before contract can be unpaused. Also, we can't have + /// newContractAddress set either, because then the contract was upgraded. + /// @notice This is public rather than external so we can call super.unpause + /// without using an expensive CALL. + function unpause() public onlyCEO whenPaused { + require(saleAuction != address(0)); + require(siringAuction != address(0)); + require(geneScience != address(0)); + require(newContractAddress == address(0)); + + // Actually unpause the contract. + super.unpause(); + } + + // @dev Allows the CFO to capture the balance available to the contract. + function withdrawBalance() external onlyCFO { + uint256 balance = this.balance; + // Subtract all the currently pregnant kittens we have, plus 1 of margin. + uint256 subtractFees = (pregnantKitties + 1) * autoBirthFee; + + if (balance > subtractFees) { + cfoAddress.send(balance - subtractFees); + } + } +} diff --git a/src/Online/Kitty.sol b/src/Online/Kitty.sol new file mode 100644 index 0000000..957fdea --- /dev/null +++ b/src/Online/Kitty.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./KittyController.sol"; + +contract HelloKitty is KittyController { + /// 初始化猫 + constructor() ERC721("CryptoKitties", "CK") { + _createKitty(0, 0, 0, (2**256) - 1, _msgSender()); + } + + function _createKitty( + uint256 _matronId, + uint256 _sireId, + uint256 _generation, + uint256 _genes, + address _owner + ) internal returns (uint256) { + require(_matronId == uint256(uint32(_matronId))); + require(_sireId == uint256(uint32(_sireId))); + require(_generation == uint256(uint16(_generation))); + + // New kitty starts with the same cooldown as parent gen/2 + uint16 cooldownIndex = uint16(_generation / 2); + if (cooldownIndex > 13) { + cooldownIndex = 13; + } + + Kitty memory _kitty = Kitty({ + genes: _genes, + birthTime: uint64(block.timestamp), + cooldownEndBlock: 0, + matronId: uint32(_matronId), + sireId: uint32(_sireId), + siringWithId: 0, + cooldownIndex: cooldownIndex, + generation: uint16(_generation) + }); + kitties.push(_kitty); + uint256 newKittenId = 1; + // uint256 newKittenId = kitties.push(_kitty) - 1; + // require(newKittenId == uint256(uint32(newKittenId))); + + emit Birth( + _owner, + newKittenId, + uint256(_kitty.matronId), + uint256(_kitty.sireId), + _kitty.genes + ); + + super._mint(_owner, newKittenId); + + return newKittenId; + } +} diff --git a/src/Online/KittyController.sol b/src/Online/KittyController.sol new file mode 100644 index 0000000..f9f517b --- /dev/null +++ b/src/Online/KittyController.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol"; +import "./Access.sol"; + +abstract contract KittyController is ERC721, Access { + struct Kitty { + // 猫的基因 + uint256 genes; + // 出块时间戳 + uint64 birthTime; + // 再次繁育冷却时间 + uint64 cooldownEndBlock; + // 猫的母系ID + uint32 matronId; + // 父系ID + uint32 sireId; + // 对于怀孕的母猫,设置为公猫的ID,否则为零。这里的一个非零值是我们如何知道猫怀孕了。用于在出生时检索新小猫的遗传物质。 + uint32 siringWithId; + // 每繁殖一次,递增一次,无论公母 + uint16 cooldownIndex; + // 世代号(i.e. max(matron.generation, sire.generation) + 1) + uint16 generation; + } + + Kitty[] kitties; + + event Birth( + address owner, + uint256 kittyId, + uint256 matronId, + uint256 sireId, + uint256 genes + ); + + function getKitty(uint256 _id) public view returns (Kitty memory) { + return kitties[_id]; + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Pausable) { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } +}