diff --git a/contracts/SplitterBase.sol b/contracts/SplitterBase.sol index 4751fe6..d3c8b1f 100644 --- a/contracts/SplitterBase.sol +++ b/contracts/SplitterBase.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.23; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; -import "./interfaces/IWeiReceiver.sol"; +import "./interfaces/IReceiver.sol"; import "./interfaces/ITable.sol"; /** @@ -14,7 +14,7 @@ contract SplitterBase { bool isTableSplitter; uint[] outputs; address[] addresses; - IWeiReceiver.Type childrenType; + IReceiver.Type childrenType; } struct FlowBuffer { @@ -44,7 +44,7 @@ contract SplitterBase { function addChildToSplitter(Splitter storage _s, uint _id, address _addr) internal { emit SplitterBaseAddChild(_id, _addr); - IWeiReceiver.Type childType; + IReceiver.Type childType; if(_s.isTableSplitter) { childType = ITable(address(this)).getReceiverTypeAt(_id); @@ -52,8 +52,8 @@ contract SplitterBase { childType = IWeiReceiver(_addr).getReceiverType(); } - if((_s.childrenType != IWeiReceiver.Type.Splitter) - &&(childType != IWeiReceiver.Type.Splitter)) { + if((_s.childrenType != IReceiver.Type.Splitter) + &&(childType != IReceiver.Type.Splitter)) { require(_s.childrenType == childType); } else { _s.childrenType = childType; @@ -154,7 +154,7 @@ contract SplitterBase { function constructSplitter(bool _isTableSplitter) internal view returns(Splitter s) { uint[] memory emptyOutputs; address[] memory emptyAddresses; - return Splitter(true, _isTableSplitter, emptyOutputs, emptyAddresses, IWeiReceiver.Type.Splitter); + return Splitter(true, _isTableSplitter, emptyOutputs, emptyAddresses, IReceiver.Type.Splitter); } function _modifyFlow(FlowBuffer _b) internal pure returns(FlowBuffer) { @@ -179,8 +179,8 @@ contract SplitterBase { } if((b.i+1) < getChildrenCount(_s)) { - if((getReceiverTypeBase(_s, b.i) == IWeiReceiver.Type.Relative) - && (getReceiverTypeBase(_s, (b.i + 1)) == IWeiReceiver.Type.Relative)) { + if((getReceiverTypeBase(_s, b.i) == IReceiver.Type.Relative) + && (getReceiverTypeBase(_s, (b.i + 1)) == IReceiver.Type.Relative)) { b.relSeqQ = true; } } @@ -189,7 +189,7 @@ contract SplitterBase { return b; } - function getReceiverTypeBase(Splitter _s, uint _childNum) internal view returns(IWeiReceiver.Type t) { + function getReceiverTypeBase(Splitter _s, uint _childNum) internal view returns(IReceiver.Type t) { if(_s.isTableSplitter) { t = ITable(address(this)).getReceiverTypeAt(_s.outputs[_childNum]); } else { diff --git a/contracts/TableBase.sol b/contracts/TableBase.sol new file mode 100644 index 0000000..8b50e2a --- /dev/null +++ b/contracts/TableBase.sol @@ -0,0 +1,166 @@ +pragma solidity ^0.4.23; + +import "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "./interfaces/IWeiReceiver.sol"; +import "./interfaces/ITable.sol"; + +import "./ExpenseBase.sol"; +import "./SplitterBase.sol"; + +/** + * @title TableBase + * @dev contract for WeiTable and ERC20Table +*/ +contract TableBase is ExpenseBase, SplitterBase, Ownable { + uint public nodesCount = 0; + + event NodeAdded(uint _eId, IReceiver.Type _eType); + event FundNeed(uint _eId, uint _inputWei, uint need); + event FundStart(uint _eId, uint _totalWeiNeeded); + + event NodeConnected(uint _splitterId, uint _childId); + event NodeFlushTo(uint _eId, address _to, uint _balance); + event SplitterNeeded(uint _eId, uint _needed, uint _total, uint _currentFlow); + + mapping(uint=>IReceiver.Type) nodesType; + mapping(uint=>ExpenseBase.Expense) expenses; + mapping(uint=>SplitterBase.Splitter) splitters; + + modifier isCorrectId(uint _eId) { + require(_eId <= (nodesCount - 1)); + _; + } + + function getLastNodeId() public returns(uint) { + if(nodesCount == 0) { + return 0; + } else { + return nodesCount - 1; + } + } + + function getPartsPerMillionAt(uint _eId) public view isCorrectId(_eId) returns(uint ppm) { + ppm = expenses[_eId].partsPerMillion; + } + + function isNeedsAt(uint _eId) public view isCorrectId(_eId) returns(bool) { + if(isExpenseAt(_eId)) { + return isExpenseNeeds(expenses[_eId]); + }else { + return isSplitterNeeds(splitters[_eId]); + } + } + + function getMinNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { + if(isExpenseAt(_eId)) { + return getExpenseMinNeeded(expenses[_eId], _currentFlow); + }else { + return getSplitterMinNeeded(splitters[_eId], _currentFlow); + } + } + + function getTotalNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { + if(isExpenseAt(_eId)) { + return getExpenseTotalNeeded(expenses[_eId], _currentFlow); + } else { + return getSplitterTotalNeeded(splitters[_eId], _currentFlow); + } + } + + function balanceAt(uint _eId) public view isCorrectId(_eId) returns(uint) { + return expenses[_eId].balance; + } + + // -------------------- public SCHEME FUNCTIONS -------------------- + function addAbsoluteExpense(uint128 _totalWeiNeeded, uint128 _minWeiAmount, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { + emit NodeAdded(nodesCount, IReceiver.Type.Absolute); + expenses[nodesCount] = constructExpense(_totalWeiNeeded, _minWeiAmount, 0, _periodHours, _isSlidingAmount, _isPeriodic); + nodesType[nodesCount] = IReceiver.Type.Absolute; + nodesCount += 1; + } + + function addRelativeExpense(uint32 _partsPerMillion, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { + emit NodeAdded(nodesCount, IReceiver.Type.Relative); + expenses[nodesCount] = constructExpense(0, 0, _partsPerMillion, _periodHours, _isSlidingAmount, _isPeriodic); + nodesType[nodesCount] = IReceiver.Type.Relative; + nodesCount += 1; + } + + function addSplitter() public onlyOwner { + splitters[nodesCount] = constructSplitter(true); + emit NodeAdded(nodesCount, IReceiver.Type.Splitter); + nodesType[nodesCount] = IReceiver.Type.Splitter; + nodesCount += 1; + } + + function addChildAt(uint _splitterId, uint _childId) public onlyOwner { + // Splitter s = splitters[_splitterId]; + // addChildToSplitter(s, _childId, address(0)); + // splitters[_splitterId] = s; + if((splitters[_splitterId].childrenType != IReceiver.Type.Splitter) + && (getReceiverTypeAt(_childId) != IReceiver.Type.Splitter)) { + require(getReceiverTypeAt(_childId) == splitters[_splitterId].childrenType); + } else { + splitters[_splitterId].childrenType = getReceiverTypeAt(_childId); + } + emit NodeConnected(_splitterId, _childId); + splitters[_splitterId].outputs.push(_childId); + } + + // -------------------- public CONTROL FUNCTIONS -------------------- + function getReceiverTypeAt(uint _eId) public isCorrectId(_eId) returns(IReceiver.Type nodeType) { + if(isExpenseAt(_eId)) { + if(expenses[_eId].partsPerMillion > 0) { + nodeType = IReceiver.Type.Relative; + } else { + nodeType = IReceiver.Type.Absolute; + } + } else { + nodeType = IReceiver.Type.Splitter; + } + } + + function isExpenseAt(uint _eId) public isCorrectId(_eId) returns(bool isExpense) { + isExpense = (IReceiver.Type.Splitter != nodesType[_eId]); + } + + function isSplitterAt(uint _eId) public isCorrectId(_eId) returns(bool isSplitter) { + isSplitter = (IReceiver.Type.Splitter == nodesType[_eId]); + } + + function openAt(uint _eId) public onlyOwner isCorrectId(_eId) { + if(isExpenseAt(_eId)) { + openExpense(expenses[_eId]); + }else { + openSplitter(splitters[_eId]); + } + } + + function closeAt(uint _eId) public onlyOwner isCorrectId(_eId) { + if(isExpenseAt(_eId)) { + closeExpense(expenses[_eId]); + }else { + closeSplitter(splitters[_eId]); + } + } + + function isOpenAt(uint _eId) public view isCorrectId(_eId) returns(bool) { + if(isExpenseAt(_eId)) { + return expenses[_eId].isOpen; + }else { + return splitters[_eId].isOpen; + } + } + + function getChildrenCountAt(uint _eId) public view isCorrectId(_eId) returns(uint) { + require(isSplitterAt(_eId)); + return splitters[_eId].outputs.length; + } + + function getChildIdAt(uint _eId, uint _index) public view isCorrectId(_eId) returns(uint) { + require(isSplitterAt(_eId)); + require(splitters[_eId].outputs.length > _index); + return splitters[_eId].outputs[_index]; + } + +} \ No newline at end of file diff --git a/contracts/ether/WeiTable.sol b/contracts/ether/WeiTable.sol index 5c4356d..a91a721 100644 --- a/contracts/ether/WeiTable.sol +++ b/contracts/ether/WeiTable.sol @@ -1,5 +1,6 @@ pragma solidity ^0.4.24; +import "../TableBase.sol"; import "../ExpenseBase.sol"; import "../SplitterBase.sol"; @@ -7,80 +8,13 @@ import "../interfaces/IWeiReceiver.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; -contract WeiTable is ExpenseBase, SplitterBase, IWeiReceiver, ITable, Ownable { - uint public nodesCount = 0; +contract WeiTable is ITable, IWeiReceiver, TableBase { - event NodeAdded(uint _eId, IWeiReceiver.Type _eType); - event FundNeed(uint _eId, uint _inputWei, uint need); - event FundStart(uint _eId, uint _totalWeiNeeded); - - event NodeConnected(uint _splitterId, uint _childId); - event NodeFlushTo(uint _eId, address _to, uint _balance); - event SplitterNeeded(uint _eId, uint _needed, uint _total, uint _currentFlow); - - mapping(uint=>IWeiReceiver.Type) nodesType; - mapping(uint=>Expense) expenses; - mapping(uint=>Splitter) splitters; - - modifier isCorrectId(uint _eId) { - require(_eId <= (nodesCount - 1)); - _; - } - - function getReceiverType() public view returns(IWeiReceiver.Type) { - return IWeiReceiver.Type.Table; - } - - function getLastNodeId() public returns(uint) { - if(nodesCount == 0) { - return 0; - } else { - return nodesCount - 1; - } - } - - // -------------------- INTERNAL IWEIRECEIVER FUNCTIONS -------------------- for nodes - function getPartsPerMillionAt(uint _eId) public view isCorrectId(_eId) returns(uint ppm) { - ppm = expenses[_eId].partsPerMillion; - } - - function isNeedsAt(uint _eId) public view isCorrectId(_eId) returns(bool) { - if(isExpenseAt(_eId)) { - return isExpenseNeeds(expenses[_eId]); - }else { - return isSplitterNeeds(splitters[_eId]); - } - } - - function processFundsAt(uint _eId, uint _currentFlow, uint _amount) public isCorrectId(_eId) { - if(isExpenseAt(_eId)) { - expenses[_eId] = processWeiExpenseFunds(expenses[_eId], _currentFlow, _amount); - } else { - processWeiSplitterFunds(splitters[_eId], _currentFlow, _amount); - } + function getReceiverType() public view returns(IReceiver.Type) { + return IReceiver.Type.Table; } - function getMinNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { - if(isExpenseAt(_eId)) { - return getExpenseMinNeeded(expenses[_eId], _currentFlow); - }else { - return getSplitterMinNeeded(splitters[_eId], _currentFlow); - } - } - - function getTotalNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { - if(isExpenseAt(_eId)) { - return getExpenseTotalNeeded(expenses[_eId], _currentFlow); - } else { - return getSplitterTotalNeeded(splitters[_eId], _currentFlow); - } - } - - function balanceAt(uint _eId) public view isCorrectId(_eId) returns(uint) { - return expenses[_eId].balance; - } - - // -------------------- public IWEIRECEIVER FUNCTIONS -------------------- for all table + // -------------------- IWEIRECEIVER FUNCTIONS -------------------- function isNeedsMoney() public view returns(bool) { return isNeedsAt(0); } @@ -90,8 +24,6 @@ contract WeiTable is ExpenseBase, SplitterBase, IWeiReceiver, ITable, Ownable { } function processFunds(uint _currentFlow) public payable { - require(_currentFlow>=getMinWeiNeeded(_currentFlow)); - require(msg.value>=getMinWeiNeeded(_currentFlow)); return processFundsAt(0, _currentFlow, msg.value); } @@ -103,97 +35,7 @@ contract WeiTable is ExpenseBase, SplitterBase, IWeiReceiver, ITable, Ownable { return getTotalNeededAt(0, _currentFlow); } - // -------------------- public SCHEME FUNCTIONS -------------------- - function addAbsoluteExpense(uint128 _totalWeiNeeded, uint128 _minWeiAmount, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { - emit NodeAdded(nodesCount, IWeiReceiver.Type.Absolute); - expenses[nodesCount] = constructExpense(_totalWeiNeeded, _minWeiAmount, 0, _periodHours, _isSlidingAmount, _isPeriodic); - nodesType[nodesCount] = IWeiReceiver.Type.Absolute; - nodesCount += 1; - } - - function addRelativeExpense(uint32 _partsPerMillion, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { - emit NodeAdded(nodesCount, IWeiReceiver.Type.Relative); - expenses[nodesCount] = constructExpense(0, 0, _partsPerMillion, _periodHours, _isSlidingAmount, _isPeriodic); - nodesType[nodesCount] = IWeiReceiver.Type.Relative; - nodesCount += 1; - } - - function addSplitter() public onlyOwner { - splitters[nodesCount] = constructSplitter(true); - emit NodeAdded(nodesCount, IWeiReceiver.Type.Splitter); - nodesType[nodesCount] = IWeiReceiver.Type.Splitter; - nodesCount += 1; - } - - function addChildAt(uint _splitterId, uint _childId) public onlyOwner { - // Splitter s = splitters[_splitterId]; - // addChildToSplitter(s, _childId, address(0)); - // splitters[_splitterId] = s; - if((splitters[_splitterId].childrenType != Type.Splitter) - && (getReceiverTypeAt(_childId) != Type.Splitter)) { - require(getReceiverTypeAt(_childId) == splitters[_splitterId].childrenType); - } else { - splitters[_splitterId].childrenType = getReceiverTypeAt(_childId); - } - emit NodeConnected(_splitterId, _childId); - splitters[_splitterId].outputs.push(_childId); - } - - // -------------------- public CONTROL FUNCTIONS -------------------- - function getReceiverTypeAt(uint _eId) public isCorrectId(_eId) returns(IWeiReceiver.Type nodeType) { - if(isExpenseAt(_eId)) { - if(expenses[_eId].partsPerMillion > 0) { - nodeType = IWeiReceiver.Type.Relative; - } else { - nodeType = IWeiReceiver.Type.Absolute; - } - } else { - nodeType = IWeiReceiver.Type.Splitter; - } - } - - function isExpenseAt(uint _eId) public isCorrectId(_eId) returns(bool isExpense) { - isExpense = (IWeiReceiver.Type.Splitter != nodesType[_eId]); - } - - function isSplitterAt(uint _eId) public isCorrectId(_eId) returns(bool isSplitter) { - isSplitter = (IWeiReceiver.Type.Splitter == nodesType[_eId]); - } - - function openAt(uint _eId) public onlyOwner isCorrectId(_eId) { - if(isExpenseAt(_eId)) { - openExpense(expenses[_eId]); - }else { - openSplitter(splitters[_eId]); - } - } - - function closeAt(uint _eId) public onlyOwner isCorrectId(_eId) { - if(isExpenseAt(_eId)) { - closeExpense(expenses[_eId]); - }else { - closeSplitter(splitters[_eId]); - } - } - - function isOpenAt(uint _eId) public view isCorrectId(_eId) returns(bool) { - if(isExpenseAt(_eId)) { - return expenses[_eId].isOpen; - }else { - return splitters[_eId].isOpen; - } - } - - function getChildrenCountAt(uint _eId) public view isCorrectId(_eId) returns(uint) { - require(isSplitterAt(_eId)); - return splitters[_eId].outputs.length; - } - - function getChildIdAt(uint _eId, uint _index) public view isCorrectId(_eId) returns(uint) { - require(isSplitterAt(_eId)); - require(splitters[_eId].outputs.length > _index); - return splitters[_eId].outputs[_index]; - } + // -------------------- WEI-SPECIFIC IMPLEMENTATIONS -------------------- function flushAt(uint _eId) public onlyOwner isCorrectId(_eId) { owner.transfer(expenses[_eId].balance); @@ -205,7 +47,15 @@ contract WeiTable is ExpenseBase, SplitterBase, IWeiReceiver, ITable, Ownable { _to.transfer(expenses[_eId].balance); emit NodeFlushTo(_eId, _to, expenses[_eId].balance); expenses[_eId].balance = 0; - } + } + + function processFundsAt(uint _eId, uint _currentFlow, uint _amount) public isCorrectId(_eId) { + if(isExpenseAt(_eId)) { + expenses[_eId] = processWeiExpenseFunds(expenses[_eId], _currentFlow, _amount); + } else { + processWeiSplitterFunds(splitters[_eId], _currentFlow, _amount); + } + } function() public { } diff --git a/contracts/interfaces/IReceiver.sol b/contracts/interfaces/IReceiver.sol index 379c2a3..10a2692 100644 --- a/contracts/interfaces/IReceiver.sol +++ b/contracts/interfaces/IReceiver.sol @@ -6,6 +6,13 @@ pragma solidity ^0.4.24; * @dev Something that needs funds */ contract IReceiver { + enum Type { + Absolute, + Relative, + Splitter, + Table + } + // If this output needs more funds -> will return true // If this output does not need more funds -> will return false function isNeedsMoney() public view returns(bool); diff --git a/contracts/interfaces/ITable.sol b/contracts/interfaces/ITable.sol index 4b863be..b55384f 100644 --- a/contracts/interfaces/ITable.sol +++ b/contracts/interfaces/ITable.sol @@ -22,5 +22,5 @@ contract ITable { function flushAt(uint _eId) public; function flushToAt(uint _eId, address _to) public; - function getReceiverTypeAt(uint _eId) public returns(IWeiReceiver.Type); + function getReceiverTypeAt(uint _eId) public returns(IReceiver.Type); } \ No newline at end of file diff --git a/contracts/interfaces/IWeiReceiver.sol b/contracts/interfaces/IWeiReceiver.sol index 0c86a1c..d32f2d3 100644 --- a/contracts/interfaces/IWeiReceiver.sol +++ b/contracts/interfaces/IWeiReceiver.sol @@ -9,14 +9,6 @@ import "./IReceiver.sol"; // "Absolute": fixed amount of Wei // "Relative": percents of input contract IWeiReceiver is IReceiver { - - enum Type { - Absolute, - Relative, - Splitter, - Table - } - // Will calculate only absolute outputs, but not take into account the Percents function getMinWeiNeeded(uint _inputWei) public view returns(uint);