diff --git a/contracts/FallbackToWeiReceiver.sol b/contracts/FallbackToWeiReceiver.sol index 72d3d8c..c02e82e 100644 --- a/contracts/FallbackToWeiReceiver.sol +++ b/contracts/FallbackToWeiReceiver.sol @@ -1,6 +1,6 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; -import "./interfaces/IReceiver.sol"; +import "./interfaces/IWeiReceiver.sol"; /** @@ -16,8 +16,8 @@ contract FallbackToWeiReceiver { output = _output; } - function()public payable { - IReceiver iwr = IReceiver(output); + function() public payable { + IWeiReceiver iwr = IWeiReceiver(output); iwr.processFunds.value(msg.value)(msg.value); } } \ No newline at end of file diff --git a/contracts/bases/ExpenseBase.sol b/contracts/bases/ExpenseBase.sol index cdb8e41..7274010 100644 --- a/contracts/bases/ExpenseBase.sol +++ b/contracts/bases/ExpenseBase.sol @@ -1,5 +1,5 @@ pragma solidity ^0.4.24; - +pragma experimental ABIEncoderV2; import "../libs/ExpenseLib.sol"; import "../interfaces/IDestination.sol"; @@ -14,7 +14,7 @@ import "zeppelin-solidity/contracts/ownership/Ownable.sol"; * @dev Something that needs money (task, salary, bonus, etc) * Should be used in the Moneyflow so will automatically receive Wei. */ -contract ExpenseBase is ExpenseLib, IReceiver, IDestination, Ownable { +contract ExpenseBase is ExpenseLib, IReceiver, Ownable { Expense public expense; function getReceiverType() public view returns(Type) { @@ -25,20 +25,18 @@ contract ExpenseBase is ExpenseLib, IReceiver, IDestination, Ownable { } } - function processFunds(uint _currentFlow) public payable { - emit ExpenseProcessFunds(msg.sender, msg.value, _currentFlow); - expense = _processFunds(expense, _currentFlow, msg.value); - } - - function getIsMoneyReceived() public view returns(bool) { - return expense.totalReceived > 0; + function getExpenseParams() public view returns(uint128 totalNeeded, uint128 minAmount, uint32 partsPerMillion, uint32 periodHours, uint32 momentReceived, uint128 balance, uint128 totalReceived, uint32 momentCreated) { + totalNeeded = expense.totalNeeded; + minAmount = expense.minAmount; + partsPerMillion = expense.partsPerMillion; + periodHours = expense.periodHours; + momentReceived = expense.momentReceived; + balance = expense.balance; + totalReceived = expense.totalReceived; + momentCreated = expense.momentCreated; } - function getNeededWei() public view returns(uint) { - return expense.totalNeeded; - } - - function getTotalNeeded(uint _currentFlow)public view returns(uint) { + function getTotalNeeded(uint _currentFlow) public view returns(uint) { return _getTotalNeeded(expense, _currentFlow); } @@ -46,30 +44,10 @@ contract ExpenseBase is ExpenseLib, IReceiver, IDestination, Ownable { return _getMinNeeded(expense, _currentFlow); } - function getMomentReceived()public view returns(uint) { - return expense.momentReceived; - } - - function isNeeds()public view returns(bool) { + function isNeeds() public view returns(bool) { return _isNeeds(expense); } - function getPartsPerMillion()public view returns(uint) { - return expense.partsPerMillion; - } - - function flush()public onlyOwner { - emit ExpenseFlush(owner, address(this).balance); - owner.transfer(address(this).balance); - expense.balance = 0; - } - - function flushTo(address _to) public onlyOwner { - emit ExpenseFlush(_to, address(this).balance); - _to.transfer(address(this).balance); - expense.balance = 0; - } - function setNeededWei(uint _totalNeeded) public onlyOwner { emit ExpenseSetNeeded(_totalNeeded); expense.totalNeeded = uint128(_totalNeeded); @@ -81,5 +59,4 @@ contract ExpenseBase is ExpenseLib, IReceiver, IDestination, Ownable { } function() public {} - } diff --git a/contracts/bases/SplitterBase.sol b/contracts/bases/SplitterBase.sol index 9fdbed0..af4db5d 100644 --- a/contracts/bases/SplitterBase.sol +++ b/contracts/bases/SplitterBase.sol @@ -21,7 +21,7 @@ contract SplitterBase is SplitterLib, IReceiver, ISplitter, Ownable { return _getMinNeeded(splitter, _currentFlow); } - function getTotalNeeded(uint _currentFlow)public view returns(uint) { + function getTotalNeeded(uint _currentFlow) public view returns(uint) { return _getTotalNeeded(splitter, _currentFlow); } @@ -29,10 +29,6 @@ contract SplitterBase is SplitterLib, IReceiver, ISplitter, Ownable { return _isNeeds(splitter); } - function processFunds(uint _currentFlow) public payable { - _processFunds(splitter, _currentFlow, msg.value); - } - function open() public onlyOwner { _open(splitter); } diff --git a/contracts/bases/TableBase.sol b/contracts/bases/TableBase.sol index 8193707..ad6e130 100644 --- a/contracts/bases/TableBase.sol +++ b/contracts/bases/TableBase.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; import "../interfaces/IReceiver.sol"; @@ -45,15 +45,11 @@ contract TableBase is ExpenseLib, SplitterLib, Ownable { return getPartsPerMillionAt(0); } - function processFunds(uint _currentFlow) public payable { - return _processFundsAt(0, _currentFlow, msg.value); - } - - function _processFundsAt(uint _eId, uint _currentFlow, uint _value) internal { + function _processAmountAt(uint _eId, uint _currentFlow, uint _value) internal { if(isExpenseAt(_eId)) { - expenses[_eId] = _processFunds(expenses[_eId], _currentFlow, _value); + expenses[_eId] = _processAmount(expenses[_eId], _currentFlow, _value); }else { - _processFunds(splitters[_eId], _currentFlow, _value); + _processAmount(splitters[_eId], _currentFlow, _value); } } @@ -201,4 +197,10 @@ contract TableBase is ExpenseLib, SplitterLib, Ownable { require(splitters[_eId].outputs.length > _index); return splitters[_eId].outputs[_index]; } + + function _tableProcessing(address _target, uint _eId, uint _flow, uint _need) internal { + _processAmountAt(_eId, _flow, _need); + } + + function() public {} } \ No newline at end of file diff --git a/contracts/erc20/ERC20AbsoluteExpense.sol b/contracts/erc20/ERC20AbsoluteExpense.sol new file mode 100644 index 0000000..167b99f --- /dev/null +++ b/contracts/erc20/ERC20AbsoluteExpense.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20AbsoluteExpense is ERC20Expense { + constructor(address _tokenAddress, uint _minERC20Amount, uint _totalERC20Need) public + ERC20Expense(_tokenAddress, _minERC20Amount, _totalERC20Need, 0, 0, false, false) + {} +} \ No newline at end of file diff --git a/contracts/erc20/ERC20AbsoluteExpenseWithPeriod.sol b/contracts/erc20/ERC20AbsoluteExpenseWithPeriod.sol new file mode 100644 index 0000000..c8f9761 --- /dev/null +++ b/contracts/erc20/ERC20AbsoluteExpenseWithPeriod.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20AbsoluteExpenseWithPeriod is ERC20Expense { + constructor(address _tokenAddress, uint _minERC20Amount, uint _totalERC20Need, uint _periodHours) public + ERC20Expense(_tokenAddress, _minERC20Amount, _totalERC20Need, 0, _periodHours, false, true) + {} +} \ No newline at end of file diff --git a/contracts/erc20/ERC20AbsoluteExpenseWithPeriodSliding.sol b/contracts/erc20/ERC20AbsoluteExpenseWithPeriodSliding.sol new file mode 100644 index 0000000..fa6678d --- /dev/null +++ b/contracts/erc20/ERC20AbsoluteExpenseWithPeriodSliding.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20AbsoluteExpenseWithPeriodSliding is ERC20Expense { + constructor(address _tokenAddress, uint _minERC20Amount, uint _totalERC20Need, uint _periodHours) public + ERC20Expense(_tokenAddress, _minERC20Amount, _totalERC20Need, 0, _periodHours, true, true) + {} +} \ No newline at end of file diff --git a/contracts/erc20/ERC20RelativeExpense.sol b/contracts/erc20/ERC20RelativeExpense.sol new file mode 100644 index 0000000..026d0e3 --- /dev/null +++ b/contracts/erc20/ERC20RelativeExpense.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20RelativeExpense is ERC20Expense { + constructor(address _tokenAddress, uint _partsPerMillion) public + ERC20Expense(_tokenAddress, 0, 0, _partsPerMillion, 0, false, false) + {} +} \ No newline at end of file diff --git a/contracts/erc20/ERC20RelativeExpenseWithPeriod.sol b/contracts/erc20/ERC20RelativeExpenseWithPeriod.sol new file mode 100644 index 0000000..a9312ac --- /dev/null +++ b/contracts/erc20/ERC20RelativeExpenseWithPeriod.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20RelativeExpenseWithPeriod is ERC20Expense { + constructor(address _tokenAddress, uint _partsPerMillion, uint _periodHours) public + ERC20Expense(_tokenAddress, 0, 0, _partsPerMillion, _periodHours, false, true) + {} +} diff --git a/contracts/erc20/ERC20RelativeExpenseWithPeriodSliding.sol b/contracts/erc20/ERC20RelativeExpenseWithPeriodSliding.sol new file mode 100644 index 0000000..658db7b --- /dev/null +++ b/contracts/erc20/ERC20RelativeExpenseWithPeriodSliding.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + +import "./ERC20Expense.sol"; + + +contract ERC20RelativeExpenseWithPeriodSliding is ERC20Expense { + constructor(address _tokenAddress, uint _partsPerMillion, uint _periodHours) public + ERC20Expense(_tokenAddress, 0, 0, _partsPerMillion, _periodHours, true, true) + {} +} diff --git a/contracts/erc20/ERC20Token.sol b/contracts/erc20/ERC20Token.sol new file mode 100644 index 0000000..451a928 --- /dev/null +++ b/contracts/erc20/ERC20Token.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.24; + +import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; + +contract ERC20Token is MintableToken { + +} \ No newline at end of file diff --git a/contracts/erc20/Erc20Expense.sol b/contracts/erc20/Erc20Expense.sol index d00f2e4..8c9e069 100644 --- a/contracts/erc20/Erc20Expense.sol +++ b/contracts/erc20/Erc20Expense.sol @@ -1,9 +1,52 @@ pragma solidity ^0.4.24; +import "../bases/ExpenseBase.sol"; + +import "../interfaces/IDestination.sol"; +import "../interfaces/IReceiver.sol"; +import "../interfaces/ITokenReceiver.sol"; + import "zeppelin-solidity/contracts/math/SafeMath.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + + +/** + * @title ERC20Expense + * @dev Something that needs money (task, salary, bonus, etc) + * Should be used in the Moneyflow so will automatically receive ERC20. +*/ +contract ERC20Expense is ITokenReceiver, IDestination, ExpenseBase { + ERC20 public token; + + constructor( + address _tokenAddress, + uint _totalNeeded, + uint _minAmount, + uint _partsPerMillion, + uint _periodHours, + bool _isSlidingAmount, + bool _isPeriodic) public + { + token = ERC20(_tokenAddress); + expense = _constructExpense(uint128(_totalNeeded), uint128(_minAmount), uint32(_partsPerMillion), uint32(_periodHours), _isSlidingAmount, _isPeriodic); + } + event ProcessTokensExpense(address sender, address target, uint _value); + function processTokens(uint _currentFlow, uint _value) public { + require(_value <= token.allowance(msg.sender, address(this))); + token.transferFrom(msg.sender, address(this), _value); + emit ProcessTokensExpense(msg.sender, address(this), _value); + expense = _processAmount(expense, _currentFlow, _value); + } -contract Erc20Expense { + function flush() public onlyOwner { + token.transfer(owner, expense.balance); + expense = _processFlushTo(expense, owner); + } -} \ No newline at end of file + function flushTo(address _to) public onlyOwner { + token.transfer(_to, expense.balance); + expense = _processFlushTo(expense, _to); + } +} diff --git a/contracts/erc20/Erc20Splitter.sol b/contracts/erc20/Erc20Splitter.sol index 576c91d..0c7cab8 100644 --- a/contracts/erc20/Erc20Splitter.sol +++ b/contracts/erc20/Erc20Splitter.sol @@ -1,9 +1,36 @@ pragma solidity ^0.4.24; -import "zeppelin-solidity/contracts/math/SafeMath.sol"; +import "../bases/SplitterBase.sol"; + import "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + +import "../interfaces/ISplitter.sol"; +import "../interfaces/ITokenReceiver.sol"; +import "../interfaces/IReceiver.sol"; + + +/** + * @title Splitter + * @dev Will split money from top to down (order matters!). It is possible for some children to not receive money + * if they have ended. +*/ +contract ERC20Splitter is ITokenReceiver, SplitterBase { + ERC20 public token; + constructor(address _tokenAddress) public { + token = ERC20(_tokenAddress); + splitter = _constructSplitter(false); + } -contract Erc20Splitter { + function _elementProcessing(address _target, uint _currentFlow, uint _value) internal { + token.approve(_target, _value); + ITokenReceiver(_target).processTokens(_currentFlow, _value); + } + function processTokens(uint _currentFlow, uint _value) public { + require(_value <= token.allowance(msg.sender, address(this))); + token.transferFrom(msg.sender, address(this), _value); + _processAmount(splitter, _currentFlow, _value); + } } \ No newline at end of file diff --git a/contracts/erc20/Erc20Table.sol b/contracts/erc20/Erc20Table.sol index 3951275..fe55874 100644 --- a/contracts/erc20/Erc20Table.sol +++ b/contracts/erc20/Erc20Table.sol @@ -1,9 +1,36 @@ pragma solidity ^0.4.24; -import "zeppelin-solidity/contracts/math/SafeMath.sol"; +import "../bases/TableBase.sol"; +import "../bases/ExpenseBase.sol"; +import "../bases/SplitterBase.sol"; + +import "../interfaces/IReceiver.sol"; +import "../interfaces/ITokenReceiver.sol"; + import "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + + +contract ERC20Table is ITable, IReceiver, ITokenReceiver, TableBase { + ERC20 public token; + + constructor(address _tokenAddress) public { + token = ERC20(_tokenAddress); + } + function processTokens(uint _currentFlow, uint _value) public { + require(_value <= token.allowance(msg.sender, address(this))); + _processAmountAt(0, _currentFlow, _value); + token.transferFrom(msg.sender, address(this), _value); + } -contract Erc20Table { + function flushAt(uint _eId) public onlyOwner isCorrectId(_eId) { + token.transfer(owner, expenses[_eId].balance); + _processFlushToAt(_eId, owner); + } + function flushToAt(uint _eId, address _to) public onlyOwner isCorrectId(_eId) { + token.transfer(_to, expenses[_eId].balance); + _processFlushToAt(_eId, _to); + } } \ No newline at end of file diff --git a/contracts/ether/WeiAbsoluteExpenseWithPeriod.sol b/contracts/ether/WeiAbsoluteExpenseWithPeriod.sol index a79781e..b8176bf 100644 --- a/contracts/ether/WeiAbsoluteExpenseWithPeriod.sol +++ b/contracts/ether/WeiAbsoluteExpenseWithPeriod.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./WeiExpense.sol"; diff --git a/contracts/ether/WeiAbsoluteExpenseWithPeriodSliding.sol b/contracts/ether/WeiAbsoluteExpenseWithPeriodSliding.sol index 1bdd70d..a23102e 100644 --- a/contracts/ether/WeiAbsoluteExpenseWithPeriodSliding.sol +++ b/contracts/ether/WeiAbsoluteExpenseWithPeriodSliding.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./WeiExpense.sol"; diff --git a/contracts/ether/WeiExpense.sol b/contracts/ether/WeiExpense.sol index 576e677..80b130b 100644 --- a/contracts/ether/WeiExpense.sol +++ b/contracts/ether/WeiExpense.sol @@ -4,6 +4,7 @@ import "../bases/ExpenseBase.sol"; import "../interfaces/IDestination.sol"; import "../interfaces/IReceiver.sol"; +import "../interfaces/IWeiReceiver.sol"; import "zeppelin-solidity/contracts/math/SafeMath.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; @@ -14,9 +15,29 @@ import "zeppelin-solidity/contracts/ownership/Ownable.sol"; * @dev Something that needs money (task, salary, bonus, etc) * Should be used in the Moneyflow so will automatically receive Wei. */ -contract WeiExpense is ExpenseBase { - constructor(uint _totalNeeded, uint _minWeiAmount, uint _partsPerMillion, uint _periodHours, bool _isSlidingAmount, bool _isPeriodic) public { +contract WeiExpense is IWeiReceiver, IDestination, ExpenseBase { + constructor( + uint _totalNeeded, + uint _minWeiAmount, + uint _partsPerMillion, + uint _periodHours, + bool _isSlidingAmount, + bool _isPeriodic) public + { expense = _constructExpense(uint128(_totalNeeded), uint128(_minWeiAmount), uint32(_partsPerMillion), uint32(_periodHours), _isSlidingAmount, _isPeriodic); } + function processFunds(uint _currentFlow) public payable { + expense = _processAmount(expense, _currentFlow, msg.value); + } + + function flush() public onlyOwner { + _processFlushTo(expense, owner); + owner.transfer(address(this).balance); + } + + function flushTo(address _to) public onlyOwner { + _processFlushTo(expense, _to); + _to.transfer(address(this).balance); + } } diff --git a/contracts/ether/WeiRelativeExpense.sol b/contracts/ether/WeiRelativeExpense.sol index e632e23..7069772 100644 --- a/contracts/ether/WeiRelativeExpense.sol +++ b/contracts/ether/WeiRelativeExpense.sol @@ -1,10 +1,10 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./WeiExpense.sol"; contract WeiRelativeExpense is WeiExpense { - constructor(uint _partsPerMillion)public + constructor(uint _partsPerMillion) public WeiExpense(0, 0, _partsPerMillion, 0, false, false) {} } \ No newline at end of file diff --git a/contracts/ether/WeiRelativeExpenseWithPeriod.sol b/contracts/ether/WeiRelativeExpenseWithPeriod.sol index 56e50ac..48c9884 100644 --- a/contracts/ether/WeiRelativeExpenseWithPeriod.sol +++ b/contracts/ether/WeiRelativeExpenseWithPeriod.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./WeiExpense.sol"; diff --git a/contracts/ether/WeiRelativeExpenseWithPeriodSliding.sol b/contracts/ether/WeiRelativeExpenseWithPeriodSliding.sol index a15aa1c..77e3c5b 100644 --- a/contracts/ether/WeiRelativeExpenseWithPeriodSliding.sol +++ b/contracts/ether/WeiRelativeExpenseWithPeriodSliding.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./WeiExpense.sol"; diff --git a/contracts/ether/WeiSplitter.sol b/contracts/ether/WeiSplitter.sol index 90ede73..25d005d 100644 --- a/contracts/ether/WeiSplitter.sol +++ b/contracts/ether/WeiSplitter.sol @@ -5,6 +5,7 @@ import "../bases/SplitterBase.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; import "../interfaces/ISplitter.sol"; +import "../interfaces/IWeiReceiver.sol"; import "../interfaces/IReceiver.sol"; @@ -13,12 +14,16 @@ import "../interfaces/IReceiver.sol"; * @dev Will split money from top to down (order matters!). It is possible for some children to not receive money * if they have ended. */ -contract WeiSplitter is SplitterBase { +contract WeiSplitter is IWeiReceiver, SplitterBase { constructor() public { splitter = _constructSplitter(false); } function _elementProcessing(address _target, uint _flow, uint _need) internal { - IReceiver(_target).processFunds.value(_need)(_flow); + IWeiReceiver(_target).processFunds.value(_need)(_flow); } + + function processFunds(uint _currentFlow) public payable { + _processAmount(splitter, _currentFlow, msg.value); + } } \ No newline at end of file diff --git a/contracts/ether/WeiTable.sol b/contracts/ether/WeiTable.sol index 235fb01..68fee54 100644 --- a/contracts/ether/WeiTable.sol +++ b/contracts/ether/WeiTable.sol @@ -5,12 +5,17 @@ import "../bases/ExpenseBase.sol"; import "../bases/SplitterBase.sol"; import "../interfaces/IReceiver.sol"; +import "../interfaces/IWeiReceiver.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; -contract WeiTable is ITable, IReceiver, TableBase { +contract WeiTable is ITable, IReceiver, IWeiReceiver, TableBase { // -------------------- WEI-SPECIFIC IMPLEMENTATIONS -------------------- + function processFunds(uint _currentFlow) public payable { + return _processAmountAt(0, _currentFlow, msg.value); + } + function flushAt(uint _eId) public onlyOwner isCorrectId(_eId) { owner.transfer(expenses[_eId].balance); _processFlushToAt(_eId, owner); @@ -20,11 +25,4 @@ contract WeiTable is ITable, IReceiver, TableBase { _to.transfer(expenses[_eId].balance); _processFlushToAt(_eId, _to); } - - function _tableProcessing(address _target, uint _eId, uint _flow, uint _need) internal { - _processFundsAt(_eId, _flow, _need); - } - - function() public { - } } \ No newline at end of file diff --git a/contracts/examples/DefaultMoneyflowSchemeWithUnpackers.sol b/contracts/examples/DefaultMoneyflowSchemeWithUnpackers.sol index 37d96ee..1b4942a 100644 --- a/contracts/examples/DefaultMoneyflowSchemeWithUnpackers.sol +++ b/contracts/examples/DefaultMoneyflowSchemeWithUnpackers.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./DefaultMoneyflowScheme.sol"; diff --git a/contracts/interfaces/IDestination.sol b/contracts/interfaces/IDestination.sol index d78a86f..2c56097 100644 --- a/contracts/interfaces/IDestination.sol +++ b/contracts/interfaces/IDestination.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; /** diff --git a/contracts/interfaces/IErc20Receiver.sol b/contracts/interfaces/IErc20Receiver.sol deleted file mode 100644 index f8daaac..0000000 --- a/contracts/interfaces/IErc20Receiver.sol +++ /dev/null @@ -1,18 +0,0 @@ -pragma solidity ^0.4.22; - -import "./IReceiver.sol"; - - -// IErc20Receiver does not store funds! -// -// There are 2 types of Outputs: -// "Absolute": fixed amount of Wei -// "Relative": percents of input -contract IErc20Receiver is IReceiver { - // Will calculate only absolute outputs, but not take into account the Percents - 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) public view returns(uint); -} \ No newline at end of file diff --git a/contracts/interfaces/IReceiver.sol b/contracts/interfaces/IReceiver.sol index 0b3ce0e..8b03430 100644 --- a/contracts/interfaces/IReceiver.sol +++ b/contracts/interfaces/IReceiver.sol @@ -13,8 +13,6 @@ contract IReceiver { Table } - function processFunds(uint _currentFlow) public payable; - // If this output needs more funds -> will return true // If this output does not need more funds -> will return false function isNeeds() public view returns(bool); diff --git a/contracts/interfaces/ISplitter.sol b/contracts/interfaces/ISplitter.sol index 97ce05c..73e4a98 100644 --- a/contracts/interfaces/ISplitter.sol +++ b/contracts/interfaces/ISplitter.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; /** diff --git a/contracts/interfaces/ITokenReceiver.sol b/contracts/interfaces/ITokenReceiver.sol new file mode 100644 index 0000000..0dab04a --- /dev/null +++ b/contracts/interfaces/ITokenReceiver.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + + +/** + * @title ITokenReceiver + * @dev Something that needs tokens +*/ +contract ITokenReceiver { + function processTokens(uint _currentFlow, uint _value) public; +} \ No newline at end of file diff --git a/contracts/interfaces/IWeiReceiver.sol b/contracts/interfaces/IWeiReceiver.sol new file mode 100644 index 0000000..ee89ece --- /dev/null +++ b/contracts/interfaces/IWeiReceiver.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + + +/** + * @title IWeiReceiver + * @dev Something that needs funds +*/ +contract IWeiReceiver { + function processFunds(uint _currentFlow) public payable; +} \ No newline at end of file diff --git a/contracts/libs/ExpenseLib.sol b/contracts/libs/ExpenseLib.sol index f4de82f..f2f23c6 100644 --- a/contracts/libs/ExpenseLib.sol +++ b/contracts/libs/ExpenseLib.sol @@ -75,7 +75,8 @@ contract ExpenseLib { } } - function _processFunds(Expense _e, uint _currentFlow, uint _value) internal view returns(Expense e) { + function _processAmount(Expense _e, uint _currentFlow, uint _value) internal view returns(Expense e) { + emit ExpenseProcessFunds(msg.sender, _value, _currentFlow); e = _e; require(_value == _getTotalNeeded(e, _currentFlow)); require(_currentFlow >= _value); @@ -84,7 +85,7 @@ contract ExpenseLib { e.totalReceived += uint128(_value); e.balance += uint128(_value); - if((_getTotalNeeded(_e, _value) == 0) || (_e.periodType == PeriodType.Periodic)) { + if((_getTotalNeeded(_e, _value) == 0) || (_e.periodType == PeriodType.Periodic) || (_e.periodType == PeriodType.PeriodicSliding)) { e.momentReceived = uint32(block.timestamp); } } @@ -209,4 +210,10 @@ contract ExpenseLib { return 0; } } + + function _processFlushTo(Expense _e, address _to) internal view returns(Expense e) { + e = _e; + emit ExpenseFlush(_to, e.balance); + e.balance = 0; + } } \ No newline at end of file diff --git a/contracts/libs/SplitterLib.sol b/contracts/libs/SplitterLib.sol index f23d1e7..8800ece 100644 --- a/contracts/libs/SplitterLib.sol +++ b/contracts/libs/SplitterLib.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; import "../interfaces/IReceiver.sol"; @@ -127,7 +127,7 @@ contract SplitterLib { } } - function _processFunds(Splitter _s, uint _currentFlow, uint _value) internal { + function _processAmount(Splitter _s, uint _currentFlow, uint _value) internal { require(_s.isOpen); require(_isNeeds(_s)); FlowBuffer memory b = FlowBuffer(_currentFlow, false, 0, 0, 0, 0); diff --git a/test/ERC20_tests/ERC20_expense.tests.js b/test/ERC20_tests/ERC20_expense.tests.js new file mode 100644 index 0000000..b114fc0 --- /dev/null +++ b/test/ERC20_tests/ERC20_expense.tests.js @@ -0,0 +1,910 @@ +var IReceiver = artifacts.require('./IReceiver'); + +var StandardToken = artifacts.require('./ERC20Token'); + +var ERC20Splitter = artifacts.require('./ERC20Splitter'); +var ERC20AbsoluteExpense = artifacts.require('./ERC20AbsoluteExpense'); +var ERC20RelativeExpense = artifacts.require('./ERC20RelativeExpense'); +var ERC20AbsoluteExpenseWithPeriod = artifacts.require('./ERC20AbsoluteExpenseWithPeriod'); +var ERC20RelativeExpenseWithPeriod = artifacts.require('./ERC20RelativeExpenseWithPeriod'); + +var ERC20AbsoluteExpenseWithPeriodSliding = artifacts.require('./ERC20AbsoluteExpenseWithPeriodSliding'); +var ERC20RelativeExpenseWithPeriodSliding = artifacts.require('./ERC20RelativeExpenseWithPeriodSliding'); + +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); }); +} + + +async function checkMinNeed (targets, flowArr, needArr) { + for(var i=0; i { + var token; + var store; + var daoBase; + + var issueTokens; + var manageGroups; + var addNewProposal; + var upgradeDaoContract; + var addNewTask; + var startTask; + var startBounty; + var modifyMoneyscheme; + var withdrawDonations; + var setRootERC20Receiver; + var burnTokens; + + var tokenAmount = 1e10; + + const creator = accounts[0]; + const employee1 = accounts[1]; + const employee2 = accounts[2]; + const outsider = accounts[3]; + + beforeEach(async () => { + token = await StandardToken.new(); + await token.mint(accounts[0], 1e15); + await token.mint(accounts[1], 1e15); + await token.mint(accounts[2], 1e15); + await token.mint(accounts[3], 1e15); + await token.mint(accounts[4], 1e15); + await token.mint(accounts[5], 1e15); + await token.mint(accounts[6], 1e15); + await token.mint(accounts[7], 1e15); + await token.mint(accounts[8], 1e15); + await token.mint(accounts[9], 1e15); + }); + + it('1: Should revert when trying to add rel to abs splitter', async () => { + var abs = await ERC20AbsoluteExpense.new(token.address,1e15, 1e15); + var splitter = await ERC20Splitter.new(token.address); + var rel = await ERC20RelativeExpense.new(token.address,500000); + await splitter.addChild(abs.address); + await splitter.addChild(rel.address).should.be.rejectedWith('revert'); + }); + + + it('2: should process tokenAmount with ERC20AbsoluteExpenseWithPeriod, then 25 hours, then tokenAmount needs again', async () => { + var timePeriod = 25; + var callParams = { from: creator, gasPrice: 0 }; + var struct = {}; + var balance0 = await token.balanceOf(creator); + + Employee1 = await ERC20AbsoluteExpenseWithPeriod.new(token.address,1000*tokenAmount, 1000*tokenAmount, timePeriod, callParams); + + await token.approve(Employee1.address, 1000*tokenAmount, {from:outsider}); + await Employee1.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: outsider, gasPrice: 0 }); + await Employee1.flush({ from: outsider }).should.be.rejectedWith('revert'); + await Employee1.flush({ from: creator, gasPrice: 0 }); + + var balance = await token.balanceOf(creator); + assert.equal((new web3.BigNumber(balance).sub(balance0)).toNumber(), 1000*tokenAmount, 'Should get tokenAmount'); + + var needsEmployee1 = await Employee1.isNeeds({ from: creator }); + assert.equal(needsEmployee1, false, 'Dont need tokenAmount, because he got it'); + + await passHours(timePeriod); + var needsEmployee2 = await Employee1.isNeeds({ from: creator }); + assert.equal(needsEmployee2, true, 'Need tokenAmount, because 24 hours passed'); + + var need = await Employee1.getTotalNeeded(10000*tokenAmount); + assert.equal(need.toNumber(), 1000*tokenAmount); + + var min = await Employee1.getMinNeeded(10000*tokenAmount); + assert.equal(min.toNumber(), 1000*tokenAmount); + + await token.approve(Employee1.address, 1000*tokenAmount, {from:outsider}); + await Employee1.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: outsider, gasPrice: 0 }); + await Employee1.flush({ from: creator, gasPrice: 0 }); + + var balance2 = await token.balanceOf(creator); + assert.equal((new web3.BigNumber(balance2).sub(balance0)).toNumber(), 2000*tokenAmount, 'Should get tokenAmount'); + + var needsEmployee3 = await Employee1.isNeeds({ from: creator }); + assert.equal(needsEmployee3, false, 'Dont need tokenAmount, because he got it'); + }); + + it('3: should process tokenAmount with ERC20AbsoluteExpenseWithPeriod, then 75 hours, then tokenAmount needs again x3', async () => { + var timePeriod = 25; + var callParams = { from: creator, gasPrice: 0 }; + var struct = {}; + var balance0 = await token.balanceOf(creator); + Employee1 = await ERC20AbsoluteExpenseWithPeriodSliding.new(token.address,1000*tokenAmount, 1000*tokenAmount, timePeriod, callParams); + + await token.approve(Employee1.address, 1000*tokenAmount, {from:outsider}); + + await Employee1.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: outsider, gasPrice: 0 }); + + await Employee1.flush({ from: outsider }).should.be.rejectedWith('revert'); + await Employee1.flush({ from: creator, gasPrice: 0 }); + + var balance = await token.balanceOf(creator); + + assert.equal(balance.toNumber() - balance0.toNumber(), 1000*tokenAmount, 'Should get tokenAmount'); + + var needsEmployee1 = await Employee1.isNeeds({ from: creator }); + + assert.equal(needsEmployee1, false, 'Dont need tokenAmount, because he got it'); + var need = await Employee1.getTotalNeeded(10000*tokenAmount); + assert.equal(need.toNumber(), 0); + + await passHours(1*timePeriod); + var need = await Employee1.getTotalNeeded(10000*tokenAmount); + assert.equal(need.toNumber(), 1000*tokenAmount); + + await passHours(1*timePeriod); + var need = await Employee1.getTotalNeeded(10000*tokenAmount); + assert.equal(need.toNumber(), 2000*tokenAmount); + + await passHours(1*timePeriod); + var need = await Employee1.getTotalNeeded(10000*tokenAmount); + assert.equal(need.toNumber(), 3000*tokenAmount); + + var needsEmployee2 = await Employee1.isNeeds({ from: creator }); + assert.equal(needsEmployee2, true, 'Need tokenAmount, because 24 hours passed'); + + await token.approve(Employee1.address, 4000*tokenAmount, {from:outsider}); + await Employee1.processTokens(4000*tokenAmount, 4000*tokenAmount, {from: outsider, gasPrice: 0 }).should.be.rejectedWith('revert'); + await token.approve(Employee1.address, 2000*tokenAmount, {from:outsider}); + await Employee1.processTokens(2000*tokenAmount, 2000*tokenAmount, {from: outsider, gasPrice: 0 }).should.be.rejectedWith('revert'); + + await token.approve(Employee1.address, 3000*tokenAmount, {from:outsider}); + await Employee1.processTokens(3000*tokenAmount, 3000*tokenAmount, {from: outsider, gasPrice: 0 }); + await Employee1.flush({ from: creator, gasPrice: 0 }); + + var balance2 = await token.balanceOf(creator); + assert.equal(balance2.toNumber() - balance0.toNumber(), 4000*tokenAmount, 'Should get tokenAmount'); + + var needsEmployee3 = await Employee1.isNeeds({ from: creator }); + assert.equal(needsEmployee3, false, 'Dont need tokenAmount, because he got it'); + }); + + it('4: Splitter should access tokenAmount then close then not accept', async () => { + var callParams = { from: creator, gasPrice: 0 }; + var balance0 = await token.balanceOf(creator); + + var tax = await ERC20RelativeExpense.new(token.address,1000000, callParams); + + Splitter = await ERC20Splitter.new(token.address,callParams); + await Splitter.addChild(tax.address, callParams); + + var need1 = await Splitter.isNeeds({ from: creator }); + var totalNeed1 = await Splitter.getTotalNeeded(1000*tokenAmount); + assert.equal(need1, true, 'should need tokenAmount'); + assert.equal(totalNeed1.toNumber(), 1000*tokenAmount, 'should be 10% of 1000 tokenAmount'); + + await Splitter.close(callParams); + + var need3 = await Splitter.isNeeds({ from: creator }); + var totalNeed3 = await Splitter.getTotalNeeded(1000*tokenAmount); + assert.equal(need3, false, 'should not need tokenAmount'); + assert.equal(totalNeed3.toNumber(), 0, 'should be 0 tokenAmount'); + + await token.approve(Splitter.address, 1000*tokenAmount, {from:outsider}); + await Splitter.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: outsider, gasPrice: 0 }).should.be.rejectedWith('revert'); + await Splitter.open(callParams); + + var need3 = await Splitter.isNeeds({ from: creator }); + var totalNeed3 = await Splitter.getTotalNeeded(1000*tokenAmount); + assert.equal(need3, true, 'should not need tokenAmount'); + assert.equal(totalNeed3.toNumber(), 1000*tokenAmount, 'should be 0 tokenAmount'); + + await token.approve(Splitter.address, 1000*tokenAmount, {from:outsider}); + await Splitter.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: outsider, gasPrice: 0 }) + + var taxBalance = await token.balanceOf(tax.address); + assert.equal(taxBalance.toNumber(), 1000*tokenAmount, 'Tax receiver should get 100 tokenAmount'); + + }); + + it('5: should process tokenAmount with ERC20Splitter + 3 ERC20AbsoluteExpense', async () => { + // create ERC20Splitter + var erc20TopDownSplitter = await ERC20Splitter.new(token.address); + + var erc20AbsoluteExpense1 = await ERC20AbsoluteExpense.new(token.address,1*tokenAmount, 1*tokenAmount, { from: creator, gasPrice: 0 }); + var erc20AbsoluteExpense2 = await ERC20AbsoluteExpense.new(token.address,2*tokenAmount, 2*tokenAmount, { from: creator, gasPrice: 0 }); + var erc20AbsoluteExpense3 = await ERC20AbsoluteExpense.new(token.address,3*tokenAmount, 3*tokenAmount, { from: creator, gasPrice: 0 }); + + // // add 3 ERC20AbsoluteExpense outputs to the splitter + await erc20TopDownSplitter.addChild(erc20AbsoluteExpense1.address); + await erc20TopDownSplitter.addChild(erc20AbsoluteExpense2.address); + await erc20TopDownSplitter.addChild(erc20AbsoluteExpense3.address); + + // now send some tokenAmount to the revenue endpoint + await token.approve(erc20TopDownSplitter.address, 6*tokenAmount, {from:creator}); + await erc20TopDownSplitter.processTokens(6*tokenAmount, 6*tokenAmount, {from: creator }); + + // tokenAmount should end up in the outputs + var erc20AbsoluteExpense1Balance = await token.balanceOf(erc20AbsoluteExpense1.address); + assert.equal(erc20AbsoluteExpense1Balance.toNumber(), 1*tokenAmount, 'resource point received tokenAmount from splitter'); + + var erc20AbsoluteExpense2Balance = await token.balanceOf(erc20AbsoluteExpense2.address); + assert.equal(erc20AbsoluteExpense2Balance.toNumber(), 2*tokenAmount, 'resource point received tokenAmount from splitter'); + + var erc20AbsoluteExpense3Balance = await token.balanceOf(erc20AbsoluteExpense3.address); + assert.equal(erc20AbsoluteExpense3Balance.toNumber(), 3*tokenAmount, 'resource point received tokenAmount from splitter'); + }); + + it('6: should process tokenAmount with ERC20Splitter + 3 ERC20AbsoluteExpense', async () => { + // create ERC20Splitter + var erc20UnsortedSplitter = await ERC20Splitter.new(token.address); + + var erc20AbsoluteExpense1 = await ERC20AbsoluteExpense.new(token.address,1*tokenAmount, 1*tokenAmount, { from: creator, gasPrice: 0 }); + var erc20AbsoluteExpense2 = await ERC20AbsoluteExpense.new(token.address,2*tokenAmount, 2*tokenAmount, { from: creator, gasPrice: 0 }); + var erc20AbsoluteExpense3 = await ERC20AbsoluteExpense.new(token.address,3*tokenAmount, 3*tokenAmount, { from: creator, gasPrice: 0 }); + + // // add 3 ERC20AbsoluteExpense outputs to the splitter + await erc20UnsortedSplitter.addChild(erc20AbsoluteExpense1.address); + await erc20UnsortedSplitter.addChild(erc20AbsoluteExpense2.address); + await erc20UnsortedSplitter.addChild(erc20AbsoluteExpense3.address); + + // now send some tokenAmount to the revenue endpoint + await token.approve(erc20UnsortedSplitter.address, 6*tokenAmount, {from:creator}); + await erc20UnsortedSplitter.processTokens(6*tokenAmount, 6*tokenAmount, {from: creator }); + + // tokenAmount should end up in the outputs + var erc20AbsoluteExpense1Balance = await token.balanceOf(erc20AbsoluteExpense1.address); + assert.equal(erc20AbsoluteExpense1Balance.toNumber(), 1*tokenAmount, 'resource point received tokenAmount from splitter'); + + var erc20AbsoluteExpense2Balance = await token.balanceOf(erc20AbsoluteExpense2.address); + assert.equal(erc20AbsoluteExpense2Balance.toNumber(), 2*tokenAmount, 'resource point received tokenAmount from splitter'); + + var erc20AbsoluteExpense3Balance = await token.balanceOf(erc20AbsoluteExpense3.address); + assert.equal(erc20AbsoluteExpense3Balance.toNumber(), 3*tokenAmount, 'resource point received tokenAmount from splitter'); + }); + + it('7: should process tokenAmount in structure o-> o-> o-o-o', async () => { + var AllOutputs = await ERC20Splitter.new(token.address,{ from: creator, gasPrice: 0 }); + var Salaries = await ERC20Splitter.new(token.address,{ from: creator, gasPrice: 0 }); + + var Employee1 = await ERC20AbsoluteExpense.new(token.address,1000*tokenAmount, 1000*tokenAmount, { from: creator, gasPrice: 0 }); + var Employee2 = await ERC20AbsoluteExpense.new(token.address,1500*tokenAmount, 1500*tokenAmount, { from: creator, gasPrice: 0 }); + var Employee3 = await ERC20AbsoluteExpense.new(token.address,800*tokenAmount, 800*tokenAmount, { from: creator, gasPrice: 0 }); + + await AllOutputs.addChild(Salaries.address, { from: creator, gasPrice: 0 }); + + await Salaries.addChild(Employee1.address, { from: creator, gasPrice: 0 }); + await Salaries.addChild(Employee2.address, { from: creator, gasPrice: 0 }); + await Salaries.addChild(Employee3.address, { from: creator, gasPrice: 0 }); + + var Employee1Needs = await Employee1.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee1Needs.toNumber() / tokenAmount, 1000, 'Employee1 Needs 1000 tokenAmount'); + var Employee2Needs = await Employee2.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee2Needs.toNumber() / tokenAmount, 1500, 'Employee1 Needs 1500 tokenAmount'); + var Employee3Needs = await Employee3.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee3Needs.toNumber() / tokenAmount, 800, 'Employee1 Needs 800 tokenAmount'); + + var SalariesNeeds = await Salaries.getTotalNeeded(3300*tokenAmount); + assert.equal(SalariesNeeds.toNumber() / tokenAmount, 3300, 'Salaries Needs 3300 tokenAmount'); + + var SalariesMinNeeds = await Salaries.getMinNeeded(3300*tokenAmount); + assert.equal(SalariesNeeds.toNumber() / tokenAmount, 3300, 'Salaries min Needs 3300 tokenAmount'); + + var AllOutputsNeeds = await AllOutputs.getTotalNeeded(3300*tokenAmount); + assert.equal(AllOutputsNeeds.toNumber() / tokenAmount, 3300, 'AllOutputs Needs 3300 tokenAmount'); + var MinOutpultsNeeds = await AllOutputs.getMinNeeded(3300*tokenAmount); + assert.equal(AllOutputsNeeds.toNumber() / tokenAmount, 3300, 'AllOutputs Needs min 3300 tokenAmount'); + var OutputChildrenCount = await AllOutputs.getChildrenCount(); + assert.equal(OutputChildrenCount.toNumber(), 1, 'OutputChildrenCount should be 1'); + var SalariesChildrenCount = await Salaries.getChildrenCount(); + assert.equal(SalariesChildrenCount.toNumber(), 3, 'SalariesChildrenCount should be 3'); + + var th = await token.approve(Salaries.address, 3300*tokenAmount, {from:creator}); + await Salaries.processTokens(3300*tokenAmount, 3300*tokenAmount, {from: creator, gasPrice: 0 }); + }); + + it('8: should process tokenAmount in structure o-> o-o-o, while minAmount != totalAmount', async () => { + var Salaries = await ERC20Splitter.new(token.address,{ from: creator, gasPrice: 0 }); + + var Employee1 = await ERC20AbsoluteExpense.new(token.address,1000*tokenAmount, 500*tokenAmount, { from: creator, gasPrice: 0 }); + var Employee2 = await ERC20AbsoluteExpense.new(token.address,800*tokenAmount, 200*tokenAmount, { from: creator, gasPrice: 0 }); + var Employee3 = await ERC20AbsoluteExpense.new(token.address,1500*tokenAmount, 500*tokenAmount, { from: creator, gasPrice: 0 }); + + await Salaries.addChild(Employee1.address, { from: creator, gasPrice: 0 }); + await Salaries.addChild(Employee2.address, { from: creator, gasPrice: 0 }); + await Salaries.addChild(Employee3.address, { from: creator, gasPrice: 0 }); + + var Employee1Needs = await Employee1.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee1Needs.toNumber() / tokenAmount, 1000); + var Employee2Needs = await Employee2.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee2Needs.toNumber() / tokenAmount, 800); + var Employee3Needs = await Employee3.getTotalNeeded(3300*tokenAmount); + assert.equal(Employee3Needs.toNumber() / tokenAmount, 1500); + + var SalariesNeeds = await Salaries.getTotalNeeded(3300*tokenAmount); + assert.equal(SalariesNeeds.toNumber() / tokenAmount, 3300, 'Salaries Needs 3300 tokenAmount'); + + assert.equal((await Salaries.getMinNeeded(100*tokenAmount)).toNumber() / tokenAmount, 0); + assert.equal((await Salaries.getMinNeeded(200*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(300*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(400*tokenAmount)).toNumber() / tokenAmount, 400); + assert.equal((await Salaries.getMinNeeded(500*tokenAmount)).toNumber() / tokenAmount, 500); + assert.equal((await Salaries.getMinNeeded(600*tokenAmount)).toNumber() / tokenAmount, 500); + assert.equal((await Salaries.getMinNeeded(700*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(800*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(900*tokenAmount)).toNumber() / tokenAmount, 900); + assert.equal((await Salaries.getMinNeeded(1000*tokenAmount)).toNumber() / tokenAmount, 1000); + assert.equal((await Salaries.getMinNeeded(1100*tokenAmount)).toNumber() / tokenAmount, 1000); + assert.equal((await Salaries.getMinNeeded(1200*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1300*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1400*tokenAmount)).toNumber() / tokenAmount, 1400); + assert.equal((await Salaries.getMinNeeded(1500*tokenAmount)).toNumber() / tokenAmount, 1400); + assert.equal((await Salaries.getMinNeeded(1600*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(1700*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(1800*tokenAmount)).toNumber() / tokenAmount, 1800); + assert.equal((await Salaries.getMinNeeded(1900*tokenAmount)).toNumber() / tokenAmount, 1800); + assert.equal((await Salaries.getMinNeeded(2000*tokenAmount)).toNumber() / tokenAmount, 1800); + assert.equal((await Salaries.getMinNeeded(2100*tokenAmount)).toNumber() / tokenAmount, 1800); + assert.equal((await Salaries.getMinNeeded(2200*tokenAmount)).toNumber() / tokenAmount, 1800); + assert.equal((await Salaries.getMinNeeded(2300*tokenAmount)).toNumber() / tokenAmount, 2300); + assert.equal((await Salaries.getMinNeeded(2400*tokenAmount)).toNumber() / tokenAmount, 2300); + assert.equal((await Salaries.getMinNeeded(2500*tokenAmount)).toNumber() / tokenAmount, 2300); + assert.equal((await Salaries.getMinNeeded(2600*tokenAmount)).toNumber() / tokenAmount, 2300); + assert.equal((await Salaries.getMinNeeded(2700*tokenAmount)).toNumber() / tokenAmount, 2300); + assert.equal((await Salaries.getMinNeeded(2800*tokenAmount)).toNumber() / tokenAmount, 2800); + assert.equal((await Salaries.getMinNeeded(2900*tokenAmount)).toNumber() / tokenAmount, 2800); + assert.equal((await Salaries.getMinNeeded(3000*tokenAmount)).toNumber() / tokenAmount, 2800); + assert.equal((await Salaries.getMinNeeded(3100*tokenAmount)).toNumber() / tokenAmount, 2800); + assert.equal((await Salaries.getMinNeeded(3200*tokenAmount)).toNumber() / tokenAmount, 2800); + assert.equal((await Salaries.getMinNeeded(3300*tokenAmount)).toNumber() / tokenAmount, 3300); + assert.equal((await Salaries.getMinNeeded(3400*tokenAmount)).toNumber() / tokenAmount, 3300); + assert.equal((await Salaries.getMinNeeded(3500*tokenAmount)).toNumber() / tokenAmount, 3300); + + var th = await token.approve(Salaries.address, 700*tokenAmount, {from:creator}); + await Salaries.processTokens(700*tokenAmount, 700*tokenAmount, {from: creator, gasPrice: 0 }); + + assert.equal((await Salaries.getMinNeeded(100*tokenAmount)).toNumber() / tokenAmount, 0); + assert.equal((await Salaries.getMinNeeded(200*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(300*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(400*tokenAmount)).toNumber() / tokenAmount, 400); + assert.equal((await Salaries.getMinNeeded(500*tokenAmount)).toNumber() / tokenAmount, 500); + assert.equal((await Salaries.getMinNeeded(600*tokenAmount)).toNumber() / tokenAmount, 500); + assert.equal((await Salaries.getMinNeeded(700*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(800*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(900*tokenAmount)).toNumber() / tokenAmount, 900); + assert.equal((await Salaries.getMinNeeded(1000*tokenAmount)).toNumber() / tokenAmount, 900); + assert.equal((await Salaries.getMinNeeded(1100*tokenAmount)).toNumber() / tokenAmount, 1100); + assert.equal((await Salaries.getMinNeeded(1200*tokenAmount)).toNumber() / tokenAmount, 1100); + assert.equal((await Salaries.getMinNeeded(1300*tokenAmount)).toNumber() / tokenAmount, 1100); + assert.equal((await Salaries.getMinNeeded(1400*tokenAmount)).toNumber() / tokenAmount, 1100); + assert.equal((await Salaries.getMinNeeded(1500*tokenAmount)).toNumber() / tokenAmount, 1100); + assert.equal((await Salaries.getMinNeeded(1600*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(1700*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(1800*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(1900*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(2000*tokenAmount)).toNumber() / tokenAmount, 1600); + assert.equal((await Salaries.getMinNeeded(2100*tokenAmount)).toNumber() / tokenAmount, 2100); + assert.equal((await Salaries.getMinNeeded(2200*tokenAmount)).toNumber() / tokenAmount, 2100); + assert.equal((await Salaries.getMinNeeded(2300*tokenAmount)).toNumber() / tokenAmount, 2100); + assert.equal((await Salaries.getMinNeeded(2400*tokenAmount)).toNumber() / tokenAmount, 2100); + assert.equal((await Salaries.getMinNeeded(2500*tokenAmount)).toNumber() / tokenAmount, 2100); + assert.equal((await Salaries.getMinNeeded(2600*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(2700*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(2800*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(2900*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3000*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3100*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3200*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3300*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3400*tokenAmount)).toNumber() / tokenAmount, 2600); + assert.equal((await Salaries.getMinNeeded(3500*tokenAmount)).toNumber() / tokenAmount, 2600); + + var th = await token.approve(Salaries.address, 900*tokenAmount, {from:creator}); + await Salaries.processTokens(900*tokenAmount, 900*tokenAmount, {from: creator, gasPrice: 0 }); + + assert.equal((await Salaries.getMinNeeded(100*tokenAmount)).toNumber() / tokenAmount, 0); + assert.equal((await Salaries.getMinNeeded(200*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(300*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(400*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(500*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(600*tokenAmount)).toNumber() / tokenAmount, 200); + assert.equal((await Salaries.getMinNeeded(700*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(800*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(900*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(1000*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(1100*tokenAmount)).toNumber() / tokenAmount, 700); + assert.equal((await Salaries.getMinNeeded(1200*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1300*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1400*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1500*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1600*tokenAmount)).toNumber() / tokenAmount, 1200); + assert.equal((await Salaries.getMinNeeded(1700*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(1800*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(1900*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2000*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2100*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2200*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2300*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2400*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2500*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2600*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2700*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2800*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(2900*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3000*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3100*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3200*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3300*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3400*tokenAmount)).toNumber() / tokenAmount, 1700); + assert.equal((await Salaries.getMinNeeded(3500*tokenAmount)).toNumber() / tokenAmount, 1700); + + var th = await token.approve(Salaries.address, 200*tokenAmount, {from:creator}); + await Salaries.processTokens(200*tokenAmount, 200*tokenAmount, {from: creator, gasPrice: 0 }); + + var th = await token.approve(Salaries.address, 1500*tokenAmount, {from:creator}); + await Salaries.processTokens(1500*tokenAmount, 1500*tokenAmount, {from: creator, gasPrice: 0 }); + + var th = await token.approve(Salaries.address, 200*tokenAmount, {from:creator}); + await Salaries.processTokens(200*tokenAmount, 200*tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + }); + + it('9: should process tokenAmount with a scheme just like in the paper: 75/25 others, send MORE than minNeed; ', async () => { + const CURRENT_INPUT = 30900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 750000; + var dividends = 250000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, CURRENT_INPUT*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(CURRENT_INPUT*tokenAmount, CURRENT_INPUT*tokenAmount, {from: creator, gasPrice: 0 }); + + var balances = await getBalances(token, struct); + await balancesAsserts(balances, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await splitterBalancesAsserts(balances, tokenAmount, 0, 0, 0, 0, 0, 0, 0); + }); + + it('10: should process tokenAmount with a scheme just like in the paper: 75/25 others, send EQUAL to minNeed', async () => { + const CURRENT_INPUT = 5900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 750000; + var dividends = 250000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + // console.log('------------ struct.AllOutputs.address ', struct); + await token.approve(struct.AllOutputs.address, CURRENT_INPUT*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(CURRENT_INPUT*tokenAmount, CURRENT_INPUT*tokenAmount, {from: creator, gasPrice: 0 }); + + var balances = await getBalances(token, struct); + await balancesAsserts(balances, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, 0, 0, 0, 0, 0); + await splitterBalancesAsserts(balances, tokenAmount, 0, 0, 0, 0, 0, 0, 0); + }); + + it('11: should not process tokenAmount: send LESS than minNeed', async () => { + const CURRENT_INPUT = 5900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 750000; + var dividends = 250000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, 1000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + await token.approve(struct.AllOutputs.address, 1000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000000*tokenAmount, 1000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + await token.approve(struct.AllOutputs.address, 1000000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000*tokenAmount, 1000000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + }); + + it('12: should process tokenAmount with a scheme just like in the paper: 10/15 others, send MORE than minNeed; ', async () => { + const CURRENT_INPUT = 20900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 850000; + var dividends = 150000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, CURRENT_INPUT*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(CURRENT_INPUT*tokenAmount, CURRENT_INPUT*tokenAmount, {from: creator, gasPrice: 0 }); + + var balances = await getBalances(token, struct); + await balancesAsserts(balances, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await splitterBalancesAsserts(balances, tokenAmount, 0, 0, 0, 0, 0, 0, 0); + }); + + it('13: should NOT process tokenAmount (splitter can not accumulate tokenAmount) with a scheme just like in the paper: 10/15 others, send MORE than minNeed; ', async () => { + const CURRENT_INPUT = 20900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 100000; + var dividends = 150000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, CURRENT_INPUT*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(CURRENT_INPUT*tokenAmount, CURRENT_INPUT*tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + }); + + it('14: should process tokenAmount with a scheme just like in the paper: 10/15 others, send EQUAL to minNeed; ', async () => { + const CURRENT_INPUT = 5900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 100000; + var dividends = 150000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, CURRENT_INPUT*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(CURRENT_INPUT*tokenAmount, CURRENT_INPUT*tokenAmount, {from: creator, gasPrice: 0 }); + + var balances = await getBalances(token, struct); + await balancesAsserts(balances, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, 0, 0, 0, 0, 0); + await splitterBalancesAsserts(balances, tokenAmount, 0, 0, 0, 0, 0, 0, 0); + }); + + it('15: should not process tokenAmount: send LESS than minNeed; ', async () => { + const CURRENT_INPUT = 30900; + var e1 = 1000; + var e2 = 1500; + var e3 = 800; + var office = 500; + var internet = 300; + var t1 = 500; + var t2 = 300; + var t3 = 1000; + var b1 = 10000; + var b2 = 10000; + var b3 = 20000; + var reserve = 100000; + var dividends = 150000; + + var struct = await createStructure(token, creator, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + var splitterParams = await getSplitterParams(struct, CURRENT_INPUT, tokenAmount, creator); + await totalAndMinNeedsAsserts(splitterParams, CURRENT_INPUT, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.AllOutputs.address, 1000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000*tokenAmount, 1000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + await token.approve(struct.AllOutputs.address, 1000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000000*tokenAmount, 1000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + await token.approve(struct.AllOutputs.address, 1000000*tokenAmount, {from:creator}); + await struct.AllOutputs.processTokens(1000*tokenAmount, 1000000*tokenAmount, {from: creator }).should.be.rejectedWith('revert'); + }); + + it('16: should process tokenAmount with ERC20Splitter + 3 ERC20RelativeExpenseWithPeriod', async () => { + // create ERC20Splitter + var splitter = await ERC20Splitter.new(token.address); + + var rel1 = await ERC20RelativeExpenseWithPeriod.new(token.address,100000, 24, { from: creator, gasPrice: 0 }); + var rel2 = await ERC20RelativeExpenseWithPeriod.new(token.address,250000, 24, { from: creator, gasPrice: 0 }); + var rel3 = await ERC20RelativeExpenseWithPeriod.new(token.address,370000, 48, { from: creator, gasPrice: 0 }); + + // // add 3 rel expense outputs to the splitter + await splitter.addChild(rel1.address); + await splitter.addChild(rel2.address); + await splitter.addChild(rel3.address); + + var targets = [splitter, rel1, rel2, rel3]; + var flowArr = [1000, 1000, 1000, 1000]; + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [720, 100, 250, 370]); + await checkIsNeed(targets, [true, true, true, true]); + + // now send some tokenAmount to the revenue endpoint + await token.approve(splitter.address, 720*tokenAmount, {from:creator}); + await splitter.processTokens(1000*tokenAmount, 720*tokenAmount, {from: creator}); + + assert.equal((await token.balanceOf(rel1.address)).toNumber(), 100*tokenAmount); + assert.equal((await token.balanceOf(rel2.address)).toNumber(), 250*tokenAmount); + assert.equal((await token.balanceOf(rel3.address)).toNumber(), 370*tokenAmount); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [0, 0, 0, 0]); + await checkIsNeed(targets, [false, false, false, false]); + + await passHours(24); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [350, 100, 250, 0]); + await checkIsNeed(targets, [true, true, true, false]); + + await passHours(24); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [720, 100, 250, 370]); + await checkIsNeed(targets, [true, true, true, true]); + + await token.approve(splitter.address, 720*tokenAmount, {from:creator}); + await splitter.processTokens(1000*tokenAmount, 720*tokenAmount, {from: creator }); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [0, 0, 0, 0]); + await checkIsNeed(targets, [false, false, false, false]); + + // tokenAmount should end up in the outputs + assert.equal((await token.balanceOf(rel1.address)).toNumber(), 200*tokenAmount); + assert.equal((await token.balanceOf(rel2.address)).toNumber(), 500*tokenAmount); + assert.equal((await token.balanceOf(rel3.address)).toNumber(), 740*tokenAmount); + + await passHours(24); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [350, 100, 250, 0]); + await checkIsNeed(targets, [true, true, true, false]); + + await token.approve(splitter.address, 350*tokenAmount, {from:creator}); + await splitter.processTokens(1000*tokenAmount, 350*tokenAmount, {from: creator }); + + await checkMinNeed(targets, flowArr, [0, 0, 0, 0]); + await checkTotalNeed(targets, flowArr, [0, 0, 0, 0]); + await checkIsNeed(targets, [false, false, false, false]); + }); +}); \ No newline at end of file diff --git a/test/ERC20_tests/ERC20_fund.tests.js b/test/ERC20_tests/ERC20_fund.tests.js new file mode 100644 index 0000000..4dc292e --- /dev/null +++ b/test/ERC20_tests/ERC20_fund.tests.js @@ -0,0 +1,385 @@ +var IReceiver = artifacts.require('./IReceiver'); + +var StandardToken = artifacts.require('./ERC20Token'); + +var ERC20Splitter = artifacts.require('./ERC20Splitter'); +var ERC20AbsoluteExpense = artifacts.require('./ERC20AbsoluteExpense'); +var ERC20RelativeExpense = artifacts.require('./ERC20RelativeExpense'); +var ERC20AbsoluteExpenseWithPeriod = artifacts.require('./ERC20AbsoluteExpenseWithPeriod'); +var ERC20RelativeExpenseWithPeriod = artifacts.require('./ERC20RelativeExpenseWithPeriod'); +var ERC20AbsoluteExpenseWithPeriodSliding = artifacts.require('./ERC20AbsoluteExpenseWithPeriodSliding'); +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') + .use(require('chai-as-promised')) + .use(require('chai-bignumber')(BigNumber)) + .should(); + +contract('ERC20Fund', (accounts) => { + var token; + let tokenAmount = 1e7; + + const creator = accounts[0]; + const employee1 = accounts[1]; + const employee2 = accounts[2]; + const outsider = accounts[3]; + + beforeEach(async () => { + token = await StandardToken.new(); + await token.mint(accounts[0], 1e25); + await token.mint(accounts[1], 1e25); + await token.mint(accounts[2], 1e25); + await token.mint(accounts[3], 1e25); + await token.mint(accounts[4], 1e25); + await token.mint(accounts[5], 1e25); + await token.mint(accounts[6], 1e25); + await token.mint(accounts[7], 1e25); + await token.mint(accounts[8], 1e25); + await token.mint(accounts[9], 1e25); + }); + + it('Should collect tokenAmount, then revert if more, then flush', async () => { + let fund = await ERC20AbsoluteExpense.new(token.address,1e18, 0); + + var totalNeed = await fund.getTotalNeeded(1e22); + var minNeed = await fund.getMinNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(fund.address, 3e17, {from:creator}); + await fund.processTokens(3e17, 3e17, {from: creator }); + await token.approve(fund.address , 3e17, {from:employee1}); + await fund.processTokens(3e17, 3e17, {from: employee1 }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var minNeed = await fund.getMinNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 4e17); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(fund.address , 5e17, {from:employee2}); + await fund.processTokens(5e17, 5e17, {from: employee2 }).should.be.rejectedWith('revert'); // overflow + await token.approve(fund.address , 4e17, {from:employee2}); + await fund.processTokens(4e17, 4e17, {from: employee2 }); + await token.approve(fund.address , 1e17, {from:employee2}); + await fund.processTokens(1e17, 1e17, {from: employee2 }).should.be.rejectedWith('revert'); // overflow + + var totalNeed = await fund.getTotalNeeded(1e22); + var minNeed = await fund.getMinNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, false); + + var b1 = await token.balanceOf(employee1); + await fund.flushTo(employee1); + var b2 = await token.balanceOf(employee1); + assert.equal((new web3.BigNumber(b2).sub(b1)).toNumber(), 1e18); + + var totalNeed = await fund.getTotalNeeded(1e22); + var minNeed = await fund.getMinNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, false); + }); + + it('Should collect tokenAmount (periodic, not accumulate debt), then time passed, then need tokenAmount again', async () => { + let fund = await ERC20AbsoluteExpenseWithPeriod.new(token.address,1e18, 0, 24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await token.approve(fund.address, 1e18, {from:creator}); + await fund.processTokens(1e18, 1e18, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(23); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(1); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var totalNeed2 = await fund.getTotalNeeded(5e17); + var minNeed = await fund.getMinNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(totalNeed2.toNumber(), 5e17); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(fund.address, 5e17, {from:creator}); + await fund.processTokens(5e17, 5e17, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 5e17); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 5e17); + assert.equal(isNeed, true); + + await token.approve(fund.address, 5e17, {from:creator}); + await fund.processTokens(5e17, 5e17, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(isNeed, false); + }); + + it('Should collect tokenAmount (periodic, accumulate debt), then time passed, then need tokenAmount again', async () => { + let fund = await ERC20AbsoluteExpenseWithPeriodSliding.new(token.address,1e18, 0, 24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await token.approve(fund.address, 1e18, {from:creator}); + await fund.processTokens(1e18, 1e18, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(23); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + + await passHours(1); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 2e18); + assert.equal(isNeed, true); + + await token.approve(fund.address, 5e17, {from:creator}); + await fund.processTokens(5e17, 5e17, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1.5e18); + assert.equal(isNeed, true); + + await passHours(24); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 2.5e18); + assert.equal(isNeed, true); + + await token.approve(fund.address, 2.5e18, {from:creator}); + await fund.processTokens(2.5e18, 2.5e18, {from: creator }); + + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(isNeed, false); + }); + + it('Should collect tokenAmount (periodic, accumulate debt), then time passed, then need tokenAmount again', async () => { + let fund = await ERC20AbsoluteExpenseWithPeriodSliding.new(token.address,1e18, 0, 24); + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(isNeed, true); + + await passHours(48); + var totalNeed = await fund.getTotalNeeded(1e22); + var isNeed = await fund.isNeeds(); + assert.equal(totalNeed.toNumber(), 3e18); + assert.equal(isNeed, true); + }); + + it('Should implement roadmap pattern with funds (-> abs-abs-abs)', async () => { + let splitter = await ERC20Splitter.new(token.address); + + let milestone1 = await ERC20AbsoluteExpense.new(token.address,0.1e18, 0); + let milestone2 = await ERC20AbsoluteExpense.new(token.address,0.2e18, 0); + let milestone3 = await ERC20AbsoluteExpense.new(token.address,0.7e18, 0); + await splitter.addChild(milestone1.address); + await splitter.addChild(milestone2.address); + await splitter.addChild(milestone3.address); + + var totalNeed = await splitter.getTotalNeeded(1e22); + var minNeed = await splitter.getMinNeeded(1e22); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(splitter.address, 0.01e18, {from:creator}); + await splitter.processTokens(0.01e18, 0.01e18, {from: creator }); + + assert.equal(0.01, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + await token.approve(splitter.address, 0.03e18, {from:creator}); + await splitter.processTokens(0.03e18, 0.03e18, {from: creator }); + + assert.equal(0.04, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + await token.approve(splitter.address, 0.08e18, {from:creator}); + await splitter.processTokens(0.08e18, 0.08e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.02, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + var totalNeed = await splitter.getTotalNeeded(1e22); + var minNeed = await splitter.getMinNeeded(1e22); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 0.88e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(splitter.address, 0.4e18, {from:creator}); + await splitter.processTokens(0.4e18, 0.4e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.2, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0.22, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + await token.approve(splitter.address, 0.48e18, {from:creator}); + await splitter.processTokens(0.48e18, 0.48e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.2, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0.7, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + var totalNeed = await splitter.getTotalNeeded(1e22); + var minNeed = await splitter.getMinNeeded(1e22); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 0); + assert.equal(minNeed.toNumber(), 0); + + await token.approve(splitter.address, 0.5e18, {from:creator}); + await splitter.processTokens(0.5e18, 0.5e18, {from: creator }).should.be.rejectedWith('revert'); + }); + + it('Should implement roadmap pattern with funds (-> abs-abs-abs-bigCap)', async () => { + let splitter = await ERC20Splitter.new(token.address); + + let milestone1 = await ERC20AbsoluteExpense.new(token.address,0.1e18, 0); + let milestone2 = await ERC20AbsoluteExpense.new(token.address,0.2e18, 0); + let milestone3 = await ERC20AbsoluteExpense.new(token.address,0.7e18, 0); + let stabFund = await ERC20AbsoluteExpense.new(token.address,1e30, 0); + await splitter.addChild(milestone1.address); + await splitter.addChild(milestone2.address); + await splitter.addChild(milestone3.address); + await splitter.addChild(stabFund.address); + + var totalNeed = await splitter.getTotalNeeded(1e18); + var minNeed = await splitter.getMinNeeded(1e18); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(splitter.address, 0.01e18, {from:creator}); + await splitter.processTokens(0.01e18, 0.01e18, {from: creator }); + + assert.equal(0.01, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + await token.approve(splitter.address, 0.03e18, {from:creator}); + await splitter.processTokens(0.03e18, 0.03e18, {from: creator }); + + assert.equal(0.04, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + await token.approve(splitter.address, 0.08e18, {from:creator}); + await splitter.processTokens(0.08e18, 0.08e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.02, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + var totalNeed = await splitter.getTotalNeeded(0.88e18); + var minNeed = await splitter.getMinNeeded(0.88e18); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 0.88e18); + assert.equal(minNeed.toNumber(), 0); + assert.equal(isNeed, true); + + await token.approve(splitter.address, 0.4e18, {from:creator}); + await splitter.processTokens(0.4e18, 0.4e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.2, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0.22, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + await token.approve(splitter.address, 0.48e18, {from:creator}); + await splitter.processTokens(0.48e18, 0.48e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.2, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0.7, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + + var totalNeed = await splitter.getTotalNeeded(1e18); + var minNeed = await splitter.getMinNeeded(1e18); + var isNeed = await splitter.isNeeds(); + assert.equal(totalNeed.toNumber(), 1e18); + assert.equal(minNeed.toNumber(), 0); + + await token.approve(splitter.address, 0.3e18, {from:creator}); + await splitter.processTokens(0.3e18, 0.3e18, {from: creator }); + await token.approve(splitter.address, 0.5e18, {from:creator}); + await splitter.processTokens(0.5e18, 0.5e18, {from: creator }); + await token.approve(splitter.address, 0.7e18, {from:creator}); + await splitter.processTokens(0.7e18, 0.7e18, {from: creator }); + + assert.equal(0.1, (await token.balanceOf(milestone1.address)).toNumber() / 1e18); + assert.equal(0.2, (await token.balanceOf(milestone2.address)).toNumber() / 1e18); + assert.equal(0.7, (await token.balanceOf(milestone3.address)).toNumber() / 1e18); + assert.equal(1.5, (await token.balanceOf(stabFund.address)).toNumber() / 1e18); + }); +}); diff --git a/test/ERC20_tests/ERC20_table.tests.js b/test/ERC20_tests/ERC20_table.tests.js new file mode 100644 index 0000000..9465372 --- /dev/null +++ b/test/ERC20_tests/ERC20_table.tests.js @@ -0,0 +1,594 @@ +var ERC20Table = artifacts.require('./ERC20Table'); +var IReceiver = artifacts.require('./IReceiver'); + +var StandardToken = artifacts.require('./ERC20Token'); + +var ERC20Splitter = artifacts.require('./ERC20Splitter'); +var ERC20AbsoluteExpense = artifacts.require('./ERC20AbsoluteExpense'); +var ERC20RelativeExpense = artifacts.require('./ERC20RelativeExpense'); +var ERC20AbsoluteExpenseWithPeriod = artifacts.require('./ERC20AbsoluteExpenseWithPeriod'); +var ERC20RelativeExpenseWithPeriod = artifacts.require('./ERC20RelativeExpenseWithPeriod'); + +var getEId = o => o.logs.filter(l => l.event == 'NodeAdded')[0].args._eId.toNumber(); + +function KECCAK256 (x) { + return web3.sha3(x); +} + +const BigNumber = web3.BigNumber; + +require('chai') + .use(require('chai-as-promised')) + .use(require('chai-bignumber')(BigNumber)) + .should(); + +function KECCAK256 (x) { + return web3.sha3(x); +} + +async function createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends) { + var o = {}; + o.erc20Table = await ERC20Table.new(token.address); + + o.AllOutputsId = getEId(await o.erc20Table.addSplitter()); + o.SpendsId = getEId(await o.erc20Table.addSplitter()); + o.SalariesId = getEId(await o.erc20Table.addSplitter()); + o.Employee1Id = getEId(await o.erc20Table.addAbsoluteExpense(e1 * tokenAmount, e1 * tokenAmount, false, false, 0)); + o.Employee2Id = getEId(await o.erc20Table.addAbsoluteExpense(e2 * tokenAmount, e2 * tokenAmount, false, false, 0)); + o.Employee3Id = getEId(await o.erc20Table.addAbsoluteExpense(e3 * tokenAmount, e3 * tokenAmount, false, false, 0)); + o.OtherId = getEId(await o.erc20Table.addSplitter()); + o.OfficeId = getEId(await o.erc20Table.addAbsoluteExpense(office * tokenAmount, office * tokenAmount, false, false, 0)); + o.InternetId = getEId(await o.erc20Table.addAbsoluteExpense(internet * tokenAmount, internet * tokenAmount, false, false, 0)); + o.TasksId = getEId(await o.erc20Table.addSplitter()); + o.Task1Id = getEId(await o.erc20Table.addAbsoluteExpense(t1 * tokenAmount, t1 * tokenAmount, false, false, 0)); + o.Task2Id = getEId(await o.erc20Table.addAbsoluteExpense(t2 * tokenAmount, t2 * tokenAmount, false, false, 0)); + o.Task3Id = getEId(await o.erc20Table.addAbsoluteExpense(t3 * tokenAmount, t3 * tokenAmount, false, false, 0)); + o.BonusesId = getEId(await o.erc20Table.addSplitter()); + o.Bonus1Id = getEId(await o.erc20Table.addRelativeExpense(b1, false, false, 0)); + o.Bonus2Id = getEId(await o.erc20Table.addRelativeExpense(b2, false, false, 0)); + o.Bonus3Id = getEId(await o.erc20Table.addRelativeExpense(b3, false, false, 0)); + o.RestId = getEId(await o.erc20Table.addSplitter()); + o.DividendsFundId = getEId(await o.erc20Table.addAbsoluteExpense(dividends * tokenAmount, 0, false, false, 0)); + o.ReserveFundId = getEId(await o.erc20Table.addAbsoluteExpense(reserve * tokenAmount, 0, false, false, 0)); + + await o.erc20Table.addChildAt(o.AllOutputsId, o.SpendsId); + await o.erc20Table.addChildAt(o.SpendsId, o.SalariesId); + await o.erc20Table.addChildAt(o.SalariesId, o.Employee1Id); + await o.erc20Table.addChildAt(o.SalariesId, o.Employee2Id); + await o.erc20Table.addChildAt(o.SalariesId, o.Employee3Id); + await o.erc20Table.addChildAt(o.SpendsId, o.OtherId); + await o.erc20Table.addChildAt(o.OtherId, o.OfficeId); + await o.erc20Table.addChildAt(o.OtherId, o.InternetId); + await o.erc20Table.addChildAt(o.SpendsId, o.TasksId); + await o.erc20Table.addChildAt(o.TasksId, o.Task1Id); + await o.erc20Table.addChildAt(o.TasksId, o.Task2Id); + await o.erc20Table.addChildAt(o.TasksId, o.Task3Id); + await o.erc20Table.addChildAt(o.AllOutputsId, o.BonusesId); + await o.erc20Table.addChildAt(o.BonusesId, o.Bonus1Id); + await o.erc20Table.addChildAt(o.BonusesId, o.Bonus2Id); + await o.erc20Table.addChildAt(o.BonusesId, o.Bonus3Id); + await o.erc20Table.addChildAt(o.AllOutputsId, o.RestId); + await o.erc20Table.addChildAt(o.RestId, o.DividendsFundId); + await o.erc20Table.addChildAt(o.RestId, o.ReserveFundId); + return o; +} + +async function totalAndMinNeedsAsserts (tokenAmount, i, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends) { + var totalSpend = e1 + e2 + e3 + t1 + t2 + t3 + office + internet; + var bonusesSpendPercent = (CURRENT_INPUT - totalSpend) / 1000000; + var rest = CURRENT_INPUT - totalSpend - (bonusesSpendPercent * (b1 + b2 + b3)); + + var dividendsAmount = 0; + var reserveAmount = 0; + if(rest<=0){ + + }else if(rest<=dividends){ + dividendsAmount = rest; + reserveAmount = 0; + }else{ + dividendsAmount = dividends; + reserveAmount = rest - dividendsAmount; + } + var allNeeds = totalSpend + (bonusesSpendPercent * (b1 + b2 + b3)) + (dividendsAmount + reserveAmount); + + assert.equal((new web3.BigNumber(i.AllOutputsTotalNeed).div(tokenAmount)).toNumber(), allNeeds, `AllOutputs Total Need should be ${allNeeds}`); + assert.equal((new web3.BigNumber(i.AllOutputsMinNeed).div(tokenAmount)).toNumber(), totalSpend, `AllOutputs min Need should be ${totalSpend}`); + assert.equal((new web3.BigNumber(i.SpendsTotalNeed).div(tokenAmount)).toNumber(), totalSpend, `Spends Total Need should be ${totalSpend}`); + assert.equal((new web3.BigNumber(i.SpendsMinNeed).div(tokenAmount)).toNumber(), totalSpend, `Spends min Need should be ${totalSpend}`); + assert.equal((new web3.BigNumber(i.SalariesTotalNeed).div(tokenAmount)).toNumber(), e1 + e2 + e3, `Salaries Total Need should be ${e1 + e2 + e3}`); + assert.equal((new web3.BigNumber(i.SalariesMinNeed).div(tokenAmount)).toNumber(), e1 + e2 + e3, `Salaries min Need should be ${e1 + e2 + e3}`); + assert.equal((new web3.BigNumber(i.OtherTotalNeed).div(tokenAmount)).toNumber(), office + internet, `Other Total Need should be ${office + internet}`); + assert.equal((new web3.BigNumber(i.OtherMinNeed).div(tokenAmount)).toNumber(), office + internet, `Other min Need should be ${office + internet}`); + assert.equal((new web3.BigNumber(i.TasksTotalNeed).div(tokenAmount)).toNumber(), t1 + t2 + t3, `Tasks Total Need should be ${t1 + t2 + t3}`); + assert.equal((new web3.BigNumber(i.TasksMinNeed).div(tokenAmount)).toNumber(), t1 + t2 + t3, `Tasks min Need should be ${t1 + t2 + t3}`); + assert.equal((new web3.BigNumber(i.BonusesTotalNeed).div(tokenAmount)).toNumber(), (b1 + b2 + b3) * CURRENT_INPUT / 1000000, `Bonuses Total Need should be ${(b1 + b2 + b3) * CURRENT_INPUT / 1000000}`); + assert.equal((new web3.BigNumber(i.BonusesMinNeed).div(tokenAmount)).toNumber(), 0, `Bonuses min Need should be ${0}`); + assert.equal((new web3.BigNumber(i.RestTotalNeed).div(tokenAmount)).toNumber(), CURRENT_INPUT, `Rest Total Need should be ${rest}`); + assert.equal((new web3.BigNumber(i.RestMinNeed).div(tokenAmount)).toNumber(), 0, `Rest min Need should be ${0}`); +} + +async function getBalances (i) { + var o = {}; + o.Employee1Balance = await i.erc20Table.balanceAt(i.Employee1Id); + o.Employee2Balance = await i.erc20Table.balanceAt(i.Employee2Id); + o.Employee3Balance = await i.erc20Table.balanceAt(i.Employee3Id); + o.OfficeBalance = await i.erc20Table.balanceAt(i.OfficeId); + o.InternetBalance = await i.erc20Table.balanceAt(i.InternetId); + o.Task1Balance = await i.erc20Table.balanceAt(i.Task1Id); + o.Task2Balance = await i.erc20Table.balanceAt(i.Task2Id); + o.Task3Balance = await i.erc20Table.balanceAt(i.Task3Id); + o.ReserveBalance = await i.erc20Table.balanceAt(i.ReserveFundId); + o.DividendsBalance = await i.erc20Table.balanceAt(i.DividendsFundId); + o.Bonus1Balance = await i.erc20Table.balanceAt(i.Bonus1Id); + o.Bonus2Balance = await i.erc20Table.balanceAt(i.Bonus2Id); + o.Bonus3Balance = await i.erc20Table.balanceAt(i.Bonus3Id); + o.AllOutputsBalance = await i.erc20Table.balanceAt(i.AllOutputsId); + o.SpendsBalance = await i.erc20Table.balanceAt(i.SpendsId); + o.SalariesBalance = await i.erc20Table.balanceAt(i.SalariesId); + o.OtherBalance = await i.erc20Table.balanceAt(i.OtherId); + o.TasksBalance = await i.erc20Table.balanceAt(i.TasksId); + o.BonusesBalance = await i.erc20Table.balanceAt(i.BonusesId); + o.RestBalance = await i.erc20Table.balanceAt(i.RestId); + + return o; +} + +async function getSplitterParams (tokenAmount, i, CURRENT_INPUT) { + var o = {}; + o.AllOutputsTotalNeed = await i.erc20Table.getTotalNeededAt(i.AllOutputsId, CURRENT_INPUT * tokenAmount); + o.AllOutputsMinNeed = await i.erc20Table.getMinNeededAt(i.AllOutputsId, CURRENT_INPUT * tokenAmount); + o.AllOutputsChildrenCount = await i.erc20Table.getChildrenCountAt(i.AllOutputsId); + o.SpendsTotalNeed = await i.erc20Table.getTotalNeededAt(i.SpendsId, CURRENT_INPUT * tokenAmount); + o.SpendsMinNeed = await i.erc20Table.getMinNeededAt(i.SpendsId, CURRENT_INPUT * tokenAmount); + o.SpendsChildrenCount = await i.erc20Table.getChildrenCountAt(i.SpendsId); + o.SalariesTotalNeed = await i.erc20Table.getTotalNeededAt(i.SalariesId, CURRENT_INPUT * tokenAmount); + o.SalariesMinNeed = await i.erc20Table.getMinNeededAt(i.SalariesId, CURRENT_INPUT * tokenAmount); + o.SalariesChildrenCount = await i.erc20Table.getChildrenCountAt(i.SalariesId); + o.OtherTotalNeed = await i.erc20Table.getTotalNeededAt(i.OtherId, CURRENT_INPUT * tokenAmount); + o.OtherMinNeed = await i.erc20Table.getMinNeededAt(i.OtherId, CURRENT_INPUT * tokenAmount); + o.OtherChildrenCount = await i.erc20Table.getChildrenCountAt(i.OtherId); + o.TasksTotalNeed = await i.erc20Table.getTotalNeededAt(i.TasksId, CURRENT_INPUT * tokenAmount); + o.TasksMinNeed = await i.erc20Table.getMinNeededAt(i.TasksId, CURRENT_INPUT * tokenAmount); + o.TasksChildrenCount = await i.erc20Table.getChildrenCountAt(i.TasksId); + o.BonusesTotalNeed = await i.erc20Table.getTotalNeededAt(i.BonusesId, CURRENT_INPUT * tokenAmount); + o.BonusesMinNeed = await i.erc20Table.getMinNeededAt(i.BonusesId, CURRENT_INPUT * tokenAmount); + o.BonusesChildrenCount = await i.erc20Table.getChildrenCountAt(i.BonusesId); + o.RestTotalNeed = await i.erc20Table.getTotalNeededAt(i.RestId, CURRENT_INPUT * tokenAmount); + o.RestMinNeed = await i.erc20Table.getMinNeededAt(i.RestId, CURRENT_INPUT * tokenAmount); + o.RestChildrenCount = await i.erc20Table.getChildrenCountAt(i.RestId); + + return o; +} + +async function structureAsserts (i) { + assert.equal(i.AllOutputsChildrenCount.toNumber(), 3, 'Children count should be 3'); + assert.equal(i.SpendsChildrenCount.toNumber(), 3, 'Children count should be 3'); + assert.equal(i.SalariesChildrenCount.toNumber(), 3, 'Children count should be 3'); + assert.equal(i.OtherChildrenCount.toNumber(), 2, 'Children count should be 2'); + assert.equal(i.TasksChildrenCount.toNumber(), 3, 'Children count should be 3'); + assert.equal(i.BonusesChildrenCount.toNumber(), 3, 'Children count should be 3'); + assert.equal(i.RestChildrenCount.toNumber(), 2, 'Children count should be 2'); +} + +async function balancesAsserts (tokenAmount, i, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends) { + var totalSpend = e1 + e2 + e3 + t1 + t2 + t3 + office + internet; + var bonusesSpendPercent = (CURRENT_INPUT - totalSpend) / 1000000; + var rest = CURRENT_INPUT - totalSpend - (bonusesSpendPercent * (b1 + b2 + b3)); + + var dividendsAmount = 0; + var reserveAmount = 0; + if((rest<=dividends)&&(rest>0)){ + dividendsAmount = rest; + reserveAmount = 0; + }else{ + dividendsAmount = dividends; + reserveAmount = rest - dividendsAmount; + } + + assert.equal((new web3.BigNumber(i.Employee1Balance).div(tokenAmount)).toNumber(), e1, `Employee1 balance should be ${e1} tokenAmount`); + assert.equal((new web3.BigNumber(i.Employee2Balance).div(tokenAmount)).toNumber(), e2, `Employee2 balance should be ${e2} tokenAmount`); + assert.equal((new web3.BigNumber(i.Employee3Balance).div(tokenAmount)).toNumber(), e3, `Employee3 balance should be ${e3} tokenAmount`); + assert.equal((new web3.BigNumber(i.OfficeBalance).div(tokenAmount)).toNumber(), office, `Office balance should be ${office} tokenAmount`); + assert.equal((new web3.BigNumber(i.InternetBalance).div(tokenAmount)).toNumber(), internet, `Internet balance should be ${internet} tokenAmount`); + assert.equal((new web3.BigNumber(i.Task1Balance).div(tokenAmount)).toNumber(), t1, `Task1 balance should be ${t1} tokenAmount`); + assert.equal((new web3.BigNumber(i.Task2Balance).div(tokenAmount)).toNumber(), t2, `Task2 balance should be ${t2} tokenAmount`); + assert.equal((new web3.BigNumber(i.Task3Balance).div(tokenAmount)).toNumber(), t3, `Task3 balance should be ${t3} tokenAmount`); + assert.equal((new web3.BigNumber(i.Bonus1Balance).div(tokenAmount)).toNumber(), bonusesSpendPercent * b1, `Bonus1 balance should be ${bonusesSpendPercent * b1} tokenAmount`); + assert.equal((new web3.BigNumber(i.Bonus2Balance).div(tokenAmount)).toNumber(), bonusesSpendPercent * b2, `Bonus2 balance should be ${bonusesSpendPercent * b2} tokenAmount`); + assert.equal((new web3.BigNumber(i.Bonus3Balance).div(tokenAmount)).toNumber(), bonusesSpendPercent * b3, `Bonus3 balance should be ${bonusesSpendPercent * b3} tokenAmount`); + assert.equal((new web3.BigNumber(i.DividendsBalance).div(tokenAmount)).toNumber(), dividendsAmount, `Dividends balance should be ${dividendsAmount} tokenAmount`); + assert.equal((new web3.BigNumber(i.ReserveBalance).div(tokenAmount)).toNumber(), reserveAmount, `Reserve balance should be ${reserveAmount} tokenAmount`); + +} + +contract('ERC20Table tests', (accounts) => { + var token; + var store; + var daoBase; + + var tokenAmount = 1e12; + var isPeriodic = false; + var isAccumulateDebt = false; + var periodHours = 0; + var output = '0x0'; + + const creator = accounts[0]; + const employee1 = accounts[1]; + const employee2 = accounts[2]; + const outsider = accounts[3]; + + beforeEach(async () => { + token = await StandardToken.new(); + await token.mint(accounts[0], 1e30); + await token.mint(accounts[1], 1e30); + await token.mint(accounts[2], 1e30); + await token.mint(accounts[3], 1e30); + await token.mint(accounts[4], 1e30); + await token.mint(accounts[5], 1e30); + await token.mint(accounts[6], 1e30); + await token.mint(accounts[7], 1e30); + await token.mint(accounts[8], 1e30); + await token.mint(accounts[9], 1e30); + }); + + // 0->•abs + it('1: should process tokenAmount with ERC20Splitter + 3 ERC20AbsoluteExpense', async () => { + let erc20Table = await ERC20Table.new(token.address); + var output1 = await ERC20AbsoluteExpense.new(token.address,tokenAmount, tokenAmount); + var output2 = await ERC20AbsoluteExpense.new(token.address,2 * tokenAmount, 2 * tokenAmount); + var output3 = await ERC20AbsoluteExpense.new(token.address,3 * tokenAmount, 3 * tokenAmount); + let splitterId = getEId(await erc20Table.addSplitter()); + let AbsoluteExpense1Id = getEId(await erc20Table.addAbsoluteExpense(1 * tokenAmount, 1 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense2Id = getEId(await erc20Table.addAbsoluteExpense(2 * tokenAmount, 2 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense3Id = getEId(await erc20Table.addAbsoluteExpense(3 * tokenAmount, 3 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + + // add 3 ERC20AbsoluteExpense outputs to the splitter + await erc20Table.addChildAt(splitterId, AbsoluteExpense1Id); + await erc20Table.addChildAt(splitterId, AbsoluteExpense2Id); + await erc20Table.addChildAt(splitterId, AbsoluteExpense3Id); + + var id1 = await erc20Table.getChildIdAt(splitterId, 0); + var id2 = await erc20Table.getChildIdAt(splitterId, 1); + var id3 = await erc20Table.getChildIdAt(splitterId, 2); + + assert.equal(id1, AbsoluteExpense1Id); + assert.equal(id2, AbsoluteExpense2Id); + assert.equal(id3, AbsoluteExpense3Id); + + var totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed, 6 * tokenAmount); + var minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed, 6 * tokenAmount); + var need1 = await erc20Table.isNeeds(); + assert.equal(need1, true); + // now send some tokenAmount to the revenue endpoint + await token.approve(erc20Table.address, 6 * tokenAmount, {from:creator}); + await erc20Table.processTokens(6 * tokenAmount, 6 * tokenAmount, {from: creator }); + + // tokenAmount should end up in the outputs + var absoluteExpense1Balance = await erc20Table.balanceAt(AbsoluteExpense1Id); + assert.equal(absoluteExpense1Balance.toNumber(), 1 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense2Balance = await erc20Table.balanceAt(AbsoluteExpense2Id); + assert.equal(absoluteExpense2Balance.toNumber(), 2 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense3Balance = await erc20Table.balanceAt(AbsoluteExpense3Id); + assert.equal(absoluteExpense3Balance.toNumber(), 3 * tokenAmount, 'resource point received tokenAmount from splitter'); + + assert.equal((await erc20Table.getTotalNeededAt(AbsoluteExpense1Id, 6 * tokenAmount)).toNumber(), 0); + assert.equal((await erc20Table.getTotalNeededAt(AbsoluteExpense2Id, 6 * tokenAmount)).toNumber(), 0); + assert.equal((await erc20Table.getTotalNeededAt(AbsoluteExpense3Id, 6 * tokenAmount)).toNumber(), 0); + + var totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed.toNumber(), 0 * tokenAmount); + var minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed.toNumber(), 0 * tokenAmount); + + var need2 = await erc20Table.isNeeds(); + assert.equal(need2, false); + + var b1 = await token.balanceOf(accounts[9]); + await erc20Table.flushToAt(AbsoluteExpense1Id, accounts[9], { gasPrice: 0 }); + var b2 = await token.balanceOf(accounts[9]); + assert.equal(b2.sub(b1).toNumber(), 1 * tokenAmount); + + var b1 = await token.balanceOf(accounts[9]); + await erc20Table.flushToAt(AbsoluteExpense2Id, accounts[9], { gasPrice: 0 }); + var b2 = await token.balanceOf(accounts[9]); + assert.equal(b2.sub(b1).toNumber(), 2 * tokenAmount); + + var b1 = await token.balanceOf(accounts[9]); + await erc20Table.flushToAt(AbsoluteExpense3Id, accounts[9], { gasPrice: 0 }); + var b2 = await token.balanceOf(accounts[9]); + assert.equal(b2.sub(b1).toNumber(), 3 * tokenAmount); + + var absoluteExpense1Balance = await erc20Table.balanceAt(AbsoluteExpense1Id); + assert.equal(absoluteExpense1Balance.toNumber(), 0 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense2Balance = await erc20Table.balanceAt(AbsoluteExpense2Id); + assert.equal(absoluteExpense2Balance.toNumber(), 0 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense3Balance = await erc20Table.balanceAt(AbsoluteExpense3Id); + assert.equal(absoluteExpense3Balance.toNumber(), 0 * tokenAmount, 'resource point received tokenAmount from splitter'); + var need2 = await erc20Table.isNeeds(); + }); + + it('2: should process tokenAmount with ERC20Splitter + 3 ERC20AbsoluteExpense', async () => { + let erc20Table = await ERC20Table.new(token.address); + + let unsortedSplitterId = getEId(await erc20Table.addSplitter()); + let AbsoluteExpense1Id = getEId(await erc20Table.addAbsoluteExpense(tokenAmount, tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense2Id = getEId(await erc20Table.addAbsoluteExpense(2 * tokenAmount, 2 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense3Id = getEId(await erc20Table.addAbsoluteExpense(3 * tokenAmount, 3 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + + await erc20Table.addChildAt(unsortedSplitterId, AbsoluteExpense1Id); + await erc20Table.addChildAt(unsortedSplitterId, AbsoluteExpense2Id); + await erc20Table.addChildAt(unsortedSplitterId, AbsoluteExpense3Id); + + // now send some tokenAmount to the revenue endpoint + let totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed, 6 * tokenAmount); + let minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed, 6 * tokenAmount); + + await token.approve(erc20Table.address, 6 * tokenAmount, {from:creator}); + await erc20Table.processTokens(6 * tokenAmount, 6 * tokenAmount, {from: creator }); + // tokenAmount should end up in the outputs + var absoluteExpense1Balance = await erc20Table.balanceAt(AbsoluteExpense1Id); + assert.equal(absoluteExpense1Balance.toNumber(), 1 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense2Balance = await erc20Table.balanceAt(AbsoluteExpense2Id); + assert.equal(absoluteExpense2Balance.toNumber(), 2 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense3Balance = await erc20Table.balanceAt(AbsoluteExpense3Id); + assert.equal(absoluteExpense3Balance.toNumber(), 3 * tokenAmount, 'resource point received tokenAmount from splitter'); + }); + + it('3: should process tokenAmount with a scheme just like in the paper: 75/25 others, send MORE than minNeed; ', async () => { + const tokenAmount = 1e12; + const CURRENT_INPUT = 30900; + let e1 = 1000; + let e2 = 1500; + let e3 = 800; + let office = 500; + let internet = 300; + let t1 = 500; + let t2 = 300; + let t3 = 1000; + // 5900 + let b1 = 10000; + let b2 = 10000; + let b3 = 20000; + //4% of 25K = 1000 + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }); + + let balances = await getBalances(struct); + await balancesAsserts(tokenAmount, balances, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + }); + + it('4: should process tokenAmount with a scheme just like in the paper: 75/25 others, send EQUAL to minNeed', async () => { + const tokenAmount = 1e12; + const CURRENT_INPUT = 5900; + 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 = 10000; + let b2 = 10000; + let b3 = 20000; + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }); + + let balances = await getBalances(struct); + await balancesAsserts(tokenAmount, balances, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, 0, 0, 0, 0, 0); + }); + + it('5: should not process tokenAmount: send LESS than minNeed', async () => { + const tokenAmount = 1e12; + const CURRENT_INPUT = 5900; + 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 = 10000; + let b2 = 10000; + let b3 = 20000; + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount / 100, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount / 100, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount / 100, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount / 100, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + }); + + it('6: should process tokenAmount with a scheme just like in the paper: 10/15 others, send MORE than minNeed; ', async () => { + const tokenAmount = 1e12; + 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; + // 5900 -> 4% of 15000 -> + let b1 = 10000; + let b2 = 10000; + let b3 = 20000; + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }); + + let balances = await getBalances(struct); + await balancesAsserts(tokenAmount, balances, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + }); + + it('7: should process tokenAmount with a scheme just like in the paper: 10/15 others, send EQUAL to minNeed; ', async () => { + const tokenAmount = 1e12; + const CURRENT_INPUT = 5900; + 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 = 10000; + let b2 = 10000; + let b3 = 20000; + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }); + + let balances = await getBalances(struct); + await balancesAsserts(tokenAmount, balances, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, 0, 0, 0, 0, 0); + }); + + it('8: should not process tokenAmount: send LESS than minNeed; ', async () => { + const tokenAmount = 1e12; + const CURRENT_INPUT = 30900; + 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 = 10000; + let b2 = 10000; + let b3 = 20000; + let reserve = 750000; + let dividends = 250000; + + let struct = await createStructure(token, tokenAmount, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + let splitterParams = await getSplitterParams(tokenAmount, struct, CURRENT_INPUT, creator); + await totalAndMinNeedsAsserts(tokenAmount, splitterParams, CURRENT_INPUT, e1, e2, e3, office, internet, t1, t2, t3, b1, b2, b3, reserve, dividends); + await structureAsserts(splitterParams); + + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount / 100, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount / 100, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount, CURRENT_INPUT * tokenAmount / 100, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + await token.approve(struct.erc20Table.address, CURRENT_INPUT * tokenAmount, {from:creator}); + await struct.erc20Table.processTokens(CURRENT_INPUT * tokenAmount / 100, CURRENT_INPUT * tokenAmount, {from: creator, gasPrice: 0 }).should.be.rejectedWith('revert'); + }); + + it('9: should process tokenAmount when opened and not process when closed with ERC20Splitter + 3 ERC20AbsoluteExpense', async () => { + let erc20Table = await ERC20Table.new(token.address); + + let splitterId = getEId(await erc20Table.addSplitter()); + let AbsoluteExpense1Id = getEId(await erc20Table.addAbsoluteExpense(tokenAmount, tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense2Id = getEId(await erc20Table.addAbsoluteExpense(2 * tokenAmount, 2 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + let AbsoluteExpense3Id = getEId(await erc20Table.addAbsoluteExpense(3 * tokenAmount, 3 * tokenAmount, isPeriodic, isAccumulateDebt, periodHours)); + + // add 3 ERC20AbsoluteExpense outputs to the splitter + await erc20Table.addChildAt(splitterId, AbsoluteExpense1Id); + await erc20Table.addChildAt(splitterId, AbsoluteExpense2Id); + await erc20Table.addChildAt(splitterId, AbsoluteExpense3Id); + + var totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed, 6 * tokenAmount); + var minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed, 6 * tokenAmount); + + var isOpen1At = await erc20Table.isOpenAt(AbsoluteExpense1Id); + var isOpen2At = await erc20Table.isOpenAt(AbsoluteExpense2Id); + var isOpen3At = await erc20Table.isOpenAt(AbsoluteExpense3Id); + assert.equal(isOpen1At, true); + assert.equal(isOpen2At, true); + assert.equal(isOpen3At, true); + + await erc20Table.closeAt(AbsoluteExpense3Id); + + var totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed.toNumber(), 3 * tokenAmount); + var minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed, 3 * tokenAmount); + + await erc20Table.closeAt(AbsoluteExpense1Id); + + var totalNeed = await erc20Table.getTotalNeeded(6 * tokenAmount); + assert.equal(totalNeed, 2 * tokenAmount); + var minNeed = await erc20Table.getMinNeeded(0);/*minNeedFix*/ + assert.equal(minNeed, 2 * tokenAmount); + + var isOpen1At = await erc20Table.isOpenAt(AbsoluteExpense1Id); + var isOpen2At = await erc20Table.isOpenAt(AbsoluteExpense2Id); + var isOpen3At = await erc20Table.isOpenAt(AbsoluteExpense3Id); + assert.equal(isOpen1At, false); + assert.equal(isOpen2At, true); + assert.equal(isOpen3At, false); + + await erc20Table.openAt(AbsoluteExpense3Id); + var isOpen1At = await erc20Table.isOpenAt(AbsoluteExpense1Id); + var isOpen2At = await erc20Table.isOpenAt(AbsoluteExpense2Id); + var isOpen3At = await erc20Table.isOpenAt(AbsoluteExpense3Id); + assert.equal(isOpen1At, false); + assert.equal(isOpen2At, true); + assert.equal(isOpen3At, true); + + // now send some tokenAmount to the revenue endpoint + await token.approve(erc20Table.address, 5 * tokenAmount, {from:creator}); + await erc20Table.processTokens(5 * tokenAmount, 5 * tokenAmount, {from: creator }); + + // tokenAmount should end up in the outputs + var absoluteExpense1Balance = await erc20Table.balanceAt(AbsoluteExpense1Id); + assert.equal(absoluteExpense1Balance.toNumber(), 0 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense2Balance = await erc20Table.balanceAt(AbsoluteExpense2Id); + assert.equal(absoluteExpense2Balance.toNumber(), 2 * tokenAmount, 'resource point received tokenAmount from splitter'); + + var absoluteExpense3Balance = await erc20Table.balanceAt(AbsoluteExpense3Id); + assert.equal(absoluteExpense3Balance.toNumber(), 3 * tokenAmount, 'resource point received tokenAmount from splitter'); + }); +}); diff --git a/test/moneyflow.tests.js b/test/wei_tests/wei_expense.tests.js similarity index 99% rename from test/moneyflow.tests.js rename to test/wei_tests/wei_expense.tests.js index 891e1d9..a339727 100644 --- a/test/moneyflow.tests.js +++ b/test/wei_tests/wei_expense.tests.js @@ -213,7 +213,7 @@ async function splitterBalancesAsserts (i, money, allOutpultsBalance, spendsBala assert.equal(i.RestBalance.toNumber() / money, restBalance, `Rest balance should be ${restBalance} money`); } -contract('Moneyflow', (accounts) => { +contract('WeiExpense', (accounts) => { var token; var store; var daoBase; diff --git a/test/wei_fund.tests.js b/test/wei_tests/wei_fund.tests.js similarity index 100% rename from test/wei_fund.tests.js rename to test/wei_tests/wei_fund.tests.js diff --git a/test/moneyflow_scheme.tests.js b/test/wei_tests/wei_scheme.tests.js similarity index 100% rename from test/moneyflow_scheme.tests.js rename to test/wei_tests/wei_scheme.tests.js diff --git a/test/wei_table.tests.js b/test/wei_tests/wei_table.tests.js similarity index 100% rename from test/wei_table.tests.js rename to test/wei_tests/wei_table.tests.js