diff --git a/CLI/commands/ST20Generator.js b/CLI/commands/ST20Generator.js index b372e3f81..714f87664 100644 --- a/CLI/commands/ST20Generator.js +++ b/CLI/commands/ST20Generator.js @@ -704,11 +704,11 @@ function limitsConfigUSDTieredSTO() { })); } - let nonAccreditedLimit = 10000; + let nonAccreditedLimit = 2500; if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('nonAccreditedLimitUSD')) { limits.nonAccreditedLimitUSD = web3.utils.toWei(_stoConfig.nonAccreditedLimitUSD.toString()); } else { - limits.nonAccreditedLimitUSD = web3.utils.toWei(readlineSync.question(`What is the limit for non accredited insvestors in USD? (${nonAccreditedLimit}): `, { + limits.nonAccreditedLimitUSD = web3.utils.toWei(readlineSync.question(`What is the default limit for non accredited insvestors in USD? (${nonAccreditedLimit}): `, { limit: function(input) { return new BigNumber(web3.utils.toWei(input)).gte(limits.minimumInvestmentUSD); }, @@ -998,7 +998,9 @@ async function usdTieredSTO_configure() { console.log(chalk.red(`STO is finalized`)); } else { let options = []; - options.push('Finalize STO', 'Change accredited account', 'Change accredited in batch'); + options.push('Finalize STO', + 'Change accredited account', 'Change accredited in batch', + 'Change non accredited limit for an account', 'Change non accredited limits in batch'); // If STO is not started, you can modify configuration let now = Math.floor(Date.now() / 1000); @@ -1036,22 +1038,34 @@ async function usdTieredSTO_configure() { shell.exec(`${__dirname}/scripts/script.sh Accredit ${tokenSymbol} 75`); break; case 3: + let account = readlineSync.question('Enter the address to change non accredited limit: '); + let limit = readlineSync.question(`Enter the limit in USD: `); + let accounts = [account]; + let limits = [web3.utils.toWei(limit)]; + let changeNonAccreditedLimitAction = currentSTO.methods.changeNonAccreditedLimit(accounts, limits); + // 2 GAS? + await common.sendTransaction(Issuer, changeNonAccreditedLimitAction, defaultGasPrice); + break; + case 4: + shell.exec(`${__dirname}/scripts/script.sh NonAccreditedLimit ${tokenSymbol} 75`); + break; + case 5: await modfifyTimes(); await usdTieredSTO_status(); break; - case 4: + case 6: await modfifyTiers(); await usdTieredSTO_status(); break; - case 5: + case 7: await modfifyAddresses(); await usdTieredSTO_status(); break; - case 6: + case 8: await modfifyLimits(); await usdTieredSTO_status(); break; - case 7: + case 9: await modfifyFunding(); await usdTieredSTO_status(); break; diff --git a/CLI/commands/changeNonAccreditedLimit.js b/CLI/commands/changeNonAccreditedLimit.js new file mode 100644 index 000000000..63d91d91f --- /dev/null +++ b/CLI/commands/changeNonAccreditedLimit.js @@ -0,0 +1,158 @@ +var fs = require('fs'); +var csv = require('fast-csv'); +var BigNumber = require('bignumber.js'); +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') + +/////////////////////////////ARTIFACTS////////////////////////////////////////// +let tickerRegistry; +let securityTokenRegistry; +let securityToken; +let usdTieredSTO; + +////////////////////////////USER INPUTS////////////////////////////////////////// +let tokenSymbol = process.argv.slice(2)[0]; //token symbol +let BATCH_SIZE = process.argv.slice(2)[1]; //batch size +if (!BATCH_SIZE) BATCH_SIZE = 75; +let remoteNetwork = process.argv.slice(2)[2]; + +/////////////////////////GLOBAL VARS////////////////////////////////////////// +//distribData is an array of batches. i.e. if there are 200 entries, with batch sizes of 75, we get [[75],[75],[50]] +let distribData = new Array(); +//allocData is a temporary array that stores up to the batch size, +//then gets push into distribData, then gets set to 0 to start another batch +let allocData = new Array(); +//full file data is a single array that contains all arrays. i.e. if there are 200 entries we get [[200]] +let fullFileData = new Array(); +//baa data is an array that contains invalid entries +let badData = new Array(); + +//////////////////////////////////////////ENTRY INTO SCRIPT////////////////////////////////////////// +startScript(); + +async function startScript() { + await global.initialize(remoteNetwork); + 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); + console.log("Processing investor CSV upload. Batch size is " + BATCH_SIZE + " accounts per transaction"); + readFile(); + } 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."); + return; + } +} + +///////////////////////////FUNCTION READING THE CSV FILE +function readFile() { + var stream = fs.createReadStream("./CLI/data/nonAccreditedLimits_data.csv"); + + let index = 0; + console.log(` + -------------------------------------------- + ----------- Parsing the csv file ----------- + -------------------------------------------- + `); + + var csvStream = csv() + .on("data", function (data) { + let isAddress = web3.utils.isAddress(data[0]); + let isNumber = !isNaN(data[1]); + + if (isAddress && isNumber) { + let userArray = new Array(); + let checksummedAddress = web3.utils.toChecksumAddress(data[0]); + + userArray.push(checksummedAddress); + userArray.push(data[1]); + + allocData.push(userArray); + fullFileData.push(userArray); + + index++; + if (index >= BATCH_SIZE) { + distribData.push(allocData); + allocData = []; + index = 0; + } + } else { + let userArray = new Array() + userArray.push(data[0]); + userArray.push(data[1]); + + badData.push(userArray); + fullFileData.push(userArray) + } + }) + .on("end", function () { + //Add last remainder batch + distribData.push(allocData); + allocData = []; + + changeNonAccreditedLimit(); + }); + + stream.pipe(csvStream); +} + +// MAIN FUNCTION COMMUNICATING TO BLOCKCHAIN +async function changeNonAccreditedLimit() { + // Let's check if token has already been deployed, if it has, skip to STO + let tokenDeployedAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (tokenDeployedAddress != "0x0000000000000000000000000000000000000000") { + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI, tokenDeployedAddress); + let result = await securityToken.methods.getModule(3, 0).call(); + if (result[1] != "0x0000000000000000000000000000000000000000") { + let stoName = web3.utils.toAscii(result[0]).replace(/\u0000/g, ''); + if (stoName == 'USDTieredSTO') { + let usdTieredSTOABI = abis.usdTieredSTO(); + usdTieredSTO = new web3.eth.Contract(usdTieredSTOABI, result[1]); + console.log(` +-------------------------------------------------------------- +----- Sending non accredited limit changes to blockchain ----- +-------------------------------------------------------------- + `); + //this for loop will do the batches, so it should run 75, 75, 50 with 200 + for (let i = 0; i < distribData.length; i++) { + try { + let investorArray = []; + let limitArray = []; + + //splitting the user arrays to be organized by input + for (let j = 0; j < distribData[i].length; j++) { + investorArray.push(distribData[i][j][0]); + limitArray.push(web3.utils.toWei(distribData[i][j][1].toString())); + } + + let changeNonAccreditedLimitAction = usdTieredSTO.methods.changeNonAccreditedLimit(investorArray, limitArray); + let r = await common.sendTransaction(Issuer, changeNonAccreditedLimitAction, defaultGasPrice); + console.log(`Batch ${i} - Attempting to change non accredited limits to accounts:\n\n`, investorArray, "\n\n"); + console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------"); + console.log("Change accredited transaction was successful.", r.gasUsed, "gas used. Spent:", web3.utils.fromWei(BigNumber(r.gasUsed * defaultGasPrice).toString()), "Ether"); + console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n\n"); + } catch (err) { + console.log("ERROR:", err); + } + } + } else { + console.log(chalk.red(`The STO attached is ${stoName} and this module only works for USDTieredSTO.`)); + } + } else { + console.log(chalk.red(`There is no STO module attached to the ${tokenSymbol.toUpperCase()} Token. No further actions can be taken.`)); + } + } else { + console.log(chalk.red(`Token symbol provided is not a registered Security Token.`)); + } +} diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 54c837b9b..8910f0d7e 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -296,7 +296,8 @@ async function showUserInfoForUSDTieredSTO() console.log(` - Accredited: ${(displayIsUserAccredited)?"YES":"NO"}`) if (!await currentSTO.methods.accredited(User.address).call()) { - let displayNonAccreditedLimitUSD = web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSD().call()); + let displayOverrideNonAccreditedLimitUSD = web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSDOverride(User.address).call()) + let displayNonAccreditedLimitUSD = displayOverrideNonAccreditedLimitUSD != 0 ? displayOverrideNonAccreditedLimitUSD : web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSD().call()); let displayTokensRemainingAllocation = displayNonAccreditedLimitUSD - displayInvestorInvestedUSD; console.log(` - Remaining allocation: ${(displayTokensRemainingAllocation > 0 ? displayTokensRemainingAllocation : 0)} USD`); } @@ -430,7 +431,7 @@ async function showUSDTieredSTOInfo() { - Tiers: ${tiersLength}` + displayTiers + ` - Minimum Investment: ${displayMinimumInvestmentUSD} USD - - Non Accredited Limit: ${displayNonAccreditedLimitUSD} USD + - Default NonAccredited Limit: ${displayNonAccreditedLimitUSD} USD ----------------------------------------------------------------------- - ${timeTitle} ${timeRemaining} - Tokens Sold: ${displayTokensSold} ${displayTokenSymbol}` diff --git a/CLI/commands/scripts/script.sh b/CLI/commands/scripts/script.sh index 26740fe01..1a88207fe 100755 --- a/CLI/commands/scripts/script.sh +++ b/CLI/commands/scripts/script.sh @@ -12,4 +12,8 @@ elif [ $1 = "Accredit" ]; then echo "Running the $1 script"; node $PWD/CLI/commands/accredit.js $2 $3 +elif [ $1 = "NonAccreditedLimit" ]; +then +echo "Running the $1 script"; +node $PWD/CLI/commands/changeNonAccreditedLimit.js $2 $3 fi diff --git a/CLI/data/nonAccreditedLimits_data.csv b/CLI/data/nonAccreditedLimits_data.csv new file mode 100644 index 000000000..824e92063 --- /dev/null +++ b/CLI/data/nonAccreditedLimits_data.csv @@ -0,0 +1,10 @@ +0xf26bb01507a076c83e26d3c8d0f2eab7b77f96f2,3000 +0xb42de462906eb0f6a10c7bdf5b100c5da9dc376c,5000 +0x3270d07c6552d6334e485c4e8af41ce9e0c34cfd,1000 +0x92fdab26ac0c7ae2a40d45b09c9f74846697c40a,2 +0xf1819ecb5e81a8f8c0f1d325c8de017caf4fd4fb,1000.500 +0xa26cc0567e29fda3c77369791e926b4c46456fcb,1 +0x97c09f763a699a51cb947f95e602c08f7bcba202,10000 +0x308a3ea530e5b160f1f7115f2ddb30cb44d8b54d,10000 +0xbc1bfb5d90692854672febe76ca5379dea1015e4,10000 +0x7442dcd2eb074ea6dbfaa8338300bc86238c2aae,10000 diff --git a/CLI/polymath-cli.js b/CLI/polymath-cli.js index 15d47f132..fc70417ce 100644 --- a/CLI/polymath-cli.js +++ b/CLI/polymath-cli.js @@ -108,6 +108,14 @@ program shell.exec(`${__dirname}/commands/scripts/script.sh Accredit ${tokenSymbol} ${batchSize} ${program.remoteNode}`);; }); +program + .command('nonAccreditedLimit [batchSize]') + .alias('nal') + .description('Runs changeNonAccreditedLimit') + .action(async function(tokenSymbol, batchSize) { + shell.exec(`${__dirname}/commands/scripts/script.sh NonAccreditedLimit ${tokenSymbol} ${batchSize} ${program.remoteNode}`);; + }); + program .command('strMigrator [fromStrAddress] [toStrAddress]') .alias('str')