Skip to content

Commit 30b6743

Browse files
author
H
committed
final-cleanup
1 parent 2085547 commit 30b6743

20 files changed

+964
-885
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ truffle/node_modules
44
.DS_Store
55
# Debug log from npm
66
truffle/npm-debug.log
7+
8+
truffle/coverage
9+
truffle/coverage.json

ethWithdrawal.js

-64
This file was deleted.

truffle/.eslintrc

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"plugins": [
77
"babel"
88
],
9+
"globals": {
10+
"web3": false
11+
},
912
"rules": {
1013
"key-spacing" : 0,
1114
"jsx-quotes" : [2, "prefer-single"],

truffle/.solcover.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
module.exports = {
2-
testCommand: `truffle test --network coverage`,
3-
port: 8555,
4-
testrpcOptions: " --port 8555 --gasLimit 0xffffffffffff --account='0xfacec5711eb0a84bbd13b9782df26083fc68cf41b2210681e4d478687368fdc3,100000000000000000000000000' --account='0xb7d90a23546b263a9a68a26ed7045cd6ce7d3b0dfa7d3c7b66434a4a89453cf7,100000000000000000000000000' --account='0x58823bde84d19ad2bdb6739f9ef1fc8ca4ba0c617ecc9a1fa675282175a9bc02,100000000000000000000000000' --account='0x42891283028bba9611583fcaa0dea947251b9f980a1e3d9858cd33b0e8077195,100000000000000000000000000' --account='0x6009fc3fda6c5976cfecc36b9c0c9423f78bcc971ade88f32c0e016225c1601a,100000000000000000000000000' --account='0xe598179ebee08a9b1f1afaef6ac526e5cfe615d87831aed8b080c988773bda6d,100000000000000000000000000'"
2+
norpc: true,
3+
skipFiles: ['Array256Lib.sol','BasicMathLib.sol','CrowdsaleToken.sol','CrowdsaleLib.sol','LinkedListLib.sol','TokenLib.sol'],
54
};

truffle/contracts/CrowdsaleLib.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ library CrowdsaleLib {
209209
require(msg.sender == self.owner);
210210
require(self.ownerBalance > 0);
211211

212-
uint256 amount = this.balance;
212+
uint256 amount = self.ownerBalance;
213213
self.ownerBalance = 0;
214214
self.owner.transfer(amount);
215215
LogOwnerEthWithdrawn(msg.sender,amount,"Crowdsale owner has withdrawn all funds!");

truffle/contracts/InteractiveCrowdsaleLib.sol

+85-57
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ pragma solidity ^0.4.18;
22

33
/**
44
* @title InteractiveCrowdsaleLib
5-
* @author Majoolr.io
5+
* @author Modular, Inc
66
*
77
* version 1.0.0
8-
* Copyright (c) 2017 Modular, LLC
8+
* Copyright (c) 2017 Modular, Inc
99
* The MIT License (MIT)
1010
*
1111
* The InteractiveCrowdsale Library provides functionality to create a crowdsale
@@ -63,24 +63,33 @@ library InteractiveCrowdsaleLib {
6363

6464
// amount of value committed at this valuation, cannot rely on owner balance
6565
// due to fluctations in commitment calculations needed after owner withdraws
66-
// in other words, the total amount of ETH committed, including total bids that will eventually get partial purchases
66+
// in other words, the total amount of ETH committed, including total bids
67+
// that will eventually get partial purchases
6768
uint256 valueCommitted;
6869

69-
// the bucket that sits either at or just below current total valuation. determines where the cutoff point is for bids in the sale
70+
// the bucket that sits either at or just below current total valuation.
71+
// determines where the cutoff point is for bids in the sale
7072
uint256 currentBucket;
7173

74+
// the fraction of each minimal valuation bidder's ether refund, 'q' is from the paper
75+
// and is calculated when finalizing the sale
76+
uint256 q;
77+
7278
// minimim amount that the sale needs to make to be successfull
7379
uint256 minimumRaise;
7480

7581
// percentage of total tokens being sold in this sale
7682
uint8 percentBeingSold;
7783

78-
// the bonus amount for early bidders. This is a percentage of the base token price that gets added on the the base token price
79-
// used in getCurrentBonus()
84+
// the bonus amount for early bidders. This is a percentage of the base token
85+
// price that gets added on the the base token price used in getCurrentBonus()
8086
uint256 priceBonusPercent;
8187

8288
// Indicates that the owner has finalized the sale and withdrawn Ether
83-
bool ownerHasWithdrawnETH;
89+
bool isFinalized;
90+
91+
// Set to true if the sale is canceled
92+
bool isCanceled;
8493

8594
// shows the price that the address purchased tokens at
8695
mapping (address => uint256) pricePurchasedAt;
@@ -113,15 +122,16 @@ library InteractiveCrowdsaleLib {
113122
// Indicates when the price of the token changes
114123
event LogTokenPriceChange(uint256 amount, string Msg);
115124

116-
// Logs the current bucket that the valuation points to, the total valuation of the sale, and the amount of ETH committed, including total bids that will eventually get partial purchases
125+
// Logs the current bucket that the valuation points to, the total valuation of
126+
// the sale, and the amount of ETH committed, including total bids that will eventually get partial purchases
117127
event BucketAndValuationAndCommitted(uint256 bucket, uint256 valuation, uint256 committed);
118128

119129
/// @dev Called by a crowdsale contract upon creation.
120130
/// @param self Stored crowdsale from crowdsale contract
121131
/// @param _owner Address of crowdsale owner
122132
/// @param _saleData Array of 3 item arrays such that, in each 3 element
123133
/// array index-0 is a timestamp, index-1 is price in tokens/ETH
124-
/// index-2 is address purchase valuation at that time, 0 if no address valuation
134+
/// index-2 is address purchase cap at that time, 0 if no address cap
125135
/// @param _priceBonusPercent the bonus amount for early bidders
126136
/// @param _minimumRaise minimim amount that the sale needs to make to be successfull
127137
/// @param _endWithdrawalTime timestamp that indicates that manual withdrawals are no longer allowed
@@ -208,18 +218,17 @@ library InteractiveCrowdsaleLib {
208218
remainder = weiTokens % 1000000000000000000;
209219
remainder = remainder / _price;
210220

211-
// make sure there are enough tokens available to satisfy the bid
212-
assert(numTokens <= self.base.token.balanceOf(this));
213-
214221
return (numTokens,remainder);
215222
}
216223

217-
/// @dev Called when an address wants to submit bid to the sale
224+
/// @dev Called when an address wants to submit a bid to the sale
218225
/// @param self Stored crowdsale from crowdsale contract
219226
/// @return currentBonus percentage of the bonus that is applied for the purchase
220227
function getCurrentBonus(InteractiveCrowdsaleStorage storage self) internal view returns (uint256){
221-
uint256 bonusTime = self.endWithdrawalTime - self.base.startTime; // can't underflow becuase endWithdrawalTime > startTime
222-
uint256 elapsed = now - self.base.startTime; // can't underflow becuase now > startTime
228+
// can't underflow becuase endWithdrawalTime > startTime
229+
uint256 bonusTime = self.endWithdrawalTime - self.base.startTime;
230+
// can't underflow because now > startTime
231+
uint256 elapsed = now - self.base.startTime;
223232
uint256 percentElapsed = (elapsed * 100)/bonusTime;
224233

225234
bool err;
@@ -244,7 +253,7 @@ library InteractiveCrowdsaleLib {
244253
require(msg.sender != self.base.owner);
245254
require(self.base.validPurchase());
246255
// bidder can't have already bid
247-
require(self.personalCaps[msg.sender] == 0 && self.base.hasContributed[msg.sender] == 0);
256+
require((self.personalCaps[msg.sender] == 0) && (self.base.hasContributed[msg.sender] == 0));
248257

249258
uint256 _bonusPercent;
250259
// token purchase bonus only applies before the withdrawal lock
@@ -258,7 +267,8 @@ library InteractiveCrowdsaleLib {
258267
}
259268

260269
// personal valuation and minimum should be set to the proper granularity,
261-
// only three most significant values can be non-zero. reduces the number of possible valuation buckets in the linked list
270+
// only three most significant values can be non-zero. reduces the number of possible
271+
// valuation buckets in the linked list
262272
uint256 digits = numDigits(_personalCap);
263273
if(digits > 3) {
264274
require((_personalCap % (10**(digits - 3))) == 0);
@@ -376,13 +386,15 @@ library InteractiveCrowdsaleLib {
376386
//uint256 multiplierPercent = (100*(t - s))/t;
377387
//self.pricePurchasedAt = pa-((pa-pu)/3)
378388

379-
uint256 multiplierPercent = (100*(self.endWithdrawalTime - now))/(self.endWithdrawalTime-self.base.startTime);
380-
refundWei = (multiplierPercent*self.base.hasContributed[msg.sender])/100;
389+
uint256 multiplierPercent = (100 * (self.endWithdrawalTime - now)) /
390+
(self.endWithdrawalTime - self.base.startTime);
391+
refundWei = (multiplierPercent * self.base.hasContributed[msg.sender]) / 100;
381392

382393
self.valuationSums[self.personalCaps[msg.sender]] -= refundWei;
383394
self.numBidsAtValuation[self.personalCaps[msg.sender]] -= 1;
384395

385-
self.pricePurchasedAt[msg.sender] = self.pricePurchasedAt[msg.sender]-((self.pricePurchasedAt[msg.sender]-self.base.tokensPerEth)/3);
396+
self.pricePurchasedAt[msg.sender] = self.pricePurchasedAt[msg.sender] -
397+
((self.pricePurchasedAt[msg.sender] - self.base.tokensPerEth) / 3);
386398

387399
self.hasManuallyWithdrawn[msg.sender] = true;
388400

@@ -462,26 +474,45 @@ library InteractiveCrowdsaleLib {
462474
/// @param self stored crowdsale from crowdsale contract
463475
function finalizeSale(InteractiveCrowdsaleStorage storage self) public returns (bool) {
464476
require(now >= self.base.endTime);
465-
require(!self.ownerHasWithdrawnETH); // can only be called once
477+
require(!self.isFinalized); // can only be called once
478+
require(setCanceled(self));
466479

480+
self.isFinalized = true;
467481
require(launchToken(self));
468-
469-
self.ownerHasWithdrawnETH = true;
470-
self.base.ownerBalance = self.totalValuation; // sets ETH raised in the sale to be ready for withdrawal
482+
// may need to be computed due to EVM rounding errors
483+
uint256 computedValue;
484+
485+
if(!self.isCanceled){
486+
if(self.totalValuation == self.currentBucket){
487+
// calculate the fraction of each minimal valuation bidders ether and tokens to refund
488+
self.q = (100*(self.valueCommitted - self.totalValuation)/(self.valuationSums[self.totalValuation])) + 1;
489+
computedValue = self.valueCommitted - self.valuationSums[self.totalValuation];
490+
computedValue += (self.q * self.valuationSums[self.totalValuation])/100;
491+
} else {
492+
// no computation necessary
493+
computedValue = self.totalValuation;
494+
}
495+
self.base.ownerBalance = computedValue; // sets ETH raised in the sale to be ready for withdrawal
496+
}
471497
}
472498

473499
/// @dev Mints the token being sold by taking the percentage of the token supply
474500
/// being sold in this sale along with the valuation, derives all necessary
475501
/// values and then transfers owner tokens to the owner.
476502
/// @param self Stored crowdsale from crowdsale contract
477503
function launchToken(InteractiveCrowdsaleStorage storage self) internal returns (bool) {
478-
479-
uint256 _fullValue = (self.totalValuation*100)/uint256(self.percentBeingSold); // total valuation of all the tokens not including the bonus
480-
uint256 _bonusValue = ((self.totalValuation * (100 + self.priceBonusPercent))/100) - self.totalValuation; // total valuation of bonus tokens
481-
uint256 _supply = (_fullValue * self.base.tokensPerEth)/1000000000000000000; // total supply of all tokens not including the bonus
482-
uint256 _bonusTokens = (_bonusValue * self.base.tokensPerEth)/1000000000000000000; // total number of bonus tokens
483-
uint256 _ownerTokens = _supply - ((_supply * uint256(self.percentBeingSold))/100); // tokens allocated to the owner of the sale
484-
uint256 _totalSupply = _supply + _bonusTokens; // total supply of tokens not including the bonus tokens
504+
// total valuation of all the tokens not including the bonus
505+
uint256 _fullValue = (self.totalValuation*100)/uint256(self.percentBeingSold);
506+
// total valuation of bonus tokens
507+
uint256 _bonusValue = ((self.totalValuation * (100 + self.priceBonusPercent))/100) - self.totalValuation;
508+
// total supply of all tokens not including the bonus
509+
uint256 _supply = (_fullValue * self.base.tokensPerEth)/1000000000000000000;
510+
// total number of bonus tokens
511+
uint256 _bonusTokens = (_bonusValue * self.base.tokensPerEth)/1000000000000000000;
512+
// tokens allocated to the owner of the sale
513+
uint256 _ownerTokens = _supply - ((_supply * uint256(self.percentBeingSold))/100);
514+
// total supply of tokens not including the bonus tokens
515+
uint256 _totalSupply = _supply + _bonusTokens;
485516

486517
// deploy new token contract with total number of tokens
487518
self.base.token = new CrowdsaleToken(address(this),
@@ -492,11 +523,11 @@ library InteractiveCrowdsaleLib {
492523
self.tokenInfo.stillMinting);
493524

494525
// if the sale got canceled, then all the tokens go to the owner and bonus tokens are burned
495-
if(saleCanceled(self)){
526+
if(!self.isCanceled){
527+
self.base.token.transfer(self.base.owner, _ownerTokens);
528+
} else {
496529
self.base.token.transfer(self.base.owner, _supply);
497530
self.base.token.burnToken(_bonusTokens);
498-
} else {
499-
self.base.token.transfer(self.base.owner, _ownerTokens);
500531
}
501532
// the owner of the crowdsale becomes the new owner of the token contract
502533
self.base.token.changeOwner(self.base.owner);
@@ -505,13 +536,17 @@ library InteractiveCrowdsaleLib {
505536
return true;
506537
}
507538

508-
/// @dev returns a boolean indicating if the sale is canceled.
509-
/// This can either be if the minimum raise hasn't been met
539+
/// @dev returns a boolean indicating if the sale is canceled.
540+
/// This can either be if the minimum raise hasn't been met
510541
/// or if it is 30 days after the sale and the owner hasn't finalized the sale.
511542
/// @return bool canceled indicating if the sale is canceled or not
512-
function saleCanceled(InteractiveCrowdsaleStorage storage self) internal view returns(bool canceled){
513-
canceled = (self.totalValuation < self.minimumRaise) ||
514-
((now > (self.base.endTime + 30 days)) && !self.ownerHasWithdrawnETH);
543+
function setCanceled(InteractiveCrowdsaleStorage storage self) internal returns(bool){
544+
bool canceled = (self.totalValuation < self.minimumRaise) ||
545+
((now > (self.base.endTime + 30 days)) && !self.isFinalized);
546+
547+
if(canceled) {self.isCanceled = true;}
548+
549+
return true;
515550
}
516551

517552
/// @dev If the address' personal cap is below the pointer, refund them all their ETH.
@@ -521,16 +556,20 @@ library InteractiveCrowdsaleLib {
521556
function retreiveFinalResult(InteractiveCrowdsaleStorage storage self) public returns (bool) {
522557
require(now > self.base.endTime);
523558
require(self.personalCaps[msg.sender] > 0);
524-
require(self.ownerHasWithdrawnETH);
525559

526560
uint256 numTokens;
527561
uint256 remainder;
528562

529-
if (saleCanceled(self)) {
563+
if(!self.isFinalized){
564+
require(setCanceled(self));
565+
require(self.isCanceled);
566+
}
567+
568+
if (self.isCanceled) {
530569
// if the sale was canceled, everyone gets a full refund
531570
self.base.leftoverWei[msg.sender] += self.base.hasContributed[msg.sender];
532571
self.base.hasContributed[msg.sender] = 0;
533-
LogErrorMsg(self.totalValuation, "totalValuation has not reached the minimumRaise. All bids have been refunded!");
572+
LogErrorMsg(self.totalValuation, "Sale is canceled, all bids have been refunded!");
534573
return true;
535574
}
536575

@@ -545,13 +584,9 @@ library InteractiveCrowdsaleLib {
545584
return self.base.withdrawLeftoverWei();
546585

547586
} else if (self.personalCaps[msg.sender] == self.totalValuation) {
548-
uint256 q;
549-
550-
// calculate the fraction of each minimal valuation bidders ether and tokens to refund
551-
q = (100*(self.valueCommitted - self.totalValuation)/(self.valuationSums[self.totalValuation])) + 1;
552587

553588
// calculate the portion that this address has to take out of their bid
554-
uint256 refundAmount = (q*self.base.hasContributed[msg.sender])/100;
589+
uint256 refundAmount = (self.q*self.base.hasContributed[msg.sender])/100;
555590

556591
// refund that amount of wei to the address
557592
self.base.leftoverWei[msg.sender] += refundAmount;
@@ -564,7 +599,9 @@ library InteractiveCrowdsaleLib {
564599
LogErrorMsg(self.pricePurchasedAt[msg.sender],"price");
565600

566601
// calculate the number of tokens that the bidder purchased
567-
(numTokens, remainder) = calculateTokenPurchase(self,self.base.hasContributed[msg.sender],self.pricePurchasedAt[msg.sender]);
602+
(numTokens, remainder) = calculateTokenPurchase(self,
603+
self.base.hasContributed[msg.sender],
604+
self.pricePurchasedAt[msg.sender]);
568605

569606
// add tokens to the bidders purchase. can't overflow because it will be under the cap
570607
self.base.withdrawTokensMap[msg.sender] += numTokens;
@@ -591,11 +628,6 @@ library InteractiveCrowdsaleLib {
591628

592629
/*Functions "inherited" from CrowdsaleLib library*/
593630

594-
function withdrawTokens(InteractiveCrowdsaleStorage storage self) internal returns (bool) {
595-
596-
return self.base.withdrawTokens();
597-
}
598-
599631
function withdrawLeftoverWei(InteractiveCrowdsaleStorage storage self) internal returns (bool) {
600632

601633
return self.base.withdrawLeftoverWei();
@@ -618,10 +650,6 @@ library InteractiveCrowdsaleLib {
618650
return self.personalCaps[_bidder];
619651
}
620652

621-
function getSaleData(InteractiveCrowdsaleStorage storage self, uint256 _timestamp) internal view returns (uint256[3]) {
622-
return self.base.getSaleData(_timestamp);
623-
}
624-
625653
function getTokensSold(InteractiveCrowdsaleStorage storage self) internal view returns (uint256) {
626654
return self.base.getTokensSold();
627655
}

0 commit comments

Comments
 (0)