Skip to content

Commit 0af235c

Browse files
authored
Merge pull request #313 from windingtree/add-deployer
Add TGEDeployer contract with tests
2 parents e90838b + fd9576f commit 0af235c

File tree

12 files changed

+1135
-45
lines changed

12 files changed

+1135
-45
lines changed

contracts/LifCrowdsale.sol

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,6 @@ contract LifCrowdsale is Ownable, Pausable {
8282
// Amount of lif minted and transferred during the TGE
8383
uint256 public tokensSold;
8484

85-
// Amount of wei received as private presale payments
86-
uint256 public totalPresaleWei;
87-
8885
// Address of the vesting schedule for the foundation created at the
8986
// end of the crowdsale
9087
VestedPayment public foundationVestedPayment;
@@ -257,7 +254,7 @@ contract LifCrowdsale is Ownable, Pausable {
257254

258255
uint256 tokens = weiSent.mul(rate);
259256

260-
totalPresaleWei = totalPresaleWei.add(weiSent);
257+
weiRaised = weiRaised.add(weiSent);
261258

262259
token.mint(beneficiary, tokens);
263260

@@ -268,17 +265,18 @@ contract LifCrowdsale is Ownable, Pausable {
268265
@dev Internal. Forwards funds to the foundation wallet and in case the soft
269266
cap was exceeded it also creates and funds the Market Validation Mechanism.
270267
*/
271-
function forwardFunds() internal {
268+
function forwardFunds(bool deployMVM) internal {
272269

273270
// calculate the max amount of wei for the foundation
274271
uint256 foundationBalanceCapWei = maxFoundationCapUSD.mul(weiPerUSDinTGE);
275272

276-
// If the minimiun cap for the MVM is not reached transfer all funds to foundation
277-
// else if the min cap for the MVM is reached, create it and send the remaining funds.
273+
// If the minimiun cap for the MVM is not reached or the MVM cant be deployed
274+
// transfer all funds to foundation else if the min cap for the MVM is reached,
275+
// create it and send the remaining funds.
278276
// We use weiRaised to compare becuase that is the total amount of wei raised in all TGE
279277
// but we have to distribute the balance using `this.balance` because thats the amount
280278
// raised by the crowdsale
281-
if (weiRaised <= foundationBalanceCapWei) {
279+
if ((weiRaised <= foundationBalanceCapWei) || !deployMVM) {
282280

283281
foundationWallet.transfer(this.balance);
284282

@@ -332,7 +330,8 @@ contract LifCrowdsale is Ownable, Pausable {
332330
foundersVestedPayment.transferOwnership(foundersWallet);
333331

334332
// create the vested payment schedule for the foundation
335-
uint256 foundationPaymentStart = foundationMonthsStart.mul(30 days);
333+
uint256 foundationPaymentStart = foundationMonthsStart.mul(30 days)
334+
.add(30 days);
336335
foundationVestedPayment = new VestedPayment(
337336
block.timestamp.add(foundationPaymentStart), 30 days,
338337
foundationMonthsStart, 0, foundationTokens, token
@@ -418,13 +417,13 @@ contract LifCrowdsale is Ownable, Pausable {
418417
Mechanism in case the soft cap was exceeded. It also unpauses the token to
419418
enable transfers. It can be called only once, after `end2Timestamp`
420419
*/
421-
function finalize() public onlyOwner hasEnded {
420+
function finalize(bool deployMVM) public onlyOwner hasEnded {
422421
require(!isFinalized);
423422

424423
// foward founds and unpause token only if minCap is reached
425424
if (funded()) {
426425

427-
forwardFunds();
426+
forwardFunds(deployMVM);
428427

429428
// finish the minting of the token
430429
token.finishMinting();

contracts/LifMarketValidationMechanism.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ contract LifMarketValidationMechanism is Ownable {
121121
funded = true;
122122
}
123123

124+
/**
125+
@dev Change the LifToken address
126+
*/
127+
function changeToken(address newToken) public onlyOwner {
128+
lifToken = LifToken(newToken);
129+
}
130+
124131
/**
125132
@dev calculates the exponential distribution curve. It determines how much
126133
wei can be distributed back to the foundation every month. It starts with

contracts/VestedPayment.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ contract VestedPayment is Ownable {
6969
token = LifToken(tokenAddress);
7070
}
7171

72+
/**
73+
@dev Change the LifToken address
74+
*/
75+
function changeToken(address newToken) public onlyOwner {
76+
token = LifToken(newToken);
77+
}
78+
7279
/**
7380
@dev Get how many tokens are available to be claimed
7481
*/

contracts/deploy/TGEDeployer.sol

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "../LifCrowdsale.sol";
4+
5+
/**
6+
@title TGEDeployer, A deployer contract for the Winding Tree TGE
7+
8+
This contract is used to create a crowdsale and issue presale tokens in batches
9+
it will also set the weiPerUSD and transfer ownership, after that everything is
10+
ready for the TGE to succed.
11+
*/
12+
contract TGEDeployer {
13+
14+
LifCrowdsale public crowdsale;
15+
address public wallet;
16+
address public owner;
17+
18+
function TGEDeployer(
19+
uint256 startTimestamp,
20+
uint256 end1Timestamp,
21+
uint256 end2Timestamp,
22+
uint256 rate1,
23+
uint256 rate2,
24+
uint256 setWeiLockSeconds,
25+
address foundationWallet,
26+
address foundersWallet
27+
) public {
28+
crowdsale = new LifCrowdsale(
29+
startTimestamp, end1Timestamp, end2Timestamp, rate1, rate2,
30+
setWeiLockSeconds, foundationWallet, foundersWallet
31+
);
32+
wallet = foundationWallet;
33+
owner = msg.sender;
34+
}
35+
36+
// Mint a batch of presale tokens
37+
function addPresaleTokens(address[] contributors, uint256[] values, uint256 rate) public {
38+
require(msg.sender == owner);
39+
require(contributors.length == values.length);
40+
for (uint32 i = 0; i < contributors.length; i ++) {
41+
crowdsale.addPrivatePresaleTokens(contributors[i], values[i], rate);
42+
}
43+
}
44+
45+
// Set the wei per USD in the crowdsale and then transfer ownership to foundation
46+
function finish(uint256 weiPerUSDinTGE) public {
47+
require(msg.sender == owner);
48+
crowdsale.setWeiPerUSDinTGE(weiPerUSDinTGE);
49+
crowdsale.transferOwnership(wallet);
50+
}
51+
52+
}

scripts/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ start_testrpc() {
3535
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501210,1000000000000000000000000"
3636
)
3737

38-
node_modules/.bin/testrpc --gasLimit 0xfffffffffff "${accounts[@]}" > /dev/null &
38+
node_modules/.bin/testrpc --gasLimit 8000000 --gasPrice 41000000000 "${accounts[@]}" > /dev/null &
3939

4040
testrpc_pid=$!
4141
}

test/Crowdsale.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ contract('LifToken Crowdsale', function (accounts) {
281281
await crowdsale.buyTokens(beneficiary, { value: weiAmount, from: accounts[5] });
282282

283283
await increaseTimeTestRPCTo(end2 + 2);
284-
await crowdsale.finalize();
284+
await crowdsale.finalize(true);
285285

286286
// TO DO: Find out why the balance difference in this assert
287287
// const balanceBeforeClaim = web3.eth.getBalance(beneficiary);
@@ -307,7 +307,7 @@ contract('LifToken Crowdsale', function (accounts) {
307307
await crowdsale.buyTokens(accounts[6], { value: weiAmount, from: accounts[5] });
308308

309309
await increaseTimeTestRPCTo(end2 + 2);
310-
await crowdsale.finalize();
310+
await crowdsale.finalize(true);
311311

312312
try {
313313
await crowdsale.claimEth({ from: accounts[6] });
@@ -361,7 +361,7 @@ contract('LifToken Crowdsale', function (accounts) {
361361
await crowdsale.buyTokens(beneficiary, { value: weiAmount, from: accounts[5] });
362362

363363
await increaseTimeTestRPCTo(end2 + 2);
364-
await crowdsale.finalize();
364+
await crowdsale.finalize(true);
365365

366366
try {
367367
await crowdsale.claimEth({ from: accounts[8] });
@@ -390,10 +390,10 @@ contract('LifToken Crowdsale', function (accounts) {
390390
await crowdsale.buyTokens(beneficiary, { value: weiAmount, from: accounts[5] });
391391

392392
await increaseTimeTestRPCTo(end2 + 2);
393-
await crowdsale.finalize();
393+
await crowdsale.finalize(true);
394394

395395
try {
396-
await crowdsale.finalize();
396+
await crowdsale.finalize(true);
397397
assert(false, 'should have thrown');
398398
} catch (e) {
399399
assert(help.isInvalidOpcodeEx(e));
@@ -419,7 +419,7 @@ contract('LifToken Crowdsale', function (accounts) {
419419
await crowdsale.buyTokens(beneficiary, { value: weiAmount, from: accounts[5] });
420420

421421
await increaseTimeTestRPCTo(end2 + 2);
422-
await crowdsale.finalize();
422+
await crowdsale.finalize(true);
423423

424424
const MVMAddress = await crowdsale.MVM.call();
425425
assert(MVMAddress !== help.zeroAddress);
@@ -447,7 +447,7 @@ contract('LifToken Crowdsale', function (accounts) {
447447
await crowdsale.buyTokens(beneficiary, { value: weiAmount, from: accounts[5] });
448448

449449
await increaseTimeTestRPCTo(end2 + 2);
450-
await crowdsale.finalize();
450+
await crowdsale.finalize(true);
451451

452452
const MVMAddress = await crowdsale.MVM.call();
453453
assert(MVMAddress === help.zeroAddress, 'no MVM should have been created: ' + MVMAddress);

test/CrowdsaleGenTest.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,11 @@ contract('LifCrowdsale Property-based test', function (accounts) {
3737
let tokensInPurchases = sumBigNumbers(_.map(state.purchases, (p) => p.tokens));
3838
tokensInPurchases.should.be.bignumber.equal(help.lifWei2Lif(await crowdsale.tokensSold()));
3939

40-
let presaleWei = sumBigNumbers(_.map(state.presalePurchases, (p) => p.wei));
41-
42-
presaleWei.should.be.bignumber.equal(await crowdsale.totalPresaleWei.call());
43-
4440
help.debug('checking purchases total wei, purchases:', JSON.stringify(state.purchases));
4541
let weiInPurchases = sumBigNumbers(_.map(state.purchases, (p) => p.wei));
46-
weiInPurchases.should.be.bignumber.equal(await crowdsale.weiRaised());
42+
if (state.crowdsaleFinalized) { new BigNumber(0).should.be.bignumber.equal(await web3.eth.getBalance(crowdsale.address)); } else { weiInPurchases.should.be.bignumber.equal(await web3.eth.getBalance(crowdsale.address)); }
4743

4844
// Check presale tokens sold
49-
state.totalPresaleWei.should.be.bignumber.equal(await crowdsale.totalPresaleWei.call());
5045
assert.equal(state.crowdsaleFinalized, await crowdsale.isFinalized.call());
5146
if (state.crowdsaleFinalized && state.weiPerUSDinTGE > 0) {
5247
assert.equal(state.crowdsaleFunded, await crowdsale.funded());
@@ -179,7 +174,6 @@ contract('LifCrowdsale Property-based test', function (accounts) {
179174
presalePurchases: [],
180175
claimedEth: {},
181176
weiRaised: zero,
182-
totalPresaleWei: zero,
183177
crowdsalePaused: false,
184178
tokenPaused: true,
185179
crowdsaleFinalized: false,

test/commands.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,7 @@ async function runFinalizeCrowdsaleCommand (command, state) {
283283
let crowdsaleFunded = (state.weiRaised >= state.crowdsaleData.minCapUSD * state.weiPerUSDinTGE);
284284

285285
help.debug('finishing crowdsale on block', nextTimestamp, ', from address:', gen.getAccount(command.fromAccount), ', funded:', crowdsaleFunded);
286-
287-
let tx = await state.crowdsaleContract.finalize({ from: account });
286+
let tx = await state.crowdsaleContract.finalize(true, { from: account });
288287
help.debug('gas used in finalize:', tx.receipt.gasUsed);
289288

290289
if (!help.inCoverage()) { // gas cannot be measured correctly when running coverage
@@ -375,7 +374,7 @@ async function runFinalizeCrowdsaleCommand (command, state) {
375374
async function runAddPrivatePresalePaymentCommand (command, state) {
376375
let { startTimestamp } = state.crowdsaleData,
377376
nextTimestamp = latestTime(),
378-
weiToSend = web3.toWei(command.eth, 'ether'),
377+
weiSent = web3.toWei(command.eth, 'ether'),
379378
account = gen.getAccount(command.fromAccount),
380379
beneficiary = gen.getAccount(command.beneficiaryAccount),
381380
hasZeroAddress = _.some([account, beneficiary], isZeroAddress);
@@ -385,20 +384,20 @@ async function runAddPrivatePresalePaymentCommand (command, state) {
385384
(account !== gen.getAccount(state.owner)) ||
386385
(state.crowdsaleFinalized) ||
387386
hasZeroAddress ||
388-
(weiToSend === 0) ||
387+
(weiSent === 0) ||
389388
(command.rate <= state.crowdsaleData.rate1);
390389

391390
try {
392391
help.debug('Adding presale private tokens for account:', command.beneficiaryAccount, 'eth:', command.eth, 'fromAccount:', command.fromAccount, 'blockTimestamp:', nextTimestamp);
393392

394-
const tx = await state.crowdsaleContract.addPrivatePresaleTokens(beneficiary, weiToSend, command.rate, { from: account });
393+
const tx = await state.crowdsaleContract.addPrivatePresaleTokens(beneficiary, weiSent, command.rate, { from: account });
395394

396395
assert.equal(false, shouldThrow, 'buyTokens should have thrown but it did not');
397396

398-
state.totalSupply = state.totalSupply.plus(weiToSend * command.rate);
399-
state.totalPresaleWei = state.totalPresaleWei.plus(weiToSend);
397+
state.totalSupply = state.totalSupply.plus(weiSent * command.rate);
398+
state.weiRaised = state.weiRaised.plus(weiSent);
400399
state.presalePurchases = _.concat(state.presalePurchases,
401-
{ rate: command.rate, wei: weiToSend, beneficiary: command.beneficiary, account: command.fromAccount }
400+
{ rate: command.rate, wei: weiSent, beneficiary: command.beneficiary, account: command.fromAccount }
402401
);
403402
state = decreaseEthBalance(state, command.fromAccount, help.txGasCost(tx));
404403
} catch (e) {

0 commit comments

Comments
 (0)