From 43fbef36a0dc0fdd575d0bbe83abefccaa241130 Mon Sep 17 00:00:00 2001 From: Anthony Akentiev Date: Fri, 28 Sep 2018 18:13:22 +0300 Subject: [PATCH 1/3] Adding ProxyOwnedByDAO --- contracts/ProxyOwnedByDao.sol | 29 +++++++ migrations/3_deploy_contracts_dao.js | 7 ++ package.json | 7 +- test/icomalta_dao.js | 111 +++++++++++++++++++++++++++ truffle.js | 2 +- 5 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 contracts/ProxyOwnedByDao.sol create mode 100644 migrations/3_deploy_contracts_dao.js create mode 100644 test/icomalta_dao.js diff --git a/contracts/ProxyOwnedByDao.sol b/contracts/ProxyOwnedByDao.sol new file mode 100644 index 0000000..a4d238d --- /dev/null +++ b/contracts/ProxyOwnedByDao.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.4.23; + +import "@thetta/core/contracts/DaoBase.sol"; +import "@thetta/core/contracts/DaoClient.sol"; + +import "./Proxy.sol"; + +contract ProxyOwnedByDAO is DaoClient { + Proxy public proxy; + + bytes32 public constant TRANSFER_DELEGATION = keccak256("Proxy_TransferDelegation"); + bytes32 public constant TRANSFER_OWNERSHIP = keccak256("Proxy_TransferOwnership"); + + constructor(DaoBase _daoBase, Proxy _proxy) DaoClient(_daoBase) { + // Please don't forget to call _proxy.transferOwnership() to this contract immediately + // after ProxyOwnedByDAO_1 is instantiated + proxy = _proxy; + } + +// These methods now requires special custom permissions: + function DAO_transferDelegation(address _newDelegation) public isCanDo(TRANSFER_DELEGATION) { + proxy.transferDelegation(_newDelegation); + } + + function DAO_transferOwnership(address _newOwner) public isCanDo(TRANSFER_OWNERSHIP) { + proxy.transferOwnership(_newOwner); + } +} + diff --git a/migrations/3_deploy_contracts_dao.js b/migrations/3_deploy_contracts_dao.js new file mode 100644 index 0000000..a636eda --- /dev/null +++ b/migrations/3_deploy_contracts_dao.js @@ -0,0 +1,7 @@ +var migrateLibs = require('@thetta/core/scripts/migrateLibs'); + +module.exports = function (deployer, network, accounts) { + let additionalContracts = [ "./ProxyOwnedByDAO"]; + + return migrateLibs(artifacts, additionalContracts, deployer, network, accounts); +}; diff --git a/package.json b/package.json index f2cb22d..e20691d 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,17 @@ "babel-preset-stage-2": "^6.18.0", "babel-preset-stage-3": "^6.17.0", "babel-register": "^6.23.0", - "truffle-privatekey-provider": "0.0.6", "dotenv": "^5.0.1", "ganache-cli": "^6.0.3", "solium": "^1.1.2", - "truffle": "4.0.6" + "truffle": "^4.1.14", + "truffle-privatekey-provider": "0.0.6" }, "dependencies": { + "@thetta/core": "^1.4.1", "bignumber.js": "^4.0.1", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "zeppelin-solidity": "^1.6.0" } } diff --git a/test/icomalta_dao.js b/test/icomalta_dao.js new file mode 100644 index 0000000..8d2c9f9 --- /dev/null +++ b/test/icomalta_dao.js @@ -0,0 +1,111 @@ +const Proxy = artifacts.require('./Proxy.sol'); +const ProxyOwnedByDAO = artifacts.require('./ProxyOwnedByDAO.sol'); +const Controller = artifacts.require('./Controller.sol'); + +const { assertRevert } = require('./helpers/assertThrow') + +// Thetta +var DaoBase = artifacts.require('./DaoBase'); +var StdDaoToken = artifacts.require('./StdDaoToken'); +var DaoStorage = artifacts.require('./DaoStorage'); + +contract('ProxyOwnedByDAO', (accounts) => { + let proxy; + let proxyDao; + let token; + let controller; + + beforeEach(async () => { + proxy = await Proxy.new(); + + controller = await Controller.new(); + token = Controller.at(proxy.address); + }); + + /* + it('should be initializable through proxy', async () => { + // initialize contract + await token.initialize(controller.address, 400000000); + // check total supply + let totalSupply = await token.totalSupply(); + assert.equal(totalSupply.toNumber(), 0); + // check cap + let cap = await token.cap(); + assert.equal(cap.toNumber(), 400000000); + // check wiring to proxy + let del = await proxy.delegation(); + assert.equal(del, controller.address); + // check wiring to proxy + let addr = await token.thisAddr(); + assert.equal(addr, controller.address); + }); + */ + + describe('proxy + proxyDAO with no permissions', async() => { + beforeEach(async () => { + // Create new proxyDao + let t = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); + let store = await DaoStorage.new([t.address]); + let daoBase = await DaoBase.new(store.address); + proxyDao = await ProxyOwnedByDAO.new(daoBase.address, proxy.address); + + // initialize token contract + await token.initialize(controller.address, 400000000); + + // transfer ownership to proxyDao + await proxy.transferOwnership(proxyDao.address); + assert.equal(await proxy.owner(), proxyDao.address); + }); + + it('should not allow to transfer ownership directly', async() => { + return assertRevert(async () => { + await proxy.transferOwnership(accounts[1]); + }); + assert.notEqual(await proxy.owner(), accounts[1]); + }); + + it('should not allow to transfer ownership through the proxyDAO', async() => { + return assertRevert(async () => { + await proxyDao.DAO_transferOwnership(accounts[1]); + }); + assert.notEqual(await proxy.owner(), accounts[1]); + }); + }); + + describe('proxy + proxyDAO with permissions', async() => { + beforeEach(async () => { + // Create new proxyDao + let t = await StdDaoToken.new("StdToken","STDT",18, true, true, 1000000000); + let store = await DaoStorage.new([t.address]); + let daoBase = await DaoBase.new(store.address); + proxyDao = await ProxyOwnedByDAO.new(daoBase.address, proxy.address); + + // set permissions + const transferDelegationPerm = await proxyDao.TRANSFER_DELEGATION(); + // --->> this fails + await daoBase.allowActionByAnyMemberOfGroup(transferDelegationPerm, "Employees"); + await daoBase.addGroupMember("Employees", accounts[0]); + await daoBase.renounceOwnership(); + + // initialize token contract + await token.initialize(controller.address, 400000000); + + // transfer ownership to proxyDao + await proxy.transferOwnership(proxyDao.address); + assert.equal(await proxy.owner(), proxyDao.address); + }); + + it('should not allow to transfer ownership directly', async() => { + return assertRevert(async () => { + await proxy.transferOwnership(accounts[1]); + }); + assert.notEqual(await proxy.owner(), accounts[1]); + }); + + it('should allow to transfer ownership through the proxyDAO!', async() => { + await proxyDao.DAO_transferOwnership(accounts[1]); + assert.equal(await proxy.owner(), accounts[1]); + }); + }); + +}); diff --git a/truffle.js b/truffle.js index 204e3e7..09c8212 100644 --- a/truffle.js +++ b/truffle.js @@ -9,7 +9,7 @@ module.exports = { host: "localhost", port: 8545, gasPrice: 10000000000, - gas: 5000000, + gas: 9000000, network_id: "*" // Match any network id }, kovan: { From a06b0a29cd56068984d260ce86c0758a0383a158 Mon Sep 17 00:00:00 2001 From: Anthony Akentiev Date: Sun, 30 Sep 2018 17:27:26 +0300 Subject: [PATCH 2/3] Fixing permissions --- contracts/ProxyOwnedByDao.sol | 2 +- test/icomalta_dao.js | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/contracts/ProxyOwnedByDao.sol b/contracts/ProxyOwnedByDao.sol index a4d238d..3b60022 100644 --- a/contracts/ProxyOwnedByDao.sol +++ b/contracts/ProxyOwnedByDao.sol @@ -17,7 +17,7 @@ contract ProxyOwnedByDAO is DaoClient { proxy = _proxy; } -// These methods now requires special custom permissions: +// These methods now require special custom permissions: function DAO_transferDelegation(address _newDelegation) public isCanDo(TRANSFER_DELEGATION) { proxy.transferDelegation(_newDelegation); } diff --git a/test/icomalta_dao.js b/test/icomalta_dao.js index 8d2c9f9..53a2205 100644 --- a/test/icomalta_dao.js +++ b/test/icomalta_dao.js @@ -79,12 +79,19 @@ contract('ProxyOwnedByDAO', (accounts) => { let store = await DaoStorage.new([t.address]); let daoBase = await DaoBase.new(store.address); proxyDao = await ProxyOwnedByDAO.new(daoBase.address, proxy.address); + await t.transferOwnership(daoBase.address); + await store.transferOwnership(daoBase.address); // set permissions + await daoBase.addGroupMember("Employees", accounts[0]); + const transferDelegationPerm = await proxyDao.TRANSFER_DELEGATION(); - // --->> this fails + const transferOwnershipPerm = await proxyDao.TRANSFER_OWNERSHIP(); + // NOTE: we can use votings here instead! await daoBase.allowActionByAnyMemberOfGroup(transferDelegationPerm, "Employees"); - await daoBase.addGroupMember("Employees", accounts[0]); + await daoBase.allowActionByAnyMemberOfGroup(transferOwnershipPerm, "Employees"); + + // finish initialization of DAOBase await daoBase.renounceOwnership(); // initialize token contract From c875f9d11130790607fa97aa4ec7b68a5ca29f1b Mon Sep 17 00:00:00 2001 From: Anthony Akentiev Date: Sun, 30 Sep 2018 17:29:51 +0300 Subject: [PATCH 3/3] Updating tests --- test/icomalta_dao.js | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/test/icomalta_dao.js b/test/icomalta_dao.js index 53a2205..75a0ebc 100644 --- a/test/icomalta_dao.js +++ b/test/icomalta_dao.js @@ -22,25 +22,6 @@ contract('ProxyOwnedByDAO', (accounts) => { token = Controller.at(proxy.address); }); - /* - it('should be initializable through proxy', async () => { - // initialize contract - await token.initialize(controller.address, 400000000); - // check total supply - let totalSupply = await token.totalSupply(); - assert.equal(totalSupply.toNumber(), 0); - // check cap - let cap = await token.cap(); - assert.equal(cap.toNumber(), 400000000); - // check wiring to proxy - let del = await proxy.delegation(); - assert.equal(del, controller.address); - // check wiring to proxy - let addr = await token.thisAddr(); - assert.equal(addr, controller.address); - }); - */ - describe('proxy + proxyDAO with no permissions', async() => { beforeEach(async () => { // Create new proxyDao @@ -48,6 +29,11 @@ contract('ProxyOwnedByDAO', (accounts) => { let store = await DaoStorage.new([t.address]); let daoBase = await DaoBase.new(store.address); proxyDao = await ProxyOwnedByDAO.new(daoBase.address, proxy.address); + await t.transferOwnership(daoBase.address); + await store.transferOwnership(daoBase.address); + + // finish initialization of DAOBase + await daoBase.renounceOwnership(); // initialize token contract await token.initialize(controller.address, 400000000);