From 35ac9291a57c3ec016b74cdc34ecac3175d40a6e Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Wed, 12 Dec 2018 20:16:51 +0100 Subject: [PATCH 1/5] Beta: allow different aragonID name and token name when creating organization through one call --- kits/beta-base/contracts/BetaKitBase.sol | 4 ++-- kits/democracy/contracts/DemocracyKit.sol | 19 ++++++++++--------- kits/multisig/contracts/MultisigKit.sol | 19 ++++++++++--------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/kits/beta-base/contracts/BetaKitBase.sol b/kits/beta-base/contracts/BetaKitBase.sol index 84248db5..ba23af5a 100644 --- a/kits/beta-base/contracts/BetaKitBase.sol +++ b/kits/beta-base/contracts/BetaKitBase.sol @@ -48,7 +48,7 @@ contract BetaKitBase is KitBase, IsContract { } function createDAO( - string name, + string aragonId, MiniMeToken token, address[] holders, uint256[] stakes, @@ -140,7 +140,7 @@ contract BetaKitBase is KitBase, IsContract { cleanupPermission(acl, voting, dao, dao.APP_MANAGER_ROLE()); cleanupPermission(acl, voting, tokenManager, tokenManager.MINT_ROLE()); - registerAragonID(name, dao); + registerAragonID(aragonId, dao); emit DeployInstance(dao, token); return (dao, acl, finance, tokenManager, vault, voting); diff --git a/kits/democracy/contracts/DemocracyKit.sol b/kits/democracy/contracts/DemocracyKit.sol index d3cd12f1..1b009bff 100644 --- a/kits/democracy/contracts/DemocracyKit.sol +++ b/kits/democracy/contracts/DemocracyKit.sol @@ -16,8 +16,9 @@ contract DemocracyKit is BetaKitBase { {} function newTokenAndInstance( - string name, - string symbol, + string tokenName, + string tokenSymbol, + string aragonId, address[] holders, uint256[] tokens, uint64 supportNeeded, @@ -25,9 +26,9 @@ contract DemocracyKit is BetaKitBase { uint64 voteDuration ) public { - newToken(name, symbol); + newToken(tokenName, tokenSymbol); newInstance( - name, + aragonId, holders, tokens, supportNeeded, @@ -36,20 +37,20 @@ contract DemocracyKit is BetaKitBase { ); } - function newToken(string name, string symbol) public returns (MiniMeToken token) { + function newToken(string tokenName, string tokenSymbol) public returns (MiniMeToken token) { token = minimeFac.createCloneToken( MiniMeToken(address(0)), 0, - name, + tokenName, 18, - symbol, + tokenSymbol, true ); cacheToken(token, msg.sender); } function newInstance( - string name, + string aragonId, address[] holders, uint256[] tokens, uint64 supportNeeded, @@ -65,7 +66,7 @@ contract DemocracyKit is BetaKitBase { ACL acl; Voting voting; (dao, acl, , , , voting) = createDAO( - name, + aragonId, token, holders, tokens, diff --git a/kits/multisig/contracts/MultisigKit.sol b/kits/multisig/contracts/MultisigKit.sol index e51dae80..e5279034 100644 --- a/kits/multisig/contracts/MultisigKit.sol +++ b/kits/multisig/contracts/MultisigKit.sol @@ -18,29 +18,30 @@ contract MultisigKit is BetaKitBase { {} function newTokenAndInstance( - string name, - string symbol, + string tokenName, + string tokenSymbol, + string aragonId, address[] signers, uint256 neededSignatures ) public { - newToken(name, symbol); - newInstance(name, signers, neededSignatures); + newToken(tokenName, tokenSymbol); + newInstance(aragonId, signers, neededSignatures); } - function newToken(string name, string symbol) public returns (MiniMeToken token) { + function newToken(string tokenName, string tokenSymbol) public returns (MiniMeToken token) { token = minimeFac.createCloneToken( MiniMeToken(address(0)), 0, - name, + tokenName, 0, - symbol, + tokenSymbol, true ); cacheToken(token, msg.sender); } - function newInstance(string name, address[] signers, uint256 neededSignatures) public { + function newInstance(string aragonId, address[] signers, uint256 neededSignatures) public { require(signers.length > 0 && neededSignatures > 0); require(neededSignatures <= signers.length); @@ -56,7 +57,7 @@ contract MultisigKit is BetaKitBase { TokenManager tokenManager; Voting voting; (dao, acl, , tokenManager, , voting) = createDAO( - name, + aragonId, token, signers, stakes, From 0d24ec65cd62245a1ed90a159d73660d5a279a84 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Wed, 12 Dec 2018 20:17:21 +0100 Subject: [PATCH 2/5] test(Beta): add parameterized tests for organizations created through one call --- kits/democracy/test/democracy.js | 2 +- kits/multisig/test/multisig.js | 519 ++++++++++++++++--------------- 2 files changed, 271 insertions(+), 250 deletions(-) diff --git a/kits/democracy/test/democracy.js b/kits/democracy/test/democracy.js index dc44267f..0bba1a9d 100644 --- a/kits/democracy/test/democracy.js +++ b/kits/democracy/test/democracy.js @@ -47,7 +47,7 @@ const getKit = async (networkName) => { const kitContractName = arappFile.path.split('/').pop().split('.sol')[0] const kit = getContract(kitContractName).at(kitAddress) - return new Promise((resolve) => resolve(kit)) + return kit } contract('Democracy Kit', accounts => { diff --git a/kits/multisig/test/multisig.js b/kits/multisig/test/multisig.js index 38d87c4b..cc5b15e6 100644 --- a/kits/multisig/test/multisig.js +++ b/kits/multisig/test/multisig.js @@ -46,7 +46,7 @@ const getKit = async (networkName) => { const kitContractName = arappFile.path.split('/').pop().split('.sol')[0] const kit = getContract(kitContractName).at(kitAddress) - return new Promise((resolve) => resolve(kit)) + return kit } @@ -77,264 +77,285 @@ contract('Multisig Kit', accounts => { await web3.eth.sendTransaction({ from: owner, to: nonHolder, value: web3.toWei(10, 'ether') }) } kit = await getKit(networkName) - - // create Token - const receiptToken = await kit.newToken('MultisigToken', 'MTT') - tokenAddress = getEventResult(receiptToken, 'DeployToken', 'token') - - // create Instance - receiptInstance = await kit.newInstance('MultisigDao-' + Math.random() * 1000, signers, neededSignatures) - daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') - - // generated apps - financeAddress = getAppProxy(receiptInstance, appIds[0]) - finance = await Finance.at(financeAddress) - tokenManagerAddress = getAppProxy(receiptInstance, appIds[1]) - tokenManager = TokenManager.at(tokenManagerAddress) - vaultAddress = getAppProxy(receiptInstance, appIds[2]) - vault = await Vault.at(vaultAddress) - votingAddress = getAppProxy(receiptInstance, appIds[3]) - voting = Voting.at(votingAddress) }) - context('Creating a DAO and signing', () => { - - it('creates and initializes a DAO with its Token', async() => { - assert.notEqual(tokenAddress, '0x0', 'Token not generated') - assert.notEqual(daoAddress, '0x0', 'Instance not generated') - assert.equal((await voting.supportRequiredPct()).toString(), multisigSupport.toString()) - assert.equal((await voting.minAcceptQuorumPct()).toString(), multisigSupport.toString()) - const maxUint64 = new web3.BigNumber(2).pow(64).minus(1) - // TODO assert.equal((await voting.voteTime()).toString(), maxUint64.toString()) - // check that it's initialized and can not be initialized again - try { - await voting.initialize(tokenAddress, 1e18, 1e18, 1000) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('has correct permissions', async () =>{ - const dao = await getContract('Kernel').at(daoAddress) - const acl = await getContract('ACL').at(await dao.acl()) - - const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { - assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) - assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) - } - - // app manager role - await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') - - // create permissions role - await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') - - // evm script registry - const regConstants = await getContract('EVMScriptRegistryConstants').new() - const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) - await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') - await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') - - // voting - await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) - await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') - await checkRole(votingAddress, await voting.MODIFY_SUPPORT_ROLE(), votingAddress, 'Voting', 'MODIFY_SUPPORT') - - // vault - await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) - - // finance - await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') - await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') - await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') - - // token manager - await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') - await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') - }) - - it('fails trying to modify support threshold directly', async () => { - try { - await voting.changeSupportRequiredPct(multisigSupport.add(1), { from: owner }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('changes support threshold thru voting', async () => { - const action1 = { to: voting.address, calldata: voting.contract.changeSupportRequiredPct.getData(multisigSupport.add(1)) } - const script1 = encodeCallScript([action1]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script1, 'metadata') } - const script2 = encodeCallScript([action2]) - const r1 = await tokenManager.forward(script2, { from: signer1 }) - const voteId1 = getVoteId(r1) - await voting.vote(voteId1, true, true, { from: signer1 }) - await voting.vote(voteId1, true, true, { from: signer2 }) - const supportThreshold1 = await voting.supportRequiredPct() - assert.equal(supportThreshold1.toString(), multisigSupport.add(1).toString(), 'Support should have changed') - const vote = await voting.getVote(voteId1) - assert.equal(vote[4].toString(), multisigSupport.toString(), 'Support for previous vote should not have changed') - - // back to original value - const action3 = { to: voting.address, calldata: voting.contract.changeSupportRequiredPct.getData(multisigSupport) } - const script3 = encodeCallScript([action3]) - const action4 = { to: voting.address, calldata: voting.contract.newVote.getData(script3, 'metadata') } - const script4 = encodeCallScript([action4]) - const r2 = await tokenManager.forward(script4, { from: signer1 }) - const voteId2 = getVoteId(r2) - await voting.vote(voteId2, true, true, { from: signer1 }) - await voting.vote(voteId2, true, true, { from: signer2 }) - await voting.vote(voteId2, true, true, { from: signer3 }) - const supportThreshold2 = await voting.supportRequiredPct() - assert.equal(supportThreshold2.toString(), multisigSupport.toString(), 'Support should have changed again') - }) - - context('creating vote', () => { - let voteId = {} - let executionTarget = {}, script - - beforeEach(async () => { - executionTarget = await getContract('ExecutionTarget').new() - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - const r = await tokenManager.forward(script2, { from: signer1 }) - voteId = getVoteId(r) - }) - - it('has correct state', async() => { - const [isOpen, isExecuted, startDate, snapshotBlock, requiredSupport, minQuorum, y, n, totalVoters, execScript] = await voting.getVote(voteId) - - assert.isTrue(isOpen, 'vote should be open') - assert.isFalse(isExecuted, 'vote should be executed') - assert.equal(snapshotBlock.toString(), await getBlockNumber() - 1, 'snapshot block should be correct') - assert.equal(requiredSupport.toString(), multisigSupport.toString(), 'min quorum should be app min quorum') - assert.equal(minQuorum.toString(), multisigSupport.toString(), 'min quorum should be app min quorum') - assert.equal(y, 0, 'initial yea should be 0') - assert.equal(n, 0, 'initial nay should be 0') - assert.equal(totalVoters.toString(), signers.length, 'total voters should be number of signers') - assert.equal(execScript, script, 'script should be correct') - }) - - it('holder can vote', async () => { - await voting.vote(voteId, false, true, { from: signer2 }) - const state = await voting.getVote(voteId) - - assert.equal(state[7].toString(), 1, 'nay vote should have been counted') - }) - - it('holder can modify vote', async () => { - await voting.vote(voteId, true, true, { from: signer2 }) - await voting.vote(voteId, false, true, { from: signer2 }) - await voting.vote(voteId, true, true, { from: signer2 }) - const state = await voting.getVote(voteId) - - assert.equal(state[6].toString(), 1, 'yea vote should have been counted') - assert.equal(state[7], 0, 'nay vote should have been removed') - }) - - it('throws when non-holder tries to create a vote directly', async () => { - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - try { - await voting.newVote(script, 'metadata', { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return + // Test when organization is created in one call with `newTokenAndInstance()` and in + // two calls with `newToken()` and `newInstance()` + const creationStyles = ['single', 'separate'] + for (const creationStyle of creationStyles) { + context(`Creation through ${creationStyle} transaction`, () => { + let aragonId, tokenName, tokenSymbol + before(async () => { + aragonId = 'MultisigDao-' + Math.random() * 1000 + tokenName = 'MultisigToken' + tokenSymbol = 'MTT' + + if (creationStyle === 'single') { + // create token and instance + const receipt = await kit.newTokenAndInstance(tokenName, tokenSymbol, aragonId, signers, neededSignatures) + tokenAddress = getEventResult(receipt, 'DeployToken', 'token') + daoAddress = getEventResult(receipt, 'DeployInstance', 'dao') + } else if (creationStyle === 'separate') { + // create token + const receiptToken = await kit.newToken(tokenName, tokenSymbol) + tokenAddress = getEventResult(receiptToken, 'DeployToken', 'token') + + // create instance + receiptInstance = await kit.newInstance(aragonId, signers, neededSignatures) + daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') } - assert.isFalse(true, "It should have thrown") - }) - it('throws when non-holder tries to create a vote thru token manager', async () => { - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - try { - await tokenManager.forward(script2, { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") + // generated apps + financeAddress = getAppProxy(receiptInstance, appIds[0]) + finance = await Finance.at(financeAddress) + tokenManagerAddress = getAppProxy(receiptInstance, appIds[1]) + tokenManager = TokenManager.at(tokenManagerAddress) + vaultAddress = getAppProxy(receiptInstance, appIds[2]) + vault = await Vault.at(vaultAddress) + votingAddress = getAppProxy(receiptInstance, appIds[3]) + voting = Voting.at(votingAddress) }) - it('throws when non-holder votes', async () => { - try { - await voting.vote(voteId, true, true, { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") + context('Creating a DAO and signing', () => { + + it('creates and initializes a DAO with its Token', async() => { + assert.notEqual(tokenAddress, '0x0', 'Token not generated') + assert.notEqual(daoAddress, '0x0', 'Instance not generated') + assert.equal((await voting.supportRequiredPct()).toString(), multisigSupport.toString()) + assert.equal((await voting.minAcceptQuorumPct()).toString(), multisigSupport.toString()) + const maxUint64 = new web3.BigNumber(2).pow(64).minus(1) + // TODO assert.equal((await voting.voteTime()).toString(), maxUint64.toString()) + // check that it's initialized and can not be initialized again + try { + await voting.initialize(tokenAddress, 1e18, 1e18, 1000) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('has correct permissions', async () =>{ + const dao = await getContract('Kernel').at(daoAddress) + const acl = await getContract('ACL').at(await dao.acl()) + + const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { + assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) + assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) + } + + // app manager role + await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') + + // create permissions role + await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') + + // evm script registry + const regConstants = await getContract('EVMScriptRegistryConstants').new() + const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) + await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') + await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') + + // voting + await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) + await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') + await checkRole(votingAddress, await voting.MODIFY_SUPPORT_ROLE(), votingAddress, 'Voting', 'MODIFY_SUPPORT') + + // vault + await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) + + // finance + await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') + await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') + await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') + + // token manager + await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') + await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') + }) + + it('fails trying to modify support threshold directly', async () => { + try { + await voting.changeSupportRequiredPct(multisigSupport.add(1), { from: owner }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('changes support threshold thru voting', async () => { + const action1 = { to: voting.address, calldata: voting.contract.changeSupportRequiredPct.getData(multisigSupport.add(1)) } + const script1 = encodeCallScript([action1]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script1, 'metadata') } + const script2 = encodeCallScript([action2]) + const r1 = await tokenManager.forward(script2, { from: signer1 }) + const voteId1 = getVoteId(r1) + await voting.vote(voteId1, true, true, { from: signer1 }) + await voting.vote(voteId1, true, true, { from: signer2 }) + const supportThreshold1 = await voting.supportRequiredPct() + assert.equal(supportThreshold1.toString(), multisigSupport.add(1).toString(), 'Support should have changed') + const vote = await voting.getVote(voteId1) + assert.equal(vote[4].toString(), multisigSupport.toString(), 'Support for previous vote should not have changed') + + // back to original value + const action3 = { to: voting.address, calldata: voting.contract.changeSupportRequiredPct.getData(multisigSupport) } + const script3 = encodeCallScript([action3]) + const action4 = { to: voting.address, calldata: voting.contract.newVote.getData(script3, 'metadata') } + const script4 = encodeCallScript([action4]) + const r2 = await tokenManager.forward(script4, { from: signer1 }) + const voteId2 = getVoteId(r2) + await voting.vote(voteId2, true, true, { from: signer1 }) + await voting.vote(voteId2, true, true, { from: signer2 }) + await voting.vote(voteId2, true, true, { from: signer3 }) + const supportThreshold2 = await voting.supportRequiredPct() + assert.equal(supportThreshold2.toString(), multisigSupport.toString(), 'Support should have changed again') + }) + + context('creating vote', () => { + let voteId = {} + let executionTarget = {}, script + + beforeEach(async () => { + executionTarget = await getContract('ExecutionTarget').new() + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + const r = await tokenManager.forward(script2, { from: signer1 }) + voteId = getVoteId(r) + }) + + it('has correct state', async() => { + const [isOpen, isExecuted, startDate, snapshotBlock, requiredSupport, minQuorum, y, n, totalVoters, execScript] = await voting.getVote(voteId) + + assert.isTrue(isOpen, 'vote should be open') + assert.isFalse(isExecuted, 'vote should be executed') + assert.equal(snapshotBlock.toString(), await getBlockNumber() - 1, 'snapshot block should be correct') + assert.equal(requiredSupport.toString(), multisigSupport.toString(), 'min quorum should be app min quorum') + assert.equal(minQuorum.toString(), multisigSupport.toString(), 'min quorum should be app min quorum') + assert.equal(y, 0, 'initial yea should be 0') + assert.equal(n, 0, 'initial nay should be 0') + assert.equal(totalVoters.toString(), signers.length, 'total voters should be number of signers') + assert.equal(execScript, script, 'script should be correct') + }) + + it('holder can vote', async () => { + await voting.vote(voteId, false, true, { from: signer2 }) + const state = await voting.getVote(voteId) + + assert.equal(state[7].toString(), 1, 'nay vote should have been counted') + }) + + it('holder can modify vote', async () => { + await voting.vote(voteId, true, true, { from: signer2 }) + await voting.vote(voteId, false, true, { from: signer2 }) + await voting.vote(voteId, true, true, { from: signer2 }) + const state = await voting.getVote(voteId) + + assert.equal(state[6].toString(), 1, 'yea vote should have been counted') + assert.equal(state[7], 0, 'nay vote should have been removed') + }) + + it('throws when non-holder tries to create a vote directly', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + try { + await voting.newVote(script, 'metadata', { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('throws when non-holder tries to create a vote thru token manager', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + try { + await tokenManager.forward(script2, { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('throws when non-holder votes', async () => { + try { + await voting.vote(voteId, true, true, { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('automatically executes if vote is approved by enough signers', async () => { + await voting.vote(voteId, true, true, { from: signer2 }) + await voting.vote(voteId, true, true, { from: signer1 }) + assert.equal((await executionTarget.counter()).toString(), 1, 'should have executed result') + }) + + it('cannot execute vote if not enough signatures', async () => { + await voting.vote(voteId, true, true, { from: signer1 }) + assert.equal(await executionTarget.counter(), 0, 'should have not executed result') + try { + await voting.executeVote(voteId, {from: owner}) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + }) }) - it('automatically executes if vote is approved by enough signers', async () => { - await voting.vote(voteId, true, true, { from: signer2 }) - await voting.vote(voteId, true, true, { from: signer1 }) - assert.equal((await executionTarget.counter()).toString(), 1, 'should have executed result') + context('finance access', () => { + let financeProxyAddress, finance, vaultProxyAddress, vault, voteId = {}, script + const payment = new web3.BigNumber(2e16) + beforeEach(async () => { + // generated Finance app + financeProxyAddress = getAppProxy(receiptInstance, appIds[0]) + finance = getContract('Finance').at(financeProxyAddress) + // generated Vault app + vaultProxyAddress = getAppProxy(receiptInstance, appIds[2]) + vault = getContract('Vault').at(vaultProxyAddress) + // Fund Finance + //await logBalances(financeProxyAddress, vaultProxyAddress) + await finance.sendTransaction({ value: payment, from: owner }) + //await logBalances(financeProxyAddress, vaultProxyAddress) + const action = { to: financeProxyAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + const r = await tokenManager.forward(script2, { from: signer1 }) + voteId = getVoteId(r) + }) + + it('finance can not be accessed directly (without a vote)', async () => { + try { + await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('transfers funds if vote is approved', async () => { + const receiverInitialBalance = await getBalance(nonHolder) + //await logBalances(financeProxyAddress, vaultProxyAddress) + await voting.vote(voteId, true, true, { from: signer2 }) + await voting.vote(voteId, true, true, { from: signer1 }) + //await logBalances(financeProxyAddress, vaultProxyAddress) + assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') + }) }) - - it('cannot execute vote if not enough signatures', async () => { - await voting.vote(voteId, true, true, { from: signer1 }) - assert.equal(await executionTarget.counter(), 0, 'should have not executed result') - try { - await voting.executeVote(voteId, {from: owner}) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - }) - }) - - context('finance access', () => { - let financeProxyAddress, finance, vaultProxyAddress, vault, voteId = {}, script - const payment = new web3.BigNumber(2e16) - beforeEach(async () => { - // generated Finance app - financeProxyAddress = getAppProxy(receiptInstance, appIds[0]) - finance = getContract('Finance').at(financeProxyAddress) - // generated Vault app - vaultProxyAddress = getAppProxy(receiptInstance, appIds[2]) - vault = getContract('Vault').at(vaultProxyAddress) - // Fund Finance - //await logBalances(financeProxyAddress, vaultProxyAddress) - await finance.sendTransaction({ value: payment, from: owner }) - //await logBalances(financeProxyAddress, vaultProxyAddress) - const action = { to: financeProxyAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - const r = await tokenManager.forward(script2, { from: signer1 }) - voteId = getVoteId(r) - }) - - it('finance can not be accessed directly (without a vote)', async () => { - try { - await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('transfers funds if vote is approved', async () => { - const receiverInitialBalance = await getBalance(nonHolder) - //await logBalances(financeProxyAddress, vaultProxyAddress) - await voting.vote(voteId, true, true, { from: signer2 }) - await voting.vote(voteId, true, true, { from: signer1 }) - //await logBalances(financeProxyAddress, vaultProxyAddress) - assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') }) - }) + } const logBalances = async(financeProxyAddress, vaultProxyAddress) => { console.log('Owner ETH: ' + await getBalance(owner)) From 9401b9c54c814633a34664fd6e819d064c331fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Wed, 12 Dec 2018 22:50:39 +0100 Subject: [PATCH 3/5] Fix receipt for single tx --- kits/multisig/test/multisig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kits/multisig/test/multisig.js b/kits/multisig/test/multisig.js index cc5b15e6..67adce07 100644 --- a/kits/multisig/test/multisig.js +++ b/kits/multisig/test/multisig.js @@ -92,9 +92,9 @@ contract('Multisig Kit', accounts => { if (creationStyle === 'single') { // create token and instance - const receipt = await kit.newTokenAndInstance(tokenName, tokenSymbol, aragonId, signers, neededSignatures) - tokenAddress = getEventResult(receipt, 'DeployToken', 'token') - daoAddress = getEventResult(receipt, 'DeployInstance', 'dao') + receiptInstance = await kit.newTokenAndInstance(tokenName, tokenSymbol, aragonId, signers, neededSignatures) + tokenAddress = getEventResult(receiptInstance, 'DeployToken', 'token') + daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') } else if (creationStyle === 'separate') { // create token const receiptToken = await kit.newToken(tokenName, tokenSymbol) From 4ce2bdbf472a14f618a536363f2f7afe7c27695f Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 13 Dec 2018 14:33:26 +0100 Subject: [PATCH 4/5] test(Beta): finish adding parameterized tests to multisig and democracy templates --- kits/democracy/test/democracy.js | 553 ++++++++++++++++--------------- kits/multisig/scripts/deploy.js | 2 +- kits/multisig/test/multisig.js | 184 +++++----- 3 files changed, 375 insertions(+), 364 deletions(-) diff --git a/kits/democracy/test/democracy.js b/kits/democracy/test/democracy.js index 0bba1a9d..b049545f 100644 --- a/kits/democracy/test/democracy.js +++ b/kits/democracy/test/democracy.js @@ -1,4 +1,5 @@ require('dotenv').config({ path: './node_modules/@aragon/kits-beta-base/.env'}) + const getBlockNumber = require('@aragon/test-helpers/blockNumber')(web3) const getBlock = require('@aragon/test-helpers/block')(web3) //const timeTravel = require('@aragon/test-helpers/timeTravel')(web3) @@ -8,6 +9,11 @@ const keccak256 = require('js-sha3').keccak_256 const { encodeCallScript, EMPTY_SCRIPT } = require('@aragon/test-helpers/evmScript') +const ENS = artifacts.require('ENS') +const PublicResolver = artifacts.require('PublicResolver') + +const MiniMeToken = artifacts.require('MiniMeToken') + const Finance = artifacts.require('Finance') const TokenManager = artifacts.require('TokenManager') const Vault = artifacts.require('Vault') @@ -30,7 +36,7 @@ const getVoteId = (receipt) => { const getAppProxy = (receipt, id) => receipt.logs.filter(l => l.event == 'InstalledApp' && l.args.appId == id)[0].args.appProxy const networks = require("@aragon/os/truffle-config").networks const getNetwork = require('../../../helpers/networks.js') -const getKit = async (networkName) => { +const getKitConfiguration = async (networkName) => { let arappFilename if (networkName == 'devnet' || networkName == 'rpc') { arappFilename = 'arapp_local' @@ -47,11 +53,12 @@ const getKit = async (networkName) => { const kitContractName = arappFile.path.split('/').pop().split('.sol')[0] const kit = getContract(kitContractName).at(kitAddress) - return kit + return { ens, kit } } contract('Democracy Kit', accounts => { const ETH = '0x0' + let ens let daoAddress, tokenAddress let financeAddress, tokenManagerAddress, vaultAddress, votingAddress let finance, tokenManager, vault, voting @@ -65,7 +72,7 @@ contract('Democracy Kit', accounts => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) - const votingTime = 120 + const votingTime = 60 before(async () => { // create Democracy Kit @@ -77,291 +84,307 @@ contract('Democracy Kit', accounts => { await web3.eth.sendTransaction({ from: owner, to: holder51, value: web3.toWei(10, 'ether') }) await web3.eth.sendTransaction({ from: owner, to: nonHolder, value: web3.toWei(10, 'ether') }) } - kit = await getKit(networkName) - const holders = [holder20, holder29, holder51] - const stakes = [20e18, 29e18, 51e18] - - // create Token - const receiptToken = await kit.newToken('DemocracyToken', 'DTT', { from: owner }) - tokenAddress = getEventResult(receiptToken, 'DeployToken', 'token') - - // create Instance - receiptInstance = await kit.newInstance('DemocracyDao-' + Math.random() * 1000, holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime, { from: owner }) - daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') - - // generated apps - financeAddress = getAppProxy(receiptInstance, appIds[0]) - finance = await Finance.at(financeAddress) - tokenManagerAddress = getAppProxy(receiptInstance, appIds[1]) - tokenManager = TokenManager.at(tokenManagerAddress) - vaultAddress = getAppProxy(receiptInstance, appIds[2]) - vault = await Vault.at(vaultAddress) - votingAddress = getAppProxy(receiptInstance, appIds[3]) - voting = Voting.at(votingAddress) + const configuration = await getKitConfiguration(networkName) + ens = configuration.ens + kit = configuration.kit }) - context('Creating a DAO and votes', () => { - - it('fails creating a DAO if holders and stakes don\'t match', async() => { - const holders = [holder20, holder29, holder51] - const stakes = [20e18, 29e18] - // create Token - await kit.newToken('BadDemocracyToken', 'DTT') - // create Instance - try { - await kit.newInstance('BadDemocracyDao', holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('creates and initializes a DAO with its Token', async() => { - assert.notEqual(tokenAddress, '0x0', 'Token not generated') - assert.notEqual(daoAddress, '0x0', 'Instance not generated') - assert.equal((await voting.supportRequiredPct()).toString(), neededSupport.toString()) - assert.equal((await voting.minAcceptQuorumPct()).toString(), minimumAcceptanceQuorum.toString()) - assert.equal((await voting.voteTime()).toString(), votingTime.toString()) - // check that it's initialized and can not be initialized again - try { - await voting.initialize(tokenAddress, neededSupport, minimumAcceptanceQuorum, votingTime) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('has correct permissions', async () =>{ - const dao = await getContract('Kernel').at(daoAddress) - const acl = await getContract('ACL').at(await dao.acl()) - - const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { - assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) - assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) - } - - // app manager role - await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') - - // create permissions role - await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') - - // evm script registry - const regConstants = await getContract('EVMScriptRegistryConstants').new() - const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) - await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') - await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') - - // voting - await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) - await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') - assert.equal(await acl.getPermissionManager(votingAddress, await voting.MODIFY_SUPPORT_ROLE()), await acl.BURN_ENTITY(), 'Voting MODIFY_SUPPORT Manager should be burned') - - // vault - await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) - - // finance - await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') - await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') - await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') - - // token manager - await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') - await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') - }) - - it('fails trying to modify support threshold', async () => { - try { - await voting.changeSupportRequiredPct(neededSupport.add(1)) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + // Test when organization is created in one call with `newTokenAndInstance()` and in + // two calls with `newToken()` and `newInstance()` + const creationStyles = ['single', 'separate'] + for (const creationStyle of creationStyles) { + context(`> Creation through ${creationStyle} transaction`, () => { + let aragonId, tokenName, tokenSymbol + + before(async () => { + aragonId = 'democracydao-' + Math.floor(Math.random() * 1000) + tokenName = 'DemocracyToken' + tokenSymbol = 'DTT' + + const holders = [holder20, holder29, holder51] + const stakes = [20e18, 29e18, 51e18] + + if (creationStyle === 'single') { + // create token and instance + receiptInstance = await kit.newTokenAndInstance(tokenName, tokenSymbol, aragonId, holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime) + tokenAddress = getEventResult(receiptInstance, 'DeployToken', 'token') + daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') + } else if (creationStyle === 'separate') { + // create Token + const receiptToken = await kit.newToken(tokenName, tokenSymbol) + tokenAddress = getEventResult(receiptToken, 'DeployToken', 'token') + + // create Instance + receiptInstance = await kit.newInstance(aragonId, holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime) + daoAddress = getEventResult(receiptInstance, 'DeployInstance', 'dao') + } - context('creating vote', () => { - let voteId = {} - let executionTarget = {}, script - - beforeEach(async () => { - executionTarget = await getContract('ExecutionTarget').new() - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - const r = await tokenManager.forward(script2, { from: holder20 }) - voteId = getVoteId(r) + // generated apps + financeAddress = getAppProxy(receiptInstance, appIds[0]) + finance = await Finance.at(financeAddress) + tokenManagerAddress = getAppProxy(receiptInstance, appIds[1]) + tokenManager = TokenManager.at(tokenManagerAddress) + vaultAddress = getAppProxy(receiptInstance, appIds[2]) + vault = await Vault.at(vaultAddress) + votingAddress = getAppProxy(receiptInstance, appIds[3]) + voting = Voting.at(votingAddress) }) - it('has correct state', async() => { - const [isOpen, isExecuted, startDate, snapshotBlock, requiredSupport, minQuorum, y, n, totalVoters, execScript] = await voting.getVote(voteId) - - assert.isTrue(isOpen, 'vote should be open') - assert.isFalse(isExecuted, 'vote should be executed') - assert.equal(snapshotBlock.toString(), await getBlockNumber() - 1, 'snapshot block should be correct') - assert.equal(requiredSupport.toString(), neededSupport.toString(), 'min quorum should be app min quorum') - assert.equal(minQuorum.toString(), minimumAcceptanceQuorum.toString(), 'min quorum should be app min quorum') - assert.equal(y, 0, 'initial yea should be 0') - assert.equal(n, 0, 'initial nay should be 0') - assert.equal(totalVoters.toString(), new web3.BigNumber(100e18).toString(), 'total voters should be 100') - assert.equal(execScript, script, 'script should be correct') - }) + it('has correct permissions', async () =>{ + const dao = await getContract('Kernel').at(daoAddress) + const acl = await getContract('ACL').at(await dao.acl()) - it('holder can vote', async () => { - await voting.vote(voteId, false, true, { from: holder29 }) - const state = await voting.getVote(voteId) + const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { + assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) + assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) + } - assert.equal(state[7].toString(), new web3.BigNumber(29e18).toString(), 'nay vote should have been counted') - }) + // app manager role + await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') - it('holder can modify vote', async () => { - await voting.vote(voteId, true, true, { from: holder29 }) - await voting.vote(voteId, false, true, { from: holder29 }) - await voting.vote(voteId, true, true, { from: holder29 }) - const state = await voting.getVote(voteId) + // create permissions role + await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') - assert.equal(state[6].toString(), new web3.BigNumber(29e18).toString(), 'yea vote should have been counted') - assert.equal(state[7], 0, 'nay vote should have been removed') - }) + // evm script registry + const regConstants = await getContract('EVMScriptRegistryConstants').new() + const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) + await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') + await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') - it('throws when non-holder tries to create a vote directly', async () => { - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - try { - await voting.newVote(script, 'metadata', { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('throws when non-holder tries to create a vote thru token manager', async () => { - const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - try { - await tokenManager.forward(script2, { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + // voting + await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) + await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') + assert.equal(await acl.getPermissionManager(votingAddress, await voting.MODIFY_SUPPORT_ROLE()), await acl.BURN_ENTITY(), 'Voting MODIFY_SUPPORT Manager should be burned') - it('throws when non-holder votes', async () => { - try { - await voting.vote(voteId, true, true, { from: nonHolder }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + // vault + await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) - it('throws when voting after voting closes', async () => { - //await timeTravel(votingTime + 1) - await sleep(votingTime+1) - try { - await voting.vote(voteId, true, true, { from: holder29 }) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + // finance + await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') + await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') + await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') - it('can execute if vote is approved with support and quorum', async () => { - await voting.vote(voteId, true, true, { from: holder29 }) - await voting.vote(voteId, false, true, { from: holder20 }) - //await timeTravel(votingTime + 1) - //console.log("Time: + " + (await getBlock(await getBlockNumber())).timestamp) - await sleep(votingTime+1) - //console.log("Time: + " + (await getBlock(await getBlockNumber())).timestamp) - await voting.executeVote(voteId, {from: owner}) - assert.equal((await executionTarget.counter()).toString(), 1, 'should have executed result') + // token manager + await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') + await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') }) - it('cannot execute vote if not enough quorum met', async () => { - await voting.vote(voteId, true, true, { from: holder20 }) - //await timeTravel(votingTime + 1) - await sleep(votingTime+1) - try { - await voting.executeVote(voteId, {from: owner}) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return + it('fails creating a DAO if holders and stakes don\'t match', async() => { + const aragonId = 'baddemocracydao' + const tokenName = 'BadDemocracyToken' + const tokenSymbol = 'BDT' + const holders = [holder20, holder29, holder51] + const stakes = [20e18, 29e18] + + if (creationStyle === 'single') { + try { + await kit.newTokenAndInstance(tokenName, tokenSymbol, aragonId, holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + } else if (creationStyle === 'separate') { + // create Token + await kit.newToken(tokenName, tokenSymbol) + // create Instance + try { + await kit.newInstance(aragonId, holders, stakes, neededSupport, minimumAcceptanceQuorum, votingTime) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } } assert.isFalse(true, "It should have thrown") }) - it('cannot execute vote if not support met', async () => { - await voting.vote(voteId, false, true, { from: holder29 }) - await voting.vote(voteId, false, true, { from: holder20 }) - //await timeTravel(votingTime + 1) - await sleep(votingTime+1) - try { - await voting.executeVote(voteId, {from: owner}) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") + context('> Vote access', () => { + it('fails trying to modify support threshold', async () => { + try { + await voting.changeSupportRequiredPct(neededSupport.add(1)) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + context('> Creating vote', () => { + let voteId + let executionTarget, script + + beforeEach(async () => { + executionTarget = await getContract('ExecutionTarget').new() + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + const r = await tokenManager.forward(script2, { from: holder20 }) + voteId = getVoteId(r) + }) + + it('has correct state', async() => { + const [isOpen, isExecuted, startDate, snapshotBlock, requiredSupport, minQuorum, y, n, totalVoters, execScript] = await voting.getVote(voteId) + + assert.isTrue(isOpen, 'vote should be open') + assert.isFalse(isExecuted, 'vote should be executed') + assert.equal(snapshotBlock.toString(), await getBlockNumber() - 1, 'snapshot block should be correct') + assert.equal(requiredSupport.toString(), neededSupport.toString(), 'min quorum should be app min quorum') + assert.equal(minQuorum.toString(), minimumAcceptanceQuorum.toString(), 'min quorum should be app min quorum') + assert.equal(y, 0, 'initial yea should be 0') + assert.equal(n, 0, 'initial nay should be 0') + assert.equal(totalVoters.toString(), new web3.BigNumber(100e18).toString(), 'total voters should be 100') + assert.equal(execScript, script, 'script should be correct') + }) + + it('holder can vote', async () => { + await voting.vote(voteId, false, true, { from: holder29 }) + const state = await voting.getVote(voteId) + + assert.equal(state[7].toString(), new web3.BigNumber(29e18).toString(), 'nay vote should have been counted') + }) + + it('holder can modify vote', async () => { + await voting.vote(voteId, true, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder29 }) + await voting.vote(voteId, true, true, { from: holder29 }) + const state = await voting.getVote(voteId) + + assert.equal(state[6].toString(), new web3.BigNumber(29e18).toString(), 'yea vote should have been counted') + assert.equal(state[7], 0, 'nay vote should have been removed') + }) + + it('throws when non-holder tries to create a vote directly', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + try { + await voting.newVote(script, 'metadata', { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('throws when non-holder tries to create a vote thru token manager', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + try { + await tokenManager.forward(script2, { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('throws when non-holder votes', async () => { + try { + await voting.vote(voteId, true, true, { from: nonHolder }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('throws when voting after voting closes', async () => { + //await timeTravel(votingTime + 1) + await sleep(votingTime + 1) + try { + await voting.vote(voteId, true, true, { from: holder29 }) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('can execute if vote is approved with support and quorum', async () => { + await voting.vote(voteId, true, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder20 }) + //await timeTravel(votingTime + 1) + //console.log("Time: + " + (await getBlock(await getBlockNumber())).timestamp) + await sleep(votingTime + 1) + //console.log("Time: + " + (await getBlock(await getBlockNumber())).timestamp) + await voting.executeVote(voteId, {from: owner}) + assert.equal((await executionTarget.counter()).toString(), 1, 'should have executed result') + }) + + it('cannot execute vote if not enough quorum met', async () => { + await voting.vote(voteId, true, true, { from: holder20 }) + //await timeTravel(votingTime + 1) + await sleep(votingTime + 1) + try { + await voting.executeVote(voteId, {from: owner}) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('cannot execute vote if not support met', async () => { + await voting.vote(voteId, false, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder20 }) + //await timeTravel(votingTime + 1) + await sleep(votingTime + 1) + try { + await voting.executeVote(voteId, {from: owner}) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + }) + + context('> Finance access', () => { + let voteId, script + const payment = new web3.BigNumber(2e16) + + beforeEach(async () => { + // Fund Finance + await finance.sendTransaction({ value: payment, from: owner }) + const action = { to: financeAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + const r = await tokenManager.forward(script2, { from: holder20 }) + voteId = getVoteId(r) + }) + + it('finance can not be accessed directly (without a vote)', async () => { + try { + await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('transfers funds if vote is approved', async () => { + const receiverInitialBalance = await getBalance(nonHolder) + //await logBalances(financeAddress, vaultAddress) + await voting.vote(voteId, true, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder20 }) + //await timeTravel(votingTime + 1) + await sleep(votingTime + 1) + await voting.executeVote(voteId, {from: owner}) + //await logBalances(financeAddress, vaultAddress) + assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') + }) + }) }) }) - }) - - context('finance access', () => { - let financeProxyAddress, finance, vaultProxyAddress, vault, voteId = {}, script - const payment = new web3.BigNumber(2e16) - beforeEach(async () => { - // generated Finance app - financeProxyAddress = getAppProxy(receiptInstance, appIds[0]) - finance = getContract('Finance').at(financeProxyAddress) - // generated Vault app - vaultProxyAddress = getAppProxy(receiptInstance, appIds[2]) - vault = getContract('Vault').at(vaultProxyAddress) - // Fund Finance - await finance.sendTransaction({ value: payment, from: owner }) - const action = { to: financeProxyAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - const r = await tokenManager.forward(script2, { from: holder20 }) - voteId = getVoteId(r) - }) - - it('finance can not be accessed directly (without a vote)', async () => { - try { - await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) - - it('transfers funds if vote is approved', async () => { - const receiverInitialBalance = await getBalance(nonHolder) - //await logBalances(financeProxyAddress, vaultProxyAddress) - await voting.vote(voteId, true, true, { from: holder29 }) - await voting.vote(voteId, false, true, { from: holder20 }) - //await timeTravel(votingTime + 1) - await sleep(votingTime+1) - await voting.executeVote(voteId, {from: owner}) - //await logBalances(financeProxyAddress, vaultProxyAddress) - assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') - }) - }) + } - const logBalances = async(financeProxyAddress, vaultProxyAddress) => { + const logBalances = async(financeAddress, vaultAddress) => { console.log('Owner ETH: ' + await getBalance(owner)) - console.log('Finance ETH: ' + await getBalance(financeProxyAddress)) - console.log('Vault ETH: ' + await getBalance(vaultProxyAddress)) + console.log('Finance ETH: ' + await getBalance(financeAddress)) + console.log('Vault ETH: ' + await getBalance(vaultAddress)) console.log('Receiver ETH: ' + await getBalance(nonHolder)) console.log('-----------------') } diff --git a/kits/multisig/scripts/deploy.js b/kits/multisig/scripts/deploy.js index b80492da..4f9726c7 100644 --- a/kits/multisig/scripts/deploy.js +++ b/kits/multisig/scripts/deploy.js @@ -17,7 +17,7 @@ module.exports = async (callback) => { const { ens } = await deploy_ens(null, { artifacts }) // APM - await deploy_apm(null, {artifacts, web3, ensAddress: ens.address }) + await deploy_apm(null, { artifacts, web3, ensAddress: ens.address }) // aragonID await deploy_id(null, { artifacts, web3, ensAddress: ens.address }) diff --git a/kits/multisig/test/multisig.js b/kits/multisig/test/multisig.js index 67adce07..b2e8ef36 100644 --- a/kits/multisig/test/multisig.js +++ b/kits/multisig/test/multisig.js @@ -1,4 +1,5 @@ require('dotenv').config({ path: './node_modules/@aragon/kits-beta-base/.env'}) + const getBlockNumber = require('@aragon/test-helpers/blockNumber')(web3) const getBlock = require('@aragon/test-helpers/block')(web3) //const timeTravel = require('@aragon/test-helpers/timeTravel')(web3) @@ -8,6 +9,11 @@ const keccak256 = require('js-sha3').keccak_256 const { encodeCallScript, EMPTY_SCRIPT } = require('@aragon/test-helpers/evmScript') +const ENS = artifacts.require('ENS') +const PublicResolver = artifacts.require('PublicResolver') + +const MiniMeToken = artifacts.require('MiniMeToken') + const Finance = artifacts.require('Finance') const TokenManager = artifacts.require('TokenManager') const Vault = artifacts.require('Vault') @@ -29,7 +35,7 @@ const getVoteId = (receipt) => { const getAppProxy = (receipt, id) => receipt.logs.filter(l => l.event == 'InstalledApp' && l.args.appId == id)[0].args.appProxy const networks = require("@aragon/os/truffle-config").networks const getNetwork = require('../../../helpers/networks.js') -const getKit = async (networkName) => { +const getKitConfiguration = async (networkName) => { let arappFilename if (networkName == 'devnet' || networkName == 'rpc') { arappFilename = 'arapp_local' @@ -46,12 +52,13 @@ const getKit = async (networkName) => { const kitContractName = arappFile.path.split('/').pop().split('.sol')[0] const kit = getContract(kitContractName).at(kitAddress) - return kit + return { ens, kit } } contract('Multisig Kit', accounts => { const ETH = '0x0' + let ens let daoAddress, tokenAddress let financeAddress, tokenManagerAddress, vaultAddress, votingAddress let finance, tokenManager, vault, voting @@ -65,6 +72,7 @@ contract('Multisig Kit', accounts => { const signers = [signer1, signer2, signer3] const neededSignatures = 2 const multisigSupport = new web3.BigNumber(10 ** 18).times(neededSignatures).dividedToIntegerBy(signers.length).minus(1) + const multisigVotingTime = 1825 * 24 * 60 * 60 // 1825 days; ~5 years before(async () => { // create Multisig Kit @@ -76,17 +84,20 @@ contract('Multisig Kit', accounts => { await web3.eth.sendTransaction({ from: owner, to: signer3, value: web3.toWei(10, 'ether') }) await web3.eth.sendTransaction({ from: owner, to: nonHolder, value: web3.toWei(10, 'ether') }) } - kit = await getKit(networkName) + const configuration = await getKitConfiguration(networkName) + ens = configuration.ens + kit = configuration.kit }) // Test when organization is created in one call with `newTokenAndInstance()` and in // two calls with `newToken()` and `newInstance()` const creationStyles = ['single', 'separate'] for (const creationStyle of creationStyles) { - context(`Creation through ${creationStyle} transaction`, () => { + context(`> Creation through ${creationStyle} transaction`, () => { let aragonId, tokenName, tokenSymbol + before(async () => { - aragonId = 'MultisigDao-' + Math.random() * 1000 + aragonId = 'multisigdao-' + Math.floor(Math.random() * 1000) tokenName = 'MultisigToken' tokenSymbol = 'MTT' @@ -116,64 +127,46 @@ contract('Multisig Kit', accounts => { voting = Voting.at(votingAddress) }) - context('Creating a DAO and signing', () => { - - it('creates and initializes a DAO with its Token', async() => { - assert.notEqual(tokenAddress, '0x0', 'Token not generated') - assert.notEqual(daoAddress, '0x0', 'Instance not generated') - assert.equal((await voting.supportRequiredPct()).toString(), multisigSupport.toString()) - assert.equal((await voting.minAcceptQuorumPct()).toString(), multisigSupport.toString()) - const maxUint64 = new web3.BigNumber(2).pow(64).minus(1) - // TODO assert.equal((await voting.voteTime()).toString(), maxUint64.toString()) - // check that it's initialized and can not be initialized again - try { - await voting.initialize(tokenAddress, 1e18, 1e18, 1000) - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + it('has correct permissions', async () => { + const dao = await getContract('Kernel').at(daoAddress) + const acl = await getContract('ACL').at(await dao.acl()) - it('has correct permissions', async () =>{ - const dao = await getContract('Kernel').at(daoAddress) - const acl = await getContract('ACL').at(await dao.acl()) - - const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { - assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) - assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) - } + const checkRole = async (appAddress, permission, managerAddress, appName='', roleName='', granteeAddress=managerAddress) => { + assert.equal(await acl.getPermissionManager(appAddress, permission), managerAddress, `${appName} ${roleName} Manager should match`) + assert.isTrue(await acl.hasPermission(granteeAddress, appAddress, permission), `Grantee should have ${appName} role ${roleName}`) + } - // app manager role - await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') + // app manager role + await checkRole(daoAddress, await dao.APP_MANAGER_ROLE(), votingAddress, 'Kernel', 'APP_MANAGER') - // create permissions role - await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') + // create permissions role + await checkRole(acl.address, await acl.CREATE_PERMISSIONS_ROLE(), votingAddress, 'ACL', 'CREATE_PERMISSION') - // evm script registry - const regConstants = await getContract('EVMScriptRegistryConstants').new() - const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) - await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') - await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') + // evm script registry + const regConstants = await getContract('EVMScriptRegistryConstants').new() + const reg = await getContract('EVMScriptRegistry').at(await acl.getEVMScriptRegistry()) + await checkRole(reg.address, await reg.REGISTRY_ADD_EXECUTOR_ROLE(), votingAddress, 'EVMScriptRegistry', 'ADD_EXECUTOR') + await checkRole(reg.address, await reg.REGISTRY_MANAGER_ROLE(), votingAddress, 'EVMScriptRegistry', 'REGISTRY_MANAGER') - // voting - await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) - await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') - await checkRole(votingAddress, await voting.MODIFY_SUPPORT_ROLE(), votingAddress, 'Voting', 'MODIFY_SUPPORT') + // voting + await checkRole(votingAddress, await voting.CREATE_VOTES_ROLE(), votingAddress, 'Voting', 'CREATE_VOTES', tokenManagerAddress) + await checkRole(votingAddress, await voting.MODIFY_QUORUM_ROLE(), votingAddress, 'Voting', 'MODIFY_QUORUM') + await checkRole(votingAddress, await voting.MODIFY_SUPPORT_ROLE(), votingAddress, 'Voting', 'MODIFY_SUPPORT') - // vault - await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) + // vault + await checkRole(vaultAddress, await vault.TRANSFER_ROLE(), votingAddress, 'Vault', 'TRANSFER', financeAddress) - // finance - await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') - await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') - await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') + // finance + await checkRole(financeAddress, await finance.CREATE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'CREATE_PAYMENTS') + await checkRole(financeAddress, await finance.EXECUTE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'EXECUTE_PAYMENTS') + await checkRole(financeAddress, await finance.MANAGE_PAYMENTS_ROLE(), votingAddress, 'Finance', 'MANAGE_PAYMENTS') - // token manager - await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') - await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') - }) + // token manager + await checkRole(tokenManagerAddress, await tokenManager.ASSIGN_ROLE(), votingAddress, 'TokenManager', 'ASSIGN') + await checkRole(tokenManagerAddress, await tokenManager.REVOKE_VESTINGS_ROLE(), votingAddress, 'TokenManager', 'REVOKE_VESTINGS') + }) + context('> Voting access', () => { it('fails trying to modify support threshold directly', async () => { try { await voting.changeSupportRequiredPct(multisigSupport.add(1), { from: owner }) @@ -212,9 +205,9 @@ contract('Multisig Kit', accounts => { assert.equal(supportThreshold2.toString(), multisigSupport.toString(), 'Support should have changed again') }) - context('creating vote', () => { - let voteId = {} - let executionTarget = {}, script + context('> Creating vote', () => { + let voteId + let executionTarget beforeEach(async () => { executionTarget = await getContract('ExecutionTarget').new() @@ -311,56 +304,51 @@ contract('Multisig Kit', accounts => { assert.isFalse(true, "It should have thrown") }) }) - }) - context('finance access', () => { - let financeProxyAddress, finance, vaultProxyAddress, vault, voteId = {}, script - const payment = new web3.BigNumber(2e16) - beforeEach(async () => { - // generated Finance app - financeProxyAddress = getAppProxy(receiptInstance, appIds[0]) - finance = getContract('Finance').at(financeProxyAddress) - // generated Vault app - vaultProxyAddress = getAppProxy(receiptInstance, appIds[2]) - vault = getContract('Vault').at(vaultProxyAddress) - // Fund Finance - //await logBalances(financeProxyAddress, vaultProxyAddress) - await finance.sendTransaction({ value: payment, from: owner }) - //await logBalances(financeProxyAddress, vaultProxyAddress) - const action = { to: financeProxyAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } - script = encodeCallScript([action]) - const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } - const script2 = encodeCallScript([action2]) - const r = await tokenManager.forward(script2, { from: signer1 }) - voteId = getVoteId(r) - }) + context('> Finance access', () => { + let voteId, script + const payment = new web3.BigNumber(2e16) - it('finance can not be accessed directly (without a vote)', async () => { - try { - await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") - } catch (err) { - assert.equal(err.receipt.status, 0, "It should have thrown") - return - } - assert.isFalse(true, "It should have thrown") - }) + beforeEach(async () => { + // Fund Finance + //await logBalances(financeAddress, vaultAddress) + await finance.sendTransaction({ value: payment, from: owner }) + //await logBalances(financeAddress, vaultAddress) + const action = { to: financeAddress, calldata: finance.contract.newPayment.getData(ETH, nonHolder, payment, 0, 0, 1, "voting payment") } + script = encodeCallScript([action]) + const action2 = { to: voting.address, calldata: voting.contract.newVote.getData(script, 'metadata') } + const script2 = encodeCallScript([action2]) + const r = await tokenManager.forward(script2, { from: signer1 }) + voteId = getVoteId(r) + }) - it('transfers funds if vote is approved', async () => { - const receiverInitialBalance = await getBalance(nonHolder) - //await logBalances(financeProxyAddress, vaultProxyAddress) - await voting.vote(voteId, true, true, { from: signer2 }) - await voting.vote(voteId, true, true, { from: signer1 }) - //await logBalances(financeProxyAddress, vaultProxyAddress) - assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') + it('finance can not be accessed directly (without a vote)', async () => { + try { + await finance.newPayment(ETH, nonHolder, 2e16, 0, 0, 1, "voting payment") + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + + it('transfers funds if vote is approved', async () => { + const receiverInitialBalance = await getBalance(nonHolder) + //await logBalances(financeAddress, vaultAddress) + await voting.vote(voteId, true, true, { from: signer2 }) + await voting.vote(voteId, true, true, { from: signer1 }) + //await logBalances(financeAddress, vaultAddress) + assert.equal((await getBalance(nonHolder)).toString(), receiverInitialBalance.plus(payment).toString(), 'Receiver didn\'t get the payment') + }) }) }) }) } - const logBalances = async(financeProxyAddress, vaultProxyAddress) => { + const logBalances = async(financeAddress, vaultAddress) => { console.log('Owner ETH: ' + await getBalance(owner)) - console.log('Finance ETH: ' + await getBalance(financeProxyAddress)) - console.log('Vault ETH: ' + await getBalance(vaultProxyAddress)) + console.log('Finance ETH: ' + await getBalance(financeAddress)) + console.log('Vault ETH: ' + await getBalance(vaultAddress)) console.log('Receiver ETH: ' + await getBalance(nonHolder)) console.log('-----------------') } From e189bee9ff017decf69776dd32e0c00a96c8f61c Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 13 Dec 2018 14:34:13 +0100 Subject: [PATCH 5/5] test(Beta): add more tests to each template --- kits/democracy/test/democracy.js | 39 ++++++++++++++++++++++++++++++++ kits/multisig/test/multisig.js | 38 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/kits/democracy/test/democracy.js b/kits/democracy/test/democracy.js index b049545f..7ef68ff1 100644 --- a/kits/democracy/test/democracy.js +++ b/kits/democracy/test/democracy.js @@ -130,6 +130,28 @@ contract('Democracy Kit', accounts => { voting = Voting.at(votingAddress) }) + it('creates and initializes a DAO with its Token', async() => { + assert.notEqual(tokenAddress, '0x0', 'Token not generated') + assert.notEqual(daoAddress, '0x0', 'Instance not generated') + + // Check ENS assignment + const aragonIdNamehash = namehash(`${aragonId}.aragonid.eth`) + const resolvedAddr = await PublicResolver.at(await ens.resolver(aragonIdNamehash)).addr(aragonIdNamehash) + assert.equal(resolvedAddr, daoAddress, "aragonId ENS name doesn't match") + + // Check token values + const token = MiniMeToken.at(tokenAddress) + assert.equal(await token.name(), tokenName, "token name doesn't match") + assert.equal(await token.symbol(), tokenSymbol, "token symbol doesn't match") + }) + + it('has initialized all apps', async () => { + assert.isTrue(await finance.hasInitialized(), 'finance not initialized') + assert.isTrue(await tokenManager.hasInitialized(), 'tokenManager not initialized') + assert.isTrue(await vault.hasInitialized(), 'vault not initialized') + assert.isTrue(await voting.hasInitialized(), 'voting not initialized') + }) + it('has correct permissions', async () =>{ const dao = await getContract('Kernel').at(daoAddress) const acl = await getContract('ACL').at(await dao.acl()) @@ -198,6 +220,23 @@ contract('Democracy Kit', accounts => { }) context('> Vote access', () => { + it('set up voting correctly', async () => { + assert.equal((await voting.supportRequiredPct()).toString(), neededSupport.toString(), 'support required not correct') + assert.equal((await voting.minAcceptQuorumPct()).toString(), minimumAcceptanceQuorum.toString(), 'accept quorum not correct') + assert.equal((await voting.voteTime()).toString(), votingTime.toString(), 'voting time not correct') + }) + + it('cannot reinitialize voting', async () => { + // check that it's initialized and can not be initialized again + try { + await voting.initialize(tokenAddress, neededSupport, minimumAcceptanceQuorum, votingTime) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + it('fails trying to modify support threshold', async () => { try { await voting.changeSupportRequiredPct(neededSupport.add(1)) diff --git a/kits/multisig/test/multisig.js b/kits/multisig/test/multisig.js index b2e8ef36..0fba8ed2 100644 --- a/kits/multisig/test/multisig.js +++ b/kits/multisig/test/multisig.js @@ -127,6 +127,28 @@ contract('Multisig Kit', accounts => { voting = Voting.at(votingAddress) }) + it('creates and initializes a DAO with its Token', async() => { + assert.notEqual(tokenAddress, '0x0', 'Token not generated') + assert.notEqual(daoAddress, '0x0', 'Instance not generated') + + // Check ENS assignment + const aragonIdNamehash = namehash(`${aragonId}.aragonid.eth`) + const resolvedAddr = await PublicResolver.at(await ens.resolver(aragonIdNamehash)).addr(aragonIdNamehash) + assert.equal(resolvedAddr, daoAddress, "aragonId ENS name doesn't match") + + // Check token values + const token = MiniMeToken.at(tokenAddress) + assert.equal(await token.name(), tokenName, "token name doesn't match") + assert.equal(await token.symbol(), tokenSymbol, "token symbol doesn't match") + }) + + it('has initialized all apps', async () => { + assert.isTrue(await finance.hasInitialized(), 'finance not initialized') + assert.isTrue(await tokenManager.hasInitialized(), 'tokenManager not initialized') + assert.isTrue(await vault.hasInitialized(), 'vault not initialized') + assert.isTrue(await voting.hasInitialized(), 'voting not initialized') + }) + it('has correct permissions', async () => { const dao = await getContract('Kernel').at(daoAddress) const acl = await getContract('ACL').at(await dao.acl()) @@ -167,6 +189,22 @@ contract('Multisig Kit', accounts => { }) context('> Voting access', () => { + it('set up voting correctly', async () => { + assert.equal((await voting.supportRequiredPct()).toString(), multisigSupport.toString(), 'support required not correct') + assert.equal((await voting.minAcceptQuorumPct()).toString(), multisigSupport.toString(), 'accept quorum not correct') + assert.equal((await voting.voteTime()).toString(), multisigVotingTime, 'voting time not correct') + }) + + it('cannot reinitialize voting', async () => { + try { + await voting.initialize(tokenAddress, 1e18, 1e18, 1000) + } catch (err) { + assert.equal(err.receipt.status, 0, "It should have thrown") + return + } + assert.isFalse(true, "It should have thrown") + }) + it('fails trying to modify support threshold directly', async () => { try { await voting.changeSupportRequiredPct(multisigSupport.add(1), { from: owner })