Skip to content

Commit 84c0077

Browse files
VictorVicentepabloruiz55
authored andcommitted
Cli permissions manager (#335)
* Permission_manager command added * newDelegate fix * Issuer account is read from ./privKeyLocal * CLI Permission Manager update
1 parent e940a2d commit 84c0077

File tree

6 files changed

+247
-4
lines changed

6 files changed

+247
-4
lines changed

CLI/commands/ST20Generator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const MODULES_TYPES = {
2020
PERMISSION: 1,
2121
TRANSFER: 2,
2222
STO: 3,
23-
DIVIDENDS: 4
23+
DIVIDENDS: 4,
24+
BURN: 5
2425
}
2526

2627
const cappedSTOFee = 20000;

CLI/commands/dividends_manager.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ const MODULES_TYPES = {
1818
PERMISSION: 1,
1919
TRANSFER: 2,
2020
STO: 3,
21-
DIVIDENDS: 4
21+
DIVIDENDS: 4,
22+
BURN: 5
2223
}
2324

2425
// App flow

CLI/commands/helpers/contract_abis.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ let stoInterfaceABI;
77
let cappedSTOABI;
88
let usdTieredSTOABI;
99
let generalTransferManagerABI;
10+
let generalPermissionManagerABI;
1011
let polyTokenABI;
1112
let cappedSTOFactoryABI;
1213
let usdTieredSTOFactoryABI;
1314
let erc20DividendCheckpointABI;
1415
let etherDividendCheckpointABI;
16+
let moduleInterfaceABI;
1517
let ownableABI;
1618
let moduleFactoryABI;
1719

@@ -25,16 +27,18 @@ try {
2527
cappedSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTO.json').toString()).abi;
2628
usdTieredSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTO.json').toString()).abi;
2729
generalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralTransferManager.json').toString()).abi;
30+
generalPermissionManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManager.json').toString()).abi;
2831
polyTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).abi;
2932
cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).abi;
3033
usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTOFactory.json').toString()).abi;
3134
erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpoint.json').toString()).abi;
3235
etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpoint.json').toString()).abi;
36+
moduleInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/IModule.json').toString()).abi;
3337
ownableABI = JSON.parse(require('fs').readFileSync('./build/contracts/Ownable.json').toString()).abi;
3438
moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi;
3539
} catch (err) {
3640
console.log('\x1b[31m%s\x1b[0m',"Couldn't find contracts' artifacts. Make sure you ran truffle compile first");
37-
return;
41+
throw err;
3842
}
3943

