From 33fb78e32e51ab0e8323bcc9e5ecb21e0fa73ee7 Mon Sep 17 00:00:00 2001 From: Rostyslav Bortman Date: Wed, 25 Jul 2018 01:57:30 +0300 Subject: [PATCH] Improve StdDaoToken - startVoting/endVoting through the DaoBase #210 --- contracts/governance/IVoting.sol | 2 + contracts/governance/Voting.sol | 25 ++-- contracts/tokens/ITokenVotingSupport.sol | 2 +- contracts/tokens/StdDaoToken.sol | 17 ++- contracts/utils/GenericCaller.sol | 13 ++- test/governance/voting_1p1v.tests.js | 3 +- test/governance/voting_liquid.tests.js | 43 ++++++- test/governance/voting_liquid_func.tests.js | 47 ++++---- test/governance/voting_quadratic.tests.js | 17 ++- .../voting_simple_token.functional.tests.js | 15 +++ test/governance/voting_simple_token.tests.js | 24 ++-- test/tokens/stddaotoken.tests.js | 109 ++++++++++++------ 12 files changed, 228 insertions(+), 89 deletions(-) diff --git a/contracts/governance/IVoting.sol b/contracts/governance/IVoting.sol index 154947d..8e3f4ee 100644 --- a/contracts/governance/IVoting.sol +++ b/contracts/governance/IVoting.sol @@ -19,6 +19,8 @@ contract IVoting { // Can get this stats if voting is NOT finished. function getVotingStats() public view returns(uint yesResults, uint noResults, uint totalResults); + function setStdDaoTokenVotingID(uint _stdDaoTokenVotingID) public; + // Is voting finished? // // 1 - First we check if minutesToVote!=0 and time elapsed diff --git a/contracts/governance/Voting.sol b/contracts/governance/Voting.sol index bd44d43..ca62adf 100644 --- a/contracts/governance/Voting.sol +++ b/contracts/governance/Voting.sol @@ -18,6 +18,9 @@ contract Voting is IVoting, Ownable { event CallAction(); event DelegatedTo(address _sender, uint _tokensAmount); event DelegationRemoved(address _from, address _to); + uint private yesResults; + uint private noResults; + uint private votersTotal; /* * @param _dao – DAO where proposal was created. @@ -40,12 +43,10 @@ contract Voting is IVoting, Ownable { } function setStdDaoTokenVotingID(uint _stdDaoTokenVotingID) public onlyOwner { - if(VotingType.Voting1p1v!=store.votingType){ - store.votingID = _stdDaoTokenVotingID; - } + store.votingID = _stdDaoTokenVotingID; + vote(true); } - function quorumPercent()view returns(uint){ return store.quorumPercent; } @@ -68,9 +69,12 @@ contract Voting is IVoting, Ownable { function vote(bool _isYes) public{ store.libVote(msg.sender, _isYes); - if(store.isFinished()){ - StdDaoToken(store.tokenAddress).finishVoting(store.votingID); - } + } + + function finishVoting() public { + require (isFinished()); + + StdDaoToken(store.tokenAddress).finishVoting(store.votingID); } function callActionIfEnded() public { @@ -85,7 +89,7 @@ contract Voting is IVoting, Ownable { return store.isYes(); } - function getVotingStats() public constant returns(uint yesResults, uint noResults, uint votersTotal){ + function getVotingStats() public constant returns(uint _yesResults, uint _noResults, uint _votersTotal){ return store.getVotingStats(); } @@ -176,7 +180,10 @@ library VotingLib { store.genesis = now; store.tokenAddress = _tokenAddress; - libVote(store, _origin, true); + if(VotingType.Voting1p1v==store.votingType){ + libVote(store, _origin, true); + } + } function getNow() public view returns(uint){ diff --git a/contracts/tokens/ITokenVotingSupport.sol b/contracts/tokens/ITokenVotingSupport.sol index 09965a0..097bc87 100644 --- a/contracts/tokens/ITokenVotingSupport.sol +++ b/contracts/tokens/ITokenVotingSupport.sol @@ -25,7 +25,7 @@ pragma solidity ^0.4.22; */ interface ITokenVotingSupport { // can throw if 20 votings are already started - function startNewVoting() external returns(uint); + function startNewVoting(address _voting) external returns(uint); function finishVoting(uint _votingID) external; diff --git a/contracts/tokens/StdDaoToken.sol b/contracts/tokens/StdDaoToken.sol index c1ecbc8..b49ac12 100644 --- a/contracts/tokens/StdDaoToken.sol +++ b/contracts/tokens/StdDaoToken.sol @@ -5,7 +5,6 @@ import "zeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol"; import "./CopyOnWriteToken.sol"; import "./ITokenVotingSupport.sol"; -import "../DaoBase.sol"; /** * @title StdDaoToken @@ -25,7 +24,7 @@ import "../DaoBase.sol"; * finishVoting() * getBalanceAtVoting() */ -contract StdDaoToken is DetailedERC20, PausableToken, CopyOnWriteToken, DaoBase, ITokenVotingSupport { +contract StdDaoToken is DetailedERC20, PausableToken, CopyOnWriteToken, ITokenVotingSupport { uint256 public cap; bool isBurnable; bool isPausable; @@ -33,9 +32,17 @@ contract StdDaoToken is DetailedERC20, PausableToken, CopyOnWriteToken, DaoBase, address[] public holders; mapping (address => bool) isHolder; mapping (uint => address) votingCreated; + mapping (bytes32 => mapping(address => bool)) permissions; bytes32 public TOKEN_StartNewVoting = keccak256(abi.encodePacked("TOKEN_StartNewVoting")); + bytes32 public allow_actions = keccak256(abi.encodePacked("allow_actions")); + bytes32 public disallow_actions = keccak256(abi.encodePacked("disallow_actions")); + modifier isCanDo(bytes32 _permissionID) { + require (permissions[_permissionID][msg.sender]); + _; + } + modifier isBurnable_() { require (isBurnable); _; @@ -56,10 +63,16 @@ contract StdDaoToken is DetailedERC20, PausableToken, CopyOnWriteToken, DaoBase, cap = _cap; isBurnable = _isBurnable; isPausable = _isPausable; + permissions[allow_actions][msg.sender] = true; + permissions[TOKEN_StartNewVoting][msg.sender] = true; holders.push(this); } + function allowActionByAddress(address _who, bytes32 _action) public isCanDo(allow_actions) { + permissions[_action][_who] = true; + } + // ITokenVotingSupport implementation // TODO: VULNERABILITY! no onlyOwner! function startNewVoting(address _voting) public whenNotPaused isCanDo(TOKEN_StartNewVoting) returns(uint) { diff --git a/contracts/utils/GenericCaller.sol b/contracts/utils/GenericCaller.sol index 958aa30..69ec4a8 100644 --- a/contracts/utils/GenericCaller.sol +++ b/contracts/utils/GenericCaller.sol @@ -114,19 +114,20 @@ contract GenericCaller is DaoClient, Ownable { function createVoting(bytes32 _permissionIdHash, IProposal _proposal, address _origin)public returns(IVoting){ VotingParams memory vp = votingParams[_permissionIdHash]; - StdDaoToken memory token = StdDaoToken(address(vp.param5)); + StdDaoToken token = StdDaoToken(address(vp.param5)); IVoting V = new Voting(dao, _proposal, _origin, vp.votingType, uint(vp.param1), bytes32ToString(vp.param2), uint(vp.param3), uint(vp.param4), - address(vp.param5), - stdDaoTokenVotingID + address(vp.param5) ); - - uint stdDaoTokenVotingID = token.startNewVoting(V.address); - V.setStdDaoTokenVotingID(stdDaoTokenVotingID); + + if(vp.votingType != VotingLib.VotingType.Voting1p1v){ + uint stdDaoTokenVotingID = token.startNewVoting(address(V)); + V.setStdDaoTokenVotingID(stdDaoTokenVotingID); + } return V; } diff --git a/test/governance/voting_1p1v.tests.js b/test/governance/voting_1p1v.tests.js index 8c14a6d..4b57def 100644 --- a/test/governance/voting_1p1v.tests.js +++ b/test/governance/voting_1p1v.tests.js @@ -186,8 +186,9 @@ contract('Voting 1P1V', (accounts) => { var isY = await voting.isYes(); assert.strictEqual(fin,true,'Voting should be finished: 4/6 voted'); assert.strictEqual(isY,true,'Voting is finished: 4/6 voted, all said yes'); - await daoBase.removeGroupMember("Employees", employee3, {from:creator}); + await daoBase.removeGroupMember("Employees", employee3, {from:creator}); + // WARNING: // if voting is finished -> even if we remove some employees from the group // the voting results should not be changed!!! diff --git a/test/governance/voting_liquid.tests.js b/test/governance/voting_liquid.tests.js index 4456af5..eaff1e6 100644 --- a/test/governance/voting_liquid.tests.js +++ b/test/governance/voting_liquid.tests.js @@ -36,11 +36,11 @@ contract('Voting liquid', (accounts) => { let r2; let token; - let voting; let daoBase; beforeEach(async() => { token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); + await token.mintFor(creator, 1); await token.mintFor(employee1, 1); await token.mintFor(employee2, 2); @@ -52,6 +52,12 @@ contract('Voting liquid', (accounts) => { describe('getPowerOf()', function () { it('Check getPower()',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getPowerOf(creator); assert.equal(r2.toNumber(),1,'yes'); }); @@ -59,6 +65,11 @@ contract('Voting liquid', (accounts) => { it('Check getPower() when voice delegated',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + await voting.delegateMyVoiceTo(employee1, 1); r2 = await voting.getPowerOf(creator); @@ -72,6 +83,11 @@ contract('Voting liquid', (accounts) => { it('Check getPower() when voice delegated and delegation removed',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + await voting.delegateMyVoiceTo(employee1, 1); r2 = await voting.getPowerOf(creator); @@ -96,6 +112,11 @@ contract('Voting liquid', (accounts) => { it('Check getDelegatedPowerOf()',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getDelegatedPowerOf(creator); assert.equal(r2.toNumber(),0,'yes'); @@ -114,6 +135,11 @@ contract('Voting liquid', (accounts) => { it('Check getDelegatedPowerByMe()',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getDelegatedPowerByMe(creator); assert.equal(r2.toNumber(),0,'yes'); @@ -132,6 +158,11 @@ contract('Voting liquid', (accounts) => { it('Should delegate from A to B',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getDelegatedPowerOf(creator); assert.equal(r2.toNumber(),0); @@ -144,6 +175,11 @@ contract('Voting liquid', (accounts) => { it('Should delegate from A to B then from A to B again with the same amount',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getDelegatedPowerOf(creator); assert.equal(r2.toNumber(),0); @@ -163,6 +199,11 @@ contract('Voting liquid', (accounts) => { it('Check removeDelegation()',async() => { const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, token.address); + const tx = await token.startNewVoting(voting.address); + const events = tx.logs.filter(l => l.event == 'VotingStarted'); + const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await voting.setStdDaoTokenVotingID(votingID); + r2 = await voting.getDelegatedPowerOf(creator); assert.equal(r2.toNumber(),0); diff --git a/test/governance/voting_liquid_func.tests.js b/test/governance/voting_liquid_func.tests.js index bcf1943..0854567 100644 --- a/test/governance/voting_liquid_func.tests.js +++ b/test/governance/voting_liquid_func.tests.js @@ -86,6 +86,7 @@ contract('Voting liquid (func)', (accounts) => { let daoBase; let moneyflowInstance; let aacInstance; + let startNewVoting; let issueTokens; let manageGroups; @@ -98,18 +99,20 @@ contract('Voting liquid (func)', (accounts) => { beforeEach(async() => { token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); - await token.mintFor(creator, 1); - await token.mintFor(employee1, 1); - await token.mintFor(employee2, 1); - await token.mintFor(employee3, 1); - await token.mintFor(employee4, 1); - // await token.mintFor(employee5, 1); + startNewVoting = await token.TOKEN_StartNewVoting(); let store = await DaoStorage.new([token.address],{ from: creator }); daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); moneyflowInstance = await MoneyFlow.new(daoBase.address, {from: creator}); aacInstance = await MoneyflowAuto.new(daoBase.address, moneyflowInstance.address, {from: creator}); + await token.mintFor(creator, 1); + await token.mintFor(employee1, 1); + await token.mintFor(employee2, 1); + await token.mintFor(employee3, 1); + //await token.mintFor(employee4, 1); + await token.mintFor(aacInstance.address, 1); + issueTokens = await daoBase.ISSUE_TOKENS(); manageGroups = await daoBase.MANAGE_GROUPS(); @@ -120,6 +123,8 @@ contract('Voting liquid (func)', (accounts) => { setRootWeiReceiver = await moneyflowInstance.SET_ROOT_WEI_RECEIVER(); + await token.allowActionByAddress(aacInstance.address, startNewVoting); + await store.addGroupMember(KECCAK256("Employees"), creator); await store.allowActionByAddress(manageGroups,creator); await store.allowActionByAddress(issueTokens,creator); @@ -170,7 +175,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting is still not finished'); assert.strictEqual(await voting.isYes(),false,'Voting is still not finished'); - await voting.vote(true,{from:employee2}); + await voting.vote(true,{from:employee1}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),2,'yes'); assert.equal(r2[1].toNumber(),0,'no'); @@ -178,7 +183,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting should be finished'); assert.strictEqual(await voting.isYes(),false,'Voting is finished'); - await voting.vote(true,{from:employee3}); + await voting.vote(true,{from:employee2}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),3,'yes'); assert.equal(r2[1].toNumber(),0,'no'); @@ -186,7 +191,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting should be finished'); assert.strictEqual(await voting.isYes(),false,'Voting is finished'); - await voting.vote(true,{from:employee4}); + await voting.vote(true,{from:employee3}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),4,'yes'); assert.equal(r2[1].toNumber(),0,'no'); @@ -195,7 +200,9 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isYes(),false,'Voting is finished'); await voting.vote(true); + r2 = await voting.getVotingStats(); + assert.equal(r2[0].toNumber(),5,'yes'); assert.equal(r2[1].toNumber(),0,'no'); @@ -259,7 +266,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting should be finished'); assert.strictEqual(await voting.isYes(),false,'Voting is finished'); - await voting.vote(false,{from:employee4}); + await voting.vote(false,{from:employee1}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),1,'yes'); assert.equal(r2[1].toNumber(),3,'no'); @@ -304,7 +311,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting should be finished'); assert.strictEqual(await voting.isYes(),false,'Voting is finished'); - await voting.vote(false,{from:employee4}); + await voting.vote(false,{from:employee1}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),1,'yes'); assert.equal(r2[1].toNumber(),3,'no'); @@ -349,7 +356,7 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting should be finished'); assert.strictEqual(await voting.isYes(),false,'Voting is finished'); - await voting.vote(false,{from:employee4}); + await voting.vote(false,{from:employee1}); r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),1,'yes'); assert.equal(r2[1].toNumber(),3,'no'); @@ -488,7 +495,7 @@ contract('Voting liquid (func)', (accounts) => { await voting.vote(true,{from:employee2}); await voting.vote(false,{from:employee3}); - await voting.vote(false,{from:employee4}); + await voting.vote(false,{from:employee1}); await voting.vote(false); assert.strictEqual(await voting.isFinished(),false,'Voting is still not finished'); @@ -496,8 +503,8 @@ contract('Voting liquid (func)', (accounts) => { await increaseTime(3600 * 25 * 1000); - assert.strictEqual(await voting.isFinished(),true,'Voting is still not finished'); - assert.strictEqual(await voting.isYes(),true,'Voting is still not finished'); + assert.strictEqual(await voting.isFinished(),true,'Voting is finished'); + assert.strictEqual(await voting.isYes(),true,'Voting is finished'); }); @@ -519,15 +526,15 @@ contract('Voting liquid (func)', (accounts) => { assert.strictEqual(await voting.isFinished(),false,'Voting is still not finished'); assert.strictEqual(await voting.isYes(),false,'Voting is still not finished'); - await voting.vote(true,{from:employee4}); + await voting.vote(true,{from:employee1}); assert.strictEqual(await voting.isFinished(),false,'Voting is still not finished'); assert.strictEqual(await voting.isYes(),false,'Voting is still not finished'); await increaseTime(3600 * 25 * 1000); - assert.strictEqual(await voting.isFinished(),true,'Voting is still not finished'); - assert.strictEqual(await voting.isYes(),true,'Voting is still not finished'); + assert.strictEqual(await voting.isFinished(),true,'Voting is finished'); + assert.strictEqual(await voting.isYes(),true,'Voting is finished'); }); it('1.12. T Scenario: yes, params(20,20) => isYes==true',async() => { @@ -611,11 +618,11 @@ contract('Voting liquid (func)', (accounts) => { it('1.16. Q Scenario: creator have 11/15 tokens, (50,50) => isYes==true',async() => { await aacInstance.setVotingParams(setRootWeiReceiver, VOTING_TYPE_LIQUID, UintToToBytes32(0), fromUtf8("Employees"), UintToToBytes32(50), UintToToBytes32(50), addressToBytes32(token.address)); - await daoBase.issueTokens(token.address, creator, 10); + await daoBase.issueTokens(token.address, aacInstance.address, 10); let totalSupply = await token.totalSupply(); assert.equal(totalSupply.toNumber(), 15); - let creatorBalance = await token.balanceOf(creator); + let creatorBalance = await token.balanceOf(aacInstance.address); assert.equal(creatorBalance.toNumber(), 11); const wae = await WeiAbsoluteExpense.new(1000); diff --git a/test/governance/voting_quadratic.tests.js b/test/governance/voting_quadratic.tests.js index 3de3e50..858a141 100644 --- a/test/governance/voting_quadratic.tests.js +++ b/test/governance/voting_quadratic.tests.js @@ -77,6 +77,7 @@ contract('Voting Quadratic', (accounts) => { let daoBase; let moneyflowInstance; let aacInstance; + let startNewVoting; let issueTokens; let manageGroups; @@ -92,17 +93,19 @@ contract('Voting Quadratic', (accounts) => { beforeEach(async() => { token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); + let store = await DaoStorage.new([token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + moneyflowInstance = await MoneyFlow.new(daoBase.address, {from: creator}); + aacInstance = await MoneyflowAuto.new(daoBase.address, moneyflowInstance.address, { from: creator }); + await token.mintFor(creator, 25); - await token.mintFor(employee1, 11); + await token.mintFor(aacInstance.address, 11); await token.mintFor(employee2, 9); await token.mintFor(employee3, 4); await token.mintFor(employee4, 16); // await token.mint(employee5, 1); - - let store = await DaoStorage.new([token.address],{ from: creator }); - daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); - moneyflowInstance = await MoneyFlow.new(daoBase.address, {from: creator}); - aacInstance = await MoneyflowAuto.new(daoBase.address, moneyflowInstance.address, { from: creator }); + + startNewVoting = await token.TOKEN_StartNewVoting(); issueTokens = await daoBase.ISSUE_TOKENS(); @@ -114,6 +117,8 @@ contract('Voting Quadratic', (accounts) => { setRootWeiReceiver = await moneyflowInstance.SET_ROOT_WEI_RECEIVER(); + await token.allowActionByAddress(aacInstance.address, startNewVoting); + await store.addGroupMember(KECCAK256("Employees"), creator); await store.allowActionByAddress(manageGroups,creator); await store.allowActionByAddress(issueTokens,creator); diff --git a/test/governance/voting_simple_token.functional.tests.js b/test/governance/voting_simple_token.functional.tests.js index a7c2f5e..266ece9 100644 --- a/test/governance/voting_simple_token.functional.tests.js +++ b/test/governance/voting_simple_token.functional.tests.js @@ -56,6 +56,21 @@ contract('Multiple Votings', (accounts) => { let simpleVoting = await Voting.new(daoBase.address, employee1, employee1, VOTING_TYPE_SIMPLE_TOKEN, 60, '', 51, 71, token.address); let qudraticVoting = await Voting.new(daoBase.address, employee1, employee1, VOTING_TYPE_QUADRATIC, 60, '', 51, 71, token.address); + let tx = await token.startNewVoting(liquidVoting.address); + let events = tx.logs.filter(l => l.event == 'VotingStarted'); + let votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await liquidVoting.setStdDaoTokenVotingID(votingID); + + tx = await token.startNewVoting(simpleVoting.address); + events = tx.logs.filter(l => l.event == 'VotingStarted'); + votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await simpleVoting.setStdDaoTokenVotingID(votingID); + + tx = await token.startNewVoting(qudraticVoting.address); + events = tx.logs.filter(l => l.event == 'VotingStarted'); + votingID = events.filter(e => e.args._address == creator)[0].args._votingID; + await qudraticVoting.setStdDaoTokenVotingID(votingID); + r2 = await liquidVoting.getPowerOf(creator); assert.equal(r2.toNumber(),1,'yes'); diff --git a/test/governance/voting_simple_token.tests.js b/test/governance/voting_simple_token.tests.js index 1d69ab3..5595113 100644 --- a/test/governance/voting_simple_token.tests.js +++ b/test/governance/voting_simple_token.tests.js @@ -87,6 +87,7 @@ contract('Voting simple token', (accounts) => { let daoBase; let moneyflowInstance; let aacInstance; + let startNewVoting; let issueTokens; let manageGroups; @@ -99,17 +100,18 @@ contract('Voting simple token', (accounts) => { beforeEach(async() => { token = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); + let store = await DaoStorage.new([token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + moneyflowInstance = await MoneyFlow.new(daoBase.address, {from: creator}); + aacInstance = await MoneyflowAuto.new(daoBase.address, moneyflowInstance.address, { from: creator }); await token.mintFor(creator, 1); - await token.mintFor(employee1, 1); + await token.mintFor(aacInstance.address, 1); await token.mintFor(employee2, 1); await token.mintFor(employee3, 1); await token.mintFor(employee4, 1); // await token.mintFor(employee5, 1); - let store = await DaoStorage.new([token.address],{ from: creator }); - daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); - moneyflowInstance = await MoneyFlow.new(daoBase.address, {from: creator}); - aacInstance = await MoneyflowAuto.new(daoBase.address, moneyflowInstance.address, {from: creator}); + startNewVoting = await token.TOKEN_StartNewVoting(); issueTokens = await daoBase.ISSUE_TOKENS(); @@ -121,6 +123,8 @@ contract('Voting simple token', (accounts) => { setRootWeiReceiver = await moneyflowInstance.SET_ROOT_WEI_RECEIVER(); + await token.allowActionByAddress(aacInstance.address, startNewVoting); + await store.addGroupMember(KECCAK256("Employees"), creator); await store.allowActionByAddress(manageGroups,creator); await store.allowActionByAddress(issueTokens,creator); @@ -612,11 +616,11 @@ contract('Voting simple token', (accounts) => { it('1.16. Q Scenario: creator have 11/15 tokens, (50,50) => isYes==true',async() => { await aacInstance.setVotingParams(setRootWeiReceiver, VOTING_TYPE_SIMPLE_TOKEN, UintToToBytes32(0), fromUtf8("Employees"), UintToToBytes32(50), UintToToBytes32(50), addressToBytes32(token.address)); - await daoBase.issueTokens(token.address, creator, 10); + await daoBase.issueTokens(token.address, aacInstance.address, 10); let totalSupply = await token.totalSupply(); assert.equal(totalSupply.toNumber(), 15); - let creatorBalance = await token.balanceOf(creator); + let creatorBalance = await token.balanceOf(aacInstance.address); assert.equal(creatorBalance.toNumber(), 11); const wae = await WeiAbsoluteExpense.new(1000); @@ -635,7 +639,7 @@ contract('Voting simple token', (accounts) => { assert.strictEqual(await voting.isYes(),true,'Voting is finished'); }); - it('1.17. Q Scenario: creator have 1/15 tokens, employee1 have 10 and vote no, (50,50) => isYes==false',async() => { + it('1.17. Q Scenario: creator have 1/15 tokens, employee1 have 11 and vote no, (50,50) => isYes==false',async() => { await aacInstance.setVotingParams(setRootWeiReceiver, VOTING_TYPE_SIMPLE_TOKEN, UintToToBytes32(0), fromUtf8("Employees"), UintToToBytes32(50), UintToToBytes32(50), addressToBytes32(token.address)); await daoBase.issueTokens(token.address, employee1, 10); const wae = await WeiAbsoluteExpense.new(1000); @@ -644,7 +648,7 @@ contract('Voting simple token', (accounts) => { let totalSupply = await token.totalSupply(); assert.equal(totalSupply.toNumber(), 15); let e1Balance = await token.balanceOf(employee1); - assert.equal(e1Balance.toNumber(), 11); + assert.equal(e1Balance.toNumber(), 10); const pa = await daoBase.getProposalAtIndex(0); const proposal = await IProposal.at(pa); @@ -657,7 +661,7 @@ contract('Voting simple token', (accounts) => { r2 = await voting.getVotingStats(); assert.equal(r2[0].toNumber(),1,'yes'); - assert.equal(r2[1].toNumber(),11,'no'); + assert.equal(r2[1].toNumber(),10,'no'); assert.equal(r2[2].toNumber(),15,'total'); assert.strictEqual(await voting.isFinished(),true,'Voting should be finished'); diff --git a/test/tokens/stddaotoken.tests.js b/test/tokens/stddaotoken.tests.js index 11f40eb..4184b89 100644 --- a/test/tokens/stddaotoken.tests.js +++ b/test/tokens/stddaotoken.tests.js @@ -1,6 +1,9 @@ const BigNumber = web3.BigNumber; const StdDaoToken = artifacts.require('StdDaoToken'); +const DaoStorage = artifacts.require("./DaoStorage"); +var DaoBaseWithUnpackers = artifacts.require("./DaoBaseWithUnpackers"); +var Voting = artifacts.require("./Voting"); require('chai') .use(require('chai-as-promised')) @@ -12,8 +15,11 @@ require('chai') const employee3 = accounts[3]; const employee4 = accounts[4]; const employee5 = accounts[5]; + let store; + let daoBase; const ETH = 1000000000000000000; + const VOTING_TYPE_LIQUID = 4; beforeEach(async function () { @@ -126,40 +132,49 @@ require('chai') it('should not be possible to call if paused',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); await this.token.pause(); - await this.token.startNewVoting().should.be.rejectedWith('revert'); + await this.token.startNewVoting(voting.address).should.be.rejectedWith('revert'); }); it('should not allow to create > 20 separate votings',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); - - await this.token.startNewVoting();//1 - await this.token.startNewVoting();//2 - await this.token.startNewVoting();//3 - await this.token.startNewVoting();//4 - await this.token.startNewVoting();//5 - await this.token.startNewVoting();//6 - await this.token.startNewVoting();//7 - await this.token.startNewVoting();//8 - await this.token.startNewVoting();//9 - await this.token.startNewVoting();//10 - await this.token.startNewVoting();//11 - await this.token.startNewVoting();//12 - await this.token.startNewVoting();//13 - await this.token.startNewVoting();//14 - await this.token.startNewVoting();//15 - await this.token.startNewVoting();//16 - await this.token.startNewVoting();//17 - await this.token.startNewVoting();//18 - await this.token.startNewVoting();//19 - await this.token.startNewVoting();//20 - await this.token.startNewVoting().should.be.rejectedWith('revert'); //should be revert(); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); + + await this.token.startNewVoting(voting.address);//1 + await this.token.startNewVoting(voting.address);//2 + await this.token.startNewVoting(voting.address);//3 + await this.token.startNewVoting(voting.address);//4 + await this.token.startNewVoting(voting.address);//5 + await this.token.startNewVoting(voting.address);//6 + await this.token.startNewVoting(voting.address);//7 + await this.token.startNewVoting(voting.address);//8 + await this.token.startNewVoting(voting.address);//9 + await this.token.startNewVoting(voting.address);//10 + await this.token.startNewVoting(voting.address);//11 + await this.token.startNewVoting(voting.address);//12 + await this.token.startNewVoting(voting.address);//13 + await this.token.startNewVoting(voting.address);//14 + await this.token.startNewVoting(voting.address);//15 + await this.token.startNewVoting(voting.address);//16 + await this.token.startNewVoting(voting.address);//17 + await this.token.startNewVoting(voting.address);//18 + await this.token.startNewVoting(voting.address);//19 + await this.token.startNewVoting(voting.address);//20 + await this.token.startNewVoting(voting.address).should.be.rejectedWith('revert'); //should be revert(); }); }); describe('getBalanceAtVoting()', function () { it('should preserve balances if no transfers happened after voting is started',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); await this.token.mintFor(employee4, 1); let employee4Balance = await this.token.balanceOf(employee4); @@ -168,7 +183,7 @@ require('chai') assert.equal(employee4Balance.toNumber(), 1); assert.equal(employee5Balance.toNumber(), 0); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -181,9 +196,12 @@ require('chai') it('should preserve balances after voting is started',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); await this.token.mintFor(employee4, 1); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -204,15 +222,18 @@ require('chai') it('should preserve balances in 2 different votings',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); await this.token.mintFor(employee4, 1); - let tx = await this.token.startNewVoting(); + let tx = await this.token.startNewVoting(voting.address); let events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; await this.token.transfer(employee5, 1, {from: employee4}); - tx = await this.token.startNewVoting(); + tx = await this.token.startNewVoting(voting.address); events = tx.logs.filter(l => l.event == 'VotingStarted'); const secondVotingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -237,9 +258,13 @@ require('chai') it('should preserve balances after voting is started and burnFor called',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, true, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); + await this.token.mintFor(employee4, 1); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -256,8 +281,11 @@ require('chai') it('should preserve balances after voting is started and mintFor called',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -274,9 +302,13 @@ require('chai') it('should throw exception when trying to check balancesAtVoting after voting is ended',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); + await this.token.mintFor(employee4, 1); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(creator); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -301,9 +333,13 @@ require('chai') it('should preserve balances after voting is started and transferFrom is called',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); + await this.token.mintFor(employee4, 1); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -351,8 +387,11 @@ require('chai') it('should not be possible to call if paused',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID; @@ -362,9 +401,13 @@ require('chai') it('should throw revert() if VotingID is wrong',async() => { this.token = await StdDaoToken.new("StdToken","STDT",18, false, true, ETH); + store = await DaoStorage.new([this.token.address],{ from: creator }); + daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator }); + const voting = await Voting.new(daoBase.address, creator, creator, VOTING_TYPE_LIQUID, 0, '', 100, 100, this.token.address); + await this.token.mintFor(employee4, 1); - const tx = await this.token.startNewVoting(); + const tx = await this.token.startNewVoting(voting.address); const events = tx.logs.filter(l => l.event == 'VotingStarted'); const votingID = events.filter(e => e.args._address == creator)[0].args._votingID;