-
Notifications
You must be signed in to change notification settings - Fork 89
/
AxiePresale.sol
282 lines (233 loc) · 7.28 KB
/
AxiePresale.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
pragma solidity ^0.4.19;
import "zeppelin/contracts/lifecycle/Pausable.sol";
import "zeppelin/contracts/math/SafeMath.sol";
import "zeppelin/contracts/ownership/HasNoEther.sol";
contract AxiePresale is HasNoEther, Pausable {
using SafeMath for uint256;
// No Axies can be adopted after this end date: Friday, March 16, 2018 11:59:59 PM GMT.
uint256 constant public PRESALE_END_TIMESTAMP = 1521244799;
uint8 constant public CLASS_BEAST = 0;
uint8 constant public CLASS_AQUATIC = 2;
uint8 constant public CLASS_PLANT = 4;
uint256 constant public INITIAL_PRICE_INCREMENT = 1600 szabo; // 0.0016 Ether
uint256 constant public INITIAL_PRICE = INITIAL_PRICE_INCREMENT;
uint256 constant public REF_CREDITS_PER_AXIE = 5;
mapping (uint8 => uint256) public currentPrices;
mapping (uint8 => uint256) public priceIncrements;
mapping (uint8 => uint256) public totalAxiesAdopted;
mapping (address => mapping (uint8 => uint256)) public axiesAdopted;
mapping (address => uint256) public referralCredits;
mapping (address => uint256) public axiesRewarded;
uint256 public totalAxiesRewarded;
event AxiesAdopted(
address indexed adopter,
uint8 indexed clazz,
uint256 quantity,
address indexed referrer
);
event AxiesRewarded(address indexed receiver, uint256 quantity);
event AdoptedAxiesRedeemed(address indexed receiver, uint8 indexed clazz, uint256 quantity);
event RewardedAxiesRedeemed(address indexed receiver, uint256 quantity);
function AxiePresale() public {
priceIncrements[CLASS_BEAST] = priceIncrements[CLASS_AQUATIC] = //
priceIncrements[CLASS_PLANT] = INITIAL_PRICE_INCREMENT;
currentPrices[CLASS_BEAST] = currentPrices[CLASS_AQUATIC] = //
currentPrices[CLASS_PLANT] = INITIAL_PRICE;
}
function axiesPrice(
uint256 beastQuantity,
uint256 aquaticQuantity,
uint256 plantQuantity
)
public
view
returns (uint256 totalPrice)
{
uint256 price;
(price,,) = _axiesPrice(CLASS_BEAST, beastQuantity);
totalPrice = totalPrice.add(price);
(price,,) = _axiesPrice(CLASS_AQUATIC, aquaticQuantity);
totalPrice = totalPrice.add(price);
(price,,) = _axiesPrice(CLASS_PLANT, plantQuantity);
totalPrice = totalPrice.add(price);
}
function adoptAxies(
uint256 beastQuantity,
uint256 aquaticQuantity,
uint256 plantQuantity,
address referrer
)
public
payable
whenNotPaused
{
require(now <= PRESALE_END_TIMESTAMP);
require(beastQuantity <= 3);
require(aquaticQuantity <= 3);
require(plantQuantity <= 3);
address adopter = msg.sender;
address actualReferrer = 0x0;
// An adopter cannot be his/her own referrer.
if (referrer != adopter) {
actualReferrer = referrer;
}
uint256 value = msg.value;
uint256 price;
if (beastQuantity > 0) {
price = _adoptAxies(
adopter,
CLASS_BEAST,
beastQuantity,
actualReferrer
);
require(value >= price);
value -= price;
}
if (aquaticQuantity > 0) {
price = _adoptAxies(
adopter,
CLASS_AQUATIC,
aquaticQuantity,
actualReferrer
);
require(value >= price);
value -= price;
}
if (plantQuantity > 0) {
price = _adoptAxies(
adopter,
CLASS_PLANT,
plantQuantity,
actualReferrer
);
require(value >= price);
value -= price;
}
msg.sender.transfer(value);
// The current referral is ignored if the referrer's address is 0x0.
if (actualReferrer != 0x0) {
uint256 numCredit = referralCredits[actualReferrer]
.add(beastQuantity)
.add(aquaticQuantity)
.add(plantQuantity);
uint256 numReward = numCredit / REF_CREDITS_PER_AXIE;
if (numReward > 0) {
referralCredits[actualReferrer] = numCredit % REF_CREDITS_PER_AXIE;
axiesRewarded[actualReferrer] = axiesRewarded[actualReferrer].add(numReward);
totalAxiesRewarded = totalAxiesRewarded.add(numReward);
AxiesRewarded(actualReferrer, numReward);
} else {
referralCredits[actualReferrer] = numCredit;
}
}
}
function redeemAdoptedAxies(
address receiver,
uint256 beastQuantity,
uint256 aquaticQuantity,
uint256 plantQuantity
)
public
onlyOwner
returns (
uint256 /* remainingBeastQuantity */,
uint256 /* remainingAquaticQuantity */,
uint256 /* remainingPlantQuantity */
)
{
return (
_redeemAdoptedAxies(receiver, CLASS_BEAST, beastQuantity),
_redeemAdoptedAxies(receiver, CLASS_AQUATIC, aquaticQuantity),
_redeemAdoptedAxies(receiver, CLASS_PLANT, plantQuantity)
);
}
function redeemRewardedAxies(
address receiver,
uint256 quantity
)
public
onlyOwner
returns (uint256 remainingQuantity)
{
remainingQuantity = axiesRewarded[receiver] = axiesRewarded[receiver].sub(quantity);
if (quantity > 0) {
// This requires that rewarded Axies are always included in the total
// to make sure overflow won't happen.
totalAxiesRewarded -= quantity;
RewardedAxiesRedeemed(receiver, quantity);
}
}
/**
* @dev Calculate price of Axies from the same class.
* @param clazz The class of Axies.
* @param quantity Number of Axies to be calculated.
*/
function _axiesPrice(
uint8 clazz,
uint256 quantity
)
private
view
returns (uint256 totalPrice, uint256 priceIncrement, uint256 currentPrice)
{
priceIncrement = priceIncrements[clazz];
currentPrice = currentPrices[clazz];
uint256 nextPrice;
for (uint256 i = 0; i < quantity; i++) {
totalPrice = totalPrice.add(currentPrice);
nextPrice = currentPrice.add(priceIncrement);
if (nextPrice / 100 finney != currentPrice / 100 finney) {
priceIncrement >>= 1;
}
currentPrice = nextPrice;
}
}
/**
* @dev Adopt some Axies from the same class.
* @param adopter Address of the adopter.
* @param clazz The class of adopted Axies.
* @param quantity Number of Axies to be adopted, this should be positive.
* @param referrer Address of the referrer.
*/
function _adoptAxies(
address adopter,
uint8 clazz,
uint256 quantity,
address referrer
)
private
returns (uint256 totalPrice)
{
(totalPrice, priceIncrements[clazz], currentPrices[clazz]) = _axiesPrice(clazz, quantity);
axiesAdopted[adopter][clazz] = axiesAdopted[adopter][clazz].add(quantity);
totalAxiesAdopted[clazz] = totalAxiesAdopted[clazz].add(quantity);
AxiesAdopted(
adopter,
clazz,
quantity,
referrer
);
}
/**
* @dev Redeem adopted Axies from the same class.
* @param receiver Address of the receiver.
* @param clazz The class of adopted Axies.
* @param quantity Number of adopted Axies to be redeemed.
*/
function _redeemAdoptedAxies(
address receiver,
uint8 clazz,
uint256 quantity
)
private
returns (uint256 remainingQuantity)
{
remainingQuantity = axiesAdopted[receiver][clazz] = axiesAdopted[receiver][clazz].sub(quantity);
if (quantity > 0) {
// This requires that adopted Axies are always included in the total
// to make sure overflow won't happen.
totalAxiesAdopted[clazz] -= quantity;
AdoptedAxiesRedeemed(receiver, clazz, quantity);
}
}
}