diff --git a/contracts/DaoBase.sol b/contracts/DaoBase.sol index d738afc..9ba09e5 100644 --- a/contracts/DaoBase.sol +++ b/contracts/DaoBase.sol @@ -55,20 +55,16 @@ contract DaoBase is IDaoBase, Ownable { } modifier isCanDo(bytes32 _what){ - require(_isCanDoAction(msg.sender,_what)); + require(isCanDoAction(msg.sender,_what)); _; } // IDaoBase: - function addObserver(IDaoObserver _observer) external { + function addObserver(IDaoObserver _observer) public { store.addObserver(_observer); } - function upgradeDaoContract(IDaoBase _new) external isCanDo(UPGRADE_DAO_CONTRACT) { - _upgradeDaoContract(_new); - } - - function _upgradeDaoContract(IDaoBase _new) internal{ + function upgradeDaoContract(IDaoBase _new) public isCanDo(UPGRADE_DAO_CONTRACT) { emit DaoBase_UpgradeDaoContract(_new); // call observers.onUpgrade() for all observers for(uint i=0; i allow * 4. deny */ - function isCanDoAction(address _a, bytes32 _permissionNameHash) external constant returns(bool){ - return _isCanDoAction(_a, _permissionNameHash); - } - - function _isCanDoAction(address _a, bytes32 _permissionNameHash) internal constant returns(bool){ + function isCanDoAction(address _a, bytes32 _permissionNameHash) public constant returns(bool){ // 0 - is can do by address? if(store.isCanDoByAddress(_permissionNameHash, _a)){ return true; @@ -188,25 +180,21 @@ contract DaoBase is IDaoBase, Ownable { } // Proposals: - function addNewProposal(IProposal _proposal) external isCanDo(ADD_NEW_PROPOSAL) { + function addNewProposal(IProposal _proposal) public isCanDo(ADD_NEW_PROPOSAL) { emit DaoBase_AddNewProposal(address(_proposal)); store.addNewProposal(_proposal); } - function getProposalAtIndex(uint _i)external constant returns(IProposal){ + function getProposalAtIndex(uint _i)public constant returns(IProposal){ return store.getProposalAtIndex(_i); } - function getProposalsCount()external constant returns(uint){ + function getProposalsCount()public constant returns(uint){ return store.getProposalsCount(); } // Tokens: - function issueTokens(address _tokenAddress, address _to, uint _amount)external isCanDo(ISSUE_TOKENS) { - _issueTokens(_tokenAddress,_to,_amount); - } - - function _issueTokens(address _tokenAddress, address _to, uint _amount)internal{ + function issueTokens(address _tokenAddress, address _to, uint _amount)public isCanDo(ISSUE_TOKENS) { emit DaoBase_IssueTokens(_tokenAddress, _to, _amount); for(uint i=0; i if this voting type DOES NOT use tokens -> set to any value (e.g., 0); // will execute action automatically if the voting is finished - function vote(bool _yes, uint _tokenAmount) external; + function vote(bool _yes, uint _tokenAmount) public; // stop the voting - function cancelVoting() external; + function cancelVoting() public; // This is for statistics // Can get this stats if voting is finished. // Can get this stats if voting is NOT finished. - function getVotingStats() external view returns(uint yesResults, uint noResults, uint totalResults); + function getVotingStats() public view returns(uint yesResults, uint noResults, uint totalResults); // Is voting finished? // @@ -29,7 +29,7 @@ interface IVoting { // When isFinished(): // 1 - i can not vote any more // 2 - i can get results with isYes() - function isFinished()external view returns(bool); + function isFinished()public view returns(bool); // The result of voting // @@ -38,12 +38,13 @@ interface IVoting { // 2 - all these conditions should be met: // 2.1 - isFinished() // 2.2 - is quorum reached - function isYes()external view returns(bool); + function isYes()public view returns(bool); } // for "liquid democracy" // in this case the delegate does all voting -interface IDelegationTable { + +contract IDelegationTable { function delegateMyVoiceTo(address _to, uint _tokenAmount) public; function removeDelegation(address _to) public; } diff --git a/contracts/governance/Proposals.sol b/contracts/governance/Proposals.sol index 732b207..ced2d6c 100644 --- a/contracts/governance/Proposals.sol +++ b/contracts/governance/Proposals.sol @@ -31,7 +31,7 @@ contract GenericProposal is IProposal, Ownable { event GenericProposal_Action(IVoting _voting); // IVoting implementation - function action() external { + function action() public { emit GenericProposal_Action(voting); // in some cases voting is still not set @@ -48,15 +48,15 @@ contract GenericProposal is IProposal, Ownable { uint256(params.length), // length of the array params) ){ - revert(); + //revert(); } } - function setVoting(IVoting _voting) external onlyOwner{ + function setVoting(IVoting _voting) public onlyOwner{ voting = _voting; } - function getVoting()external view returns(IVoting){ + function getVoting() public view returns(IVoting){ return voting; } } @@ -76,16 +76,16 @@ contract InformalProposal is IProposal, Ownable { proposalText = _proposalText; } - function getProposalText()external view returns(string){ + function getProposalText() public view returns(string){ return proposalText; } // IVoting implementation - function setVoting(IVoting _voting) external onlyOwner{ + function setVoting(IVoting _voting) public onlyOwner{ voting = _voting; } - function getVoting()external view returns(IVoting){ + function getVoting() public view returns(IVoting){ return voting; } diff --git a/contracts/governance/Voting_1p1v.sol b/contracts/governance/Voting_1p1v.sol index 924d56d..534e0c6 100644 --- a/contracts/governance/Voting_1p1v.sol +++ b/contracts/governance/Voting_1p1v.sol @@ -61,11 +61,7 @@ contract Voting_1p1v is IVoting, Ownable { internalVote(_origin, true); } - function isFinished()external view returns(bool){ - return _isFinished(); - } - - function _isFinished()internal view returns(bool){ + function isFinished() public view returns(bool){ if(minutesToVote!=0){ return _isTimeElapsed(); } @@ -90,7 +86,7 @@ contract Voting_1p1v is IVoting, Ownable { uint noResults = 0; uint votersTotal = 0; - (yesResults, noResults, votersTotal) = _getVotingStats(); + (yesResults, noResults, votersTotal) = getVotingStats(); return ((yesResults + noResults) * 100) >= (votersTotal * quorumPercent); } @@ -99,30 +95,26 @@ contract Voting_1p1v is IVoting, Ownable { uint noResults = 0; uint votersTotal = 0; - (yesResults, noResults, votersTotal) = _getVotingStats(); + (yesResults, noResults, votersTotal) = getVotingStats(); return (yesResults * 100) >= ((yesResults + noResults) * consensusPercent); } - function isYes()external view returns(bool){ - return _isYes(); - } - - function _isYes()internal view returns(bool){ + function isYes() public view returns(bool){ if(true==finishedWithYes){ return true; } - return _isFinished() && + return isFinished() && _isQuorumReached() && _isConsensusReached(); } - function cancelVoting() external onlyOwner { + function cancelVoting() public onlyOwner { // TODO: } - function vote(bool _yes, uint _tokenAmount) external { - require(!_isFinished()); + function vote(bool _yes, uint _tokenAmount) public { + require(!isFinished()); // This voting type does not deal with tokens, that's why _tokenAmount should be ZERO require(0==_tokenAmount); @@ -142,15 +134,11 @@ contract Voting_1p1v is IVoting, Ownable { addressVotedAlready[_who] = true; emit Voting1p1v_Vote(_who, _yes); - _callActionIfEnded(); + callActionIfEnded(); } - function callActionIfEnded() external { - _callActionIfEnded(); - } - - function _callActionIfEnded() internal { - if(!finishedWithYes && _isFinished() && _isYes()){ + function callActionIfEnded() public { + if(!finishedWithYes && isFinished() && isYes()){ // should not be callable again!!! finishedWithYes = true; @@ -172,14 +160,11 @@ contract Voting_1p1v is IVoting, Ownable { return votedCount; } - function getVotingStats() external constant returns(uint yesResults, uint noResults, uint votersTotal){ - return _getVotingStats(); - } - - function _getVotingStats() internal constant returns(uint yesResults, uint noResults, uint votersTotal){ + function getVotingStats() public constant returns(uint yesResults, uint noResults, uint votersTotal){ yesResults = filterResults(employeesVotedYes); noResults = filterResults(employeesVotedNo); votersTotal = dao.getMembersCount(groupName); return; } + } diff --git a/contracts/governance/Voting_SimpleToken.sol b/contracts/governance/Voting_SimpleToken.sol index 2c930c4..a3f37f4 100644 --- a/contracts/governance/Voting_SimpleToken.sol +++ b/contracts/governance/Voting_SimpleToken.sol @@ -73,11 +73,7 @@ contract Voting_SimpleToken is IVoting, Ownable { internalVote(_origin, true); } - function isFinished()external view returns(bool){ - return _isFinished(); - } - - function _isFinished()internal view returns(bool){ + function isFinished()public view returns(bool){ if(minutesToVote!=0){ return _isTimeElapsed(); } @@ -102,7 +98,7 @@ contract Voting_SimpleToken is IVoting, Ownable { uint noResults = 0; uint votersTotal = 0; - (yesResults, noResults, votersTotal) = _getVotingStats(); + (yesResults, noResults, votersTotal) = getVotingStats(); return ((yesResults + noResults) * 100) >= (votersTotal * quorumPercent); } @@ -111,30 +107,26 @@ contract Voting_SimpleToken is IVoting, Ownable { uint noResults = 0; uint votersTotal = 0; - (yesResults, noResults, votersTotal) = _getVotingStats(); + (yesResults, noResults, votersTotal) = getVotingStats(); return (yesResults * 100) >= ((yesResults + noResults) * consensusPercent); } - function isYes()external view returns(bool){ - return _isYes(); - } - - function _isYes()internal view returns(bool){ + function isYes()public view returns(bool){ if(true==finishedWithYes){ return true; } - return _isFinished() && + return isFinished() && _isQuorumReached() && _isConsensusReached(); } - function cancelVoting() external onlyOwner { + function cancelVoting() public onlyOwner { // TODO: } - function vote(bool _yes, uint _tokenAmount) external { - require(!_isFinished()); + function vote(bool _yes, uint _tokenAmount) public { + require(!isFinished()); require(0==_tokenAmount); internalVote(msg.sender, _yes); @@ -150,11 +142,10 @@ contract Voting_SimpleToken is IVoting, Ownable { emit VotingSimpleToken_Vote(_who, _yes, tokenBalance); - _callActionIfEnded(); } function callActionIfEnded() public { - if(!finishedWithYes && _isFinished() && _isYes()){ + if(!finishedWithYes && isFinished() && isYes()){ // should not be callable again!!! finishedWithYes = true; @@ -164,11 +155,7 @@ contract Voting_SimpleToken is IVoting, Ownable { } } - function getVotingStats() external constant returns(uint yesResults, uint noResults, uint votersTotal){ - return _getVotingStats(); - } - - function _getVotingStats() internal constant returns(uint yesResults, uint noResults, uint votersTotal){ + function getVotingStats() public constant returns(uint yesResults, uint noResults, uint votersTotal){ yesResults = 0; noResults = 0; if(isQuadraticVoting){ diff --git a/contracts/moneyflow/IMoneyflow.sol b/contracts/moneyflow/IMoneyflow.sol index 41b3456..0bf8898 100644 --- a/contracts/moneyflow/IMoneyflow.sol +++ b/contracts/moneyflow/IMoneyflow.sol @@ -4,46 +4,46 @@ pragma solidity ^0.4.22; * @title IDestination * @dev Keeps all money until flush is called */ -interface IDestination { +contract IDestination { // pull model - function flush() external; - function flushTo(address _to) external; + function flush() public; + function flushTo(address _to) public; } /** * @title ISplitter * @dev does not store funds. Splits them between the children */ -interface ISplitter { - function getChildrenCount()external view returns(uint); - function getChild(uint _index)external view returns(address); - function addChild(address _newChild) external; - - function open() external; - function close() external; - function isOpen() external view returns(bool); +contract ISplitter { + function getChildrenCount()public view returns(uint); + function getChild(uint _index)public view returns(address); + function addChild(address _newChild) public; + + function open() public; + function close() public; + function isOpen() public view returns(bool); } /** * @title IReceiver * @dev Something that needs funds */ -interface IReceiver { +contract IReceiver { // In case we have absolute output -> will return 0 // in 1/100th percents of input. Examples: // 12 is 0.12% of input; // 100 is 1% of input - function getPercentsMul100()constant external returns(uint); + function getPercentsMul100()constant public returns(uint); // If this output needs more funds -> will return true // If this output does not need more funds -> will return false - function isNeedsMoney()constant external returns(bool); + function isNeedsMoney()constant public returns(bool); // WeiReceiver should process all tokens here (hold it or send it somewhere else) - function processFunds(uint _currentFlow) external payable; + function processFunds(uint _currentFlow) public payable; // non payable! - function()external; + function()public; } // IWeiReceiver does not store funds! @@ -53,11 +53,11 @@ interface IReceiver { // "Relative": percents of input contract IWeiReceiver is IReceiver { // Will calculate only absolute outputs, but not take into account the Percents - function getMinWeiNeeded()external view returns(uint); + function getMinWeiNeeded()public view returns(uint); // In case we have absolute output -> will return fixed amount that is equal to 'getMinWeiNeeded' // In case we have relative output -> will calculate percents of _inputWei - function getTotalWeiNeeded(uint _inputWei)external view returns(uint); + function getTotalWeiNeeded(uint _inputWei)public view returns(uint); } // IErc20Receiver does not store funds! @@ -67,32 +67,32 @@ contract IWeiReceiver is IReceiver { // "Relative": percents of input contract IErc20Receiver is IReceiver { // Will calculate only absolute outputs, but not take into account the Percents - function getMinTokensNeeded()external view returns(uint); + function getMinTokensNeeded()public view returns(uint); // In case we have absolute output -> will return fixed amount that is equal to 'getMinTokensNeeded' // In case we have relative output -> will calculate percents of _inputWei - function getTotalTokensNeeded(uint _inputTokens)external view returns(uint); + function getTotalTokensNeeded(uint _inputTokens)public view returns(uint); } /** * @title Moneyflow */ -interface IMoneyflow { +contract IMoneyflow { // send Ether using 'sendFunds' method here - function getRevenueEndpoint()external view returns(IWeiReceiver); - function getDonationEndpoint()external view returns(IWeiReceiver); + function getRevenueEndpoint()public view returns(IWeiReceiver); + function getDonationEndpoint()public view returns(IWeiReceiver); // send Ether using default fallback functions here - function getRevenueEndpointAddress()external view returns(address); - function getDonationEndpointAddress()external view returns(address); + function getRevenueEndpointAddress()public view returns(address); + function getDonationEndpointAddress()public view returns(address); // send all donations to the msg.sender (onlyOwner of this contract) - function withdrawDonationsTo(address _out)external; + function withdrawDonationsTo(address _out)public; // Receivers // usually _receiver is a MoneyFlowScheme // see Schemes.sol for example - function setRootWeiReceiver(IWeiReceiver _receiver) external; + function setRootWeiReceiver(IWeiReceiver _receiver) public; } diff --git a/contracts/moneyflow/Moneyflow.sol b/contracts/moneyflow/Moneyflow.sol index 21b74f7..7ffb92f 100644 --- a/contracts/moneyflow/Moneyflow.sol +++ b/contracts/moneyflow/Moneyflow.sol @@ -2,6 +2,7 @@ pragma solidity ^0.4.22; import "./IMoneyflow.sol"; import "./ether/WeiFund.sol"; +import "./ether/WeiExpense.sol"; import "../IDaoBase.sol"; @@ -34,7 +35,7 @@ contract FallbackToWeiReceiver { * 'setRootWeiReceiverGeneric', etc */ contract MoneyFlow is IMoneyflow, DaoClient, Ownable { - WeiFund donationEndpoint; + WeiRelativeExpenseWithPeriod donationEndpoint; // by default - this is 0x0, please use setWeiReceiver method // this can be a ISplitter (top-down or unsorted) IWeiReceiver rootReceiver; @@ -52,55 +53,47 @@ contract MoneyFlow is IMoneyflow, DaoClient, Ownable { DaoClient(_dao) { // do not set output! - donationEndpoint = new WeiFund(0x0, true, 10000); + donationEndpoint = new WeiRelativeExpenseWithPeriod(10000, 0, false); donationF2WR = new FallbackToWeiReceiver(donationEndpoint); } // IMoneyflow: // will withdraw donations - function withdrawDonationsTo(address _out) external isCanDo(WITHDRAW_DONATIONS){ - _withdrawDonationsTo(_out); - } - - function _withdrawDonationsTo(address _out) internal{ + function withdrawDonationsTo(address _out) public isCanDo(WITHDRAW_DONATIONS){ emit MoneyFlow_WithdrawDonations(msg.sender, _out, address(donationEndpoint).balance); donationEndpoint.flushTo(_out); } - function getDonationEndpoint()external constant returns(IWeiReceiver){ + function getDonationEndpoint()public constant returns(IWeiReceiver){ return donationEndpoint; } - function getRevenueEndpoint()external constant returns(IWeiReceiver){ + function getRevenueEndpoint()public constant returns(IWeiReceiver){ return rootReceiver; } - function getDonationEndpointAddress()external constant returns(address){ + function getDonationEndpointAddress()public constant returns(address){ return address(donationF2WR); } - function getRevenueEndpointAddress()external constant returns(address){ + function getRevenueEndpointAddress()public constant returns(address){ return address(revenueF2WR); } - function setRootWeiReceiverGeneric(bytes32[] _params) external { + function setRootWeiReceiverGeneric(bytes32[] _params) public { IWeiReceiver receiver = IWeiReceiver(address(_params[0])); - _setRootWeiReceiver(receiver); + setRootWeiReceiver(receiver); } - function withdrawDonationsToGeneric(bytes32[] _params) external { + function withdrawDonationsToGeneric(bytes32[] _params) public { address out = address(_params[0]); - _withdrawDonationsTo(out); + withdrawDonationsTo(out); } // WeiReceivers: // receiver can be a splitter, fund or event task // _receiver can be 0x0! - function setRootWeiReceiver(IWeiReceiver _receiver) external isCanDo(SET_ROOT_WEI_RECEIVER){ - _setRootWeiReceiver(_receiver); - } - - function _setRootWeiReceiver(IWeiReceiver _receiver) internal{ + function setRootWeiReceiver(IWeiReceiver _receiver) public isCanDo(SET_ROOT_WEI_RECEIVER){ emit MoneyFlow_SetRootWeiReceiver(msg.sender, address(_receiver)); rootReceiver = _receiver; revenueF2WR = new FallbackToWeiReceiver(address(rootReceiver)); diff --git a/contracts/moneyflow/Schemes.sol b/contracts/moneyflow/Schemes.sol index c6a5640..3d7c6e1 100644 --- a/contracts/moneyflow/Schemes.sol +++ b/contracts/moneyflow/Schemes.sol @@ -37,8 +37,8 @@ contract DefaultMoneyflowScheme is DaoClient { WeiUnsortedSplitter other; WeiUnsortedSplitter tasks; - WeiFund reserveFund; - WeiFund dividendsFund; + WeiRelativeExpenseWithPeriod reserveFund; + WeiRelativeExpenseWithPeriod dividendsFund; bytes32 public ADD_NEW_TASK = keccak256(abi.encodePacked("addNewTask")); bytes32 public MODIFY_MONEY_SCHEME = keccak256(abi.encodePacked("modifyMoneyscheme")); diff --git a/contracts/moneyflow/ether/WeiExpense.sol b/contracts/moneyflow/ether/WeiExpense.sol index 6e073f8..64026ca 100644 --- a/contracts/moneyflow/ether/WeiExpense.sol +++ b/contracts/moneyflow/ether/WeiExpense.sol @@ -41,15 +41,11 @@ contract WeiExpense is IWeiReceiver, IDestination, Ownable { isPeriodic = _isPeriodic; } - function processFunds(uint _currentFlow) external payable{ + function processFunds(uint _currentFlow) public payable{ emit WeiExpense_ProcessFunds(msg.sender, msg.value, _currentFlow); - _processFunds(_currentFlow); - } - - function _processFunds(uint _currentFlow) internal{ - require(_isNeedsMoney()); + require(isNeedsMoney()); - require(msg.value == _getTotalWeiNeeded(_currentFlow)); + require(msg.value == getTotalWeiNeeded(_currentFlow)); // TODO: why not working without if???? if(isPeriodic){ @@ -60,50 +56,38 @@ contract WeiExpense is IWeiReceiver, IDestination, Ownable { moneySource = msg.sender; } - function getIsMoneyReceived() external view returns(bool){ + function getIsMoneyReceived() public view returns(bool){ return isMoneyReceived; } - function getNeededWei() external view returns(uint){ + function getNeededWei() public view returns(uint){ return neededWei; } - function getTotalWeiNeeded(uint _inputWei)external view returns(uint){ - return _getTotalWeiNeeded(_inputWei); - } - - function _getTotalWeiNeeded(uint _inputWei)internal view returns(uint){ - if(!_isNeedsMoney()){ + function getTotalWeiNeeded(uint _inputWei)public view returns(uint){ + if(!isNeedsMoney()){ return 0; } if(0!=percentsMul100){ - return (_getDebtMultiplier()*(percentsMul100 * _inputWei)) / 10000; + return (getDebtMultiplier()*(percentsMul100 * _inputWei)) / 10000; }else{ - return _getMinWeiNeeded(); + return getMinWeiNeeded(); } } - function getMinWeiNeeded()external view returns(uint){ - return _getMinWeiNeeded(); - } - - function _getMinWeiNeeded()internal view returns(uint){ - if(!_isNeedsMoney() || (0!=percentsMul100)){ + function getMinWeiNeeded()public view returns(uint){ + if(!isNeedsMoney() || (0!=percentsMul100)){ return 0; } - return _getDebtMultiplier()*neededWei; + return getDebtMultiplier()*neededWei; } - function getMomentReceived()external view returns(uint){ + function getMomentReceived()public view returns(uint){ return momentReceived; } - function getDebtMultiplier()external view returns(uint){ - return _getDebtMultiplier(); - } - - function _getDebtMultiplier()internal view returns(uint){ + function getDebtMultiplier()public view returns(uint){ if((isAccumulateDebt)&&(0!=momentReceived)){ return ((now - momentReceived) / (periodHours * 3600 * 1000)); } else{ @@ -111,11 +95,7 @@ contract WeiExpense is IWeiReceiver, IDestination, Ownable { } } - function isNeedsMoney()external view returns(bool){ - return _isNeedsMoney(); - } - - function _isNeedsMoney()internal view returns(bool){ + function isNeedsMoney()public view returns(bool){ if(isPeriodic){ // For period Weiexpense if ((uint64(now) - momentReceived) >= periodHours * 3600 * 1000){ return true; @@ -130,30 +110,31 @@ contract WeiExpense is IWeiReceiver, IDestination, Ownable { _; } - function getPercentsMul100()external view returns(uint){ + function getPercentsMul100()public view returns(uint){ return percentsMul100; } // TODO: remove from here - function getNow()external view returns(uint){ + function getNow()public view returns(uint){ return now; } - function flush()external onlyOwner{ + function flush()public onlyOwner{ emit WeiExpense_Flush(owner, address(this).balance); owner.transfer(address(this).balance); } - function flushTo(address _to) external onlyOwner { - if(_to==_to) revert(); + function flushTo(address _to) public onlyOwner { + emit WeiExpense_Flush(_to, address(this).balance); + _to.transfer(address(this).balance); } - function setNeededWei(uint _neededWei) external onlyOwner { + function setNeededWei(uint _neededWei) public onlyOwner { emit WeiExpense_SetNeededWei(_neededWei); neededWei = _neededWei; } - function setPercents(uint _percentsMul100) external onlyOwner { + function setPercents(uint _percentsMul100) public onlyOwner { emit WeiExpense_SetPercents(_percentsMul100); percentsMul100 = _percentsMul100; } diff --git a/contracts/moneyflow/ether/WeiFund.sol b/contracts/moneyflow/ether/WeiFund.sol index f941494..423efcc 100644 --- a/contracts/moneyflow/ether/WeiFund.sol +++ b/contracts/moneyflow/ether/WeiFund.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.15; +pragma solidity ^0.4.21; import "../IMoneyflow.sol"; @@ -12,143 +12,103 @@ import "zeppelin-solidity/contracts/ownership/Ownable.sol"; * @dev WeiFund stores funds until 'flush' is called (pull model) * This is a terminal item, that has no children. */ -contract WeiFund is WeiRelativeExpense { - address public output; // will not be able to change that later! - bool public allowFlushTo = true; - event WeiFund_FlushTo(address _to, uint _balance); - event WeiFund_Flush(address _to, uint _balance); - - constructor(address _output, bool _allowFlushTo, uint _percentsDiv100Needed) public - WeiRelativeExpense(_percentsDiv100Needed) - { - output = _output; - allowFlushTo = _allowFlushTo; - } - - // Process funds, send it to the Output - function flushTo(address _to) external onlyOwner { - require(allowFlushTo); // this operation can be prohibited - emit WeiFund_FlushTo(_to, address(this).balance); - _to.transfer(address(this).balance); - } - - // Process funds, send it to the Output - function flush() external onlyOwner { - require(0x0!=output); +contract WeiFund is IWeiReceiver, IDestination, Ownable {// + using SafeMath for uint; - // TODO: check for vulnerabilities - isMoneyReceived = false; - emit WeiFund_FlushTo(output, address(this).balance); - output.transfer(address(this).balance); - } - - function isNeedsMoney()external view returns(bool){ - // fund always needs money! - return true; - } -} + uint neededWei; + uint totalWeiReceived; + uint momentReceived; + uint balanceOnMomentReceived; + uint momentCreated; -/*contract ConditionalFund is WeiExpense { - address output; // will not be able to change that later! - address nextTargetOutput; - bool public allowFlushTo = true; - bool isAutoWithdraw; bool isPeriodic; - + bool isAccumulateDebt; uint periodHours; - uint neededAmount; - - uint momentReceived; - uint balanceOnMomentReceived; event WeiFund_FlushTo(address _to, uint _balance); - event WeiFund_Flush(address _to, uint _balance); - - constructor(uint _neededAmount, address _output, bool _isAutoWithdraw, address _nextTargetOutput, bool _allowFlushTo, bool _isPeriodic, uint _periodHours) public - WeiExpense(_neededAmount, 0, _periodHours, false, _isPeriodic) - { - output = _output; - allowFlushTo = _allowFlushTo; - isAutoWithdraw = _isAutoWithdraw; - nextTargetOutput = _nextTargetOutput; + + constructor(uint _neededWei, bool _isPeriodic, bool _isAccumulateDebt, uint _periodHours) public { + require(!((_isAccumulateDebt)&&(_periodHours==0))); + require(!(!(_isPeriodic)&&(_periodHours!=0))); + require(!((_isAccumulateDebt)&&(!_isPeriodic))); + require(_neededWei!=0); + neededWei = _neededWei; isPeriodic = _isPeriodic; + isAccumulateDebt = _isAccumulateDebt; periodHours = _periodHours; - neededAmount = _neededAmount; + momentCreated = now; } - // Process funds, send it to the Output - function flushTo(address _to) external onlyOwner { - require(allowFlushTo); // this operation can be prohibited - emit WeiFund_FlushTo(_to, address(this).balance); - _to.transfer(address(this).balance); + function getTotalFundNeededAmount()public view returns(uint){ + return neededWei; } - // Process funds, send it to the Output - function flush() external onlyOwner { - // TODO: check for vulnerabilities - // isMoneyReceived = false; - emit WeiFund_FlushTo(output, address(this).balance); - output.transfer(address(this).balance); - } - - function getTotalWeiNeeded()external view returns(uint){ - return _getTotalWeiNeeded(this.balance); + function getDebtMultiplier()public view returns(uint){ + if((isPeriodic)&&(!isAccumulateDebt)&&( (now - momentReceived) / (periodHours * 3600 * 1000) >=1)){ + return (balanceOnMomentReceived/neededWei) + 1; + } else if((isPeriodic)&&(isAccumulateDebt)){ + return 1 + ((now - momentCreated) / (periodHours * 3600 * 1000)); + }else{ + return 1; + } } - function _getTotalWeiNeeded(uint balance)internal view returns(uint){ - uint need = 0; + // -------------- IWeiReceiver - if((isPeriodic) && (now-momentReceived totalWeiReceived){ + need = getDebtMultiplier()*neededWei - totalWeiReceived; + }else{ need = 0; + } - }else if((neededAmount >= balance)&&(!isPeriodic)){ - need = neededAmount - balance; - - }else if((isPeriodic) && (now-momentReceived>periodHours*3600*1000) && (momentReceived!=0)){ - need = _getDebtMultiplier()*neededAmount + balanceOnMomentReceived - balance; - + if(need<=_inputWei){ + return need; }else{ - need = 0; + return _inputWei; } + } - return need; + function getMinWeiNeeded()public view returns(uint){ + return 0; } - function _processFunds(uint _currentFlow) internal{ - uint rest = 0; - uint selfNeed = _getTotalWeiNeeded(this.balance - _currentFlow); + function getPercentsMul100() view public returns(uint){ + return 0; + } - if(_currentFlow>selfNeed){ - rest = _currentFlow - selfNeed; - } + function isNeedsMoney()public view returns(bool){ + return getDebtMultiplier()*neededWei > totalWeiReceived; + } - if(((0==momentReceived) && (_currentFlow>=selfNeed)) || - ((_currentFlow>=selfNeed)&&(isPeriodic))){ - momentReceived = now; - balanceOnMomentReceived = this.balance; - } + // -------------- IDestination - if((rest>0)||(_getTotalWeiNeeded(this.balance)==0)){ - if(isAutoWithdraw){ - output.transfer(this.balance); - }else{ - IWeiReceiver(nextTargetOutput).processFunds.value(rest)(rest); - } - } + function flushTo(address _to) public onlyOwner { + emit WeiFund_FlushTo(_to, this.balance); + _to.transfer(this.balance); } - function _getMinWeiNeeded()internal view returns(uint){ - return 0; - } + function flush() public onlyOwner { + emit WeiFund_FlushTo(owner, this.balance); + owner.transfer(this.balance); + } + + function() public{ - function isNeedsMoney()external view returns(bool){ // fund always needs money! - return true; } -}*/ \ No newline at end of file +} diff --git a/contracts/moneyflow/ether/WeiSplitter.sol b/contracts/moneyflow/ether/WeiSplitter.sol index ee3377e..94d6a30 100644 --- a/contracts/moneyflow/ether/WeiSplitter.sol +++ b/contracts/moneyflow/ether/WeiSplitter.sol @@ -26,32 +26,30 @@ contract SplitterBase is ISplitter, Ownable { name = _name; } - function _isOpen() internal view returns(bool){ - return opened; - } - // ISplitter: - function open() external onlyOwner{ + function open() public onlyOwner{ emit SplitterBase_Open(msg.sender); opened = true; } - function close() external onlyOwner{ + function close() public onlyOwner{ emit SplitterBase_Close(msg.sender); opened = false; } - function isOpen() external view returns(bool){ + function isOpen() public view returns(bool){ return opened; } - function getChildrenCount()external view returns(uint){ + function getChildrenCount()public view returns(uint){ return childrenCount; } - function getChild(uint _index)external view returns(address){ + + function getChild(uint _index)public view returns(address){ return children[_index]; } - function addChild(address _newChild) external onlyOwner { + + function addChild(address _newChild) public onlyOwner { emit SplitterBase_AddChild(_newChild); children[childrenCount] = _newChild; childrenCount = childrenCount + 1; @@ -69,8 +67,9 @@ contract WeiTopDownSplitter is SplitterBase, IWeiReceiver { // IWeiReceiver: // calculate only absolute outputs, but do not take into account the Percents - function getMinWeiNeeded()external view returns(uint){ - if(!_isOpen()){ + + function getMinWeiNeeded()public view returns(uint){ + if(!isOpen()){ return 0; } uint out = 0; @@ -85,12 +84,8 @@ contract WeiTopDownSplitter is SplitterBase, IWeiReceiver { return out; } - function getTotalWeiNeeded(uint _inputWei)external view returns(uint){ - return _getTotalWeiNeeded(_inputWei); - } - - function _getTotalWeiNeeded(uint _inputWei)internal view returns(uint){ - if(!_isOpen()){ + function getTotalWeiNeeded(uint _inputWei)public view returns(uint){ + if(!isOpen()){ return 0; } @@ -110,7 +105,7 @@ contract WeiTopDownSplitter is SplitterBase, IWeiReceiver { return total; } - function getPercentsMul100()external view returns(uint){ + function getPercentsMul100()public view returns(uint){ uint total = 0; for(uint i=0; i=_getTotalWeiNeeded(_currentFlow)); + require(amount>=getTotalWeiNeeded(_currentFlow)); // ??? - //require(amount>=getMinWeiNeeded()); + // require(amount>=_getMinWeiNeeded()); // DO NOT SEND LESS! // DO NOT SEND MORE! @@ -167,15 +162,21 @@ contract WeiTopDownSplitter is SplitterBase, IWeiReceiver { // send money. can throw! // we sent needed money but specifying TOTAL amount of flow // this help relative Splitters to calculate how to split money - c.processFunds.value(needed)(amount); - - // this should be reduced because next child can get only 'amount minus what prev. child got' - if(amount>=needed){ - amount = amount - needed; - }else{ - amount = 0; + if(needed>0){ + c.processFunds.value(needed)(amount); + + // this should be reduced because next child can get only 'amount minus what prev. child got' + if(amount>=needed){ + amount = amount - needed; + }else{ + amount = 0; + } } } + + if(this.balance>0){ + revert(); + } } function() public { @@ -187,31 +188,38 @@ contract WeiTopDownSplitter is SplitterBase, IWeiReceiver { * @dev Will split money (order does not matter!). */ contract WeiUnsortedSplitter is SplitterBase, IWeiReceiver { + event consoleUint(string a, uint b); + constructor(string _name) SplitterBase(_name) public { } -// IWeiReceiver: + // IWeiReceiver: // calculate only absolute outputs, but do not take into account the Percents - function getMinWeiNeeded()external view returns(uint){ - if(!_isOpen()){ + function getMinWeiNeeded()public view returns(uint){ + if(!isOpen()){ return 0; } - uint total = 0; + uint absSum = 0; + uint percentsMul100ReverseSum = 10000; + for(uint i=0; i exception - function processFunds(uint _currentFlow) external payable{ - require(_isOpen()); + function processFunds(uint _currentFlow) public payable{ + require(isOpen()); emit SplitterBase_ProcessFunds(msg.sender, msg.value, _currentFlow); uint amount = msg.value; // TODO: can remove this line? // transfer below will throw if not enough money? - require(amount>=_getTotalWeiNeeded(_currentFlow)); + require(amount>=getTotalWeiNeeded(_currentFlow)); // DO NOT SEND LESS! // DO NOT SEND MORE! @@ -274,8 +282,14 @@ contract WeiUnsortedSplitter is SplitterBase, IWeiReceiver { // send money. can throw! // we sent needed money but specifying TOTAL amount of flow // this help relative Splitters to calculate how to split money - c.processFunds.value(needed)(_currentFlow); - } + if(needed>0){ + c.processFunds.value(needed)(_currentFlow); + } + } + + if(this.balance>0){ + revert(); + } } function() public { diff --git a/contracts/tasks/Tasks.sol b/contracts/tasks/Tasks.sol index efeae31..775d6d9 100644 --- a/contracts/tasks/Tasks.sol +++ b/contracts/tasks/Tasks.sol @@ -133,26 +133,22 @@ contract WeiGenericTask is WeiAbsoluteExpense { } // who will complete this task - function setEmployee(address _employee) external onlyOwner { + function setEmployee(address _employee) public onlyOwner { emit WeiGenericTask_SetEmployee(_employee); employee = _employee; } // where to send money - function setOutput(address _output) external onlyOwner { + function setOutput(address _output) public onlyOwner { emit WeiGenericTask_SetOutput(_output); output = _output; } - function getBalance()external view returns(uint){ + function getBalance()public view returns(uint){ return address(this).balance; } - function getCurrentState()external view returns(State){ - return _getCurrentState(); - } - - function _getCurrentState()internal view returns(State){ + function getCurrentState()public view returns(State){ // for Prepaid task -> client should call processFunds method to put money into this task // when state is Init if((State.Init==state) && (neededWei!=0) && (!isPostpaid)){ @@ -169,12 +165,12 @@ contract WeiGenericTask is WeiAbsoluteExpense { } } - return state; + return state; } - function cancell() external isCanCancell onlyOwner { - require(_getCurrentState()==State.Init || _getCurrentState()==State.PrePaid); - if(_getCurrentState()==State.PrePaid){ + function cancell() public isCanCancell onlyOwner { + require(getCurrentState()==State.Init || getCurrentState()==State.PrePaid); + if(getCurrentState()==State.PrePaid){ // return money to 'moneySource' moneySource.transfer(address(this).balance); } @@ -182,8 +178,8 @@ contract WeiGenericTask is WeiAbsoluteExpense { emit WeiGenericTask_StateChanged(state); } - function returnMoney() external isDeadlineMissed onlyOwner { - require(_getCurrentState()==State.InProgress); + function returnMoney() public isDeadlineMissed onlyOwner { + require(getCurrentState()==State.InProgress); if(address(this).balance > 0){ // return money to 'moneySource' moneySource.transfer(address(this).balance); @@ -192,8 +188,8 @@ contract WeiGenericTask is WeiAbsoluteExpense { emit WeiGenericTask_StateChanged(state); } - function notifyThatCompleted() external onlyEmployeeOrOwner { - require(_getCurrentState()==State.InProgress); + function notifyThatCompleted() public onlyEmployeeOrOwner { + require(getCurrentState()==State.InProgress); if((0!=neededWei) || (isDonation)){ // if donation or prePaid - no need in ev-ion; if postpaid with unknown payment - neededWei=0 yet @@ -205,8 +201,8 @@ contract WeiGenericTask is WeiAbsoluteExpense { } } - function evaluateAndSetNeededWei(uint _neededWei) external onlyOwner { - require(_getCurrentState()==State.CompleteButNeedsEvaluation); + function evaluateAndSetNeededWei(uint _neededWei) public onlyOwner { + require(getCurrentState()==State.CompleteButNeedsEvaluation); require(0==neededWei); neededWei = _neededWei; @@ -216,8 +212,8 @@ contract WeiGenericTask is WeiAbsoluteExpense { // for Prepaid tasks only! // for Postpaid: call processFunds and transfer money instead! - function confirmCompletion() external onlyByMoneySource { - require(_getCurrentState()==State.Complete); + function confirmCompletion() public onlyByMoneySource { + require(getCurrentState()==State.Complete); require(!isPostpaid); require(0!=neededWei); @@ -227,8 +223,8 @@ contract WeiGenericTask is WeiAbsoluteExpense { // IDestination overrides: // pull model - function flush() external { - require(_getCurrentState()==State.CanGetFunds); + function flush() public { + require(getCurrentState()==State.CanGetFunds); require(0x0!=output); output.transfer(address(this).balance); @@ -236,11 +232,11 @@ contract WeiGenericTask is WeiAbsoluteExpense { emit WeiGenericTask_StateChanged(state); } - function flushTo(address _to) external { + function flushTo(address _to) public { if(_to==_to) revert(); } - function processFunds(uint _currentFlow) external payable{ + function processFunds(uint _currentFlow) public payable{ emit WeiGenericTask_ProcessFunds(msg.sender, msg.value, _currentFlow); if(isPostpaid && (0==neededWei) && (State.Complete==state)){ // this is a donation @@ -248,7 +244,7 @@ contract WeiGenericTask is WeiAbsoluteExpense { neededWei = msg.value; } - super._processFunds(_currentFlow); + super.processFunds(_currentFlow); } // non-payable @@ -271,9 +267,9 @@ contract WeiTask is WeiGenericTask { // callable by any Employee of the current DaoBase or Owner function startTask(address _employee) public isCanDo(START_TASK) { - require(_getCurrentState()==State.Init || _getCurrentState()==State.PrePaid); + require(getCurrentState()==State.Init || getCurrentState()==State.PrePaid); - if(_getCurrentState()==State.Init){ + if(getCurrentState()==State.Init){ // can start only if postpaid task require(isPostpaid); } @@ -300,7 +296,7 @@ contract WeiBounty is WeiGenericTask { // callable by anyone function startTask() public isCanDo(START_BOUNTY) { - require(_getCurrentState()==State.PrePaid); + require(getCurrentState()==State.PrePaid); startTime = now; employee = msg.sender; state = State.InProgress; diff --git a/test/moneyflowTable_tests.js b/test/moneyflowTable_tests.js index 6c1cb5a..a8efe50 100644 --- a/test/moneyflowTable_tests.js +++ b/test/moneyflowTable_tests.js @@ -376,6 +376,45 @@ contract('MoneyflowTable tests', (accounts) => { assert.equal(absoluteExpense3Balance.toNumber(),1*neededAmount, 'resource point received money from splitter'); }); + it('should process money with WeiUnsortedSplitter + 2 WeiAbsoluteExpense + WeiRelativeExpense',async() => { + let moneyflowTable = await MoneyflowTable.new(); + + let SplitterId = getEId(await moneyflowTable.addUnsortedSplitter()); + let AbsoluteExpense1Id = getEId(await moneyflowTable.addAbsoluteExpense(neededAmount, isPeriodic, isAccumulateDebt, periodHours, output)); + let RelativeExpense1Id = getEId(await moneyflowTable.addRelativeExpense(9000, isPeriodic, isAccumulateDebt, periodHours, output)); + let AbsoluteExpense3Id = getEId(await moneyflowTable.addAbsoluteExpense(neededAmount, isPeriodic, isAccumulateDebt, periodHours, output)); + + // add 3 WeiAbsoluteExpense outputs to the splitter + await moneyflowTable.addChild(SplitterId, AbsoluteExpense1Id); + await moneyflowTable.addChild(SplitterId, RelativeExpense1Id); + await moneyflowTable.addChild(SplitterId, AbsoluteExpense3Id); + + // add WeiSplitter to the moneyflow + await moneyflowInstance.setRootWeiReceiver(moneyflowTable.address); + + var revenueEndpointAddress = await moneyflowInstance.getRevenueEndpoint(); + + global.assert.equal(revenueEndpointAddress, moneyflowTable.address, 'weiSplitter.address saved in moneyflowInstance as revenueEndpointAddress'); + + let totalNeed = await moneyflowTable.getTotalWeiNeeded(20*neededAmount); + global.assert.equal(totalNeed.toNumber(), 20*neededAmount); + let minNeed = await moneyflowTable.getMinWeiNeeded(); + global.assert.equal(minNeed.toNumber(), 20*neededAmount); + + // now send some money to the revenue endpoint + await moneyflowTable.processFunds(20*neededAmount, {value:20*neededAmount, from:creator}); + + // money should end up in the outputs + var absoluteExpense1Balance = await moneyflowTable.getElementBalance(AbsoluteExpense1Id); + global.assert.equal(absoluteExpense1Balance.toNumber(),1*neededAmount, 'resource point received money from splitter'); + + var relativeExpense2Balance = await moneyflowTable.getElementBalance(RelativeExpense1Id); + global.assert.equal(relativeExpense2Balance.toNumber(),18*neededAmount, 'resource point received money from splitter'); + + var absoluteExpense3Balance = await moneyflowTable.getElementBalance(AbsoluteExpense3Id); + global.assert.equal(absoluteExpense3Balance.toNumber(),1*neededAmount, 'resource point received money from splitter'); + }); + it('should process money with a scheme just like in the paper: 75/25 others, send MORE than minNeed; ',async() => { const money = web3.toWei(0.0001, "ether"); const CURRENT_INPUT = 30900; diff --git a/test/moneyflow_tests.js b/test/moneyflow_tests.js index 9177246..caf568f 100644 --- a/test/moneyflow_tests.js +++ b/test/moneyflow_tests.js @@ -45,8 +45,8 @@ async function createStructure(creator, money, e1, e2, e3, office, internet, t1, o.Bonus2 = await WeiRelativeExpense.new(b2, callParams); o.Bonus3 = await WeiRelativeExpense.new(b3, callParams); o.Rest = await WeiUnsortedSplitter.new('Rest', callParams); - o.ReserveFund = await WeiFund.new(creator, false, reserve, callParams); - o.DividendsFund = await WeiFund.new(creator, false, dividends, callParams); + o.ReserveFund = await WeiRelativeExpenseWithPeriod.new(reserve, 0, false, callParams); + o.DividendsFund = await WeiRelativeExpenseWithPeriod.new(dividends, 0, false, callParams); // CONNECTIONS await o.AllOutpults.addChild(o.Spends.address, callParams); @@ -217,12 +217,12 @@ contract('Moneyflow', (accounts) => { const outsider = accounts[3]; beforeEach(async() => { - token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000000000000000000000); + token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000000000000000000000, {gasPrice:0}); await token.mintFor(creator, 1000, {gasPrice: 0}); - store = await DaoStorage.new([token.address],{from: creator}); - daoBase = await DaoBase.new(store.address,{from: creator}); + store = await DaoStorage.new([token.address],{from: creator, gasPrice:0}); + daoBase = await DaoBase.new(store.address,{from: creator, gasPrice:0}); issueTokens = await daoBase.ISSUE_TOKENS(); @@ -234,7 +234,7 @@ contract('Moneyflow', (accounts) => { burnTokens = await daoBase.BURN_TOKENS(); - moneyflowInstance = await MoneyFlow.new(daoBase.address); + moneyflowInstance = await MoneyFlow.new(daoBase.address, {gasPrice:0}); withdrawDonations = await moneyflowInstance.WITHDRAW_DONATIONS(); @@ -262,6 +262,26 @@ contract('Moneyflow', (accounts) => { //await daoBase.allowActionByAddress("addNewProposal", moneyflowInstance.address); }); + it('Should revert when some money stays on unsorted splitter (U-> abs-rel50%)',async() => { + let abs = await WeiAbsoluteExpense.new(1e15); + let splitter = await WeiUnsortedSplitter.new('splitter'); + let rel = await WeiRelativeExpense.new(5000); + await splitter.addChild(abs.address); + await splitter.addChild(rel.address); + + await splitter.processFunds(1e16, {value:1e16}).should.be.rejectedWith('revert'); + }); + + it('Should revert when some money stays on topdown splitter (T-> abs-rel50%)',async() => { + let abs = await WeiAbsoluteExpense.new(1e15); + let splitter = await WeiTopDownSplitter.new('splitter'); + let rel = await WeiRelativeExpense.new(5000); + await splitter.addChild(abs.address); + await splitter.addChild(rel.address); + + await splitter.processFunds(1e16, {value:1e16}).should.be.rejectedWith('revert'); + }); + it('should process money with WeiAbsoluteExpenseWithPeriod, then 25 hours, then money needs again',async() => { const CURRENT_INPUT = 30900; let timePeriod = 25; @@ -365,7 +385,7 @@ contract('Moneyflow', (accounts) => { let struct = {}; let balance0 = await web3.eth.getBalance(creator); - let tax = await WeiRelativeExpenseWithPeriod.new(1000, 0, false, callParams); + let tax = await WeiRelativeExpenseWithPeriod.new(10000, 0, false, callParams); Splitter = await WeiTopDownSplitter.new('SimpleSplitter', callParams); await Splitter.addChild(tax.address, callParams); @@ -373,17 +393,17 @@ contract('Moneyflow', (accounts) => { let need1 = await Splitter.isNeedsMoney({from:creator}); let totalNeed1 = await Splitter.getTotalWeiNeeded(1000*money); assert.equal(need1, true, 'should need money'); - assert.equal(totalNeed1.toNumber(), 100*money, 'should be 10% of 1000 money'); + assert.equal(totalNeed1.toNumber(), 1000*money, 'should be 10% of 1000 money'); - await Splitter.processFunds(1000*money, {value:100*money, from:outsider, gasPrice:0}); + await Splitter.processFunds(1000*money, {value:1000*money, from:outsider, gasPrice:0}); let taxBalance = await web3.eth.getBalance(tax.address); - assert.equal(taxBalance.toNumber(), 100*money, 'Tax receiver should get 100 money'); + assert.equal(taxBalance.toNumber(), 1000*money, 'Tax receiver should get 100 money'); let need2 = await Splitter.isNeedsMoney({from:creator}); let totalNeed2 = await Splitter.getTotalWeiNeeded(1000*money); assert.equal(need2, true, 'should need money'); - assert.equal(totalNeed2.toNumber(), 100*money, 'should be 10% of 1000 money'); + assert.equal(totalNeed2.toNumber(), 1000*money, 'should be 10% of 1000 money'); await Splitter.close(callParams); @@ -392,7 +412,7 @@ contract('Moneyflow', (accounts) => { assert.equal(need3, false, 'should not need money'); assert.equal(totalNeed3.toNumber(), 0, 'should be 0 money'); - await Splitter.processFunds(100*money, {value:100*money, from:outsider, gasPrice:0}).should.be.rejectedWith('revert'); + await Splitter.processFunds(1000*money, {value:1000*money, from:outsider, gasPrice:0}).should.be.rejectedWith('revert'); }); it('should allow to send revenue',async() => { @@ -401,7 +421,7 @@ contract('Moneyflow', (accounts) => { assert.equal(revEndpoint,0x0,'Endpoint should be zero'); const isEnableFlushTo = true; - let fund = await WeiFund.new(creator,isEnableFlushTo,10000); + let fund = await await WeiRelativeExpenseWithPeriod.new(10000, 0, false); await moneyflowInstance.setRootWeiReceiver(fund.address); const revEndpoint2 = await moneyflowInstance.getRevenueEndpoint(); @@ -448,7 +468,7 @@ contract('Moneyflow', (accounts) => { it('should allow to get donations',async() => { const isEnableFlushTo = true; - let fund = await WeiFund.new(creator,isEnableFlushTo,10000); + let fund = await WeiRelativeExpenseWithPeriod.new(10000, 0, false); /// const dea = await moneyflowInstance.getDonationEndpoint(); @@ -546,6 +566,44 @@ contract('Moneyflow', (accounts) => { let weiAbsoluteExpense3Balance = await web3.eth.getBalance(weiAbsoluteExpense3.address); assert.equal(weiAbsoluteExpense3Balance.toNumber(),money, 'resource point received money from splitter'); + }); + + it('should process money with WeiUnsortedSplitter + 2 WeiAbsoluteExpense + WeiRelativeExpense',async() => { + // create WeiTopDownSplitter + let weiUnsortedSplitter = await WeiUnsortedSplitter.new('JustSplitter'); + + let weiAbsoluteExpense1 = await WeiAbsoluteExpense.new(money, {from:creator, gasPrice:0}); + let weiRelativeExpense1 = await WeiRelativeExpense.new(9000, {from:creator, gasPrice:0}); + let weiAbsoluteExpense3 = await WeiAbsoluteExpense.new(money, {from:creator, gasPrice:0}); + + // // add 3 WeiAbsoluteExpense outputs to the splitter + await weiUnsortedSplitter.addChild(weiAbsoluteExpense1.address); + await weiUnsortedSplitter.addChild(weiRelativeExpense1.address); + await weiUnsortedSplitter.addChild(weiAbsoluteExpense3.address); + + // add WeiTopDownSplitter to the moneyflow + await moneyflowInstance.setRootWeiReceiver(weiUnsortedSplitter.address); + + let revenueEndpointAddress = await moneyflowInstance.getRevenueEndpoint(); + + assert.equal(revenueEndpointAddress, weiUnsortedSplitter.address, 'weiUnsortedSplitter.address saved in moneyflowInstance as revenueEndpointAddress'); + + // now send some money to the revenue endpoint + + let minNeed = await weiUnsortedSplitter.getMinWeiNeeded(); + assert.equal(minNeed, 20*money); + + await weiUnsortedSplitter.processFunds(20*money, {value:20*money, from:creator}); + + // money should end up in the outputs + let weiAbsoluteExpense1Balance = await web3.eth.getBalance(weiAbsoluteExpense1.address); + assert.equal(weiAbsoluteExpense1Balance.toNumber(), money, 'resource point received money from splitter'); + + let weiRelativeExpense1Balance = await web3.eth.getBalance(weiRelativeExpense1.address); + assert.equal(weiRelativeExpense1Balance.toNumber(), 18*money, 'resource point received money from splitter'); + + let weiAbsoluteExpense3Balance = await web3.eth.getBalance(weiAbsoluteExpense3.address); + assert.equal(weiAbsoluteExpense3Balance.toNumber(), money, 'resource point received money from splitter'); }); it('should process money with WeiUnsortedSplitter + 3 WeiAbsoluteExpense',async() => { @@ -726,7 +784,7 @@ contract('Moneyflow', (accounts) => { let b1 = 100; let b2 = 100; let b3 = 200; - let reserve = 1000; + let reserve = 8500; let dividends = 1500; let struct = await createStructure(creator, money, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); @@ -738,9 +796,33 @@ contract('Moneyflow', (accounts) => { let balances = await getBalances(struct); await balancesAsserts(balances, CURRENT_INPUT, money, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); - await splitterBalancesAsserts(balances, money, 10800, 0, 0, 0, 0, 0, 0); + await splitterBalancesAsserts(balances, money, 0, 0, 0, 0, 0, 0, 0); }); + it('should NOT process money (splitter can not accumulate money) with a scheme just like in the paper: 10/15 others, send MORE than minNeed; ',async() => { + const CURRENT_INPUT = 20900; + let e1 = 1000; + let e2 = 1500; + let e3 = 800; + let office = 500; + let internet = 300; + let t1 = 500; + let t2 = 300; + let t3 = 1000; + let b1 = 100; + let b2 = 100; + let b3 = 200; + let reserve = 1000; + let dividends = 1500; + + let struct = await createStructure(creator, money, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(struct, CURRENT_INPUT, money, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, money, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await struct.AllOutpults.processFunds(CURRENT_INPUT*money, {value:CURRENT_INPUT*money, from:creator, gasPrice:0}).should.be.rejectedWith('revert'); + }); + it('should process money with a scheme just like in the paper: 10/15 others, send EQUAL to minNeed; ',async() => { const CURRENT_INPUT = 5900; let e1 = 1000; diff --git a/test/voting_1p1v_tests.js b/test/voting_1p1v_tests.js index 3e66d47..583f090 100644 --- a/test/voting_1p1v_tests.js +++ b/test/voting_1p1v_tests.js @@ -195,6 +195,7 @@ contract('Voting_1p1v(quorumPercent, consensusPercent)', (accounts) => { it('1.1. Q Scenario: 5 employees, 5/5 voted yes, params(100,100) => isYes==true',async() => { await aacInstance.setVotingParams(setRootWeiReceiver, VOTING_TYPE_1P1V, UintToToBytes32(0), fromUtf8("Employees"), UintToToBytes32(100), UintToToBytes32(100), 0); + const wae = await WeiAbsoluteExpense.new(1000); await aacInstance.setRootWeiReceiverAuto(wae.address, {from:employee1}); @@ -207,6 +208,7 @@ contract('Voting_1p1v(quorumPercent, consensusPercent)', (accounts) => { await voting.vote(true,0,{from:employee2}); r2 = await voting.getVotingStats(); + assert.equal(r2[0].toNumber(),2,'yes'); assert.equal(r2[1].toNumber(),0,'no'); diff --git a/test/weiFund_tests.js b/test/weiFund_tests.js index e085ad2..5899ce4 100644 --- a/test/weiFund_tests.js +++ b/test/weiFund_tests.js @@ -6,7 +6,6 @@ var DaoStorage = artifacts.require("./DaoStorage"); var MoneyFlow = artifacts.require("./MoneyFlow"); var WeiFund = artifacts.require("./WeiFund"); -// var ConditionalFund = artifacts.require("./WeiFund2"); var IWeiReceiver = artifacts.require("./IWeiReceiver"); var CheckExceptions = require('./utils/checkexceptions'); @@ -21,6 +20,15 @@ var WeiRelativeExpenseWithPeriod = artifacts.require("./WeiRelativeExpenseWithPe const getEId=o=> o.logs.filter(l => l.event == 'elementAdded')[0].args._eId.toNumber(); const KECCAK256=x=> web3.sha3(x); +async function passHours(hours){ + await web3.currentProvider.sendAsync({ + jsonrpc: '2.0', + method: 'evm_increaseTime', + params: [3600 * hours * 1000], + id: new Date().getTime() + }, function(err){if(err) console.log('err:', err)}); +} + const BigNumber = web3.BigNumber; require('chai') @@ -28,6 +36,331 @@ require('chai') .use(require('chai-bignumber')(BigNumber)) .should(); +contract('WeiFund', (accounts) => { + let money = web3.toWei(0.001, "ether"); + + const creator = accounts[0]; + const employee1 = accounts[1]; + const employee2 = accounts[2]; + const outsider = accounts[3]; + + beforeEach(async() => { + }); + + it('Should not create fund with wrong args',async() => { + await WeiFund.new(0, false, false, 0).should.be.rejectedWith('revert'); + await WeiFund.new(1e18, true, true, 0).should.be.rejectedWith('revert'); + await WeiFund.new(1e18, false, true, 24).should.be.rejectedWith('revert'); + await WeiFund.new(1e18, false, true, 0).should.be.rejectedWith('revert'); + await WeiFund.new(1e18, true, true, 0).should.be.rejectedWith('revert'); + }); + + it('Should collect money, then revert if more, then flush',async() => { + let fund = await WeiFund.new(1e18, false, false, 0); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var minNeed = await fund.getMinWeiNeeded(); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await fund.processFunds(3e17, {value:3e17, from:creator}); + await fund.processFunds(3e17, {value:3e17, from:employee1}); + + var totalNeed = await fund.getTotalWeiNeeded(4e17); + var minNeed = await fund.getMinWeiNeeded(); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 4e17); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await fund.processFunds(5e17, {value:5e17}).should.be.rejectedWith('revert'); //overflow + await fund.processFunds(4e17, {value:4e17, from:employee2}); + await fund.processFunds(1e17, {value:1e17}).should.be.rejectedWith('revert'); //overflow + + var totalNeed = await fund.getTotalWeiNeeded(0); + var minNeed = await fund.getMinWeiNeeded(); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, false); + + var b1 = await web3.eth.getBalance(employee1); + await fund.flushTo(employee1); + var b2 = await web3.eth.getBalance(employee1); + assert.equal(b2.toNumber()-b1.toNumber(), 1e18); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var minNeed = await fund.getMinWeiNeeded(); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, false); + }); + + it('Should collect money (periodic, not accumulate debt), then time passed, then need money again',async() => { + let fund = await WeiFund.new(1e18, true, false, 24); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await fund.processFunds(1e18, {value:1e18}); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(23); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(1); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await fund.processFunds(5e17, {value:5e17}); + + var totalNeed = await fund.getTotalWeiNeeded(5e17); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 5e17); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalWeiNeeded(5e17); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 5e17); + assert.equal(isNeed, true); + + await fund.processFunds(5e17, {value:5e17}); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + }); + + it('Should collect money (periodic, accumulate debt), then time passed, then need money again',async() => { + let fund = await WeiFund.new(1e18, true, true, 24); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await fund.processFunds(1e18, {value:1e18}); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(23); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(1); + + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalWeiNeeded(2e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 2e18); + assert.equal(isNeed, true); + + await fund.processFunds(5e17, {value:5e17}); + + var totalNeed = await fund.getTotalWeiNeeded(1.5e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1.5e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalWeiNeeded(2.5e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 2.5e18); + assert.equal(isNeed, true); + + await fund.processFunds(2.5e18, {value:2.5e18}); + + var totalNeed = await fund.getTotalWeiNeeded(0); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + }); + + it('Should collect money (periodic, accumulate debt), then time passed, then need money again',async() => { + let fund = await WeiFund.new(1e18, true, true, 24); + var totalNeed = await fund.getTotalWeiNeeded(1e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(48); + var totalNeed = await fund.getTotalWeiNeeded(3e18); + var isNeed = await fund.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 3e18); + assert.equal(isNeed, true); + }); + + it('Should implement roadmap pattern with funds (-> abs-abs-abs)',async() => { + let splitter = await WeiTopDownSplitter.new('Splitter'); + + let milestone1 = await WeiFund.new(0.1e18, false, false, 0); + let milestone2 = await WeiFund.new(0.2e18, false, false, 0); + let milestone3 = await WeiFund.new(0.7e18, false, false, 0); + await splitter.addChild(milestone1.address); + await splitter.addChild(milestone2.address); + await splitter.addChild(milestone3.address); + + var totalNeed = await splitter.getTotalWeiNeeded(1e18); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await splitter.processFunds(0.01e18,{value:0.01e18}); + + assert.equal(0.01, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + await splitter.processFunds(0.03e18,{value:0.03e18}); + + assert.equal(0.04, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + await splitter.processFunds(0.08e18,{value:0.08e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.02, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + var totalNeed = await splitter.getTotalWeiNeeded(0.88e18); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0.88e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await splitter.processFunds(0.4e18,{value:0.4e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.2, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0.22, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + await splitter.processFunds(0.48e18,{value:0.48e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.2, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0.7, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + var totalNeed = await splitter.getTotalWeiNeeded(0); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + + await splitter.processFunds(0.5e18,{value:0.5e18}).should.be.rejectedWith('revert'); + }); + + it('Should implement roadmap pattern with funds (-> abs-abs-abs-rel100%)',async() => { + let splitter = await WeiTopDownSplitter.new('Splitter'); + + let milestone1 = await WeiFund.new(0.1e18, false, false, 0); + let milestone2 = await WeiFund.new(0.2e18, false, false, 0); + let milestone3 = await WeiFund.new(0.7e18, false, false, 0); + let stabFund = await WeiRelativeExpenseWithPeriod.new(10000, 0, false); + await splitter.addChild(milestone1.address); + await splitter.addChild(milestone2.address); + await splitter.addChild(milestone3.address); + await splitter.addChild(stabFund.address); + + var totalNeed = await splitter.getTotalWeiNeeded(1e18); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await splitter.processFunds(0.01e18,{value:0.01e18}); + + assert.equal(0.01, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + await splitter.processFunds(0.03e18,{value:0.03e18}); + + assert.equal(0.04, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + await splitter.processFunds(0.08e18,{value:0.08e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.02, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + var totalNeed = await splitter.getTotalWeiNeeded(0.88e18); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0.88e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await splitter.processFunds(0.4e18,{value:0.4e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.2, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0.22, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + await splitter.processFunds(0.48e18,{value:0.48e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.2, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0.7, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + + var totalNeed = await splitter.getTotalWeiNeeded(0); + var minNeed = await splitter.getMinWeiNeeded(); + var isNeed = await splitter.isNeedsMoney(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + + await splitter.processFunds(0.3e18,{value:0.3e18}); + await splitter.processFunds(0.5e18,{value:0.5e18}); + await splitter.processFunds(0.7e18,{value:0.7e18}); + + assert.equal(0.1, (await web3.eth.getBalance(milestone1.address)).toNumber()/1e18); + assert.equal(0.2, (await web3.eth.getBalance(milestone2.address)).toNumber()/1e18); + assert.equal(0.7, (await web3.eth.getBalance(milestone3.address)).toNumber()/1e18); + assert.equal(1.5, (await web3.eth.getBalance(stabFund.address)).toNumber()/1e18); + }); +}); + /*contract('ConditionalFund', (accounts) => { let money = web3.toWei(0.001, "ether");