4044
module.exports = {
@@ -65,6 +69,9 @@ module.exports = {
6569
generalTransferManager: function () {
6670
return generalTransferManagerABI;
6771
},
72+
generalPermissionManager: function () {
73+
return generalPermissionManagerABI;
74+
},
6875
polyToken: function () {
6976
return polyTokenABI;
7077
},
@@ -80,6 +87,9 @@ module.exports = {
8087
etherDividendCheckpoint: function () {
8188
return etherDividendCheckpointABI;
8289
},
90+
moduleInterface: function () {
91+
return moduleInterfaceABI;
92+
},
8393
ownable: function () {
8494
return ownableABI;
8595
},

CLI/commands/permission_manager.js

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
var readlineSync = require('readline-sync');
2+
var chalk = require('chalk');
3+
var common = require('./common/common_functions');
4+
var global = require('./common/global');
5+
var contracts = require('./helpers/contract_addresses');
6+
var abis = require('./helpers/contract_abis');
7+
8+
// App flow
9+
let tokenSymbol;
10+
let securityTokenRegistry;
11+
let securityToken;
12+
let generalPermissionManager;
13+
14+
const MODULES_TYPES = {
15+
PERMISSION: 1,
16+
TRANSFER: 2,
17+
STO: 3,
18+
DIVIDEND: 4,
19+
BURN: 5
20+
}
21+
22+
async function executeApp(remoteNetwork) {
23+
await global.initialize(remoteNetwork);
24+
25+
common.logAsciiBull();
26+
console.log("***********************************************");
27+
console.log("Welcome to the Command-Line Permission Manager.");
28+
console.log("***********************************************");
29+
console.log("Issuer Account: " + Issuer.address + "\n");
30+
31+
await setup();
32+
try {
33+
await selectST();
34+
await addPermissionModule();
35+
await changePermissionStep();
36+
} catch (err) {
37+
console.log(err);
38+
return;
39+
}
40+
};
41+
42+
async function setup(){
43+
try {
44+
let securityTokenRegistryAddress = await contracts.securityTokenRegistry();
45+
let securityTokenRegistryABI = abis.securityTokenRegistry();
46+
securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress);
47+
securityTokenRegistry.setProvider(web3.currentProvider);
48+
} catch (err) {
49+
console.log(err)
50+
console.log('\x1b[31m%s\x1b[0m',"There was a problem getting the contracts. Make sure they are deployed to the selected network.");
51+
process.exit(0);
52+
}
53+
}
54+
55+
async function selectST() {
56+
if (!tokenSymbol)
57+
tokenSymbol = readlineSync.question('Enter the token symbol: ');
58+
59+
let result = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call();
60+
if (result == "0x0000000000000000000000000000000000000000") {
61+
tokenSymbol = undefined;
62+
console.log(chalk.red(`Token symbol provided is not a registered Security Token.`));
63+
await selectST();
64+
} else {
65+
let securityTokenABI = abis.securityToken();
66+
securityToken = new web3.eth.Contract(securityTokenABI,result);
67+
}
68+
}
69+
70+
async function addPermissionModule() {
71+
let generalPermissionManagerAddress;
72+
let result = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralPermissionManager')).call();
73+
if (result.length == 0) {
74+
console.log(chalk.red(`General Permission Manager is not attached.`));
75+
if (readlineSync.keyInYNStrict('Do you want to add General Permission Manager Module to your Security Token?')) {
76+
let permissionManagerFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.PERMISSION, 'GeneralPermissionManager');
77+
let addModuleAction = securityToken.methods.addModule(permissionManagerFactoryAddress, web3.utils.fromAscii('', 16), 0, 0);
78+
let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice);
79+
let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded');
80+
console.log(`Module deployed at address: ${event._module}`);
81+
generalPermissionManagerAddress = event._module;
82+
} else {
83+
process.exit(0);
84+
}
85+
} else {
86+
generalPermissionManagerAddress = result[0];
87+
}
88+
89+
let generalPermissionManagerABI = abis.generalPermissionManager();
90+
generalPermissionManager = new web3.eth.Contract(generalPermissionManagerABI, generalPermissionManagerAddress);
91+
generalPermissionManager.setProvider(web3.currentProvider);
92+
}
93+
94+
async function changePermissionStep() {
95+
console.log('\n\x1b[34m%s\x1b[0m',"Permission Manager - Change Permission");
96+
let selectedDelegate = await selectDelegate();
97+
let selectedModule = await selectModule();
98+
let selectedPermission = await selectPermission(selectedModule.permissions);
99+
let isValid = isPermissionValid();
100+
await changePermission(selectedDelegate, selectedModule.address, selectedPermission, isValid);
101+
}
102+
103+
// Helper functions
104+
async function selectDelegate() {
105+
let result;
106+
let delegates = await getDelegates();
107+
108+
let options = ['Add new delegate'];
109+
options = options.concat(delegates.map(function(d) {
110+
return `Account: ${d.address}
111+
Details: ${d.details}`
112+
}));
113+
114+
let index = readlineSync.keyInSelect(options, 'Select a delegate:', {cancel: false});
115+
if (index == 0) {
116+
let newDelegate = await addNewDelegate();
117+
result = newDelegate;
118+
} else {
119+
result = delegates[index - 1].address;
120+
}
121+
122+
return result;
123+
}
124+
125+
async function selectModule() {
126+
let modules = await getModulesWithPermissions();
127+
let options = modules.map(function(m) {
128+
return m.name;
129+
});
130+
let index = readlineSync.keyInSelect(options, 'Select a module:', {cancel: false});
131+
return modules[index];
132+
}
133+
134+
async function selectPermission(permissions) {
135+
let options = permissions.map(function(p) {
136+
return p
137+
});
138+
let index = readlineSync.keyInSelect(options, 'Select a permission:', {cancel: false});
139+
return permissions[index];
140+
}
141+
142+
function isPermissionValid() {
143+
let options = ['Grant permission', 'Revoke permission'];
144+
let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false});
145+
return index == 0;
146+
}
147+
148+
async function changePermission(delegate, moduleAddress, permission, isValid) {
149+
let changePermissionAction = generalPermissionManager.methods.changePermission(delegate, moduleAddress, web3.utils.asciiToHex(permission), isValid);
150+
let receipt = await common.sendTransaction(Issuer, changePermissionAction, defaultGasPrice, 0, 1.5);
151+
common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'ChangePermission');
152+
console.log(`Permission changed succesfully,`);
153+
}
154+
155+
async function getDelegates() {
156+
let result = [];
157+
/*
158+
let events = await generalPermissionManager.getPastEvents('LogAddPermission', { fromBlock: 0});
159+
for (let event of events) {
160+
let delegate = {};
161+
delegate.address = event.returnValues._delegate;
162+
delegate.details = web3.utils.hexToAscii(event.returnValues._details);
163+
result.push(delegate);
164+
}
165+
*/
166+
let delegates = await generalPermissionManager.methods.getAllDelegates().call();
167+
for (let d of delegates) {
168+
let delegate = {};
169+
delegate.address = d;
170+
delegate.details = web3.utils.hexToAscii(await generalPermissionManager.methods.delegateDetails(d).call());
171+
result.push(delegate);
172+
}
173+
return result;
174+
}
175+
176+
async function addNewDelegate() {
177+
let newDelegate = readlineSync.question('Enter the delegate address: ', {
178+
limit: function (input) {
179+
return web3.utils.isAddress(input);
180+
},
181+
limitMessage: "Must be a valid address"
182+
});
183+
let details = readlineSync.question('Enter the delegate details (i.e `Belongs to financial firm`): ', {
184+
limit: function(input) {
185+
return input.length > 0;
186+
},
187+
limitMessage: "Must be a valid string"
188+
});
189+
let addPermissionAction = generalPermissionManager.methods.addDelegate(newDelegate, web3.utils.asciiToHex(details));
190+
let receipt = await common.sendTransaction(Issuer, addPermissionAction, defaultGasPrice);
191+
let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'AddDelegate');
192+
console.log(`Delegate added succesfully: ${event._delegate} - ${event._details}`);
193+
return event._delegate;
194+
}
195+
196+
async function getModulesWithPermissions() {
197+
let modules = [];
198+
let moduleABI = abis.moduleInterface();
199+
200+
for (const type in MODULES_TYPES) {
201+
let modulesAttached = await securityToken.methods.getModulesByType(MODULES_TYPES[type]).call();
202+
for (const m of modulesAttached) {
203+
let contractTemp = new web3.eth.Contract(moduleABI, m);
204+
let permissions = await contractTemp.methods.getPermissions().call();
205+
if (permissions.length > 0) {
206+
modules.push({
207+
name: web3.utils.hexToAscii((await securityToken.methods.getModule(m).call())[0]),
208+
address: m,
209+
permissions: permissions.map(function (p) { return web3.utils.hexToAscii(p) })
210+
})
211+
}
212+
}
213+
}
214+
215+
return modules;
216+
}
217+
218+
module.exports = {
219+
executeApp: async function(remoteNetwork) {
220+
return executeApp(remoteNetwork);
221+
}
222+
}

