├── .gitignore ├── LICENSE ├── README.md ├── clock_auction.sol ├── clock_auction_base.sol ├── erc20.sol ├── erc721.sol ├── erc721_metadata.sol ├── gen_science_interface.sol ├── ownable.sol ├── panda_access_control.sol ├── panda_auction.sol ├── panda_base.sol ├── panda_breeding.sol ├── panda_core.sol ├── panda_minting.sol ├── panda_ownership.sol ├── pausable.sol ├── sale_clock_auction.sol ├── sale_clock_auction_erc20.sol └── siring_clock_auction.sol /.gitignore: -------------------------------------------------------------------------------- 1 | adoption.sol 2 | gene.sol 3 | panda.sol 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basics of CryptoPandas: 2 | 3 | CryptoPandas is composed of 4 public facing contracts. Below we'll provide an overview on these contracts: 4 | 5 | ##### PandaCore.sol - `` 6 | 7 | Also referred as the main contract, is where Pandas and their ownership are stored. 8 | This also mediates all the main operations, such as breeding, exchange, and part of auctions. 9 | 10 | ##### SaleClockAuction.sol - `` 11 | 12 | Where users are expected to acquire their gen0 kitten. It is also a marketplace where anyone can post their panda for auction. 13 | [See Dutch/Clock auction](https://en.wikipedia.org/wiki/Dutch_auction) - note we also accept an increasing price. 14 | ps: Panda auctions take an initial time and duration, and after duration is over they are not closed. Instead they hold the final price indefinitely 15 | 16 | 17 | ##### SiringClockAuction.sol - `` 18 | 19 | A marketplace where any user can offer their Panda as a potential sire for any takers. 20 | 21 | ##### GeneScience.sol 22 | 23 | It's a mystery! Not public for this release. 24 | 25 | ## Basic Breeding Rules 26 | 27 | - 2 Pandas in defferent gender(of course) can breed, except with their siblings or parents. 28 | - They must also be either owned by the same user, or one user offers a Panda siring to another user. 29 | - If you have 2 Panda that fits this condition, you can use one as sire (father) and the other as matron (mother). 30 | - After breeding, the assigned matron will be pregnant and under cooldown, the father will also be under cooldown. 31 | - Cooldown stands for the period of time the panda cannot perform any breeding actions, a Panda's cooldown increases each time it breeds. 32 | - The gensis pandas keep their cooldown time fixed. 33 | - After the matron cooldown is complete, it can give birth, and engage in breeding again right away. 34 | - As pandas breed their cooldowns increase. For the bounty program, the table can be found in `PandaBase.sol`. 35 | 36 | 37 | ## Breeding With Panda You Don't Own 38 | 39 | In this case you provide the matron, and have a permissioned sire. There are 2 practical cases this can happen: 40 | 41 | 1. The owner of a Panda can allow siring to another ethereum address. 42 | 2. The owner of another Panda can submit their Panda into a siring clock auction, and the bidder must supply their matron. 43 | 44 | ## Trading 45 | 46 | There are 2 main ways to trade: direct transfer, or auctions: 47 | 48 | 1. A owner of a panda can put their panda to a clock sales auction. 49 | 2. Either transfer to another Ethereum address, or approve to another address. 50 | 51 | # Common functions 52 | 53 | Here's what we expect to be the most usual flow, and what function are to be called. 54 | 55 | 1. COO will periodically put a panda to gen0 auction (Main `createGen0Auction()`) 56 | 1. user go an buy gen0 panda (Sale Auction `bid()`) 57 | 1. user can get panda data (Main `getPanda()`) 58 | 1. user can breed their own pandas (Main `breedWithAuto()`) 59 | 1. after cooldown is passed, the COO would have a pregnant panda giving birth (Main `giveBirth()`) 60 | 1. user can offer one of their pandas as sire via auction (Main `createSiringAuction()`) 61 | 1. user can offer their panda as sire to another user (Main `approveSiring()`) 62 | 1. user can bid on an active siring auction (Main `createSiringAuction()`) 63 | 1. user can put their panda for sale on auction (Main `createSaleAuction()`) 64 | 1. user can buy a panda that is on auction from another user (Sale Auction `bid()`) 65 | 1. user can check info of a panda that is to auction (Sale/Siring Auction `getAuction()`) 66 | 1. user can cancel an auction they started (Sale/Siring Auction `cancelAuction()`) 67 | 1. user can transfer a panda they own to another user (Main `transfer()`) 68 | 1. user can allow another user to take ownership of a panda they own (Main `approve()`) 69 | 1. once an user has a panda ownership approved, they can claim a panda (Main `transferFrom()`) 70 | 1. CEO is the only one that may replace COO or CFO (Main `setCEO()` `setCFO()` `setCOO()`) 71 | 1. COO can mint and distribute gensis panda (Main `createWizzPanda()`) before the count reach the total quota. 72 | 1. COO can transfer the balance from auctions (Main `withdrawAuctionBalances()`) 73 | 1. CFO can drain funds from main contract (Main `withdrawBalance()`) 74 | 75 | Please see complete the explanation of roles in `PandaAccessControl.sol` 76 | There are more rules and comments on the source code, please refer to the code and tests in case things don't work as first expected. 77 | 78 | 79 | -------------------------------------------------------------------------------- /clock_auction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./pausable.sol"; 4 | import "./clock_auction_base.sol"; 5 | 6 | /// @title Clock auction for non-fungible tokens. 7 | /// @notice We omit a fallback function to prevent accidental sends to this contract. 8 | contract ClockAuction is Pausable, ClockAuctionBase { 9 | 10 | /// @dev The ERC-165 interface signature for ERC-721. 11 | /// Ref: https://github.com/ethereum/EIPs/issues/165 12 | /// Ref: https://github.com/ethereum/EIPs/issues/721 13 | bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9a20483d); 14 | 15 | /// @dev Constructor creates a reference to the NFT ownership contract 16 | /// and verifies the owner cut is in the valid range. 17 | /// @param _nftAddress - address of a deployed contract implementing 18 | /// the Nonfungible Interface. 19 | /// @param _cut - percent cut the owner takes on each auction, must be 20 | /// between 0-10,000. 21 | function ClockAuction(address _nftAddress, uint256 _cut) public { 22 | require(_cut <= 10000); 23 | ownerCut = _cut; 24 | 25 | ERC721 candidateContract = ERC721(_nftAddress); 26 | require(candidateContract.supportsInterface(InterfaceSignature_ERC721)); 27 | nonFungibleContract = candidateContract; 28 | } 29 | 30 | /// @dev Remove all Ether from the contract, which is the owner's cuts 31 | /// as well as any Ether sent directly to the contract address. 32 | /// Always transfers to the NFT contract, but can be called either by 33 | /// the owner or the NFT contract. 34 | function withdrawBalance() external { 35 | address nftAddress = address(nonFungibleContract); 36 | 37 | require( 38 | msg.sender == owner || 39 | msg.sender == nftAddress 40 | ); 41 | // We are using this boolean method to make sure that even if one fails it will still work 42 | bool res = nftAddress.send(this.balance); 43 | } 44 | 45 | /// @dev Creates and begins a new auction. 46 | /// @param _tokenId - ID of token to auction, sender must be owner. 47 | /// @param _startingPrice - Price of item (in wei) at beginning of auction. 48 | /// @param _endingPrice - Price of item (in wei) at end of auction. 49 | /// @param _duration - Length of time to move between starting 50 | /// price and ending price (in seconds). 51 | /// @param _seller - Seller, if not the message sender 52 | function createAuction( 53 | uint256 _tokenId, 54 | uint256 _startingPrice, 55 | uint256 _endingPrice, 56 | uint256 _duration, 57 | address _seller 58 | ) 59 | external 60 | whenNotPaused 61 | { 62 | // Sanity check that no inputs overflow how many bits we've allocated 63 | // to store them in the auction struct. 64 | require(_startingPrice == uint256(uint128(_startingPrice))); 65 | require(_endingPrice == uint256(uint128(_endingPrice))); 66 | require(_duration == uint256(uint64(_duration))); 67 | 68 | require(_owns(msg.sender, _tokenId)); 69 | _escrow(msg.sender, _tokenId); 70 | Auction memory auction = Auction( 71 | _seller, 72 | uint128(_startingPrice), 73 | uint128(_endingPrice), 74 | uint64(_duration), 75 | uint64(now), 76 | 0 77 | ); 78 | _addAuction(_tokenId, auction); 79 | } 80 | 81 | /// @dev Bids on an open auction, completing the auction and transferring 82 | /// ownership of the NFT if enough Ether is supplied. 83 | /// @param _tokenId - ID of token to bid on. 84 | function bid(uint256 _tokenId) 85 | external 86 | payable 87 | whenNotPaused 88 | { 89 | // _bid will throw if the bid or funds transfer fails 90 | _bid(_tokenId, msg.value); 91 | _transfer(msg.sender, _tokenId); 92 | } 93 | 94 | /// @dev Cancels an auction that hasn't been won yet. 95 | /// Returns the NFT to original owner. 96 | /// @notice This is a state-modifying function that can 97 | /// be called while the contract is paused. 98 | /// @param _tokenId - ID of token on auction 99 | function cancelAuction(uint256 _tokenId) 100 | external 101 | { 102 | Auction storage auction = tokenIdToAuction[_tokenId]; 103 | require(_isOnAuction(auction)); 104 | address seller = auction.seller; 105 | require(msg.sender == seller); 106 | _cancelAuction(_tokenId, seller); 107 | } 108 | 109 | /// @dev Cancels an auction when the contract is paused. 110 | /// Only the owner may do this, and NFTs are returned to 111 | /// the seller. This should only be used in emergencies. 112 | /// @param _tokenId - ID of the NFT on auction to cancel. 113 | function cancelAuctionWhenPaused(uint256 _tokenId) 114 | whenPaused 115 | onlyOwner 116 | external 117 | { 118 | Auction storage auction = tokenIdToAuction[_tokenId]; 119 | require(_isOnAuction(auction)); 120 | _cancelAuction(_tokenId, auction.seller); 121 | } 122 | 123 | /// @dev Returns auction info for an NFT on auction. 124 | /// @param _tokenId - ID of NFT on auction. 125 | function getAuction(uint256 _tokenId) 126 | external 127 | view 128 | returns 129 | ( 130 | address seller, 131 | uint256 startingPrice, 132 | uint256 endingPrice, 133 | uint256 duration, 134 | uint256 startedAt 135 | ) { 136 | Auction storage auction = tokenIdToAuction[_tokenId]; 137 | require(_isOnAuction(auction)); 138 | return ( 139 | auction.seller, 140 | auction.startingPrice, 141 | auction.endingPrice, 142 | auction.duration, 143 | auction.startedAt 144 | ); 145 | } 146 | 147 | /// @dev Returns the current price of an auction. 148 | /// @param _tokenId - ID of the token price we are checking. 149 | function getCurrentPrice(uint256 _tokenId) 150 | external 151 | view 152 | returns (uint256) 153 | { 154 | Auction storage auction = tokenIdToAuction[_tokenId]; 155 | require(_isOnAuction(auction)); 156 | return _currentPrice(auction); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /clock_auction_base.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./erc721.sol"; 5 | import "./erc20.sol"; 6 | 7 | 8 | 9 | /// @title Auction Core 10 | /// @dev Contains models, variables, and internal methods for the auction. 11 | /// @notice We omit a fallback function to prevent accidental sends to this contract. 12 | contract ClockAuctionBase { 13 | 14 | // Represents an auction on an NFT 15 | struct Auction { 16 | // Current owner of NFT 17 | address seller; 18 | // Price (in wei) at beginning of auction 19 | uint128 startingPrice; 20 | // Price (in wei) at end of auction 21 | uint128 endingPrice; 22 | // Duration (in seconds) of auction 23 | uint64 duration; 24 | // Time when auction started 25 | // NOTE: 0 if this auction has been concluded 26 | uint64 startedAt; 27 | // is this auction for gen0 panda 28 | uint64 isGen0; 29 | } 30 | 31 | // Reference to contract tracking NFT ownership 32 | ERC721 public nonFungibleContract; 33 | 34 | // Cut owner takes on each auction, measured in basis points (1/100 of a percent). 35 | // Values 0-10,000 map to 0%-100% 36 | uint256 public ownerCut; 37 | 38 | // Map from token ID to their corresponding auction. 39 | mapping (uint256 => Auction) tokenIdToAuction; 40 | 41 | event AuctionCreated(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration); 42 | event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner); 43 | event AuctionCancelled(uint256 tokenId); 44 | 45 | /// @dev Returns true if the claimant owns the token. 46 | /// @param _claimant - Address claiming to own the token. 47 | /// @param _tokenId - ID of token whose ownership to verify. 48 | function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) { 49 | return (nonFungibleContract.ownerOf(_tokenId) == _claimant); 50 | } 51 | 52 | /// @dev Escrows the NFT, assigning ownership to this contract. 53 | /// Throws if the escrow fails. 54 | /// @param _owner - Current owner address of token to escrow. 55 | /// @param _tokenId - ID of token whose approval to verify. 56 | function _escrow(address _owner, uint256 _tokenId) internal { 57 | // it will throw if transfer fails 58 | nonFungibleContract.transferFrom(_owner, this, _tokenId); 59 | } 60 | 61 | /// @dev Transfers an NFT owned by this contract to another address. 62 | /// Returns true if the transfer succeeds. 63 | /// @param _receiver - Address to transfer NFT to. 64 | /// @param _tokenId - ID of token to transfer. 65 | function _transfer(address _receiver, uint256 _tokenId) internal { 66 | // it will throw if transfer fails 67 | nonFungibleContract.transfer(_receiver, _tokenId); 68 | } 69 | 70 | /// @dev Adds an auction to the list of open auctions. Also fires the 71 | /// AuctionCreated event. 72 | /// @param _tokenId The ID of the token to be put on auction. 73 | /// @param _auction Auction to add. 74 | function _addAuction(uint256 _tokenId, Auction _auction) internal { 75 | // Require that all auctions have a duration of 76 | // at least one minute. (Keeps our math from getting hairy!) 77 | require(_auction.duration >= 1 minutes); 78 | 79 | tokenIdToAuction[_tokenId] = _auction; 80 | 81 | AuctionCreated( 82 | uint256(_tokenId), 83 | uint256(_auction.startingPrice), 84 | uint256(_auction.endingPrice), 85 | uint256(_auction.duration) 86 | ); 87 | } 88 | 89 | /// @dev Cancels an auction unconditionally. 90 | function _cancelAuction(uint256 _tokenId, address _seller) internal { 91 | _removeAuction(_tokenId); 92 | _transfer(_seller, _tokenId); 93 | AuctionCancelled(_tokenId); 94 | } 95 | 96 | /// @dev Computes the price and transfers winnings. 97 | /// Does NOT transfer ownership of token. 98 | function _bid(uint256 _tokenId, uint256 _bidAmount) 99 | internal 100 | returns (uint256) 101 | { 102 | // Get a reference to the auction struct 103 | Auction storage auction = tokenIdToAuction[_tokenId]; 104 | 105 | // Explicitly check that this auction is currently live. 106 | // (Because of how Ethereum mappings work, we can't just count 107 | // on the lookup above failing. An invalid _tokenId will just 108 | // return an auction object that is all zeros.) 109 | require(_isOnAuction(auction)); 110 | 111 | // Check that the bid is greater than or equal to the current price 112 | uint256 price = _currentPrice(auction); 113 | require(_bidAmount >= price); 114 | 115 | // Grab a reference to the seller before the auction struct 116 | // gets deleted. 117 | address seller = auction.seller; 118 | 119 | // The bid is good! Remove the auction before sending the fees 120 | // to the sender so we can't have a reentrancy attack. 121 | _removeAuction(_tokenId); 122 | 123 | // Transfer proceeds to seller (if there are any!) 124 | if (price > 0) { 125 | // Calculate the auctioneer's cut. 126 | // (NOTE: _computeCut() is guaranteed to return a 127 | // value <= price, so this subtraction can't go negative.) 128 | uint256 auctioneerCut = _computeCut(price); 129 | uint256 sellerProceeds = price - auctioneerCut; 130 | 131 | // NOTE: Doing a transfer() in the middle of a complex 132 | // method like this is generally discouraged because of 133 | // reentrancy attacks and DoS attacks if the seller is 134 | // a contract with an invalid fallback function. We explicitly 135 | // guard against reentrancy attacks by removing the auction 136 | // before calling transfer(), and the only thing the seller 137 | // can DoS is the sale of their own asset! (And if it's an 138 | // accident, they can call cancelAuction(). ) 139 | seller.transfer(sellerProceeds); 140 | } 141 | 142 | // Calculate any excess funds included with the bid. If the excess 143 | // is anything worth worrying about, transfer it back to bidder. 144 | // NOTE: We checked above that the bid amount is greater than or 145 | // equal to the price so this cannot underflow. 146 | uint256 bidExcess = _bidAmount - price; 147 | 148 | // Return the funds. Similar to the previous transfer, this is 149 | // not susceptible to a re-entry attack because the auction is 150 | // removed before any transfers occur. 151 | msg.sender.transfer(bidExcess); 152 | 153 | // Tell the world! 154 | AuctionSuccessful(_tokenId, price, msg.sender); 155 | 156 | return price; 157 | } 158 | 159 | /// @dev Computes the price and transfers winnings. 160 | /// Does NOT transfer ownership of token. 161 | function _bidERC20(address _erc20Address,address _buyerAddress, uint256 _tokenId, uint256 _bidAmount) 162 | internal 163 | returns (uint256) 164 | { 165 | // Get a reference to the auction struct 166 | Auction storage auction = tokenIdToAuction[_tokenId]; 167 | 168 | // Explicitly check that this auction is currently live. 169 | // (Because of how Ethereum mappings work, we can't just count 170 | // on the lookup above failing. An invalid _tokenId will just 171 | // return an auction object that is all zeros.) 172 | require(_isOnAuction(auction)); 173 | 174 | // Check that the bid is greater than or equal to the current price 175 | uint256 price = _currentPrice(auction); 176 | require(_bidAmount >= price); 177 | 178 | // Grab a reference to the seller before the auction struct 179 | // gets deleted. 180 | address seller = auction.seller; 181 | 182 | // The bid is good! Remove the auction before sending the fees 183 | // to the sender so we can't have a reentrancy attack. 184 | _removeAuction(_tokenId); 185 | 186 | // Transfer proceeds to seller (if there are any!) 187 | if (price > 0) { 188 | // Calculate the auctioneer's cut. 189 | // (NOTE: _computeCut() is guaranteed to return a 190 | // value <= price, so this subtraction can't go negative.) 191 | uint256 auctioneerCut = _computeCut(price); 192 | uint256 sellerProceeds = price - auctioneerCut; 193 | 194 | // Send Erc20 Token to seller should call Erc20 contract 195 | // Reference to contract 196 | require(ERC20(_erc20Address).transferFrom(_buyerAddress,seller,sellerProceeds)); 197 | } 198 | 199 | // Tell the world! 200 | AuctionSuccessful(_tokenId, price, msg.sender); 201 | 202 | return price; 203 | } 204 | 205 | /// @dev Removes an auction from the list of open auctions. 206 | /// @param _tokenId - ID of NFT on auction. 207 | function _removeAuction(uint256 _tokenId) internal { 208 | delete tokenIdToAuction[_tokenId]; 209 | } 210 | 211 | /// @dev Returns true if the NFT is on auction. 212 | /// @param _auction - Auction to check. 213 | function _isOnAuction(Auction storage _auction) internal view returns (bool) { 214 | return (_auction.startedAt > 0); 215 | } 216 | 217 | /// @dev Returns current price of an NFT on auction. Broken into two 218 | /// functions (this one, that computes the duration from the auction 219 | /// structure, and the other that does the price computation) so we 220 | /// can easily test that the price computation works correctly. 221 | function _currentPrice(Auction storage _auction) 222 | internal 223 | view 224 | returns (uint256) 225 | { 226 | uint256 secondsPassed = 0; 227 | 228 | // A bit of insurance against negative values (or wraparound). 229 | // Probably not necessary (since Ethereum guarnatees that the 230 | // now variable doesn't ever go backwards). 231 | if (now > _auction.startedAt) { 232 | secondsPassed = now - _auction.startedAt; 233 | } 234 | 235 | return _computeCurrentPrice( 236 | _auction.startingPrice, 237 | _auction.endingPrice, 238 | _auction.duration, 239 | secondsPassed 240 | ); 241 | } 242 | 243 | /// @dev Computes the current price of an auction. Factored out 244 | /// from _currentPrice so we can run extensive unit tests. 245 | /// When testing, make this function public and turn on 246 | /// `Current price computation` test suite. 247 | function _computeCurrentPrice( 248 | uint256 _startingPrice, 249 | uint256 _endingPrice, 250 | uint256 _duration, 251 | uint256 _secondsPassed 252 | ) 253 | internal 254 | pure 255 | returns (uint256) 256 | { 257 | // NOTE: We don't use SafeMath (or similar) in this function because 258 | // all of our public functions carefully cap the maximum values for 259 | // time (at 64-bits) and currency (at 128-bits). _duration is 260 | // also known to be non-zero (see the require() statement in 261 | // _addAuction()) 262 | if (_secondsPassed >= _duration) { 263 | // We've reached the end of the dynamic pricing portion 264 | // of the auction, just return the end price. 265 | return _endingPrice; 266 | } else { 267 | // Starting price can be higher than ending price (and often is!), so 268 | // this delta can be negative. 269 | int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice); 270 | 271 | // This multiplication can't overflow, _secondsPassed will easily fit within 272 | // 64-bits, and totalPriceChange will easily fit within 128-bits, their product 273 | // will always fit within 256-bits. 274 | int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration); 275 | 276 | // currentPriceChange can be negative, but if so, will have a magnitude 277 | // less that _startingPrice. Thus, this result will always end up positive. 278 | int256 currentPrice = int256(_startingPrice) + currentPriceChange; 279 | 280 | return uint256(currentPrice); 281 | } 282 | } 283 | 284 | /// @dev Computes owner's cut of a sale. 285 | /// @param _price - Sale price of NFT. 286 | function _computeCut(uint256 _price) internal view returns (uint256) { 287 | // NOTE: We don't use SafeMath (or similar) in this function because 288 | // all of our entry functions carefully cap the maximum values for 289 | // currency (at 128-bits), and ownerCut <= 10000 (see the require() 290 | // statement in the ClockAuction constructor). The result of this 291 | // function is always guaranteed to be <= _price. 292 | return _price * ownerCut / 10000; 293 | } 294 | 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /erc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | contract ERC20 { 5 | function totalSupply() constant returns (uint supply); 6 | function balanceOf( address who ) constant returns (uint value); 7 | function allowance( address owner, address spender ) constant returns (uint _allowance); 8 | 9 | function transfer( address to, uint value) returns (bool ok); 10 | function transferFrom( address from, address to, uint value) returns (bool ok); 11 | function approve( address spender, uint value ) returns (bool ok); 12 | 13 | event Transfer( address indexed from, address indexed to, uint value); 14 | event Approval( address indexed owner, address indexed spender, uint value); 15 | } -------------------------------------------------------------------------------- /erc721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens 4 | /// @author Dieter Shirley (https://github.com/dete) 5 | contract ERC721 { 6 | // Required methods 7 | function totalSupply() public view returns (uint256 total); 8 | function balanceOf(address _owner) public view returns (uint256 balance); 9 | function ownerOf(uint256 _tokenId) external view returns (address owner); 10 | function approve(address _to, uint256 _tokenId) external; 11 | function transfer(address _to, uint256 _tokenId) external; 12 | function transferFrom(address _from, address _to, uint256 _tokenId) external; 13 | 14 | // Events 15 | event Transfer(address from, address to, uint256 tokenId); 16 | event Approval(address owner, address approved, uint256 tokenId); 17 | 18 | // Optional 19 | // function name() public view returns (string name); 20 | // function symbol() public view returns (string symbol); 21 | // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds); 22 | // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl); 23 | 24 | // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165) 25 | function supportsInterface(bytes4 _interfaceID) external view returns (bool); 26 | } -------------------------------------------------------------------------------- /erc721_metadata.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | /// @title The external contract that is responsible for generating metadata for the pandas, 3 | /// it has one function that will return the data as bytes. 4 | contract ERC721Metadata { 5 | /// @dev Given a token Id, returns a byte array that is supposed to be converted into string. 6 | function getMetadata(uint256 _tokenId, string) public view returns (bytes32[4] buffer, uint256 count) { 7 | if (_tokenId == 1) { 8 | buffer[0] = "Hello World! :D"; 9 | count = 15; 10 | } else if (_tokenId == 2) { 11 | buffer[0] = "I would definitely choose a medi"; 12 | buffer[1] = "um length string."; 13 | count = 49; 14 | } else if (_tokenId == 3) { 15 | buffer[0] = "Lorem ipsum dolor sit amet, mi e"; 16 | buffer[1] = "st accumsan dapibus augue lorem,"; 17 | buffer[2] = " tristique vestibulum id, libero"; 18 | buffer[3] = " suscipit varius sapien aliquam."; 19 | count = 128; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /gen_science_interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | contract GeneScienceInterface { 4 | /// @dev simply a boolean to indicate this is the contract we expect to be 5 | function isGeneScience() public pure returns (bool); 6 | 7 | /// @dev given genes of kitten 1 & 2, return a genetic combination - may have a random factor 8 | /// @param genes1 genes of mom 9 | /// @param genes2 genes of sire 10 | /// @return the genes that are supposed to be passed down the child 11 | function mixGenes(uint256[2] genes1, uint256[2] genes2,uint256 g1,uint256 g2, uint256 targetBlock) public returns (uint256[2]); 12 | 13 | function getPureFromGene(uint256[2] gene) public view returns(uint256); 14 | 15 | /// @dev get sex from genes 0: female 1: male 16 | function getSex(uint256[2] gene) public view returns(uint256); 17 | 18 | /// @dev get wizz type from gene 19 | function getWizzType(uint256[2] gene) public view returns(uint256); 20 | 21 | function clearWizzType(uint256[2] _gene) public returns(uint256[2]); 22 | } -------------------------------------------------------------------------------- /ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | /** 3 | * @title Ownable 4 | * @dev The Ownable contract has an owner address, and provides basic authorization control 5 | * functions, this simplifies the implementation of "user permissions". 6 | */ 7 | contract Ownable { 8 | address public owner; 9 | 10 | 11 | /** 12 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 13 | * account. 14 | */ 15 | function Ownable() { 16 | owner = msg.sender; 17 | } 18 | 19 | 20 | /** 21 | * @dev Throws if called by any account other than the owner. 22 | */ 23 | modifier onlyOwner() { 24 | require(msg.sender == owner); 25 | _; 26 | } 27 | 28 | 29 | /** 30 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 31 | * @param newOwner The address to transfer ownership to. 32 | */ 33 | function transferOwnership(address newOwner) onlyOwner { 34 | if (newOwner != address(0)) { 35 | owner = newOwner; 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /panda_access_control.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /// @title A facet of PandaCore that manages special access privileges. 4 | /// @author Axiom Zen (https://www.axiomzen.co) 5 | /// @dev See the PandaCore contract documentation to understand how the various contract facets are arranged. 6 | contract PandaAccessControl { 7 | // This facet controls access control for CryptoPandas. There are four roles managed here: 8 | // 9 | // - The CEO: The CEO can reassign other roles and change the addresses of our dependent smart 10 | // contracts. It is also the only role that can unpause the smart contract. It is initially 11 | // set to the address that created the smart contract in the PandaCore constructor. 12 | // 13 | // - The CFO: The CFO can withdraw funds from PandaCore and its auction contracts. 14 | // 15 | // - The COO: The COO can release gen0 pandas to auction, and mint promo cats. 16 | // 17 | // It should be noted that these roles are distinct without overlap in their access abilities, the 18 | // abilities listed for each role above are exhaustive. In particular, while the CEO can assign any 19 | // address to any role, the CEO address itself doesn't have the ability to act in those roles. This 20 | // restriction is intentional so that we aren't tempted to use the CEO address frequently out of 21 | // convenience. The less we use an address, the less likely it is that we somehow compromise the 22 | // account. 23 | 24 | /// @dev Emited when contract is upgraded - See README.md for updgrade plan 25 | event ContractUpgrade(address newContract); 26 | 27 | // The addresses of the accounts (or contracts) that can execute actions within each roles. 28 | address public ceoAddress; 29 | address public cfoAddress; 30 | address public cooAddress; 31 | 32 | // @dev Keeps track whether the contract is paused. When that is true, most actions are blocked 33 | bool public paused = false; 34 | 35 | /// @dev Access modifier for CEO-only functionality 36 | modifier onlyCEO() { 37 | require(msg.sender == ceoAddress); 38 | _; 39 | } 40 | 41 | /// @dev Access modifier for CFO-only functionality 42 | modifier onlyCFO() { 43 | require(msg.sender == cfoAddress); 44 | _; 45 | } 46 | 47 | /// @dev Access modifier for COO-only functionality 48 | modifier onlyCOO() { 49 | require(msg.sender == cooAddress); 50 | _; 51 | } 52 | 53 | modifier onlyCLevel() { 54 | require( 55 | msg.sender == cooAddress || 56 | msg.sender == ceoAddress || 57 | msg.sender == cfoAddress 58 | ); 59 | _; 60 | } 61 | 62 | /// @dev Assigns a new address to act as the CEO. Only available to the current CEO. 63 | /// @param _newCEO The address of the new CEO 64 | function setCEO(address _newCEO) external onlyCEO { 65 | require(_newCEO != address(0)); 66 | 67 | ceoAddress = _newCEO; 68 | } 69 | 70 | /// @dev Assigns a new address to act as the CFO. Only available to the current CEO. 71 | /// @param _newCFO The address of the new CFO 72 | function setCFO(address _newCFO) external onlyCEO { 73 | require(_newCFO != address(0)); 74 | 75 | cfoAddress = _newCFO; 76 | } 77 | 78 | /// @dev Assigns a new address to act as the COO. Only available to the current CEO. 79 | /// @param _newCOO The address of the new COO 80 | function setCOO(address _newCOO) external onlyCEO { 81 | require(_newCOO != address(0)); 82 | 83 | cooAddress = _newCOO; 84 | } 85 | 86 | /*** Pausable functionality adapted from OpenZeppelin ***/ 87 | 88 | /// @dev Modifier to allow actions only when the contract IS NOT paused 89 | modifier whenNotPaused() { 90 | require(!paused); 91 | _; 92 | } 93 | 94 | /// @dev Modifier to allow actions only when the contract IS paused 95 | modifier whenPaused { 96 | require(paused); 97 | _; 98 | } 99 | 100 | /// @dev Called by any "C-level" role to pause the contract. Used only when 101 | /// a bug or exploit is detected and we need to limit damage. 102 | function pause() external onlyCLevel whenNotPaused { 103 | paused = true; 104 | } 105 | 106 | /// @dev Unpauses the smart contract. Can only be called by the CEO, since 107 | /// one reason we may pause the contract is when CFO or COO accounts are 108 | /// compromised. 109 | /// @notice This is public rather than external so it can be called by 110 | /// derived contracts. 111 | function unpause() public onlyCEO whenPaused { 112 | // can't unpause if contract was upgraded 113 | paused = false; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /panda_auction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./panda_breeding.sol"; 4 | 5 | /// @title Handles creating auctions for sale and siring of pandas. 6 | /// This wrapper of ReverseAuction exists only so that users can create 7 | /// auctions with only one transaction. 8 | contract PandaAuction is PandaBreeding { 9 | 10 | // @notice The auction contract variables are defined in PandaBase to allow 11 | // us to refer to them in PandaOwnership to prevent accidental transfers. 12 | // `saleAuction` refers to the auction for gen0 and p2p sale of pandas. 13 | // `siringAuction` refers to the auction for siring rights of pandas. 14 | 15 | /// @dev Sets the reference to the sale auction. 16 | /// @param _address - Address of sale contract. 17 | function setSaleAuctionAddress(address _address) external onlyCEO { 18 | SaleClockAuction candidateContract = SaleClockAuction(_address); 19 | 20 | // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 21 | require(candidateContract.isSaleClockAuction()); 22 | 23 | // Set the new contract address 24 | saleAuction = candidateContract; 25 | } 26 | 27 | function setSaleAuctionERC20Address(address _address) external onlyCEO { 28 | SaleClockAuctionERC20 candidateContract = SaleClockAuctionERC20(_address); 29 | 30 | // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 31 | require(candidateContract.isSaleClockAuctionERC20()); 32 | 33 | // Set the new contract address 34 | saleAuctionERC20 = candidateContract; 35 | } 36 | 37 | /// @dev Sets the reference to the siring auction. 38 | /// @param _address - Address of siring contract. 39 | function setSiringAuctionAddress(address _address) external onlyCEO { 40 | SiringClockAuction candidateContract = SiringClockAuction(_address); 41 | 42 | // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 43 | require(candidateContract.isSiringClockAuction()); 44 | 45 | // Set the new contract address 46 | siringAuction = candidateContract; 47 | } 48 | 49 | /// @dev Put a panda up for auction. 50 | /// Does some ownership trickery to create auctions in one tx. 51 | function createSaleAuction( 52 | uint256 _pandaId, 53 | uint256 _startingPrice, 54 | uint256 _endingPrice, 55 | uint256 _duration 56 | ) 57 | external 58 | whenNotPaused 59 | { 60 | // Auction contract checks input sizes 61 | // If panda is already on any auction, this will throw 62 | // because it will be owned by the auction contract. 63 | require(_owns(msg.sender, _pandaId)); 64 | // Ensure the panda is not pregnant to prevent the auction 65 | // contract accidentally receiving ownership of the child. 66 | // NOTE: the panda IS allowed to be in a cooldown. 67 | require(!isPregnant(_pandaId)); 68 | _approve(_pandaId, saleAuction); 69 | // Sale auction throws if inputs are invalid and clears 70 | // transfer and sire approval after escrowing the panda. 71 | saleAuction.createAuction( 72 | _pandaId, 73 | _startingPrice, 74 | _endingPrice, 75 | _duration, 76 | msg.sender 77 | ); 78 | } 79 | 80 | /// @dev Put a panda up for auction. 81 | /// Does some ownership trickery to create auctions in one tx. 82 | function createSaleAuctionERC20( 83 | uint256 _pandaId, 84 | uint256 _startingPrice, 85 | uint256 _endingPrice, 86 | uint256 _duration 87 | ) 88 | external 89 | whenNotPaused 90 | { 91 | // Auction contract checks input sizes 92 | // If panda is already on any auction, this will throw 93 | // because it will be owned by the auction contract. 94 | require(_owns(msg.sender, _pandaId)); 95 | // Ensure the panda is not pregnant to prevent the auction 96 | // contract accidentally receiving ownership of the child. 97 | // NOTE: the panda IS allowed to be in a cooldown. 98 | require(!isPregnant(_pandaId)); 99 | _approve(_pandaId, saleAuctionERC20); 100 | // Sale auction throws if inputs are invalid and clears 101 | // transfer and sire approval after escrowing the panda. 102 | saleAuctionERC20.createAuction( 103 | _pandaId, 104 | _startingPrice, 105 | _endingPrice, 106 | _duration, 107 | msg.sender 108 | ); 109 | } 110 | 111 | /// @dev Put a panda up for auction to be sire. 112 | /// Performs checks to ensure the panda can be sired, then 113 | /// delegates to reverse auction. 114 | function createSiringAuction( 115 | uint256 _pandaId, 116 | uint256 _startingPrice, 117 | uint256 _endingPrice, 118 | uint256 _duration 119 | ) 120 | external 121 | whenNotPaused 122 | { 123 | // Auction contract checks input sizes 124 | // If panda is already on any auction, this will throw 125 | // because it will be owned by the auction contract. 126 | require(_owns(msg.sender, _pandaId)); 127 | require(isReadyToBreed(_pandaId)); 128 | _approve(_pandaId, siringAuction); 129 | // Siring auction throws if inputs are invalid and clears 130 | // transfer and sire approval after escrowing the panda. 131 | siringAuction.createAuction( 132 | _pandaId, 133 | _startingPrice, 134 | _endingPrice, 135 | _duration, 136 | msg.sender 137 | ); 138 | } 139 | 140 | /// @dev Completes a siring auction by bidding. 141 | /// Immediately breeds the winning matron with the sire on auction. 142 | /// @param _sireId - ID of the sire on auction. 143 | /// @param _matronId - ID of the matron owned by the bidder. 144 | function bidOnSiringAuction( 145 | uint256 _sireId, 146 | uint256 _matronId 147 | ) 148 | external 149 | payable 150 | whenNotPaused 151 | { 152 | // Auction contract checks input sizes 153 | require(_owns(msg.sender, _matronId)); 154 | require(isReadyToBreed(_matronId)); 155 | require(_canBreedWithViaAuction(_matronId, _sireId)); 156 | 157 | // Define the current price of the auction. 158 | uint256 currentPrice = siringAuction.getCurrentPrice(_sireId); 159 | require(msg.value >= currentPrice + autoBirthFee); 160 | 161 | // Siring auction will throw if the bid fails. 162 | siringAuction.bid.value(msg.value - autoBirthFee)(_sireId); 163 | _breedWith(uint32(_matronId), uint32(_sireId), msg.sender); 164 | } 165 | 166 | /// @dev Transfers the balance of the sale auction contract 167 | /// to the PandaCore contract. We use two-step withdrawal to 168 | /// prevent two transfer calls in the auction bid function. 169 | function withdrawAuctionBalances() external onlyCLevel { 170 | saleAuction.withdrawBalance(); 171 | siringAuction.withdrawBalance(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /panda_base.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./panda_access_control.sol"; 5 | 6 | import "./siring_clock_auction.sol"; 7 | 8 | import "./gen_science_interface.sol"; 9 | 10 | import "./sale_clock_auction_erc20.sol"; 11 | 12 | import "./sale_clock_auction.sol"; 13 | 14 | 15 | /// @title Base contract for CryptoPandas. Holds all common structs, events and base variables. 16 | /// @author Axiom Zen (https://www.axiomzen.co) 17 | /// @dev See the PandaCore contract documentation to understand how the various contract facets are arranged. 18 | contract PandaBase is PandaAccessControl { 19 | /*** EVENTS ***/ 20 | 21 | uint256 public constant GEN0_TOTAL_COUNT = 16200; 22 | uint256 public gen0CreatedCount; 23 | 24 | /// @dev The Birth event is fired whenever a new kitten comes into existence. This obviously 25 | /// includes any time a cat is created through the giveBirth method, but it is also called 26 | /// when a new gen0 cat is created. 27 | event Birth(address owner, uint256 pandaId, uint256 matronId, uint256 sireId, uint256[2] genes); 28 | 29 | /// @dev Transfer event as defined in current draft of ERC721. Emitted every time a kitten 30 | /// ownership is assigned, including births. 31 | event Transfer(address from, address to, uint256 tokenId); 32 | 33 | /*** DATA TYPES ***/ 34 | 35 | /// @dev The main Panda struct. Every cat in CryptoPandas is represented by a copy 36 | /// of this structure, so great care was taken to ensure that it fits neatly into 37 | /// exactly two 256-bit words. Note that the order of the members in this structure 38 | /// is important because of the byte-packing rules used by Ethereum. 39 | /// Ref: http://solidity.readthedocs.io/en/develop/miscellaneous.html 40 | struct Panda { 41 | // The Panda's genetic code is packed into these 256-bits, the format is 42 | // sooper-sekret! A cat's genes never change. 43 | uint256[2] genes; 44 | 45 | // The timestamp from the block when this cat came into existence. 46 | uint64 birthTime; 47 | 48 | // The minimum timestamp after which this cat can engage in breeding 49 | // activities again. This same timestamp is used for the pregnancy 50 | // timer (for matrons) as well as the siring cooldown. 51 | uint64 cooldownEndBlock; 52 | 53 | // The ID of the parents of this panda, set to 0 for gen0 cats. 54 | // Note that using 32-bit unsigned integers limits us to a "mere" 55 | // 4 billion cats. This number might seem small until you realize 56 | // that Ethereum currently has a limit of about 500 million 57 | // transactions per year! So, this definitely won't be a problem 58 | // for several years (even as Ethereum learns to scale). 59 | uint32 matronId; 60 | uint32 sireId; 61 | 62 | // Set to the ID of the sire cat for matrons that are pregnant, 63 | // zero otherwise. A non-zero value here is how we know a cat 64 | // is pregnant. Used to retrieve the genetic material for the new 65 | // kitten when the birth transpires. 66 | uint32 siringWithId; 67 | 68 | // Set to the index in the cooldown array (see below) that represents 69 | // the current cooldown duration for this Panda. This starts at zero 70 | // for gen0 cats, and is initialized to floor(generation/2) for others. 71 | // Incremented by one for each successful breeding action, regardless 72 | // of whether this cat is acting as matron or sire. 73 | uint16 cooldownIndex; 74 | 75 | // The "generation number" of this cat. Cats minted by the CK contract 76 | // for sale are called "gen0" and have a generation number of 0. The 77 | // generation number of all other cats is the larger of the two generation 78 | // numbers of their parents, plus one. 79 | // (i.e. max(matron.generation, sire.generation) + 1) 80 | uint16 generation; 81 | } 82 | 83 | /*** CONSTANTS ***/ 84 | 85 | /// @dev A lookup table indicating the cooldown duration after any successful 86 | /// breeding action, called "pregnancy time" for matrons and "siring cooldown" 87 | /// for sires. Designed such that the cooldown roughly doubles each time a cat 88 | /// is bred, encouraging owners not to just keep breeding the same cat over 89 | /// and over again. Caps out at one week (a cat can breed an unbounded number 90 | /// of times, and the maximum cooldown is always seven days). 91 | uint32[9] public cooldowns = [ 92 | uint32(5 minutes), 93 | uint32(30 minutes), 94 | uint32(2 hours), 95 | uint32(4 hours), 96 | uint32(8 hours), 97 | uint32(24 hours), 98 | uint32(48 hours), 99 | uint32(72 hours), 100 | uint32(7 days) 101 | ]; 102 | 103 | // An approximation of currently how many seconds are in between blocks. 104 | uint256 public secondsPerBlock = 15; 105 | 106 | /*** STORAGE ***/ 107 | 108 | /// @dev An array containing the Panda struct for all Pandas in existence. The ID 109 | /// of each cat is actually an index into this array. Note that ID 0 is a negacat, 110 | /// the unPanda, the mythical beast that is the parent of all gen0 cats. A bizarre 111 | /// creature that is both matron and sire... to itself! Has an invalid genetic code. 112 | /// In other words, cat ID 0 is invalid... ;-) 113 | Panda[] pandas; 114 | 115 | /// @dev A mapping from cat IDs to the address that owns them. All cats have 116 | /// some valid owner address, even gen0 cats are created with a non-zero owner. 117 | mapping (uint256 => address) public pandaIndexToOwner; 118 | 119 | // @dev A mapping from owner address to count of tokens that address owns. 120 | // Used internally inside balanceOf() to resolve ownership count. 121 | mapping (address => uint256) ownershipTokenCount; 122 | 123 | /// @dev A mapping from PandaIDs to an address that has been approved to call 124 | /// transferFrom(). Each Panda can only have one approved address for transfer 125 | /// at any time. A zero value means no approval is outstanding. 126 | mapping (uint256 => address) public pandaIndexToApproved; 127 | 128 | /// @dev A mapping from PandaIDs to an address that has been approved to use 129 | /// this Panda for siring via breedWith(). Each Panda can only have one approved 130 | /// address for siring at any time. A zero value means no approval is outstanding. 131 | mapping (uint256 => address) public sireAllowedToAddress; 132 | 133 | /// @dev The address of the ClockAuction contract that handles sales of Pandas. This 134 | /// same contract handles both peer-to-peer sales as well as the gen0 sales which are 135 | /// initiated every 15 minutes. 136 | SaleClockAuction public saleAuction; 137 | 138 | /// @dev The address of a custom ClockAuction subclassed contract that handles siring 139 | /// auctions. Needs to be separate from saleAuction because the actions taken on success 140 | /// after a sales and siring auction are quite different. 141 | SiringClockAuction public siringAuction; 142 | 143 | 144 | GeneScienceInterface public geneScienceRef; 145 | 146 | 147 | SaleClockAuctionERC20 public saleAuctionERC20; 148 | 149 | 150 | // wizz panda total 151 | mapping (uint256 => uint256) public wizzPandaQuota; 152 | mapping (uint256 => uint256) public wizzPandaCount; 153 | 154 | 155 | /// wizz panda control 156 | function getWizzPandaQuotaOf(uint256 _tp) view external returns(uint256) { 157 | return wizzPandaQuota[_tp]; 158 | } 159 | 160 | function getWizzPandaCountOf(uint256 _tp) view external returns(uint256) { 161 | return wizzPandaCount[_tp]; 162 | } 163 | 164 | function setTotalWizzPandaOf(uint256 _tp,uint256 _total) external onlyCLevel { 165 | require (wizzPandaQuota[_tp]==0); 166 | require (_total==uint256(uint32(_total))); 167 | wizzPandaQuota[_tp] = _total; 168 | } 169 | 170 | function getWizzTypeOf(uint256 _id) view external returns(uint256) { 171 | Panda memory _p = pandas[_id]; 172 | return geneScienceRef.getWizzType(_p.genes); 173 | } 174 | 175 | /// @dev Assigns ownership of a specific Panda to an address. 176 | function _transfer(address _from, address _to, uint256 _tokenId) internal { 177 | // Since the number of kittens is capped to 2^32 we can't overflow this 178 | ownershipTokenCount[_to]++; 179 | // transfer ownership 180 | pandaIndexToOwner[_tokenId] = _to; 181 | // When creating new kittens _from is 0x0, but we can't account that address. 182 | if (_from != address(0)) { 183 | ownershipTokenCount[_from]--; 184 | // once the kitten is transferred also clear sire allowances 185 | delete sireAllowedToAddress[_tokenId]; 186 | // clear any previously approved ownership exchange 187 | delete pandaIndexToApproved[_tokenId]; 188 | } 189 | // Emit the transfer event. 190 | Transfer(_from, _to, _tokenId); 191 | } 192 | 193 | /// @dev An internal method that creates a new panda and stores it. This 194 | /// method doesn't do any checking and should only be called when the 195 | /// input data is known to be valid. Will generate both a Birth event 196 | /// and a Transfer event. 197 | /// @param _matronId The panda ID of the matron of this cat (zero for gen0) 198 | /// @param _sireId The panda ID of the sire of this cat (zero for gen0) 199 | /// @param _generation The generation number of this cat, must be computed by caller. 200 | /// @param _genes The panda's genetic code. 201 | /// @param _owner The inital owner of this cat, must be non-zero (except for the unPanda, ID 0) 202 | function _createPanda( 203 | uint256 _matronId, 204 | uint256 _sireId, 205 | uint256 _generation, 206 | uint256[2] _genes, 207 | address _owner 208 | ) 209 | internal 210 | returns (uint) 211 | { 212 | // These requires are not strictly necessary, our calling code should make 213 | // sure that these conditions are never broken. However! _createPanda() is already 214 | // an expensive call (for storage), and it doesn't hurt to be especially careful 215 | // to ensure our data structures are always valid. 216 | require(_matronId == uint256(uint32(_matronId))); 217 | require(_sireId == uint256(uint32(_sireId))); 218 | require(_generation == uint256(uint16(_generation))); 219 | 220 | 221 | // New panda starts with the same cooldown as parent gen/2 222 | uint16 cooldownIndex = 0; 223 | // when contract creation, geneScienceRef is null 224 | if (pandas.length>0){ 225 | uint16 pureDegree = uint16(geneScienceRef.getPureFromGene(_genes)); 226 | if (pureDegree==0) { 227 | pureDegree = 1; 228 | } 229 | cooldownIndex = 1000/pureDegree; 230 | if (cooldownIndex%10 < 5){ 231 | cooldownIndex = cooldownIndex/10; 232 | }else{ 233 | cooldownIndex = cooldownIndex/10 + 1; 234 | } 235 | cooldownIndex = cooldownIndex - 1; 236 | if (cooldownIndex > 8) { 237 | cooldownIndex = 8; 238 | } 239 | uint256 _tp = geneScienceRef.getWizzType(_genes); 240 | if (_tp>0 && wizzPandaQuota[_tp]<=wizzPandaCount[_tp]) { 241 | _genes = geneScienceRef.clearWizzType(_genes); 242 | _tp = 0; 243 | } 244 | // gensis panda cooldownIndex should be 24 hours 245 | if (_tp == 1){ 246 | cooldownIndex = 5; 247 | } 248 | 249 | // increase wizz counter 250 | if (_tp>0){ 251 | wizzPandaCount[_tp] = wizzPandaCount[_tp] + 1; 252 | } 253 | // all gen0&gen1 except gensis 254 | if (_generation <= 1 && _tp != 1){ 255 | require(gen0CreatedCount address) childOwner; 35 | 36 | 37 | /// @dev Update the address of the genetic contract, can only be called by the CEO. 38 | /// @param _address An address of a GeneScience contract instance to be used from this point forward. 39 | function setGeneScienceAddress(address _address) external onlyCEO { 40 | GeneScienceInterface candidateContract = GeneScienceInterface(_address); 41 | 42 | // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117 43 | require(candidateContract.isGeneScience()); 44 | 45 | // Set the new contract address 46 | geneScience = candidateContract; 47 | 48 | geneScienceRef = candidateContract; 49 | } 50 | 51 | /// @dev Checks that a given kitten is able to breed. Requires that the 52 | /// current cooldown is finished (for sires) and also checks that there is 53 | /// no pending pregnancy. 54 | function _isReadyToBreed(Panda _kit) internal view returns (bool) { 55 | // In addition to checking the cooldownEndBlock, we also need to check to see if 56 | // the cat has a pending birth; there can be some period of time between the end 57 | // of the pregnacy timer and the birth event. 58 | return (_kit.siringWithId == 0) && (_kit.cooldownEndBlock <= uint64(block.number)); 59 | } 60 | 61 | /// @dev Check if a sire has authorized breeding with this matron. True if both sire 62 | /// and matron have the same owner, or if the sire has given siring permission to 63 | /// the matron's owner (via approveSiring()). 64 | function _isSiringPermitted(uint256 _sireId, uint256 _matronId) internal view returns (bool) { 65 | address matronOwner = pandaIndexToOwner[_matronId]; 66 | address sireOwner = pandaIndexToOwner[_sireId]; 67 | 68 | // Siring is okay if they have same owner, or if the matron's owner was given 69 | // permission to breed with this sire. 70 | return (matronOwner == sireOwner || sireAllowedToAddress[_sireId] == matronOwner); 71 | } 72 | 73 | /// @dev Set the cooldownEndTime for the given Panda, based on its current cooldownIndex. 74 | /// Also increments the cooldownIndex (unless it has hit the cap). 75 | /// @param _kitten A reference to the Panda in storage which needs its timer started. 76 | function _triggerCooldown(Panda storage _kitten) internal { 77 | // Compute an estimation of the cooldown time in blocks (based on current cooldownIndex). 78 | _kitten.cooldownEndBlock = uint64((cooldowns[_kitten.cooldownIndex]/secondsPerBlock) + block.number); 79 | 80 | 81 | // Increment the breeding count, clamping it at 13, which is the length of the 82 | // cooldowns array. We could check the array size dynamically, but hard-coding 83 | // this as a constant saves gas. Yay, Solidity! 84 | if (_kitten.cooldownIndex < 8 && geneScience.getWizzType(_kitten.genes) != 1) { 85 | _kitten.cooldownIndex += 1; 86 | } 87 | } 88 | 89 | /// @notice Grants approval to another user to sire with one of your Pandas. 90 | /// @param _addr The address that will be able to sire with your Panda. Set to 91 | /// address(0) to clear all siring approvals for this Panda. 92 | /// @param _sireId A Panda that you own that _addr will now be able to sire with. 93 | function approveSiring(address _addr, uint256 _sireId) 94 | external 95 | whenNotPaused 96 | { 97 | require(_owns(msg.sender, _sireId)); 98 | sireAllowedToAddress[_sireId] = _addr; 99 | } 100 | 101 | /// @dev Updates the minimum payment required for calling giveBirthAuto(). Can only 102 | /// be called by the COO address. (This fee is used to offset the gas cost incurred 103 | /// by the autobirth daemon). 104 | function setAutoBirthFee(uint256 val) external onlyCOO { 105 | autoBirthFee = val; 106 | } 107 | 108 | /// @dev Checks to see if a given Panda is pregnant and (if so) if the gestation 109 | /// period has passed. 110 | function _isReadyToGiveBirth(Panda _matron) private view returns (bool) { 111 | return (_matron.siringWithId != 0) && (_matron.cooldownEndBlock <= uint64(block.number)); 112 | } 113 | 114 | /// @notice Checks that a given kitten is able to breed (i.e. it is not pregnant or 115 | /// in the middle of a siring cooldown). 116 | /// @param _pandaId reference the id of the kitten, any user can inquire about it 117 | function isReadyToBreed(uint256 _pandaId) 118 | public 119 | view 120 | returns (bool) 121 | { 122 | require(_pandaId > 0); 123 | Panda storage kit = pandas[_pandaId]; 124 | return _isReadyToBreed(kit); 125 | } 126 | 127 | /// @dev Checks whether a panda is currently pregnant. 128 | /// @param _pandaId reference the id of the kitten, any user can inquire about it 129 | function isPregnant(uint256 _pandaId) 130 | public 131 | view 132 | returns (bool) 133 | { 134 | require(_pandaId > 0); 135 | // A panda is pregnant if and only if this field is set 136 | return pandas[_pandaId].siringWithId != 0; 137 | } 138 | 139 | /// @dev Internal check to see if a given sire and matron are a valid mating pair. DOES NOT 140 | /// check ownership permissions (that is up to the caller). 141 | /// @param _matron A reference to the Panda struct of the potential matron. 142 | /// @param _matronId The matron's ID. 143 | /// @param _sire A reference to the Panda struct of the potential sire. 144 | /// @param _sireId The sire's ID 145 | function _isValidMatingPair( 146 | Panda storage _matron, 147 | uint256 _matronId, 148 | Panda storage _sire, 149 | uint256 _sireId 150 | ) 151 | private 152 | view 153 | returns(bool) 154 | { 155 | // A Panda can't breed with itself! 156 | if (_matronId == _sireId) { 157 | return false; 158 | } 159 | 160 | // Pandas can't breed with their parents. 161 | if (_matron.matronId == _sireId || _matron.sireId == _sireId) { 162 | return false; 163 | } 164 | if (_sire.matronId == _matronId || _sire.sireId == _matronId) { 165 | return false; 166 | } 167 | 168 | // We can short circuit the sibling check (below) if either cat is 169 | // gen zero (has a matron ID of zero). 170 | if (_sire.matronId == 0 || _matron.matronId == 0) { 171 | return true; 172 | } 173 | 174 | // Pandas can't breed with full or half siblings. 175 | if (_sire.matronId == _matron.matronId || _sire.matronId == _matron.sireId) { 176 | return false; 177 | } 178 | if (_sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId) { 179 | return false; 180 | } 181 | 182 | // male should get breed with female 183 | if (geneScience.getSex(_matron.genes) + geneScience.getSex(_sire.genes) != 1) { 184 | return false; 185 | } 186 | 187 | // Everything seems cool! Let's get DTF. 188 | return true; 189 | } 190 | 191 | /// @dev Internal check to see if a given sire and matron are a valid mating pair for 192 | /// breeding via auction (i.e. skips ownership and siring approval checks). 193 | function _canBreedWithViaAuction(uint256 _matronId, uint256 _sireId) 194 | internal 195 | view 196 | returns (bool) 197 | { 198 | Panda storage matron = pandas[_matronId]; 199 | Panda storage sire = pandas[_sireId]; 200 | return _isValidMatingPair(matron, _matronId, sire, _sireId); 201 | } 202 | 203 | /// @notice Checks to see if two cats can breed together, including checks for 204 | /// ownership and siring approvals. Does NOT check that both cats are ready for 205 | /// breeding (i.e. breedWith could still fail until the cooldowns are finished). 206 | /// TODO: Shouldn't this check pregnancy and cooldowns?!? 207 | /// @param _matronId The ID of the proposed matron. 208 | /// @param _sireId The ID of the proposed sire. 209 | function canBreedWith(uint256 _matronId, uint256 _sireId) 210 | external 211 | view 212 | returns(bool) 213 | { 214 | require(_matronId > 0); 215 | require(_sireId > 0); 216 | Panda storage matron = pandas[_matronId]; 217 | Panda storage sire = pandas[_sireId]; 218 | return _isValidMatingPair(matron, _matronId, sire, _sireId) && 219 | _isSiringPermitted(_sireId, _matronId); 220 | } 221 | 222 | function _exchangeMatronSireId(uint256 _matronId, uint256 _sireId) internal returns(uint256,uint256) { 223 | if (geneScience.getSex(pandas[_matronId].genes) == 1){ 224 | return (_sireId,_matronId); 225 | }else{ 226 | return (_matronId,_sireId); 227 | } 228 | } 229 | 230 | /// @dev Internal utility function to initiate breeding, assumes that all breeding 231 | /// requirements have been checked. 232 | function _breedWith(uint256 _matronId, uint256 _sireId, address _owner) internal { 233 | // make id point real gender 234 | (_matronId,_sireId) = _exchangeMatronSireId(_matronId,_sireId); 235 | // Grab a reference to the Pandas from storage. 236 | Panda storage sire = pandas[_sireId]; 237 | Panda storage matron = pandas[_matronId]; 238 | 239 | // Mark the matron as pregnant, keeping track of who the sire is. 240 | matron.siringWithId = uint32(_sireId); 241 | 242 | // Trigger the cooldown for both parents. 243 | _triggerCooldown(sire); 244 | _triggerCooldown(matron); 245 | 246 | // Clear siring permission for both parents. This may not be strictly necessary 247 | // but it's likely to avoid confusion! 248 | delete sireAllowedToAddress[_matronId]; 249 | delete sireAllowedToAddress[_sireId]; 250 | 251 | // Every time a panda gets pregnant, counter is incremented. 252 | pregnantPandas++; 253 | 254 | childOwner[_matronId] = _owner; 255 | 256 | // Emit the pregnancy event. 257 | Pregnant(pandaIndexToOwner[_matronId], _matronId, _sireId, matron.cooldownEndBlock); 258 | } 259 | 260 | /// @notice Breed a Panda you own (as matron) with a sire that you own, or for which you 261 | /// have previously been given Siring approval. Will either make your cat pregnant, or will 262 | /// fail entirely. Requires a pre-payment of the fee given out to the first caller of giveBirth() 263 | /// @param _matronId The ID of the Panda acting as matron (will end up pregnant if successful) 264 | /// @param _sireId The ID of the Panda acting as sire (will begin its siring cooldown if successful) 265 | function breedWithAuto(uint256 _matronId, uint256 _sireId) 266 | external 267 | payable 268 | whenNotPaused 269 | { 270 | // Checks for payment. 271 | require(msg.value >= autoBirthFee); 272 | 273 | // Caller must own the matron. 274 | require(_owns(msg.sender, _matronId)); 275 | 276 | // Neither sire nor matron are allowed to be on auction during a normal 277 | // breeding operation, but we don't need to check that explicitly. 278 | // For matron: The caller of this function can't be the owner of the matron 279 | // because the owner of a Panda on auction is the auction house, and the 280 | // auction house will never call breedWith(). 281 | // For sire: Similarly, a sire on auction will be owned by the auction house 282 | // and the act of transferring ownership will have cleared any oustanding 283 | // siring approval. 284 | // Thus we don't need to spend gas explicitly checking to see if either cat 285 | // is on auction. 286 | 287 | // Check that matron and sire are both owned by caller, or that the sire 288 | // has given siring permission to caller (i.e. matron's owner). 289 | // Will fail for _sireId = 0 290 | require(_isSiringPermitted(_sireId, _matronId)); 291 | 292 | // Grab a reference to the potential matron 293 | Panda storage matron = pandas[_matronId]; 294 | 295 | // Make sure matron isn't pregnant, or in the middle of a siring cooldown 296 | require(_isReadyToBreed(matron)); 297 | 298 | // Grab a reference to the potential sire 299 | Panda storage sire = pandas[_sireId]; 300 | 301 | // Make sure sire isn't pregnant, or in the middle of a siring cooldown 302 | require(_isReadyToBreed(sire)); 303 | 304 | // Test that these cats are a valid mating pair. 305 | require(_isValidMatingPair( 306 | matron, 307 | _matronId, 308 | sire, 309 | _sireId 310 | )); 311 | 312 | // All checks passed, panda gets pregnant! 313 | _breedWith(_matronId, _sireId, msg.sender); 314 | } 315 | 316 | /// @notice Have a pregnant Panda give birth! 317 | /// @param _matronId A Panda ready to give birth. 318 | /// @return The Panda ID of the new kitten. 319 | /// @dev Looks at a given Panda and, if pregnant and if the gestation period has passed, 320 | /// combines the genes of the two parents to create a new kitten. The new Panda is assigned 321 | /// to the current owner of the matron. Upon successful completion, both the matron and the 322 | /// new kitten will be ready to breed again. Note that anyone can call this function (if they 323 | /// are willing to pay the gas!), but the new kitten always goes to the mother's owner. 324 | function giveBirth(uint256 _matronId,uint256[2] _childGenes,uint256 _envFactor) 325 | external 326 | whenNotPaused 327 | onlyCLevel 328 | returns(uint256) 329 | { 330 | // Grab a reference to the matron in storage. 331 | Panda storage matron = pandas[_matronId]; 332 | 333 | // Check that the matron is a valid cat. 334 | require(matron.birthTime != 0); 335 | 336 | // Check that the matron is pregnant, and that its time has come! 337 | require(_isReadyToGiveBirth(matron)); 338 | 339 | // Grab a reference to the sire in storage. 340 | uint256 sireId = matron.siringWithId; 341 | Panda storage sire = pandas[sireId]; 342 | 343 | // Determine the higher generation number of the two parents 344 | uint16 parentGen = matron.generation; 345 | if (sire.generation > matron.generation) { 346 | parentGen = sire.generation; 347 | } 348 | 349 | // Call the sooper-sekret gene mixing operation. 350 | //uint256[2] memory childGenes = geneScience.mixGenes(matron.genes, sire.genes,matron.generation,sire.generation, matron.cooldownEndBlock - 1); 351 | uint256[2] memory childGenes = _childGenes; 352 | 353 | // birth failed 354 | uint256 matronPure = geneScience.getPureFromGene(matron.genes); 355 | uint256 sirePure = geneScience.getPureFromGene(sire.genes); 356 | if (matronPure*sirePure*_envFactor/10000<50) { 357 | delete matron.siringWithId; 358 | pregnantPandas--; 359 | msg.sender.send(autoBirthFee); 360 | delete childOwner[_matronId]; 361 | Abortion(pandaIndexToOwner[_matronId],_matronId,sireId); 362 | return 0; 363 | } 364 | // Make the new kitten! 365 | //address owner = pandaIndexToOwner[_matronId]; 366 | address owner = childOwner[_matronId]; 367 | uint256 kittenId = _createPanda(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner); 368 | 369 | // Clear the reference to sire from the matron (REQUIRED! Having siringWithId 370 | // set is what marks a matron as being pregnant.) 371 | delete matron.siringWithId; 372 | 373 | // Every time a panda gives birth counter is decremented. 374 | pregnantPandas--; 375 | 376 | // Send the balance fee to the person who made birth happen. 377 | msg.sender.send(autoBirthFee); 378 | 379 | delete childOwner[_matronId]; 380 | 381 | // return the new kitten's ID 382 | return kittenId; 383 | } 384 | } 385 | 386 | 387 | 388 | 389 | -------------------------------------------------------------------------------- /panda_core.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./panda_minting.sol"; 4 | 5 | 6 | /// @title CryptoPandas: Collectible, breedable, and oh-so-adorable cats on the Ethereum blockchain. 7 | /// @author Axiom Zen (https://www.axiomzen.co) 8 | /// @dev The main CryptoPandas contract, keeps track of kittens so they don't wander around and get lost. 9 | contract PandaCore is PandaMinting { 10 | 11 | // This is the main CryptoPandas contract. In order to keep our code seperated into logical sections, 12 | // we've broken it up in two ways. First, we have several seperately-instantiated sibling contracts 13 | // that handle auctions and our super-top-secret genetic combination algorithm. The auctions are 14 | // seperate since their logic is somewhat complex and there's always a risk of subtle bugs. By keeping 15 | // them in their own contracts, we can upgrade them without disrupting the main contract that tracks 16 | // panda ownership. The genetic combination algorithm is kept seperate so we can open-source all of 17 | // the rest of our code without making it _too_ easy for folks to figure out how the genetics work. 18 | // Don't worry, I'm sure someone will reverse engineer it soon enough! 19 | // 20 | // Secondly, we break the core contract into multiple files using inheritence, one for each major 21 | // facet of functionality of CK. This allows us to keep related code bundled together while still 22 | // avoiding a single giant file with everything in it. The breakdown is as follows: 23 | // 24 | // - PandaBase: This is where we define the most fundamental code shared throughout the core 25 | // functionality. This includes our main data storage, constants and data types, plus 26 | // internal functions for managing these items. 27 | // 28 | // - PandaAccessControl: This contract manages the various addresses and constraints for operations 29 | // that can be executed only by specific roles. Namely CEO, CFO and COO. 30 | // 31 | // - PandaOwnership: This provides the methods required for basic non-fungible token 32 | // transactions, following the draft ERC-721 spec (https://github.com/ethereum/EIPs/issues/721). 33 | // 34 | // - PandaBreeding: This file contains the methods necessary to breed cats together, including 35 | // keeping track of siring offers, and relies on an external genetic combination contract. 36 | // 37 | // - PandaAuctions: Here we have the public methods for auctioning or bidding on cats or siring 38 | // services. The actual auction functionality is handled in two sibling contracts (one 39 | // for sales and one for siring), while auction creation and bidding is mostly mediated 40 | // through this facet of the core contract. 41 | // 42 | // - PandaMinting: This final facet contains the functionality we use for creating new gen0 cats. 43 | // We can make up to 5000 "promo" cats that can be given away (especially important when 44 | // the community is new), and all others can only be created and then immediately put up 45 | // for auction via an algorithmically determined starting price. Regardless of how they 46 | // are created, there is a hard limit of 50k gen0 cats. After that, it's all up to the 47 | // community to breed, breed, breed! 48 | 49 | // Set in case the core contract is broken and an upgrade is required 50 | address public newContractAddress; 51 | 52 | 53 | /// @notice Creates the main CryptoPandas smart contract instance. 54 | function PandaCore() public { 55 | // Starts paused. 56 | paused = true; 57 | 58 | // the creator of the contract is the initial CEO 59 | ceoAddress = msg.sender; 60 | 61 | // the creator of the contract is also the initial COO 62 | cooAddress = msg.sender; 63 | 64 | // move these code to init(), so we not excceed gas limit 65 | //uint256[2] memory _genes = [uint256(-1),uint256(-1)]; 66 | 67 | //wizzPandaQuota[1] = 100; 68 | 69 | //_createPanda(0, 0, 0, _genes, address(0)); 70 | } 71 | 72 | /// init contract 73 | function init() external onlyCEO whenPaused { 74 | // make sure init() only run once 75 | require(pandas.length == 0); 76 | // start with the mythical kitten 0 - so we don't have generation-0 parent issues 77 | uint256[2] memory _genes = [uint256(-1),uint256(-1)]; 78 | 79 | wizzPandaQuota[1] = 100; 80 | _createPanda(0, 0, 0, _genes, address(0)); 81 | } 82 | 83 | /// @dev Used to mark the smart contract as upgraded, in case there is a serious 84 | /// breaking bug. This method does nothing but keep track of the new contract and 85 | /// emit a message indicating that the new address is set. It's up to clients of this 86 | /// contract to update to the new contract address in that case. (This contract will 87 | /// be paused indefinitely if such an upgrade takes place.) 88 | /// @param _v2Address new address 89 | function setNewAddress(address _v2Address) external onlyCEO whenPaused { 90 | // See README.md for updgrade plan 91 | newContractAddress = _v2Address; 92 | ContractUpgrade(_v2Address); 93 | } 94 | 95 | 96 | /// @notice No tipping! 97 | /// @dev Reject all Ether from being sent here, unless it's from one of the 98 | /// two auction contracts. (Hopefully, we can prevent user accidents.) 99 | function() external payable { 100 | require( 101 | msg.sender == address(saleAuction) || 102 | msg.sender == address(siringAuction) 103 | ); 104 | } 105 | 106 | /// @notice Returns all the relevant information about a specific panda. 107 | /// @param _id The ID of the panda of interest. 108 | function getPanda(uint256 _id) 109 | external 110 | view 111 | returns ( 112 | bool isGestating, 113 | bool isReady, 114 | uint256 cooldownIndex, 115 | uint256 nextActionAt, 116 | uint256 siringWithId, 117 | uint256 birthTime, 118 | uint256 matronId, 119 | uint256 sireId, 120 | uint256 generation, 121 | uint256[2] genes 122 | ) { 123 | Panda storage kit = pandas[_id]; 124 | 125 | // if this variable is 0 then it's not gestating 126 | isGestating = (kit.siringWithId != 0); 127 | isReady = (kit.cooldownEndBlock <= block.number); 128 | cooldownIndex = uint256(kit.cooldownIndex); 129 | nextActionAt = uint256(kit.cooldownEndBlock); 130 | siringWithId = uint256(kit.siringWithId); 131 | birthTime = uint256(kit.birthTime); 132 | matronId = uint256(kit.matronId); 133 | sireId = uint256(kit.sireId); 134 | generation = uint256(kit.generation); 135 | genes = kit.genes; 136 | } 137 | 138 | /// @dev Override unpause so it requires all external contract addresses 139 | /// to be set before contract can be unpaused. Also, we can't have 140 | /// newContractAddress set either, because then the contract was upgraded. 141 | /// @notice This is public rather than external so we can call super.unpause 142 | /// without using an expensive CALL. 143 | function unpause() public onlyCEO whenPaused { 144 | require(saleAuction != address(0)); 145 | require(siringAuction != address(0)); 146 | require(geneScience != address(0)); 147 | require(newContractAddress == address(0)); 148 | 149 | // Actually unpause the contract. 150 | super.unpause(); 151 | } 152 | 153 | // @dev Allows the CFO to capture the balance available to the contract. 154 | function withdrawBalance() external onlyCFO { 155 | uint256 balance = this.balance; 156 | // Subtract all the currently pregnant kittens we have, plus 1 of margin. 157 | uint256 subtractFees = (pregnantPandas + 1) * autoBirthFee; 158 | 159 | if (balance > subtractFees) { 160 | cfoAddress.send(balance - subtractFees); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /panda_minting.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./panda_auction.sol"; 5 | 6 | import "./panda_access_control.sol"; 7 | 8 | 9 | /// @title all functions related to creating kittens 10 | contract PandaMinting is PandaAuction { 11 | 12 | // Limits the number of cats the contract owner can ever create. 13 | //uint256 public constant PROMO_CREATION_LIMIT = 5000; 14 | uint256 public constant GEN0_CREATION_LIMIT = 45000; 15 | 16 | 17 | // Constants for gen0 auctions. 18 | uint256 public constant GEN0_STARTING_PRICE = 10 finney; 19 | uint256 public constant GEN0_AUCTION_DURATION = 1 days; 20 | uint256 public constant OPEN_PACKAGE_PRICE = 10 finney; 21 | 22 | 23 | // Counts the number of cats the contract owner has created. 24 | uint256 public promoCreatedCount; 25 | 26 | 27 | /// @dev we can create promo kittens, up to a limit. Only callable by COO 28 | /// @param _genes the encoded genes of the kitten to be created, any value is accepted 29 | /// @param _owner the future owner of the created kittens. Default to contract COO 30 | function createWizzPanda(uint256[2] _genes,uint256 _generation, address _owner) external onlyCOO { 31 | address pandaOwner = _owner; 32 | if (pandaOwner == address(0)) { 33 | pandaOwner = cooAddress; 34 | } 35 | 36 | _createPanda(0, 0, _generation, _genes, pandaOwner); 37 | } 38 | 39 | /// @dev create pandaWithGenes 40 | /// @param _genes panda genes 41 | /// @param _type 0 common 1 rare 42 | function createPanda(uint256[2] _genes,uint256 _type) 43 | external 44 | payable 45 | onlyCOO 46 | whenNotPaused 47 | { 48 | require(msg.value >= OPEN_PACKAGE_PRICE); 49 | uint256 kittenId = _createPanda(0, 0, 0, _genes, saleAuction); 50 | saleAuction.createPanda(kittenId,_type); 51 | } 52 | 53 | function buyPandaERC20(address _erc20Address,address _buyerAddress,uint256 _pandaID,uint256 _amount) 54 | external 55 | onlyCOO 56 | whenNotPaused 57 | { 58 | saleAuctionERC20.bid(_erc20Address,_buyerAddress,_pandaID,_amount); 59 | } 60 | 61 | /// @dev Creates a new gen0 panda with the given genes and 62 | /// creates an auction for it. 63 | //function createGen0Auction(uint256[2] _genes) external onlyCOO { 64 | // require(gen0CreatedCount < GEN0_CREATION_LIMIT); 65 | // 66 | // uint256 pandaId = _createPanda(0, 0, 0, _genes, address(this)); 67 | // _approve(pandaId, saleAuction); 68 | // 69 | // saleAuction.createAuction( 70 | // pandaId, 71 | // _computeNextGen0Price(), 72 | // 0, 73 | // GEN0_AUCTION_DURATION, 74 | // address(this) 75 | // ); 76 | // 77 | // gen0CreatedCount++; 78 | //} 79 | 80 | function createGen0Auction(uint256 _pandaId) external onlyCOO { 81 | require(_owns(msg.sender, _pandaId)); 82 | //require(pandas[_pandaId].generation==1); 83 | 84 | _approve(_pandaId, saleAuction); 85 | 86 | saleAuction.createGen0Auction( 87 | _pandaId, 88 | _computeNextGen0Price(), 89 | 0, 90 | GEN0_AUCTION_DURATION, 91 | msg.sender 92 | ); 93 | } 94 | 95 | /// @dev Computes the next gen0 auction starting price, given 96 | /// the average of the past 5 prices + 50%. 97 | function _computeNextGen0Price() internal view returns (uint256) { 98 | uint256 avePrice = saleAuction.averageGen0SalePrice(); 99 | 100 | // Sanity check to ensure we don't overflow arithmetic 101 | require(avePrice == uint256(uint128(avePrice))); 102 | 103 | uint256 nextPrice = avePrice + (avePrice / 2); 104 | 105 | // We never auction for less than starting price 106 | if (nextPrice < GEN0_STARTING_PRICE) { 107 | nextPrice = GEN0_STARTING_PRICE; 108 | } 109 | 110 | return nextPrice; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /panda_ownership.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./erc721.sol"; 5 | 6 | import "./panda_base.sol"; 7 | 8 | import "./panda_access_control.sol"; 9 | 10 | 11 | 12 | /// @title The facet of the CryptoPandas core contract that manages ownership, ERC-721 (draft) compliant. 13 | /// @author Axiom Zen (https://www.axiomzen.co) 14 | /// @dev Ref: https://github.com/ethereum/EIPs/issues/721 15 | /// See the PandaCore contract documentation to understand how the various contract facets are arranged. 16 | contract PandaOwnership is PandaBase, ERC721 { 17 | 18 | /// @notice Name and symbol of the non fungible token, as defined in ERC721. 19 | string public constant name = "PandaEarth"; 20 | string public constant symbol = "PE"; 21 | 22 | bytes4 constant InterfaceSignature_ERC165 = 23 | bytes4(keccak256('supportsInterface(bytes4)')); 24 | 25 | bytes4 constant InterfaceSignature_ERC721 = 26 | bytes4(keccak256('name()')) ^ 27 | bytes4(keccak256('symbol()')) ^ 28 | bytes4(keccak256('totalSupply()')) ^ 29 | bytes4(keccak256('balanceOf(address)')) ^ 30 | bytes4(keccak256('ownerOf(uint256)')) ^ 31 | bytes4(keccak256('approve(address,uint256)')) ^ 32 | bytes4(keccak256('transfer(address,uint256)')) ^ 33 | bytes4(keccak256('transferFrom(address,address,uint256)')) ^ 34 | bytes4(keccak256('tokensOfOwner(address)')) ^ 35 | bytes4(keccak256('tokenMetadata(uint256,string)')); 36 | 37 | /// @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165). 38 | /// Returns true for any standardized interfaces implemented by this contract. We implement 39 | /// ERC-165 (obviously!) and ERC-721. 40 | function supportsInterface(bytes4 _interfaceID) external view returns (bool) 41 | { 42 | // DEBUG ONLY 43 | //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d)); 44 | 45 | return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721)); 46 | } 47 | 48 | // Internal utility functions: These functions all assume that their input arguments 49 | // are valid. We leave it to public methods to sanitize their inputs and follow 50 | // the required logic. 51 | 52 | /// @dev Checks if a given address is the current owner of a particular Panda. 53 | /// @param _claimant the address we are validating against. 54 | /// @param _tokenId kitten id, only valid when > 0 55 | function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) { 56 | return pandaIndexToOwner[_tokenId] == _claimant; 57 | } 58 | 59 | /// @dev Checks if a given address currently has transferApproval for a particular Panda. 60 | /// @param _claimant the address we are confirming kitten is approved for. 61 | /// @param _tokenId kitten id, only valid when > 0 62 | function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) { 63 | return pandaIndexToApproved[_tokenId] == _claimant; 64 | } 65 | 66 | /// @dev Marks an address as being approved for transferFrom(), overwriting any previous 67 | /// approval. Setting _approved to address(0) clears all transfer approval. 68 | /// NOTE: _approve() does NOT send the Approval event. This is intentional because 69 | /// _approve() and transferFrom() are used together for putting Pandas on auction, and 70 | /// there is no value in spamming the log with Approval events in that case. 71 | function _approve(uint256 _tokenId, address _approved) internal { 72 | pandaIndexToApproved[_tokenId] = _approved; 73 | } 74 | 75 | /// @notice Returns the number of Pandas owned by a specific address. 76 | /// @param _owner The owner address to check. 77 | /// @dev Required for ERC-721 compliance 78 | function balanceOf(address _owner) public view returns (uint256 count) { 79 | return ownershipTokenCount[_owner]; 80 | } 81 | 82 | /// @notice Transfers a Panda to another address. If transferring to a smart 83 | /// contract be VERY CAREFUL to ensure that it is aware of ERC-721 (or 84 | /// CryptoPandas specifically) or your Panda may be lost forever. Seriously. 85 | /// @param _to The address of the recipient, can be a user or contract. 86 | /// @param _tokenId The ID of the Panda to transfer. 87 | /// @dev Required for ERC-721 compliance. 88 | function transfer( 89 | address _to, 90 | uint256 _tokenId 91 | ) 92 | external 93 | whenNotPaused 94 | { 95 | // Safety check to prevent against an unexpected 0x0 default. 96 | require(_to != address(0)); 97 | // Disallow transfers to this contract to prevent accidental misuse. 98 | // The contract should never own any pandas (except very briefly 99 | // after a gen0 cat is created and before it goes on auction). 100 | require(_to != address(this)); 101 | // Disallow transfers to the auction contracts to prevent accidental 102 | // misuse. Auction contracts should only take ownership of pandas 103 | // through the allow + transferFrom flow. 104 | require(_to != address(saleAuction)); 105 | require(_to != address(siringAuction)); 106 | 107 | // You can only send your own cat. 108 | require(_owns(msg.sender, _tokenId)); 109 | 110 | // Reassign ownership, clear pending approvals, emit Transfer event. 111 | _transfer(msg.sender, _to, _tokenId); 112 | } 113 | 114 | /// @notice Grant another address the right to transfer a specific Panda via 115 | /// transferFrom(). This is the preferred flow for transfering NFTs to contracts. 116 | /// @param _to The address to be granted transfer approval. Pass address(0) to 117 | /// clear all approvals. 118 | /// @param _tokenId The ID of the Panda that can be transferred if this call succeeds. 119 | /// @dev Required for ERC-721 compliance. 120 | function approve( 121 | address _to, 122 | uint256 _tokenId 123 | ) 124 | external 125 | whenNotPaused 126 | { 127 | // Only an owner can grant transfer approval. 128 | require(_owns(msg.sender, _tokenId)); 129 | 130 | // Register the approval (replacing any previous approval). 131 | _approve(_tokenId, _to); 132 | 133 | // Emit approval event. 134 | Approval(msg.sender, _to, _tokenId); 135 | } 136 | 137 | /// @notice Transfer a Panda owned by another address, for which the calling address 138 | /// has previously been granted transfer approval by the owner. 139 | /// @param _from The address that owns the Panda to be transfered. 140 | /// @param _to The address that should take ownership of the Panda. Can be any address, 141 | /// including the caller. 142 | /// @param _tokenId The ID of the Panda to be transferred. 143 | /// @dev Required for ERC-721 compliance. 144 | function transferFrom( 145 | address _from, 146 | address _to, 147 | uint256 _tokenId 148 | ) 149 | external 150 | whenNotPaused 151 | { 152 | // Safety check to prevent against an unexpected 0x0 default. 153 | require(_to != address(0)); 154 | // Disallow transfers to this contract to prevent accidental misuse. 155 | // The contract should never own any pandas (except very briefly 156 | // after a gen0 cat is created and before it goes on auction). 157 | require(_to != address(this)); 158 | // Check for approval and valid ownership 159 | require(_approvedFor(msg.sender, _tokenId)); 160 | require(_owns(_from, _tokenId)); 161 | 162 | // Reassign ownership (also clears pending approvals and emits Transfer event). 163 | _transfer(_from, _to, _tokenId); 164 | } 165 | 166 | /// @notice Returns the total number of Pandas currently in existence. 167 | /// @dev Required for ERC-721 compliance. 168 | function totalSupply() public view returns (uint) { 169 | return pandas.length - 1; 170 | } 171 | 172 | /// @notice Returns the address currently assigned ownership of a given Panda. 173 | /// @dev Required for ERC-721 compliance. 174 | function ownerOf(uint256 _tokenId) 175 | external 176 | view 177 | returns (address owner) 178 | { 179 | owner = pandaIndexToOwner[_tokenId]; 180 | 181 | require(owner != address(0)); 182 | } 183 | 184 | /// @notice Returns a list of all Panda IDs assigned to an address. 185 | /// @param _owner The owner whose Pandas we are interested in. 186 | /// @dev This method MUST NEVER be called by smart contract code. First, it's fairly 187 | /// expensive (it walks the entire Panda array looking for cats belonging to owner), 188 | /// but it also returns a dynamic array, which is only supported for web3 calls, and 189 | /// not contract-to-contract calls. 190 | function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) { 191 | uint256 tokenCount = balanceOf(_owner); 192 | 193 | if (tokenCount == 0) { 194 | // Return an empty array 195 | return new uint256[](0); 196 | } else { 197 | uint256[] memory result = new uint256[](tokenCount); 198 | uint256 totalCats = totalSupply(); 199 | uint256 resultIndex = 0; 200 | 201 | // We count on the fact that all cats have IDs starting at 1 and increasing 202 | // sequentially up to the totalCat count. 203 | uint256 catId; 204 | 205 | for (catId = 1; catId <= totalCats; catId++) { 206 | if (pandaIndexToOwner[catId] == _owner) { 207 | result[resultIndex] = catId; 208 | resultIndex++; 209 | } 210 | } 211 | 212 | return result; 213 | } 214 | } 215 | 216 | /// @dev Adapted from memcpy() by @arachnid (Nick Johnson ) 217 | /// This method is licenced under the Apache License. 218 | /// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol 219 | function _memcpy(uint _dest, uint _src, uint _len) private view { 220 | // Copy word-length chunks while possible 221 | for(; _len >= 32; _len -= 32) { 222 | assembly { 223 | mstore(_dest, mload(_src)) 224 | } 225 | _dest += 32; 226 | _src += 32; 227 | } 228 | 229 | // Copy remaining bytes 230 | uint256 mask = 256 ** (32 - _len) - 1; 231 | assembly { 232 | let srcpart := and(mload(_src), not(mask)) 233 | let destpart := and(mload(_dest), mask) 234 | mstore(_dest, or(destpart, srcpart)) 235 | } 236 | } 237 | 238 | /// @dev Adapted from toString(slice) by @arachnid (Nick Johnson ) 239 | /// This method is licenced under the Apache License. 240 | /// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol 241 | function _toString(bytes32[4] _rawBytes, uint256 _stringLength) private view returns (string) { 242 | var outputString = new string(_stringLength); 243 | uint256 outputPtr; 244 | uint256 bytesPtr; 245 | 246 | assembly { 247 | outputPtr := add(outputString, 32) 248 | bytesPtr := _rawBytes 249 | } 250 | 251 | _memcpy(outputPtr, bytesPtr, _stringLength); 252 | 253 | return outputString; 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./ownable.sol"; 4 | 5 | /** 6 | * @title Pausable 7 | * @dev Base contract which allows children to implement an emergency stop mechanism. 8 | */ 9 | contract Pausable is Ownable { 10 | event Pause(); 11 | event Unpause(); 12 | 13 | bool public paused = false; 14 | 15 | 16 | /** 17 | * @dev modifier to allow actions only when the contract IS paused 18 | */ 19 | modifier whenNotPaused() { 20 | require(!paused); 21 | _; 22 | } 23 | 24 | /** 25 | * @dev modifier to allow actions only when the contract IS NOT paused 26 | */ 27 | modifier whenPaused { 28 | require(paused); 29 | _; 30 | } 31 | 32 | /** 33 | * @dev called by the owner to pause, triggers stopped state 34 | */ 35 | function pause() onlyOwner whenNotPaused returns (bool) { 36 | paused = true; 37 | Pause(); 38 | return true; 39 | } 40 | 41 | /** 42 | * @dev called by the owner to unpause, returns to normal state 43 | */ 44 | function unpause() onlyOwner whenPaused returns (bool) { 45 | paused = false; 46 | Unpause(); 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sale_clock_auction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./clock_auction.sol"; 5 | 6 | /// @title Clock auction modified for sale of pandas 7 | /// @notice We omit a fallback function to prevent accidental sends to this contract. 8 | contract SaleClockAuction is ClockAuction { 9 | 10 | // @dev Sanity check that allows us to ensure that we are pointing to the 11 | // right auction in our setSaleAuctionAddress() call. 12 | bool public isSaleClockAuction = true; 13 | 14 | // Tracks last 5 sale price of gen0 panda sales 15 | uint256 public gen0SaleCount; 16 | uint256[5] public lastGen0SalePrices; 17 | uint256 public constant SurpriseValue = 10 finney; 18 | 19 | uint256[] CommonPanda; 20 | uint256[] RarePanda; 21 | uint256 CommonPandaIndex; 22 | uint256 RarePandaIndex; 23 | 24 | // Delegate constructor 25 | function SaleClockAuction(address _nftAddr, uint256 _cut) public 26 | ClockAuction(_nftAddr, _cut) { 27 | CommonPandaIndex = 1; 28 | RarePandaIndex = 1; 29 | } 30 | 31 | /// @dev Creates and begins a new auction. 32 | /// @param _tokenId - ID of token to auction, sender must be owner. 33 | /// @param _startingPrice - Price of item (in wei) at beginning of auction. 34 | /// @param _endingPrice - Price of item (in wei) at end of auction. 35 | /// @param _duration - Length of auction (in seconds). 36 | /// @param _seller - Seller, if not the message sender 37 | function createAuction( 38 | uint256 _tokenId, 39 | uint256 _startingPrice, 40 | uint256 _endingPrice, 41 | uint256 _duration, 42 | address _seller 43 | ) 44 | external 45 | { 46 | // Sanity check that no inputs overflow how many bits we've allocated 47 | // to store them in the auction struct. 48 | require(_startingPrice == uint256(uint128(_startingPrice))); 49 | require(_endingPrice == uint256(uint128(_endingPrice))); 50 | require(_duration == uint256(uint64(_duration))); 51 | 52 | require(msg.sender == address(nonFungibleContract)); 53 | _escrow(_seller, _tokenId); 54 | Auction memory auction = Auction( 55 | _seller, 56 | uint128(_startingPrice), 57 | uint128(_endingPrice), 58 | uint64(_duration), 59 | uint64(now), 60 | 0 61 | ); 62 | _addAuction(_tokenId, auction); 63 | } 64 | 65 | function createGen0Auction( 66 | uint256 _tokenId, 67 | uint256 _startingPrice, 68 | uint256 _endingPrice, 69 | uint256 _duration, 70 | address _seller 71 | ) 72 | external 73 | { 74 | // Sanity check that no inputs overflow how many bits we've allocated 75 | // to store them in the auction struct. 76 | require(_startingPrice == uint256(uint128(_startingPrice))); 77 | require(_endingPrice == uint256(uint128(_endingPrice))); 78 | require(_duration == uint256(uint64(_duration))); 79 | 80 | require(msg.sender == address(nonFungibleContract)); 81 | _escrow(_seller, _tokenId); 82 | Auction memory auction = Auction( 83 | _seller, 84 | uint128(_startingPrice), 85 | uint128(_endingPrice), 86 | uint64(_duration), 87 | uint64(now), 88 | 1 89 | ); 90 | _addAuction(_tokenId, auction); 91 | } 92 | 93 | /// @dev Updates lastSalePrice if seller is the nft contract 94 | /// Otherwise, works the same as default bid method. 95 | function bid(uint256 _tokenId) 96 | external 97 | payable 98 | { 99 | // _bid verifies token ID size 100 | uint64 isGen0 = tokenIdToAuction[_tokenId].isGen0; 101 | uint256 price = _bid(_tokenId, msg.value); 102 | _transfer(msg.sender, _tokenId); 103 | 104 | // If not a gen0 auction, exit 105 | if (isGen0 == 1) { 106 | // Track gen0 sale prices 107 | lastGen0SalePrices[gen0SaleCount % 5] = price; 108 | gen0SaleCount++; 109 | } 110 | } 111 | 112 | function createPanda(uint256 _tokenId,uint256 _type) 113 | external 114 | { 115 | require(msg.sender == address(nonFungibleContract)); 116 | if (_type == 0) { 117 | CommonPanda.push(_tokenId); 118 | }else { 119 | RarePanda.push(_tokenId); 120 | } 121 | } 122 | 123 | function surprisePanda() 124 | external 125 | payable 126 | { 127 | bytes32 bHash = keccak256(block.blockhash(block.number),block.blockhash(block.number-1)); 128 | uint256 PandaIndex; 129 | if (bHash[25] > 0xC8) { 130 | require(uint256(RarePanda.length) >= RarePandaIndex); 131 | PandaIndex = RarePandaIndex; 132 | RarePandaIndex ++; 133 | 134 | } else{ 135 | require(uint256(CommonPanda.length) >= CommonPandaIndex); 136 | PandaIndex = CommonPandaIndex; 137 | CommonPandaIndex ++; 138 | } 139 | _transfer(msg.sender,PandaIndex); 140 | } 141 | 142 | function packageCount() external view returns(uint256 common,uint256 surprise) { 143 | common = CommonPanda.length + 1 - CommonPandaIndex; 144 | surprise = RarePanda.length + 1 - RarePandaIndex; 145 | } 146 | 147 | function averageGen0SalePrice() external view returns (uint256) { 148 | uint256 sum = 0; 149 | for (uint256 i = 0; i < 5; i++) { 150 | sum += lastGen0SalePrices[i]; 151 | } 152 | return sum / 5; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /sale_clock_auction_erc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./clock_auction.sol"; 4 | 5 | 6 | /// @title Clock auction modified for sale of pandas 7 | /// @notice We omit a fallback function to prevent accidental sends to this contract. 8 | contract SaleClockAuctionERC20 is ClockAuction { 9 | 10 | // @dev Sanity check that allows us to ensure that we are pointing to the 11 | // right auction in our setSaleAuctionAddress() call. 12 | bool public isSaleClockAuctionERC20 = true; 13 | 14 | // Tracks last 5 sale price of gen0 panda sales 15 | uint256 public gen0SaleCount; 16 | uint256[5] public lastGen0SalePrices; 17 | 18 | // Delegate constructor 19 | function SaleClockAuctionERC20(address _nftAddr, uint256 _cut) public 20 | ClockAuction(_nftAddr, _cut) {} 21 | 22 | /// @dev Creates and begins a new auction. 23 | /// @param _tokenId - ID of token to auction, sender must be owner. 24 | /// @param _startingPrice - Price of item (in wei) at beginning of auction. 25 | /// @param _endingPrice - Price of item (in wei) at end of auction. 26 | /// @param _duration - Length of auction (in seconds). 27 | /// @param _seller - Seller, if not the message sender 28 | function createAuction( 29 | uint256 _tokenId, 30 | uint256 _startingPrice, 31 | uint256 _endingPrice, 32 | uint256 _duration, 33 | address _seller 34 | ) 35 | external 36 | { 37 | // Sanity check that no inputs overflow how many bits we've allocated 38 | // to store them in the auction struct. 39 | require(_startingPrice == uint256(uint128(_startingPrice))); 40 | require(_endingPrice == uint256(uint128(_endingPrice))); 41 | require(_duration == uint256(uint64(_duration))); 42 | 43 | require(msg.sender == address(nonFungibleContract)); 44 | _escrow(_seller, _tokenId); 45 | Auction memory auction = Auction( 46 | _seller, 47 | uint128(_startingPrice), 48 | uint128(_endingPrice), 49 | uint64(_duration), 50 | uint64(now), 51 | 0 52 | ); 53 | _addAuction(_tokenId, auction); 54 | } 55 | 56 | /// @dev Updates lastSalePrice if seller is the nft contract 57 | /// Otherwise, works the same as default bid method. 58 | function bid(address _erc20address,address _buyerAddress,uint256 _tokenId,uint256 _amount) 59 | external 60 | { 61 | require(msg.sender == address(nonFungibleContract)); 62 | // _bid verifies token ID size 63 | address seller = tokenIdToAuction[_tokenId].seller; 64 | uint256 price = _bidERC20(_erc20address,_buyerAddress,_tokenId, _amount); 65 | _transfer(_buyerAddress, _tokenId); 66 | 67 | // If not a gen0 auction, exit 68 | if (seller == address(nonFungibleContract)) { 69 | // Track gen0 sale prices 70 | lastGen0SalePrices[gen0SaleCount % 5] = price; 71 | gen0SaleCount++; 72 | } 73 | } 74 | 75 | function averageGen0SalePrice() external view returns (uint256) { 76 | uint256 sum = 0; 77 | for (uint256 i = 0; i < 5; i++) { 78 | sum += lastGen0SalePrices[i]; 79 | } 80 | return sum / 5; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /siring_clock_auction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | 5 | import "./clock_auction.sol"; 6 | 7 | /// @title Reverse auction modified for siring 8 | /// @notice We omit a fallback function to prevent accidental sends to this contract. 9 | contract SiringClockAuction is ClockAuction { 10 | 11 | // @dev Sanity check that allows us to ensure that we are pointing to the 12 | // right auction in our setSiringAuctionAddress() call. 13 | bool public isSiringClockAuction = true; 14 | 15 | // Delegate constructor 16 | function SiringClockAuction(address _nftAddr, uint256 _cut) public 17 | ClockAuction(_nftAddr, _cut) {} 18 | 19 | /// @dev Creates and begins a new auction. Since this function is wrapped, 20 | /// require sender to be PandaCore contract. 21 | /// @param _tokenId - ID of token to auction, sender must be owner. 22 | /// @param _startingPrice - Price of item (in wei) at beginning of auction. 23 | /// @param _endingPrice - Price of item (in wei) at end of auction. 24 | /// @param _duration - Length of auction (in seconds). 25 | /// @param _seller - Seller, if not the message sender 26 | function createAuction( 27 | uint256 _tokenId, 28 | uint256 _startingPrice, 29 | uint256 _endingPrice, 30 | uint256 _duration, 31 | address _seller 32 | ) 33 | external 34 | { 35 | // Sanity check that no inputs overflow how many bits we've allocated 36 | // to store them in the auction struct. 37 | require(_startingPrice == uint256(uint128(_startingPrice))); 38 | require(_endingPrice == uint256(uint128(_endingPrice))); 39 | require(_duration == uint256(uint64(_duration))); 40 | 41 | require(msg.sender == address(nonFungibleContract)); 42 | _escrow(_seller, _tokenId); 43 | Auction memory auction = Auction( 44 | _seller, 45 | uint128(_startingPrice), 46 | uint128(_endingPrice), 47 | uint64(_duration), 48 | uint64(now), 49 | 0 50 | ); 51 | _addAuction(_tokenId, auction); 52 | } 53 | 54 | /// @dev Places a bid for siring. Requires the sender 55 | /// is the PandaCore contract because all bid methods 56 | /// should be wrapped. Also returns the panda to the 57 | /// seller rather than the winner. 58 | function bid(uint256 _tokenId) 59 | external 60 | payable 61 | { 62 | require(msg.sender == address(nonFungibleContract)); 63 | address seller = tokenIdToAuction[_tokenId].seller; 64 | // _bid checks that token ID is valid and will throw if bid fails 65 | _bid(_tokenId, msg.value); 66 | // We transfer the panda back to the seller, the winner will get 67 | // the offspring 68 | _transfer(seller, _tokenId); 69 | } 70 | 71 | } 72 | 73 | --------------------------------------------------------------------------------