From b322fbaea904c5cb4d08da0241b4c6b91ebcf58c Mon Sep 17 00:00:00 2001 From: enkogu Date: Sat, 22 Dec 2018 02:00:34 +0700 Subject: [PATCH] #42 #44 --- contracts/{ether => }/ExpenseBase.sol | 32 +++--- contracts/WeiTable.sol | 145 ++++-------------------- contracts/ether/WeiExpense.sol | 6 +- contracts/examples/weiTable/Budget.sol | 28 ++--- contracts/examples/weiTable/Roadmap.sol | 2 +- contracts/interfaces/IReceiver.sol | 3 - test/wei_table.tests.js | 11 +- 7 files changed, 60 insertions(+), 167 deletions(-) rename contracts/{ether => }/ExpenseBase.sol (93%) diff --git a/contracts/ether/ExpenseBase.sol b/contracts/ExpenseBase.sol similarity index 93% rename from contracts/ether/ExpenseBase.sol rename to contracts/ExpenseBase.sol index 9202cbf..d32b680 100644 --- a/contracts/ether/ExpenseBase.sol +++ b/contracts/ExpenseBase.sol @@ -1,12 +1,4 @@ pragma solidity ^0.4.24; -pragma experimental ABIEncoderV2; - -import "../interfaces/IDestination.sol"; -import "../interfaces/IWeiReceiver.sol"; - -import "zeppelin-solidity/contracts/math/SafeMath.sol"; -import "zeppelin-solidity/contracts/ownership/Ownable.sol"; - /** * @title ExpenseBase @@ -32,6 +24,8 @@ contract ExpenseBase { uint128 totalReceived; uint32 momentCreated; + + bool isOpen; } enum PeriodType { @@ -69,6 +63,7 @@ contract ExpenseBase { e.totalNeeded = _totalNeeded; e.minAmount = _minAmount; e.momentCreated = uint32(block.timestamp); + e.isOpen = true; if(!_isPeriodic) { e.periodType = PeriodType.NonPeriodic; @@ -86,12 +81,10 @@ contract ExpenseBase { // all inputs divide _minAmount == INTEGER e.totalReceived += uint128(_value); - // e.isMoneyReceived = true; - + e.balance += uint128(_value); if((getTotalNeeded(_e, _value) == 0) || (_e.periodType == PeriodType.Periodic)) { e.momentReceived = uint32(block.timestamp); - // e.balanceOnMomentReceived = e.totalReceived; } } @@ -107,9 +100,14 @@ contract ExpenseBase { baseNeed = _e.totalNeeded; } - if(_e.minAmount == _e.totalNeeded) { + if(!_e.isOpen) { + need = 0; + + } else if(_e.minAmount == _e.totalNeeded) { if(_e.periodType == PeriodType.NonPeriodic) { - need = baseNeed; + if(_e.balance == 0) { + need = baseNeed; + } } else if(_e.periodType == PeriodType.Periodic) { if(receiveTimeDelta >= periodLength) { need = baseNeed; @@ -129,9 +127,9 @@ contract ExpenseBase { } else if(_e.minAmount == 0) { if(_e.periodType == PeriodType.NonPeriodic) { if(_currentFlow >= (_e.totalNeeded - _e.totalReceived)) { - return (_e.totalNeeded - _e.totalReceived); + need = (_e.totalNeeded - _e.totalReceived); } else { - return _currentFlow; + need = _currentFlow; } } else if(_e.periodType == PeriodType.Periodic) { need = getDebtIfNoSliding(_e); @@ -177,7 +175,6 @@ contract ExpenseBase { isNeed = (getTotalNeeded(_e, 1e30) > 0); } - // -------------------- INTERNAL FUNCTIONS function numberOfEntitiesPlusOne(uint _inclusive, uint _inluded) internal view returns(uint count) { if(_inclusive < _inluded) { @@ -202,6 +199,5 @@ contract ExpenseBase { } else { return 0; } - } - + } } \ No newline at end of file diff --git a/contracts/WeiTable.sol b/contracts/WeiTable.sol index 0007a47..bfff52b 100644 --- a/contracts/WeiTable.sol +++ b/contracts/WeiTable.sol @@ -1,10 +1,12 @@ pragma solidity ^0.4.24; +import "./ExpenseBase.sol"; + import "./interfaces/IWeiReceiver.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; -contract WeiTable is IWeiReceiver, Ownable { +contract WeiTable is ExpenseBase, IWeiReceiver, Ownable { uint public nodesCount = 0; struct FlowBuffer { @@ -27,25 +29,6 @@ contract WeiTable is IWeiReceiver, Ownable { mapping(uint=>Expense) expenses; mapping(uint=>Splitter) splitters; - struct Expense { - uint totalWeiNeeded; - uint minWeiAmount; - uint neededPpm; - - uint periodHours; - - bool isPeriodic; - bool isSlidingAmount; - - uint momentReceived; - bool isMoneyReceived; - bool isOpen; - uint balance; - uint totalWeiReceived; - uint momentCreated; - uint balanceOnMomentReceived; - } - struct Splitter { bool isOpen; uint[] outputs; @@ -71,7 +54,7 @@ contract WeiTable is IWeiReceiver, Ownable { // -------------------- INTERNAL IWEIRECEIVER FUNCTIONS -------------------- for nodes function getPartsPerMillionAt(uint _eId) public view isCorrectId(_eId) returns(uint ppm) { - ppm = expenses[_eId].neededPpm; + ppm = expenses[_eId].partsPerMillion; } function isNeedsMoneyAt(uint _eId) public view isCorrectId(_eId) returns(bool) { @@ -95,18 +78,7 @@ contract WeiTable is IWeiReceiver, Ownable { } function isNeedsMoneyExpenseAt(uint _eId) internal view returns(bool isNeed) { - Expense e = expenses[_eId]; - if(e.isPeriodic) { - if ((uint64(block.timestamp) - e.momentReceived) >= e.periodHours * 3600 * 1000) { - isNeed = true; - } - } else if((e.minWeiAmount == 0) && (e.totalWeiNeeded>0)) { - isNeed = ((getDebtMultiplierAt(_eId) * e.totalWeiNeeded) - e.totalWeiReceived) > 0; - } else if((e.minWeiAmount>0) && (e.minWeiAmount < e.totalWeiNeeded)) { - isNeed = (e.totalWeiNeeded - e.totalWeiReceived) > 0; - } else { - isNeed = !e.isMoneyReceived; - } + return super.isNeedsMoney(expenses[_eId]); } function processFundsAt(uint _eId, uint _currentFlow, uint _amount) internal isCorrectId(_eId) { @@ -137,21 +109,10 @@ contract WeiTable is IWeiReceiver, Ownable { } function processFundsExpenseAt(uint _eId, uint _currentFlow, uint _amount) internal { - expenses[_eId].totalWeiReceived += _amount; - expenses[_eId].balance += _amount; - expenses[_eId].isMoneyReceived = true; - - if((getTotalWeiNeededAt(_eId, _amount) == 0) || (expenses[_eId].isPeriodic)) { - expenses[_eId].momentReceived = block.timestamp; - expenses[_eId].balanceOnMomentReceived = expenses[_eId].totalWeiReceived; - } + expenses[_eId] = processWeiFunds(expenses[_eId], _currentFlow, _amount); } function getMinWeiNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { - if(!isNeedsMoneyAt(_eId)) { - return 0; - } - if(isExpenseAt(_eId)) { return getMinWeiNeededExpenseAt(_eId, _currentFlow); }else { @@ -171,35 +132,12 @@ contract WeiTable is IWeiReceiver, Ownable { } function getMinWeiNeededExpenseAt(uint _eId, uint _currentFlow) internal view returns(uint totalNeed) { - if( !((expenses[_eId].minWeiAmount == 0) && (expenses[_eId].totalWeiNeeded > 0)) - && !(expenses[_eId].neededPpm > 0) ) { - totalNeed = getTotalWeiNeededAt(_eId, _currentFlow); - } - } - - function getDebtMultiplierAt(uint _eId) internal isCorrectId(_eId) view returns(uint) { - Expense e = expenses[_eId]; - // if periodic, not sliding amount and periods Count From Last Receive > 1 - if((e.isPeriodic) && (!e.isSlidingAmount) && (((block.timestamp - e.momentReceived) / (e.periodHours * 3600 * 1000)) >= 1)) { - if(0 != e.neededPpm) { - return 1; - } else { - return (e.balanceOnMomentReceived/e.totalWeiNeeded) + 1; - } - } else if((e.isPeriodic) && (e.isSlidingAmount)) { - return 1 + ((block.timestamp - e.momentCreated) / (e.periodHours * 3600 * 1000)); - } else { - return 1; - } + return getMinNeeded(expenses[_eId], _currentFlow); } function getTotalWeiNeededAt(uint _eId, uint _currentFlow) public view isCorrectId(_eId) returns(uint) { - if(!isNeedsMoneyAt(_eId)) { - return 0; - } - if(isExpenseAt(_eId)) { - return getTotalWeiNeededExpenseAt(_eId, _currentFlow); + return getTotalNeeded(expenses[_eId], _currentFlow); } else { return getTotalWeiNeededSplitterAt(_eId, _currentFlow); } @@ -216,30 +154,9 @@ contract WeiTable is IWeiReceiver, Ownable { } } - function getTotalWeiNeededExpenseAt(uint _eId, uint _currentFlow)internal view returns(uint totalNeed) { - Expense e = expenses[_eId]; - if(0 != e.neededPpm) { - totalNeed = ((getDebtMultiplierAt(_eId) * (e.neededPpm * _currentFlow)) / 1000000); - - } else if(getDebtMultiplierAt(_eId) * e.totalWeiNeeded > e.totalWeiReceived) { - totalNeed = getDebtMultiplierAt(_eId) * e.totalWeiNeeded - e.totalWeiReceived; - if((e.minWeiAmount == 0) && (e.totalWeiNeeded>0)) { - if(totalNeed > _currentFlow) { - totalNeed = _currentFlow; - } - } else if((e.minWeiAmount > 0) && (e.minWeiAmount < e.totalWeiNeeded)) { // fund with discrete input - if(totalNeed >= _currentFlow) { - if(_currentFlow >= e.minWeiAmount) { - totalNeed = _currentFlow - (_currentFlow % e.minWeiAmount); - } else { - totalNeed = 0; - } - } - } - } else { - totalNeed = 0; - } - } + // function getTotalWeiNeededExpenseAt(uint _eId, uint _currentFlow)internal view returns(uint) { + // return getTotalNeeded(expenses[_eId], _currentFlow); + // } function balanceAt(uint _eId) public view isCorrectId(_eId) returns(uint) { return expenses[_eId].balance; @@ -269,40 +186,16 @@ contract WeiTable is IWeiReceiver, Ownable { } // -------------------- public SCHEME FUNCTIONS -------------------- - function addAbsoluteExpense(uint _totalWeiNeeded, uint _minWeiAmount, bool _isPeriodic, bool _isSlidingAmount, uint _periodHours) public onlyOwner { - expenses[nodesCount] = Expense( - _totalWeiNeeded, _minWeiAmount, 0, - _periodHours, _isPeriodic, _isSlidingAmount, - 0, false, true, 0, 0, now, 0 - ); - nodesType[nodesCount] = Type.Absolute; - emit NodeAdded(nodesCount, Type.Absolute); - nodesCount += 1; - // TODO: add requires - require(!((_isSlidingAmount) && (_periodHours == 0))); - require(!(!(_isPeriodic) && (_periodHours != 0))); - require(!((_isSlidingAmount) && (!_isPeriodic))); - require(!((_totalWeiNeeded == 0) && (_minWeiAmount != 0))); - - require(_minWeiAmount <= _totalWeiNeeded); - if(_minWeiAmount != 0) { - require(_totalWeiNeeded%_minWeiAmount == 0); - } + function addAbsoluteExpense(uint128 _totalWeiNeeded, uint128 _minWeiAmount, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { + emit NodeAdded(nodesCount, Type.Absolute); + expenses[nodesCount] = constructExpense(_totalWeiNeeded, _minWeiAmount, 0, _periodHours, _isSlidingAmount, _isPeriodic); + nodesCount += 1; } - function addRelativeExpense(uint _neededPpm, bool _isPeriodic, bool _isSlidingAmount, uint _periodHours) public onlyOwner { - // TODO: add requires - require(!((_isSlidingAmount) && (_periodHours == 0))); - require(!(!(_isPeriodic) && (_periodHours != 0))); - require(!((_isSlidingAmount) && (!_isPeriodic))); - expenses[nodesCount] = Expense( - 0, 0, _neededPpm, - _periodHours, _isPeriodic, _isSlidingAmount, - 0, false, true, 0, 0, now, 0 - ); - nodesType[nodesCount] = Type.Relative; + function addRelativeExpense(uint32 _partsPerMillion, bool _isPeriodic, bool _isSlidingAmount, uint32 _periodHours) public onlyOwner { emit NodeAdded(nodesCount, Type.Relative); - nodesCount += 1; + expenses[nodesCount] = constructExpense(0, 0, _partsPerMillion, _periodHours, _isSlidingAmount, _isPeriodic); + nodesCount += 1; } function addSplitter() public onlyOwner { @@ -327,7 +220,7 @@ contract WeiTable is IWeiReceiver, Ownable { // -------------------- public CONTROL FUNCTIONS -------------------- function getReceiverTypeAt(uint _eId) internal isCorrectId(_eId) returns(Type nodeType) { if(isExpenseAt(_eId)) { - if(expenses[_eId].neededPpm > 0) { + if(expenses[_eId].partsPerMillion > 0) { nodeType = Type.Relative; } else { nodeType = Type.Absolute; diff --git a/contracts/ether/WeiExpense.sol b/contracts/ether/WeiExpense.sol index ac2f1b2..bbbafc0 100644 --- a/contracts/ether/WeiExpense.sol +++ b/contracts/ether/WeiExpense.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -pragma experimental ABIEncoderV2; -import "./ExpenseBase.sol"; + +import "../ExpenseBase.sol"; import "../interfaces/IDestination.sol"; import "../interfaces/IWeiReceiver.sol"; @@ -66,11 +66,13 @@ contract WeiExpense is ExpenseBase, IWeiReceiver, IDestination, Ownable { 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 _totalWeiNeeded) public onlyOwner { diff --git a/contracts/examples/weiTable/Budget.sol b/contracts/examples/weiTable/Budget.sol index 3e12b56..e15e598 100644 --- a/contracts/examples/weiTable/Budget.sol +++ b/contracts/examples/weiTable/Budget.sol @@ -16,37 +16,37 @@ contract Budget is Ownable { uint funds; uint bonuses; - uint budgetPeriod; + uint32 budgetPeriod; - constructor(uint _budgetPeriod) { + constructor(uint32 _budgetPeriod) { WeiTable weiTable = new WeiTable(); budgetPeriod = _budgetPeriod; createLayout(); - uint[] employeeSalaries; + uint128[] employeeSalaries; employeeSalaries.push(10e18); employeeSalaries.push(20e18); employeeSalaries.push(20e18); addEmployees(employeeSalaries); - uint[] bonuses; + uint32[] bonuses; bonuses.push(10000); bonuses.push(20000); bonuses.push(20000); addBonuses(bonuses); - uint[] tasks; + uint128[] tasks; tasks.push(0.1e18); tasks.push(0.3e18); tasks.push(0.5e18); addTasks(tasks); - uint[] utils; + uint128[] utils; utils.push(2e18); utils.push(3e18); addUtils(utils); - uint[] funds; + uint128[] funds; funds.push(20000*1e18); funds.push(20000000*1e18); addFunds(funds); @@ -82,7 +82,7 @@ contract Budget is Ownable { weiTable.addChildAt(bubgetEntry, funds); } - function addEmployees(uint[] _employeeSalaries) public onlyOwner { + function addEmployees(uint128[] _employeeSalaries) public onlyOwner { for(uint i=0; i<_employeeSalaries.length; i++) { weiTable.addAbsoluteExpense(_employeeSalaries[i], _employeeSalaries[i], true, true, budgetPeriod); uint employee = weiTable.getLastNodeId(); @@ -90,7 +90,7 @@ contract Budget is Ownable { } } - function addBonuses(uint[] _bonuses) public onlyOwner { + function addBonuses(uint32[] _bonuses) public onlyOwner { for(uint i=0; i<_bonuses.length; i++) { weiTable.addRelativeExpense(_bonuses[i], true, true, budgetPeriod); uint bonus = weiTable.getLastNodeId(); @@ -98,23 +98,23 @@ contract Budget is Ownable { } } - function addTasks(uint[] _tasks) public onlyOwner { + function addTasks(uint128[] _tasks) public onlyOwner { for(uint i=0; i<_tasks.length; i++) { - weiTable.addRelativeExpense(_tasks[i], false, false, 0); + weiTable.addAbsoluteExpense(_tasks[i], _tasks[i], false, false, 0); uint task = weiTable.getLastNodeId(); weiTable.addChildAt(tasks, task); } } - function addUtils(uint[] _oneTimeUtils) public onlyOwner { + function addUtils(uint128[] _oneTimeUtils) public onlyOwner { for(uint i=0; i<_oneTimeUtils.length; i++) { - weiTable.addRelativeExpense(_oneTimeUtils[i], false, false, 0); + weiTable.addAbsoluteExpense(_oneTimeUtils[i], _oneTimeUtils[i], false, false, 0); uint oneTimeUtil = weiTable.getLastNodeId(); weiTable.addChildAt(oneTimeUtils, oneTimeUtil); } } - function addFunds(uint[] _funds) public onlyOwner { + function addFunds(uint128[] _funds) public onlyOwner { for(uint i=0; i<_funds.length; i++) { weiTable.addAbsoluteExpense(_funds[i], 0, false, false, 0); uint fund = weiTable.getLastNodeId(); diff --git a/contracts/examples/weiTable/Roadmap.sol b/contracts/examples/weiTable/Roadmap.sol index dad57a4..6b4fb7b 100644 --- a/contracts/examples/weiTable/Roadmap.sol +++ b/contracts/examples/weiTable/Roadmap.sol @@ -12,7 +12,7 @@ contract Roadmap { uint milestone2; uint milestone3; - constructor(uint _sum1, uint _sum2, uint _sum3) { + constructor(uint128 _sum1, uint128 _sum2, uint128 _sum3) { WeiTable weiTable = new WeiTable(); weiTable.addSplitter(); uint roadmap = weiTable.getLastNodeId(); diff --git a/contracts/interfaces/IReceiver.sol b/contracts/interfaces/IReceiver.sol index 136bb50..379c2a3 100644 --- a/contracts/interfaces/IReceiver.sol +++ b/contracts/interfaces/IReceiver.sol @@ -12,7 +12,4 @@ contract IReceiver { // WeiReceiver should process all tokens here (hold it or send it somewhere else) function processFunds(uint _currentFlow) public payable; - - // non payable! - function() public; } \ No newline at end of file diff --git a/test/wei_table.tests.js b/test/wei_table.tests.js index 39e3bef..dc671aa 100644 --- a/test/wei_table.tests.js +++ b/test/wei_table.tests.js @@ -216,7 +216,7 @@ contract('WeiTable tests', (accounts) => { const employee2 = accounts[2]; const outsider = accounts[3]; - // // 0->•abs + // 0->•abs it('should process money with WeiSplitter + 3 WeiAbsoluteExpense', async () => { let weiTable = await WeiTable.new(); var output1 = await WeiAbsoluteExpense.new(money, money); @@ -245,9 +245,10 @@ contract('WeiTable tests', (accounts) => { var minNeed = await weiTable.getMinWeiNeeded(0);/*minNeedFix*/ assert.equal(minNeed, 6 * money); var need1 = await weiTable.isNeedsMoney(); + assert.equal(need1, true); // now send some money to the revenue endpoint await weiTable.processFunds(6 * money, { value: 6 * money, from: creator }); - assert.equal(need1, true); + // money should end up in the outputs var absoluteExpense1Balance = await weiTable.balanceAt(AbsoluteExpense1Id); assert.equal(absoluteExpense1Balance.toNumber(), 1 * money, 'resource point received money from splitter'); @@ -258,6 +259,10 @@ contract('WeiTable tests', (accounts) => { var absoluteExpense3Balance = await weiTable.balanceAt(AbsoluteExpense3Id); assert.equal(absoluteExpense3Balance.toNumber(), 3 * money, 'resource point received money from splitter'); + assert.equal((await weiTable.getTotalWeiNeededAt(AbsoluteExpense1Id, 6 * money)).toNumber(), 0); + assert.equal((await weiTable.getTotalWeiNeededAt(AbsoluteExpense2Id, 6 * money)).toNumber(), 0); + assert.equal((await weiTable.getTotalWeiNeededAt(AbsoluteExpense3Id, 6 * money)).toNumber(), 0); + var totalNeed = await weiTable.getTotalWeiNeeded(6 * money); assert.equal(totalNeed.toNumber(), 0 * money); var minNeed = await weiTable.getMinWeiNeeded(0);/*minNeedFix*/ @@ -519,7 +524,7 @@ contract('WeiTable tests', (accounts) => { await weiTable.closeAt(AbsoluteExpense3Id); var totalNeed = await weiTable.getTotalWeiNeeded(6 * money); - assert.equal(totalNeed, 3 * money); + assert.equal(totalNeed.toNumber(), 3 * money); var minNeed = await weiTable.getMinWeiNeeded(0);/*minNeedFix*/ assert.equal(minNeed, 3 * money);