From 78bf819f4f9521f1dcf19b20745deabb12f7edc1 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Mon, 3 Dec 2018 16:42:52 -0300 Subject: [PATCH 1/5] adding and listing stable coins in sto manager --- CLI/commands/common/common_functions.js | 11 +- CLI/commands/common/constants.js | 2 +- CLI/commands/helpers/contract_abis.js | 5 + CLI/commands/sto_manager.js | 133 ++++++++++++++++++++---- 4 files changed, 123 insertions(+), 28 deletions(-) diff --git a/CLI/commands/common/common_functions.js b/CLI/commands/common/common_functions.js index 0d21d06f3..e9d4b61a1 100644 --- a/CLI/commands/common/common_functions.js +++ b/CLI/commands/common/common_functions.js @@ -3,7 +3,7 @@ const Tx = require('ethereumjs-tx'); const permissionsList = require('./permissions_list'); const abis = require('../helpers/contract_abis'); -async function connect(abi, address) { +function connect(abi, address) { contractRegistry = new web3.eth.Contract(abi, address); contractRegistry.setProvider(web3.currentProvider); return contractRegistry @@ -15,7 +15,7 @@ async function checkPermission(contractName, functionName, contractRegistry) { return true } else { let stAddress = await contractRegistry.methods.securityToken().call(); - let securityToken = await connect(abis.securityToken(), stAddress); + let securityToken = connect(abis.securityToken(), stAddress); let stOwner = await securityToken.methods.owner().call(); if (stOwner == Issuer.address) { return true @@ -47,11 +47,11 @@ async function getGasLimit(options, action) { } async function checkPermissions(action) { - let contractRegistry = await connect(action._parent.options.jsonInterface, action._parent._address); + let contractRegistry = connect(action._parent.options.jsonInterface, action._parent._address); //NOTE this is a condition to verify if the transaction comes from a module or not. if (contractRegistry.methods.hasOwnProperty('factory')) { let moduleAddress = await contractRegistry.methods.factory().call(); - let moduleRegistry = await connect(abis.moduleFactory(), moduleAddress); + let moduleRegistry = connect(abis.moduleFactory(), moduleAddress); let parentModule = await moduleRegistry.methods.getName().call(); let result = await checkPermission(web3.utils.hexToUtf8(parentModule), action._method.name, contractRegistry); if (!result) { @@ -145,5 +145,8 @@ module.exports = { let eventJsonInterface = jsonInterface.find(o => o.name === eventName && o.type === 'event'); let filteredLogs = logs.filter(l => l.topics.includes(eventJsonInterface.signature)); return filteredLogs.map(l => web3.eth.abi.decodeLog(eventJsonInterface.inputs, l.data, l.topics.slice(1))); + }, + connect: function (abi, address) { + return connect(abi, address) } }; diff --git a/CLI/commands/common/constants.js b/CLI/commands/common/constants.js index c3e2b796c..2f10d6930 100644 --- a/CLI/commands/common/constants.js +++ b/CLI/commands/common/constants.js @@ -29,6 +29,6 @@ module.exports = Object.freeze({ FUND_RAISE_TYPES: { ETH: 0, POLY: 1, - DAI: 2 + STABLE: 2 } }); \ No newline at end of file diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index f93f18ac7..aa98aa5f1 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -19,6 +19,7 @@ let ownableABI; let iSTOABI; let iTransferManagerABI; let moduleFactoryABI; +let erc20; try { polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; @@ -42,6 +43,7 @@ try { iSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi iTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ITransferManager.json').toString()).abi moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi; + erc20ABI = JSON.parse(require('fs').readFileSync('./build/contracts/DetailedERC20.json').toString()).abi; } catch (err) { console.log('\x1b[31m%s\x1b[0m', "Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); throw err; @@ -110,5 +112,8 @@ module.exports = { }, moduleFactory: function () { return moduleFactoryABI; + }, + erc20: function () { + return erc20ABI; } } \ No newline at end of file diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index d0fd1c3ea..18e342e57 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -6,6 +6,7 @@ const contracts = require('./helpers/contract_addresses'); const abis = require('./helpers/contract_abis'); const common = require('./common/common_functions'); const gbl = require('./common/global'); +const STABLE = 'STABLE'; /////////////////// // Crowdsale params @@ -270,7 +271,7 @@ async function cappedSTO_status(currentSTO) { function fundingConfigUSDTieredSTO() { let funding = {}; - let selectedFunding = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise,' + chalk.green(` D `) + 'for DAI raise,' + chalk.green(` E `) + 'for Ether raise or any combination of them (i.e.' + chalk.green(` PED `) + 'for all): ').toUpperCase(); + let selectedFunding = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise,' + chalk.green(` S `) + 'for Stable Coin raise,' + chalk.green(` E `) + 'for Ether raise or any combination of them (i.e.' + chalk.green(` PSE `) + 'for all): ').toUpperCase(); funding.raiseType = []; if (selectedFunding.includes('E')) { @@ -279,17 +280,17 @@ function fundingConfigUSDTieredSTO() { if (selectedFunding.includes('P')) { funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.POLY); } - if (selectedFunding.includes('D')) { - funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.DAI); + if (selectedFunding.includes('S')) { + funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.STABLE); } if (funding.raiseType.length == 0) { - funding.raiseType = [gbl.constants.FUND_RAISE_TYPES.ETH, gbl.constants.FUND_RAISE_TYPES.POLY, gbl.constants.FUND_RAISE_TYPES.DAI]; + funding.raiseType = [gbl.constants.FUND_RAISE_TYPES.ETH, gbl.constants.FUND_RAISE_TYPES.POLY, gbl.constants.FUND_RAISE_TYPES.STABLE]; } return funding; } -function addressesConfigUSDTieredSTO(usdTokenRaise) { +async function addressesConfigUSDTieredSTO(usdTokenRaise) { let addresses = {}; addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { @@ -310,22 +311,74 @@ function addressesConfigUSDTieredSTO(usdTokenRaise) { }); if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + let listOfAddress; + if (usdTokenRaise) { - addresses.usdToken = readlineSync.question('Enter the address of the USD Token or stable coin (' + usdToken.options.address + '): ', { + addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { limit: function (input) { - return web3.utils.isAddress(input); + listOfAddress = input.split(','); + let response = true + listOfAddress.forEach((addr) => { + if (!web3.utils.isAddress(addr)) { + response = false + } + }) + return response }, limitMessage: "Must be a valid address", defaultInput: usdToken.options.address }); - if (addresses.usdToken == "") addresses.usdToken = usdToken.options.address; + if (addresses.usdToken == "") { + listOfAddress = [usdToken.options.address] + addresses.usdToken = [usdToken.options.address]; + } } else { - addresses.usdToken = '0x0000000000000000000000000000000000000000'; + listOfAddress = ['0x0000000000000000000000000000000000000000'] + addresses.usdToken = ['0x0000000000000000000000000000000000000000']; + } + + if (!await processArray(listOfAddress)) { + console.log(`Please, verify your stable coins addresses to continue with this process.`) + process.exit(0) + } + + if (typeof addresses.usdToken === 'string') { + addresses.usdToken = addresses.usdToken.split(",") } return addresses; } +async function checkSymbol(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.symbol().call(); + } catch (e) { + return "" + } +} + +async function processArray(array) { + let result = true; + for (const address of array) { + let symbol = await checkSymbol(address); + if (symbol == "") { + result = false; + console.log(`${address} seems not to be a stable coin`) + } + } + return result +} + +async function processAddress(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + list.push(symbol) + } + return list +} + function tiersConfigUSDTieredSTO(polyRaise) { let tiers = {}; @@ -474,7 +527,7 @@ async function usdTieredSTO_launch(stoConfig) { let useConfigFile = typeof stoConfig !== 'undefined'; let funding = useConfigFile ? stoConfig.funding : fundingConfigUSDTieredSTO(); - let addresses = useConfigFile ? stoConfig.addresses : addressesConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.DAI)); + let addresses = useConfigFile ? stoConfig.addresses : await addressesConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.STABLE)); let tiers = useConfigFile ? stoConfig.tiers : tiersConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.POLY)); let limits = useConfigFile ? stoConfig.limits : limitsConfigUSDTieredSTO(); let times = timesConfigUSDTieredSTO(stoConfig); @@ -519,12 +572,17 @@ async function usdTieredSTO_status(currentSTO) { let displayInvestorCount = await currentSTO.methods.investorCount().call(); let displayIsFinalized = await currentSTO.methods.isFinalized().call() ? "YES" : "NO"; let displayTokenSymbol = await securityToken.methods.symbol().call(); - - let tiersLength = await currentSTO.methods.getNumberOfTiers().call();; - + let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; let raiseTypes = []; + let stableSymbols = []; + for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } raiseTypes.push(fundType); } } @@ -554,8 +612,13 @@ async function usdTieredSTO_status(currentSTO) { } let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; - displayMintedPerTierPerType += ` - Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` + Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } } displayTiers += ` @@ -579,28 +642,52 @@ async function usdTieredSTO_status(currentSTO) { let balance = await getBalance(displayWallet, gbl.constants.FUND_RAISE_TYPES[type]); let walletBalance = web3.utils.fromWei(balance); let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); - displayWalletBalancePerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayWalletBalancePerType += ` + Balance ${stableSymbols.toString()}:\t ${walletBalanceUSD} USD`; + } else { + displayWalletBalancePerType += ` Balance ${type}:\t\t ${walletBalance} ${type} (${walletBalanceUSD} USD)`; - + } + balance = await getBalance(displayReserveWallet, gbl.constants.FUND_RAISE_TYPES[type]); let reserveWalletBalance = web3.utils.fromWei(balance); let reserveWalletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); - displayReserveWalletBalancePerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayReserveWalletBalancePerType += ` + Balance ${stableSymbols.toString()}:\t ${reserveWalletBalanceUSD} USD`; + } else { + displayReserveWalletBalancePerType += ` Balance ${type}:\t\t ${reserveWalletBalance} ${type} (${reserveWalletBalanceUSD} USD)`; + } let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayFundsRaisedPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayFundsRaisedPerType += ` + ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + } else { + displayFundsRaisedPerType += ` ${type}:\t\t\t ${fundsRaised} ${type}`; + } //Only show sold for if more than one raise type are allowed if (raiseTypes.length > 1) { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` - Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } else { + displayTokensSoldPerType += ` + Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } } } let displayRaiseType = raiseTypes.join(' - '); + //If STO has stable coins, we list them one by one + if (stableSymbols.length) { + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + } let now = Math.floor(Date.now() / 1000); let timeTitle; @@ -747,7 +834,7 @@ async function modfifyFunding(currentSTO) { } async function modfifyAddresses(currentSTO) { - let addresses = addressesConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.DAI).call()); + let addresses = await addressesConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()); let modifyAddressesAction = currentSTO.methods.modifyAddresses(addresses.wallet, addresses.reserveWallet, addresses.usdToken); await common.sendTransaction(modifyAddressesAction); } @@ -772,7 +859,7 @@ async function getBalance(from, type) { return await web3.eth.getBalance(from); case gbl.constants.FUND_RAISE_TYPES.POLY: return await polyToken.methods.balanceOf(from).call(); - case gbl.constants.FUND_RAISE_TYPES.DAI: + case gbl.constants.FUND_RAISE_TYPES.STABLE: return await usdToken.methods.balanceOf(from).call(); } } From b040af43908ca3bd6e739a8f46eb799d2a58dc23 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Tue, 4 Dec 2018 08:47:42 -0300 Subject: [PATCH 2/5] investor portal improvements --- CLI/commands/investor_portal.js | 72 ++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 1ced21ce0..7502d7d8e 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -10,6 +10,7 @@ var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); const STO_KEY = 3; +const STABLE = 'STABLE'; let securityTokenRegistry; let securityToken; @@ -157,13 +158,13 @@ async function showUserInfo(_user) { console.log(` ******************* User Information ******************** - Address: ${_user}`); - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.POLY)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.POLY).call()) { console.log(` - POLY balance:\t ${await polyBalance(_user)}`); } - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.ETH)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.ETH).call()) { console.log(` - ETH balance:\t ${web3.utils.fromWei(await web3.eth.getBalance(_user))}`); } - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.DAI)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()) { console.log(` - DAI balance:\t ${await usdBalance(_user)}`); } } @@ -237,12 +238,41 @@ async function showCappedSTOInfo() { } } +async function processAddress(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + list.push(symbol) + } + return list +} + +async function checkSymbol(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.symbol().call(); + } catch (e) { + return "" + } +} + async function showUserInfoForUSDTieredSTO() { + let stableSymbols = []; + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } let displayInvestorInvested = web3.utils.fromWei(await currentSTO.methods.investorInvested(User.address, gbl.constants.FUND_RAISE_TYPES[fundType]).call()); - console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); + if ((fundType == STABLE) && (stableSymbols.length)) { + console.log(` - Invested in ${stableSymbols.toString()}:\t ${displayInvestorInvested} USD`); + } else { + console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); + } } } @@ -280,10 +310,16 @@ async function showUSDTieredSTOInfo() { let displayIsOpen = await currentSTO.methods.isOpen().call(); let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); + let stableSymbols = []; + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { raiseTypes.push(fundType); + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } } } @@ -313,8 +349,13 @@ async function showUSDTieredSTOInfo() { let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; - displayMintedPerTierPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } } displayTiers += ` @@ -334,18 +375,31 @@ async function showUSDTieredSTOInfo() { let displayTokensSoldPerType = ''; for (const type of raiseTypes) { let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayFundsRaisedPerType += ` - ${type}:\t\t\t ${fundsRaised} ${type}`; - + if ((type == STABLE) && (stableSymbols.length)) { + displayFundsRaisedPerType += ` + ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + } else { + displayFundsRaisedPerType += ` + ${type}:\t\t\t ${fundsRaised} ${type}`; + } //Only show sold per raise type is more than one are allowed if (raiseTypes.length > 1) { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } else { + displayTokensSoldPerType += ` Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } } } let displayRaiseType = raiseTypes.join(' - '); + //If STO has stable coins, we list them one by one + if (stableSymbols.length) { + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + } let now = Math.floor(Date.now()/1000); let timeTitle; From 3b983b5eeee2a1c59dec41c28bc22318c3f850db Mon Sep 17 00:00:00 2001 From: shuffledex Date: Thu, 6 Dec 2018 13:10:57 -0300 Subject: [PATCH 3/5] code advances --- CLI/commands/investor_portal.js | 101 ++++++++++++++++++++++++-------- CLI/commands/sto_manager.js | 3 +- package.json | 1 + 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 4b01705b3..54aba70c4 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -10,6 +10,8 @@ var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); const STO_KEY = 3; +const ETH = 'ETH'; +const POLY = 'POLY'; const STABLE = 'STABLE'; let securityTokenRegistry; @@ -17,7 +19,6 @@ let securityToken; let selectedSTO; let currentSTO; let polyToken; -let usdToken; let generalTransferManager; let raiseTypes = []; @@ -90,10 +91,6 @@ async function setup() { let polytokenABI = abis.polyToken(); polyToken = new web3.eth.Contract(polytokenABI, polytokenAddress); polyToken.setProvider(web3.currentProvider); - - let usdTokenAddress = await contracts.usdToken(); - usdToken = new web3.eth.Contract(polytokenABI, usdTokenAddress); - usdToken.setProvider(web3.currentProvider); } catch (err) { console.log(err); console.log(chalk.red(`There was a problem getting the contracts. Make sure they are deployed to the selected network.`)); @@ -155,6 +152,8 @@ async function showTokenInfo() { // Show info async function showUserInfo(_user) { + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + console.log(` ******************* User Information ******************** - Address: ${_user}`); @@ -165,7 +164,10 @@ async function showUserInfo(_user) { console.log(` - ETH balance:\t ${web3.utils.fromWei(await web3.eth.getBalance(_user))}`); } if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()) { - console.log(` - DAI balance:\t ${await usdBalance(_user)}`); + let stableSymbolsAndBalance = await processAddressWithBalance(listOfStableCoins); + stableSymbolsAndBalance.forEach(stable => { + console.log(` - ${stable.symbol} balance:\t ${web3.utils.fromWei(stable.balance)}`); + }); } } @@ -238,6 +240,16 @@ async function showCappedSTOInfo() { } } +async function processAddressWithBalance(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + let balance = await checkBalance(address); + list.push({'address': address, 'symbol': symbol, 'balance': balance}) + } + return list +} + async function processAddress(array) { let list = []; for (const address of array) { @@ -256,11 +268,19 @@ async function checkSymbol(address) { } } +async function checkBalance(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.balanceOf(User.address).call(); + } catch (e) { + return "" + } +} + async function showUserInfoForUSDTieredSTO() { let stableSymbols = []; - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { @@ -311,8 +331,7 @@ async function showUSDTieredSTOInfo() { let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); let stableSymbols = []; - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { @@ -496,7 +515,11 @@ async function investCappedSTO(currency, amount) { // Allow investor to buy tokens. async function investUsdTieredSTO(currency, amount) { + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + let stableSymbols = await processAddress(listOfStableCoins); + let raiseType; + if (typeof currency !== 'undefined') { if (!raiseTypes.inlcudes(currency)) { console.log(chalk.red(`${currency} is not allowed for current STO`)); @@ -507,9 +530,20 @@ async function investUsdTieredSTO(currency, amount) { } else { for (const type of raiseTypes) { let displayPrice = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], web3.utils.toWei("1")).call()); - console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + if ((type == STABLE) && (stableSymbols.length)) { + console.log(chalk.green(` Current ${stableSymbols.toString()} price:\t\t ${displayPrice} USD`)); + } else { + console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + } } if (raiseTypes.length > 1) { + const stableIndex = raiseTypes.indexOf(STABLE); + if (stableIndex > -1) { + raiseTypes.splice(stableIndex, 1) + stableSymbols.forEach((symbol) => { + raiseTypes.push(symbol) + }) + } let index = readlineSync.keyInSelect(raiseTypes, 'Choose one of the allowed raise types: ', { cancel: false }); raiseType = raiseTypes[index]; } else { @@ -522,7 +556,15 @@ async function investUsdTieredSTO(currency, amount) { if (typeof amount === 'undefined') { let investorInvestedUSD = web3.utils.fromWei(await currentSTO.methods.investorInvestedUSD(User.address).call()); let minimumInvestmentUSD = await currentSTO.methods.minimumInvestmentUSD().call(); - let minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[raiseType], minimumInvestmentUSD).call(); + let minimumInvestmentRaiseType; + + // if raiseType is different than ETH or POLY, we assume is STABLE + if ((raiseType != ETH) && (raiseType != POLY)) { + minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[STABLE], minimumInvestmentUSD).call(); + } else { + minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[raiseType], minimumInvestmentUSD).call(); + } + cost = readlineSync.question(chalk.yellow(`Enter the amount of ${raiseType} you would like to invest or press 'Enter' to exit: `), { limit: function (input) { return investorInvestedUSD != 0 || parseInt(input) > parseInt(web3.utils.fromWei(minimumInvestmentRaiseType)); @@ -536,7 +578,14 @@ async function investUsdTieredSTO(currency, amount) { let costWei = web3.utils.toWei(cost.toString()); - let tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[raiseType]).call(); + let tokensToBuy; + // if raiseType is different than ETH or POLY, we assume is STABLE + if ((raiseType != ETH) && (raiseType != POLY)) { + tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[STABLE]).call(); + } else { + tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[raiseType]).call(); + } + let minTokenToBuy = tokensToBuy.tokensMinted; console.log(chalk.yellow(`You are going to spend ${web3.utils.fromWei(tokensToBuy.spentValue)} ${raiseType} (${web3.utils.fromWei(tokensToBuy.spentUSD)} USD) to buy ${web3.utils.fromWei(minTokenToBuy)} ${STSymbol} approx.`)); console.log(chalk.yellow(`Due to ${raiseType} price changes and network delays, it is possible that the final amount of purchased tokens is lower.`)); @@ -544,7 +593,7 @@ async function investUsdTieredSTO(currency, amount) { minTokenToBuy = 0; } - if (raiseType == 'POLY') { + if (raiseType == POLY) { let userBalance = await polyBalance(User.address); if (parseInt(userBalance) >= parseInt(cost)) { let allowance = await polyToken.methods.allowance(STOAddress, User.address).call(); @@ -560,19 +609,24 @@ async function investUsdTieredSTO(currency, amount) { console.log(chalk.red(`Please purchase a smaller amount of tokens or access the POLY faucet to get the POLY to complete this txn.`)); process.exit(); } - } else if (raiseType == 'DAI') { - let userBalance = await usdBalance(User.address); - if (parseInt(userBalance) >= parseInt(cost)) { - let allowance = await usdToken.methods.allowance(STOAddress, User.address).call(); + } else if ((raiseType != POLY) && (raiseType != ETH)) { + + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + let stableSymbolsAndBalance = await processAddressWithBalance(listOfStableCoins); + let stableInfo = stableSymbolsAndBalance.find(o => o.symbol === raiseType); + + if (parseInt(stableInfo.balance) >= parseInt(cost)) { + let stableCoin = common.connect(abis.erc20(), stableInfo.address); + let allowance = await stableCoin.methods.allowance(STOAddress, User.address).call(); if (allowance < costWei) { - let approveAction = usdToken.methods.approve(STOAddress, costWei); + let approveAction = stableCoin.methods.approve(STOAddress, costWei); await common.sendTransaction(approveAction, { from: User }); } - let actionBuyWithUSD = currentSTO.methods.buyWithUSDRateLimited(User.address, costWei, minTokenToBuy); + let actionBuyWithUSD = currentSTO.methods.buyWithUSDRateLimited(User.address, costWei, minTokenToBuy, stableInfo.address); let receipt = await common.sendTransaction(actionBuyWithUSD, { from: User, factor: 1.5 }); logTokensPurchasedUSDTieredSTO(receipt); } else { - console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} DAI but have ${userBalance} DAI.`)); + console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} ${stableInfo.symbol} but have ${stableInfo.balance} ${stableInfo.symbol}.`)); console.log(chalk.red(`Please purchase a smaller amount of tokens.`)); process.exit(); } @@ -591,11 +645,6 @@ async function polyBalance(_user) { return web3.utils.fromWei(balance); } -async function usdBalance(_user) { - let balance = await usdToken.methods.balanceOf(_user).call(); - return web3.utils.fromWei(balance); -} - function logTokensPurchasedUSDTieredSTO(receipt) { console.log(chalk.green(`Congratulations! The token purchase was successfully completed.`)); let events = common.getMultipleEventsFromLogs(currentSTO._jsonInterface, receipt.logs, 'TokenPurchase'); diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index 316bdceb7..d9e95f723 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -577,8 +577,7 @@ async function usdTieredSTO_status(currentSTO) { let displayIsFinalized = await currentSTO.methods.isFinalized().call() ? "YES" : "NO"; let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); let raiseTypes = []; let stableSymbols = []; diff --git a/package.json b/package.json index ef2d76c38..7341ea9da 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "bignumber.js": "^5.0.0", "chalk": "^2.4.1", "coveralls": "^3.0.1", + "csv-parse": "^4.0.1", "ethereumjs-testrpc": "^6.0.3", "ethers": "^3.0.15", "fs": "0.0.1-security", From e14a18e71a761d69c21e773c78907885f50843c5 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Thu, 6 Dec 2018 18:51:43 -0300 Subject: [PATCH 4/5] final code --- CLI/commands/investor_portal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 54aba70c4..bbebc003c 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -59,17 +59,18 @@ async function executeApp(investorAddress, investorPrivKey, symbol, currency, am try { await inputSymbol(symbol); - await showUserInfo(User.address); switch (selectedSTO) { case 'CappedSTO': let cappedSTOABI = abis.cappedSTO(); currentSTO = new web3.eth.Contract(cappedSTOABI, STOAddress); + await showUserInfo(User.address); await showCappedSTOInfo(); await investCappedSTO(currency, amount); break; case 'USDTieredSTO': let usdTieredSTOABI = abis.usdTieredSTO(); currentSTO = new web3.eth.Contract(usdTieredSTOABI, STOAddress); + await showUserInfo(User.address); await showUserInfoForUSDTieredSTO(); await showUSDTieredSTOInfo(); await investUsdTieredSTO(currency, amount) From 4d9af7b243f5fce5f89c6b9e02ddd0ecfd6592fd Mon Sep 17 00:00:00 2001 From: shuffledex Date: Mon, 10 Dec 2018 09:42:47 -0300 Subject: [PATCH 5/5] PR fixes --- CLI/commands/sto_manager.js | 93 +++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index d9e95f723..796f7387f 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -295,60 +295,65 @@ function fundingConfigUSDTieredSTO() { } async function addressesConfigUSDTieredSTO(usdTokenRaise) { - let addresses = {}; - addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - if (addresses.wallet == "") addresses.wallet = Issuer.address; + let addresses, menu; - addresses.reserveWallet = readlineSync.question('Enter the address that will receive remaining tokens in the case the cap is not met (' + Issuer.address + '): ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + do { - let listOfAddress; + addresses = {}; - if (usdTokenRaise) { - addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { + addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { limit: function (input) { - listOfAddress = input.split(','); - let response = true - listOfAddress.forEach((addr) => { - if (!web3.utils.isAddress(addr)) { - response = false - } - }) - return response + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: Issuer.address + }); + if (addresses.wallet == "") addresses.wallet = Issuer.address; + + addresses.reserveWallet = readlineSync.question('Enter the address that will receive remaining tokens in the case the cap is not met (' + Issuer.address + '): ', { + limit: function (input) { + return web3.utils.isAddress(input); }, limitMessage: "Must be a valid address", - defaultInput: usdToken.options.address + defaultInput: Issuer.address }); - if (addresses.usdToken == "") { - listOfAddress = [usdToken.options.address] - addresses.usdToken = [usdToken.options.address]; + if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + + let listOfAddress; + + if (usdTokenRaise) { + addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { + limit: function (input) { + listOfAddress = input.split(','); + return listOfAddress.every((addr) => { + return web3.utils.isAddress(addr) + }) + }, + limitMessage: "Must be a valid address", + defaultInput: usdToken.options.address + }); + if (addresses.usdToken == "") { + listOfAddress = [usdToken.options.address] + addresses.usdToken = [usdToken.options.address]; + } + } else { + listOfAddress = ['0x0000000000000000000000000000000000000000'] + addresses.usdToken = ['0x0000000000000000000000000000000000000000']; + } + + if (!await processArray(listOfAddress)) { + console.log(chalk.yellow(`\nPlease, verify your stable coins addresses to continue with this process.\n`)) + menu = true; + } else { + menu = false; + } + + if (typeof addresses.usdToken === 'string') { + addresses.usdToken = addresses.usdToken.split(",") } - } else { - listOfAddress = ['0x0000000000000000000000000000000000000000'] - addresses.usdToken = ['0x0000000000000000000000000000000000000000']; - } - - if (!await processArray(listOfAddress)) { - console.log(`Please, verify your stable coins addresses to continue with this process.`) - process.exit(0) - } - if (typeof addresses.usdToken === 'string') { - addresses.usdToken = addresses.usdToken.split(",") - } + } while (menu); return addresses; }