├── 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 |
--------------------------------------------------------------------------------