From 27a746261cad2ff2829ed564a11836c807245c1d Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 17 May 2019 15:54:52 +0200 Subject: [PATCH 1/4] Add JS wrapper for kredits kit This makes it easier to deploy new DAOs and the deploy script is less dependent on truffle --- lib/abis/KreditsKit.json | 1 + lib/kreditskit.js | 49 ++++++++++++++++++++++++++++++++++++++++ scripts/new-dao.js | 26 +++++++++------------ 3 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 lib/abis/KreditsKit.json create mode 100644 lib/kreditskit.js diff --git a/lib/abis/KreditsKit.json b/lib/abis/KreditsKit.json new file mode 100644 index 0000000..8202592 --- /dev/null +++ b/lib/abis/KreditsKit.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fac","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"appId","type":"bytes32"}],"name":"latestVersionAppBase","outputs":[{"name":"base","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"appIds","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_fac","type":"address"},{"name":"_ens","type":"address"},{"name":"_appIds","type":"bytes32[4]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"}],"name":"DeployInstance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"},{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"constant":false,"inputs":[],"name":"newInstance","outputs":[{"name":"dao","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/lib/kreditskit.js b/lib/kreditskit.js new file mode 100644 index 0000000..30538a1 --- /dev/null +++ b/lib/kreditskit.js @@ -0,0 +1,49 @@ +const ethers = require('ethers'); + +const ABI = require('./abis/KreditsKit.json'); +const Addresses = require('./addresses/KreditsKit.json'); + +class KreditsKit { + + constructor(provider, signer, options = {}) { + let { address, abi, ipfsConfig } = options; + + this.provider = provider; + this.signer = signer; + this.options = options; + this.address = address + this.abi = abi || ABI; + } + + init() { + return this.provider.getNetwork().then((network) => { + this.address = this.address || Addresses[network.chainId.toString()]; + this.contract = new ethers.Contract( + this.address, + this.abi, + (this.signer || this.provider) + ); + return this; + }); + } + + appIdFor(contractName) { + // see appIds in KreditsKit.sol for more details + const knownContracts = ['Contribution', 'Contributor', 'Proposal', 'Token']; + return this.contract.functions.appIds(knownContracts.indexOf(contractName)); + } + + newDAO(options = {}) { + return this.contract.functions.newInstance(options).then(transaction => { + return transaction.wait().then(result => { + const deployEvent = result.events.find(e => e.event === 'DeployInstance'); + return { + daoAddress: deployEvent.args.dao, + transactionHash: transaction.hash + } + }); + }); + } +} + +module.exports = KreditsKit; diff --git a/scripts/new-dao.js b/scripts/new-dao.js index 4694c48..8601034 100644 --- a/scripts/new-dao.js +++ b/scripts/new-dao.js @@ -1,13 +1,13 @@ const fs = require('fs'); const path = require('path'); +const ethers = require('ethers'); const fileInject = require('./helpers/file_inject.js'); const getNetworkId = require('./helpers/networkid.js'); +const KreditsKit = require('../lib/kreditskit'); const addressesPath = path.join(__dirname, '..', 'lib/addresses'); -const KreditsKit = artifacts.require('KreditsKit') - module.exports = async function(callback) { const networkId = await getNetworkId(web3) console.log(`Deploying to networkId: ${networkId}`) @@ -20,25 +20,21 @@ module.exports = async function(callback) { } console.log(`Using KreditsKit at: ${kreditsKitAddress}`); - let kreditsKit = KreditsKit.at(kreditsKitAddress) + const provider = new ethers.providers.Web3Provider(web3.currentProvider); + let signer = provider.getSigner(); - kreditsKit.newInstance().then((ret) => { - console.log(ret.logs); - const installedEvents = ret.logs.filter(log => log.event === 'InstalledApp').map(log => log.args) - const deployEvents = ret.logs.filter(log => log.event === 'DeployInstance').map(log => log.args) + let kit = await new KreditsKit(provider, signer).init() - if (deployEvents.length > 1) { - callback(new Error("More than one DAO was deployed. Something is wrong")) - } - const daoAddress = deployEvents[0].dao; + // TODO: get rid of the hard coded gas limit + kit.newDAO({ gasLimit: 10000000 }).then(result => { + console.log(result); + fileInject(path.join(addressesPath, 'dao.json'), networkId, result.daoAddress) - fileInject(path.join(addressesPath, 'dao.json'), networkId, daoAddress) - - console.log(`\n\nCreated new DAO at: ${daoAddress}`) + console.log(`\n\nCreated new DAO at: ${result.daoAddress}`) callback(); }).catch((err) => { - console.log('failed to create a new instance') + console.log('failed to create a new DAO') callback(err) }) } -- 2.25.1 From 3df0831d9ba98b274a84b40e99cfca8bd42e1803 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 17 May 2019 15:56:02 +0200 Subject: [PATCH 2/4] Cleanup whitespace --- contracts/KreditsKit.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/KreditsKit.sol b/contracts/KreditsKit.sol index 3e82052..19fe2b8 100644 --- a/contracts/KreditsKit.sol +++ b/contracts/KreditsKit.sol @@ -34,20 +34,20 @@ contract KreditsKit is KitBase { Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)])); contributor.initialize(root, appIds); acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), this); - + Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)])); token.initialize(appIds); - + Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)])); contribution.initialize(appIds); - + Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)])); proposal.initialize(appIds); acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this); acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this); acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE()); - + uint256[] memory params = new uint256[](1); params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor); acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params); @@ -57,7 +57,7 @@ contract KreditsKit is KitBase { //acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE(); acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this); acl.grantPermissionP(acl.ANY_ENTITY(), proposal, proposal.VOTE_PROPOSAL_ROLE(), params); - + acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this); //acl.grantPermissionP(address(-1), proposal, proposal.ADD_PROPOSAL_ROLE(), params); acl.grantPermission(acl.ANY_ENTITY(), proposal, proposal.ADD_PROPOSAL_ROLE()); @@ -67,9 +67,9 @@ contract KreditsKit is KitBase { acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE()); acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE()); acl.setPermissionManager(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE()); - + acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this); - acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE()); + acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE()); acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE()); -- 2.25.1 From ffff09ab23d504a60715355ae507952c4270c582 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 17 May 2019 15:56:24 +0200 Subject: [PATCH 3/4] Add contract address accessor --- lib/contracts/base.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/contracts/base.js b/lib/contracts/base.js index 8d9d189..9cbcb7a 100644 --- a/lib/contracts/base.js +++ b/lib/contracts/base.js @@ -7,6 +7,10 @@ class Base { return this.contract.functions; } + get address () { + return this.contract.address; + } + get ipfs () { if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); } return this._ipfsAPI; -- 2.25.1 From ccd52f6ee56503dfebaec2e1e94577c99c6c3d18 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 17 May 2019 18:24:55 +0200 Subject: [PATCH 4/4] Cleanup --- lib/kreditskit.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/kreditskit.js b/lib/kreditskit.js index 30538a1..bc08763 100644 --- a/lib/kreditskit.js +++ b/lib/kreditskit.js @@ -5,17 +5,17 @@ const Addresses = require('./addresses/KreditsKit.json'); class KreditsKit { - constructor(provider, signer, options = {}) { - let { address, abi, ipfsConfig } = options; + constructor (provider, signer, options = {}) { + let { address, abi } = options; this.provider = provider; this.signer = signer; this.options = options; - this.address = address + this.address = address; this.abi = abi || ABI; } - init() { + init () { return this.provider.getNetwork().then((network) => { this.address = this.address || Addresses[network.chainId.toString()]; this.contract = new ethers.Contract( @@ -27,20 +27,20 @@ class KreditsKit { }); } - appIdFor(contractName) { + appIdFor (contractName) { // see appIds in KreditsKit.sol for more details const knownContracts = ['Contribution', 'Contributor', 'Proposal', 'Token']; return this.contract.functions.appIds(knownContracts.indexOf(contractName)); } - newDAO(options = {}) { + newDAO (options = {}) { return this.contract.functions.newInstance(options).then(transaction => { return transaction.wait().then(result => { const deployEvent = result.events.find(e => e.event === 'DeployInstance'); return { daoAddress: deployEvent.args.dao, - transactionHash: transaction.hash - } + transactionHash: transaction.hash, + }; }); }); } -- 2.25.1