-
Notifications
You must be signed in to change notification settings - Fork 0
/
EwaAuction.sol
375 lines (306 loc) · 13.2 KB
/
EwaAuction.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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
pragma solidity ^0.4.19;
import "./EwaToken";
/// @title Dutch auction contract - distribution of a fixed number of tokens using an auction.
/// The auction ends if a fixed number of tokens was sold.
contract DutchAuction {
/*
* Auction for the EWA Token.
*
* Terminology:
* 1 token unit = Wei
* 1 token = EWA = Wei * token_multiplier
* token_multiplier set from token's number of decimals (i.e. 10 ** decimals)
*/
// Wait 14 days after the end of the auction, before anyone can claim tokens
uint constant public token_claim_waiting_period = 14 days;
// Bid value over which the address has to be whitelisted
// At deployment moment, less than 1k$
uint constant public bid_threshold = 0.5 ether;
/*
* Storage
*/
EwaToken public token;
address public owner_address;
address public wallet_address;
address public whitelister_address;
// Price decay function parameters to be changed depending on the desired outcome
// Starting price in WEI; e.g. 2 * 10 ** 18
uint public price_start;
// Divisor constant; e.g. 524880000
uint public price_constant;
// Divisor exponent; e.g. 3
uint32 public price_exponent;
// For calculating elapsed time for price
uint public start_time;
uint public end_time;
uint public start_block;
// Keep track of all ETH received in the bids
uint public received_wei;
// Keep track of cumulative ETH funds for which the tokens have been claimed
uint public funds_claimed;
uint public token_multiplier;
// Total number of Rei (EWA * token_multiplier) that will be auctioned
uint public num_tokens_auctioned;
// Wei per EWA (Rei * token_multiplier)
uint public final_price;
// Bidder address => bid value
mapping (address => uint) public bids;
// Whitelist for addresses that want to bid more than bid_threshold
mapping (address => bool) public whitelist;
Stages public stage;
/*
* Enums
*/
enum Stages {
AuctionDeployed,
AuctionSetUp,
AuctionStarted,
AuctionEnded,
TokensDistributed
}
/*
* Modifiers
*/
modifier atStage(Stages _stage) {
require(stage == _stage);
_;
}
modifier isOwner() {
require(msg.sender == owner_address);
_;
}
modifier isWhitelister() {
require(msg.sender == whitelister_address);
_;
}
/*
* Events
*/
event Deployed(
uint indexed _price_start,
uint indexed _price_constant,
uint32 indexed _price_exponent
);
event Setup();
event AuctionStarted(uint indexed _start_time, uint indexed _block_number);
event BidSubmission(address indexed _sender, uint _amount, uint _missing_fund);
event ClaimedTokens(address indexed _recipient, uint _sent_amount);
event AuctionEnded(uint _final_price);
event TokensDistributed();
/*
* Public functions
*/
/// @dev Contract constructor function sets the starting price, divisor constant and
/// divisor exponent for calculating the Dutch Auction price.
/// @param _wallet_address Wallet address to which all contributed ETH will be forwarded.
/// @param _price_start High price in WEI at which the auction starts.
/// @param _price_constant Auction price divisor constant.
/// @param _price_exponent Auction price divisor exponent.
function DutchAuction(
address _wallet_address,
address _whitelister_address,
uint _price_start,
uint _price_constant,
uint32 _price_exponent)
public
{
require(_wallet_address != 0x0);
require(_whitelister_address != 0x0);
wallet_address = _wallet_address;
whitelister_address = _whitelister_address;
owner_address = msg.sender;
stage = Stages.AuctionDeployed;
changeSettings(_price_start, _price_constant, _price_exponent);
emit Deployed(_price_start, _price_constant, _price_exponent);
}
/// @dev Fallback function for the contract, which calls bid() if the auction has started.
function () public payable atStage(Stages.AuctionStarted) {
bid();
}
/// @notice Set `_token_address` as the token address to be used in the auction.
/// @dev Setup function sets external contracts addresses.
/// @param _token_address Token address.
function setup(address _token_address) public isOwner atStage(Stages.AuctionDeployed) {
require(_token_address != 0x0);
token = EwaToken(_token_address);
// Get number of Rei (EWA * token_multiplier) to be auctioned from token auction balance
num_tokens_auctioned = token.balanceOf(address(this));
// Set the number of the token multiplier for its decimals
token_multiplier = 10 ** uint(18);
stage = Stages.AuctionSetUp;
emit Setup();
}
/// @notice Set `_price_start`, `_price_constant` and `_price_exponent` as
/// the new starting price, price divisor constant and price divisor exponent.
/// @dev Changes auction price function parameters before auction is started.
/// @param _price_start Updated start price.
/// @param _price_constant Updated price divisor constant.
/// @param _price_exponent Updated price divisor exponent.
function changeSettings(
uint _price_start,
uint _price_constant,
uint32 _price_exponent)
internal
{
require(stage == Stages.AuctionDeployed || stage == Stages.AuctionSetUp);
require(_price_start > 0);
require(_price_constant > 0);
price_start = _price_start;
price_constant = _price_constant;
price_exponent = _price_exponent;
}
/// @notice Adds account addresses to whitelist.
/// @dev Adds account addresses to whitelist.
/// @param _bidder_addresses Array of addresses.
function addToWhitelist(address[] _bidder_addresses) public isWhitelister {
for (uint32 i = 0; i < _bidder_addresses.length; i++) {
whitelist[_bidder_addresses[i]] = true;
}
}
/// @notice Removes account addresses from whitelist.
/// @dev Removes account addresses from whitelist.
/// @param _bidder_addresses Array of addresses.
function removeFromWhitelist(address[] _bidder_addresses) public isWhitelister {
for (uint32 i = 0; i < _bidder_addresses.length; i++) {
whitelist[_bidder_addresses[i]] = false;
}
}
/// @notice Start the auction.
/// @dev Starts auction and sets start_time.
function startAuction() public isOwner atStage(Stages.AuctionSetUp) {
stage = Stages.AuctionStarted;
start_time = block;
start_block = block.number;
emit AuctionStarted(start_time, start_block);
}
/// @notice Finalize the auction - sets the final EWA token price and changes the auction
/// stage after no bids are allowed anymore.
/// @dev Finalize auction and set the final EWA token price.
function finalizeAuction() public atStage(Stages.AuctionStarted)
{
// Missing funds should be 0 at this point
uint missing_funds = missingFundsToEndAuction();
require(missing_funds == 0);
// Calculate the final price = WEI / EWA = WEI / (Rei / token_multiplier)
// Reminder: num_tokens_auctioned is the number of Rei (EWA * token_multiplier) that are auctioned
final_price = token_multiplier * received_wei / num_tokens_auctioned;
end_time = block;
stage = Stages.AuctionEnded;
emit AuctionEnded(final_price);
assert(final_price > 0);
}
/// --------------------------------- Auction Functions ------------------
/// @notice Send `msg.value` WEI to the auction from the `msg.sender` account.
/// @dev Allows to send a bid to the auction.
function bid()
public
payable
atStage(Stages.AuctionStarted)
{
require(msg.value > 0);
require(bids[msg.sender] + msg.value <= bid_threshold || whitelist[msg.sender]);
assert(bids[msg.sender] + msg.value >= msg.value);
// Missing funds without the current bid value
uint missing_funds = missingFundsToEndAuction();
// We require bid values to be less than the funds missing to end the auction
// at the current price.
require(msg.value <= missing_funds);
bids[msg.sender] += msg.value;
received_wei += msg.value;
// Send bid amount to wallet
wallet_address.transfer(msg.value);
emit BidSubmission(msg.sender, msg.value, missing_funds);
assert(received_wei >= msg.value);
}
/// @notice Claim auction tokens for `msg.sender` after the auction has ended.
/// @dev Claims tokens for `msg.sender` after auction. To be used if tokens can
/// be claimed by beneficiaries, individually.
function claimTokens() public atStage(Stages.AuctionEnded) returns (bool) {
return proxyClaimTokens(msg.sender);
}
/// @notice Claim auction tokens for `receiver_address` after the auction has ended.
/// @dev Claims tokens for `receiver_address` after auction has ended.
/// @param receiver_address Tokens will be assigned to this address if eligible.
function proxyClaimTokens(address receiver_address)
public
atStage(Stages.AuctionEnded)
returns (bool)
{
// Waiting period after the end of the auction, before anyone can claim tokens
// Ensures enough time to check if auction was finalized correctly
// before users start transacting tokens
require(block > end_time + token_claim_waiting_period);
require(receiver_address != 0x0);
if (bids[receiver_address] == 0) {
return false;
}
// Number of Rei = bid_wei / Rei = bid_wei / (wei_per_EWA * token_multiplier)
uint num = (token_multiplier * bids[receiver_address]) / final_price;
// Due to final_price floor rounding, the number of assigned tokens may be higher
// than expected. Therefore, the number of remaining unassigned auction tokens
// may be smaller than the number of tokens needed for the last claimTokens call
uint auction_tokens_balance = token.balanceOf(address(this));
if (num > auction_tokens_balance) {
num = auction_tokens_balance;
}
// Update the total amount of funds for which tokens have been claimed
funds_claimed += bids[receiver_address];
// Set receiver bid to 0 before assigning tokens
bids[receiver_address] = 0;
require(token.transfer(receiver_address, num));
emit ClaimedTokens(receiver_address, num);
// After the last tokens are claimed, we change the auction stage
// Due to the above logic, rounding errors will not be an issue
if (funds_claimed == received_wei) {
stage = Stages.TokensDistributed;
emit TokensDistributed();
}
assert(token.balanceOf(receiver_address) >= num);
assert(bids[receiver_address] == 0);
return true;
}
/// @notice Get the EWA price in WEI during the auction, at the time of
/// calling this function. Returns `0` if auction has ended.
/// Returns `price_start` before auction has started.
/// @dev Calculates the current EWA token price in WEI.
/// @return Returns WEI per EWA (token_multiplier * Rei).
function price() public view returns (uint) {
if (stage == Stages.AuctionEnded ||
stage == Stages.TokensDistributed) {
return 0;
}
return calcTokenPrice();
}
/// @notice Get the missing funds needed to end the auction,
/// calculated at the current EWA price in WEI.
/// @dev The missing funds amount necessary to end the auction at the current EWA price in WEI.
/// @return Returns the missing funds amount in WEI.
function missingFundsToEndAuction() view public returns (uint) {
// num_tokens_auctioned = total number of Rei (EWA * token_multiplier) that is auctioned
uint required_wei_at_price = num_tokens_auctioned * price() / token_multiplier;
if (required_wei_at_price <= received_wei) {
return 0;
}
// assert(required_wei_at_price - received_wei > 0);
return required_wei_at_price - received_wei;
}
/*
* Private functions
*/
/// @dev Calculates the token price (WEI / EWA) at the current timestamp
/// during the auction; elapsed time = 0 before auction starts.
/// Based on the provided parameters, the price does not change in the first
/// `price_constant^(1/price_exponent)` seconds due to rounding.
/// Rounding in `decay_rate` also produces values that increase instead of decrease
/// in the beginning; these spikes decrease over time and are noticeable
/// only in first hours. This should be calculated before usage.
/// @return Returns the token price - Wei per EWA.
function calcTokenPrice() view private returns (uint) {
uint elapsed;
if (stage == Stages.AuctionStarted) {
elapsed = block - start_time;
}
uint decay_rate = elapsed ** price_exponent / price_constant;
return price_start * (1 + elapsed) / (1 + elapsed + decay_rate);
}
}