aboutsummaryrefslogtreecommitdiffstats
path: root/test/compilationTests/corion/ico.sol
blob: 804443958c351393f0fead28070099a96dc00428 (plain) (blame)
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
376
pragma solidity ^0.4.11;

import "./safeMath.sol";
import "./token.sol";
import "./premium.sol";
import "./moduleHandler.sol";

contract ico is safeMath {
    
    struct icoLevels_s {
        uint256 block;
        uint8 rate;
    }
    struct affiliate_s {
        uint256 weight;
        uint256 paid;
    }
    struct interest_s {
        uint256 amount;
        bool empty;
    }
    struct brought_s {
        uint256 eth;
        uint256 cor;
        uint256 corp;
    }
    
    uint256 constant oneSegment = 40320;
    
    address public owner;
    address public tokenAddr;
    address public premiumAddr;
    uint256 public startBlock;
    uint256 public icoDelay;
    address public foundationAddress;
    address public icoEtcPriceAddr;
    uint256 public icoExchangeRate;
    uint256 public icoExchangeRateSetBlock;
    uint256 constant icoExchangeRateM = 1e4;
    uint256 constant interestOnICO   = 25;
    uint256 constant interestOnICOM  = 1e3;
    uint256 constant interestBlockDelay = 720;
    uint256 constant exchangeRateDelay = 125;
    bool public aborted;
    bool public closed;
    icoLevels_s[] public icoLevels;
    mapping (address => affiliate_s) public affiliate;
    mapping (address => brought_s) public brought;
    mapping (address => mapping(uint256 => interest_s)) public interestDB;
    uint256 public totalMint;
    uint256 public totalPremiumMint;

    constructor(address foundation, address priceSet, uint256 exchangeRate, uint256 startBlockNum, address[] genesisAddr, uint256[] genesisValue) public {
        /*
            Installation function.
            
            @foundation     The ETC address of the foundation
            @priceSet       The address which will be able to make changes on the rate later on.
            @exchangeRate   The current ETC/USD rate multiplied by 1e4. For example: 2.5 USD/ETC = 25000
            @startBlockNum  The height (level) of the beginning of the ICO. If it is 0 then it will be the current array’s height.
            @genesisAddr    Array of Genesis addresses
            @genesisValue   Array of balance of genesis addresses
        */
        foundationAddress = foundation;
        icoExchangeRate = exchangeRate;
        icoExchangeRateSetBlock = block.number + exchangeRateDelay;
        icoEtcPriceAddr = priceSet;
        owner = msg.sender;
        if ( startBlockNum > 0 ) {
            require( startBlockNum >= block.number );
            startBlock = startBlockNum;
        } else {
            startBlock = block.number;
        }
        icoLevels.push(icoLevels_s(startBlock + oneSegment * 1, 3));
        icoLevels.push(icoLevels_s(startBlock + oneSegment / 7, 5));
        icoLevels.push(icoLevels_s(startBlock, 10));
        icoDelay = startBlock + oneSegment * 3;
        for ( uint256 a=0 ; a<genesisAddr.length ; a++ ) {
            interestDB[genesisAddr[a]][0].amount = genesisValue[a];
        }
    }
    
    function ICObonus() public view returns(uint256 bonus) {
        /*
            Query of current bonus
            
            @bonus  Bonus %
        */
        for ( uint8 a=0 ; a<icoLevels.length ; a++ ) {
            if ( block.number > icoLevels[a].block ) {
                return icoLevels[a].rate;
            }
        }
    }
    
    function setInterestDB(address addr, uint256 balance) external returns(bool success) {
        /*
            Setting interest database. It can be requested by Token contract only.
            A database has to be built in order  that after ICO closed everybody can get their compound interest on their capital accumulated 
            
            @addr       Sender
            @balance    Quantity
            
            @success    Was the process successful or not
        */
        require( msg.sender == tokenAddr );
        uint256 _num = (block.number - startBlock) / interestBlockDelay;
        interestDB[addr][_num].amount = balance;
        if ( balance == 0 ) { 
            interestDB[addr][_num].empty = true;
        }
        return true;
    }
    
    function checkInterest(address addr) public view returns(uint256 amount) {
        /*
            Query of compound interest
            
            @addr       Address
            
            @amount     Amount of compound interest
        */
        uint256 _lastBal;
        uint256 _tamount;
        bool _empty;
        interest_s memory _idb;
        uint256 _to = (block.number - startBlock) / interestBlockDelay;
        
        if ( _to == 0 || aborted ) { return 0; }
        
        for ( uint256 r=0 ; r < _to ; r++ ) {
            if ( r*interestBlockDelay+startBlock >= icoDelay ) { break; }
            _idb = interestDB[addr][r];
            if ( _idb.amount > 0 ) {
                if ( _empty ) {
                    _lastBal = _idb.amount + amount;
                } else {
                    _lastBal = _idb.amount;
                }
            }
            if ( _idb.empty ) {
                _lastBal = 0;
                _empty = _idb.empty;
            }
            _lastBal += _tamount;
            _tamount = _lastBal * interestOnICO / interestOnICOM / 100;
            amount += _tamount;
        }
    }
    
    function getInterest(address beneficiary) external {
        /*
            Request of  compound interest. This is deleted  from the database after the ICO closed and following the query of the compound interest.
            
            @beneficiary    Beneficiary who will receive the interest
        */
        uint256 _lastBal;
        uint256 _tamount;
        uint256 _amount;
        bool _empty;
        interest_s memory _idb;
        address _addr = beneficiary;
        uint256 _to = (block.number - startBlock) / interestBlockDelay;
        if ( _addr == address(0x00) ) { _addr = msg.sender; }
        
        require( block.number > icoDelay );
        require( ! aborted );
        
        for ( uint256 r=0 ; r < _to ; r++ ) {
            if ( r*interestBlockDelay+startBlock >= icoDelay ) { break; }
            _idb = interestDB[msg.sender][r];
            if ( _idb.amount > 0 ) {
                if ( _empty ) {
                    _lastBal = _idb.amount + _amount;
                } else {
                    _lastBal = _idb.amount;
                }
            }
            if ( _idb.empty ) {
                _lastBal = 0;
                _empty = _idb.empty;
            }
            _lastBal += _tamount;
            _tamount = _lastBal * interestOnICO / interestOnICOM / 100;
            _amount += _tamount;
            delete interestDB[msg.sender][r];
        }
        
        require( _amount > 0 );
        token(tokenAddr).mint(_addr, _amount);
    }
    
    function setICOEthPrice(uint256 value) external {
        /*
            Setting of the ICO ETC USD rates which can only be calle by a pre-defined address. 
            After this function is completed till the call of the next function (which is at least an exchangeRateDelay array) this rate counts.
            With this process avoiding the sudden rate changes.
            
            @value  The ETC/USD rate multiplied by 1e4. For example: 2.5 USD/ETC = 25000
        */
        require( isICO() );
        require( icoEtcPriceAddr == msg.sender );
        require( icoExchangeRateSetBlock < block.number);
        icoExchangeRateSetBlock = block.number + exchangeRateDelay;
        icoExchangeRate = value;
    }
    
    function extendICO() external {
        /*
            Extend the period of the ICO with one segment.
            
            It is only possible during the ICO and only callable by the owner.
        */
        require( isICO() );
        require( msg.sender == owner );
        icoDelay += oneSegment;
    }
    
    function closeICO() external {
        /*
            Closing the ICO.
            It is only possible when the ICO period passed and only by the owner.
            The 96% of the whole amount of the token is generated to the address of the fundation.
            Ethers which are situated in this contract will be sent to the address of the fundation.
        */
        require( msg.sender == owner );
        require( block.number > icoDelay );
        require( ! closed );
        closed = true;
        require( ! aborted );
        require( token(tokenAddr).mint(foundationAddress, token(tokenAddr).totalSupply() * 96 / 100) );
        require( premium(premiumAddr).mint(foundationAddress, totalMint / 5000 - totalPremiumMint) );
        require( foundationAddress.send(this.balance) );
        require( token(tokenAddr).closeIco() );
        require( premium(premiumAddr).closeIco() );
    }
    
    function abortICO() external {
        /*
            Withdrawal of the ICO.            
            It is only possible during the ICO period.
            Only callable by the owner.
            After this process only the receiveFunds function will be available for the customers.
        */
        require( isICO() );
        require( msg.sender == owner );
        aborted = true;
    }
    
    function connectTokens(address tokenContractAddr, address premiumContractAddr) external {
        /*
            Installation function which joins the two token contracts with this contract.
            Only callable by the owner
            
            @tokenContractAddr      Address of the corion token contract.
            @premiumContractAddr    Address of the corion premium token contract
        */
        require( msg.sender == owner );
        require( tokenAddr == address(0x00) && premiumAddr == address(0x00) );
        tokenAddr = tokenContractAddr;
        premiumAddr = premiumContractAddr;
    }
    
    function receiveFunds() external {
        /*
            Refund the amount which was purchased during the ICO period.
            This one is only callable if the ICO is withdrawn.
            In this case the address gets back the 90% of the amount which was spent for token during the ICO period.
        */
        require( aborted );
        require( brought[msg.sender].eth > 0 );
        uint256 _val = brought[msg.sender].eth * 90 / 100;
        delete brought[msg.sender];
        require( msg.sender.send(_val) );
    }
    
    function () external payable {
        /*
            Callback function. Simply calls the buy function as a beneficiary and there is no affilate address.
            If they call the contract without any function then this process will be taken place.
        */
        require( isICO() );
        require( buy(msg.sender, address(0x00)) );
    }

    function buy(address beneficiaryAddress, address affilateAddress) public payable returns (bool success) {
        /*
            Buying a token
            
            If there is not at least 0.2 ether balance on the beneficiaryAddress then the amount of the ether which was intended for the purchase will be reduced by 0.2 and that will be sent to the address of the beneficiary.
            From the remaining amount calculate the reward with the help of the getIcoReward function.
            Only that affilate address is valid which has some token on it’s account.
            If there is a valid affilate address then calculate and credit the reward as well in the following way:
            With more than 1e12 token contract credit 5% reward based on the calculation that how many tokens did they buy when he was added as an affilate.
                More than 1e11 token: 4%
                More than 1e10 token: 3%
                More than 1e9 token: 2% below 1%
            @beneficiaryAddress     The address of the accredited where the token will be sent.
            @affilateAddress        The address of the person who offered who will get the referral reward. It can not be equal with the beneficiaryAddress.
        */
        require( isICO() );
        if ( beneficiaryAddress == address(0x00)) { beneficiaryAddress = msg.sender; }
        if ( beneficiaryAddress == affilateAddress ) {
            affilateAddress = address(0x00);
        }
        uint256 _value = msg.value;
        if ( beneficiaryAddress.balance < 0.2 ether ) {
            require( beneficiaryAddress.send(0.2 ether) );
            _value = safeSub(_value, 0.2 ether);
        }
        uint256 _reward = getIcoReward(_value);
        require( _reward > 0 );
        require( token(tokenAddr).mint(beneficiaryAddress, _reward) );
        brought[beneficiaryAddress].eth = safeAdd(brought[beneficiaryAddress].eth, _value);
        brought[beneficiaryAddress].cor = safeAdd(brought[beneficiaryAddress].cor, _reward);
        totalMint = safeAdd(totalMint, _reward);
        require( foundationAddress.send(_value * 10 / 100) );
        uint256 extra;
        if ( affilateAddress != address(0x00) && ( brought[affilateAddress].eth > 0 || interestDB[affilateAddress][0].amount > 0 ) ) {
            affiliate[affilateAddress].weight = safeAdd(affiliate[affilateAddress].weight, _reward);
            extra = affiliate[affilateAddress].weight;
            uint256 rate;
            if (extra >= 1e12) {
                rate = 5;
            } else if (extra >= 1e11) {
                rate = 4;
            } else if (extra >= 1e10) {
                rate = 3;
            } else if (extra >= 1e9) { 
                rate = 2;
            } else {
                rate = 1;
            }
            extra = safeSub(extra * rate / 100, affiliate[affilateAddress].paid);
            affiliate[affilateAddress].paid = safeAdd(affiliate[affilateAddress].paid, extra);
            token(tokenAddr).mint(affilateAddress, extra);
        }
        checkPremium(beneficiaryAddress);
        emit EICO(beneficiaryAddress, _reward, affilateAddress, extra);
        return true;
    }

    function checkPremium(address owner) internal {
        /*
            Crediting the premium token
        
            @owner The corion token balance of this address will be set based on the calculation which shows that how many times can be the amount of the purchased tokens devided by 5000. So after each 5000 token we give 1 premium token.
        */
        uint256 _reward = (brought[owner].cor / 5e9) - brought[owner].corp;
        if ( _reward > 0 ) {
            require( premium(premiumAddr).mint(owner, _reward) );
            brought[owner].corp = safeAdd(brought[owner].corp, _reward);
            totalPremiumMint = safeAdd(totalPremiumMint, _reward);
        }
    }
    
    function getIcoReward(uint256 value) public view returns (uint256 reward) {
        /*
            Expected token volume at token purchase
            
            @value The amount of ether for the purchase
            @reward Amount of the token
                x = (value * 1e6 * USD_ETC_exchange rate / 1e4 / 1e18) * bonus percentage
                2.700000 token = (1e18 * 1e6 * 22500 / 1e4 / 1e18) * 1.20
        */
        reward = (value * 1e6 * icoExchangeRate / icoExchangeRateM / 1 ether) * (ICObonus() + 100) / 100;
        if ( reward < 5e6) { return 0; }
    }
    
    function isICO() public view returns (bool success) {
        return startBlock <= block.number && block.number <= icoDelay && ( ! aborted ) && ( ! closed );
    }
    
    event EICO(address indexed Address, uint256 indexed value, address Affilate, uint256 AffilateValue);
}