From c8db1954ed32ecc7d701ce34d8cc0fcc31e6efe0 Mon Sep 17 00:00:00 2001 From: Victor Vicente Date: Thu, 13 Sep 2018 08:49:14 -0300 Subject: [PATCH 1/4] Permission_manager command added --- CLI/commands/helpers/contract_abis.js | 12 +- CLI/commands/helpers/contract_addresses.js | 9 + CLI/commands/permission_manager.js | 234 +++++++++++++++++++++ CLI/polymath-cli.js | 9 + 4 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 CLI/commands/permission_manager.js diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index 04b3b6210..52a4e43c9 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -5,11 +5,13 @@ let securityTokenABI; let cappedSTOABI; let usdTieredSTOABI; let generalTransferManagerABI; +let generalPermissionManagerABI; let polyTokenABI; let cappedSTOFactoryABI; let usdTieredSTOFactoryABI; let erc20DividendCheckpointABI; let etherDividendCheckpointABI; +let moduleInterfaceABI; try { polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; @@ -19,14 +21,16 @@ try { cappedSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTO.json').toString()).abi; usdTieredSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTO.json').toString()).abi; generalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralTransferManager.json').toString()).abi; + generalPermissionManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManager.json').toString()).abi; polyTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).abi; cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).abi; usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTOFactory.json').toString()).abi; erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpoint.json').toString()).abi; etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpoint.json').toString()).abi; + moduleInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/IModule.json').toString()).abi; } catch (err) { console.log('\x1b[31m%s\x1b[0m',"Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); - return; + throw err; } module.exports = { @@ -51,6 +55,9 @@ module.exports = { generalTransferManager: function () { return generalTransferManagerABI; }, + generalPermissionManager: function () { + return generalPermissionManagerABI; + }, polyToken: function () { return polyTokenABI; }, @@ -65,5 +72,8 @@ module.exports = { }, etherDividendCheckpoint: function () { return etherDividendCheckpointABI; + }, + moduleInterface: function () { + return moduleInterfaceABI; } } \ No newline at end of file diff --git a/CLI/commands/helpers/contract_addresses.js b/CLI/commands/helpers/contract_addresses.js index a2896a6fa..290c69be1 100644 --- a/CLI/commands/helpers/contract_addresses.js +++ b/CLI/commands/helpers/contract_addresses.js @@ -103,5 +103,14 @@ module.exports = { return "0x7e823f5df6ed1bb6cc005c692febc6aedf3b8889"; else return JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpointFactory.json').toString()).networks[networkId].address; + }, + generalPermissionManagerFactoryAddress: async function() { + let networkId = await web3.eth.net.getId(); + if (networkId == 1) + return "0xeba0348e243f2de2f1687060f9c795ac279c66af"; + else if (networkId == 42) + return "0x6f5fec2934a34d2e2374042cca6505f1c87ef79b"; + else + return JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManagerFactory.json').toString()).networks[networkId].address; } }; diff --git a/CLI/commands/permission_manager.js b/CLI/commands/permission_manager.js new file mode 100644 index 000000000..6e6483a02 --- /dev/null +++ b/CLI/commands/permission_manager.js @@ -0,0 +1,234 @@ +var readlineSync = require('readline-sync'); +var chalk = require('chalk'); +var common = require('./common/common_functions'); +var global = require('./common/global'); +var contracts = require('./helpers/contract_addresses'); +var abis = require('./helpers/contract_abis'); + +// App flow +let tokenSymbol; +let securityToken; +let generalPermissionManager; + +const MODULES_TYPES = { + PERMISSION: 1, + TRANSFER: 2, + STO: 3, + DIVIDEND: 4 +} + +async function executeApp(remoteNetwork) { + await global.initialize(remoteNetwork); + + common.logAsciiBull(); + console.log("***********************************************"); + console.log("Welcome to the Command-Line Permission Manager."); + console.log("***********************************************"); + console.log("Issuer Account: " + Issuer.address + "\n"); + + await setup(); + try { + await selectST(); + await addPermissionModule(); + await changePermissionStep(); + } catch (err) { + console.log(err); + return; + } +}; + +async function setup(){ + try { + let tickerRegistryAddress = await contracts.tickerRegistry(); + let tickerRegistryABI = abis.tickerRegistry(); + tickerRegistry = new web3.eth.Contract(tickerRegistryABI, tickerRegistryAddress); + tickerRegistry.setProvider(web3.currentProvider); + + let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); + let securityTokenRegistryABI = abis.securityTokenRegistry(); + securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); + securityTokenRegistry.setProvider(web3.currentProvider); + + let polyTokenAddress = await contracts.polyToken(); + let polyTokenABI = abis.polyToken(); + polyToken = new web3.eth.Contract(polyTokenABI, polyTokenAddress); + polyToken.setProvider(web3.currentProvider); + } catch (err) { + console.log(err) + console.log('\x1b[31m%s\x1b[0m',"There was a problem getting the contracts. Make sure they are deployed to the selected network."); + process.exit(0); + } +} + +async function selectST() { + if (!tokenSymbol) + tokenSymbol = readlineSync.question('Enter the token symbol: '); + + let result = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (result == "0x0000000000000000000000000000000000000000") { + tokenSymbol = undefined; + console.log(chalk.red(`Token symbol provided is not a registered Security Token.`)); + await selectST(); + } else { + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI,result); + } +} + +async function addPermissionModule() { + let generalPermissionManagerAddress; + let result = await securityToken.methods.getModule(1, 0).call(); + if (result[1] == "0x0000000000000000000000000000000000000000") { + console.log(chalk.red(`General Permission Manager is not attached.`)); + if (readlineSync.keyInYNStrict('Do you want to add General Permission Manager Module to your Security Token?')) { + let permissionManagerFactoryAddress = await contracts.generalPermissionManagerFactoryAddress(); + let addModuleAction = securityToken.methods.addModule(permissionManagerFactoryAddress, web3.utils.fromAscii('', 16), 0, 0); + let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'LogModuleAdded'); + console.log(`Module deployed at address: ${event._module}`); + generalPermissionManagerAddress = event._module; + } else { + process.exit(0); + } + } else { + generalPermissionManagerAddress = result[1]; + } + + let generalPermissionManagerABI = abis.generalPermissionManager(); + generalPermissionManager = new web3.eth.Contract(generalPermissionManagerABI, generalPermissionManagerAddress); + generalPermissionManager.setProvider(web3.currentProvider); +} + +async function changePermissionStep() { + console.log('\n\x1b[34m%s\x1b[0m',"Permission Manager - Change Permission"); + let selectedDelegate = await selectDelegate(); + let selectedModule = await selectModule(); + let selectedPermission = await selectPermission(selectedModule.permissions); + let isValid = isPermissionValid(); + await changePermission(selectedDelegate, selectedModule.address, selectedPermission, isValid); +} + +async function selectDelegate() { + let result; + let delegates = await getDelegates(); + + let options = ['Add new delegate']; + options = options.concat(delegates.map(function(d) { + return `Account: ${d.address} + Details: ${d.details}` + })); + + let index = readlineSync.keyInSelect(options, 'Select a delegate:', {cancel: false}); + if (index == 0) { + let newDelegate = await addNewDelegate(); + result = newDelegate; + } else { + result = delegates[index - 1].address; + } + + return result; +} + +async function selectModule() { + let modules = await getModulesWithPermissions(); + let options = modules.map(function(m) { + return m.name; + }); + let index = readlineSync.keyInSelect(options, 'Select a module:', {cancel: false}); + return modules[index]; +} + +async function selectPermission(permissions) { + let options = permissions.map(function(p) { + return p + }); + let index = readlineSync.keyInSelect(options, 'Select a permission:', {cancel: false}); + return permissions[index]; +} + +function isPermissionValid() { + let options = ['Grant permission', 'Revoke permission']; + let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false}); + return index == 0; +} + +async function changePermission(delegate, moduleAddress, permission, isValid) { + let changePermissionAction = generalPermissionManager.methods.changePermission(delegate, moduleAddress, web3.utils.asciiToHex(permission), isValid); + let receipt = await common.sendTransaction(Issuer, changePermissionAction, defaultGasPrice, 0, 1.5); + common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'LogChangePermission'); + console.log(`Permission changed succesfully,`); +} + +// Helper functions +async function getDelegates() { + let result = []; + + let events = await generalPermissionManager.getPastEvents('LogAddPermission', { fromBlock: 0}); + for (let event of events) { + let delegate = {}; + delegate.address = event.returnValues._delegate; + delegate.details = web3.utils.hexToAscii(event.returnValues._details); + result.push(delegate); + } + + return result; +} + +async function addNewDelegate() { + let newDelegate = readlineSync.question('Enter the delegate address: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let details = readlineSync.question('Enter the delegate details (i.e `Belongs to financial firm`): ', { + limit: function(input) { + return input.length > 0; + }, + limitMessage: "Must be a valid string" + }); + let addPermissionAction = generalPermissionManager.methods.addPermission(newDelegate, web3.utils.asciiToHex(details)); + let receipt = await common.sendTransaction(Issuer, addPermissionAction, defaultGasPrice); + let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'LogAddPermission'); + console.log(`Delegate added succesfully: ${event._delegate} - ${event._details}`); + return event; +} + +async function getModulesWithPermissions() { + let modules = []; + let moduleABI = abis.moduleInterface(); + + for (const type in MODULES_TYPES) { + let counter = 0; + let keepIterating = true; + while (keepIterating) { + try { + let details = await securityToken.methods.getModule(MODULES_TYPES[type], counter).call(); + if (details[1] != "0x0000000000000000000000000000000000000000") { + let contractTemp = new web3.eth.Contract(moduleABI, details[1]); + let permissions = await contractTemp.methods.getPermissions().call(); + if (permissions.length > 0) { + modules.push({ + name: web3.utils.hexToAscii(details[0]), + address: details[1], + permissions: permissions.map(function (p) { return web3.utils.hexToAscii(p) }) + }) + } + counter++; + } else { + keepIterating = false; + } + } catch (error) { + keepIterating = false; + } + } + } + + return modules; +} + +module.exports = { + executeApp: async function(remoteNetwork) { + return executeApp(remoteNetwork); + } +} \ No newline at end of file diff --git a/CLI/polymath-cli.js b/CLI/polymath-cli.js index a45544bea..fd2cb799b 100644 --- a/CLI/polymath-cli.js +++ b/CLI/polymath-cli.js @@ -8,6 +8,7 @@ var st20generator = require('./commands/ST20Generator'); var transfer = require('./commands/transfer'); var dividends_manager = require('./commands/dividends_manager'); var strMigrator = require('./commands/strMigrator'); +var permission_manager = require('./commands/permission_manager'); var program = require('commander'); const yaml = require('js-yaml'); const fs = require('fs'); @@ -115,6 +116,14 @@ program await strMigrator.executeApp(fromStrAddress, toStrAddress, program.remoteNode); }); +program + .command('permission_manager') + .alias('pm') + .description('Runs permission_manager') + .action(async function() { + await permission_manager.executeApp(program.remoteNode); + }); + program.parse(process.argv); if (typeof program.commands.length == 0) { From 994860e6d0532a78b9ed8084d863eb696a939114 Mon Sep 17 00:00:00 2001 From: Victor Vicente Date: Thu, 13 Sep 2018 09:34:36 -0300 Subject: [PATCH 2/4] newDelegate fix --- CLI/commands/permission_manager.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/CLI/commands/permission_manager.js b/CLI/commands/permission_manager.js index 6e6483a02..b436ea545 100644 --- a/CLI/commands/permission_manager.js +++ b/CLI/commands/permission_manager.js @@ -7,6 +7,7 @@ var abis = require('./helpers/contract_abis'); // App flow let tokenSymbol; +let securityTokenRegistry; let securityToken; let generalPermissionManager; @@ -39,20 +40,10 @@ async function executeApp(remoteNetwork) { async function setup(){ try { - let tickerRegistryAddress = await contracts.tickerRegistry(); - let tickerRegistryABI = abis.tickerRegistry(); - tickerRegistry = new web3.eth.Contract(tickerRegistryABI, tickerRegistryAddress); - tickerRegistry.setProvider(web3.currentProvider); - let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); securityTokenRegistry.setProvider(web3.currentProvider); - - let polyTokenAddress = await contracts.polyToken(); - let polyTokenABI = abis.polyToken(); - polyToken = new web3.eth.Contract(polyTokenABI, polyTokenAddress); - polyToken.setProvider(web3.currentProvider); } catch (err) { console.log(err) console.log('\x1b[31m%s\x1b[0m',"There was a problem getting the contracts. Make sure they are deployed to the selected network."); @@ -191,7 +182,7 @@ async function addNewDelegate() { let receipt = await common.sendTransaction(Issuer, addPermissionAction, defaultGasPrice); let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'LogAddPermission'); console.log(`Delegate added succesfully: ${event._delegate} - ${event._details}`); - return event; + return event._delegate; } async function getModulesWithPermissions() { From f75b915006794e90b9d4a0697bee50540a799564 Mon Sep 17 00:00:00 2001 From: Victor Vicente Date: Thu, 13 Sep 2018 09:41:13 -0300 Subject: [PATCH 3/4] Issuer account is read from ./privKeyLocal --- CLI/commands/common/global.js | 7 ++----- README.md | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/CLI/commands/common/global.js b/CLI/commands/common/global.js index 3a005d320..e9107e694 100644 --- a/CLI/commands/common/global.js +++ b/CLI/commands/common/global.js @@ -30,11 +30,8 @@ module.exports = { Issuer = await web3.eth.accounts.privateKeyToAccount("0x" + privKey); } else { web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); - let accounts = await web3.eth.getAccounts(); - Issuer = { - address: accounts[0], - privateKey: require('fs').readFileSync('./privKeyLocal').toString() - }; + let privKey = require('fs').readFileSync('./privKeyLocal').toString(); + Issuer = await web3.eth.accounts.privateKeyToAccount("0x" + privKey); } defaultGasPrice = getGasPrice(await web3.eth.net.getId()); } diff --git a/README.md b/README.md index fc4498ecf..820fc8cf6 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ You can access Ethereum via the Infura load-balanced nodes. You have to save you node CLI/polymath-cli faucet --remote-node kovan ``` 3. Connected to a local private test network using `ganache-cli`. -You have to save the private key for the first account generated by ganache into `./privKeyLocal`. +You have to save the private key for the one of the accounts generated by ganache into `./privKeyLocal`. ## Poly Faucet From 31e7227d395b1af2cef9874ba9617e872bc3c52d Mon Sep 17 00:00:00 2001 From: Victor Vicente Date: Thu, 11 Oct 2018 10:03:47 -0300 Subject: [PATCH 4/4] CLI Permission Manager update --- CLI/commands/ST20Generator.js | 3 +- CLI/commands/dividends_manager.js | 3 +- CLI/commands/permission_manager.js | 63 ++++++++++++++---------------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/CLI/commands/ST20Generator.js b/CLI/commands/ST20Generator.js index 1b7fd034b..5f67eee00 100644 --- a/CLI/commands/ST20Generator.js +++ b/CLI/commands/ST20Generator.js @@ -20,7 +20,8 @@ const MODULES_TYPES = { PERMISSION: 1, TRANSFER: 2, STO: 3, - DIVIDENDS: 4 + DIVIDENDS: 4, + BURN: 5 } const cappedSTOFee = 20000; diff --git a/CLI/commands/dividends_manager.js b/CLI/commands/dividends_manager.js index a757b78ef..631e780f3 100644 --- a/CLI/commands/dividends_manager.js +++ b/CLI/commands/dividends_manager.js @@ -18,7 +18,8 @@ const MODULES_TYPES = { PERMISSION: 1, TRANSFER: 2, STO: 3, - DIVIDENDS: 4 + DIVIDENDS: 4, + BURN: 5 } // App flow diff --git a/CLI/commands/permission_manager.js b/CLI/commands/permission_manager.js index b436ea545..49a20f7e3 100644 --- a/CLI/commands/permission_manager.js +++ b/CLI/commands/permission_manager.js @@ -15,7 +15,8 @@ const MODULES_TYPES = { PERMISSION: 1, TRANSFER: 2, STO: 3, - DIVIDEND: 4 + DIVIDEND: 4, + BURN: 5 } async function executeApp(remoteNetwork) { @@ -68,21 +69,21 @@ async function selectST() { async function addPermissionModule() { let generalPermissionManagerAddress; - let result = await securityToken.methods.getModule(1, 0).call(); - if (result[1] == "0x0000000000000000000000000000000000000000") { + let result = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralPermissionManager')).call(); + if (result.length == 0) { console.log(chalk.red(`General Permission Manager is not attached.`)); if (readlineSync.keyInYNStrict('Do you want to add General Permission Manager Module to your Security Token?')) { - let permissionManagerFactoryAddress = await contracts.generalPermissionManagerFactoryAddress(); + let permissionManagerFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.PERMISSION, 'GeneralPermissionManager'); let addModuleAction = securityToken.methods.addModule(permissionManagerFactoryAddress, web3.utils.fromAscii('', 16), 0, 0); let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'LogModuleAdded'); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); console.log(`Module deployed at address: ${event._module}`); generalPermissionManagerAddress = event._module; } else { process.exit(0); } } else { - generalPermissionManagerAddress = result[1]; + generalPermissionManagerAddress = result[0]; } let generalPermissionManagerABI = abis.generalPermissionManager(); @@ -99,6 +100,7 @@ async function changePermissionStep() { await changePermission(selectedDelegate, selectedModule.address, selectedPermission, isValid); } +// Helper functions async function selectDelegate() { let result; let delegates = await getDelegates(); @@ -146,14 +148,13 @@ function isPermissionValid() { async function changePermission(delegate, moduleAddress, permission, isValid) { let changePermissionAction = generalPermissionManager.methods.changePermission(delegate, moduleAddress, web3.utils.asciiToHex(permission), isValid); let receipt = await common.sendTransaction(Issuer, changePermissionAction, defaultGasPrice, 0, 1.5); - common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'LogChangePermission'); + common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'ChangePermission'); console.log(`Permission changed succesfully,`); } -// Helper functions async function getDelegates() { let result = []; - + /* let events = await generalPermissionManager.getPastEvents('LogAddPermission', { fromBlock: 0}); for (let event of events) { let delegate = {}; @@ -161,7 +162,14 @@ async function getDelegates() { delegate.details = web3.utils.hexToAscii(event.returnValues._details); result.push(delegate); } - + */ + let delegates = await generalPermissionManager.methods.getAllDelegates().call(); + for (let d of delegates) { + let delegate = {}; + delegate.address = d; + delegate.details = web3.utils.hexToAscii(await generalPermissionManager.methods.delegateDetails(d).call()); + result.push(delegate); + } return result; } @@ -178,9 +186,9 @@ async function addNewDelegate() { }, limitMessage: "Must be a valid string" }); - let addPermissionAction = generalPermissionManager.methods.addPermission(newDelegate, web3.utils.asciiToHex(details)); + let addPermissionAction = generalPermissionManager.methods.addDelegate(newDelegate, web3.utils.asciiToHex(details)); let receipt = await common.sendTransaction(Issuer, addPermissionAction, defaultGasPrice); - let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'LogAddPermission'); + let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'AddDelegate'); console.log(`Delegate added succesfully: ${event._delegate} - ${event._details}`); return event._delegate; } @@ -190,27 +198,16 @@ async function getModulesWithPermissions() { let moduleABI = abis.moduleInterface(); for (const type in MODULES_TYPES) { - let counter = 0; - let keepIterating = true; - while (keepIterating) { - try { - let details = await securityToken.methods.getModule(MODULES_TYPES[type], counter).call(); - if (details[1] != "0x0000000000000000000000000000000000000000") { - let contractTemp = new web3.eth.Contract(moduleABI, details[1]); - let permissions = await contractTemp.methods.getPermissions().call(); - if (permissions.length > 0) { - modules.push({ - name: web3.utils.hexToAscii(details[0]), - address: details[1], - permissions: permissions.map(function (p) { return web3.utils.hexToAscii(p) }) - }) - } - counter++; - } else { - keepIterating = false; - } - } catch (error) { - keepIterating = false; + let modulesAttached = await securityToken.methods.getModulesByType(MODULES_TYPES[type]).call(); + for (const m of modulesAttached) { + let contractTemp = new web3.eth.Contract(moduleABI, m); + let permissions = await contractTemp.methods.getPermissions().call(); + if (permissions.length > 0) { + modules.push({ + name: web3.utils.hexToAscii((await securityToken.methods.getModule(m).call())[0]), + address: m, + permissions: permissions.map(function (p) { return web3.utils.hexToAscii(p) }) + }) } } }