CLI/polymath-cli.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var dividends_manager = require('./commands/dividends_manager');
1111
var transfer_manager = require('./commands/transfer_manager');
1212
var contract_manager = require('./commands/contract_manager');
1313
var strMigrator = require('./commands/strMigrator');
14+
var permission_manager = require('./commands/permission_manager');
1415
var program = require('commander');
1516
const yaml = require('js-yaml');
1617
const fs = require('fs');
@@ -142,6 +143,14 @@ program
142143
await strMigrator.executeApp(toStrAddress, fromTrAddress, fromStrAddress, program.remoteNode);
143144
});
144145

146+
program
147+
.command('permission_manager')
148+
.alias('pm')
149+
.description('Runs permission_manager')
150+
.action(async function() {
151+
await permission_manager.executeApp(program.remoteNode);
152+
});
153+
145154
program.parse(process.argv);
146155

147156
if (typeof program.commands.length == 0) {

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ You can access Ethereum via the Infura load-balanced nodes. You have to save you
7878
node CLI/polymath-cli faucet --remote-node kovan
7979
```
8080
3. Connected to a local private test network using `ganache-cli`.
81-
You have to save the private key for the first account generated by ganache into `./privKeyLocal`.
81+
You have to save the private key for the one of the accounts generated by ganache into `./privKeyLocal`.
8282

8383

8484
## Poly Faucet

0 commit comments

Comments
 (0)