├── README.md └── contracts ├── Migrations.sol ├── core ├── AxieAccessControl.sol ├── AxieCore.sol ├── dependency │ ├── AxieDependency.sol │ ├── AxieManager.sol │ └── AxieManagerCustomizable.sol ├── erc721 │ ├── AxieERC721.sol │ ├── AxieERC721BaseEnumerable.sol │ └── AxieERC721Metadata.sol └── lifecycle │ ├── AxiePausable.sol │ └── AxieUpgradeable.sol ├── erc ├── erc165 │ ├── ERC165.sol │ └── IERC165.sol └── erc721 │ ├── ERC721TokenReceiver.sol │ ├── IERC721Base.sol │ ├── IERC721Enumerable.sol │ ├── IERC721Metadata.sol │ └── IERC721TokenReceiver.sol ├── marketplace └── AxieClockAuction.sol └── presale ├── AxiePresale.sol ├── AxiePresaleExtended.sol ├── AxieRedemption.sol ├── AxieRedemptionDev.sol ├── AxieRedemptionInternal.sol └── AxieRedemptionRinkeby.sol /README.md: -------------------------------------------------------------------------------- 1 | # Public smart contracts 2 | 3 | Even though all public contracts' source code will be published 4 | on Etherscan, this repository acts as a centralized place 5 | for people to read and review those contracts more easily. 6 | 7 | This repository also contains official addresses of Axie Infinity's contracts. 8 | 9 | *Note: Some contract logics are not fully decentralized as of now. 10 | After the development is done, all those logics will be locked 11 | so no changes can be made and full decentralization would be achieved.* 12 | 13 | ### Presale contract 14 | 15 | This contract holds data for the Presale. 16 | 17 | * Source code: [AxiePresale.sol](contracts/presale/AxiePresale.sol) 18 | * MainNet address: 0xb28a3dd24036151c819c6d401f7a222d9aa3671b 19 | 20 | ### Presale (extended) contract 21 | 22 | This contracts wraps the old presale contract and extend presale duration. 23 | 24 | * Source code: [AxiePresaleExtended.sol](contracts/presale/AxiePresaleExtended.sol) 25 | * MainNet address: 0x3d5be9a472d6b5c8d45b4b3a3bffb80e0c52ef15 26 | 27 | ### Redemption contract 28 | 29 | This contracts allows players to redeem their presale Axies. 30 | 31 | * Source code: [AxieRedemption.sol](contracts/presale/AxieRedemption.sol) 32 | * MainNet address: 0x020f9e661e512132890d259dc363ed084cf4e5a6 33 | 34 | ### Core contract 35 | 36 | This contracts holds Axie data and is ERC-721 compliant. 37 | 38 | * Source code: [AxieCore.sol](contracts/core/AxieCore.sol) 39 | * MainNet address: 0xf5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d 40 | 41 | ### Clock auction contract 42 | 43 | This contracts manages clock auctions in the Marketplace. 44 | 45 | * Source code: [AxieClockAuction.sol](contracts/marketplace/AxieClockAuction.sol) 46 | * MainNet address: 0xf4985070ce32b6b1994329df787d1acc9a2dd9e2 47 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public lastCompletedMigration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) { 10 | _; 11 | } 12 | } 13 | 14 | function Migrations() public { 15 | owner = msg.sender; 16 | } 17 | 18 | function setCompleted(uint completed) public restricted { 19 | lastCompletedMigration = completed; 20 | } 21 | 22 | function upgrade(address newAddress) public restricted { 23 | Migrations upgraded = Migrations(newAddress); 24 | upgraded.setCompleted(lastCompletedMigration); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/core/AxieAccessControl.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | contract AxieAccessControl { 5 | 6 | address public ceoAddress; 7 | address public cfoAddress; 8 | address public cooAddress; 9 | 10 | function AxieAccessControl() internal { 11 | ceoAddress = msg.sender; 12 | } 13 | 14 | modifier onlyCEO() { 15 | require(msg.sender == ceoAddress); 16 | _; 17 | } 18 | 19 | modifier onlyCFO() { 20 | require(msg.sender == cfoAddress); 21 | _; 22 | } 23 | 24 | modifier onlyCOO() { 25 | require(msg.sender == cooAddress); 26 | _; 27 | } 28 | 29 | modifier onlyCLevel() { 30 | require( 31 | // solium-disable operator-whitespace 32 | msg.sender == ceoAddress || 33 | msg.sender == cfoAddress || 34 | msg.sender == cooAddress 35 | // solium-enable operator-whitespace 36 | ); 37 | _; 38 | } 39 | 40 | function setCEO(address _newCEO) external onlyCEO { 41 | require(_newCEO != address(0)); 42 | ceoAddress = _newCEO; 43 | } 44 | 45 | function setCFO(address _newCFO) external onlyCEO { 46 | cfoAddress = _newCFO; 47 | } 48 | 49 | function setCOO(address _newCOO) external onlyCEO { 50 | cooAddress = _newCOO; 51 | } 52 | 53 | function withdrawBalance() external onlyCFO { 54 | cfoAddress.transfer(this.balance); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/core/AxieCore.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./erc721/AxieERC721.sol"; 5 | 6 | 7 | // solium-disable-next-line no-empty-blocks 8 | contract AxieCore is AxieERC721 { 9 | struct Axie { 10 | uint256 genes; 11 | uint256 bornAt; 12 | } 13 | 14 | Axie[] axies; 15 | 16 | event AxieSpawned(uint256 indexed _axieId, address indexed _owner, uint256 _genes); 17 | event AxieRebirthed(uint256 indexed _axieId, uint256 _genes); 18 | event AxieRetired(uint256 indexed _axieId); 19 | event AxieEvolved(uint256 indexed _axieId, uint256 _oldGenes, uint256 _newGenes); 20 | 21 | function AxieCore() public { 22 | axies.push(Axie(0, now)); // The void Axie 23 | _spawnAxie(0, msg.sender); // Will be Puff 24 | _spawnAxie(0, msg.sender); // Will be Kotaro 25 | _spawnAxie(0, msg.sender); // Will be Ginger 26 | _spawnAxie(0, msg.sender); // Will be Stella 27 | } 28 | 29 | function getAxie( 30 | uint256 _axieId 31 | ) 32 | external 33 | view 34 | mustBeValidToken(_axieId) 35 | returns (uint256 /* _genes */, uint256 /* _bornAt */) 36 | { 37 | Axie storage _axie = axies[_axieId]; 38 | return (_axie.genes, _axie.bornAt); 39 | } 40 | 41 | function spawnAxie( 42 | uint256 _genes, 43 | address _owner 44 | ) 45 | external 46 | onlySpawner 47 | whenSpawningAllowed(_genes, _owner) 48 | returns (uint256) 49 | { 50 | return _spawnAxie(_genes, _owner); 51 | } 52 | 53 | function rebirthAxie( 54 | uint256 _axieId, 55 | uint256 _genes 56 | ) 57 | external 58 | onlySpawner 59 | mustBeValidToken(_axieId) 60 | whenRebirthAllowed(_axieId, _genes) 61 | { 62 | Axie storage _axie = axies[_axieId]; 63 | _axie.genes = _genes; 64 | _axie.bornAt = now; 65 | AxieRebirthed(_axieId, _genes); 66 | } 67 | 68 | function retireAxie( 69 | uint256 _axieId, 70 | bool _rip 71 | ) 72 | external 73 | onlyByeSayer 74 | whenRetirementAllowed(_axieId, _rip) 75 | { 76 | _burn(_axieId); 77 | 78 | if (_rip) { 79 | delete axies[_axieId]; 80 | } 81 | 82 | AxieRetired(_axieId); 83 | } 84 | 85 | function evolveAxie( 86 | uint256 _axieId, 87 | uint256 _newGenes 88 | ) 89 | external 90 | onlyGeneScientist 91 | mustBeValidToken(_axieId) 92 | whenEvolvementAllowed(_axieId, _newGenes) 93 | { 94 | uint256 _oldGenes = axies[_axieId].genes; 95 | axies[_axieId].genes = _newGenes; 96 | AxieEvolved(_axieId, _oldGenes, _newGenes); 97 | } 98 | 99 | function _spawnAxie(uint256 _genes, address _owner) private returns (uint256 _axieId) { 100 | Axie memory _axie = Axie(_genes, now); 101 | _axieId = axies.push(_axie) - 1; 102 | _mint(_owner, _axieId); 103 | AxieSpawned(_axieId, _owner, _genes); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /contracts/core/dependency/AxieDependency.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./AxieManager.sol"; 5 | 6 | 7 | contract AxieDependency { 8 | 9 | address public whitelistSetterAddress; 10 | 11 | AxieSpawningManager public spawningManager; 12 | AxieRetirementManager public retirementManager; 13 | AxieMarketplaceManager public marketplaceManager; 14 | AxieGeneManager public geneManager; 15 | 16 | mapping (address => bool) public whitelistedSpawner; 17 | mapping (address => bool) public whitelistedByeSayer; 18 | mapping (address => bool) public whitelistedMarketplace; 19 | mapping (address => bool) public whitelistedGeneScientist; 20 | 21 | function AxieDependency() internal { 22 | whitelistSetterAddress = msg.sender; 23 | } 24 | 25 | modifier onlyWhitelistSetter() { 26 | require(msg.sender == whitelistSetterAddress); 27 | _; 28 | } 29 | 30 | modifier whenSpawningAllowed(uint256 _genes, address _owner) { 31 | require( 32 | spawningManager == address(0) || 33 | spawningManager.isSpawningAllowed(_genes, _owner) 34 | ); 35 | _; 36 | } 37 | 38 | modifier whenRebirthAllowed(uint256 _axieId, uint256 _genes) { 39 | require( 40 | spawningManager == address(0) || 41 | spawningManager.isRebirthAllowed(_axieId, _genes) 42 | ); 43 | _; 44 | } 45 | 46 | modifier whenRetirementAllowed(uint256 _axieId, bool _rip) { 47 | require( 48 | retirementManager == address(0) || 49 | retirementManager.isRetirementAllowed(_axieId, _rip) 50 | ); 51 | _; 52 | } 53 | 54 | modifier whenTransferAllowed(address _from, address _to, uint256 _axieId) { 55 | require( 56 | marketplaceManager == address(0) || 57 | marketplaceManager.isTransferAllowed(_from, _to, _axieId) 58 | ); 59 | _; 60 | } 61 | 62 | modifier whenEvolvementAllowed(uint256 _axieId, uint256 _newGenes) { 63 | require( 64 | geneManager == address(0) || 65 | geneManager.isEvolvementAllowed(_axieId, _newGenes) 66 | ); 67 | _; 68 | } 69 | 70 | modifier onlySpawner() { 71 | require(whitelistedSpawner[msg.sender]); 72 | _; 73 | } 74 | 75 | modifier onlyByeSayer() { 76 | require(whitelistedByeSayer[msg.sender]); 77 | _; 78 | } 79 | 80 | modifier onlyMarketplace() { 81 | require(whitelistedMarketplace[msg.sender]); 82 | _; 83 | } 84 | 85 | modifier onlyGeneScientist() { 86 | require(whitelistedGeneScientist[msg.sender]); 87 | _; 88 | } 89 | 90 | /* 91 | * @dev Setting the whitelist setter address to `address(0)` would be a irreversible process. 92 | * This is to lock changes to Axie's contracts after their development is done. 93 | */ 94 | function setWhitelistSetter(address _newSetter) external onlyWhitelistSetter { 95 | whitelistSetterAddress = _newSetter; 96 | } 97 | 98 | function setSpawningManager(address _manager) external onlyWhitelistSetter { 99 | spawningManager = AxieSpawningManager(_manager); 100 | } 101 | 102 | function setRetirementManager(address _manager) external onlyWhitelistSetter { 103 | retirementManager = AxieRetirementManager(_manager); 104 | } 105 | 106 | function setMarketplaceManager(address _manager) external onlyWhitelistSetter { 107 | marketplaceManager = AxieMarketplaceManager(_manager); 108 | } 109 | 110 | function setGeneManager(address _manager) external onlyWhitelistSetter { 111 | geneManager = AxieGeneManager(_manager); 112 | } 113 | 114 | function setSpawner(address _spawner, bool _whitelisted) external onlyWhitelistSetter { 115 | require(whitelistedSpawner[_spawner] != _whitelisted); 116 | whitelistedSpawner[_spawner] = _whitelisted; 117 | } 118 | 119 | function setByeSayer(address _byeSayer, bool _whitelisted) external onlyWhitelistSetter { 120 | require(whitelistedByeSayer[_byeSayer] != _whitelisted); 121 | whitelistedByeSayer[_byeSayer] = _whitelisted; 122 | } 123 | 124 | function setMarketplace(address _marketplace, bool _whitelisted) external onlyWhitelistSetter { 125 | require(whitelistedMarketplace[_marketplace] != _whitelisted); 126 | whitelistedMarketplace[_marketplace] = _whitelisted; 127 | } 128 | 129 | function setGeneScientist(address _geneScientist, bool _whitelisted) external onlyWhitelistSetter { 130 | require(whitelistedGeneScientist[_geneScientist] != _whitelisted); 131 | whitelistedGeneScientist[_geneScientist] = _whitelisted; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /contracts/core/dependency/AxieManager.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | interface AxieSpawningManager { 5 | function isSpawningAllowed(uint256 _genes, address _owner) external returns (bool); 6 | function isRebirthAllowed(uint256 _axieId, uint256 _genes) external returns (bool); 7 | } 8 | 9 | interface AxieRetirementManager { 10 | function isRetirementAllowed(uint256 _axieId, bool _rip) external returns (bool); 11 | } 12 | 13 | interface AxieMarketplaceManager { 14 | function isTransferAllowed(address _from, address _to, uint256 _axieId) external returns (bool); 15 | } 16 | 17 | interface AxieGeneManager { 18 | function isEvolvementAllowed(uint256 _axieId, uint256 _newGenes) external returns (bool); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/core/dependency/AxieManagerCustomizable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/ownership/Ownable.sol"; 5 | 6 | import "./AxieManager.sol"; 7 | 8 | 9 | // solium-disable-next-line lbrace 10 | contract AxieManagerCustomizable is 11 | AxieSpawningManager, 12 | AxieRetirementManager, 13 | AxieMarketplaceManager, 14 | AxieGeneManager, 15 | Ownable 16 | { 17 | 18 | bool public allowedAll; 19 | 20 | function setAllowAll(bool _allowedAll) external onlyOwner { 21 | allowedAll = _allowedAll; 22 | } 23 | 24 | function isSpawningAllowed(uint256, address) external returns (bool) { 25 | return allowedAll; 26 | } 27 | 28 | function isRebirthAllowed(uint256, uint256) external returns (bool) { 29 | return allowedAll; 30 | } 31 | 32 | function isRetirementAllowed(uint256, bool) external returns (bool) { 33 | return allowedAll; 34 | } 35 | 36 | function isTransferAllowed(address, address, uint256) external returns (bool) { 37 | return allowedAll; 38 | } 39 | 40 | function isEvolvementAllowed(uint256, uint256) external returns (bool) { 41 | return allowedAll; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/core/erc721/AxieERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./AxieERC721BaseEnumerable.sol"; 5 | import "./AxieERC721Metadata.sol"; 6 | 7 | 8 | // solium-disable-next-line no-empty-blocks 9 | contract AxieERC721 is AxieERC721BaseEnumerable, AxieERC721Metadata { 10 | } 11 | -------------------------------------------------------------------------------- /contracts/core/erc721/AxieERC721BaseEnumerable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/math/SafeMath.sol"; 5 | 6 | import "../../erc/erc165/ERC165.sol"; 7 | import "../../erc/erc721/IERC721Base.sol"; 8 | import "../../erc/erc721/IERC721Enumerable.sol"; 9 | import "../../erc/erc721/IERC721TokenReceiver.sol"; 10 | import "../dependency/AxieDependency.sol"; 11 | import "../lifecycle/AxiePausable.sol"; 12 | 13 | 14 | contract AxieERC721BaseEnumerable is ERC165, IERC721Base, IERC721Enumerable, AxieDependency, AxiePausable { 15 | using SafeMath for uint256; 16 | 17 | // @dev Total amount of tokens. 18 | uint256 private _totalTokens; 19 | 20 | // @dev Mapping from token index to ID. 21 | mapping (uint256 => uint256) private _overallTokenId; 22 | 23 | // @dev Mapping from token ID to index. 24 | mapping (uint256 => uint256) private _overallTokenIndex; 25 | 26 | // @dev Mapping from token ID to owner. 27 | mapping (uint256 => address) private _tokenOwner; 28 | 29 | // @dev For a given owner and a given operator, store whether 30 | // the operator is allowed to manage tokens on behalf of the owner. 31 | mapping (address => mapping (address => bool)) private _tokenOperator; 32 | 33 | // @dev Mapping from token ID to approved address. 34 | mapping (uint256 => address) private _tokenApproval; 35 | 36 | // @dev Mapping from owner to list of owned token IDs. 37 | mapping (address => uint256[]) private _ownedTokens; 38 | 39 | // @dev Mapping from token ID to index in the owned token list. 40 | mapping (uint256 => uint256) private _ownedTokenIndex; 41 | 42 | function AxieERC721BaseEnumerable() internal { 43 | supportedInterfaces[0x6466353c] = true; // ERC-721 Base 44 | supportedInterfaces[0x780e9d63] = true; // ERC-721 Enumerable 45 | } 46 | 47 | // solium-disable function-order 48 | 49 | modifier mustBeValidToken(uint256 _tokenId) { 50 | require(_tokenOwner[_tokenId] != address(0)); 51 | _; 52 | } 53 | 54 | function _isTokenOwner(address _ownerToCheck, uint256 _tokenId) private view returns (bool) { 55 | return _tokenOwner[_tokenId] == _ownerToCheck; 56 | } 57 | 58 | function _isTokenOperator(address _operatorToCheck, uint256 _tokenId) private view returns (bool) { 59 | return whitelistedMarketplace[_operatorToCheck] || 60 | _tokenOperator[_tokenOwner[_tokenId]][_operatorToCheck]; 61 | } 62 | 63 | function _isApproved(address _approvedToCheck, uint256 _tokenId) private view returns (bool) { 64 | return _tokenApproval[_tokenId] == _approvedToCheck; 65 | } 66 | 67 | modifier onlyTokenOwner(uint256 _tokenId) { 68 | require(_isTokenOwner(msg.sender, _tokenId)); 69 | _; 70 | } 71 | 72 | modifier onlyTokenOwnerOrOperator(uint256 _tokenId) { 73 | require(_isTokenOwner(msg.sender, _tokenId) || _isTokenOperator(msg.sender, _tokenId)); 74 | _; 75 | } 76 | 77 | modifier onlyTokenAuthorized(uint256 _tokenId) { 78 | require( 79 | // solium-disable operator-whitespace 80 | _isTokenOwner(msg.sender, _tokenId) || 81 | _isTokenOperator(msg.sender, _tokenId) || 82 | _isApproved(msg.sender, _tokenId) 83 | // solium-enable operator-whitespace 84 | ); 85 | _; 86 | } 87 | 88 | // ERC-721 Base 89 | 90 | function balanceOf(address _owner) external view returns (uint256) { 91 | require(_owner != address(0)); 92 | return _ownedTokens[_owner].length; 93 | } 94 | 95 | function ownerOf(uint256 _tokenId) external view mustBeValidToken(_tokenId) returns (address) { 96 | return _tokenOwner[_tokenId]; 97 | } 98 | 99 | function _addTokenTo(address _to, uint256 _tokenId) private { 100 | require(_to != address(0)); 101 | 102 | _tokenOwner[_tokenId] = _to; 103 | 104 | uint256 length = _ownedTokens[_to].length; 105 | _ownedTokens[_to].push(_tokenId); 106 | _ownedTokenIndex[_tokenId] = length; 107 | } 108 | 109 | function _mint(address _to, uint256 _tokenId) internal { 110 | require(_tokenOwner[_tokenId] == address(0)); 111 | 112 | _addTokenTo(_to, _tokenId); 113 | 114 | _overallTokenId[_totalTokens] = _tokenId; 115 | _overallTokenIndex[_tokenId] = _totalTokens; 116 | _totalTokens = _totalTokens.add(1); 117 | 118 | Transfer(address(0), _to, _tokenId); 119 | } 120 | 121 | function _removeTokenFrom(address _from, uint256 _tokenId) private { 122 | require(_from != address(0)); 123 | 124 | uint256 _tokenIndex = _ownedTokenIndex[_tokenId]; 125 | uint256 _lastTokenIndex = _ownedTokens[_from].length.sub(1); 126 | uint256 _lastTokenId = _ownedTokens[_from][_lastTokenIndex]; 127 | 128 | _tokenOwner[_tokenId] = address(0); 129 | 130 | // Insert the last token into the position previously occupied by the removed token. 131 | _ownedTokens[_from][_tokenIndex] = _lastTokenId; 132 | _ownedTokenIndex[_lastTokenId] = _tokenIndex; 133 | 134 | // Resize the array. 135 | delete _ownedTokens[_from][_lastTokenIndex]; 136 | _ownedTokens[_from].length--; 137 | 138 | // Remove the array if no more tokens are owned to prevent pollution. 139 | if (_ownedTokens[_from].length == 0) { 140 | delete _ownedTokens[_from]; 141 | } 142 | 143 | // Update the index of the removed token. 144 | delete _ownedTokenIndex[_tokenId]; 145 | } 146 | 147 | function _burn(uint256 _tokenId) internal { 148 | address _from = _tokenOwner[_tokenId]; 149 | 150 | require(_from != address(0)); 151 | 152 | _removeTokenFrom(_from, _tokenId); 153 | _totalTokens = _totalTokens.sub(1); 154 | 155 | uint256 _tokenIndex = _overallTokenIndex[_tokenId]; 156 | uint256 _lastTokenId = _overallTokenId[_totalTokens]; 157 | 158 | delete _overallTokenIndex[_tokenId]; 159 | delete _overallTokenId[_totalTokens]; 160 | _overallTokenId[_tokenIndex] = _lastTokenId; 161 | _overallTokenIndex[_lastTokenId] = _tokenIndex; 162 | 163 | Transfer(_from, address(0), _tokenId); 164 | } 165 | 166 | function _isContract(address _address) private view returns (bool) { 167 | uint _size; 168 | // solium-disable-next-line security/no-inline-assembly 169 | assembly { _size := extcodesize(_address) } 170 | return _size > 0; 171 | } 172 | 173 | function _transferFrom( 174 | address _from, 175 | address _to, 176 | uint256 _tokenId, 177 | bytes _data, 178 | bool _check 179 | ) 180 | internal 181 | mustBeValidToken(_tokenId) 182 | onlyTokenAuthorized(_tokenId) 183 | whenTransferAllowed(_from, _to, _tokenId) 184 | { 185 | require(_isTokenOwner(_from, _tokenId)); 186 | require(_to != address(0)); 187 | require(_to != _from); 188 | 189 | _removeTokenFrom(_from, _tokenId); 190 | 191 | delete _tokenApproval[_tokenId]; 192 | Approval(_from, address(0), _tokenId); 193 | 194 | _addTokenTo(_to, _tokenId); 195 | 196 | if (_check && _isContract(_to)) { 197 | IERC721TokenReceiver(_to).onERC721Received.gas(50000)(_from, _tokenId, _data); 198 | } 199 | 200 | Transfer(_from, _to, _tokenId); 201 | } 202 | 203 | // solium-disable arg-overflow 204 | 205 | function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable { 206 | _transferFrom(_from, _to, _tokenId, _data, true); 207 | } 208 | 209 | function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable { 210 | _transferFrom(_from, _to, _tokenId, "", true); 211 | } 212 | 213 | function transferFrom(address _from, address _to, uint256 _tokenId) external payable { 214 | _transferFrom(_from, _to, _tokenId, "", false); 215 | } 216 | 217 | // solium-enable arg-overflow 218 | 219 | function approve( 220 | address _approved, 221 | uint256 _tokenId 222 | ) 223 | external 224 | payable 225 | mustBeValidToken(_tokenId) 226 | onlyTokenOwnerOrOperator(_tokenId) 227 | whenNotPaused 228 | { 229 | address _owner = _tokenOwner[_tokenId]; 230 | 231 | require(_owner != _approved); 232 | require(_tokenApproval[_tokenId] != _approved); 233 | 234 | _tokenApproval[_tokenId] = _approved; 235 | 236 | Approval(_owner, _approved, _tokenId); 237 | } 238 | 239 | function setApprovalForAll(address _operator, bool _approved) external whenNotPaused { 240 | require(_tokenOperator[msg.sender][_operator] != _approved); 241 | _tokenOperator[msg.sender][_operator] = _approved; 242 | ApprovalForAll(msg.sender, _operator, _approved); 243 | } 244 | 245 | function getApproved(uint256 _tokenId) external view mustBeValidToken(_tokenId) returns (address) { 246 | return _tokenApproval[_tokenId]; 247 | } 248 | 249 | function isApprovedForAll(address _owner, address _operator) external view returns (bool) { 250 | return _tokenOperator[_owner][_operator]; 251 | } 252 | 253 | // ERC-721 Enumerable 254 | 255 | function totalSupply() external view returns (uint256) { 256 | return _totalTokens; 257 | } 258 | 259 | function tokenByIndex(uint256 _index) external view returns (uint256) { 260 | require(_index < _totalTokens); 261 | return _overallTokenId[_index]; 262 | } 263 | 264 | function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _tokenId) { 265 | require(_owner != address(0)); 266 | require(_index < _ownedTokens[_owner].length); 267 | return _ownedTokens[_owner][_index]; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /contracts/core/erc721/AxieERC721Metadata.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "../../erc/erc721/IERC721Metadata.sol"; 5 | import "./AxieERC721BaseEnumerable.sol"; 6 | 7 | 8 | contract AxieERC721Metadata is AxieERC721BaseEnumerable, IERC721Metadata { 9 | string public tokenURIPrefix = "https://axieinfinity.com/erc/721/axies/"; 10 | string public tokenURISuffix = ".json"; 11 | 12 | function AxieERC721Metadata() internal { 13 | supportedInterfaces[0x5b5e139f] = true; // ERC-721 Metadata 14 | } 15 | 16 | function name() external pure returns (string) { 17 | return "Axie"; 18 | } 19 | 20 | function symbol() external pure returns (string) { 21 | return "AXIE"; 22 | } 23 | 24 | function setTokenURIAffixes(string _prefix, string _suffix) external onlyCEO { 25 | tokenURIPrefix = _prefix; 26 | tokenURISuffix = _suffix; 27 | } 28 | 29 | function tokenURI( 30 | uint256 _tokenId 31 | ) 32 | external 33 | view 34 | mustBeValidToken(_tokenId) 35 | returns (string) 36 | { 37 | bytes memory _tokenURIPrefixBytes = bytes(tokenURIPrefix); 38 | bytes memory _tokenURISuffixBytes = bytes(tokenURISuffix); 39 | uint256 _tmpTokenId = _tokenId; 40 | uint256 _length; 41 | 42 | do { 43 | _length++; 44 | _tmpTokenId /= 10; 45 | } while (_tmpTokenId > 0); 46 | 47 | bytes memory _tokenURIBytes = new bytes(_tokenURIPrefixBytes.length + _length + 5); 48 | uint256 _i = _tokenURIBytes.length - 6; 49 | 50 | _tmpTokenId = _tokenId; 51 | 52 | do { 53 | _tokenURIBytes[_i--] = byte(48 + _tmpTokenId % 10); 54 | _tmpTokenId /= 10; 55 | } while (_tmpTokenId > 0); 56 | 57 | for (_i = 0; _i < _tokenURIPrefixBytes.length; _i++) { 58 | _tokenURIBytes[_i] = _tokenURIPrefixBytes[_i]; 59 | } 60 | 61 | for (_i = 0; _i < _tokenURISuffixBytes.length; _i++) { 62 | _tokenURIBytes[_tokenURIBytes.length + _i - 5] = _tokenURISuffixBytes[_i]; 63 | } 64 | 65 | return string(_tokenURIBytes); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /contracts/core/lifecycle/AxiePausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "../AxieAccessControl.sol"; 5 | 6 | 7 | contract AxiePausable is AxieAccessControl { 8 | 9 | bool public paused = false; 10 | 11 | modifier whenNotPaused() { 12 | require(!paused); 13 | _; 14 | } 15 | 16 | modifier whenPaused { 17 | require(paused); 18 | _; 19 | } 20 | 21 | function pause() external onlyCLevel whenNotPaused { 22 | paused = true; 23 | } 24 | 25 | function unpause() public onlyCEO whenPaused { 26 | paused = false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/core/lifecycle/AxieUpgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./AxiePausable.sol"; 5 | 6 | 7 | contract AxieUpgradeable is AxiePausable { 8 | 9 | address public newContractAddress; 10 | 11 | event ContractUpgraded(address _newAddress); 12 | 13 | function setNewAddress(address _newAddress) external onlyCEO whenPaused { 14 | newContractAddress = _newAddress; 15 | ContractUpgraded(_newAddress); 16 | } 17 | 18 | function unpause() public onlyCEO whenPaused { 19 | require(newContractAddress == address(0)); 20 | super.unpause(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/erc/erc165/ERC165.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./IERC165.sol"; 5 | 6 | 7 | contract ERC165 is IERC165 { 8 | /// @dev You must not set element 0xffffffff to true 9 | mapping (bytes4 => bool) internal supportedInterfaces; 10 | 11 | function ERC165() internal { 12 | supportedInterfaces[0x01ffc9a7] = true; // ERC-165 13 | } 14 | 15 | function supportsInterface(bytes4 interfaceID) external view returns (bool) { 16 | return supportedInterfaces[interfaceID]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/erc/erc165/IERC165.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | /// @title ERC-165 Standard Interface Detection 5 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md 6 | interface IERC165 { 7 | /// @notice Query if a contract implements an interface 8 | /// @param interfaceID The interface identifier, as specified in ERC-165 9 | /// @dev Interface identification is specified in ERC-165. This function 10 | /// uses less than 30,000 gas. 11 | /// @return `true` if the contract implements `interfaceID` and 12 | /// `interfaceID` is not 0xffffffff, `false` otherwise 13 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/erc/erc721/ERC721TokenReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "./IERC721TokenReceiver.sol"; 5 | 6 | 7 | contract ERC721TokenReceiver is IERC721TokenReceiver { 8 | function onERC721Received(address, uint256, bytes) external returns (bytes4) { 9 | return bytes4(keccak256("onERC721Received(address,uint256,bytes)")); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/erc/erc721/IERC721Base.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | /// @title ERC-721 Non-Fungible Token Standard 5 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 6 | /// Note: the ERC-165 identifier for this interface is 0x6466353c 7 | interface IERC721Base /* is IERC165 */ { 8 | /// @dev This emits when ownership of any NFT changes by any mechanism. 9 | /// This event emits when NFTs are created (`from` == 0) and destroyed 10 | /// (`to` == 0). Exception: during contract creation, any number of NFTs 11 | /// may be created and assigned without emitting Transfer. At the time of 12 | /// any transfer, the approved address for that NFT (if any) is reset to none. 13 | event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); 14 | 15 | /// @dev This emits when the approved address for an NFT is changed or 16 | /// reaffirmed. The zero address indicates there is no approved address. 17 | /// When a Transfer event emits, this also indicates that the approved 18 | /// address for that NFT (if any) is reset to none. 19 | event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); 20 | 21 | /// @dev This emits when an operator is enabled or disabled for an owner. 22 | /// The operator can manage all NFTs of the owner. 23 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 24 | 25 | /// @notice Count all NFTs assigned to an owner 26 | /// @dev NFTs assigned to the zero address are considered invalid, and this 27 | /// function throws for queries about the zero address. 28 | /// @param _owner An address for whom to query the balance 29 | /// @return The number of NFTs owned by `_owner`, possibly zero 30 | function balanceOf(address _owner) external view returns (uint256); 31 | 32 | /// @notice Find the owner of an NFT 33 | /// @param _tokenId The identifier for an NFT 34 | /// @dev NFTs assigned to zero address are considered invalid, and queries 35 | /// about them do throw. 36 | /// @return The address of the owner of the NFT 37 | function ownerOf(uint256 _tokenId) external view returns (address); 38 | 39 | /// @notice Transfers the ownership of an NFT from one address to another address 40 | /// @dev Throws unless `msg.sender` is the current owner, an authorized 41 | /// operator, or the approved address for this NFT. Throws if `_from` is 42 | /// not the current owner. Throws if `_to` is the zero address. Throws if 43 | /// `_tokenId` is not a valid NFT. When transfer is complete, this function 44 | /// checks if `_to` is a smart contract (code size > 0). If so, it calls 45 | /// `onERC721Received` on `_to` and throws if the return value is not 46 | /// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`. 47 | /// @param _from The current owner of the NFT 48 | /// @param _to The new owner 49 | /// @param _tokenId The NFT to transfer 50 | /// @param _data Additional data with no specified format, sent in call to `_to` 51 | // solium-disable-next-line arg-overflow 52 | function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable; 53 | 54 | /// @notice Transfers the ownership of an NFT from one address to another address 55 | /// @dev This works identically to the other function with an extra data parameter, 56 | /// except this function just sets data to [] 57 | /// @param _from The current owner of the NFT 58 | /// @param _to The new owner 59 | /// @param _tokenId The NFT to transfer 60 | function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; 61 | 62 | /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE 63 | /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE 64 | /// THEY MAY BE PERMANENTLY LOST 65 | /// @dev Throws unless `msg.sender` is the current owner, an authorized 66 | /// operator, or the approved address for this NFT. Throws if `_from` is 67 | /// not the current owner. Throws if `_to` is the zero address. Throws if 68 | /// `_tokenId` is not a valid NFT. 69 | /// @param _from The current owner of the NFT 70 | /// @param _to The new owner 71 | /// @param _tokenId The NFT to transfer 72 | function transferFrom(address _from, address _to, uint256 _tokenId) external payable; 73 | 74 | /// @notice Set or reaffirm the approved address for an NFT 75 | /// @dev The zero address indicates there is no approved address. 76 | /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized 77 | /// operator of the current owner. 78 | /// @param _approved The new approved NFT controller 79 | /// @param _tokenId The NFT to approve 80 | function approve(address _approved, uint256 _tokenId) external payable; 81 | 82 | /// @notice Enable or disable approval for a third party ("operator") to manage 83 | /// all your asset. 84 | /// @dev Emits the ApprovalForAll event 85 | /// @param _operator Address to add to the set of authorized operators. 86 | /// @param _approved True if the operators is approved, false to revoke approval 87 | function setApprovalForAll(address _operator, bool _approved) external; 88 | 89 | /// @notice Get the approved address for a single NFT 90 | /// @dev Throws if `_tokenId` is not a valid NFT 91 | /// @param _tokenId The NFT to find the approved address for 92 | /// @return The approved address for this NFT, or the zero address if there is none 93 | function getApproved(uint256 _tokenId) external view returns (address); 94 | 95 | /// @notice Query if an address is an authorized operator for another address 96 | /// @param _owner The address that owns the NFTs 97 | /// @param _operator The address that acts on behalf of the owner 98 | /// @return True if `_operator` is an approved operator for `_owner`, false otherwise 99 | function isApprovedForAll(address _owner, address _operator) external view returns (bool); 100 | } 101 | -------------------------------------------------------------------------------- /contracts/erc/erc721/IERC721Enumerable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension 5 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 6 | /// Note: the ERC-165 identifier for this interface is 0x780e9d63 7 | interface IERC721Enumerable /* is IERC721Base */ { 8 | /// @notice Count NFTs tracked by this contract 9 | /// @return A count of valid NFTs tracked by this contract, where each one of 10 | /// them has an assigned and queryable owner not equal to the zero address 11 | function totalSupply() external view returns (uint256); 12 | 13 | /// @notice Enumerate valid NFTs 14 | /// @dev Throws if `_index` >= `totalSupply()`. 15 | /// @param _index A counter less than `totalSupply()` 16 | /// @return The token identifier for the `_index`th NFT, 17 | /// (sort order not specified) 18 | function tokenByIndex(uint256 _index) external view returns (uint256); 19 | 20 | /// @notice Enumerate NFTs assigned to an owner 21 | /// @dev Throws if `_index` >= `balanceOf(_owner)` or if 22 | /// `_owner` is the zero address, representing invalid NFTs. 23 | /// @param _owner An address where we are interested in NFTs owned by them 24 | /// @param _index A counter less than `balanceOf(_owner)` 25 | /// @return The token identifier for the `_index`th NFT assigned to `_owner`, 26 | /// (sort order not specified) 27 | function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _tokenId); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/erc/erc721/IERC721Metadata.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension 5 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 6 | /// Note: the ERC-165 identifier for this interface is 0x5b5e139f 7 | interface IERC721Metadata /* is IERC721Base */ { 8 | /// @notice A descriptive name for a collection of NFTs in this contract 9 | function name() external pure returns (string _name); 10 | 11 | /// @notice An abbreviated name for NFTs in this contract 12 | function symbol() external pure returns (string _symbol); 13 | 14 | /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. 15 | /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC 16 | /// 3986. The URI may point to a JSON file that conforms to the "ERC721 17 | /// Metadata JSON Schema". 18 | function tokenURI(uint256 _tokenId) external view returns (string); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/erc/erc721/IERC721TokenReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | /// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba 5 | interface IERC721TokenReceiver { 6 | /// @notice Handle the receipt of an NFT 7 | /// @dev The ERC721 smart contract calls this function on the recipient 8 | /// after a `transfer`. This function MAY throw to revert and reject the 9 | /// transfer. This function MUST use 50,000 gas or less. Return of other 10 | /// than the magic value MUST result in the transaction being reverted. 11 | /// Note: the contract address is always the message sender. 12 | /// @param _from The sending address 13 | /// @param _tokenId The NFT identifier which is being transfered 14 | /// @param _data Additional data with no specified format 15 | /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` 16 | /// unless throwing 17 | function onERC721Received(address _from, uint256 _tokenId, bytes _data) external returns (bytes4); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/marketplace/AxieClockAuction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/lifecycle/Pausable.sol"; 5 | import "zeppelin/contracts/ownership/HasNoEther.sol"; 6 | 7 | import "../erc/erc721/IERC721Base.sol"; 8 | 9 | 10 | /// @title Clock auction for non-fungible tokens. 11 | contract AxieClockAuction is HasNoEther, Pausable { 12 | 13 | // Represents an auction on an NFT 14 | struct Auction { 15 | // Current owner of NFT 16 | address seller; 17 | // Price (in wei) at beginning of auction 18 | uint128 startingPrice; 19 | // Price (in wei) at end of auction 20 | uint128 endingPrice; 21 | // Duration (in seconds) of auction 22 | uint64 duration; 23 | // Time when auction started 24 | // NOTE: 0 if this auction has been concluded 25 | uint64 startedAt; 26 | } 27 | 28 | // Cut owner takes on each auction, measured in basis points (1/100 of a percent). 29 | // Values 0-10,000 map to 0%-100% 30 | uint256 public ownerCut; 31 | 32 | // Map from token ID to their corresponding auction. 33 | mapping (address => mapping (uint256 => Auction)) public auctions; 34 | 35 | event AuctionCreated( 36 | address indexed _nftAddress, 37 | uint256 indexed _tokenId, 38 | uint256 _startingPrice, 39 | uint256 _endingPrice, 40 | uint256 _duration, 41 | address _seller 42 | ); 43 | 44 | event AuctionSuccessful( 45 | address indexed _nftAddress, 46 | uint256 indexed _tokenId, 47 | uint256 _totalPrice, 48 | address _winner 49 | ); 50 | 51 | event AuctionCancelled( 52 | address indexed _nftAddress, 53 | uint256 indexed _tokenId 54 | ); 55 | 56 | /// @dev Constructor creates a reference to the NFT ownership contract 57 | /// and verifies the owner cut is in the valid range. 58 | /// @param _ownerCut - percent cut the owner takes on each auction, must be 59 | /// between 0-10,000. 60 | function AxieClockAuction(uint256 _ownerCut) public { 61 | require(_ownerCut <= 10000); 62 | ownerCut = _ownerCut; 63 | } 64 | 65 | /// @dev DON'T give me your money. 66 | function () external {} 67 | 68 | // Modifiers to check that inputs can be safely stored with a certain 69 | // number of bits. We use constants and multiple modifiers to save gas. 70 | modifier canBeStoredWith64Bits(uint256 _value) { 71 | require(_value <= 18446744073709551615); 72 | _; 73 | } 74 | 75 | modifier canBeStoredWith128Bits(uint256 _value) { 76 | require(_value < 340282366920938463463374607431768211455); 77 | _; 78 | } 79 | 80 | /// @dev Returns auction info for an NFT on auction. 81 | /// @param _nftAddress - Address of the NFT. 82 | /// @param _tokenId - ID of NFT on auction. 83 | function getAuction( 84 | address _nftAddress, 85 | uint256 _tokenId 86 | ) 87 | external 88 | view 89 | returns ( 90 | address seller, 91 | uint256 startingPrice, 92 | uint256 endingPrice, 93 | uint256 duration, 94 | uint256 startedAt 95 | ) 96 | { 97 | Auction storage _auction = auctions[_nftAddress][_tokenId]; 98 | require(_isOnAuction(_auction)); 99 | return ( 100 | _auction.seller, 101 | _auction.startingPrice, 102 | _auction.endingPrice, 103 | _auction.duration, 104 | _auction.startedAt 105 | ); 106 | } 107 | 108 | /// @dev Returns the current price of an auction. 109 | /// @param _nftAddress - Address of the NFT. 110 | /// @param _tokenId - ID of the token price we are checking. 111 | function getCurrentPrice( 112 | address _nftAddress, 113 | uint256 _tokenId 114 | ) 115 | external 116 | view 117 | returns (uint256) 118 | { 119 | Auction storage _auction = auctions[_nftAddress][_tokenId]; 120 | require(_isOnAuction(_auction)); 121 | return _getCurrentPrice(_auction); 122 | } 123 | 124 | /// @dev Creates and begins a new auction. 125 | /// @param _nftAddress - address of a deployed contract implementing 126 | /// the Nonfungible Interface. 127 | /// @param _tokenId - ID of token to auction, sender must be owner. 128 | /// @param _startingPrice - Price of item (in wei) at beginning of auction. 129 | /// @param _endingPrice - Price of item (in wei) at end of auction. 130 | /// @param _duration - Length of time to move between starting 131 | /// price and ending price (in seconds). 132 | function createAuction( 133 | address _nftAddress, 134 | uint256 _tokenId, 135 | uint256 _startingPrice, 136 | uint256 _endingPrice, 137 | uint256 _duration 138 | ) 139 | external 140 | whenNotPaused 141 | canBeStoredWith128Bits(_startingPrice) 142 | canBeStoredWith128Bits(_endingPrice) 143 | canBeStoredWith64Bits(_duration) 144 | { 145 | address _seller = msg.sender; 146 | require(_owns(_nftAddress, _seller, _tokenId)); 147 | _escrow(_nftAddress, _seller, _tokenId); 148 | Auction memory _auction = Auction( 149 | _seller, 150 | uint128(_startingPrice), 151 | uint128(_endingPrice), 152 | uint64(_duration), 153 | uint64(now) 154 | ); 155 | _addAuction( 156 | _nftAddress, 157 | _tokenId, 158 | _auction, 159 | _seller 160 | ); 161 | } 162 | 163 | /// @dev Bids on an open auction, completing the auction and transferring 164 | /// ownership of the NFT if enough Ether is supplied. 165 | /// @param _nftAddress - address of a deployed contract implementing 166 | /// the Nonfungible Interface. 167 | /// @param _tokenId - ID of token to bid on. 168 | function bid( 169 | address _nftAddress, 170 | uint256 _tokenId 171 | ) 172 | external 173 | payable 174 | whenNotPaused 175 | { 176 | // _bid will throw if the bid or funds transfer fails 177 | _bid(_nftAddress, _tokenId, msg.value); 178 | _transfer(_nftAddress, msg.sender, _tokenId); 179 | } 180 | 181 | /// @dev Cancels an auction that hasn't been won yet. 182 | /// Returns the NFT to original owner. 183 | /// @notice This is a state-modifying function that can 184 | /// be called while the contract is paused. 185 | /// @param _nftAddress - Address of the NFT. 186 | /// @param _tokenId - ID of token on auction 187 | function cancelAuction(address _nftAddress, uint256 _tokenId) external { 188 | Auction storage _auction = auctions[_nftAddress][_tokenId]; 189 | require(_isOnAuction(_auction)); 190 | require(msg.sender == _auction.seller); 191 | _cancelAuction(_nftAddress, _tokenId, _auction.seller); 192 | } 193 | 194 | /// @dev Cancels an auction when the contract is paused. 195 | /// Only the owner may do this, and NFTs are returned to 196 | /// the seller. This should only be used in emergencies. 197 | /// @param _nftAddress - Address of the NFT. 198 | /// @param _tokenId - ID of the NFT on auction to cancel. 199 | function cancelAuctionWhenPaused( 200 | address _nftAddress, 201 | uint256 _tokenId 202 | ) 203 | external 204 | whenPaused 205 | onlyOwner 206 | { 207 | Auction storage _auction = auctions[_nftAddress][_tokenId]; 208 | require(_isOnAuction(_auction)); 209 | _cancelAuction(_nftAddress, _tokenId, _auction.seller); 210 | } 211 | 212 | /// @dev Returns true if the NFT is on auction. 213 | /// @param _auction - Auction to check. 214 | function _isOnAuction(Auction storage _auction) internal view returns (bool) { 215 | return (_auction.startedAt > 0); 216 | } 217 | 218 | /// @dev Gets the NFT object from an address, validating that implementsERC721 is true. 219 | /// @param _nftAddress - Address of the NFT. 220 | function _getNftContract(address _nftAddress) internal pure returns (IERC721Base) { 221 | IERC721Base candidateContract = IERC721Base(_nftAddress); 222 | // require(candidateContract.implementsERC721()); 223 | return candidateContract; 224 | } 225 | 226 | /// @dev Returns current price of an NFT on auction. Broken into two 227 | /// functions (this one, that computes the duration from the auction 228 | /// structure, and the other that does the price computation) so we 229 | /// can easily test that the price computation works correctly. 230 | function _getCurrentPrice( 231 | Auction storage _auction 232 | ) 233 | internal 234 | view 235 | returns (uint256) 236 | { 237 | uint256 _secondsPassed = 0; 238 | 239 | // A bit of insurance against negative values (or wraparound). 240 | // Probably not necessary (since Ethereum guarantees that the 241 | // now variable doesn't ever go backwards). 242 | if (now > _auction.startedAt) { 243 | _secondsPassed = now - _auction.startedAt; 244 | } 245 | 246 | return _computeCurrentPrice( 247 | _auction.startingPrice, 248 | _auction.endingPrice, 249 | _auction.duration, 250 | _secondsPassed 251 | ); 252 | } 253 | 254 | /// @dev Computes the current price of an auction. Factored out 255 | /// from _currentPrice so we can run extensive unit tests. 256 | /// When testing, make this function external and turn on 257 | /// `Current price computation` test suite. 258 | function _computeCurrentPrice( 259 | uint256 _startingPrice, 260 | uint256 _endingPrice, 261 | uint256 _duration, 262 | uint256 _secondsPassed 263 | ) 264 | internal 265 | pure 266 | returns (uint256) 267 | { 268 | // NOTE: We don't use SafeMath (or similar) in this function because 269 | // all of our external functions carefully cap the maximum values for 270 | // time (at 64-bits) and currency (at 128-bits). _duration is 271 | // also known to be non-zero (see the require() statement in 272 | // _addAuction()) 273 | if (_secondsPassed >= _duration) { 274 | // We've reached the end of the dynamic pricing portion 275 | // of the auction, just return the end price. 276 | return _endingPrice; 277 | } else { 278 | // Starting price can be higher than ending price (and often is!), so 279 | // this delta can be negative. 280 | int256 _totalPriceChange = int256(_endingPrice) - int256(_startingPrice); 281 | 282 | // This multiplication can't overflow, _secondsPassed will easily fit within 283 | // 64-bits, and _totalPriceChange will easily fit within 128-bits, their product 284 | // will always fit within 256-bits. 285 | int256 _currentPriceChange = _totalPriceChange * int256(_secondsPassed) / int256(_duration); 286 | 287 | // _currentPriceChange can be negative, but if so, will have a magnitude 288 | // less that _startingPrice. Thus, this result will always end up positive. 289 | int256 _currentPrice = int256(_startingPrice) + _currentPriceChange; 290 | 291 | return uint256(_currentPrice); 292 | } 293 | } 294 | 295 | /// @dev Returns true if the claimant owns the token. 296 | /// @param _nftAddress - The address of the NFT. 297 | /// @param _claimant - Address claiming to own the token. 298 | /// @param _tokenId - ID of token whose ownership to verify. 299 | function _owns(address _nftAddress, address _claimant, uint256 _tokenId) internal view returns (bool) { 300 | IERC721Base _nftContract = _getNftContract(_nftAddress); 301 | return (_nftContract.ownerOf(_tokenId) == _claimant); 302 | } 303 | 304 | /// @dev Adds an auction to the list of open auctions. Also fires the 305 | /// AuctionCreated event. 306 | /// @param _tokenId The ID of the token to be put on auction. 307 | /// @param _auction Auction to add. 308 | function _addAuction( 309 | address _nftAddress, 310 | uint256 _tokenId, 311 | Auction _auction, 312 | address _seller 313 | ) 314 | internal 315 | { 316 | // Require that all auctions have a duration of 317 | // at least one minute. (Keeps our math from getting hairy!) 318 | require(_auction.duration >= 1 minutes); 319 | 320 | auctions[_nftAddress][_tokenId] = _auction; 321 | 322 | AuctionCreated( 323 | _nftAddress, 324 | _tokenId, 325 | uint256(_auction.startingPrice), 326 | uint256(_auction.endingPrice), 327 | uint256(_auction.duration), 328 | _seller 329 | ); 330 | } 331 | 332 | /// @dev Removes an auction from the list of open auctions. 333 | /// @param _tokenId - ID of NFT on auction. 334 | function _removeAuction(address _nftAddress, uint256 _tokenId) internal { 335 | delete auctions[_nftAddress][_tokenId]; 336 | } 337 | 338 | /// @dev Cancels an auction unconditionally. 339 | function _cancelAuction(address _nftAddress, uint256 _tokenId, address _seller) internal { 340 | _removeAuction(_nftAddress, _tokenId); 341 | _transfer(_nftAddress, _seller, _tokenId); 342 | AuctionCancelled(_nftAddress, _tokenId); 343 | } 344 | 345 | /// @dev Escrows the NFT, assigning ownership to this contract. 346 | /// Throws if the escrow fails. 347 | /// @param _nftAddress - The address of the NFT. 348 | /// @param _owner - Current owner address of token to escrow. 349 | /// @param _tokenId - ID of token whose approval to verify. 350 | function _escrow(address _nftAddress, address _owner, uint256 _tokenId) internal { 351 | IERC721Base _nftContract = _getNftContract(_nftAddress); 352 | 353 | // It will throw if transfer fails 354 | _nftContract.transferFrom(_owner, this, _tokenId); 355 | } 356 | 357 | /// @dev Transfers an NFT owned by this contract to another address. 358 | /// Returns true if the transfer succeeds. 359 | /// @param _nftAddress - The address of the NFT. 360 | /// @param _receiver - Address to transfer NFT to. 361 | /// @param _tokenId - ID of token to transfer. 362 | function _transfer(address _nftAddress, address _receiver, uint256 _tokenId) internal { 363 | IERC721Base _nftContract = _getNftContract(_nftAddress); 364 | 365 | // It will throw if transfer fails 366 | _nftContract.transferFrom(this, _receiver, _tokenId); 367 | } 368 | 369 | /// @dev Computes owner's cut of a sale. 370 | /// @param _price - Sale price of NFT. 371 | function _computeCut(uint256 _price) internal view returns (uint256) { 372 | // NOTE: We don't use SafeMath (or similar) in this function because 373 | // all of our entry functions carefully cap the maximum values for 374 | // currency (at 128-bits), and ownerCut <= 10000 (see the require() 375 | // statement in the ClockAuction constructor). The result of this 376 | // function is always guaranteed to be <= _price. 377 | return _price * ownerCut / 10000; 378 | } 379 | 380 | /// @dev Computes the price and transfers winnings. 381 | /// Does NOT transfer ownership of token. 382 | function _bid( 383 | address _nftAddress, 384 | uint256 _tokenId, 385 | uint256 _bidAmount 386 | ) 387 | internal 388 | returns (uint256) 389 | { 390 | // Get a reference to the auction struct 391 | Auction storage _auction = auctions[_nftAddress][_tokenId]; 392 | 393 | // Explicitly check that this auction is currently live. 394 | // (Because of how Ethereum mappings work, we can't just count 395 | // on the lookup above failing. An invalid _tokenId will just 396 | // return an auction object that is all zeros.) 397 | require(_isOnAuction(_auction)); 398 | 399 | // Check that the incoming bid is higher than the current 400 | // price 401 | uint256 _price = _getCurrentPrice(_auction); 402 | require(_bidAmount >= _price); 403 | 404 | // Grab a reference to the seller before the auction struct 405 | // gets deleted. 406 | address _seller = _auction.seller; 407 | 408 | // The bid is good! Remove the auction before sending the fees 409 | // to the sender so we can't have a reentrancy attack. 410 | _removeAuction(_nftAddress, _tokenId); 411 | 412 | // Transfer proceeds to seller (if there are any!) 413 | if (_price > 0) { 414 | // Calculate the auctioneer's cut. 415 | // (NOTE: _computeCut() is guaranteed to return a 416 | // value <= price, so this subtraction can't go negative.) 417 | uint256 _auctioneerCut = _computeCut(_price); 418 | uint256 _sellerProceeds = _price - _auctioneerCut; 419 | 420 | // NOTE: Doing a transfer() in the middle of a complex 421 | // method like this is generally discouraged because of 422 | // reentrancy attacks and DoS attacks if the seller is 423 | // a contract with an invalid fallback function. We explicitly 424 | // guard against reentrancy attacks by removing the auction 425 | // before calling transfer(), and the only thing the seller 426 | // can DoS is the sale of their own asset! (And if it's an 427 | // accident, they can call cancelAuction(). ) 428 | _seller.transfer(_sellerProceeds); 429 | } 430 | 431 | if (_bidAmount > _price) { 432 | // Calculate any excess funds included with the bid. If the excess 433 | // is anything worth worrying about, transfer it back to bidder. 434 | // NOTE: We checked above that the bid amount is greater than or 435 | // equal to the price so this cannot underflow. 436 | uint256 _bidExcess = _bidAmount - _price; 437 | 438 | // Return the funds. Similar to the previous transfer, this is 439 | // not susceptible to a re-entry attack because the auction is 440 | // removed before any transfers occur. 441 | msg.sender.transfer(_bidExcess); 442 | } 443 | 444 | // Tell the world! 445 | AuctionSuccessful( 446 | _nftAddress, 447 | _tokenId, 448 | _price, 449 | msg.sender 450 | ); 451 | 452 | return _price; 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /contracts/presale/AxiePresale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/lifecycle/Pausable.sol"; 5 | import "zeppelin/contracts/math/SafeMath.sol"; 6 | import "zeppelin/contracts/ownership/HasNoEther.sol"; 7 | 8 | 9 | contract AxiePresale is HasNoEther, Pausable { 10 | using SafeMath for uint256; 11 | 12 | // No Axies can be adopted after this end date: Friday, March 16, 2018 11:59:59 PM GMT. 13 | uint256 constant public PRESALE_END_TIMESTAMP = 1521244799; 14 | 15 | uint8 constant public CLASS_BEAST = 0; 16 | uint8 constant public CLASS_AQUATIC = 2; 17 | uint8 constant public CLASS_PLANT = 4; 18 | 19 | uint256 constant public INITIAL_PRICE_INCREMENT = 1600 szabo; // 0.0016 Ether 20 | uint256 constant public INITIAL_PRICE = INITIAL_PRICE_INCREMENT; 21 | uint256 constant public REF_CREDITS_PER_AXIE = 5; 22 | 23 | mapping (uint8 => uint256) public currentPrices; 24 | mapping (uint8 => uint256) public priceIncrements; 25 | 26 | mapping (uint8 => uint256) public totalAxiesAdopted; 27 | mapping (address => mapping (uint8 => uint256)) public axiesAdopted; 28 | 29 | mapping (address => uint256) public referralCredits; 30 | mapping (address => uint256) public axiesRewarded; 31 | uint256 public totalAxiesRewarded; 32 | 33 | event AxiesAdopted( 34 | address indexed adopter, 35 | uint8 indexed clazz, 36 | uint256 quantity, 37 | address indexed referrer 38 | ); 39 | 40 | event AxiesRewarded(address indexed receiver, uint256 quantity); 41 | 42 | event AdoptedAxiesRedeemed(address indexed receiver, uint8 indexed clazz, uint256 quantity); 43 | event RewardedAxiesRedeemed(address indexed receiver, uint256 quantity); 44 | 45 | function AxiePresale() public { 46 | priceIncrements[CLASS_BEAST] = priceIncrements[CLASS_AQUATIC] = // 47 | priceIncrements[CLASS_PLANT] = INITIAL_PRICE_INCREMENT; 48 | 49 | currentPrices[CLASS_BEAST] = currentPrices[CLASS_AQUATIC] = // 50 | currentPrices[CLASS_PLANT] = INITIAL_PRICE; 51 | } 52 | 53 | function axiesPrice( 54 | uint256 beastQuantity, 55 | uint256 aquaticQuantity, 56 | uint256 plantQuantity 57 | ) 58 | public 59 | view 60 | returns (uint256 totalPrice) 61 | { 62 | uint256 price; 63 | 64 | (price,,) = _axiesPrice(CLASS_BEAST, beastQuantity); 65 | totalPrice = totalPrice.add(price); 66 | 67 | (price,,) = _axiesPrice(CLASS_AQUATIC, aquaticQuantity); 68 | totalPrice = totalPrice.add(price); 69 | 70 | (price,,) = _axiesPrice(CLASS_PLANT, plantQuantity); 71 | totalPrice = totalPrice.add(price); 72 | } 73 | 74 | function adoptAxies( 75 | uint256 beastQuantity, 76 | uint256 aquaticQuantity, 77 | uint256 plantQuantity, 78 | address referrer 79 | ) 80 | public 81 | payable 82 | whenNotPaused 83 | { 84 | require(now <= PRESALE_END_TIMESTAMP); 85 | 86 | require(beastQuantity <= 3); 87 | require(aquaticQuantity <= 3); 88 | require(plantQuantity <= 3); 89 | 90 | address adopter = msg.sender; 91 | address actualReferrer = 0x0; 92 | 93 | // An adopter cannot be his/her own referrer. 94 | if (referrer != adopter) { 95 | actualReferrer = referrer; 96 | } 97 | 98 | uint256 value = msg.value; 99 | uint256 price; 100 | 101 | if (beastQuantity > 0) { 102 | price = _adoptAxies( 103 | adopter, 104 | CLASS_BEAST, 105 | beastQuantity, 106 | actualReferrer 107 | ); 108 | 109 | require(value >= price); 110 | value -= price; 111 | } 112 | 113 | if (aquaticQuantity > 0) { 114 | price = _adoptAxies( 115 | adopter, 116 | CLASS_AQUATIC, 117 | aquaticQuantity, 118 | actualReferrer 119 | ); 120 | 121 | require(value >= price); 122 | value -= price; 123 | } 124 | 125 | if (plantQuantity > 0) { 126 | price = _adoptAxies( 127 | adopter, 128 | CLASS_PLANT, 129 | plantQuantity, 130 | actualReferrer 131 | ); 132 | 133 | require(value >= price); 134 | value -= price; 135 | } 136 | 137 | msg.sender.transfer(value); 138 | 139 | // The current referral is ignored if the referrer's address is 0x0. 140 | if (actualReferrer != 0x0) { 141 | uint256 numCredit = referralCredits[actualReferrer] 142 | .add(beastQuantity) 143 | .add(aquaticQuantity) 144 | .add(plantQuantity); 145 | 146 | uint256 numReward = numCredit / REF_CREDITS_PER_AXIE; 147 | 148 | if (numReward > 0) { 149 | referralCredits[actualReferrer] = numCredit % REF_CREDITS_PER_AXIE; 150 | axiesRewarded[actualReferrer] = axiesRewarded[actualReferrer].add(numReward); 151 | totalAxiesRewarded = totalAxiesRewarded.add(numReward); 152 | AxiesRewarded(actualReferrer, numReward); 153 | } else { 154 | referralCredits[actualReferrer] = numCredit; 155 | } 156 | } 157 | } 158 | 159 | function redeemAdoptedAxies( 160 | address receiver, 161 | uint256 beastQuantity, 162 | uint256 aquaticQuantity, 163 | uint256 plantQuantity 164 | ) 165 | public 166 | onlyOwner 167 | returns ( 168 | uint256 /* remainingBeastQuantity */, 169 | uint256 /* remainingAquaticQuantity */, 170 | uint256 /* remainingPlantQuantity */ 171 | ) 172 | { 173 | return ( 174 | _redeemAdoptedAxies(receiver, CLASS_BEAST, beastQuantity), 175 | _redeemAdoptedAxies(receiver, CLASS_AQUATIC, aquaticQuantity), 176 | _redeemAdoptedAxies(receiver, CLASS_PLANT, plantQuantity) 177 | ); 178 | } 179 | 180 | function redeemRewardedAxies( 181 | address receiver, 182 | uint256 quantity 183 | ) 184 | public 185 | onlyOwner 186 | returns (uint256 remainingQuantity) 187 | { 188 | remainingQuantity = axiesRewarded[receiver] = axiesRewarded[receiver].sub(quantity); 189 | 190 | if (quantity > 0) { 191 | // This requires that rewarded Axies are always included in the total 192 | // to make sure overflow won't happen. 193 | totalAxiesRewarded -= quantity; 194 | 195 | RewardedAxiesRedeemed(receiver, quantity); 196 | } 197 | } 198 | 199 | /** 200 | * @dev Calculate price of Axies from the same class. 201 | * @param clazz The class of Axies. 202 | * @param quantity Number of Axies to be calculated. 203 | */ 204 | function _axiesPrice( 205 | uint8 clazz, 206 | uint256 quantity 207 | ) 208 | private 209 | view 210 | returns (uint256 totalPrice, uint256 priceIncrement, uint256 currentPrice) 211 | { 212 | priceIncrement = priceIncrements[clazz]; 213 | currentPrice = currentPrices[clazz]; 214 | 215 | uint256 nextPrice; 216 | 217 | for (uint256 i = 0; i < quantity; i++) { 218 | totalPrice = totalPrice.add(currentPrice); 219 | nextPrice = currentPrice.add(priceIncrement); 220 | 221 | if (nextPrice / 100 finney != currentPrice / 100 finney) { 222 | priceIncrement >>= 1; 223 | } 224 | 225 | currentPrice = nextPrice; 226 | } 227 | } 228 | 229 | /** 230 | * @dev Adopt some Axies from the same class. 231 | * @param adopter Address of the adopter. 232 | * @param clazz The class of adopted Axies. 233 | * @param quantity Number of Axies to be adopted, this should be positive. 234 | * @param referrer Address of the referrer. 235 | */ 236 | function _adoptAxies( 237 | address adopter, 238 | uint8 clazz, 239 | uint256 quantity, 240 | address referrer 241 | ) 242 | private 243 | returns (uint256 totalPrice) 244 | { 245 | (totalPrice, priceIncrements[clazz], currentPrices[clazz]) = _axiesPrice(clazz, quantity); 246 | 247 | axiesAdopted[adopter][clazz] = axiesAdopted[adopter][clazz].add(quantity); 248 | totalAxiesAdopted[clazz] = totalAxiesAdopted[clazz].add(quantity); 249 | 250 | AxiesAdopted( 251 | adopter, 252 | clazz, 253 | quantity, 254 | referrer 255 | ); 256 | } 257 | 258 | /** 259 | * @dev Redeem adopted Axies from the same class. 260 | * @param receiver Address of the receiver. 261 | * @param clazz The class of adopted Axies. 262 | * @param quantity Number of adopted Axies to be redeemed. 263 | */ 264 | function _redeemAdoptedAxies( 265 | address receiver, 266 | uint8 clazz, 267 | uint256 quantity 268 | ) 269 | private 270 | returns (uint256 remainingQuantity) 271 | { 272 | remainingQuantity = axiesAdopted[receiver][clazz] = axiesAdopted[receiver][clazz].sub(quantity); 273 | 274 | if (quantity > 0) { 275 | // This requires that adopted Axies are always included in the total 276 | // to make sure overflow won't happen. 277 | totalAxiesAdopted[clazz] -= quantity; 278 | 279 | AdoptedAxiesRedeemed(receiver, clazz, quantity); 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /contracts/presale/AxiePresaleExtended.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/lifecycle/Pausable.sol"; 5 | import "zeppelin/contracts/math/SafeMath.sol"; 6 | import "zeppelin/contracts/ownership/HasNoContracts.sol"; 7 | 8 | import "./AxiePresale.sol"; 9 | 10 | 11 | contract AxiePresaleExtended is HasNoContracts, Pausable { 12 | using SafeMath for uint256; 13 | 14 | // No Axies can be adopted after this end date: Monday, April 16, 2018 11:59:59 PM GMT. 15 | uint256 constant public PRESALE_END_TIMESTAMP = 1523923199; 16 | 17 | // The total number of adopted Axies will be capped at 5250, 18 | // so the number of Axies which have Mystic parts will be capped roughly at 2000. 19 | uint256 constant public MAX_TOTAL_ADOPTED_AXIES = 5250; 20 | 21 | uint8 constant public CLASS_BEAST = 0; 22 | uint8 constant public CLASS_AQUATIC = 2; 23 | uint8 constant public CLASS_PLANT = 4; 24 | 25 | // The initial price increment and the initial price are for reference only 26 | uint256 constant public INITIAL_PRICE_INCREMENT = 1600 szabo; // 0.0016 Ether 27 | uint256 constant public INITIAL_PRICE = INITIAL_PRICE_INCREMENT; 28 | 29 | uint256 constant public REF_CREDITS_PER_AXIE = 5; 30 | 31 | AxiePresale public presaleContract; 32 | address public redemptionAddress; 33 | 34 | mapping (uint8 => uint256) public currentPrice; 35 | mapping (uint8 => uint256) public priceIncrement; 36 | 37 | mapping (uint8 => uint256) private _totalAdoptedAxies; 38 | mapping (uint8 => uint256) private _totalDeductedAdoptedAxies; 39 | mapping (address => mapping (uint8 => uint256)) private _numAdoptedAxies; 40 | mapping (address => mapping (uint8 => uint256)) private _numDeductedAdoptedAxies; 41 | 42 | mapping (address => uint256) private _numRefCredits; 43 | mapping (address => uint256) private _numDeductedRefCredits; 44 | uint256 public numBountyCredits; 45 | 46 | uint256 private _totalRewardedAxies; 47 | uint256 private _totalDeductedRewardedAxies; 48 | mapping (address => uint256) private _numRewardedAxies; 49 | mapping (address => uint256) private _numDeductedRewardedAxies; 50 | 51 | event AxiesAdopted( 52 | address indexed _adopter, 53 | uint8 indexed _class, 54 | uint256 _quantity, 55 | address indexed _referrer 56 | ); 57 | 58 | event AxiesRewarded(address indexed _receiver, uint256 _quantity); 59 | 60 | event AdoptedAxiesRedeemed(address indexed _receiver, uint8 indexed _class, uint256 _quantity); 61 | event RewardedAxiesRedeemed(address indexed _receiver, uint256 _quantity); 62 | 63 | event RefCreditsMinted(address indexed _receiver, uint256 _numMintedCredits); 64 | 65 | function AxiePresaleExtended() public payable { 66 | require(msg.value == 0); 67 | paused = true; 68 | numBountyCredits = 300; 69 | } 70 | 71 | function () external payable { 72 | require(msg.sender == address(presaleContract)); 73 | } 74 | 75 | modifier whenNotInitialized { 76 | require(presaleContract == address(0)); 77 | _; 78 | } 79 | 80 | modifier whenInitialized { 81 | require(presaleContract != address(0)); 82 | _; 83 | } 84 | 85 | modifier onlyRedemptionAddress { 86 | require(msg.sender == redemptionAddress); 87 | _; 88 | } 89 | 90 | function reclaimEther() external onlyOwner whenInitialized { 91 | presaleContract.reclaimEther(); 92 | owner.transfer(this.balance); 93 | } 94 | 95 | /** 96 | * @dev This must be called only once after the owner of the presale contract 97 | * has been updated to this contract. 98 | */ 99 | function initialize(address _presaleAddress) external onlyOwner whenNotInitialized { 100 | // Set the presale address. 101 | presaleContract = AxiePresale(_presaleAddress); 102 | 103 | presaleContract.pause(); 104 | 105 | // Restore price increments from the old contract. 106 | priceIncrement[CLASS_BEAST] = presaleContract.priceIncrements(CLASS_BEAST); 107 | priceIncrement[CLASS_AQUATIC] = presaleContract.priceIncrements(CLASS_AQUATIC); 108 | priceIncrement[CLASS_PLANT] = presaleContract.priceIncrements(CLASS_PLANT); 109 | 110 | // Restore current prices from the old contract. 111 | currentPrice[CLASS_BEAST] = presaleContract.currentPrices(CLASS_BEAST); 112 | currentPrice[CLASS_AQUATIC] = presaleContract.currentPrices(CLASS_AQUATIC); 113 | currentPrice[CLASS_PLANT] = presaleContract.currentPrices(CLASS_PLANT); 114 | 115 | paused = false; 116 | } 117 | 118 | function setRedemptionAddress(address _redemptionAddress) external onlyOwner whenInitialized { 119 | redemptionAddress = _redemptionAddress; 120 | } 121 | 122 | function totalAdoptedAxies( 123 | uint8 _class, 124 | bool _deduction 125 | ) 126 | external 127 | view 128 | whenInitialized 129 | returns (uint256 _number) 130 | { 131 | _number = _totalAdoptedAxies[_class] 132 | .add(presaleContract.totalAxiesAdopted(_class)); 133 | 134 | if (_deduction) { 135 | _number = _number.sub(_totalDeductedAdoptedAxies[_class]); 136 | } 137 | } 138 | 139 | function numAdoptedAxies( 140 | address _owner, 141 | uint8 _class, 142 | bool _deduction 143 | ) 144 | external 145 | view 146 | whenInitialized 147 | returns (uint256 _number) 148 | { 149 | _number = _numAdoptedAxies[_owner][_class] 150 | .add(presaleContract.axiesAdopted(_owner, _class)); 151 | 152 | if (_deduction) { 153 | _number = _number.sub(_numDeductedAdoptedAxies[_owner][_class]); 154 | } 155 | } 156 | 157 | function numRefCredits( 158 | address _owner, 159 | bool _deduction 160 | ) 161 | external 162 | view 163 | whenInitialized 164 | returns (uint256 _number) 165 | { 166 | _number = _numRefCredits[_owner] 167 | .add(presaleContract.referralCredits(_owner)); 168 | 169 | if (_deduction) { 170 | _number = _number.sub(_numDeductedRefCredits[_owner]); 171 | } 172 | } 173 | 174 | function totalRewardedAxies( 175 | bool _deduction 176 | ) 177 | external 178 | view 179 | whenInitialized 180 | returns (uint256 _number) 181 | { 182 | _number = _totalRewardedAxies 183 | .add(presaleContract.totalAxiesRewarded()); 184 | 185 | if (_deduction) { 186 | _number = _number.sub(_totalDeductedRewardedAxies); 187 | } 188 | } 189 | 190 | function numRewardedAxies( 191 | address _owner, 192 | bool _deduction 193 | ) 194 | external 195 | view 196 | whenInitialized 197 | returns (uint256 _number) 198 | { 199 | _number = _numRewardedAxies[_owner] 200 | .add(presaleContract.axiesRewarded(_owner)); 201 | 202 | if (_deduction) { 203 | _number = _number.sub(_numDeductedRewardedAxies[_owner]); 204 | } 205 | } 206 | 207 | function axiesPrice( 208 | uint256 _beastQuantity, 209 | uint256 _aquaticQuantity, 210 | uint256 _plantQuantity 211 | ) 212 | external 213 | view 214 | whenInitialized 215 | returns (uint256 _totalPrice) 216 | { 217 | uint256 price; 218 | 219 | (price,,) = _sameClassAxiesPrice(CLASS_BEAST, _beastQuantity); 220 | _totalPrice = _totalPrice.add(price); 221 | 222 | (price,,) = _sameClassAxiesPrice(CLASS_AQUATIC, _aquaticQuantity); 223 | _totalPrice = _totalPrice.add(price); 224 | 225 | (price,,) = _sameClassAxiesPrice(CLASS_PLANT, _plantQuantity); 226 | _totalPrice = _totalPrice.add(price); 227 | } 228 | 229 | function adoptAxies( 230 | uint256 _beastQuantity, 231 | uint256 _aquaticQuantity, 232 | uint256 _plantQuantity, 233 | address _referrer 234 | ) 235 | external 236 | payable 237 | whenInitialized 238 | whenNotPaused 239 | { 240 | require(now <= PRESALE_END_TIMESTAMP); 241 | require(_beastQuantity <= 3 && _aquaticQuantity <= 3 && _plantQuantity <= 3); 242 | 243 | uint256 _totalAdopted = this.totalAdoptedAxies(CLASS_BEAST, false) 244 | .add(this.totalAdoptedAxies(CLASS_AQUATIC, false)) 245 | .add(this.totalAdoptedAxies(CLASS_PLANT, false)) 246 | .add(_beastQuantity) 247 | .add(_aquaticQuantity) 248 | .add(_plantQuantity); 249 | 250 | require(_totalAdopted <= MAX_TOTAL_ADOPTED_AXIES); 251 | 252 | address _adopter = msg.sender; 253 | address _actualReferrer = 0x0; 254 | 255 | // An adopter cannot be his/her own referrer. 256 | if (_referrer != _adopter) { 257 | _actualReferrer = _referrer; 258 | } 259 | 260 | uint256 _value = msg.value; 261 | uint256 _price; 262 | 263 | if (_beastQuantity > 0) { 264 | _price = _adoptSameClassAxies( 265 | _adopter, 266 | CLASS_BEAST, 267 | _beastQuantity, 268 | _actualReferrer 269 | ); 270 | 271 | require(_value >= _price); 272 | _value -= _price; 273 | } 274 | 275 | if (_aquaticQuantity > 0) { 276 | _price = _adoptSameClassAxies( 277 | _adopter, 278 | CLASS_AQUATIC, 279 | _aquaticQuantity, 280 | _actualReferrer 281 | ); 282 | 283 | require(_value >= _price); 284 | _value -= _price; 285 | } 286 | 287 | if (_plantQuantity > 0) { 288 | _price = _adoptSameClassAxies( 289 | _adopter, 290 | CLASS_PLANT, 291 | _plantQuantity, 292 | _actualReferrer 293 | ); 294 | 295 | require(_value >= _price); 296 | _value -= _price; 297 | } 298 | 299 | msg.sender.transfer(_value); 300 | 301 | // The current referral is ignored if the referrer's address is 0x0. 302 | if (_actualReferrer != 0x0) { 303 | _applyRefCredits( 304 | _actualReferrer, 305 | _beastQuantity.add(_aquaticQuantity).add(_plantQuantity) 306 | ); 307 | } 308 | } 309 | 310 | function mintRefCredits( 311 | address _receiver, 312 | uint256 _numMintedCredits 313 | ) 314 | external 315 | onlyOwner 316 | whenInitialized 317 | returns (uint256) 318 | { 319 | require(_receiver != address(0)); 320 | numBountyCredits = numBountyCredits.sub(_numMintedCredits); 321 | _applyRefCredits(_receiver, _numMintedCredits); 322 | RefCreditsMinted(_receiver, _numMintedCredits); 323 | return numBountyCredits; 324 | } 325 | 326 | function redeemAdoptedAxies( 327 | address _receiver, 328 | uint256 _beastQuantity, 329 | uint256 _aquaticQuantity, 330 | uint256 _plantQuantity 331 | ) 332 | external 333 | onlyRedemptionAddress 334 | whenInitialized 335 | returns ( 336 | uint256 /* remainingBeastQuantity */, 337 | uint256 /* remainingAquaticQuantity */, 338 | uint256 /* remainingPlantQuantity */ 339 | ) 340 | { 341 | return ( 342 | _redeemSameClassAdoptedAxies(_receiver, CLASS_BEAST, _beastQuantity), 343 | _redeemSameClassAdoptedAxies(_receiver, CLASS_AQUATIC, _aquaticQuantity), 344 | _redeemSameClassAdoptedAxies(_receiver, CLASS_PLANT, _plantQuantity) 345 | ); 346 | } 347 | 348 | function redeemRewardedAxies( 349 | address _receiver, 350 | uint256 _quantity 351 | ) 352 | external 353 | onlyRedemptionAddress 354 | whenInitialized 355 | returns (uint256 _remainingQuantity) 356 | { 357 | _remainingQuantity = this.numRewardedAxies(_receiver, true).sub(_quantity); 358 | 359 | if (_quantity > 0) { 360 | _numDeductedRewardedAxies[_receiver] = _numDeductedRewardedAxies[_receiver].add(_quantity); 361 | _totalDeductedRewardedAxies = _totalDeductedRewardedAxies.add(_quantity); 362 | 363 | RewardedAxiesRedeemed(_receiver, _quantity); 364 | } 365 | } 366 | 367 | /** 368 | * @notice Calculate price of Axies from the same class. 369 | * @param _class The class of Axies. 370 | * @param _quantity Number of Axies to be calculated. 371 | */ 372 | function _sameClassAxiesPrice( 373 | uint8 _class, 374 | uint256 _quantity 375 | ) 376 | private 377 | view 378 | returns ( 379 | uint256 _totalPrice, 380 | uint256 /* should be _subsequentIncrement */ _currentIncrement, 381 | uint256 /* should be _subsequentPrice */ _currentPrice 382 | ) 383 | { 384 | _currentIncrement = priceIncrement[_class]; 385 | _currentPrice = currentPrice[_class]; 386 | 387 | uint256 _nextPrice; 388 | 389 | for (uint256 i = 0; i < _quantity; i++) { 390 | _totalPrice = _totalPrice.add(_currentPrice); 391 | _nextPrice = _currentPrice.add(_currentIncrement); 392 | 393 | if (_nextPrice / 100 finney != _currentPrice / 100 finney) { 394 | _currentIncrement >>= 1; 395 | } 396 | 397 | _currentPrice = _nextPrice; 398 | } 399 | } 400 | 401 | /** 402 | * @notice Adopt some Axies from the same class. 403 | * @dev The quantity MUST be positive. 404 | * @param _adopter Address of the adopter. 405 | * @param _class The class of adopted Axies. 406 | * @param _quantity Number of Axies to be adopted. 407 | * @param _referrer Address of the referrer. 408 | */ 409 | function _adoptSameClassAxies( 410 | address _adopter, 411 | uint8 _class, 412 | uint256 _quantity, 413 | address _referrer 414 | ) 415 | private 416 | returns (uint256 _totalPrice) 417 | { 418 | (_totalPrice, priceIncrement[_class], currentPrice[_class]) = _sameClassAxiesPrice(_class, _quantity); 419 | 420 | _numAdoptedAxies[_adopter][_class] = _numAdoptedAxies[_adopter][_class].add(_quantity); 421 | _totalAdoptedAxies[_class] = _totalAdoptedAxies[_class].add(_quantity); 422 | 423 | AxiesAdopted( 424 | _adopter, 425 | _class, 426 | _quantity, 427 | _referrer 428 | ); 429 | } 430 | 431 | function _applyRefCredits(address _receiver, uint256 _numAppliedCredits) private { 432 | _numRefCredits[_receiver] = _numRefCredits[_receiver].add(_numAppliedCredits); 433 | 434 | uint256 _numCredits = this.numRefCredits(_receiver, true); 435 | uint256 _numRewards = _numCredits / REF_CREDITS_PER_AXIE; 436 | 437 | if (_numRewards > 0) { 438 | _numDeductedRefCredits[_receiver] = _numDeductedRefCredits[_receiver] 439 | .add(_numRewards.mul(REF_CREDITS_PER_AXIE)); 440 | 441 | _numRewardedAxies[_receiver] = _numRewardedAxies[_receiver].add(_numRewards); 442 | _totalRewardedAxies = _totalRewardedAxies.add(_numRewards); 443 | 444 | AxiesRewarded(_receiver, _numRewards); 445 | } 446 | } 447 | 448 | /** 449 | * @notice Redeem adopted Axies from the same class. 450 | * @dev Emit the `AdoptedAxiesRedeemed` event if the quantity is positive. 451 | * @param _receiver The address of the receiver. 452 | * @param _class The class of adopted Axies. 453 | * @param _quantity The number of adopted Axies to be redeemed. 454 | */ 455 | function _redeemSameClassAdoptedAxies( 456 | address _receiver, 457 | uint8 _class, 458 | uint256 _quantity 459 | ) 460 | private 461 | returns (uint256 _remainingQuantity) 462 | { 463 | _remainingQuantity = this.numAdoptedAxies(_receiver, _class, true).sub(_quantity); 464 | 465 | if (_quantity > 0) { 466 | _numDeductedAdoptedAxies[_receiver][_class] = _numDeductedAdoptedAxies[_receiver][_class].add(_quantity); 467 | _totalDeductedAdoptedAxies[_class] = _totalDeductedAdoptedAxies[_class].add(_quantity); 468 | 469 | AdoptedAxiesRedeemed(_receiver, _class, _quantity); 470 | } 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /contracts/presale/AxieRedemption.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "oraclize-api/contracts/usingOraclize.sol"; 5 | 6 | import "./AxieRedemptionInternal.sol"; 7 | 8 | 9 | contract AxieRedemption is AxieRedemptionInternal, usingOraclize { 10 | function _startTimestamp() internal returns (uint256) { 11 | // Wednesday, March 14, 2018 4:00:00 AM. 12 | return 1521000000; 13 | } 14 | 15 | function _sendRandomQuery(uint256 _gasLimit) internal returns (uint256) { 16 | return uint256(oraclize_query("WolframAlpha", "random number between 1 and 2^64", _gasLimit)); 17 | } 18 | 19 | // solium-disable-next-line function-order, mixedcase 20 | function __callback(bytes32 _queryId, string _result) public { 21 | require(msg.sender == oraclize_cbAddress()); 22 | _receiveRandomQuery(uint256(_queryId), _result); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/presale/AxieRedemptionDev.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "oraclize-api/contracts/usingOraclize.sol"; 5 | 6 | import "./AxieRedemptionInternal.sol"; 7 | 8 | 9 | contract AxieRedemptionDev is AxieRedemptionInternal, usingOraclize { 10 | function _startTimestamp() internal returns (uint256) { 11 | return 0; 12 | } 13 | 14 | function _sendRandomQuery(uint256 _gasLimit) internal returns (uint256) { 15 | return uint256(oraclize_query("WolframAlpha", "random number between 1 and 2^64", _gasLimit)); 16 | } 17 | 18 | // solium-disable-next-line function-order, mixedcase 19 | function __callback(bytes32 _queryId, string _result) public { 20 | require(msg.sender == oraclize_cbAddress()); 21 | _receiveRandomQuery(uint256(_queryId), _result); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/presale/AxieRedemptionInternal.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "zeppelin/contracts/lifecycle/Pausable.sol"; 5 | import "zeppelin/contracts/math/SafeMath.sol"; 6 | import "zeppelin/contracts/ownership/HasNoContracts.sol"; 7 | 8 | import "../core/AxieCore.sol"; 9 | import "./AxiePresaleExtended.sol"; 10 | 11 | 12 | contract AxieRedemptionInternal is HasNoContracts, Pausable { 13 | using SafeMath for uint256; 14 | 15 | uint8 constant public CLASS_BEAST = 0; 16 | uint8 constant public CLASS_BUG = 1; 17 | uint8 constant public CLASS_BIRD = 2; 18 | uint8 constant public CLASS_PLANT = 3; 19 | uint8 constant public CLASS_AQUATIC = 4; 20 | uint8 constant public CLASS_REPTILE = 5; 21 | 22 | struct Redemption { 23 | address receiver; 24 | uint256 clazz; 25 | uint256 redeemedAt; 26 | } 27 | 28 | AxieCore public coreContract; 29 | AxiePresaleExtended public presaleContract; 30 | 31 | mapping (uint256 => Redemption) public redemptionByQueryId; 32 | mapping (address => mapping (uint256 => uint256[])) public ownedRedemptions; 33 | mapping (uint256 => uint256) public ownedRedemptionIndex; 34 | 35 | event RedemptionStarted(uint256 indexed _queryId); 36 | event RedemptionRetried(uint256 indexed _queryId, uint256 indexed _oldQueryId); 37 | event RedemptionFinished(uint256 indexed _queryId); 38 | 39 | function () external payable { 40 | require(msg.sender == owner); 41 | } 42 | 43 | modifier requireDependencyContracts { 44 | require(coreContract != address(0) && presaleContract != address(0)); 45 | _; 46 | } 47 | 48 | modifier whenStarted { 49 | require(now >= _startTimestamp()); 50 | _; 51 | } 52 | 53 | function reclaimEther(uint256 remaining) external onlyOwner { 54 | if (this.balance > remaining) { 55 | owner.transfer(this.balance - remaining); 56 | } 57 | } 58 | 59 | function setCoreContract(address _coreAddress) external onlyOwner { 60 | coreContract = AxieCore(_coreAddress); 61 | } 62 | 63 | function setPresaleContract(address _presaleAddress) external onlyOwner { 64 | presaleContract = AxiePresaleExtended(_presaleAddress); 65 | } 66 | 67 | function numBeingRedeemedAxies(address _receiver, uint256 _class) external view returns (uint256) { 68 | return ownedRedemptions[_receiver][_class].length; 69 | } 70 | 71 | function redeemAdoptedAxies( 72 | uint256 _oldClass 73 | ) 74 | external 75 | requireDependencyContracts 76 | whenStarted 77 | whenNotPaused 78 | { 79 | _redeemAdoptedAxies(msg.sender, _oldClass); 80 | } 81 | 82 | function redeemPlayersAdoptedAxies( 83 | address _receiver, 84 | uint256 _oldClass 85 | ) 86 | external 87 | requireDependencyContracts 88 | onlyOwner 89 | whenStarted 90 | { 91 | _redeemAdoptedAxies(_receiver, _oldClass); 92 | } 93 | 94 | function redeemRewardedAxies() 95 | external 96 | requireDependencyContracts 97 | whenStarted 98 | whenNotPaused 99 | { 100 | _redeemRewardedAxies(msg.sender); 101 | } 102 | 103 | function redeemPlayersRewardedAxies( 104 | address _receiver 105 | ) 106 | external 107 | requireDependencyContracts 108 | onlyOwner 109 | whenStarted 110 | { 111 | _redeemRewardedAxies(_receiver); 112 | } 113 | 114 | function retryRedemption( 115 | uint256 _oldQueryId, 116 | uint256 _gasLimit 117 | ) 118 | external 119 | requireDependencyContracts 120 | onlyOwner 121 | whenStarted 122 | { 123 | Redemption memory _redemption = redemptionByQueryId[_oldQueryId]; 124 | require(_redemption.receiver != address(0)); 125 | 126 | uint256 _redemptionIndex = ownedRedemptionIndex[_oldQueryId]; 127 | uint256 _queryId = _sendRandomQuery(_gasLimit); 128 | 129 | redemptionByQueryId[_queryId] = _redemption; 130 | delete redemptionByQueryId[_oldQueryId]; 131 | 132 | ownedRedemptions[_redemption.receiver][_redemption.clazz][_redemptionIndex] = _queryId; 133 | ownedRedemptionIndex[_queryId] = _redemptionIndex; 134 | delete ownedRedemptionIndex[_oldQueryId]; 135 | 136 | RedemptionRetried(_queryId, _oldQueryId); 137 | } 138 | 139 | function _startTimestamp() internal returns (uint256); 140 | 141 | function _sendRandomQuery(uint256 _gasLimit) internal returns (uint256); 142 | 143 | function _receiveRandomQuery( 144 | uint256 _queryId, 145 | string _result 146 | ) 147 | internal 148 | whenStarted 149 | whenNotPaused 150 | { 151 | Redemption memory _redemption = redemptionByQueryId[_queryId]; 152 | require(_redemption.receiver != address(0)); 153 | 154 | uint256 _redemptionIndex = ownedRedemptionIndex[_queryId]; 155 | uint256 _lastRedemptionIndex = ownedRedemptions[_redemption.receiver][_redemption.clazz].length.sub(1); 156 | uint256 _lastRedemptionQueryId = ownedRedemptions[_redemption.receiver][_redemption.clazz][_lastRedemptionIndex]; 157 | 158 | uint256 _seed = uint256(keccak256(_result)); 159 | uint256 _genes; 160 | 161 | if (_redemption.clazz != uint256(-1)) { 162 | _genes = _generateGenes(_redemption.clazz, _seed); 163 | } else { 164 | _genes = _generateGenes(_seed); 165 | } 166 | 167 | ownedRedemptions[_redemption.receiver][_redemption.clazz][_redemptionIndex] = _lastRedemptionQueryId; 168 | ownedRedemptionIndex[_lastRedemptionQueryId] = _redemptionIndex; 169 | 170 | delete ownedRedemptions[_redemption.receiver][_redemption.clazz][_lastRedemptionIndex]; 171 | ownedRedemptions[_redemption.receiver][_redemption.clazz].length--; 172 | 173 | delete redemptionByQueryId[_queryId]; 174 | delete ownedRedemptionIndex[_queryId]; 175 | 176 | // Spawn an Axie with the generated `genes`. 177 | coreContract.spawnAxie(_genes, _redemption.receiver); 178 | 179 | RedemptionFinished(_queryId); 180 | } 181 | 182 | function _randomNumber( 183 | uint256 _upper, 184 | uint256 _initialSeed 185 | ) 186 | private 187 | view 188 | returns (uint256 _number, uint256 _seed) 189 | { 190 | _seed = uint256( 191 | keccak256( 192 | _initialSeed, 193 | block.blockhash(block.number - 1), 194 | block.coinbase, 195 | block.difficulty 196 | ) 197 | ); 198 | 199 | _number = _seed % _upper; 200 | } 201 | 202 | function _randomClass(uint256 _initialSeed) private view returns (uint256 _class, uint256 _seed) { 203 | // solium-disable-next-line comma-whitespace 204 | (_class, _seed) = _randomNumber(6 /* CLASS_REPTILE + 1 */, _initialSeed); 205 | } 206 | 207 | function _generateGenes(uint256 _class, uint256 _initialSeed) private view returns (uint256) { 208 | // Gene generation logic is removed. 209 | return 0; 210 | } 211 | 212 | function _generateGenes(uint256 _initialSeed) private view returns (uint256) { 213 | uint256 _class; 214 | uint256 _seed; 215 | (_class, _seed) = _randomClass(_initialSeed); 216 | return _generateGenes(_class, _seed); 217 | } 218 | 219 | function _redeemAxies(address _receiver, uint256 _class) private { 220 | uint256 _queryId = _sendRandomQuery(300000); 221 | 222 | Redemption memory _redemption = Redemption( 223 | _receiver, 224 | _class, 225 | now 226 | ); 227 | 228 | redemptionByQueryId[_queryId] = _redemption; 229 | 230 | uint256 _length = ownedRedemptions[_receiver][_class].length; 231 | ownedRedemptions[_receiver][_class].push(_queryId); 232 | ownedRedemptionIndex[_queryId] = _length; 233 | 234 | RedemptionStarted(_queryId); 235 | } 236 | 237 | function _redeemAdoptedAxies(address _receiver, uint256 _oldClass) private { 238 | uint256 _class; 239 | 240 | if (_oldClass == 0) { // Old Beast 241 | presaleContract.redeemAdoptedAxies(_receiver, 1, 0, 0); 242 | _class = CLASS_BEAST; 243 | } else if (_oldClass == 2) { // Old Aquatic 244 | presaleContract.redeemAdoptedAxies(_receiver, 0, 1, 0); 245 | _class = CLASS_AQUATIC; 246 | } else if (_oldClass == 4) { // Old Plant 247 | presaleContract.redeemAdoptedAxies(_receiver, 0, 0, 1); 248 | _class = CLASS_PLANT; 249 | } else { 250 | revert(); 251 | } 252 | 253 | _redeemAxies(_receiver, _class); 254 | } 255 | 256 | function _redeemRewardedAxies(address _receiver) private { 257 | presaleContract.redeemRewardedAxies(_receiver, 1); 258 | _redeemAxies(_receiver, uint256(-1)); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /contracts/presale/AxieRedemptionRinkeby.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | 4 | import "oraclize-api/contracts/usingOraclize.sol"; 5 | 6 | import "./AxieRedemptionInternal.sol"; 7 | 8 | 9 | contract AxieRedemptionRinkeby is AxieRedemptionInternal, usingOraclize { 10 | function _startTimestamp() internal returns (uint256) { 11 | return 0; 12 | } 13 | 14 | function _sendRandomQuery(uint256 _gasLimit) internal returns (uint256) { 15 | return uint256(oraclize_query("WolframAlpha", "random number between 1 and 2^64", _gasLimit)); 16 | } 17 | 18 | // solium-disable-next-line function-order, mixedcase 19 | function __callback(bytes32 _queryId, string _result) public { 20 | require(msg.sender == oraclize_cbAddress()); 21 | _receiveRandomQuery(uint256(_queryId), _result); 22 | } 23 | } 24 | --------------------------------------------------------------------------------