Skip to content

Commit

Permalink
Merge pull request #330 from Thetta/dev
Browse files Browse the repository at this point in the history
dev -> master
  • Loading branch information
AnthonyAkentiev committed Aug 29, 2018
2 parents cf1ce70 + 825cee7 commit 3e3e8d2
Show file tree
Hide file tree
Showing 8 changed files with 2,984 additions and 2,784 deletions.
4 changes: 2 additions & 2 deletions contracts/governance/GenericProposal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract GenericProposal is IProposal, Ownable {
uint256(params.length), // length of the array
params)
){
// revert();
revert();
}
}

Expand All @@ -72,4 +72,4 @@ contract GenericProposal is IProposal, Ownable {
function getParams() public view returns(bytes32[]) {
return params;
}
}
}
6 changes: 5 additions & 1 deletion contracts/governance/IVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ pragma solidity ^0.4.22;
* Any algorightm inside (1p1v, linear, quadratic, etc)
*/
contract IVoting {
// this should be called right after Voting is created by the creator
// to indicate that he is saying 'YES'
function voteFromOriginPositive() public;

// _tokenAmount -> if this voting type DOES NOT use tokens -> set to any value (e.g., 0);
// will execute action automatically if the voting is finished
function vote(bool _yes) public;
Expand Down Expand Up @@ -40,4 +44,4 @@ contract IVoting {
// 2.1 - isFinished()
// 2.2 - is quorum reached
function isYes()public view returns(bool);
}
}
9 changes: 7 additions & 2 deletions contracts/governance/Voting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ contract Voting is IVoting, Ownable {
function getPowerOf(address _voter) public view returns(uint) {
return store.getPowerOf(_voter);
}


function voteFromOriginPositive() public {
// vote posisite from the originator of the voting
store.libVote(store.votingCreator, true);
}

function vote(bool _isYes) public {
store.libVote(msg.sender, _isYes);
}
Expand Down Expand Up @@ -111,4 +116,4 @@ contract Voting is IVoting, Ownable {
function removeDelegation(address _to) public {
store.removeDelegation(_to);
}
}
}
12 changes: 10 additions & 2 deletions contracts/governance/VotingLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ library VotingLib {
struct VotingStorage {
IDaoBase dao;
IProposal proposal;

address votingCreator;
uint minutesToVote;
bool finishedWithYes;
bool canceled;
Expand Down Expand Up @@ -67,6 +69,7 @@ library VotingLib {
require((_quorumPercent<=100)&&(_quorumPercent>0));
require((_consensusPercent<=100)&&(_consensusPercent>0));

store.votingCreator = _origin;
store.dao = _dao;
store.proposal = _proposal;
store.minutesToVote = _minutesToVote;
Expand All @@ -80,7 +83,12 @@ library VotingLib {
store.tokenAddress = _tokenAddress;
store.votingID = StdDaoToken(_tokenAddress).startNewVoting();
}
libVote(store, _origin, true);

// This is now commented here. DO NOT UNCOMMENT.
// Please manually call the 'voteFromOriginPositive'
// right after Voting is created!
//
//libVote(store, _origin, true);
}

function getNow() public view returns(uint) {
Expand Down Expand Up @@ -251,4 +259,4 @@ library VotingLib {
store.proposal.action(); // can throw!
}
}
}
}
5 changes: 5 additions & 0 deletions contracts/utils/GenericCallerLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,14 @@ library GenericCallerLib {
IVoting voting = createVoting(store, _permissionId, prop, _origin);
prop.setVoting(voting);

// 3 - add the proposal
// WARNING: should be permitted to add new proposal by the current contract address!!!
// check your permissions or see examples (tests) how to do that correctly
store.dao.addNewProposal(prop);

// 4 - do first vote
// voting can be finished immediately and action can be called right here ->
voting.voteFromOriginPositive();
return prop;
}
}
Expand Down
154 changes: 139 additions & 15 deletions test/daobase_auto.functional.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ var DaoBaseWithUnpackers = artifacts.require("./DaoBaseWithUnpackers");
var StdDaoToken = artifacts.require("./StdDaoToken");
var DaoStorage = artifacts.require("./DaoStorage");
var DaoBaseAuto = artifacts.require("./DaoBaseAuto");
var MoneyFlow = artifacts.require('./MoneyFlow');
var MoneyflowAuto = artifacts.require('./MoneyflowAuto');
var WeiAbsoluteExpense = artifacts.require('./WeiAbsoluteExpense');
var Voting = artifacts.require('./Voting');

var IVoting = artifacts.require("./IVoting");
var IProposal = artifacts.require("./IProposal");
Expand Down Expand Up @@ -47,24 +51,35 @@ function fromUtf8(str) {
return padToBytes32(hex);
};

function addressToBytes32 (addr) {
while (addr.length < 66) {
addr = '0' + addr;
}
return '0x' + addr.replace('0x', '');
}

