From a15ecfb9162b3ddeb566693438dc5495be681e05 Mon Sep 17 00:00:00 2001 From: enkogu Date: Thu, 7 Feb 2019 21:49:37 +0700 Subject: [PATCH] STOContract, style fixes --- contracts/Daico/Daico.sol | 670 ++++++++++++++------------------ contracts/Daico/IDaico.sol | 3 + contracts/Daico/STOContract.sol | 31 ++ package-lock.json | 275 +++++++++---- package.json | 2 +- scripts/test.sh | 4 +- test/Daico.functional.tests.js | 95 +++-- test/utils/helpers.js | 36 +- truffle.js | 2 +- 9 files changed, 600 insertions(+), 518 deletions(-) create mode 100644 contracts/Daico/STOContract.sol diff --git a/contracts/Daico/Daico.sol b/contracts/Daico/Daico.sol index e2eaa65..e83af7c 100644 --- a/contracts/Daico/Daico.sol +++ b/contracts/Daico/Daico.sol @@ -2,13 +2,13 @@ pragma solidity ^0.4.24; import "zeppelin-solidity/contracts/math/SafeMath.sol"; import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; -import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; +import "./IDaico.sol"; - -contract Daico { +contract Daico is IDaico { using SafeMath for uint; - event InvestEvent(uint amount, address _sender, uint total, uint tapSum); + event InvestEvent(uint _amount, address _sender, uint _total, uint _tapSum, uint _startedAt); + event Vote(uint _amount, address _sender, bool _vote); enum TapStage { Preparing, @@ -22,35 +22,34 @@ contract Daico { Terminated } - enum VotingResult { - NotExist, - QuorumNotReached, - ConsensusNotReached, + // Voting result + enum VR { + NoResult, + NoCons, Success, Decline } - Project public proj; - - struct Project { - address owner; - MintableToken token; - ERC20 daiToken; - uint createdAt; - uint startedAt; - uint investDuration; - uint votingDuration; - uint additionalDuration; - uint changeRoadmapDuration; - uint quorumPercent; - // uint quorumDecresedPercent; - uint declinePercent; - uint consensusPercent; - bool rewrited; - uint roadmapsCount; - mapping(uint=>uint) tapToRId; // tapId -> roadmapId - mapping(uint=>Roadmap) roadmaps; // roadmapId -> roadmap - } + uint votesD = 7 days; + uint addVotesD = 7 days; + uint investD = 7 days; + uint roadmapD = 21 days; + uint infinity = 99999 days; + + uint quorumPercent = 70; + uint declinePercent = 80; + uint consensusPercent = 70; + + address owner; + address STOContractAddress; + ERC20 daiToken; + uint createdAt; + uint startedAt; + bool newRoadmapProposed; + uint roadmapsCount; + mapping(uint=>uint) tapToRId; // tapId -> roadmapId + mapping(uint=>Roadmap) roadmaps; // roadmapId -> roadmap + struct Roadmap { uint tapsCount; @@ -77,438 +76,335 @@ contract Daico { address[] voted; } - // owner создает Daico контракт, куда закладываются следующие параметры: - // 1. daiToken – токен Evercity - // 2. returnAddress - куда вернуть деньги в случае фейла проекта - // 3. tapFunds – массив количества выплат на каждый tap - // 4. tapDurations – массив длительностей каждого tap - // В конструкторе деплоится projectToken, которые будут получать investors в обмен на daiToken - constructor(address _owner, address _daiToken, address _returnAddress, uint[] memory _tapFunds, uint[] memory _tapDurations) public { + constructor( + address _owner, + address _daiToken, + address _STOContractAddress, + address _returnAddress, + uint[] memory _tapFunds, + uint[] memory _tapDurations) public + { require(_tapFunds.length == _tapDurations.length); - MintableToken projectToken = new MintableToken(); - proj.token = projectToken; - proj.daiToken = ERC20(_daiToken); - proj.owner = _owner; - proj.createdAt = now; - proj.investDuration = 7 days; - proj.votingDuration = 7 days; - proj.additionalDuration = 7 days; - proj.changeRoadmapDuration = 21 days; - proj.quorumPercent = 70; - // proj.quorumDecresedPercent = 50; - proj.declinePercent = 80; - proj.consensusPercent = 70; - - proj.roadmaps[proj.roadmapsCount].tapsCount = _tapFunds.length; - for(uint i = 0; i < _tapFunds.length; i++) { - require(_tapDurations[i] > 7); - proj.tapToRId[i] = 0; // just for clearness; - proj.roadmaps[proj.roadmapsCount].taps[i] = Tap(_tapFunds[i], _tapDurations[i]*(1 days), false); + STOContractAddress = _STOContractAddress; + daiToken = ERC20(_daiToken); + owner = _owner; + createdAt = now; + + roadmaps[roadmapsCount].tapsCount = _tapFunds.length; + Tap memory tap; + for(uint tapFundsNum = 0; tapFundsNum < _tapFunds.length; tapFundsNum++) { + require(_tapDurations[tapFundsNum] > 7); + tapToRId[tapFundsNum] = 0; // just for clearness; + + tap.funds = _tapFunds[tapFundsNum]; + tap.duration = _tapDurations[tapFundsNum]*(1 days); + tap.isWithdrawed = false; + roadmaps[roadmapsCount].taps[tapFundsNum] = tap; } - proj.roadmapsCount += 1; + roadmapsCount += 1; } - function getCurrentTap() internal view returns(uint) { - (uint t, TapStage[] memory tapStages, uint v) = getTapsInfo(); - return t; + function getCurrentTapNum() internal view returns(uint) { + (uint curTapNum, TapStage[] memory tapStages, uint v) = getTapsInfo(); + return curTapNum; } function getMaximalTapsLength() internal view returns(uint maximal) { - for(uint i = 0; i < proj.roadmapsCount; i++) { - if(proj.roadmaps[i].tapsCount > maximal) { - maximal = proj.roadmaps[i].tapsCount; + for(uint rmNum = 0; rmNum < roadmapsCount; rmNum++) { + if(roadmaps[rmNum].tapsCount > maximal) { + maximal = roadmaps[rmNum].tapsCount; } } } - // // Функция инициации контракта. - // // Имеется общее количество daiToken, которое должно поступить на контракт перед началом процесса – Sum(tapFunds) - // // Любой может купить любое количество оставшихся projectToken, предварительно сделав approve на то же количество daiToken - // // Как только все projectToken проданы, контракт автоматически переходит в первый stage, и owner может снять daiToken за первый tap. - // // Если lifetime закончился, а projectToken проданы не все, то можно вызвать returnTokens, которая вернет токены обратно инвесторам. - // // 1. Для примера – tap.durations == 1 month, все голосования проходят успешно, тогда Timeline такой: - // // invest ----> active |---(1TAP: 1 month - 1 week: nothing)---(1TAP: 1 week: voting for a next tap)---(2TAP: 1 month - 1 week: nothing)---(2TAP: 1 week: voting for a next tap)---... - // // 2. Не набран кворум – сразу стартует повторное голосование (7 дней) с пониженным кворумом >50%. - // // invest ----> active |---(1TAP: 1 month - 1 week: nothing)---(1TAP: 1 week: voting for a next tap)---(1TAP: 1 additional week: voting for a next tap)---... - // // 3. Кворум набран, но пороговый % проголосовавших «за» не пройден. В результате сразу инициируется голосование по пересмотру роадмапа и доработке проекта через месяц. - // // invest ----> active |---(1TAP: 1 month - 1 week: nothing)---(1TAP: 1 week: voting for a next tap)---(1TAP: 3 additional weeks: change roadmap)---(1TAP: 1 additional week: voting)---... - function invest(uint _amount) public { - (uint t, TapStage[] memory tapStages, uint v) = getTapsInfo(); - require(tapStages[t] == TapStage.Investing); + function replaceRoadmapToProposedOne() internal { + uint curTapNum = getCurrentTapNum(); + for(uint tapNum = 0; tapNum < roadmaps[roadmapsCount - 1].tapsCount; tapNum++) { + if(tapNum > curTapNum) tapToRId[tapNum] = roadmapsCount - 1; + } + } + + function addInvestor(uint _amount, address _investorAddress) public { + require(STOContractAddress == msg.sender); + (uint curTapNum, TapStage[] memory tapStages, uint votNum) = getTapsInfo(); + require(tapStages[curTapNum] == TapStage.Investing); require(_amount > 0); + - if(proj.rewrited) { - for(uint i = 0; i < proj.roadmaps[proj.roadmapsCount - 1].tapsCount; i++) { - if(i > t) { - proj.tapToRId[i] = proj.roadmapsCount - 1; - } - } - t += 1; - } + if(newRoadmapProposed) replaceRoadmapToProposedOne(); curTapNum += 1; + + uint rmNum = tapToRId[curTapNum]; + uint invId = getInvestorId(_investorAddress); - require(totalInvestitions(t) + _amount <= tapAmountsSum(t)); - proj.daiToken.transferFrom(msg.sender, address(this), _amount); - proj.token.mint(msg.sender, _amount); - if(getInvestorId(msg.sender) == proj.roadmaps[proj.tapToRId[getCurrentTap()]].investorsCount) { // not investor - uint invCount = proj.roadmaps[proj.tapToRId[t]].investorsCount; - proj.roadmaps[proj.tapToRId[t]].investors[invCount] = Investor(msg.sender, _amount); - proj.roadmaps[proj.tapToRId[t]].investorsCount += 1; + require(amountOfAllInvestments(curTapNum) + _amount <= tapAmountsSum(curTapNum)); + daiToken.transferFrom(STOContractAddress, address(this), _amount); + + bool notInvestor = (invId == roadmaps[rmNum].investorsCount); + if(notInvestor) { + uint invCount = roadmaps[rmNum].investorsCount; + roadmaps[rmNum].investors[invCount] = Investor(_investorAddress, _amount); + roadmaps[rmNum].investorsCount += 1; } else { - proj.roadmaps[proj.tapToRId[t]].investors[getInvestorId(msg.sender)].invested += _amount; + roadmaps[rmNum].investors[invId].invested += _amount; } - emit InvestEvent(_amount, msg.sender, totalInvestitions(t), tapAmountsSum(t)); - if((totalInvestitions(t) >= tapAmountsSum(t)) && (proj.startedAt==0)) { - proj.startedAt = now; - proj.rewrited = false; + + if(areAllFundsCollected(curTapNum) && (startedAt==0)) { + startedAt = now; + newRoadmapProposed = false; } + emit InvestEvent(_amount, _investorAddress, amountOfAllInvestments(curTapNum), tapAmountsSum(curTapNum), startedAt); } - /*function getVotingsResultForTap(uint t) public view returns(uint[]) { - uint[] memory vr = new uint[](4); - vr[0] = proj.taps[t].votings[0].pro;//votingState(t, 0, false); - vr[1] = proj.taps[t].votings[1].pro;//votingState(t, 1, false); - vr[2] = proj.taps[t].votings[2].pro;//votingState(t, 2, false); - vr[3] = proj.taps[t].votings[3].pro;//votingState(t, 3, false); - return vr; + function areAllFundsCollected(uint _tapNum) internal view returns(bool) { + return amountOfAllInvestments(_tapNum) >= tapAmountsSum(_tapNum); } - function getTaps() public view returns(uint[], uint[]) { - uint[] memory tapFunds = new uint[](getMaximalTapsLength()); - uint[] memory tapDurations = new uint[](getMaximalTapsLength()); - for(uint i = 0; i < getMaximalTapsLength(); i++) { - tapFunds[i] = proj.taps[i].funds; - tapDurations[i] = proj.taps[i].duration; - } - - return (tapFunds, tapDurations); - }*/ - - function totalInvestitions(uint _t) public view returns(uint sum) { - for(uint i = 0; i < proj.roadmaps[proj.tapToRId[_t]].investorsCount; i++) { - sum += proj.roadmaps[proj.tapToRId[_t]].investors[i].invested; + function amountOfAllInvestments(uint _tapNum) public view returns(uint sum) { + uint rmNum = tapToRId[_tapNum]; + uint invCount = roadmaps[rmNum].investorsCount; + for(uint invNum = 0; invNum < invCount; invNum++) { + sum += roadmaps[rmNum].investors[invNum].invested; } } - function tapAmountsSum(uint _t) public view returns(uint sum) { - for(uint t = 0; t < proj.roadmaps[proj.tapToRId[_t]].tapsCount; t++) { - sum += proj.roadmaps[proj.tapToRId[_t]].taps[t].funds; + function tapAmountsSum(uint _tapNum) public view returns(uint sum) { + uint rmNum = tapToRId[_tapNum]; + uint tapsCount = roadmaps[rmNum].tapsCount; + for(uint tapNum = 0; tapNum < tapsCount; tapNum++) { + sum += roadmaps[rmNum].taps[tapNum].funds; } } function returnTokens() external { - (uint t, TapStage[] memory tapStages, uint v) = getTapsInfo(); - require(tapStages[t] == TapStage.Terminated); + (uint curTapNum, TapStage[] memory tapStages, uint votNum) = getTapsInfo(); + uint rmNum = tapToRId[curTapNum]; + require(tapStages[curTapNum] == TapStage.Terminated); - uint remainder = proj.daiToken.balanceOf(address(this)); + uint remainder = daiToken.balanceOf(address(this)); uint part; - for(uint i = 0; i < proj.roadmaps[proj.tapToRId[getCurrentTap()]].investorsCount; i++) { - part = ((remainder * proj.roadmaps[proj.tapToRId[getCurrentTap()]].investors[i].invested)/totalInvestitions(t)); - proj.daiToken.transfer(proj.roadmaps[proj.tapToRId[getCurrentTap()]].investors[i].addr, part); + Investor memory investor; + + for(uint invNum = 0; invNum >> – переход на следующий tap - // • – terminate - - // | NOQ---• |15| | NOQ---• |20| | NOQ---• |24| - // | |16| |17| | |21| | - // | NOC------RM--VOT_RM----* | NOC----------VOTDQ--*| NOC---• |25| - // |02| | SUC->>> |18| | SUC->>> |22| | SUC->>> |26| - // | NOQ------VOTDQ----*| DEC---• |19| | DEC---• |23| | DEC---• |27| - // |00| |01| | - // INV--PREP--VOT-*| SUC->>> |03| - // | DEC---• |04| - // | | NOC---• |07| |08| | NOQ---• |11| - // | NOC--RM--VOT_RM---*| NOQ--------------VOTDQ-----*| NOC---• |12| - // |05| |06| | SUC->>> |09| | SUC->>> |13| - // | DEC---• |10| | DEC---• |14| - // */ + function getTap(uint _tapNum) internal view returns(Tap) { + uint rmNum = tapToRId[_tapNum]; + return roadmaps[rmNum].taps[_tapNum]; + } function getTapsInfo() public view returns(uint, TapStage[], uint) { // curren_tap, tapstages, current_voting - uint votD = proj.votingDuration; - uint addD = proj.additionalDuration; - uint invD = proj.investDuration; - uint tapD; - uint rmD = proj.changeRoadmapDuration; uint max = getMaximalTapsLength(); TapStage[] memory tapStages = new TapStage[](max); - uint tapStart = 0; - uint t; - for(t = 0; t < max; t++) { - tapD = proj.roadmaps[proj.tapToRId[t]].taps[t].duration; - if(proj.startedAt == 0) { - if(now > proj.createdAt + proj.investDuration) { - tapStages[0] = TapStage.Terminated; - return (t, tapStages, 0); - } else { - tapStages[0] = TapStage.Investing; - return (t, tapStages, 0); - } - } else if(t==0) { - tapStages[t] = TapStage.Success; - } else if(at(tapStart, tapD-votD)) { - tapStages[t] = TapStage.Preparing; - return (t, tapStages, 0); - } else if(at(tapStart+tapD-votD, votD)) { - tapStages[t] = TapStage.Voting; - return (t, tapStages, 0); - } else if(now >= proj.startedAt + tapStart+tapD) { - if(VotingResult.Decline == votingState(t, 0, false)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 0); - } else if(VotingResult.Success == votingState(t, 0, false)) { - tapStages[t] = TapStage.Success; - tapStart += tapD; - } else if(VotingResult.QuorumNotReached == votingState(t, 0, false)) { - if(at(tapStart+tapD, addD)) { - tapStages[t] = TapStage.VotingDQ; - return (t, tapStages, 1); - } else if(now >= proj.startedAt + tapStart+tapD+addD) { - if(VotingResult.QuorumNotReached == votingState(t, 1, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 1); - } else if(VotingResult.Decline == votingState(t, 1, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 1); - } else if(VotingResult.Success == votingState(t, 1, true)) { - tapStages[t] = TapStage.Success; - tapStart += (tapD+addD); - } else if(VotingResult.ConsensusNotReached == votingState(t, 1, true)) { - if(at(tapStart+tapD+addD, rmD)) { - tapStages[t] = TapStage.RoadmapPreparing; - return (t, tapStages, 2); - } else if(at(tapStart+tapD+addD+rmD, votD)) { - tapStages[t] = TapStage.RoadmapVoting; - return (t, tapStages, 2); - } else if(now >= proj.startedAt + tapStart+tapD+addD+rmD+votD) { - if(VotingResult.Success == votingState(t, 2, false)) { - if(at(tapStart+tapD+addD+rmD+votD, invD)) { // invest stage for a new roadmap - tapStages[t] = TapStage.Investing; - return (t, tapStages, 2); - } else if((now >= proj.startedAt + tapStart+tapD+addD+rmD+votD+invD) && (totalInvestitions(t) >= tapAmountsSum(t))) { - tapStages[t] = TapStage.Success; - tapStart += (tapStart+tapD+addD+rmD+votD+invD); - } else { - tapStages[t] = TapStage.Success; - return (t, tapStages, 2); - } - } else if(VotingResult.ConsensusNotReached == votingState(t, 2, false)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } else if(VotingResult.Decline == votingState(t, 2, false)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } else if(VotingResult.QuorumNotReached == votingState(t, 2, false)) { - if(at(tapStart+tapD+addD+rmD+votD, addD)) { - tapStages[t] = TapStage.RoadmapVotingDQ; - return (t, tapStages, 3); - } else if(now >= proj.startedAt + tapStart+tapD+addD+rmD+votD+addD) { - if(VotingResult.Success == votingState(t, 3, true)) { - if(at(tapStart+tapD+addD+rmD+votD+addD, invD)) { // invest stage for a new roadmap - tapStages[t] = TapStage.Investing; - return (t, tapStages, 3); - } else if((now >= proj.startedAt + tapStart+tapD+addD+rmD+votD+addD+invD) && (totalInvestitions(t) >= tapAmountsSum(t))) { - tapStages[t] = TapStage.Success; - tapStart += (tapStart+tapD+addD+rmD+votD+addD+invD); - } else { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 3); - } - } else if(VotingResult.ConsensusNotReached == votingState(t, 3, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 3); - } else if(VotingResult.Decline == votingState(t, 3, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 3); - } else if(VotingResult.QuorumNotReached == votingState(t, 3, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 3); - } - } - } - } - } - } - } else if(VotingResult.ConsensusNotReached == votingState(t, 0, false)) { - if(at(tapStart+tapD, rmD)) { - tapStages[t] = TapStage.RoadmapPreparing; - return (t, tapStages, 1); - } else if(at(tapStart+tapD+rmD, votD)) { - tapStages[t] = TapStage.RoadmapVoting; - return (t, tapStages, 1); - } else if(now >= proj.startedAt + tapStart+tapD+rmD+votD) { - if(VotingResult.Decline == votingState(t, 1, false)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 1); - } else if(VotingResult.Success == votingState(t, 1, false)) { - if(at(tapStart+tapD+rmD+votD, invD)) { // invest stage for a new roadmap - tapStages[t] = TapStage.Investing; - return (t, tapStages, 1); - } else if((now >= proj.startedAt + tapStart+tapD+rmD+votD+invD) && (totalInvestitions(t) >= tapAmountsSum(t))) { - tapStages[t] = TapStage.Success; - tapStart += (tapD+rmD+votD+invD); - } else { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 1); - } - } else if(VotingResult.ConsensusNotReached == votingState(t, 1, false)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 1); - } else if(VotingResult.QuorumNotReached == votingState(t, 1, false)) { - if(at(tapStart+tapD+rmD+votD, addD)) { - tapStages[t] = TapStage.RoadmapVotingDQ; - return (t, tapStages, 2); - } else if(now >= proj.startedAt + tapStart+tapD+rmD+votD+addD) { - if(VotingResult.Decline == votingState(t, 2, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } else if(VotingResult.Success == votingState(t, 2, true)) { - if(at(tapStart+tapD+rmD+votD+addD, invD)) { // invest stage for a new roadmap - tapStages[t] = TapStage.Investing; - return (t, tapStages, 2); - } else if((now >= proj.startedAt + tapStart+tapD+rmD+votD+addD+invD) && (totalInvestitions(t) >= tapAmountsSum(t))) { - tapStages[t] = TapStage.Success; - tapStart += (tapD+addD+rmD+votD+addD+invD); - } else { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } - } else if(VotingResult.ConsensusNotReached == votingState(t, 2, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } else if(VotingResult.QuorumNotReached == votingState(t, 2, true)) { - tapStages[t] = TapStage.Terminated; - return (t, tapStages, 2); - } - } - } - } - } - } + uint start = 0; + uint tapD; + uint votNum = 0; + + for(uint tapNum = 0; tapNum < max; tapNum++) { + tapD = getTap(tapNum).duration; + (votNum, tapStages[tapNum], start) = getTapStage(tapNum, tapD, start); + if((tapStages[tapNum]!=TapStage.Success)) return (tapNum, tapStages, votNum); } - return (t, tapStages, 0); + + return (tapNum, tapStages, votNum); + } + + // votingnum, tapstage, NewstartTime + function getTapStage(uint _tapNum, uint _tapD, uint _start) public view returns(uint, TapStage, uint) { + bool invC = areAllFundsCollected(_tapNum); + uint RmV = _start + _tapD + roadmapD + votesD; + uint addVRmV = _start + _tapD + addVotesD + roadmapD + votesD; + + if((startedAt == 0) && (now < createdAt + investD)) return (0, TapStage.Investing, 0); + if((startedAt == 0) && (now >= createdAt + investD)) return (0, TapStage.Terminated, 0); + if((_tapNum==0)) return (0, TapStage.Success, 0); + + // _tapNum _start time duration voting1 voting2 voting3 voting4 + //---------------------------------------------------------------------------------------------------------- + if(thisCase(_tapNum, _start, _tapD-votesD,VR.NoResult, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Preparing, 0); + if(thisCase(_tapNum, _start+_tapD-votesD, votesD, VR.NoResult, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Voting, 0); + if(thisCase(_tapNum, investD, infinity, VR.Decline, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, investD, infinity, VR.Success, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Success, _start + _tapD); + if(thisCase(_tapNum, _start+_tapD, addVotesD, VR.NoResult, VR.NoResult, VR.NoResult, VR.NoResult)) return (1, TapStage.VotingDQ, 0); + if(thisCase(_tapNum, _start+_tapD, infinity, VR.NoResult, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, _start+_tapD, infinity, VR.NoResult, VR.Decline, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, _start+_tapD, infinity, VR.NoResult, VR.Success, VR.NoResult, VR.NoResult)) return (0, TapStage.Success, _start + _tapD + addVotesD); + if(thisCase(_tapNum, _start+_tapD+addVotesD, roadmapD, VR.NoResult, VR.NoCons, VR.NoResult, VR.NoResult)) return (0, TapStage.RoadmapPreparing, 0); + if(thisCase(_tapNum, addVRmV-votesD, votesD, VR.NoResult, VR.NoCons, VR.NoResult, VR.NoResult)) return (2, TapStage.RoadmapVoting, 0); + if(thisCase(_tapNum, addVRmV, investD, VR.NoResult, VR.NoCons, VR.Success, VR.NoResult)) return (0, TapStage.Investing, 0); + if(thisCase(_tapNum, addVRmV, infinity, VR.NoResult, VR.NoCons, VR.Success, VR.NoResult) && invC) return (0, TapStage.Success, addVRmV + investD); + if(thisCase(_tapNum, addVRmV, infinity, VR.NoResult, VR.NoCons, VR.Success, VR.NoResult) && !invC) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, _start+_tapD+addVotesD, infinity, VR.NoResult, VR.NoCons, VR.NoCons, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, _start+_tapD+addVotesD, infinity, VR.NoResult, VR.NoCons, VR.Decline, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, addVRmV, addVotesD, VR.NoResult, VR.NoCons, VR.NoResult, VR.NoResult)) return (3, TapStage.RoadmapVotingDQ, 0); + if(thisCase(_tapNum, addVRmV+addVotesD, investD, VR.NoResult, VR.NoCons, VR.NoResult, VR.Success )) return (0, TapStage.Investing, 0); + if(thisCase(_tapNum, addVRmV+investD, infinity, VR.NoResult, VR.NoCons, VR.NoResult, VR.NoResult )) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, addVRmV+investD, infinity, VR.NoResult, VR.NoCons, VR.NoResult, VR.NoCons )) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, addVRmV+investD, infinity, VR.NoResult, VR.NoCons, VR.NoResult, VR.Decline )) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, addVRmV+addVotesD, infinity, VR.NoResult, VR.NoCons, VR.NoResult, VR.Success ) && invC) return (0, TapStage.Success, addVRmV + addVotesD + investD); + if(thisCase(_tapNum, addVRmV+addVotesD, infinity, VR.NoResult, VR.NoCons, VR.NoResult, VR.Success ) && !invC) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, _start+_tapD, roadmapD, VR.NoCons, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.RoadmapPreparing, 0); + if(thisCase(_tapNum, _start+_tapD+roadmapD, votesD, VR.NoCons, VR.NoResult, VR.NoResult, VR.NoResult)) return (1, TapStage.RoadmapVoting, 0); + if(thisCase(_tapNum, addVRmV+addVotesD+investD, infinity, VR.NoCons, VR.Decline, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, addVRmV+addVotesD+investD, infinity, VR.NoCons, VR.NoCons, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, RmV, investD, VR.NoCons, VR.Success, VR.NoResult, VR.NoResult)) return (0, TapStage.Investing, 0); + if(thisCase(_tapNum, RmV, infinity, VR.NoCons, VR.Success, VR.NoResult, VR.NoResult) && invC) return (0, TapStage.Success, RmV + investD); + if(thisCase(_tapNum, RmV, infinity, VR.NoCons, VR.Success, VR.NoResult, VR.NoResult) && !invC) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, RmV, addVotesD, VR.NoCons, VR.NoResult, VR.NoResult, VR.NoResult)) return (2, TapStage.RoadmapVotingDQ, 0); + if(thisCase(_tapNum, RmV+addVotesD, investD, VR.NoCons, VR.NoResult, VR.Success, VR.NoResult)) return (0, TapStage.Investing, 0); + if(thisCase(_tapNum, RmV+investD, infinity, VR.NoCons, VR.NoResult, VR.NoResult, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, RmV+investD, infinity, VR.NoCons, VR.NoResult, VR.Decline, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, RmV+investD, infinity, VR.NoCons, VR.NoResult, VR.NoCons, VR.NoResult)) return (0, TapStage.Terminated, 0); + if(thisCase(_tapNum, RmV+addVotesD, infinity, VR.NoCons, VR.NoCons, VR.Success, VR.NoResult) && invC) return (0, TapStage.Success, addVRmV + addVotesD + investD); + if(thisCase(_tapNum, RmV+addVotesD, infinity, VR.NoCons, VR.NoCons, VR.Success, VR.NoResult) && !invC) return (0, TapStage.Terminated, 0); + //---------------------------------------------------------------------------------------------------------- + return (0, TapStage.Preparing, 0); + } + + function thisCase(uint _tapNum, uint _from, uint _duration, + VR _votingRes1, + VR _votingRes2, + VR _votingRes3, + VR _votingRes4) public view returns(bool) + { + if(!at(_from, _duration)) return false; + + bool withLessQuorum = false; + if(_votingRes1 != votingState(_tapNum, 0, withLessQuorum)) return false; + withLessQuorum = (_votingRes1 == VR.NoResult); + + if(_votingRes2 != votingState(_tapNum, 1, withLessQuorum)) return false; + withLessQuorum = (_votingRes2 == VR.NoResult); + + if(_votingRes3 != votingState(_tapNum, 2, withLessQuorum)) return false; + withLessQuorum = (_votingRes3 == VR.NoResult); + + if(_votingRes4 != votingState(_tapNum, 3, withLessQuorum)) return false; + + return true; } function proposeNewRoadmap(uint[] _tapFunds, uint[] _tapDurations) external { - (uint t, TapStage[] memory tapStages, uint v) = getTapsInfo(); - require(tapStages[t] == TapStage.RoadmapPreparing); + (uint curTapNum, TapStage[] memory tapStages, uint votNum) = getTapsInfo(); + uint rmNum; + require(tapStages[curTapNum] == TapStage.RoadmapPreparing); require(_tapFunds.length == _tapDurations.length); - require(msg.sender == proj.owner); - require(_tapFunds.length >= proj.roadmaps[proj.roadmapsCount - 1].tapsCount); - require(!proj.rewrited); - - proj.roadmaps[proj.roadmapsCount].tapsCount = _tapFunds.length; - proj.roadmaps[proj.roadmapsCount].investorsCount = proj.roadmaps[proj.roadmapsCount - 1].investorsCount; - uint i; - - for(i = 0; i < _tapFunds.length; i++) { - require(_tapDurations[i] > 7); - if(i <= t) { - require(_tapDurations[i]*(1 days) == proj.roadmaps[proj.tapToRId[i]].taps[i].duration); - require(_tapFunds[i] == proj.roadmaps[proj.tapToRId[i]].taps[i].funds); - } else if(i > t) { - proj.tapToRId[i] = proj.roadmapsCount; // just for clearness; + require(msg.sender == owner); + require(_tapFunds.length >= roadmaps[roadmapsCount - 1].tapsCount); + require(!newRoadmapProposed); + + roadmaps[roadmapsCount].tapsCount = _tapFunds.length; + roadmaps[roadmapsCount].investorsCount = roadmaps[roadmapsCount - 1].investorsCount; + + for(uint tapFundsNum = 0; tapFundsNum < _tapFunds.length; tapFundsNum++) { + rmNum = tapToRId[tapFundsNum]; + require(_tapDurations[tapFundsNum] > 7); + if(tapFundsNum <= curTapNum) { + require(_tapDurations[tapFundsNum]*(1 days) == roadmaps[rmNum].taps[tapFundsNum].duration); + require(_tapFunds[tapFundsNum] == roadmaps[rmNum].taps[tapFundsNum].funds); + } else if(tapFundsNum > curTapNum) { + tapToRId[tapFundsNum] = roadmapsCount; // just for clearness; } - proj.roadmaps[proj.roadmapsCount].taps[i] = Tap(_tapFunds[i], _tapDurations[i]*(1 days), false); + Tap memory tap; + tap.funds = _tapFunds[tapFundsNum]; + tap.duration = _tapDurations[tapFundsNum]*(1 days); + tap.isWithdrawed = false; + roadmaps[roadmapsCount].taps[tapFundsNum] = tap; } - for(i = 0; i < proj.roadmaps[proj.roadmapsCount - 1].investorsCount; i++) { - proj.roadmaps[proj.roadmapsCount].investors[i] = proj.roadmaps[proj.roadmapsCount - 1].investors[i]; + uint invNum; + for(invNum = 0; invNum < roadmaps[roadmapsCount - 1].investorsCount; invNum++) { + roadmaps[roadmapsCount].investors[invNum] = roadmaps[roadmapsCount - 1].investors[invNum]; } - proj.roadmapsCount += 1; - proj.rewrited = true; + roadmapsCount += 1; + newRoadmapProposed = true; } function at(uint _from, uint _long) public view returns(bool) { - bool out = ((now >= _from + proj.startedAt) && (now < proj.startedAt + _from + _long)); - return out; + return ((now >= _from + startedAt) && (now < startedAt + _from + _long)); } - function isQuorumReached(uint _t, Voting memory _v, uint _quorumPercent) internal view returns(bool) { - return (_v.pro.add(_v.versus).mul(100) >= tapAmountsSum(_t).mul(_quorumPercent)); // FIX HERE: totalInvestitions change after roadmap + function isQuorumReached(uint _tapNum, Voting memory _voting, uint _quorumPercent) internal view returns(bool) { + return (_voting.pro.add(_voting.versus).mul(100) >= tapAmountsSum(_tapNum).mul(_quorumPercent)); } - function isConsensusReached(Voting memory _v, uint _consensusPercent) internal view returns(bool) { - return (_v.pro.mul(100 - _consensusPercent) >= _v.versus.mul(_consensusPercent)); + function isConsensusReached(Voting memory _voting, uint _consensusPercent) internal view returns(bool) { + return (_voting.pro.mul(100 - _consensusPercent) >= _voting.versus.mul(_consensusPercent)); } - function isDeclined(Voting memory _v, uint _declinePercent) internal view returns(bool) { - return (_v.versus.mul(100 - _declinePercent) >= _v.pro.mul(_declinePercent)); + function isDeclined(Voting memory _voting, uint _declinePercent) internal view returns(bool) { + return (_voting.versus.mul(100 - _declinePercent) >= _voting.pro.mul(_declinePercent)); } - function votingState(uint _t, uint _v, bool _isQuorumDecreased) public view returns(VotingResult) { - uint quorumPercent; - if(_isQuorumDecreased) { - quorumPercent = proj.quorumPercent - 20; - } else { - quorumPercent = proj.quorumPercent; - } - Voting memory v = proj.roadmaps[proj.tapToRId[_t]].taps[_t].votings[_v]; - if(isQuorumReached(_t, v, quorumPercent)) { - if(isConsensusReached(v, proj.consensusPercent)) { - return VotingResult.Success; - } else if(isDeclined(v, proj.declinePercent)) { - return VotingResult.Decline; - } else { - return VotingResult.ConsensusNotReached; - } - } else { - return VotingResult.QuorumNotReached; - } + function getVoting(uint _tapNum, uint _votNum) internal view returns(Voting) { + uint rmNum = tapToRId[_tapNum]; + return roadmaps[rmNum].taps[_tapNum].votings[_votNum]; + } + + function votingState(uint _tapNum, uint _votNum, bool _isQuorumDecreased) public view returns(VR) { + uint _quorumPercent = quorumPercent; + if(_isQuorumDecreased) _quorumPercent = quorumPercent - 20; + + Voting memory voting = getVoting(_tapNum, _votNum); + + if(!isQuorumReached(_tapNum, voting, _quorumPercent)) return VR.NoResult; + if(isConsensusReached(voting, consensusPercent)) return VR.Success; + if(isDeclined(voting, declinePercent)) return VR.Decline; + return VR.NoCons; } } \ No newline at end of file diff --git a/contracts/Daico/IDaico.sol b/contracts/Daico/IDaico.sol index 5bf89ce..c7717cd 100644 --- a/contracts/Daico/IDaico.sol +++ b/contracts/Daico/IDaico.sol @@ -2,4 +2,7 @@ pragma solidity ^0.4.24; contract IDaico { + function addInvestor(uint _amount, address _investorAddress) public; + function vote(bool _vote) external; + function proposeNewRoadmap(uint[] _tapFunds, uint[] _tapDurations) external; } diff --git a/contracts/Daico/STOContract.sol b/contracts/Daico/STOContract.sol new file mode 100644 index 0000000..8ee0dc0 --- /dev/null +++ b/contracts/Daico/STOContract.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.4.24; + +import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; +import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; +import "./IDaico.sol"; + + +contract STOContract is MintableToken { + address public ervercityTokenAddress; + address public daicoAddress; + + constructor(address _ervercityTokenAddress) public { + ervercityTokenAddress = _ervercityTokenAddress; + } + + function setDaicoAddress(address _daicoAddress) public onlyOwner { + daicoAddress = _daicoAddress; + } + + function invest(uint _amount) public { + ERC20(ervercityTokenAddress).transferFrom(msg.sender, address(this), _amount); + ERC20(ervercityTokenAddress).approve(daicoAddress, _amount); + + totalSupply_ = totalSupply_.add(_amount); + balances[msg.sender] = balances[msg.sender].add(_amount); + emit Mint(msg.sender, _amount); + emit Transfer(address(0), msg.sender, _amount); + + IDaico(daicoAddress).addInvestor(_amount, msg.sender); + } +} diff --git a/package-lock.json b/package-lock.json index b26fa73..361a512 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1259,10 +1259,6 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" }, - "bignumber.js": { - "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", - "from": "bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" - }, "binaryextensions": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz", @@ -4695,6 +4691,28 @@ } } }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, "loader-utils": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", @@ -5655,6 +5673,16 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -5833,6 +5861,44 @@ } } }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -6587,6 +6653,29 @@ "solidity-parser-sc": "0.4.11", "tree-kill": "^1.2.0", "web3": "^0.18.4" + }, + "dependencies": { + "bignumber.js": { + "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "web3": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.18.4.tgz", + "integrity": "sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0=", + "requires": { + "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "crypto-js": "^3.1.4", + "utf8": "^2.1.1", + "xhr2": "*", + "xmlhttprequest": "*" + } + } } }, "solidity-parser-sc": { @@ -7119,58 +7208,25 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, "truffle": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/truffle/-/truffle-4.1.13.tgz", - "integrity": "sha1-vydYaYi0/4RWPt+/MrR5QUCKdq0=", + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-4.1.15.tgz", + "integrity": "sha512-6gaNn9ZjvNjdalJMF/qEDUDoRdieKfdYcyFVpVxd+ogAta5kgJxI3XlEmS79Ih0vBhb00tKa9rBweVJ5892sYg==", "requires": { "mocha": "^4.1.0", "original-require": "1.0.1", - "solc": "0.4.24" + "solc": "0.4.25" }, "dependencies": { - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - } - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "solc": { + "version": "0.4.25", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.25.tgz", + "integrity": "sha512-jU1YygRVy6zatgXrLY2rRm7HW1d7a8CkkEgNJwvH2VLpWhMFsMdWcJn6kUqZwcSz/Vm+w89dy7Z/aB5p6AFTrg==", "requires": { - "has-flag": "^2.0.0" + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" } } } @@ -7184,6 +7240,29 @@ "ethereumjs-wallet": "^0.6.0", "web3": "^0.18.2", "web3-provider-engine": "^14.0.5" + }, + "dependencies": { + "bignumber.js": { + "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "web3": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.18.4.tgz", + "integrity": "sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0=", + "requires": { + "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "crypto-js": "^3.1.4", + "utf8": "^2.1.1", + "xhr2": "*", + "xmlhttprequest": "*" + } + } } }, "tslib": { @@ -7442,25 +7521,6 @@ } } }, - "web3": { - "version": "0.18.4", - "resolved": "https://registry.npmjs.org/web3/-/web3-0.18.4.tgz", - "integrity": "sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0=", - "requires": { - "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", - "crypto-js": "^3.1.4", - "utf8": "^2.1.1", - "xhr2": "*", - "xmlhttprequest": "*" - }, - "dependencies": { - "utf8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", - "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" - } - } - }, "web3-provider-engine": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-14.0.6.tgz", @@ -7872,6 +7932,83 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + }, + "dependencies": { + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + } + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, "yeoman-environment": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.3.0.tgz", diff --git a/package.json b/package.json index 3f49c55..1cba4a6 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "growl": "^1.10.0", "moment": "^2.22.2", "solidity-coverage": "^0.5.5", - "truffle": "^4.1.8", + "truffle": "^4.1.15", "truffle-hdwallet-provider": "0.0.5", "utf8": "^3.0.0", "zeppelin-solidity": "^1.9.0" diff --git a/scripts/test.sh b/scripts/test.sh index aa17c0f..e2bbbfe 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -11,14 +11,14 @@ cleanup() { } ganachecli_running() { - nc -z localhost 8555 + nc -z localhost 8545 } if ganachecli_running; then echo "Using existing ganache-cli instance" else echo "Starting ganache-cli" - ./node_modules/ganache-cli/build/cli.node.js --gasLimit 0xfffffffffff --defaultBalanceEther 200 --port 8555\ + ./node_modules/ganache-cli/build/cli.node.js --gasLimit 0xfffffffffff --defaultBalanceEther 200 --port 8545\ > /dev/null & ganachecli_pid=$! fi diff --git a/test/Daico.functional.tests.js b/test/Daico.functional.tests.js index a84bd5b..aff6618 100644 --- a/test/Daico.functional.tests.js +++ b/test/Daico.functional.tests.js @@ -2,6 +2,7 @@ const moment = require("moment"); const Daico = artifacts.require("Daico"); const MintableToken = artifacts.require("MintableToken"); +const STOContract = artifacts.require("STOContract"); const { increaseTime } = require("./utils/helpers"); @@ -17,6 +18,7 @@ contract("Daico functional tests", (accounts) => { const returnFunds = accounts[7]; const other = accounts[8]; + let stoContract; let daico; let daiToken; let projectToken; @@ -86,47 +88,78 @@ contract("Daico functional tests", (accounts) => { return true; } + // // 1.2. У инвесторов появляются daiToken + // await evercityToken.mint(investor1, 100, {from: evercity}); + // await evercityToken.mint(investor2, 100, {from: evercity}); + // await evercityToken.mint(investor3, 150, {from: evercity}); + + + // constructor(address _ervercityTokenAddress, address _daicoAddress) public { + // daicoAddress = _daicoAddress; + // ervercityTokenAddress = _ervercityTokenAddress; + // } + + // function invest(uint _amount, address _investorAddress) public { + // ERC20(ervercityTokenAddress).transferFrom(msg,sender, address(this), _amount); + // ERC20(ervercityTokenAddress).approve(daicoAddress, _amount); + // mint(msg.sender, _amount); + + // IDaico(daicoAddress).addInvestor(_amount, _investorAddress); + // } + + describe("Different scenarios", () => { - beforeEach(async() => { + beforeEach(async() => { // 1.1. evercityMember деплоит daiToken evercityToken = await MintableToken.new({from: evercity}); - // 1.2. У инвесторов появляются daiToken - await evercityToken.mint(investor1, 100, {from: evercity}); - await evercityToken.mint(investor2, 100, {from: evercity}); - await evercityToken.mint(investor3, 150, {from: evercity}); + stoContract = await STOContract.new(evercityToken.address, {from: projectOwner}); - var owner = projectOwner; - var daiToken = evercityToken.address; var returnAddress = evercity; var tapFunds = [100, 100, 100]; var tapDurations = [30, 30, 30]; - daico = await Daico.new(owner, daiToken, returnAddress, tapFunds, tapDurations, {from:projectOwner}); - - await evercityToken.approve(daico.address, 100, {from:investor1}); - await evercityToken.approve(daico.address, 100, {from:investor2}); - await evercityToken.approve(daico.address, 100, {from:investor3}); - - await daico.invest(100, {from:investor1}); - await daico.invest(50, {from:investor2}); - await daico.invest(50, {from:investor2}); - await daico.invest(100, {from:investor3}); - await daico.invest(50, {from:investor3}).should.be.rejectedWith('revert'); + daico = await Daico.new( + projectOwner, + evercityToken.address, + stoContract.address, + returnAddress, + tapFunds, + tapDurations, {from:projectOwner}); + + await stoContract.setDaicoAddress(daico.address, {from:projectOwner}); + + await evercityToken.mint(investor1, 100, {from:evercity}); + await evercityToken.mint(investor2, 100, {from:evercity}); + await evercityToken.mint(investor3, 100, {from:evercity}); + + await evercityToken.approve(stoContract.address, 100, {from:investor1}); + await evercityToken.approve(stoContract.address, 100, {from:investor2}); + await evercityToken.approve(stoContract.address, 100, {from:investor3}); + + // console.log('======= before all:\n', await getTapsInfo(),'\n') + await stoContract.invest(100, {from:investor1}); + await stoContract.invest(50, {from:investor2}); + await stoContract.invest(50, {from:investor2}); + await stoContract.invest(100, {from:investor3}); + console.log('======= after investing:\n', await getTapsInfo(),'\n') + await stoContract.invest(50, {from:investor3}).should.be.rejectedWith('revert'); }); it(`1. Сценарий: все голосования происходят вовремя и без задержек, все голоса за`, async() => { await daico.withdrawFundsFromTap(0, {from:projectOwner}); - await increaseTime(23*days); - // Голосование началось + await increaseTime(23*days); + console.log('======= before 1 voting:\n', await getTapsInfo(),'\n') await daico.vote(true, {from: investor1}); await daico.vote(true, {from: investor2}); await daico.vote(true, {from: investor3}); await increaseTime(7*days); // голосование кончилось + console.log('======= after 1 voting:\n', await getTapsInfo(),'\n') await daico.withdrawFundsFromTap(1, {from:projectOwner}); await increaseTime(23*days); // Голосование началось + await daico.vote(true, {from: investor1}); await daico.vote(true, {from: investor2}); await daico.vote(true, {from: investor3}); @@ -203,16 +236,16 @@ contract("Daico functional tests", (accounts) => { await evercityToken.mint(investor2, 50, {from: evercity}); await evercityToken.mint(investor3, 50, {from: evercity}); - await evercityToken.approve(daico.address, 50, {from:investor1}); - await evercityToken.approve(daico.address, 50, {from:investor2}); - await evercityToken.approve(daico.address, 50, {from:investor3}); + await evercityToken.approve(stoContract.address, 50, {from:investor1}); + await evercityToken.approve(stoContract.address, 50, {from:investor2}); + await evercityToken.approve(stoContract.address, 50, {from:investor3}); - await daico.invest(50, {from:investor1}); + await stoContract.invest(50, {from:investor1}); assert.equal((await getTapsInfo()).tapsStages.toString(), [TS.Success, TS.Investing, TS.Preparing, TS.Preparing].toString()) - await daico.invest(50, {from:investor2}); + await stoContract.invest(50, {from:investor2}); assert.equal((await getTapsInfo()).tapsStages.toString(), [TS.Success, TS.Investing, TS.Preparing, TS.Preparing].toString()) - await daico.invest(50, {from:investor3}); + await stoContract.invest(50, {from:investor3}); await increaseTime(7*days); assert.equal((await getTapsInfo()).tapsStages.toString(), [TS.Success, TS.Success, TS.Preparing, TS.Preparing].toString()) @@ -235,13 +268,3 @@ contract("Daico functional tests", (accounts) => { }); }); }); - - - - - - - - - - diff --git a/test/utils/helpers.js b/test/utils/helpers.js index 461b37a..64899ee 100644 --- a/test/utils/helpers.js +++ b/test/utils/helpers.js @@ -7,30 +7,22 @@ const utf8 = require('utf8'); /** * Increases latest block time by duration in seconds */ -const increaseTime = function increaseTime (duration) { - - const id = Date.now(); - - return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync({ - jsonrpc: '2.0', - method: 'evm_increaseTime', - params: [duration], - id: id, - }, err1 => { - if (err1) return reject(err1); - - web3.currentProvider.sendAsync({ - jsonrpc: '2.0', - method: 'evm_mine', - id: id + 1, - }, (err2, res) => { - return err2 ? reject(err2) : resolve(res); - }); - }); - }); +async function increaseTime (duration) { + var id = Date.now(); + await web3.currentProvider.sendAsync({ + jsonrpc: '2.0', + method: 'evm_increaseTime', + params: [duration], + id: id + }, function (err) { if (err) console.log('err:', err); }); + await web3.currentProvider.sendAsync({ + jsonrpc: '2.0', + method: 'evm_mine', + id: id + 1 + }, function (err) { if (err) console.log('err:', err); }); } + function uintToBytes32(n) { n = Number(n).toString(16); while (n.length < 64) { diff --git a/truffle.js b/truffle.js index 658c81e..f998f0c 100644 --- a/truffle.js +++ b/truffle.js @@ -9,7 +9,7 @@ module.exports = { networks: { development: { host: "localhost", - port: 8555, + port: 8545, network_id: "*", gas: 100000000 },