const VOTING_TYPE_SIMPLE_TOKEN = 2;

contract('DaoBaseAuto', (accounts) => {
const creator = accounts[0];
const employee1 = accounts[1];
const employee2 = accounts[2];
const employee3 = accounts[3];
const outsider = accounts[4];
const output = accounts[5];
const employee4 = accounts[4];
const outsider = accounts[5];
const output = accounts[6];

let issueTokens;
let manageGroups;
let addNewProposal;
let upgradeDaoContract;
let moneyflowInstance;
let addNewTask;
let startTask;
let startBounty;
let setRootWeiReceiver;
let modifyMoneyscheme;
let withdrawDonations;
let setRootWeiReceiver;
let burnTokens;

let money = web3.toWei(0.001, "ether");
Expand All @@ -80,7 +95,7 @@ contract('DaoBaseAuto', (accounts) => {
await token.mintFor(employee1, 600);
await token.mintFor(employee2, 600);
await token.mintFor(employee3, 600);

await token.mintFor(employee4, 600);
// store = await DaoStorage.new([token.address],{ from: creator });
store = await DaoStorage.new([token.address],{from: creator});
daoBase = await DaoBaseWithUnpackers.new(store.address,{ from: creator });
Expand Down Expand Up @@ -110,13 +125,14 @@ contract('DaoBaseAuto', (accounts) => {

it('should not automatically create proposal because AAC has no rights',async() => {
// Set permissions:
await daoBase.allowActionByAnyMemberOfGroup(addNewProposal, "Employees");
await daoBase.allowActionByVoting(manageGroups, token.address);
await daoBase.allowActionByVoting(issueTokens, token.address);
await daoBase.allowActionByAnyMemberOfGroup(addNewProposal, "Employees");
await daoBase.allowActionByVoting(manageGroups, token.address);
await daoBase.allowActionByVoting(issueTokens, token.address);

// THIS IS REQUIRED because issueTokensAuto() will add new proposal (voting)
// because of this AAC can't add new proposal!
//
//await daoBase.allowActionByAddress("addNewProposal", aacInstance.address);
//await daoBase.allowActionByAddress(addNewProposal, aacInstance.address);

//////
const proposalsCount1 = await daoBase.getProposalsCount();
Expand All @@ -143,7 +159,7 @@ contract('DaoBaseAuto', (accounts) => {

// SEE this -> this permissions is commented! So even if AAC has rights to add proposal,
// the proposal will never be finished
// await daoBase.allowActionByVoting("issueTokens", token.address);
//await daoBase.allowActionByVoting(issueTokens, token.address);

// THIS IS REQUIRED because issueTokensAuto() will add new proposal (voting)
await daoBase.allowActionByAddress(addNewProposal, aacInstance.address);
Expand All @@ -155,7 +171,7 @@ contract('DaoBaseAuto', (accounts) => {

// even creator cant issue token directly!
// Even creator cant issue tokens
await daoBase.issueTokens.sendTransaction(token.address, employee1, 1500, {from:creator}).should.be.rejectedWith('revert');
await daoBase.issueTokens.sendTransaction(token.address, employee1, 1500, {from:creator}).should.be.rejectedWith('revert');

const proposalsCount1 = await daoBase.getProposalsCount();
assert.equal(proposalsCount1,0,'No proposals should be added');
Expand Down Expand Up @@ -204,23 +220,27 @@ contract('DaoBaseAuto', (accounts) => {

// already voted!
// dont vote again!
await voting.vote.sendTransaction(true, {from: employee1}).should.be.rejectedWith('revert');
await voting.vote.sendTransaction(true, {from: employee1}).should.be.rejectedWith('revert');

// vote by employee 2
await voting.vote(true,{from:employee2});

const r2 = await voting.getVotingStats();
assert.equal(r2[0],2,'yes'); // 1 already voted (who started the voting)
assert.equal(r2[1],0,'no');

assert.strictEqual(await voting.isFinished(),false,'Voting should not be finished');
assert.strictEqual(await voting.isYes(),false,'Voting results are still not calculated');

// vote by employee 3
await voting.vote(true,{from:employee3});
await voting.vote(true,{from:employee3}).should.be.rejectedWith('revert');

// get voting results again
assert.strictEqual(await voting.isFinished(),true,'Voting should be finished');
assert.strictEqual(await voting.isYes(),true,'Voting is finished');
assert.strictEqual(await voting.isFinished(),false,'Voting can not be finished');
assert.strictEqual(await voting.isYes(),false,'Voting is finished');

const balance2 = await token.balanceOf(employee1);
assert.notEqual(balance2.toNumber(),1000,'employee1 balance should not be updated');
assert.notEqual(balance2.toNumber(),0,'employee1 balance should not be updated');
});

it('should automatically create proposal and 1P1V voting to issue more tokens',async() => {
Expand Down Expand Up @@ -365,4 +385,108 @@ contract('DaoBaseAuto', (accounts) => {
const proposalsCount2 = await daoBase.getProposalsCount();
assert.equal(proposalsCount2,1,'New proposal should be added');
});

it('bug 283 test - should call action immediately when voting is created because i am the majority',async() => {
await daoBase.allowActionByAnyMemberOfGroup(addNewProposal,"Employees");
await daoBase.allowActionByVoting(manageGroups, token.address);
await daoBase.allowActionByVoting(issueTokens, token.address);

// THIS IS REQUIRED because issueTokensAuto() will add new proposal (voting)
await daoBase.allowActionByAddress(addNewProposal, aacInstance.address);
// these actions required if AAC will call this actions DIRECTLY (without voting)
await daoBase.allowActionByAddress(manageGroups, aacInstance.address);
await daoBase.allowActionByAddress(issueTokens, aacInstance.address);

const proposalsCount1 = await daoBase.getProposalsCount();
assert.equal(proposalsCount1,0,'No proposals should be added');

// add new employee1
//await daoBase.addGroupMember("Employees",employee1);
//const isEmployeeAdded = await daoBase.isGroupMember("Employees",employee1);
//assert.strictEqual(isEmployeeAdded,true,'employee1 should be added as the company`s employee');

// should not be able to do action directly!
// employee1 is NOT in the majority
const isCanDo1 = await daoBase.isCanDoAction(creator, issueTokens);
assert.strictEqual(isCanDo1,false,'cretor is NOT in the majority, so can issue token only with voting');

const balance0 = await token.balanceOf(employee1);
assert.notEqual(balance0.toNumber(),0,'employee1 balance is 0');

// new proposal should be added
await aacInstance.issueTokensAuto(token.address,employee1,1000,{from: creator});
const proposalsCount2 = await daoBase.getProposalsCount();
assert.equal(proposalsCount2,1,'New proposal should be added');

// check voting state
const pa = await daoBase.getProposalAtIndex(0);
const proposal = await IProposal.at(pa);
const votingAddress = await proposal.getVoting();
const voting = await IVoting.at(votingAddress);
assert.strictEqual(await voting.isFinished(),true,'Voting is finished');

const balance1 = await token.balanceOf(employee1);
assert.notEqual(balance1.toNumber(),1000,'employee1 balance is 1000');
});

it('should revert due to token dont have permissions setRootWeiReceiver',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);

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 });

addNewProposal = await daoBase.ADD_NEW_PROPOSAL();

withdrawDonations = await moneyflowInstance.WITHDRAW_DONATIONS();

setRootWeiReceiver = await moneyflowInstance.SET_ROOT_WEI_RECEIVER();

await store.addGroupMember(KECCAK256('Employees'), creator);
await store.allowActionByAddress(manageGroups, creator);
await store.allowActionByAddress(issueTokens, creator);

// do not forget to transfer ownership
await token.transferOwnership(daoBase.address);
await store.transferOwnership(daoBase.address);

// AAC requires special permissions
await daoBase.allowActionByAddress(addNewProposal, aacInstance.address);
await daoBase.allowActionByAddress(withdrawDonations, aacInstance.address);
await daoBase.allowActionByAddress(setRootWeiReceiver, aacInstance.address);
await daoBase.allowActionByAddress(issueTokens, aacInstance.address);

// do not forget to transfer ownership
await daoBase.allowActionByAnyMemberOfGroup(addNewProposal, 'Employees');

await daoBase.allowActionByVoting(manageGroups, token.address);
await daoBase.allowActionByVoting(issueTokens, token.address);
await daoBase.allowActionByVoting(addNewProposal, token.address);

await daoBase.addGroupMember('Employees', employee1);
await daoBase.addGroupMember('Employees', employee2);
await daoBase.addGroupMember('Employees', employee3);
await daoBase.addGroupMember('Employees', employee4);

await aacInstance.setVotingParams(setRootWeiReceiver, VOTING_TYPE_SIMPLE_TOKEN, UintToToBytes32(0), fromUtf8(''), UintToToBytes32(100), UintToToBytes32(100), addressToBytes32(token.address));
const wae = await WeiAbsoluteExpense.new(1000);
await aacInstance.setRootWeiReceiverAuto(wae.address, { from: employee1 });

const pa = await daoBase.getProposalAtIndex(0);
const proposal = await IProposal.at(pa);
const votingAddress = await proposal.getVoting();
const voting = await Voting.at(votingAddress);

await voting.vote(true, { from: employee2 });
await voting.vote(true, { from: employee3 });
await voting.vote(true, { from: employee4 });

await voting.vote(true, {from: creator}).should.be.rejectedWith('revert'); // call action
});
});
Loading

0 comments on commit 3e3e8d2

Please sign in to comment.