diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..8bd70f869 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,96 @@ +version: 2 +jobs: + build: + docker: + - image: maxsam4/solidity-kit + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: yarn install + - run: node --version + - run: truffle version + - run: truffle compile + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + test: + docker: + - image: maxsam4/solidity-kit + parallelism: 2 + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: yarn install + - run: node --version + - run: truffle version + - run: npm run test + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + - store_test_results: + path: test-results + - store_artifacts: + path: ./test-results/mocha/results.xml + coverage: + docker: + - image: maxsam4/solidity-kit + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: yarn install + - run: node --version + - run: truffle version + - run: + command: npm run coverage + no_output_timeout: 1h + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + - store_artifacts: + path: ./coverage/lcov.info + docs: + docker: + - image: maxsam4/solidity-kit + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: yarn install + - run: node --version + - run: truffle version + - run: npm run docs + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules +workflows: + version: 2 + commit: + jobs: + - coverage + daily-builds: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + - dev-2.1.0 + - dev-2.2.0 + - dev-3.0.0 + jobs: + - coverage + docs: + jobs: + - docs: + filters: + branches: + only: + - master diff --git a/.eslintrc.js b/.eslintrc.js index 463539deb..8b0c0ac0f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,6 +14,8 @@ module.exports = { "quotes": 0, "semi": 0, "no-undef": 0, - "key-spacing": 0 + "key-spacing": 0, + "no-tabs": 0, + "no-mixed-spaces-and-tabs":0 } }; diff --git a/.gitignore b/.gitignore index 1693ab75c..dac654947 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,8 @@ package-lock.json bridge.log .node-xml* .solcover.js.bk -allFiredEvents \ No newline at end of file +allFiredEvents +extract/ +extract.py +extract.zip +/test-results diff --git a/.solcover.js b/.solcover.js index 23ac4d3ef..2e5750f8d 100644 --- a/.solcover.js +++ b/.solcover.js @@ -2,8 +2,8 @@ module.exports = { norpc: true, port: 8545, copyPackages: ['openzeppelin-solidity'], - testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` --network coverage', + testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js -and ! -name q_usd_tiered_sto_sim.js -and ! -name z_general_permission_manager_fuzzer.js` --network coverage', deepSkip: true, - skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage', 'modules/Experimental'], - forceParse: ['mocks', 'oracles', 'modules/Experimental'] + skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'libraries/BokkyPooBahsDateTimeLibrary.sol', 'storage', 'modules/Experimental'], + forceParse: ['mocks', 'oracles', 'helpers', 'modules/Experimental'] }; diff --git a/.travis.yml b/.travis.yml index 14c03d06b..29ddc4a89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,17 +4,17 @@ node_js: cache: directories: - node_modules -matrix: - fast_finish: true -before_install: - - echo -ne '\n' | sudo add-apt-repository ppa:ethereum/ethereum - - sudo apt-get -y update - - sudo apt-get -y install solc -before_script: - - truffle version - - wget -O node_modules/solidity-coverage/lib/app.js https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js -script: - - npm run test +jobs: + include: + - stage: Test + install: + - echo -ne '\n' | sudo add-apt-repository ppa:ethereum/ethereum + - sudo apt-get update + - sudo apt-get install -y dpkg + - sudo apt-get install -y solc + - yarn install + before_script: truffle version + script: npm run test notifications: slack: - secure: W4FZSabLrzF74f317hutolEHnlq2GBlQxU6b85L5XymrjgLEhlgE16c5Qz7Emoyt6le6PXL+sfG2ujJc3XYys/6hppgrHSAasuJnKCdQNpmMZ9BNyMs6WGkmB3enIf3K/FLXb26AQdwpQdIXuOeJUTf879u+YoiZV0eZH8d3+fsIOyovq9N6X5pKOpDM9iT8gGB4t7fie7xf51s+iUaHxyO9G7jDginZ4rBXHcU7mxCub9z+Z1H8+kCTnPWaF+KKVEXx4Z0nI3+urboD7E4OIP02LwrThQls2CppA3X0EoesTcdvj/HLErY/JvsXIFiFEEHZzB1Wi+k2TiOeLcYwEuHIVij+HPxxlJNX/j8uy01Uk8s4rd+0EhvfdKHJqUKqxH4YN2npcKfHEss7bU3y7dUinXQfYShW5ZewHdvc7pnnxBTfhvmdi64HdNrXAPq+s1rhciH7MmnU+tsm4lhrpr+FBuHzUMA9fOCr7b0SQytZEgWpiUls88gdbh3yG8TjyZxmZJGx09cwEP0q7VoH0UwFh7mIu5XmYdd5tWUhavTiO7YV8cUPn7MvwMsTltB3YBpF/fB26L7ka8zBhCsjm9prW6SVYU/dyO3m91VeZtO/zJFHRDA6Q58JGVW2rgzO39z193qC1EGRXqTie96VwAAtNg8+hRb+bI/CWDVzSPc= + secure: W4FZSabLrzF74f317hutolEHnlq2GBlQxU6b85L5XymrjgLEhlgE16c5Qz7Emoyt6le6PXL+sfG2ujJc3XYys/6hppgrHSAasuJnKCdQNpmMZ9BNyMs6WGkmB3enIf3K/FLXb26AQdwpQdIXuOeJUTf879u+YoiZV0eZH8d3+fsIOyovq9N6X5pKOpDM9iT8gGB4t7fie7xf51s+iUaHxyO9G7jDginZ4rBXHcU7mxCub9z+Z1H8+kCTnPWaF+KKVEXx4Z0nI3+urboD7E4OIP02LwrThQls2CppA3X0EoesTcdvj/HLErY/JvsXIFiFEEHZzB1Wi+k2TiOeLcYwEuHIVij+HPxxlJNX/j8uy01Uk8s4rd+0EhvfdKHJqUKqxH4YN2npcKfHEss7bU3y7dUinXQfYShW5ZewHdvc7pnnxBTfhvmdi64HdNrXAPq+s1rhciH7MmnU+tsm4lhrpr+FBuHzUMA9fOCr7b0SQytZEgWpiUls88gdbh3yG8TjyZxmZJGx09cwEP0q7VoH0UwFh7mIu5XmYdd5tWUhavTiO7YV8cUPn7MvwMsTltB3YBpF/fB26L7ka8zBhCsjm9prW6SVYU/dyO3m91VeZtO/zJFHRDA6Q58JGVW2rgzO39z193qC1EGRXqTie96VwAAtNg8+hRb+bI/CWDVzSPc= \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c5796e34..94a8c2ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,74 @@ # Changelog All notable changes to this project will be documented in this file. +# v3.0.0 - Release Candidate + +[__3.0.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __10-11-18__ + +## SecurityToken +* Added new function `addModuleWithLabel()` which takes an extra param `_label` that used for giving the customize label to the module for display purpose. #428 +* Changed the first three params in Security Token `event ModuleAdded()` to be indexed for search. Params are `unit8[] types`, `bytes32 _name` and `address _moduleFactory` +* Fixed `addModule` function to be backwards compatible and call the new `addModuleWithLabel` function with an empty label. +* Fixed event `ModuleAdded` to also emit `_label`. +* Fixed function `getModule` to also return the respective module label. +* Added datastore that is used to store data like investor list that is shared among modules. +* `getInvestorCount()` now returns length of investor array that is everyone who ever held some st or has kyc data attached. +* `holderCount()` returns the number of current st holders. +* Added flags for Investors. Accredited and canbuyfromsto are now flags + +## STR +* Introduce new contract `STRGetter.sol`. It only contains the getter functions of the STR. +* Replaced `updatePolyTokenAddress()` function with `updateFromRegistry()` in `SecurityTokenRegistry`. +* Migrate all the getters of `SecurityTokenRegsitry.sol` to `STRGetter.sol` contract. +* Removed `_polyToken` parameter from `initialize` function in `SecurityTokenRegistry`. + +## GeneralTransferManager +* `modifyWhitelist()` function renamed to `modifyKYCData()`. +* Added functions to modify and get flags +* `canBuyFromSto` is now `canNotBuyFromSto` and it is the flag `1` + +## Generalize +* Removed `_polyAddress` parameter from constructors of all modules and module factories. + + +# v2.1.0 - Release Candidate + +[__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-09-18__ + +## CappedSTO 2.0.1 +* `rate` is now accepted as multiplied by 10^18 to allow settting higher price than 1ETH/POLY per token. +* Indivisble tokens are now supported. When trying to buy partial tokens, allowed full units of tokens will be purchased and remaining funds will be returned. + +## USDTieredSTO 2.1.0 +* Added `buyTokensView` and `getTokensMintedByTier` to USDTSTO. +* Added `getSTODetails` to USDTSTO. +* Added an Array of Tiers that will hold data about every tier in USDTSTO. +* Added `buyWithETHRateLimited`, `buyWithPOLYRateLimited` and `buyWithUSDRateLimited` to USDTSTO. +* Added `getTokensSoldByTier` to return sold (not minted during finalisation) tokens in each tier to USDTSTO. +* Removed individual mappings for tier data removed in UDSTSTO. +* Removed the old Proxy deployment method of USDTieredSTO and adopt the new inherited proxy deployment approach. +* Bump the version to `2.1.0` + +## GeneralTransferManager +* `getInvestors`, `getAllInvestorsData`, `getInvestorsData` added to GTM to allow easy data queries. +* `modifyDefaults(uint64 _defaultFromTime, uint64 _defaultToTime)` added which sets a default timestamp used when `fromTime` or `toTime` are 0 +* Add `address[] public investors` to record a list of all addresses that have been added to the whitelist (`getInvestors`). +* General Transfer Manager: Fix for when `allowAllWhitelistIssuances` is FALSE +* General Transfer Manager: Make GTM a Proxy based implementation to reduce deployment gas costs +* Changed the version of `GeneralTransferManagerFactory` from `1.0.0` to `2.1.0`. + +## Manual Approval TransferManager +* Removed `0x0` check for the `_from` address to `ManualApprovalTransferManager`. This allows for the Issuer/Transfer Agent to approve a one-off mint of tokens that otherwise would not be possible. +* Changed the version of `ManualApprovalTransferManagerFactory` from `1.0.0` to `2.1.0`. +* Deployed 2.0.1 `ManualApprovalTransferManagerFactory` to address 0x6af2afad53cb334e62b90ddbdcf3a086f654c298 + +## Dividends +* Changed the version of `ERC20DividendCheckpointFactory` & `EtherDividendCheckpointFactory` from `1.0.0` to `2.1.0`. +* Applied proxy pattern to Dividends modules + +## Changed +* `getAllModulesAndPermsFromTypes()` does not take securityToken address as a parameter anymore. + # v1.5.0 - Release Candidate [__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__ @@ -31,15 +99,16 @@ All notable changes to this project will be documented in this file. * Add `getReputationOfFactory()` & `getModuleListOfType()` functions to get the array type data from the ModuleRegistry contract. * Add `_setupCost` in `LogGenerateModuleFromFactory` event. * Add new function `getAllModulesByName()`, To get the list of modules having the same name. #198. -* Add new function `modifyTickerDetails()`, To modify the details of undeployed ticker. #230 +* Add new function `modifyTickerDetails()`, To modify the details of undeployed ticker. #230 + + ## Fixed -* `getAllModulesAndPermsFromTypes()` does not take securityToken address as a parameter anymore. * 0x0 and duplicate address in exclusions are no longer allowed in dividend modules. * All permissions are denied if no permission manager is active. * Generalize the STO varaible names and added them in `ISTO.sol` to use the common standard in all STOs. * Generalize the event when any new token get registered with the polymath ecosystem. `LogNewSecurityToken` should emit _ticker, _name, _securityTokenAddress, _owner, _addedAt, _registrant respectively. #230 -* Change the function name of `withdraPoly` to `withdrawERC20` and make the function generalize to extract tokens from the ST contract. parmeters are contract address and the value need to extract from the securityToken. +* Change the function name of `withdraPoly` to `withdrawERC20` and make the function generalize to extract tokens from the ST contract. parmeters are contract address and the value need to extract from the securityToken. ## Removed * Removed investors list pruning @@ -49,7 +118,7 @@ All notable changes to this project will be documented in this file. ====== -# v1.4.1 - Release Candidate +# v1.4.1 [__1.4.1__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-08-18__ @@ -73,7 +142,7 @@ All notable changes to this project will be documented in this file. * Fix #238: make beneficial investments optionally supported (default to not allowed) -# v1.4.0 - Release candidate +# v1.4.0 [__1.4.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-08-18__ diff --git a/CLI/commands/ST20Generator.js b/CLI/commands/ST20Generator.js index a52499b2f..0863c727e 100644 --- a/CLI/commands/ST20Generator.js +++ b/CLI/commands/ST20Generator.js @@ -1,60 +1,22 @@ -var readlineSync = require('readline-sync'); -var BigNumber = require('bignumber.js'); -var moment = require('moment'); -var chalk = require('chalk'); -const shell = require('shelljs'); -var contracts = require('./helpers/contract_addresses'); -var abis = require('./helpers/contract_abis'); -var common = require('./common/common_functions'); -var global = require('./common/global'); +const readlineSync = require('readline-sync'); +const BigNumber = require('bignumber.js'); +const moment = require('moment'); +const chalk = require('chalk'); +const tokenManager = require('./token_manager'); +const contracts = require('./helpers/contract_addresses'); +const abis = require('./helpers/contract_abis'); +const common = require('./common/common_functions'); +//////////////////////// let securityTokenRegistryAddress; - -/////////////////// -// Crowdsale params -let tokenName; let tokenSymbol; -let selectedSTO; - -const MODULES_TYPES = { - PERMISSION: 1, - TRANSFER: 2, - STO: 3, - DIVIDENDS: 4, - BURN: 5 -} +let tokenLaunched; -const cappedSTOFee = 20000; -const usdTieredSTOFee = 100000; -const tokenDetails = ""; -const FUND_RAISE_TYPES = { - ETH: 0, - POLY: 1, - DAI: 2 -} -//////////////////////// // Artifacts let securityTokenRegistry; let polyToken; -let usdToken; -let securityToken; -let currentSTO; - -// App flow -let _tokenConfig; -let _mintingConfig; -let _stoConfig; - -let network; - -async function executeApp(tokenConfig, mintingConfig, stoConfig, remoteNetwork) { - _tokenConfig = tokenConfig; - _mintingConfig = mintingConfig; - _stoConfig = stoConfig; - - network = remoteNetwork; - await global.initialize(remoteNetwork); +async function executeApp(_ticker, _transferOwnership, _name, _details, _divisible) { common.logAsciiBull(); console.log("********************************************"); console.log("Welcome to the Command-Line ST-20 Generator."); @@ -65,19 +27,21 @@ async function executeApp(tokenConfig, mintingConfig, stoConfig, remoteNetwork) await setup(); try { - await step_ticker_reg(); - await step_token_deploy(); - await step_Wallet_Issuance(); - await step_STO_launch(); - await step_STO_Status(); - await step_STO_configure(); + await step_ticker_registration(_ticker); + if (!tokenLaunched) { + await step_transfer_ticker_ownership(_transferOwnership); + await step_token_deploy(_name, _details, _divisible); + } + if (typeof _divisible === 'undefined') { + await tokenManager.executeApp(tokenSymbol); + } } catch (err) { console.log(err); return; } }; -async function setup(){ +async function setup() { try { securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); @@ -88,1049 +52,131 @@ async function setup(){ let polytokenABI = abis.polyToken(); polyToken = new web3.eth.Contract(polytokenABI, polytokenAddress); polyToken.setProvider(web3.currentProvider); - - //TODO: Use proper DAI token here - let usdTokenAddress = await contracts.usdToken(); - usdToken = new web3.eth.Contract(polytokenABI, usdTokenAddress); - usdToken.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."); + console.log(chalk.red('\nThere was a problem getting the contracts. Make sure they are deployed to the selected network.')); process.exit(0); } } -async function step_ticker_reg(){ - console.log('\n\x1b[34m%s\x1b[0m',"Token Symbol Registration"); +async function step_ticker_registration(_ticker) { + console.log(chalk.blue('\nToken Symbol Registration')); - let available = false; let regFee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); - let isDeployed; + let available = false; while (!available) { - console.log(chalk.green(`\nRegistering the new token symbol requires ${regFee} POLY & deducted from '${Issuer.address}', Current balance is ${(await currentBalance(Issuer.address))} POLY\n`)); + console.log(chalk.yellow(`\nRegistering the new token symbol requires ${regFee} POLY & deducted from '${Issuer.address}', Current balance is ${(web3.utils.fromWei(await polyToken.methods.balanceOf(Issuer.address).call()))} POLY\n`)); - if (typeof _tokenConfig !== 'undefined' && _tokenConfig.hasOwnProperty('symbol')) { - tokenSymbol = _tokenConfig.symbol; + if (typeof _ticker !== 'undefined') { + tokenSymbol = _ticker; + console.log(`Token Symbol: ${tokenSymbol}`); } else { - tokenSymbol = await selectTicker(true); + tokenSymbol = await selectTicker(); } let details = await securityTokenRegistry.methods.getTickerDetails(tokenSymbol).call(); - isDeployed = details[4]; if (new BigNumber(details[1]).toNumber() == 0) { + // If it has no registration date, it is available available = true; await approvePoly(securityTokenRegistryAddress, regFee); let registerTickerAction = securityTokenRegistry.methods.registerTicker(Issuer.address, tokenSymbol, ""); - await common.sendTransaction(Issuer, registerTickerAction, defaultGasPrice, 0, 1.5); + await common.sendTransaction(registerTickerAction, { factor: 1.5 }); } else if (details[0] == Issuer.address) { + // If it has registration date and its owner is Issuer available = true; + tokenLaunched = details[4]; } else { - console.log('\n\x1b[31m%s\x1b[0m',"Token Symbol has already been registered, please choose another symbol"); - } - } - - if (!isDeployed) { - if (typeof _tokenConfig === 'undefined' && readlineSync.keyInYNStrict(`Do you want to transfer the ownership of ${tokenSymbol} ticker?`)) { - let newOwner = readlineSync.question('Enter the address that will be the new owner: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let transferTickerOwnershipAction = securityTokenRegistry.methods.transferTickerOwnership(newOwner, tokenSymbol); - let receipt = await common.sendTransaction(Issuer, transferTickerOwnershipAction, defaultGasPrice, 0, 1.5); - let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, receipt.logs, 'ChangeTickerOwnership'); - console.log(chalk.green(`Ownership trasferred successfully. The new owner is ${event._newOwner}`)); - process.exit(0); - } - } -} - -async function step_token_deploy(){ - // Let's check if token has already been deployed, if it has, skip to STO - let tokenAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); - if (tokenAddress != "0x0000000000000000000000000000000000000000") { - console.log('\n\x1b[32m%s\x1b[0m',"Token has already been deployed at address " + tokenAddress + ". Skipping deployment."); - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI, tokenAddress); - } else { - console.log('\n\x1b[34m%s\x1b[0m',"Token Creation - Token Deployment"); - - let launchFee = web3.utils.fromWei(await securityTokenRegistry.methods.getSecurityTokenLaunchFee().call()); - console.log(chalk.green(`\nToken deployment requires ${launchFee} POLY & deducted from '${Issuer.address}', Current balance is ${(await currentBalance(Issuer.address))} POLY\n`)); - - if (typeof _tokenConfig !== 'undefined' && _tokenConfig.hasOwnProperty('name')) { - tokenName = _tokenConfig.name; - } else { - tokenName = readlineSync.question('Enter the name for your new token: '); - } - if (tokenName == "") tokenName = 'default'; - - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"Select the Token divisibility type"); - - let divisibility; - if (typeof _tokenConfig !== 'undefined' && _tokenConfig.hasOwnProperty('divisible')) { - divisibility = _tokenConfig.divisible; - } else { - let divisible = readlineSync.question('Press "N" for Non-divisible type token or hit Enter for divisible type token (Default):'); - if (divisible == 'N' || divisible == 'n') - divisibility = false; - else - divisibility = true; - } - - await approvePoly(securityTokenRegistryAddress, launchFee); - let generateSecurityTokenAction = securityTokenRegistry.methods.generateSecurityToken(tokenName, tokenSymbol, tokenDetails, divisibility); - let receipt = await common.sendTransaction(Issuer, generateSecurityTokenAction, defaultGasPrice); - let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, receipt.logs, 'NewSecurityToken'); - console.log(`Deployed Token at address: ${event._securityTokenAddress}`); - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI, event._securityTokenAddress); - } -} - -async function step_Wallet_Issuance(){ - let result = await securityToken.methods.getModulesByType(MODULES_TYPES.STO).call(); - if (result.length > 0) { - console.log('\x1b[32m%s\x1b[0m',"STO has already been created at address " + result[0] + ". Skipping initial minting"); - } else { - let initialMint = await securityToken.getPastEvents('Transfer', { - filter: {from: "0x0000000000000000000000000000000000000000"}, // Using an array means OR: e.g. 20 or 23 - fromBlock: 0, - toBlock: 'latest' - }); - if (initialMint.length > 0) { - console.log('\x1b[32m%s\x1b[0m',web3.utils.fromWei(initialMint[0].returnValues.value) +" Tokens have already been minted for " + initialMint[0].returnValues.to + ". Skipping initial minting"); - } else { - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"Token Creation - Token Minting for Issuer"); - - console.log("Before setting up the STO, you can mint any amount of tokens that will remain under your control or you can transfer to affiliates"); - - let multimint; - if (typeof _mintingConfig !== 'undefined' && _mintingConfig.hasOwnProperty('multimint')) { - multimint = _mintingConfig.multimint; - } else { - let isAffiliate = readlineSync.question('Press'+ chalk.green(` "Y" `) + 'if you have list of affiliates addresses with you otherwise hit' + chalk.green(' Enter ') + 'and get the minted tokens to a particular address: '); - multimint = (isAffiliate == "Y" || isAffiliate == "y"); - } - - if (multimint) - await multi_mint_tokens(); - else { - let mintWallet; - if (typeof _mintingConfig !== 'undefined' && _mintingConfig.hasOwnProperty('singleMint') && _mintingConfig.singleMint.hasOwnProperty('wallet')) { - mintWallet = _mintingConfig.singleMint.wallet; - } else { - mintWallet = readlineSync.question('Add the address that will hold the issued tokens to the whitelist (' + Issuer.address + '): '); - } - if (mintWallet == "") mintWallet = Issuer.address; - - let canBuyFromSTO; - if (typeof _mintingConfig !== 'undefined' && _mintingConfig.hasOwnProperty('singleMint') && _mintingConfig.singleMint.hasOwnProperty('allowedToBuy')) { - canBuyFromSTO = _mintingConfig.singleMint.allowedToBuy; - } else { - canBuyFromSTO = readlineSync.keyInYNStrict(`Is address '(${mintWallet})' allowed to buy tokens from the STO? `); - } - - // Add address to whitelist - let generalTransferManagerAddress = (await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call())[0]; - let generalTransferManagerABI = abis.generalTransferManager(); - let generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, generalTransferManagerAddress); - let modifyWhitelistAction = generalTransferManager.methods.modifyWhitelist(mintWallet,Math.floor(Date.now()/1000),Math.floor(Date.now()/1000),Math.floor(Date.now()/1000 + 31536000), canBuyFromSTO); - await common.sendTransaction(Issuer, modifyWhitelistAction, defaultGasPrice); - - // Mint tokens - if (typeof _mintingConfig !== 'undefined' && _mintingConfig.hasOwnProperty('singleMint') && _mintingConfig.singleMint.hasOwnProperty('tokenAmount')) { - issuerTokens = _mintingConfig.singleMint.tokenAmount; - } else { - issuerTokens = readlineSync.question('How many tokens do you plan to mint for the wallet you entered? (500.000): '); - } - if (issuerTokens == "") issuerTokens = '500000'; - - let mintAction = securityToken.methods.mint(mintWallet, web3.utils.toWei(issuerTokens)); - await common.sendTransaction(Issuer, mintAction, defaultGasPrice); - } + // If it has registration date and its owner is not Issuer + console.log(chalk.yellow('\nToken Symbol has already been registered, please choose another symbol')); } } } -async function multi_mint_tokens() { - //await whitelist.startWhitelisting(tokenSymbol); - shell.exec(`${__dirname}/scripts/script.sh Whitelist ${tokenSymbol} 75 ${network}`); - console.log(chalk.green(`\nCongrats! All the affiliates get succssfully whitelisted, Now its time to Mint the tokens\n`)); - console.log(chalk.red(`WARNING: `) + `Please make sure all the addresses that get whitelisted are only eligible to hold or get Security token\n`); - - shell.exec(`${__dirname}/scripts//script.sh Multimint ${tokenSymbol} 75 ${network}`); - console.log(chalk.green(`\nHurray!! Tokens get successfully Minted and transferred to token holders`)); -} - -async function step_STO_launch() { - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"Token Creation - STO Configuration"); - - let result = await securityToken.methods.getModulesByType(MODULES_TYPES.STO).call(); - if (result.length > 0) { - STO_Address = result[0]; - let stoModuleData = await securityToken.methods.getModule(STO_Address).call(); - selectedSTO = web3.utils.toAscii(stoModuleData[0]).replace(/\u0000/g, ''); - console.log('\x1b[32m%s\x1b[0m',selectedSTO + " has already been created at address " + STO_Address + ". Skipping STO creation"); - switch (selectedSTO) { - case 'CappedSTO': - let cappedSTOABI = abis.cappedSTO(); - currentSTO = new web3.eth.Contract(cappedSTOABI,STO_Address); - break; - case 'USDTieredSTO': - let usdTieredSTOABI = abis.usdTieredSTO(); - currentSTO = new web3.eth.Contract(usdTieredSTOABI,STO_Address); - break; - } - } else { - let index; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('type')) { - index = _stoConfig.type; - } else { - let options = ['Capped STO', 'USD Tiered STO', 'Select STO later']; - index = readlineSync.keyInSelect(options, 'What type of STO do you want?', { cancel: false }); - } - switch (index) { - case 0: - selectedSTO = 'CappedSTO'; - await cappedSTO_launch(); - break; - case 1: - selectedSTO = 'USDTieredSTO'; - await usdTieredSTO_launch(); - break; - case 2: - process.exit(0); - break; - } - } -} - -async function step_STO_Status() { - switch (selectedSTO) { - case 'CappedSTO': - await cappedSTO_status(); - break; - case 'USDTieredSTO': - await usdTieredSTO_status(); - break; - } -} - -async function step_STO_configure() { - switch (selectedSTO) { - case 'CappedSTO': - break; - case 'USDTieredSTO': - await usdTieredSTO_configure(); - break; - } -} - -//////////////// -// Capped STO // -//////////////// -async function cappedSTO_launch() { - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"Token Creation - Capped STO in No. of Tokens"); - - let stoFee = cappedSTOFee; - let contractBalance = await polyToken.methods.balanceOf(securityToken._address).call(); - let requiredAmount = web3.utils.toWei(stoFee.toString()); - if (parseInt(contractBalance) < parseInt(requiredAmount)) { - let transferAmount = parseInt(requiredAmount) - parseInt(contractBalance); - let ownerBalance = await polyToken.methods.balanceOf(Issuer.address).call(); - if(parseInt(ownerBalance) < transferAmount) { - console.log(chalk.red(`\n**************************************************************************************************************************************************`)); - console.log(chalk.red(`Not enough balance to pay the CappedSTO fee, Requires ${(new BigNumber(transferAmount).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY but have ${(new BigNumber(ownerBalance).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY. Access POLY faucet to get the POLY to complete this txn`)); - console.log(chalk.red(`**************************************************************************************************************************************************\n`)); - process.exit(0); - } else { - let transferAction = polyToken.methods.transfer(securityToken._address, new BigNumber(transferAmount)); - let receipt = await common.sendTransaction(Issuer, transferAction, defaultGasPrice, 0, 2); - let event = common.getEventFromLogs(polyToken._jsonInterface, receipt.logs, 'Transfer'); - console.log(`Number of POLY sent: ${web3.utils.fromWei(new web3.utils.BN(event._value))}`) - } - } - - let cap; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('cap')) { - cap = _stoConfig.cap.toString(); - } else { - cap = readlineSync.question('How many tokens do you plan to sell on the STO? (500.000): '); - } - if (cap == "") cap = '500000'; - - let oneMinuteFromNow = BigNumber((Math.floor(Date.now() / 1000) + 60)); - let startTime; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('startTime')) { - startTime = _stoConfig.startTime; - } else { - startTime = readlineSync.question('Enter the start time for the STO (Unix Epoch time)\n(1 minutes from now = ' + oneMinuteFromNow + ' ): '); - } - if (startTime == "") startTime = oneMinuteFromNow; - - let oneMonthFromNow = BigNumber((Math.floor(Date.now()/1000)+ (30 * 24 * 60 * 60))); - let endTime; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('endTime')) { - endTime = _stoConfig.endTime; - } else { - endTime = readlineSync.question('Enter the end time for the STO (Unix Epoch time)\n(1 month from now = ' + oneMonthFromNow + ' ): '); - } - if (endTime == "") endTime = oneMonthFromNow; - - let wallet; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('wallet')) { - wallet = _stoConfig.wallet; - } else { - wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): '); - } - if (wallet == "") wallet = Issuer.address; - - let raiseType; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('raiseType')) { - raiseType = [_stoConfig.raiseType]; - } else { - raiseType = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise or leave empty for Ether raise (E):'); - if (raiseType.toUpperCase() == 'P' ) { - raiseType = [1]; - } else { - raiseType = [0]; - } - } - - let rate; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('rate')) { - rate = _stoConfig.rate; - } else { - rate = readlineSync.question(`Enter the rate (1 ${(raiseType == 1 ? 'POLY' : 'ETH')} = X ST) for the STO (1000): `); - } - if (rate == "") rate = 1000; - - let bytesSTO = web3.eth.abi.encodeFunctionCall( { - name: 'configure', - type: 'function', - inputs: [ - { - type: 'uint256', - name: '_startTime' - },{ - type: 'uint256', - name: '_endTime' - },{ - type: 'uint256', - name: '_cap' - },{ - type: 'uint256', - name: '_rate' - },{ - type: 'uint8[]', - name: '_fundRaiseTypes' - },{ - type: 'address', - name: '_fundsReceiver' - } - ] - }, [startTime, endTime, web3.utils.toWei(cap), rate, raiseType, wallet]); - - let cappedSTOFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.STO, "CappedSTO"); - let addModuleAction = securityToken.methods.addModule(cappedSTOFactoryAddress, bytesSTO, new BigNumber(stoFee).times(new BigNumber(10).pow(18)), 0); - let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); - console.log(`STO deployed at address: ${event._module}`); - - STO_Address = event._module; - let cappedSTOABI = abis.cappedSTO(); - currentSTO = new web3.eth.Contract(cappedSTOABI, STO_Address); -} - -async function cappedSTO_status() { - let displayStartTime = await currentSTO.methods.startTime().call(); - let displayEndTime = await currentSTO.methods.endTime().call(); - let displayRate = await currentSTO.methods.rate().call(); - let displayCap = await currentSTO.methods.cap().call(); - let displayWallet = await currentSTO.methods.wallet().call(); - let displayRaiseType; - let displayFundsRaised; - let displayWalletBalance; - let raiseType = await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES.ETH).call(); - if (raiseType) { - displayRaiseType = 'ETH'; - displayFundsRaised = await currentSTO.methods.fundsRaised(FUND_RAISE_TYPES.ETH).call(); - displayWalletBalance = web3.utils.fromWei(await web3.eth.getBalance(displayWallet)); - } else { - displayRaiseType = 'POLY'; - displayFundsRaised = await currentSTO.methods.fundsRaised(FUND_RAISE_TYPES.POLY).call(); - displayWalletBalance = await currentBalance(displayWallet); - } - let displayTokensSold = await currentSTO.methods.totalTokensSold().call(); - let displayInvestorCount = await currentSTO.methods.investorCount().call(); - let displayTokenSymbol = await securityToken.methods.symbol().call(); - - let formattedCap = BigNumber(web3.utils.fromWei(displayCap)); - let formattedSold = BigNumber(web3.utils.fromWei(displayTokensSold)); - - let now = Math.floor(Date.now()/1000); - let timeTitle; - let timeRemaining; - - if (now < displayStartTime) { - timeTitle = "STO starts in: "; - timeRemaining = displayStartTime - now; - } else { - timeTitle = "Time remaining:"; - timeRemaining = displayEndTime - now; - } - - timeRemaining = common.convertToDaysRemaining(timeRemaining); - - console.log(` - ***** STO Information ***** - - Address: ${STO_Address} - - Raise Cap: ${web3.utils.fromWei(displayCap)} ${displayTokenSymbol.toUpperCase()} - - Start Time: ${new Date(displayStartTime * 1000)} - - End Time: ${new Date(displayEndTime * 1000)} - - Raise Type: ${displayRaiseType} - - Rate: 1 ${displayRaiseType} = ${displayRate} ${displayTokenSymbol.toUpperCase()} - - Wallet: ${displayWallet} - - Wallet Balance: ${displayWalletBalance} ${displayRaiseType} - -------------------------------------- - - ${timeTitle} ${timeRemaining} - - Funds raised: ${web3.utils.fromWei(displayFundsRaised)} ${displayRaiseType} - - Tokens sold: ${web3.utils.fromWei(displayTokensSold)} ${displayTokenSymbol.toUpperCase()} - - Tokens remaining: ${formattedCap.minus(formattedSold).toNumber()} ${displayTokenSymbol.toUpperCase()} - - Investor count: ${displayInvestorCount} - `); - - console.log(chalk.green(`\n${(await currentBalance(Issuer.address))} POLY balance remaining at issuer address ${Issuer.address}`)); -} - -//////////////////// -// USD Tiered STO // -//////////////////// -function fundingConfigUSDTieredSTO() { - let funding = {}; - - let selectedFunding; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('fundingType')) { - selectedFunding = _stoConfig.fundingType; - } else { - 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(); - } - - funding.raiseType = []; - if (selectedFunding.includes('E')) { - funding.raiseType.push(FUND_RAISE_TYPES.ETH); - } - if (selectedFunding.includes('P')) { - funding.raiseType.push(FUND_RAISE_TYPES.POLY); - } - if (selectedFunding.includes('D')) { - funding.raiseType.push(FUND_RAISE_TYPES.DAI); - } - if (funding.raiseType.length == 0) { - funding.raiseType = [FUND_RAISE_TYPES.ETH, FUND_RAISE_TYPES.POLY, FUND_RAISE_TYPES.DAI]; - } - - return funding; -} - -function addressesConfigUSDTieredSTO(usdTokenRaise) { - let addresses = {}; - - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('wallet')) { - addresses.wallet = _stoConfig.wallet; - } else { - 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; - - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('reserveWallet')) { - addresses.reserveWallet = _stoConfig.reserveWallet; - } else { - 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) { +async function step_transfer_ticker_ownership(_transferOwnership) { + let newOwner = null; + if (typeof _transferOwnership !== 'undefined' && _transferOwnership != 'false') { + newOwner = _transferOwnership; + console.log(`Transfer ownership to: ${newOwner}`); + } else if (_transferOwnership != 'false' && readlineSync.keyInYNStrict(`Do you want to transfer the ownership of ${tokenSymbol} ticker?`)) { + newOwner = readlineSync.question('Enter the address that will be the new owner: ', { + limit: function (input) { return web3.utils.isAddress(input); }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - } - if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; - - if (usdTokenRaise) { - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('usdToken')) { - addresses.usdToken = _stoConfig.usdToken; - } else { - addresses.usdToken = readlineSync.question('Enter the address of the USD Token or stable coin (' + usdToken.options.address + '): ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: usdToken.options.address - }); - } - if (addresses.usdToken == "") addresses.usdToken = usdToken.options.address; - } else { - addresses.usdToken = '0x0000000000000000000000000000000000000000'; - } - - return addresses; -} - -function tiersConfigUSDTieredSTO(polyRaise) { - let tiers = {}; - - let defaultTiers = 3; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('numberOfTiers')) { - tiers.tiers = _stoConfig.numberOfTiers; - } else { - tiers.tiers = readlineSync.questionInt(`Enter the number of tiers for the STO? (${defaultTiers}): `, { - limit: function(input) { - return input > 0; - }, - limitMessage: 'Must be greater than zero', - defaultInput: defaultTiers + limitMessage: "Must be a valid address" }); } - let defaultTokensPerTier = [190000000, 100000000, 200000000]; - let defaultRatePerTier = ['0.05', '0.10', '0.15']; - let defaultTokensPerTierDiscountPoly = [90000000, 50000000, 100000000]; - let defaultRatePerTierDiscountPoly = ['0.025', '0.05', '0.075']; - tiers.tokensPerTier = []; - tiers.ratePerTier = []; - tiers.tokensPerTierDiscountPoly = []; - tiers.ratePerTierDiscountPoly = []; - for (let i = 0; i < tiers.tiers; i++) { - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('tokensPerTiers') && i < _stoConfig.tokensPerTiers.length) { - tiers.tokensPerTier[i] = web3.utils.toWei(_stoConfig.tokensPerTiers[i].toString()); - } else { - tiers.tokensPerTier[i] = web3.utils.toWei(readlineSync.question(`How many tokens do you plan to sell on tier No. ${i+1}? (${defaultTokensPerTier[i]}): `, { - limit: function(input) { - return parseFloat(input) > 0; - }, - limitMessage: 'Must be greater than zero', - defaultInput: defaultTokensPerTier[i] - })); - } - - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('ratePerTiers') && i < _stoConfig.ratePerTiers.length) { - tiers.ratePerTier[i] = web3.utils.toWei(_stoConfig.ratePerTiers[i].toString()); - } else { - tiers.ratePerTier[i] = web3.utils.toWei(readlineSync.question(`What is the USD per token rate for tier No. ${i+1}? (${defaultRatePerTier[i]}): `, { - limit: function(input) { - return parseFloat(input) > 0; - }, - limitMessage: 'Must be greater than zero', - defaultInput: defaultRatePerTier[i] - })); - } - - let isTPTDPDefined = (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('discountedTokensPerTiers') && i < _stoConfig.discountedTokensPerTiers.length); //If it's defined by config file - let isRPTDPDefined = (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('discountedRatePerTiers') && i < _stoConfig.discountedRatePerTiers.length); //If it's defined by config file - //If funds can be raised in POLY and discounts are defined in config file or are choosen by user - if (polyRaise && ((isTPTDPDefined && isRPTDPDefined) || readlineSync.keyInYNStrict(`Do you plan to have a discounted rate for POLY investments for tier No. ${i+1}? `))) { - if (isTPTDPDefined) { - tiers.tokensPerTierDiscountPoly[i] = web3.utils.toWei(_stoConfig.discountedTokensPerTiers[i].toString()); - } else { - tiers.tokensPerTierDiscountPoly[i] = web3.utils.toWei(readlineSync.question(`How many of those tokens do you plan to sell at discounted rate on tier No. ${i+1}? (${defaultTokensPerTierDiscountPoly[i]}): `, { - limit: function(input) { - return new BigNumber(web3.utils.toWei(input)).lte(tiers.tokensPerTier[i]) - }, - limitMessage: 'Must be less than the No. of tokens of the tier', - defaultInput: defaultTokensPerTierDiscountPoly[i] - })); - } - - if (isRPTDPDefined) { - tiers.ratePerTierDiscountPoly[i] = web3.utils.toWei(_stoConfig.discountedRatePerTiers[i].toString()); - } else { - tiers.ratePerTierDiscountPoly[i] = web3.utils.toWei(readlineSync.question(`What is the discounted rate for tier No. ${i+1}? (${defaultRatePerTierDiscountPoly[i]}): `, { - limit: function(input) { - return new BigNumber(web3.utils.toWei(input)).lte(tiers.ratePerTier[i]) - }, - limitMessage: 'Must be less than the rate of the tier', - defaultInput: defaultRatePerTierDiscountPoly[i] - })); - } - } else { - tiers.tokensPerTierDiscountPoly[i] = 0; - tiers.ratePerTierDiscountPoly[i] = 0; - } - } - - return tiers; -} - -function timesConfigUSDTieredSTO() { - let times = {}; - - let oneMinuteFromNow = Math.floor(Date.now() / 1000) + 60; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('startTime')) { - times.startTime = _stoConfig.startTime; - } else { - times.startTime = readlineSync.questionInt('Enter the start time for the STO (Unix Epoch time)\n(1 minutes from now = ' + oneMinuteFromNow + ' ): ', { - limit: function(input) { - return input > Math.floor(Date.now() / 1000); - }, - limitMessage: "Must be a future time", - defaultInput: oneMinuteFromNow - }); - } - if (times.startTime == "") times.startTime = oneMinuteFromNow; - - let oneMonthFromNow = Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60); - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('endTime')) { - times.endTime = _stoConfig.endTime; - } else { - times.endTime = readlineSync.questionInt('Enter the end time for the STO (Unix Epoch time)\n(1 month from now = ' + oneMonthFromNow + ' ): ', { - limit: function(input) { - return input > times.startTime; - }, - limitMessage: "Must be greater than Start Time", - defaultInput: oneMonthFromNow - }); + if (newOwner) { + let transferTickerOwnershipAction = securityTokenRegistry.methods.transferTickerOwnership(newOwner, tokenSymbol); + let receipt = await common.sendTransaction(transferTickerOwnershipAction, { factor: 1.5 }); + let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, receipt.logs, 'ChangeTickerOwnership'); + console.log(chalk.green(`Ownership trasferred successfully. The new owner is ${event._newOwner}`)); + process.exit(0); } - if (times.endTime == "") times.endTime = oneMonthFromNow; - - return times; } -function limitsConfigUSDTieredSTO() { - let limits = {}; +async function step_token_deploy(_name, _details, _divisible) { + console.log(chalk.blue('\nToken Creation - Token Deployment')); - let defaultMinimumInvestment = 5; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('minimumInvestmentUSD')) { - limits.minimumInvestmentUSD = web3.utils.toWei(_stoConfig.minimumInvestmentUSD.toString()); - } else { - limits.minimumInvestmentUSD = web3.utils.toWei(readlineSync.question(`What is the minimum investment in USD? (${defaultMinimumInvestment}): `, { - limit: function(input) { - return parseInt(input) > 0; - }, - limitMessage: "Must be greater than zero", - defaultInput: defaultMinimumInvestment - })); - } + let launchFee = web3.utils.fromWei(await securityTokenRegistry.methods.getSecurityTokenLaunchFee().call()); + console.log(chalk.green(`\nToken deployment requires ${launchFee} POLY & deducted from '${Issuer.address}', Current balance is ${(web3.utils.fromWei(await polyToken.methods.balanceOf(Issuer.address).call()))} POLY\n`)); - let nonAccreditedLimit = 2500; - if (typeof _stoConfig !== 'undefined' && _stoConfig.hasOwnProperty('nonAccreditedLimitUSD')) { - limits.nonAccreditedLimitUSD = web3.utils.toWei(_stoConfig.nonAccreditedLimitUSD.toString()); + let tokenName; + if (typeof _name !== 'undefined') { + tokenName = _name; + console.log(`Token Name: ${tokenName}`); } else { - limits.nonAccreditedLimitUSD = web3.utils.toWei(readlineSync.question(`What is the default limit for non accredited investors in USD? (${nonAccreditedLimit}): `, { - limit: function(input) { - return new BigNumber(web3.utils.toWei(input)).gte(limits.minimumInvestmentUSD); - }, - limitMessage: "Must be greater than minimum investment", - defaultInput: nonAccreditedLimit - })); - } - - return limits; -} - -async function usdTieredSTO_launch() { - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"Token Creation - USD Tiered STO"); - - let stoFee = usdTieredSTOFee; - let contractBalance = await polyToken.methods.balanceOf(securityToken._address).call(); - let requiredAmount = web3.utils.toWei(stoFee.toString(), "ether"); - if (new web3.utils.BN(contractBalance).lt(new web3.utils.BN(requiredAmount))) { - let transferAmount = (new web3.utils.BN(requiredAmount)).sub(new web3.utils.BN(contractBalance)); - let ownerBalance = new web3.utils.BN(await polyToken.methods.balanceOf(Issuer.address).call()); - if (ownerBalance.lt(transferAmount)) { - console.log(chalk.red(`\n**************************************************************************************************************************************************`)); - console.log(chalk.red(`Not enough balance to pay the ${selectedSTO} fee, Requires ${web3.utils.fromWei(transferAmount)} POLY but have ${web3.utils.fromWei(ownerBalance)} POLY. Access POLY faucet to get the POLY to complete this txn`)); - console.log(chalk.red(`**************************************************************************************************************************************************\n`)); - process.exit(0); - } else { - let transferAction = polyToken.methods.transfer(securityToken._address, transferAmount); - let receipt = await common.sendTransaction(Issuer, transferAction, defaultGasPrice, 0, 2); - let event = common.getEventFromLogs(polyToken._jsonInterface, receipt.logs, 'Transfer'); - console.log(`Number of POLY sent: ${web3.utils.fromWei(new web3.utils.BN(event._value))}`) - } - } - - let funding = fundingConfigUSDTieredSTO(); - let addresses = addressesConfigUSDTieredSTO(funding.raiseType.includes(FUND_RAISE_TYPES.DAI)); - let tiers = tiersConfigUSDTieredSTO(funding.raiseType.includes(FUND_RAISE_TYPES.POLY)); - let limits = limitsConfigUSDTieredSTO(); - let times = timesConfigUSDTieredSTO(); - let bytesSTO = web3.eth.abi.encodeFunctionCall( { - name: 'configure', - type: 'function', - inputs: [ - { - type: 'uint256', - name: '_startTime' - },{ - type: 'uint256', - name: '_endTime' - },{ - type: 'uint256[]', - name: '_ratePerTier' - },{ - type: 'uint256[]', - name: '_ratePerTierDiscountPoly' - },{ - type: 'uint256[]', - name: '_tokensPerTier' - },{ - type: 'uint256[]', - name: '_tokensPerTierDiscountPoly' - },{ - type: 'uint256', - name: '_nonAccreditedLimitUSD' - },{ - type: 'uint256', - name: '_minimumInvestmentUSD' - },{ - type: 'uint8[]', - name: '_fundRaiseTypes' - },{ - type: 'address', - name: '_wallet' - },{ - type: 'address', - name: '_reserveWallet' - },{ - type: 'address', - name: '_usdToken' - } - ] - }, [times.startTime, - times.endTime, - tiers.ratePerTier, - tiers.ratePerTierDiscountPoly, - tiers.tokensPerTier, - tiers.tokensPerTierDiscountPoly, - limits.nonAccreditedLimitUSD, - limits.minimumInvestmentUSD, - funding.raiseType, - addresses.wallet, - addresses.reserveWallet, - addresses.usdToken - ]); - - let usdTieredSTOFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.STO, 'USDTieredSTO'); - let addModuleAction = securityToken.methods.addModule(usdTieredSTOFactoryAddress, bytesSTO, new BigNumber(stoFee).times(new BigNumber(10).pow(18)), 0); - let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); - console.log(`STO deployed at address: ${event._module}`); - - STO_Address = event._module; - let usdTieredSTOABI = abis.usdTieredSTO(); - currentSTO = new web3.eth.Contract(usdTieredSTOABI,STO_Address); -} - -async function usdTieredSTO_status() { - let displayStartTime = await currentSTO.methods.startTime().call(); - let displayEndTime = await currentSTO.methods.endTime().call(); - let displayCurrentTier = parseInt(await currentSTO.methods.currentTier().call()) + 1; - let displayNonAccreditedLimitUSD = web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSD().call()); - let displayMinimumInvestmentUSD = web3.utils.fromWei(await currentSTO.methods.minimumInvestmentUSD().call()); - let displayWallet = await currentSTO.methods.wallet().call(); - let displayReserveWallet = await currentSTO.methods.reserveWallet().call(); - let displayTokensSold = web3.utils.fromWei(await currentSTO.methods.getTokensSold().call()); - 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 raiseTypes = []; - for (const fundType in FUND_RAISE_TYPES) { - if (await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES[fundType]).call()) { - raiseTypes.push(fundType); - } - } - - let displayTiers = ""; - let displayMintedPerTier = ""; - for (let t = 0; t < tiersLength; t++) { - let ratePerTier = await currentSTO.methods.ratePerTier(t).call(); - let tokensPerTierTotal = await currentSTO.methods.tokensPerTierTotal(t).call(); - let mintedPerTierTotal = await currentSTO.methods.mintedPerTierTotal(t).call(); - - let displayMintedPerTierPerType = ""; - let displayDiscountTokens = ""; - for (const type of raiseTypes) { - let displayDiscountMinted = ""; - if (type == 'POLY') { - let tokensPerTierDiscountPoly = await currentSTO.methods.tokensPerTierDiscountPoly(t).call(); - if (tokensPerTierDiscountPoly > 0) { - let ratePerTierDiscountPoly = await currentSTO.methods.ratePerTierDiscountPoly(t).call(); - let mintedPerTierDiscountPoly = await currentSTO.methods.mintedPerTierDiscountPoly(t).call(); - displayDiscountTokens = ` - Tokens at discounted rate: ${web3.utils.fromWei(tokensPerTierDiscountPoly)} ${displayTokenSymbol} - Discounted rate: ${web3.utils.fromWei(ratePerTierDiscountPoly, 'ether')} USD per Token`; - - displayDiscountMinted = `(${web3.utils.fromWei(mintedPerTierDiscountPoly)} ${displayTokenSymbol} at discounted rate)`; - } - } - - let mintedPerTier = await currentSTO.methods.mintedPerTier(FUND_RAISE_TYPES[type], t).call(); - displayMintedPerTierPerType += ` - Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; - } - - displayTiers += ` - - Tier ${t+1}: - Tokens: ${web3.utils.fromWei(tokensPerTierTotal)} ${displayTokenSymbol} - Rate: ${web3.utils.fromWei(ratePerTier)} USD per Token` - + displayDiscountTokens; - - displayMintedPerTier += ` - - Tokens minted in Tier ${t+1}: ${web3.utils.fromWei(mintedPerTierTotal)} ${displayTokenSymbol}` - + displayMintedPerTierPerType; + tokenName = readlineSync.question('Enter the name for your new token: ', { defaultInput: 'default' }); } - let displayFundsRaisedUSD = web3.utils.fromWei(await currentSTO.methods.fundsRaisedUSD().call()); - - let displayWalletBalancePerType = ''; - let displayReserveWalletBalancePerType = ''; - let displayFundsRaisedPerType = ''; - let displayTokensSoldPerType = ''; - for (const type of raiseTypes) { - let balance = await getBalance(displayWallet, type); - let walletBalance = web3.utils.fromWei(balance); - let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(FUND_RAISE_TYPES[type], balance).call()); - displayWalletBalancePerType += ` - Balance ${type}:\t\t ${walletBalance} ${type} (${walletBalanceUSD} USD)`; - - balance = await getBalance(displayReserveWallet, type); - let reserveWalletBalance = web3.utils.fromWei(balance); - let reserveWalletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(FUND_RAISE_TYPES[type], balance).call()); - displayReserveWalletBalancePerType += ` - Balance ${type}:\t\t ${reserveWalletBalance} ${type} (${reserveWalletBalanceUSD} USD)`; - - let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(FUND_RAISE_TYPES[type]).call()); - 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(FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` - Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; - } - } - - let displayRaiseType = raiseTypes.join(' - '); - - let now = Math.floor(Date.now()/1000); - let timeTitle; - let timeRemaining; - if (now < displayStartTime) { - timeTitle = "STO starts in: "; - timeRemaining = displayStartTime - now; + let tokenDetails; + if (typeof _details !== 'undefined') { + tokenDetails = _details; + console.log(`Token details: ${tokenDetails.toString()}`) } else { - timeTitle = "Time remaining:"; - timeRemaining = displayEndTime - now; + tokenDetails = readlineSync.question('Enter off-chain details of the token (i.e. Dropbox folder url): '); } - timeRemaining = common.convertToDaysRemaining(timeRemaining); - - console.log(` - ****************** STO Information ****************** - - Address: ${STO_Address} - - Start Time: ${new Date(displayStartTime * 1000)} - - End Time: ${new Date(displayEndTime * 1000)} - - Raise Type: ${displayRaiseType} - - Tiers: ${tiersLength}` - + displayTiers + ` - - Minimum Investment: ${displayMinimumInvestmentUSD} USD - - Non Accredited Limit: ${displayNonAccreditedLimitUSD} USD - - Wallet: ${displayWallet}` - + displayWalletBalancePerType + ` - - Reserve Wallet: ${displayReserveWallet}` - + displayReserveWalletBalancePerType + ` - - -------------------------------------- - - ${timeTitle} ${timeRemaining} - - Is Finalized: ${displayIsFinalized} - - Tokens Sold: ${displayTokensSold} ${displayTokenSymbol}` - + displayTokensSoldPerType + ` - - Current Tier: ${displayCurrentTier}` - + displayMintedPerTier + ` - - Investor count: ${displayInvestorCount} - - Funds Raised` - + displayFundsRaisedPerType + ` - USD: ${displayFundsRaisedUSD} USD - `); - - console.log(chalk.green(`\n${(await currentBalance(Issuer.address))} POLY balance remaining at issuer address ${Issuer.address}`)); -} - -async function usdTieredSTO_configure() { - console.log("\n"); - console.log('\x1b[34m%s\x1b[0m',"STO Configuration - USD Tiered STO"); - - let isFinalized = await currentSTO.methods.isFinalized().call(); - if (isFinalized) { - console.log(chalk.red(`STO is finalized`)); + let divisibility; + if (typeof _divisible !== 'undefined') { + divisibility = _divisible.toString() == 'true'; + console.log(`Divisible: ${divisibility.toString()}`) } else { - let options = []; - 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); - let startTime = await currentSTO.methods.startTime().call.call(); - if (now < startTime) { - options.push('Modify times configuration', 'Modify tiers configuration', 'Modify addresses configuration', - 'Modify limits configuration', 'Modify funding configuration'); - } - - if (typeof _stoConfig === 'undefined') { - let index = readlineSync.keyInSelect(options, 'What do you want to do?'); - switch (index) { - case 0: - let reserveWallet = await currentSTO.methods.reserveWallet().call(); - let isVerified = await securityToken.methods.verifyTransfer(STO_Address, reserveWallet, 0, web3.utils.fromAscii("")).call(); - if (isVerified == "2") { - if (readlineSync.keyInYNStrict()) { - let finalizeAction = currentSTO.methods.finalize(); - await common.sendTransaction(Issuer, finalizeAction, defaultGasPrice); - } - } else { - console.log(chalk.red(`Reserve wallet (${reserveWallet}) is not able to receive remaining tokens. Check if this address is whitelisted.`)); - } - break; - case 1: - let investor = readlineSync.question('Enter the address to change accreditation: '); - let isAccredited = readlineSync.keyInYNStrict(`Is ${investor} accredited?`); - let investors = [investor]; - let accredited = [isAccredited]; - let changeAccreditedAction = currentSTO.methods.changeAccredited(investors, accredited); - // 2 GAS? - await common.sendTransaction(Issuer, changeAccreditedAction, defaultGasPrice); - break; - case 2: - shell.exec(`${__dirname}/scripts/script.sh Accredit ${tokenSymbol} 75 ${network}`); - 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 ${network}`); - break; - case 5: - await modfifyTimes(); - await usdTieredSTO_status(); - break; - case 6: - await modfifyTiers(); - await usdTieredSTO_status(); - break; - case 7: - await modfifyAddresses(); - await usdTieredSTO_status(); - break; - case 8: - await modfifyLimits(); - await usdTieredSTO_status(); - break; - case 9: - await modfifyFunding(); - await usdTieredSTO_status(); - break; - } - } + let divisible = readlineSync.question('Press "N" for Non-divisible type token or hit Enter for divisible type token (Default): '); + divisibility = (divisible != 'N' && divisible != 'n'); } -} - -async function modfifyTimes() { - let times = timesConfigUSDTieredSTO(); - let modifyTimesAction = currentSTO.methods.modifyTimes(times.startTime, times.endTime); - await common.sendTransaction(Issuer, modifyTimesAction, defaultGasPrice); -} - -async function modfifyLimits() { - let limits = limitsConfigUSDTieredSTO(); - let modifyLimitsAction = currentSTO.methods.modifyLimits(limits.nonAccreditedLimitUSD, limits.minimumInvestmentUSD); - await common.sendTransaction(Issuer, modifyLimitsAction, defaultGasPrice); -} -async function modfifyFunding() { - let funding = fundingConfigUSDTieredSTO(); - let modifyFundingAction = currentSTO.methods.modifyFunding(funding.raiseType); - await common.sendTransaction(Issuer, modifyFundingAction, defaultGasPrice); -} - -async function modfifyAddresses() { - let addresses = addressesConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES.DAI).call()); - let modifyAddressesAction = currentSTO.methods.modifyAddresses(addresses.wallet, addresses.reserveWallet, addresses.usdToken); - await common.sendTransaction(Issuer, modifyAddressesAction, defaultGasPrice); -} - -async function modfifyTiers() { - let tiers = tiersConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES.POLY).call()); - let modifyTiersAction = currentSTO.methods.modifyTiers( - tiers.ratePerTier, - tiers.ratePerTierDiscountPoly, - tiers.tokensPerTier, - tiers.tokensPerTierDiscountPoly, - ); - await common.sendTransaction(Issuer, modifyTiersAction, defaultGasPrice); + await approvePoly(securityTokenRegistryAddress, launchFee); + let generateSecurityTokenAction = securityTokenRegistry.methods.generateSecurityToken(tokenName, tokenSymbol, tokenDetails, divisibility); + let receipt = await common.sendTransaction(generateSecurityTokenAction); + let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, receipt.logs, 'NewSecurityToken'); + console.log(chalk.green(`Security Token has been successfully deployed at address ${event._securityTokenAddress}`)); } ////////////////////// // HELPER FUNCTIONS // ////////////////////// -async function getBalance(from, type) { - switch (type) { - case 'ETH': - return await web3.eth.getBalance(from); - case 'POLY': - return await polyToken.methods.balanceOf(from).call(); - case 'DAI': - return await usdToken.methods.balanceOf(from).call(); - } -} - -async function currentBalance(from) { - let balance = await polyToken.methods.balanceOf(from).call(); - let balanceInPoly = new BigNumber(balance).dividedBy(new BigNumber(10).pow(18)); - return balanceInPoly; -} - -async function selectTicker(includeCreate) { +async function selectTicker() { let result; - let userTickers = (await securityTokenRegistry.methods.getTickersByOwner(Issuer.address).call()).map(function (t) {return web3.utils.hexToAscii(t)}); + let userTickers = (await securityTokenRegistry.methods.getTickersByOwner(Issuer.address).call()).map(t => web3.utils.hexToAscii(t)); let options = await Promise.all(userTickers.map(async function (t) { let tickerDetails = await securityTokenRegistry.methods.getTickerDetails(t).call(); - let tickerInfo = tickerDetails[4] ? 'Token launched' : `Expires at: ${moment.unix(tickerDetails[2]).format('MMMM Do YYYY, HH:mm:ss')}`; + let tickerInfo; + if (tickerDetails[4]) { + tickerInfo = `Token launched at ${(await securityTokenRegistry.methods.getSecurityTokenAddress(t).call())}`; + } else { + tickerInfo = `Expires at ${moment.unix(tickerDetails[2]).format('MMMM Do YYYY, HH:mm:ss')}`; + } return `${t} ${tickerInfo}`; })); - if (includeCreate) { - options.push('Register a new ticker'); - } + options.push('Register a new ticker'); let index = readlineSync.keyInSelect(options, 'Select a ticker:'); if (index == -1) { process.exit(0); - } else if (includeCreate && index == options.length - 1) { + } else if (index == options.length - 1) { result = readlineSync.question('Enter a symbol for your new ticker: '); } else { result = userTickers[index]; @@ -1148,19 +194,19 @@ async function approvePoly(spender, fee) { return true; } else { let approveAction = polyToken.methods.approve(spender, web3.utils.toWei(fee.toString(), "ether")); - await common.sendTransaction(Issuer, approveAction, defaultGasPrice); + await common.sendTransaction(approveAction); } } else { - let requiredBalance = parseInt(requiredAmount) - parseInt(polyBalance); - console.log(chalk.red(`\n*****************************************************************************************************************************************`)); - console.log(chalk.red(`Not enough balance to Pay the Fee, Require ${(new BigNumber(requiredBalance).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY but have ${(new BigNumber(polyBalance).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY. Access POLY faucet to get the POLY to complete this txn`)); - console.log(chalk.red(`******************************************************************************************************************************************\n`)); - process.exit(0); + let requiredBalance = parseInt(requiredAmount) - parseInt(polyBalance); + console.log(chalk.red(`\n*****************************************************************************************************************************************`)); + console.log(chalk.red(`Not enough balance to Pay the Fee, Require ${(new BigNumber(requiredBalance).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY but have ${(new BigNumber(polyBalance).dividedBy(new BigNumber(10).pow(18))).toNumber()} POLY. Access POLY faucet to get the POLY to complete this txn`)); + console.log(chalk.red(`******************************************************************************************************************************************\n`)); + process.exit(0); } } module.exports = { - executeApp: async function(tokenConfig, mintingConfig, stoConfig, remoteNetwork) { - return executeApp(tokenConfig, mintingConfig, stoConfig, remoteNetwork); + executeApp: async function (ticker, transferOwnership, name, details, divisible) { + return executeApp(ticker, transferOwnership, name, details, divisible); } } diff --git a/CLI/commands/TickerRollForward.js b/CLI/commands/TickerRollForward.js index 97bd1f519..266749da1 100644 --- a/CLI/commands/TickerRollForward.js +++ b/CLI/commands/TickerRollForward.js @@ -3,15 +3,11 @@ 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'); ///////////////////////// ARTIFACTS ///////////////////////// var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); -////////////////////////////USER INPUTS////////////////////////////////////////// -let remoteNetwork = process.argv.slice(2)[0]; //batch size - ///////////////////////// GLOBAL VARS ///////////////////////// let ticker_data = []; let registered_tickers = []; @@ -24,9 +20,9 @@ let securityTokenRegistry; let securityTokenRegistryAddress; function Ticker(_owner, _symbol, _name) { - this.owner = _owner; - this.symbol = _symbol; - this.name = _name; + this.owner = _owner; + this.symbol = _symbol; + this.name = _name; } function FailedRegistration(_ticker, _error) { @@ -47,7 +43,6 @@ function FailedRegistration(_ticker, _error) { startScript(); async function startScript() { - await global.initialize(remoteNetwork); securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); @@ -63,11 +58,11 @@ async function startScript() { } async function readFile() { - var stream = fs.createReadStream("./CLI/data/ticker_data.csv"); + var stream = fs.createReadStream(`${__dirname}/../data/ticker_data.csv`); var csvStream = csv() .on("data", function (data) { - ticker_data.push(new Ticker(data[0],data[1],data[2],data[3])); + ticker_data.push(new Ticker(data[0], data[1], data[2], data[3])); }) .on("end", async function () { await registerTickers(); @@ -78,17 +73,17 @@ async function readFile() { async function registerTickers() { // Poly approval for registration fees let polyBalance = BigNumber(await polyToken.methods.balanceOf(Issuer.address).call()); - let fee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); + let fee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); let totalFee = BigNumber(ticker_data.length).mul(fee); if (totalFee.gt(polyBalance)) { console.log(chalk.red(`\n*******************************************************************************`)); - console.log(chalk.red(`Not enough POLY to pay registration fee. Require ${totalFee.div(10**18).toNumber()} POLY but have ${polyBalance.div(10**18).toNumber()} POLY.`)); + console.log(chalk.red(`Not enough POLY to pay registration fee. Require ${totalFee.div(10 ** 18).toNumber()} POLY but have ${polyBalance.div(10 ** 18).toNumber()} POLY.`)); console.log(chalk.red(`*******************************************************************************\n`)); process.exit(0); } else { let approveAction = polyToken.methods.approve(securityTokenRegistryAddress, totalFee); - let receipt = await common.sendTransaction(Issuer, approveAction, defaultGasPrice); + let receipt = await common.sendTransaction(approveAction); totalGas = totalGas.add(receipt.gasUsed); } @@ -105,7 +100,7 @@ async function registerTickers() { } // validate ticker - await securityTokenRegistry.methods.getTickerDetails(ticker_data[i].symbol).call({}, function(error, result){ + await securityTokenRegistry.methods.getTickerDetails(ticker_data[i].symbol).call({}, function (error, result) { if (result[1] != 0) { failed_tickers.push(` ${i} is already registered`); valid = false; @@ -115,7 +110,7 @@ async function registerTickers() { if (valid) { try { let registerTickerAction = securityTokenRegistry.methods.registerTicker(owner, ticker_data[i].symbol, ticker_data[i].name); - let receipt = await common.sendTransaction(Issuer, registerTickerAction, defaultGasPrice); + let receipt = await common.sendTransaction(registerTickerAction); registered_tickers.push(ticker_data[i]); console.log(ticker_data[i]); totalGas = totalGas.add(receipt.gasUsed); @@ -136,7 +131,7 @@ async function logResults() { Successful registrations: ${registered_tickers.length} Failed registrations: ${failed_tickers.length} Total gas consumed: ${totalGas} - Total gas cost: ${defaultGasPrice.mul(totalGas).div(10**18)} ETH + Total gas cost: ${defaultGasPrice.mul(totalGas).div(10 ** 18)} ETH List of failed registrations: ${failed_tickers} diff --git a/CLI/commands/accredit.js b/CLI/commands/accredit.js deleted file mode 100644 index c4dbd78b6..000000000 --- a/CLI/commands/accredit.js +++ /dev/null @@ -1,148 +0,0 @@ -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 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() { - if (remoteNetwork == 'undefined') remoteNetwork = undefined; - await global.initialize(remoteNetwork); - try { - 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/accredited_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 isAccredited = (typeof JSON.parse(data[1].toLowerCase())) == "boolean" ? JSON.parse(data[1].toLowerCase()) : "not-valid"; - - if (isAddress && (isAccredited != "not-valid") ) { - let userArray = new Array() - let checksummedAddress = web3.utils.toChecksumAddress(data[0]); - - userArray.push(checksummedAddress) - userArray.push(isAccredited) - - 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(isAccredited); - - badData.push(userArray); - fullFileData.push(userArray) - } - }) - .on("end", function () { - //Add last remainder batch - distribData.push(allocData); - allocData = []; - - changeAccredited(); - }); - - stream.pipe(csvStream); -} - -// MAIN FUNCTION COMMUNICATING TO BLOCKCHAIN -async function changeAccredited() { - // 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.getModulesByName(web3.utils.toHex('USDTieredSTO')).call(); - if (result.length > 0) { - let usdTieredSTOABI = abis.usdTieredSTO(); - usdTieredSTO = new web3.eth.Contract(usdTieredSTOABI, result[0]); - console.log(` -------------------------------------------------------- ------ Sending accreditation 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 isAccreditedArray = []; - - //splitting the user arrays to be organized by input - for (let j = 0; j < distribData[i].length; j++) { - investorArray.push(distribData[i][j][0]) - isAccreditedArray.push(distribData[i][j][1]) - } - - let changeAccreditedAction = usdTieredSTO.methods.changeAccredited(investorArray, isAccreditedArray); - let r = await common.sendTransaction(Issuer, changeAccreditedAction, defaultGasPrice); - console.log(`Batch ${i} - Attempting to change accredited 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"), "Ether"); - console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n\n"); - } catch (err) { - console.log("ERROR:", err); - } - } - } else { - console.log(chalk.red(`There is no USDTieredSTO 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/changeNonAccreditedLimit.js b/CLI/commands/changeNonAccreditedLimit.js deleted file mode 100644 index b1ececbc7..000000000 --- a/CLI/commands/changeNonAccreditedLimit.js +++ /dev/null @@ -1,148 +0,0 @@ -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 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() { - if (remoteNetwork == 'undefined') remoteNetwork = undefined; - await global.initialize(remoteNetwork); - try { - 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.getModulesByName(web3.utils.toHex('USDTieredSTO')).call(); - if (result.length > 0) { - let usdTieredSTOABI = abis.usdTieredSTO(); - usdTieredSTO = new web3.eth.Contract(usdTieredSTOABI, result[0]); - 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(`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/common/common_functions.js b/CLI/commands/common/common_functions.js index af3cdc8fa..7b21e555f 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,116 +15,133 @@ 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 + } else { + let result = await securityToken.methods.checkPermission(Issuer.address, contractRegistry.options.address, web3.utils.asciiToHex(permission)).call(); + return result } - let result = await securityToken.methods.checkPermission(Issuer.address, contractRegistry.options.address, web3.utils.asciiToHex(permission)).call(); - return result } }; +function getFinalOptions(options) { + if (typeof options != "object") { + options = {} + } + const defaultOptions = { + from: Issuer, + gasPrice: defaultGasPrice, + value: undefined, + factor: 1.2, + minNonce: 0 + } + return Object.assign(defaultOptions, options) +}; + +async function getGasLimit(options, action) { + let block = await web3.eth.getBlock('latest'); + let networkGasLimit = block.gasLimit; + let gas = Math.round(options.factor * (await action.estimateGas({ from: options.from.address, value: options.value }))); + return (gas > networkGasLimit) ? networkGasLimit : gas; +} + +async function checkPermissions(action) { + let contractRegistry = await 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 parentModule = await moduleRegistry.methods.getName().call(); + let result = await checkPermission(web3.utils.hexToUtf8(parentModule), action._method.name, contractRegistry); + if (!result) { + console.log("You haven't the right permissions to execute this method."); + process.exit(0); + } + } + return +} + module.exports = { convertToDaysRemaining: function (timeRemaining) { var seconds = parseInt(timeRemaining, 10); - + var days = Math.floor(seconds / (3600 * 24)); - seconds -= days * 3600 * 24; - var hrs = Math.floor(seconds / 3600); - seconds -= hrs * 3600; + seconds -= days * 3600 * 24; + var hrs = Math.floor(seconds / 3600); + seconds -= hrs * 3600; var mnts = Math.floor(seconds / 60); - seconds -= mnts * 60; + seconds -= mnts * 60; return (days + " days, " + hrs + " Hrs, " + mnts + " Minutes, " + seconds + " Seconds"); }, - logAsciiBull: function() { - console.log(` -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(@(&&@@@@@@@@@@@@@@@@@@@@@@@@@@(((@&&&&(/@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(#(((((((#%%%#@@@@@@@@@@@@@@@@@@@@%##(((/@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(%(((((((((((#%%%%%@#@@@@@@@@@@@@(&#####@@@@@@@@%& -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#((((((((((((((##%%%%%%%&&&%%##@%#####%(@@@@@@@#%#& -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(%((((((((((((((((((###%%%%%((#####%%%####@@@@@@@###((@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(#(((((((((((((((((((((####%%%#((((######%&%@@(##&###(@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(#((((((((((((((((((((((((####%%#(((((((#((((((((((((#(@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(%(((((((((((((((((((((((((((######%(((((((((((((#&(/@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#(((((((((((((((((((((((((((((((###############(##%%#@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(#((((##############(((((((((((((((((###################%@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@(&#((#(##################((((((((((((((((((##%%##############@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@/%#(((((((##%((((##############((((((((((((((((((##%%#############%%@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@((((((((((###%%((((((##########(((((((((((((((((((#%%%############%%%#@@@@@@@@@ -@@@@@@@@@@@@@@@@@@%((((((((((####%%%((((((((#######(((((((((((####(((((@%%############%%%#@@@@@@@@@ -@@@@@@@@@####%%%%%#(((((((((#####%%%%(((((((((((###((((#######(((((((((&@@(&#########%%%%&@@@@@@@@@ -@@@@@@@@&(((#####%###(((((((#####%%%%%((((((((####%%%%%%%%&%@%#((((((((@@@@@@(#(####%%%%%%@@@@@@@@@ -@@@@@@@&(((@@@@@@@####(((((######%%%%%%##&########%%%%%#@@@@###(((((#(@@@@@@@@@@@###%%#@@@@@@@@@@@@ -@@@#%&%(((@@@@@@@@#####(((#######%%%%@@@@@@@@@@@((##@@@@@@@@%###((((/@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@#%%&%#@@@@@@@@@@############%%%%@@@@@@@@@@@@@@@@@@@@(@&&&&#####(#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@#%%%%%#((%%%%%%#@@@@@@@@@@@@@@@@@@@@(####%((((%#(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@&%%%#((((((%%&@@@@@@@@@@@@@@@@@@@@@@###%%#((@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@%%%%((((((((& @@@@@@@@@@@@@@@@@@@@@@@%%&%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@%%(((((&#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@&((###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@&####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@&&%##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@&&&%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@%##%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@#%####%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + logAsciiBull: function () { + console.log(` + /######%%, /#( + ##########%%%%%, ,%%%. % + *#############%%%%%##%%%%%%# ## + (################%%%%#####%%%%//###%, + .####################%%%%#########/ + (#########%%############%%%%%%%%%#%%% + ,(%#%%%%%%%%%%%%############%%%%%%%###%%%. + (######%%###%%%%%%%%##############%%%%%####%%%* + /#######%%%%######%%%%##########%###,.%######%%%( + #%%%%%#######%%%%%%###########%%%%%*###### /####%%%# + #. ,%%####%%%%%%%(/#%%%%%%%%( #%#### ,#%/ + *#%( .%%%##%%%%%% .%%%#* + .%%%%#%%%% .%%%###( + %%%#####% (%%. + #%###(, + *#%# + %%# + * + &% + %%%. `); }, - sendTransaction: async function (from, action, gasPrice, value, factor) { - let contractRegistry = await 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 parentModule = await moduleRegistry.methods.getName().call(); - let result = await checkPermission(web3.utils.hexToUtf8(parentModule), action._method.name, contractRegistry); - if (!result) { - console.log("You haven't the right permissions to execute this method."); - process.exit(0); - } - } - - if (typeof factor === 'undefined') factor = 1.2; + getNonce: async function (from) { + return (await web3.eth.getTransactionCount(from.address, "pending")); + }, + sendTransaction: async function (action, options) { + await checkPermissions(action); - let block = await web3.eth.getBlock("latest"); - let networkGasLimit = block.gasLimit; + options = getFinalOptions(options); + let gasLimit = await getGasLimit(options, action); - let gas = Math.round(factor * (await action.estimateGas({ from: from.address, value: value}))); - if (gas > networkGasLimit) gas = networkGasLimit; - - console.log(chalk.black.bgYellowBright(`---- Transaction executed: ${action._method.name} - Gas limit provided: ${gas} ----`)); + console.log(chalk.black.bgYellowBright(`---- Transaction executed: ${action._method.name} - Gas limit provided: ${gasLimit} ----`)); - let nonce = await web3.eth.getTransactionCount(from.address); + let nonce = await web3.eth.getTransactionCount(options.from.address); + if (nonce < options.minNonce) { + nonce = minNonce; + } let abi = action.encodeABI(); let parameter = { - from: from.address, + from: options.from.address, to: action._parent._address, data: abi, - gasLimit: gas, - gasPrice: gasPrice, + gasLimit: gasLimit, + gasPrice: options.gasPrice, nonce: nonce, - value: web3.utils.toHex(value) + value: web3.utils.toHex(options.value) }; - + const transaction = new Tx(parameter); - transaction.sign(Buffer.from(from.privateKey.replace('0x', ''), 'hex')); + transaction.sign(Buffer.from(options.from.privateKey.replace('0x', ''), 'hex')); return await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex')) - .on('transactionHash', function(hash){ - console.log(` + .on('transactionHash', function (hash) { + console.log(` Your transaction is being processed. Please wait... TxHash: ${hash}` - ); - }) - .on('receipt', function(receipt){ - console.log(` + ); + }) + .on('receipt', function (receipt) { + console.log(` Congratulations! The transaction was successfully completed. - Gas used: ${receipt.gasUsed} - Gas spent: ${web3.utils.fromWei((new web3.utils.BN(gasPrice)).mul(new web3.utils.BN(receipt.gasUsed)))} Ether + Gas used: ${receipt.gasUsed} - Gas spent: ${web3.utils.fromWei((new web3.utils.BN(options.gasPrice)).mul(new web3.utils.BN(receipt.gasUsed)))} Ether Review it on Etherscan. TxHash: ${receipt.transactionHash}\n` - ); - }); + ); + }); }, getEventFromLogs: function (jsonInterface, logs, eventName) { let eventJsonInterface = jsonInterface.find(o => o.name === eventName && o.type === 'event'); @@ -135,5 +152,25 @@ 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) + }, + splitIntoBatches: function (data, batchSize) { + let allBatches = []; + for (let index = 0; index < data.length; index += parseInt(batchSize)) { + allBatches.push(data.slice(index, index + parseInt(batchSize))); + } + return allBatches; + }, + transposeBatches: function (batches) { + let result = []; + if (batches.length > 0 && batches[0].length > 0) { + let columns = batches[0][0].length; + for (let index = 0; index < columns; index++) { + result[index] = batches.map(batch => batch.map(record => record[index])); + } + } + return result; } }; diff --git a/CLI/commands/common/constants.js b/CLI/commands/common/constants.js new file mode 100644 index 000000000..d121fd2da --- /dev/null +++ b/CLI/commands/common/constants.js @@ -0,0 +1,36 @@ +module.exports = Object.freeze({ + MODULES_TYPES: { + PERMISSION: 1, + TRANSFER: 2, + STO: 3, + DIVIDENDS: 4, + BURN: 5 + }, + DURATION: { + seconds: function (val) { + return val + }, + minutes: function (val) { + return val * this.seconds(60) + }, + hours: function (val) { + return val * this.minutes(60) + }, + days: function (val) { + return val * this.hours(24) + }, + weeks: function (val) { + return val * this.days(7) + }, + years: function (val) { + return val * this.days(365) + } + }, + FUND_RAISE_TYPES: { + ETH: 0, + POLY: 1, + STABLE: 2 + }, + DEFAULT_BATCH_SIZE: 75, + ADDRESS_ZERO: '0x0000000000000000000000000000000000000000' +}); \ No newline at end of file diff --git a/CLI/commands/common/global.js b/CLI/commands/common/global.js index fb898a708..51972ea92 100644 --- a/CLI/commands/common/global.js +++ b/CLI/commands/common/global.js @@ -1,39 +1,55 @@ const Web3 = require('web3'); +const constants = require('./constants'); -function getGasPrice (networkId) { - let gasPrice; - switch (networkId) { - case 1: //Mainnet - gasPrice = 4000000000; - break; - case 3: //Ropsten - gasPrice = 50000000000; - break; - case 15: //Ganache - gasPrice = 50000000000; - break; - case 42: //Kovan - gasPrice = 50000000000; - break; - default: - throw new Error('Network ID not identified'); - } +global.web3, global.Issuer, global.defaultGasPrice, global.remoteNetwork; + +function getGasPrice(networkId) { + let gasPrice; + switch (networkId) { + case 1: //Mainnet + gasPrice = 4000000000; + break; + case 3: //Ropsten + gasPrice = 50000000000; + break; + case 15: //Ganache + gasPrice = 50000000000; + break; + case 42: //Kovan + gasPrice = 50000000000; + break; + default: + throw new Error('Network ID not identified'); + } + return gasPrice; +} - return gasPrice; +function providerValidator(url) { + var expression = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g; + var regex = new RegExp(expression); + return url.match(regex); } + +async function httpProvider(url, file) { + web3 = new Web3(new Web3.providers.HttpProvider(url)); + Issuer = await web3.eth.accounts.privateKeyToAccount("0x" + require('fs').readFileSync(file).toString()); +} + module.exports = { - initialize: async function (remoteNetwork) { - if (typeof web3 === 'undefined' || typeof Issuer === 'undefined' || typeof defaultGasPrice === 'undefined') { - if (typeof remoteNetwork !== 'undefined') { - web3 = new Web3(new Web3.providers.HttpProvider(`https://${remoteNetwork}.infura.io/`)); - let privKey = require('fs').readFileSync('./privKey').toString(); - Issuer = await web3.eth.accounts.privateKeyToAccount("0x" + privKey); - } else { - web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); - let privKeyLocal = require('fs').readFileSync('./privKeyLocal').toString() - Issuer = await web3.eth.accounts.privateKeyToAccount("0x" + privKeyLocal); + initialize: async function (network) { + remoteNetwork = network; + if (typeof web3 === 'undefined' || typeof Issuer === 'undefined' || typeof defaultGasPrice === 'undefined') { + if (typeof remoteNetwork !== 'undefined') { + if (!providerValidator(remoteNetwork)) { + console.log("Invalid remote node") + process.exit(0) } - defaultGasPrice = getGasPrice(await web3.eth.net.getId()); + await httpProvider(remoteNetwork, './privKey'); + } else { + await httpProvider("http://localhost:8545", './privKeyLocal'); } + defaultGasPrice = getGasPrice(await web3.eth.net.getId()); } + }, + constants }; \ No newline at end of file diff --git a/CLI/commands/common/permissions_list.js b/CLI/commands/common/permissions_list.js index 4d2b58cf7..8f74c15e7 100644 --- a/CLI/commands/common/permissions_list.js +++ b/CLI/commands/common/permissions_list.js @@ -60,9 +60,7 @@ function getPermissionList() { }, ManualApprovalTransferManager: { addManualApproval: "TRANSFER_APPROVAL", - addManualBlocking: "TRANSFER_APPROVAL", revokeManualApproval: "TRANSFER_APPROVAL", - revokeManualBlocking: "TRANSFER_APPROVAL" }, PercentageTransferManager: { modifyWhitelist: "WHITELIST", @@ -70,36 +68,60 @@ function getPermissionList() { setAllowPrimaryIssuance: "ADMIN", changeHolderPercentage: "ADMIN" }, - LockupVolumeRestrictionTM: { - addLockup: "ADMIN", - addLockUpMulti: "ADMIN", - removeLockUp: "ADMIN", - modifyLockUp: "ADMIN" + VolumeRestrictionTM: { + changeExemptWalletList: "ADMIN", + addIndividualRestriction: "ADMIN", + addIndividualRestrictionMulti: "ADMIN", + addGlobalRestriction: "ADMIN", + addDailyGlobalRestriction: "ADMIN", + removeIndividualRestriction: "ADMIN", + removeIndividualRestrictionMulti: "ADMIN", + removeGlobalRestriction: "ADMIN", + removeDailyGlobalRestriction: "ADMIN", + modifyIndividualRestriction: "ADMIN", + modifyIndividualRestrictionMulti: "ADMIN", + modifyGlobalRestriction: "ADMIN", + modifyDailyGlobalRestriction: "ADMIN" }, - SingleTradeVolumeRestrictionTM: { - setAllowPrimaryIssuance: "ADMIN", - changeTransferLimitToPercentage: "ADMIN", - changeTransferLimitToTokens: "ADMIN", - changeGlobalLimitInTokens: "ADMIN", - changeGlobalLimitInPercentage: "ADMIN", - addExemptWallet: "ADMIN", - removeExemptWallet: "ADMIN", - addExemptWalletMulti: "ADMIN", - removeExemptWalletMulti: "ADMIN", - setTransferLimitInTokens: "ADMIN", - setTransferLimitInPercentage: "ADMIN", - removeTransferLimitInPercentage: "ADMIN", - removeTransferLimitInTokens: "ADMIN", - setTransferLimitInTokensMulti: "ADMIN", - setTransferLimitInPercentageMulti: "ADMIN", - removeTransferLimitInTokensMulti: "ADMIN", - removeTransferLimitInPercentageMulti: "ADMIN" + BlacklistTransferManager: { + addBlacklistType: "ADMIN", + addBlacklistTypeMulti: "ADMIN", + modifyBlacklistType: "ADMIN", + modifyBlacklistTypeMulti: "ADMIN", + deleteBlacklistType: "ADMIN", + deleteBlacklistTypeMulti: "ADMIN", + addInvestorToBlacklist: "ADMIN", + addInvestorToBlacklistMulti: "ADMIN", + addMultiInvestorToBlacklistMulti: "ADMIN", + addInvestorToNewBlacklist: "ADMIN", + deleteInvestorFromAllBlacklist: "ADMIN", + deleteInvestorFromAllBlacklistMulti: "ADMIN", + deleteInvestorFromBlacklist: "ADMIN", + deleteMultiInvestorsFromBlacklistMulti: "ADMIN", + }, + VestingEscrowWallet: { + changeTreasuryWallet: "ONLY_OWNER", + depositTokens: "ADMIN", + sendToTreasury: "ADMIN", + pushAvailableTokens: "ADMIN", + addTemplate: "ADMIN", + removeTemplate: "ADMIN", + addSchedule: "ADMIN", + addScheduleFromTemplate: "ADMIN", + modifySchedule: "ADMIN", + revokeSchedule: "ADMIN", + revokeAllSchedules: "ADMIN", + pushAvailableTokensMulti: "ADMIN", + addScheduleMulti: "ADMIN", + addScheduleFromTemplateMulti: "ADMIN", + revokeSchedulesMulti: "ADMIN", + modifyScheduleMulti: "ADMIN" } } } module.exports = { - verifyPermission: function(contractName, functionName) { + verifyPermission: function (contractName, functionName) { let list = getPermissionList(); try { return list[contractName][functionName] diff --git a/CLI/commands/contract_manager.js b/CLI/commands/contract_manager.js index bea83ce7d..cce93444e 100644 --- a/CLI/commands/contract_manager.js +++ b/CLI/commands/contract_manager.js @@ -1,23 +1,14 @@ -const duration = { - seconds: function (val) { return val; }, - minutes: function (val) { return val * this.seconds(60); }, - hours: function (val) { return val * this.minutes(60); }, - days: function (val) { return val * this.hours(24); }, - weeks: function (val) { return val * this.days(7); }, - years: function (val) { return val * this.days(365); }, -}; var readlineSync = require('readline-sync'); var chalk = require('chalk'); var common = require('./common/common_functions'); -var global = require('./common/global'); +var gbl = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); // App flow let currentContract = null; -async function executeApp(remoteNetwork) { - await global.initialize(remoteNetwork); +async function executeApp() { common.logAsciiBull(); console.log("*********************************************"); @@ -108,7 +99,7 @@ async function strActions() { let tickerExpiryDate = readlineSync.question(`Enter the Unix Epoch time on wich the ticker will expire: `); let tickerStatus = readlineSync.keyInYNStrict(`Is the token deployed?`); let modifyTickerAction = currentContract.methods.modifyTicker(tickerOwner, tickerToModify, tickerSTName, tickerRegistrationDate, tickerExpiryDate, tickerStatus); - await common.sendTransaction(Issuer, modifyTickerAction, defaultGasPrice, 0, 1.5); + await common.sendTransaction(modifyTickerAction, {factor: 1.5}); console.log(chalk.green(`Ticker has been updated successfully`)); break; case 'Remove Ticker': @@ -118,7 +109,7 @@ async function strActions() { console.log(chalk.yellow(`${ticker} does not exist.`)); } else { let removeTickerAction = currentContract.methods.removeTicker(tickerToRemove); - await common.sendTransaction(Issuer, removeTickerAction, defaultGasPrice, 0, 3); + await common.sendTransaction(removeTickerAction, {factor: 3}); console.log(chalk.green(`Ticker has been removed successfully`)); } break; @@ -159,15 +150,15 @@ async function strActions() { let tokenDetails = readlineSync.question(`Enter the token details: `); let deployedAt = readlineSync.questionInt(`Enter the Unix Epoch timestamp at which security token was deployed: `); let modifySTAction = currentContract.methods.modifySecurityToken(name, ticker, owner, stAddress, tokenDetails, deployedAt); - await common.sendTransaction(Issuer, modifySTAction, defaultGasPrice, 0, 1.5); + await common.sendTransaction(modifySTAction, {factor: 1.5}); console.log(chalk.green(`Security Token has been updated successfully`)); break; case 'Change Expiry Limit': let currentExpiryLimit = await currentContract.methods.getExpiryLimit().call(); console.log(chalk.yellow(`Current expiry limit is ${Math.floor(parseInt(currentExpiryLimit)/60/60/24)} days`)); - let newExpiryLimit = duration.days(readlineSync.questionInt('Enter a new value in days for expiry limit: ')); + let newExpiryLimit = gbl.constants.DURATION.days(readlineSync.questionInt('Enter a new value in days for expiry limit: ')); let changeExpiryLimitAction = currentContract.methods.changeExpiryLimit(newExpiryLimit); - let changeExpiryLimitReceipt = await common.sendTransaction(Issuer, changeExpiryLimitAction, defaultGasPrice); + let changeExpiryLimitReceipt = await common.sendTransaction(changeExpiryLimitAction); let changeExpiryLimitEvent = common.getEventFromLogs(currentContract._jsonInterface, changeExpiryLimitReceipt.logs, 'ChangeExpiryLimit'); console.log(chalk.green(`Expiry limit was changed successfully. New limit is ${Math.floor(parseInt(changeExpiryLimitEvent._newExpiry)/60/60/24)} days\n`)); break; @@ -176,7 +167,7 @@ async function strActions() { console.log(chalk.yellow(`\nCurrent ticker registration fee is ${currentRegFee} POLY`)); let newRegFee = web3.utils.toWei(readlineSync.questionInt('Enter a new value in POLY for ticker registration fee: ').toString()); let changeRegFeeAction = currentContract.methods.changeTickerRegistrationFee(newRegFee); - let changeRegFeeReceipt = await common.sendTransaction(Issuer, changeRegFeeAction, defaultGasPrice); + let changeRegFeeReceipt = await common.sendTransaction(changeRegFeeAction); let changeRegFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeRegFeeReceipt.logs, 'ChangeTickerRegistrationFee'); console.log(chalk.green(`Fee was changed successfully. New fee is ${web3.utils.fromWei(changeRegFeeEvent._newFee)} POLY\n`)); break; @@ -185,7 +176,7 @@ async function strActions() { console.log(chalk.yellow(`\nCurrent ST launch fee is ${currentLaunchFee} POLY`)); let newLaunchFee = web3.utils.toWei(readlineSync.questionInt('Enter a new value in POLY for ST launch fee: ').toString()); let changeLaunchFeeAction = currentContract.methods.changeSecurityLaunchFee(newLaunchFee); - let changeLaunchFeeReceipt = await common.sendTransaction(Issuer, changeLaunchFeeAction, defaultGasPrice); + let changeLaunchFeeReceipt = await common.sendTransaction(changeLaunchFeeAction); let changeLaunchFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeLaunchFeeReceipt.logs, 'ChangeSecurityLaunchFee'); console.log(chalk.green(`Fee was changed successfully. New fee is ${web3.utils.fromWei(changeLaunchFeeEvent._newFee)} POLY\n`)); break; @@ -197,7 +188,7 @@ async function strActions() { } module.exports = { - executeApp: async function(remoteNetwork) { - return executeApp(remoteNetwork); + executeApp: async function() { + return executeApp(); } } \ No newline at end of file diff --git a/CLI/commands/dividends_manager.js b/CLI/commands/dividends_manager.js index bde3aa900..0b4dd8f39 100644 --- a/CLI/commands/dividends_manager.js +++ b/CLI/commands/dividends_manager.js @@ -1,509 +1,667 @@ -const duration = { - seconds: function (val) { return val; }, - minutes: function (val) { return val * this.seconds(60); }, - hours: function (val) { return val * this.minutes(60); }, - days: function (val) { return val * this.hours(24); }, - weeks: function (val) { return val * this.days(7); }, - years: function (val) { return val * this.days(365); }, -}; -var readlineSync = require('readline-sync'); -var chalk = require('chalk'); -var moment = require('moment'); -var common = require('./common/common_functions'); -var global = require('./common/global'); -var contracts = require('./helpers/contract_addresses'); -var abis = require('./helpers/contract_abis'); - -const MODULES_TYPES = { - PERMISSION: 1, - TRANSFER: 2, - STO: 3, - DIVIDENDS: 4, - BURN: 5 -} +const readlineSync = require('readline-sync'); +const chalk = require('chalk'); +const moment = require('moment'); +const common = require('./common/common_functions'); +const gbl = require('./common/global'); +const contracts = require('./helpers/contract_addresses'); +const abis = require('./helpers/contract_abis'); +const csvParse = require('./helpers/csv'); +const { table } = require('table') + +const EXCLUSIONS_DATA_CSV = `${__dirname}/../data/Checkpoint/exclusions_data.csv`; +const TAX_WITHHOLDING_DATA_CSV = `${__dirname}/../data/Checkpoint/tax_withholding_data.csv`; // App flow let tokenSymbol; let securityToken; let polyToken; let securityTokenRegistry; -let generalTransferManager; +let moduleRegistry; let currentDividendsModule; -async function executeApp(type, remoteNetwork) { - dividendsType = type; - await global.initialize(remoteNetwork); +let dividendsType; - common.logAsciiBull(); - console.log("**********************************************"); - console.log("Welcome to the Command-Line Dividends Manager."); - console.log("**********************************************"); - console.log("Issuer Account: " + Issuer.address + "\n"); +async function executeApp() { + console.log('\n', chalk.blue('Dividends Manager - Main Menu', '\n')); - await setup(); - try { - await start_explorer(); - } catch (err) { - console.log(err); - return; + let tmModules = await getAllModulesByType(gbl.constants.MODULES_TYPES.DIVIDENDS); + let nonArchivedModules = tmModules.filter(m => !m.archived); + if (nonArchivedModules.length > 0) { + console.log(`Dividends modules attached:`); + nonArchivedModules.map(m => console.log(`- ${m.name} at ${m.address}`)) + } else { + console.log(`There are no dividends modules attached`); } -}; -async function setup(){ - try { - let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); - let securityTokenRegistryABI = abis.securityTokenRegistry(); - securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); - securityTokenRegistry.setProvider(web3.currentProvider); + let currentCheckpoint = await securityToken.methods.currentCheckpointId().call(); + if (currentCheckpoint > 0) { + console.log(`\nCurrent checkpoint: ${currentCheckpoint}`); + } - 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); + let options = ['Create checkpoint', 'Explore address balances']; + if (nonArchivedModules.length > 0) { + options.push('Config existing modules'); + } + options.push('Add new dividends module'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'EXIT' }); + let optionSelected = index != -1 ? options[index] : 'EXIT'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Create checkpoint': + await createCheckpointFromST(); + break; + case 'Explore address balances': + await exploreAddress(currentCheckpoint); + break; + case 'Config existing modules': + await configExistingModules(nonArchivedModules); + break; + case 'Add new dividends module': + await addDividendsModule(); + break; + case 'EXIT': + return; } -} -async function start_explorer(){ - console.log('\n\x1b[34m%s\x1b[0m',"Dividends Manager - Main Menu"); + await executeApp(); +} - if (!tokenSymbol) - tokenSymbol = readlineSync.question('Enter the token symbol: '); +async function createCheckpointFromST() { + let createCheckpointAction = securityToken.methods.createCheckpoint(); + let receipt = await common.sendTransaction(createCheckpointAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'CheckpointCreated'); + console.log(chalk.green(`Checkpoint ${event._checkpointId} has been created successfully!`)); +} - 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.`)); - } else { - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI,result); +async function exploreAddress(currentCheckpoint) { + let address = readlineSync.question('Enter address to explore: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let checkpoint = null; + if (currentCheckpoint > 0) { + checkpoint = await selectCheckpoint(false); + } - // Get the GTM - result = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); - if (result.length == 0) { - console.log(chalk.red(`General Transfer Manager is not attached.`)); - } else { - generalTransferManagerAddress = result[0]; - let generalTransferManagerABI = abis.generalTransferManager(); - generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, generalTransferManagerAddress); - generalTransferManager.setProvider(web3.currentProvider); - - let typeOptions = ['POLY', 'ETH']; - if (!typeOptions.includes(dividendsType)) { - let index = readlineSync.keyInSelect(typeOptions, 'What type of dividends do you want work with?', {cancel: false}); - dividendsType = typeOptions[index]; - console.log(`Selected: ${dividendsType}`) - } + let balance = web3.utils.fromWei(await securityToken.methods.balanceOf(address).call()); + let totalSupply = web3.utils.fromWei(await securityToken.methods.totalSupply().call()); + console.log(`Balance of ${address} is: ${balance} ${tokenSymbol}`); + console.log(`TotalSupply is: ${totalSupply} ${tokenSymbol}`); - let currentCheckpoint = await securityToken.methods.currentCheckpointId().call(); - console.log(chalk.yellow(`\nToken is at checkpoint: ${currentCheckpoint}`)); + if (checkpoint) { + let balanceAt = web3.utils.fromWei(await securityToken.methods.balanceOfAt(address, checkpoint).call()); + let totalSupplyAt = web3.utils.fromWei(await securityToken.methods.totalSupplyAt(checkpoint).call()); + console.log(`Balance of ${address} at checkpoint ${checkpoint}: ${balanceAt} ${tokenSymbol}`); + console.log(`TotalSupply at checkpoint ${checkpoint} is: ${totalSupplyAt} ${tokenSymbol}`); + } +} - let options = ['Mint tokens', 'Transfer tokens', 'Create checkpoint', 'Set default exclusions for dividends', 'Tax holding settings', 'Create dividends'] +async function configExistingModules(dividendModules) { + let options = dividendModules.map(m => `${m.name} at ${m.address}`); + let index = readlineSync.keyInSelect(options, 'Which module do you want to config? ', { cancel: 'RETURN' }); + console.log('Selected:', index != -1 ? options[index] : 'RETURN', '\n'); + let moduleNameSelected = index != -1 ? dividendModules[index].name : 'RETURN'; + switch (moduleNameSelected) { + case 'ERC20DividendCheckpoint': + currentDividendsModule = new web3.eth.Contract(abis.erc20DividendCheckpoint(), dividendModules[index].address); + currentDividendsModule.setProvider(web3.currentProvider); + dividendsType = 'ERC20'; + break; + case 'EtherDividendCheckpoint': + currentDividendsModule = new web3.eth.Contract(abis.etherDividendCheckpoint(), dividendModules[index].address); + currentDividendsModule.setProvider(web3.currentProvider); + dividendsType = 'ETH'; + break; + } - if (currentCheckpoint > 0) { - options.push('Explore account at checkpoint', 'Explore total supply at checkpoint') - } + await dividendsManager(); +} - // Only show dividend options if divididenModule is already attached - if (await isDividendsModuleAttached()) { - options.push('Push dividends to accounts', - `Explore ${dividendsType} balance`, 'Reclaim expired dividends') - } +async function dividendsManager() { + console.log(chalk.blue(`Dividends module at ${currentDividendsModule.options.address}`), '\n'); - let index = readlineSync.keyInSelect(options, 'What do you want to do?'); - let selected = index != -1 ? options[index] : 'Cancel'; - console.log('Selected:', selected, '\n'); - switch (selected) { - case 'Mint tokens': - let _to = readlineSync.question('Enter beneficiary of minting: '); - let _amount = readlineSync.question('Enter amount of tokens to mint: '); - await mintTokens(_to,_amount); - break; - case 'Transfer tokens': - let _to2 = readlineSync.question('Enter beneficiary of tranfer: '); - let _amount2 = readlineSync.question('Enter amount of tokens to transfer: '); - await transferTokens(_to2,_amount2); - break; - case 'Create checkpoint': - let createCheckpointAction = securityToken.methods.createCheckpoint(); - await common.sendTransaction(Issuer, createCheckpointAction, defaultGasPrice); - break; - case 'Set default exclusions for dividends': - await setDefaultExclusions(); - break; - case 'Tax holding settings': - await taxHoldingMenu(); - break; - case 'Create dividends': - let divName = readlineSync.question(`Enter a name or title to indetify this dividend: `); - let dividend = readlineSync.question(`How much ${dividendsType} would you like to distribute to token holders?: `); - await checkBalance(dividend); - let checkpointId = currentCheckpoint == 0 ? 0 : await selectCheckpoint(true); // If there are no checkpoints, it must create a new one - await createDividends(divName, dividend, checkpointId); - break; - case 'Explore account at checkpoint': - let _address = readlineSync.question('Enter address to explore: '); - let _checkpoint = await selectCheckpoint(false); - await exploreAddress(_address, _checkpoint); - break; - case 'Explore total supply at checkpoint': - let _checkpoint2 = await selectCheckpoint(false); - await exploreTotalSupply(_checkpoint2); - break; - case 'Push dividends to accounts': - let _dividend = await selectDividend({valid: true, expired: false, reclaimed: false, withRemaining: true}); - if (_dividend !== null) { - let _addresses = readlineSync.question('Enter addresses to push dividends to (ex- add1,add2,add3,...): '); - await pushDividends(_dividend, _addresses); - } - break; - case `Explore ${dividendsType} balance`: - let _address3 = readlineSync.question('Enter address to explore: '); - let _dividend3 = await selectDividend(); - if (_dividend3 !== null) { - let dividendAmounts = await currentDividendsModule.methods.calculateDividend(_dividend3.index, _address3).call(); - let dividendBalance = dividendAmounts[0]; - let dividendTax = dividendAmounts[1]; - let balance = await getBalance(_address3); - console.log(` - ${dividendsType} Balance: ${web3.utils.fromWei(balance)} ${dividendsType} - Dividends owned: ${web3.utils.fromWei(dividendBalance)} ${dividendsType} - Tax withheld: ${web3.utils.fromWei(dividendTax)} ${dividendsType} - `); - } - break; - case 'Reclaim expired dividends': - let _dividend4 = await selectDividend({expired: true, reclaimed: false}); - if (_dividend4 !== null) { - await reclaimedDividend(_dividend4); - } - break; - case 'Cancel': - process.exit(0); - break; - } - } - } - //Restart - await start_explorer(); -} + let wallet = await currentDividendsModule.methods.wallet().call(); + let currentDividends = await getDividends(); + let defaultExcluded = await currentDividendsModule.methods.getDefaultExcluded().call(); + let currentCheckpointId = await securityToken.methods.currentCheckpointId().call(); -async function mintTokens(address, amount){ - if (await securityToken.methods.mintingFrozen().call()) { - console.log(chalk.red("Minting is not possible - Minting has been permanently frozen by issuer")); - } else { - await whitelistAddress(address); + console.log(`- Wallet: ${wallet}`); + console.log(`- Current dividends: ${currentDividends.length}`); + console.log(`- Default exclusions: ${defaultExcluded.length}`); - try { - let mintAction = securityToken.methods.mint(address,web3.utils.toWei(amount)); - let receipt = await common.sendTransaction(Issuer, mintAction, defaultGasPrice); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); - console.log(` - Minted ${web3.utils.fromWei(event.value)} tokens - to account ${event.to}` - ); - } catch (err) { - console.log(err); - console.log(chalk.red("There was an error processing the transfer transaction. \n The most probable cause for this error is one of the involved accounts not being in the whitelist or under a lockup period.")); - } + let options = ['Change wallet', 'Create checkpoint']; + if (currentCheckpointId > 0) { + options.push('Explore checkpoint'); } -} - -async function transferTokens(address, amount){ - await whitelistAddress(address); + if (defaultExcluded.length > 0) { + options.push('Show current default exclusions'); + } + options.push( + 'Set default exclusions', + 'Set tax withholding' + ); + if (currentDividends.length > 0) { + options.push('Manage existing dividends'); + } + options.push('Create new dividends'); - try{ - let transferAction = securityToken.methods.transfer(address,web3.utils.toWei(amount)); - let receipt = await common.sendTransaction(Issuer, transferAction, defaultGasPrice, 0, 1.5); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); - console.log(` - Account ${event.from} - transferred ${web3.utils.fromWei(event.value)} tokens - to account ${event.to}` - ); - } catch (err) { - console.log(err); - console.log(chalk.red("There was an error processing the transfer transaction. \n The most probable cause for this error is one of the involved accounts not being in the whitelist or under a lockup period.")); + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let selected = index != -1 ? options[index] : 'RETURN'; + console.log('Selected:', selected, '\n'); + switch (selected) { + case 'Change wallet': + await changeWallet(); + break; + case 'Create checkpoint': + await createCheckpointFromDividendModule(); + break; + case 'Explore checkpoint': + await exploreCheckpoint(); + case 'Show current default exclusions': + showExcluded(defaultExcluded); + break; + case 'Set default exclusions': + await setDefaultExclusions(); + break; + case 'Set tax withholding': + await taxWithholding(); + break; + case 'Manage existing dividends': + let selectedDividend = await selectDividend(currentDividends); + if (selectedDividend) { + await manageExistingDividend(selectedDividend.index); + } + break; + case 'Create new dividends': + await createDividends(); + break; + case 'RETURN': + return; } -} -async function exploreAddress(address, checkpoint){ - let balance = await securityToken.methods.balanceOf(address).call(); - balance = web3.utils.fromWei(balance); - console.log(`Balance of ${address} is: ${balance} (Using balanceOf)`); + await dividendsManager(); +} - let balanceAt = await securityToken.methods.balanceOfAt(address,checkpoint).call(); - balanceAt = web3.utils.fromWei(balanceAt); - console.log(`Balance of ${address} is: ${balanceAt} (Using balanceOfAt - checkpoint ${checkpoint})`); +async function changeWallet() { + let newWallet = readlineSync.question('Enter the new account address to receive reclaimed dividends and tax: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let action = currentDividendsModule.methods.changeWallet(newWallet); + let receipt = await common.sendTransaction(action); + let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'SetWallet'); + console.log(chalk.green(`The wallet has been changed successfully!`)); } -async function exploreTotalSupply(checkpoint){ - let totalSupply = await securityToken.methods.totalSupply().call(); - totalSupply = web3.utils.fromWei(totalSupply); - console.log(`TotalSupply is: ${totalSupply} (Using totalSupply)`); +async function createCheckpointFromDividendModule() { + let createCheckpointAction = securityToken.methods.createCheckpoint(); + await common.sendTransaction(createCheckpointAction); + console.log(chalk.green(`Checkpoint have been created successfully!`)); +} - let totalSupplyAt = await securityToken.methods.totalSupplyAt(checkpoint).call(); - totalSupplyAt = web3.utils.fromWei(totalSupplyAt); - console.log(`TotalSupply is: ${totalSupplyAt} (Using totalSupplyAt - checkpoint ${checkpoint})`); +async function exploreCheckpoint() { + let checkpoint = await selectCheckpoint(false); + + let checkpointData = await currentDividendsModule.methods.getCheckpointData(checkpoint).call(); + let dataTable = [['Investor', `Balance at checkpoint (${tokenSymbol})`, 'Tax withholding set (%)']]; + for (let i = 0; i < checkpointData.investors.length; i++) { + dataTable.push([ + checkpointData.investors[i], + web3.utils.fromWei(checkpointData.balances[i]), + parseFloat(web3.utils.fromWei(checkpointData.withholdings[i])) * 100 + ]); + } + console.log(); + console.log(table(dataTable)); } async function setDefaultExclusions() { - await addDividendsModule(); - - let excluded = await currentDividendsModule.methods.getDefaultExcluded().call(); - showExcluded(excluded); - - console.log(chalk.yellow(`Excluded addresses will be loaded from 'dividendsExclusions_data.csv'. Please check your data before continue.`)); + console.log(chalk.yellow(`Excluded addresses will be loaded from 'exclusions_data.csv'. Please check your data before continue.`)); if (readlineSync.keyInYNStrict(`Do you want to continue?`)) { let excluded = getExcludedFromDataFile(); - let setDefaultExclusionsActions = currentDividendsModule.methods.setDefaultExcluded(excluded); - let receipt = await common.sendTransaction(Issuer, setDefaultExclusionsActions, defaultGasPrice); + let setDefaultExclusionsActions = currentDividendsModule.methods.setDefaultExcluded(excluded[0]); + let receipt = await common.sendTransaction(setDefaultExclusionsActions); let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'SetDefaultExcludedAddresses'); - console.log(chalk.green(`Exclusions were successfully set.`)); - showExcluded(event._excluded); + console.log(chalk.green(`Exclusions have been set successfully!`)); + showExcluded(event._excluded); } } -async function taxHoldingMenu() { - await addDividendsModule(); +async function manageExistingDividend(dividendIndex) { + // Show current data - let options = ['Set a % to withhold from dividends sent to an address', 'Withdraw withholding for dividend', 'Return to main menu']; - let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false}); - let selected = options[index]; - console.log("Selected:", selected); - switch (selected) { - case 'Set a % to withhold from dividends sent to an address': - let address = readlineSync.question('Enter the address of the investor: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - }); - let percentage = readlineSync.question('Enter the percentage of dividends to withhold (number between 0-100): ', { - limit: function(input) { - return (parseInt(input) >= 0 && parseInt(input) <= 100); - }, - limitMessage: "Must be a value between 0 and 100", - }); - let percentageWei = web3.utils.toWei((percentage / 100).toString()); - let setWithHoldingFixedAction = currentDividendsModule.methods.setWithholdingFixed([address], percentageWei); - let receipt = await common.sendTransaction(Issuer, setWithHoldingFixedAction, defaultGasPrice); - console.log(chalk.green(`Successfully set tax withholding of ${percentage}% for ${address}.`)); + let dividend = await currentDividendsModule.methods.dividends(dividendIndex).call(); + let dividendTokenAddress = gbl.constants.ADDRESS_ZERO; + let dividendTokenSymbol = 'ETH'; + if (dividendsType === 'ERC20') { + dividendTokenAddress = await currentDividendsModule.methods.dividendTokens(dividendIndex).call(); + let erc20token = new web3.eth.Contract(abis.erc20(), dividendTokenAddress); + dividendTokenSymbol = await erc20token.methods.symbol().call(); + } + let progress = await currentDividendsModule.methods.getDividendProgress(dividendIndex).call(); + let investorArray = progress[0]; + let claimedArray = progress[1]; + let excludedArray = progress[2]; + let withheldArray = progress[3]; + let amountArray = progress[4]; + let balanceArray = progress[5]; + + // function for adding two numbers. Easy! + const add = (a, b) => { + const bnA = new web3.utils.BN(a); + const bnB = new web3.utils.BN(b); + return bnA.add(bnB).toString(); + }; + // use reduce to sum our array + let taxesToWithHeld = withheldArray.reduce(add, 0); + let claimedInvestors = claimedArray.filter(c => c).length; + let excludedInvestors = excludedArray.filter(e => e).length; + + console.log(`- Name: ${web3.utils.hexToUtf8(dividend.name)}`); + console.log(`- Created: ${moment.unix(dividend.created).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- Maturity: ${moment.unix(dividend.maturity).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- Expiry: ${moment.unix(dividend.expiry).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- At checkpoint: ${dividend.checkpointId}`); + console.log(`- Amount: ${web3.utils.fromWei(dividend.amount)} ${dividendTokenSymbol}`); + console.log(`- Claimed amount: ${web3.utils.fromWei(dividend.claimedAmount)} ${dividendTokenSymbol}`); + console.log(`- Taxes:`); + console.log(` To withhold: ${web3.utils.fromWei(taxesToWithHeld)} ${dividendTokenSymbol}`); + console.log(` Withheld to-date: ${web3.utils.fromWei(dividend.totalWithheld)} ${dividendTokenSymbol}`); + console.log(` Withdrawn to-date: ${web3.utils.fromWei(dividend.totalWithheldWithdrawn)} ${dividendTokenSymbol}`); + console.log(`- Total investors: ${investorArray.length}`); + console.log(` Have already claimed: ${claimedInvestors} (${investorArray.length - excludedInvestors !== 0 ? claimedInvestors / (investorArray.length - excludedInvestors) * 100 : 0}%)`); + console.log(` Excluded: ${excludedInvestors} `); + // ------------------ + + + let options = ['Show investors', 'Show report', 'Explore account']; + if (isValidDividend(dividend) && hasRemaining(dividend) && !isExpiredDividend(dividend) && !dividend.reclaimed) { + options.push('Push dividends to accounts'); + } + if (hasRemainingWithheld(dividend)) { + options.push('Withdraw withholding'); + } + if (isExpiredDividend(dividend) && !dividend.reclaimed) { + options.push('Reclaim expired dividends'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Show investors': + showInvestors(investorArray, claimedArray, excludedArray); break; - case 'Withdraw withholding for dividend': - let _dividend = await selectDividend({withRemainingWithheld: true}); - if (_dividend !== null) { - let withdrawWithholdingAction = currentDividendsModule.methods.withdrawWithholding(_dividend.index); - let receipt = await common.sendTransaction(Issuer, withdrawWithholdingAction, defaultGasPrice); - let eventName; - if (dividendsType == 'POLY') { - eventName = 'ERC20DividendWithholdingWithdrawn'; - } else if (dividendsType == 'ETH') { - eventName = 'EtherDividendWithholdingWithdrawn'; - } - let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, eventName); - console.log(chalk.green(`Successfully withdrew ${web3.utils.fromWei(event._withheldAmount)} ${dividendsType} from dividend ${_dividend.index} tax withholding.`)); - } + case 'Show report': + showReport( + web3.utils.hexToUtf8(dividend.name), + dividendTokenSymbol, + dividend.amount, // Total amount of dividends sent + dividend.totalWithheld, // Total amount of taxes withheld + dividend.claimedAmount, // Total amount of dividends distributed + investorArray, // Per Address(Amount sent, Taxes withheld (%), Taxes withheld ($/ETH/# Tokens), Amount received, Withdrawn (TRUE/FALSE) + claimedArray, + excludedArray, + withheldArray, + amountArray + ); break; - case 'Return to main menu': + case 'Push dividends to accounts': + await pushDividends(dividendIndex, dividend.checkpointId); break; + case 'Explore account': + await exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol); + break; + case 'Withdraw withholding': + await withdrawWithholding(dividendIndex, dividendTokenSymbol); + break; + case 'Reclaim expired dividends': + await reclaimedDividend(dividendIndex, dividendTokenSymbol); + return; + case 'RETURN': + return; } -} -async function createDividends(name, dividend, checkpointId) { - await addDividendsModule(); + await manageExistingDividend(dividendIndex); +} - let time = Math.floor(Date.now()/1000); - let maturityTime = readlineSync.questionInt('Enter the dividend maturity time from which dividend can be paid (Unix Epoch time)\n(Now = ' + time + ' ): ', {defaultInput: time}); - let defaultTime = time + duration.minutes(10); - let expiryTime = readlineSync.questionInt('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = ' + defaultTime + ' ): ', {defaultInput: defaultTime}); - - let useDefaultExcluded = readlineSync.keyInYNStrict(`Do you want to use the default excluded addresses for this dividend? If not, data from 'dividendsExclusions_data.csv' will be used instead.`); +async function taxWithholding() { + let addresses = readlineSync.question(`Enter addresses to set tax withholding to(ex - add1, add2, add3, ...) or leave empty to read from 'tax_withholding_data.csv': `, { + limit: function (input) { + return input === '' || (input.split(',').every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(','); + if (addresses[0] !== '') { + let percentage = readlineSync.question('Enter the percentage of dividends to withhold (number between 0-100): ', { + limit: function (input) { + return (parseFloat(input) >= 0 && parseFloat(input) <= 100); + }, + limitMessage: 'Must be a value between 0 and 100', + }); + let percentageWei = web3.utils.toWei((percentage / 100).toString()); + let setWithHoldingFixedAction = currentDividendsModule.methods.setWithholdingFixed(addresses, percentageWei); + let receipt = await common.sendTransaction(setWithHoldingFixedAction); + let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'SetWithholdingFixed'); + console.log(chalk.green(`Successfully set tax rate of ${web3.utils.fromWei(event._withholding)}% for: `)); + console.log(chalk.green(event._investors)); + } else { + let parsedData = csvParse(TAX_WITHHOLDING_DATA_CSV); + let validData = parsedData.filter(row => + web3.utils.isAddress(row[0]) && + !isNaN(row[1]) + ); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, 100); + let [investorArray, taxArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + taxArray[batch] = taxArray[batch].map(t => web3.utils.toWei((t / 100).toString())); + console.log(`Batch ${batch + 1} - Attempting to set multiple tax rates to accounts: \n\n`, investorArray[batch], '\n'); + let action = await currentDividendsModule.methods.setWithholding(investorArray[batch], taxArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Multiple tax rates have benn set successfully!')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + } +} - let createDividendAction; - if (dividendsType == 'POLY') { - let approveAction = polyToken.methods.approve(currentDividendsModule._address, web3.utils.toWei(dividend)); - await common.sendTransaction(Issuer, approveAction, defaultGasPrice); - if (checkpointId > 0) { - if (useDefaultExcluded) { - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend), checkpointId, web3.utils.toHex(name)); - } else { - let excluded = getExcludedFromDataFile(); - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend), checkpointId, excluded, web3.utils.toHex(name)); - } - } else { - if (useDefaultExcluded) { - createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend), web3.utils.toHex(name)); - } else { - let excluded = getExcludedFromDataFile(); - createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend), excluded, web3.utils.toHex(name)); +async function createDividends() { + let dividendName = readlineSync.question(`Enter a name or title to indetify this dividend: `); + let dividendToken = gbl.constants.ADDRESS_ZERO; + let dividendSymbol = 'ETH'; + let token; + if (dividendsType === 'ERC20') { + do { + dividendToken = readlineSync.question(`Enter the address of ERC20 token in which dividend will be denominated(POLY = ${polyToken.options.address}): `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid ERC20 address", + defaultInput: polyToken.options.address + }); + token = new web3.eth.Contract(abis.erc20(), dividendToken); + try { + dividendSymbol = await token.methods.symbol().call(); + } catch (err) { + console.log(chalk.red(`${dividendToken} is not a valid ERC20 token address!!`)); } - } - let receipt = await common.sendTransaction(Issuer, createDividendAction, defaultGasPrice); - let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'ERC20DividendDeposited'); - console.log(chalk.green(`Dividend ${event._dividendIndex} deposited`)); - } else if (dividendsType == 'ETH') { - if (checkpointId > 0) { - if (useDefaultExcluded) { - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, checkpointId, web3.utils.toHex(name)); + } while (dividendSymbol === 'ETH'); + } + let dividendAmount = readlineSync.question(`How much ${dividendSymbol} would you like to distribute to token holders? `); + + let dividendAmountBN = new web3.utils.BN(dividendAmount); + let issuerBalance = new web3.utils.BN(web3.utils.fromWei(await getBalance(Issuer.address, dividendToken))); + if (issuerBalance.lt(dividendAmountBN)) { + console.log(chalk.red(`You have ${issuerBalance} ${dividendSymbol}.You need ${dividendAmountBN.sub(issuerBalance)} ${dividendSymbol} more!`)); + } else { + let checkpointId = await selectCheckpoint(true); // If there are no checkpoints, it must create a new one + let now = Math.floor(Date.now() / 1000); + let maturityTime = readlineSync.questionInt('Enter the dividend maturity time from which dividend can be paid (Unix Epoch time)\n(Now = ' + now + ' ): ', { defaultInput: now }); + let defaultTime = now + gbl.constants.DURATION.minutes(10); + let expiryTime = readlineSync.questionInt('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = ' + defaultTime + ' ): ', { defaultInput: defaultTime }); + + let useDefaultExcluded = !readlineSync.keyInYNStrict(`Do you want to use data from 'dividends_exclusions_data.csv' for this dividend? If not, default exclusions will apply.`); + + let createDividendAction; + if (dividendsType == 'ERC20') { + let approveAction = token.methods.approve(currentDividendsModule._address, web3.utils.toWei(dividendAmountBN)); + await common.sendTransaction(approveAction); + if (checkpointId > 0) { + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), checkpointId, web3.utils.toHex(dividendName)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), checkpointId, excluded[0], web3.utils.toHex(dividendName)); + } } else { - let excluded = getExcludedFromDataFile(); - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, checkpointId, excluded, web3.utils.toHex(name)); + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), web3.utils.toHex(dividendName)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), excluded[0], web3.utils.toHex(dividendName)); + } } + let receipt = await common.sendTransaction(createDividendAction); + let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'ERC20DividendDeposited'); + console.log(chalk.green(`Dividend ${event._dividendIndex} deposited`)); } else { - if (useDefaultExcluded) { - createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, web3.utils.toHex(name)); + if (checkpointId > 0) { + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, checkpointId, web3.utils.toHex(dividendName)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, checkpointId, excluded, web3.utils.toHex(dividendName)); + } } else { - let excluded = getExcludedFromDataFile(); - createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, excluded, web3.utils.toHex(name)); + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, web3.utils.toHex(dividendName)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, excluded, web3.utils.toHex(dividendName)); + } } + let receipt = await common.sendTransaction(createDividendAction, { value: web3.utils.toWei(dividendAmountBN) }); + let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'EtherDividendDeposited'); + console.log(` +Dividend ${ event._dividendIndex} deposited` + ); } - let receipt = await common.sendTransaction(Issuer, createDividendAction, defaultGasPrice, web3.utils.toWei(dividend)); - let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'EtherDividendDeposited'); - console.log(` - Dividend ${event._dividendIndex} deposited` - ); } } -async function pushDividends(dividend, account){ - let accs = account.split(','); - let pushDividendPaymentToAddressesAction = currentDividendsModule.methods.pushDividendPaymentToAddresses(dividend.index, accs); - let receipt = await common.sendTransaction(Issuer, pushDividendPaymentToAddressesAction, defaultGasPrice); - let successEventName; - if (dividendsType == 'POLY') { - successEventName = 'ERC20DividendClaimed'; - } else if (dividendsType == 'ETH') { - successEventName = 'EtherDividendClaimed'; - let failedEventName = 'EtherDividendClaimFailed'; - let failedEvents = common.getMultipleEventsFromLogs(currentDividendsModule._jsonInterface, receipt.logs, failedEventName); - for (const event of failedEvents) { - console.log(` - Failed to claim ${web3.utils.fromWei(event._amount)} ${dividendsType} - to account ${event._payee}` - ); - } +function showInvestors(investorsArray, claimedArray, excludedArray) { + let dataTable = [['Investor', 'Has claimed', 'Is excluded']]; + for (let i = 0; i < investorsArray.length; i++) { + dataTable.push([ + investorsArray[i], + claimedArray[i] ? 'YES' : 'NO', + excludedArray[i] ? 'YES' : 'NO' + ]); } + console.log(); + console.log(table(dataTable)); +} - let successEvents = common.getMultipleEventsFromLogs(currentDividendsModule._jsonInterface, receipt.logs, successEventName); - for (const event of successEvents) { - console.log(` - Claimed ${web3.utils.fromWei(event._amount)} ${dividendsType} - to account ${event._payee} - ${web3.utils.fromWei(event._withheld)} ${dividendsType} of tax withheld` - ); +function showReport(_name, _tokenSymbol, _amount, _witthheld, _claimed, _investorArray, _claimedArray, _excludedArray, _withheldArray, _amountArray) { + let title = `${_name.toUpperCase()} DIVIDEND REPORT`; + let dataTable = + [[ + 'Investor', + 'Amount sent', + 'Taxes withheld (%)', + `Taxes withheld (${_tokenSymbol})`, + 'Amount received', + 'Withdrawn' + ]]; + for (let i = 0; i < _investorArray.length; i++) { + let investor = _investorArray[i]; + let excluded = _excludedArray[i]; + let withdrawn = _claimedArray[i] ? 'YES' : 'NO'; + let amount = !excluded ? web3.utils.fromWei(web3.utils.toBN(_amountArray[i]).add(web3.utils.toBN(_withheldArray[i]))) : 0; + let withheld = !excluded ? web3.utils.fromWei(_withheldArray[i]) : 'NA'; + let withheldPercentage = (!excluded) ? (withheld !== '0' ? parseFloat(withheld) / parseFloat(amount) * 100 : 0) : 'NA'; + let received = !excluded ? web3.utils.fromWei(_amountArray[i]) : 0; + dataTable.push([ + investor, + amount, + withheldPercentage, + withheld, + received, + withdrawn + ]); } + console.log(chalk.yellow(`-----------------------------------------------------------------------------------------------------------------------------------------------------------`)); + console.log(title.padStart((50 - title.length) / 2, '*').padEnd((50 - title.length) / 2, '*')); + console.log(); + console.log(`- Total amount of dividends sent: ${web3.utils.fromWei(_amount)} ${_tokenSymbol} `); + console.log(`- Total amount of taxes withheld: ${web3.utils.fromWei(_witthheld)} ${_tokenSymbol} `); + console.log(`- Total amount of dividends distributed: ${web3.utils.fromWei(_claimed)} ${_tokenSymbol} `); + console.log(`- Total amount of investors: ${_investorArray.length} `); + console.log(); + console.log(table(dataTable)); + console.log(); + console.log(chalk.yellow(`NOTE: If investor has not claimed the dividend yet, TAX and AMOUNT are calculated with the current values set and they might change.`)); + console.log(chalk.yellow(`-----------------------------------------------------------------------------------------------------------------------------------------------------------`)); + console.log(); } -async function reclaimedDividend(dividend) { - let reclaimDividendAction = currentDividendsModule.methods.reclaimDividend(dividend.index); - let receipt = await common.sendTransaction(Issuer, reclaimDividendAction, defaultGasPrice); - let eventName; - if (dividendsType == 'POLY') { - eventName = 'ERC20DividendReclaimed'; - } else if (dividendsType == 'ETH') { - eventName = 'EtherDividendReclaimed'; +async function pushDividends(dividendIndex, checkpointId) { + let accounts = readlineSync.question('Enter addresses to push dividends to (ex- add1,add2,add3,...) or leave empty to push to all addresses: ', { + limit: function (input) { + return input === '' || (input.split(',').every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(','); + if (accounts[0] !== '') { + let action = currentDividendsModule.methods.pushDividendPaymentToAddresses(dividendIndex, accounts); + let receipt = await common.sendTransaction(action); + logPushResults(receipt); + } else { + let investorsAtCheckpoint = await securityToken.methods.getInvestorsAt(checkpointId).call(); + console.log(`There are ${investorsAtCheckpoint.length} investors at checkpoint ${checkpointId} `); + let batchSize = readlineSync.questionInt(`How many investors per transaction do you want to push to? `); + for (let i = 0; i < investorsAtCheckpoint.length; i += batchSize) { + let action = currentDividendsModule.methods.pushDividendPayment(dividendIndex, i, batchSize); + let receipt = await common.sendTransaction(action); + logPushResults(receipt); + } } +} + +async function exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol) { + let account = readlineSync.question('Enter address to explore: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let isExcluded = await currentDividendsModule.methods.isExcluded(account, dividendIndex).call(); + let hasClaimed = await currentDividendsModule.methods.isClaimed(account, dividendIndex).call(); + let dividendAmounts = await currentDividendsModule.methods.calculateDividend(dividendIndex, account).call(); + let dividendBalance = dividendAmounts[0]; + let dividendTax = dividendAmounts[1]; + let dividendTokenBalance = await getBalance(account, dividendTokenAddress); + let securityTokenBalance = await getBalance(account, securityToken.options.address); + + console.log(); + console.log(`Security token balance: ${web3.utils.fromWei(securityTokenBalance)} ${tokenSymbol} `); + console.log(`Dividend token balance: ${web3.utils.fromWei(dividendTokenBalance)} ${dividendTokenSymbol} `); + console.log(`Is excluded: ${isExcluded ? 'YES' : 'NO'} `); + if (!isExcluded) { + console.log(`Has claimed: ${hasClaimed ? 'YES' : 'NO'} `); + if (!hasClaimed) { + console.log(`Dividends available: ${web3.utils.fromWei(dividendBalance)} ${dividendTokenSymbol} `); + console.log(`Tax withheld: ${web3.utils.fromWei(dividendTax)} ${dividendTokenSymbol} `); + } + } + console.log(); +} + +async function withdrawWithholding(dividendIndex, dividendTokenSymbol) { + let action = currentDividendsModule.methods.withdrawWithholding(dividendIndex); + let receipt = await common.sendTransaction(action); + let eventName = dividendsType === 'ERC20' ? 'ERC20DividendWithholdingWithdrawn' : 'EtherDividendWithholdingWithdrawn'; + let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, eventName); + console.log(chalk.green(`Successfully withdrew ${web3.utils.fromWei(event._withheldAmount)} ${dividendTokenSymbol} from dividend ${event._dividendIndex} tax withholding.`)); +} + +async function reclaimedDividend(dividendIndex, dividendTokenSymbol) { + let action = currentDividendsModule.methods.reclaimDividend(dividendIndex); + let receipt = await common.sendTransaction(action); + let eventName = dividendsType === 'ERC20' ? 'ERC20DividendReclaimed' : 'EtherDividendReclaimed'; let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, eventName); console.log(` - Reclaimed Amount ${web3.utils.fromWei(event._claimedAmount)} ${dividendsType} - to account ${event._claimer}` +Reclaimed amount ${ web3.utils.fromWei(event._claimedAmount)} ${dividendTokenSymbol} +to account ${ event._claimer} ` ); } -async function whitelistAddress(address) { - let now = Math.floor(Date.now() / 1000); - let modifyWhitelistAction = generalTransferManager.methods.modifyWhitelist(address, now, now, now + 31536000, true); - await common.sendTransaction(Issuer, modifyWhitelistAction, defaultGasPrice); - console.log(chalk.green(`\nWhitelisting successful for ${address}.`)); +async function addDividendsModule() { + let availableModules = await moduleRegistry.methods.getModulesByTypeAndToken(gbl.constants.MODULES_TYPES.DIVIDENDS, securityToken.options.address).call(); + let options = await Promise.all(availableModules.map(async function (m) { + let moduleFactoryABI = abis.moduleFactory(); + let moduleFactory = new web3.eth.Contract(moduleFactoryABI, m); + return web3.utils.hexToUtf8(await moduleFactory.methods.name().call()); + })); + + let index = readlineSync.keyInSelect(options, 'Which dividends module do you want to add? ', { cancel: 'Return' }); + if (index != -1 && readlineSync.keyInYNStrict(`Are you sure you want to add ${options[index]} module? `)) { + let wallet = readlineSync.question('Enter the account address to receive reclaimed dividends and tax: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let configureFunction = abis.erc20DividendCheckpoint().find(o => o.name === 'configure' && o.type === 'function'); + let bytes = web3.eth.abi.encodeFunctionCall(configureFunction, [wallet]); + + let selectedDividendFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.MODULES_TYPES.DIVIDENDS, options[index]); + let addModuleAction = securityToken.methods.addModule(selectedDividendFactoryAddress, bytes, 0, 0); + let receipt = await common.sendTransaction(addModuleAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); + console.log(chalk.green(`Module deployed at address: ${event._module} `)); + } } // Helper functions -async function getBalance(address) { - let balance; - if (dividendsType == 'POLY') { - balance = (await polyToken.methods.balanceOf(address).call()).toString(); - } else if (dividendsType == 'ETH') { - balance = (await web3.eth.getBalance(address)).toString(); - } - - return balance; -} -async function checkBalance(_dividend) { - let issuerBalance = await getBalance(Issuer.address); - if (parseInt(web3.utils.fromWei(issuerBalance)) < parseInt(_dividend)) { - console.log(chalk.red(` - You have ${web3.utils.fromWei(issuerBalance)} ${dividendsType} need ${(parseInt(_dividend) - parseInt(web3.utils.fromWei(issuerBalance)))} more ${dividendsType} - `)); - process.exit(0); +async function getBalance(address, tokenAddress) { + if (tokenAddress !== gbl.constants.ADDRESS_ZERO) { + let token = new web3.eth.Contract(abis.erc20(), tokenAddress); + return await token.methods.balanceOf(address).call(); + } else { + return await web3.eth.getBalance(address); } } -async function isDividendsModuleAttached() { - let dividendsModuleName; - if (dividendsType == 'POLY') { - dividendsModuleName = 'ERC20DividendCheckpoint'; - } else if (dividendsType == 'ETH') { - dividendsModuleName = 'EtherDividendCheckpoint'; +function logPushResults(receipt) { + let successEventName; + if (dividendsType == 'ERC20') { + successEventName = 'ERC20DividendClaimed'; } - - let result = await securityToken.methods.getModulesByName(web3.utils.toHex(dividendsModuleName)).call(); - if (result.length > 0) { - let dividendsModuleAddress = result[0]; - let dividendsModuleABI; - if (dividendsType == 'POLY') { - dividendsModuleABI = abis.erc20DividendCheckpoint(); - } else if (dividendsType == 'ETH') { - dividendsModuleABI = abis.etherDividendCheckpoint(); + else if (dividendsType == 'ETH') { + successEventName = 'EtherDividendClaimed'; + let failedEventName = 'EtherDividendClaimFailed'; + let failedEvents = common.getMultipleEventsFromLogs(currentDividendsModule._jsonInterface, receipt.logs, failedEventName); + for (const event of failedEvents) { + console.log(chalk.red(`Failed to claim ${web3.utils.fromWei(event._amount)} ${dividendsType} to account ${event._payee} `, '\n')); } - currentDividendsModule = new web3.eth.Contract(dividendsModuleABI, dividendsModuleAddress); - currentDividendsModule.setProvider(web3.currentProvider); } - - return (typeof currentDividendsModule !== 'undefined'); -} - -async function addDividendsModule() { - if (!(await isDividendsModuleAttached())) { - let dividendsFactoryName; - let dividendsModuleABI; - if (dividendsType == 'POLY') { - dividendsFactoryName = 'ERC20DividendCheckpoint'; - dividendsModuleABI = abis.erc20DividendCheckpoint(); - } else if (dividendsType == 'ETH') { - dividendsFactoryName = 'EtherDividendCheckpoint'; - dividendsModuleABI = abis.etherDividendCheckpoint(); - } - - let dividendsFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.DIVIDENDS, dividendsFactoryName); - let addModuleAction = securityToken.methods.addModule(dividendsFactoryAddress, web3.utils.fromAscii('', 16), 0, 0); - let receipt = await common.sendTransaction(Issuer, addModuleAction, defaultGasPrice); - let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); - console.log(`Module deployed at address: ${event._module}`); - currentDividendsModule = new web3.eth.Contract(dividendsModuleABI, event._module); - currentDividendsModule.setProvider(web3.currentProvider); + let successEvents = common.getMultipleEventsFromLogs(currentDividendsModule._jsonInterface, receipt.logs, successEventName); + for (const event of successEvents) { + console.log(chalk.green(` Claimed ${web3.utils.fromWei(event._amount)} ${dividendsType} +to account ${ event._payee} +${ web3.utils.fromWei(event._withheld)} ${dividendsType} of tax withheld`, '\n')); } } async function selectCheckpoint(includeCreate) { - let options = []; - let fix = 1; //Checkpoint 0 is not included, so I need to add 1 to fit indexes for checkpoints and options - let checkpoints = (await getCheckpoints()).map(function(c) { return c.timestamp }); - if (includeCreate) { - options.push('Create new checkpoint'); - fix = 0; //If this option is added, fix isn't needed. - } - options = options.concat(checkpoints); + if (await securityToken.methods.currentCheckpointId().call() > 0) { + let options = []; + let fix = 1; //Checkpoint 0 is not included, so I need to add 1 to fit indexes for checkpoints and options + let checkpoints = (await getCheckpoints()).map(function (c) { return c.timestamp }); + if (includeCreate) { + options.push('Create new checkpoint'); + fix = 0; //If this option is added, fix isn't needed. + } + options = options.concat(checkpoints); - return readlineSync.keyInSelect(options, 'Select a checkpoint:', {cancel: false}) + fix; + return readlineSync.keyInSelect(options, 'Select a checkpoint:', { cancel: false }) + fix; + } else { + return 0; + } } async function getCheckpoints() { let result = []; - + let checkPointsTimestamps = await securityToken.methods.getCheckpointTimes().call(); for (let index = 0; index < checkPointsTimestamps.length; index++) { let checkpoint = {}; @@ -515,91 +673,217 @@ async function getCheckpoints() { return result.sort((a, b) => a.id - b.id); } -async function selectDividend(filter) { - let result = null; - let dividends = await getDividends(); +function isValidDividend(dividend) { + let now = Math.floor(Date.now() / 1000); + return now > dividend.maturity; +} - let now = Math.floor(Date.now()/1000); - if (typeof filter !== 'undefined') { - if (typeof filter.valid !== 'undefined') { - dividends = dividends.filter(d => filter.valid == (now > d.maturity)); - } - if (typeof filter.expired !== 'undefined') { - dividends = dividends.filter(d => filter.expired == (d.expiry < now)); - } - if (typeof filter.reclaimed !== 'undefined') { - dividends = dividends.filter(d => filter.reclaimed == d.reclaimed); - } - if (typeof filter.withRemainingWithheld !== 'undefined') { - dividends = dividends.filter(d => new web3.utils.BN(d.dividendWithheld).sub(new web3.utils.BN(d.dividendWithheldReclaimed)) > 0); - } - if (typeof filter.withRemaining !== 'undefined') { - dividends = dividends.filter(d => new web3.utils.BN(d.amount).sub(new web3.utils.BN(d.claimedAmount)) > 0); - } - } +function isExpiredDividend(dividend) { + let now = Math.floor(Date.now() / 1000); + return now > dividend.expiry; +} + +function hasRemaining(dividend) { + return Number(new web3.utils.BN(dividend.amount).sub(new web3.utils.BN(dividend.claimedAmount))).toFixed(10) > 0; +} + +function hasRemainingWithheld(dividend) { + return Number(new web3.utils.BN(dividend.dividendWithheld).sub(new web3.utils.BN(dividend.dividendWithheldReclaimed))).toFixed(10) > 0; +} - if (dividends.length > 0) { - let options = dividends.map(function(d) { - return `${web3.utils.toAscii(d.name)} +async function selectDividend(dividends) { + let result = null; + let options = dividends.map(function (d) { + return `${d.name} + Amount: ${web3.utils.fromWei(d.amount)} ${d.tokenSymbol} + Status: ${isExpiredDividend(d) ? 'Expired' : hasRemaining(d) ? 'In progress' : 'Completed'} + Token: ${d.tokenSymbol} Created: ${moment.unix(d.created).format('MMMM Do YYYY, HH:mm:ss')} - Maturity: ${moment.unix(d.maturity).format('MMMM Do YYYY, HH:mm:ss')} - Expiry: ${moment.unix(d.expiry).format('MMMM Do YYYY, HH:mm:ss')} - At checkpoint: ${d.checkpointId} - Amount: ${web3.utils.fromWei(d.amount)} ${dividendsType} - Claimed Amount: ${web3.utils.fromWei(d.claimedAmount)} ${dividendsType} - Withheld: ${web3.utils.fromWei(d.dividendWithheld)} ${dividendsType} - Withheld claimed: ${web3.utils.fromWei(d.dividendWithheldReclaimed)} ${dividendsType}` - }); + Expiry: ${moment.unix(d.expiry).format('MMMM Do YYYY, HH:mm:ss')} ` + }); - let index = readlineSync.keyInSelect(options, 'Select a dividend:'); - if (index != -1) { - result = dividends[index]; - } - } else { - console.log(chalk.red(`No dividends were found meeting the requirements`)) - console.log(chalk.red(`Requirements: Valid: ${filter.valid} - Expired: ${filter.expired} - Reclaimed: ${filter.reclaimed} - WithRemainingWithheld: ${filter.withRemainingWithheld} - WithRemaining: ${filter.withRemaining}\n`)) + let index = readlineSync.keyInSelect(options, 'Select a dividend:', { cancel: 'RETURN' }); + if (index != -1) { + result = dividends[index]; } return result; } async function getDividends() { - let result = []; + function DividendData(_index, _created, _maturity, _expiry, _amount, _claimedAmount, _name, _tokenSymbol) { + this.index = _index; + this.created = _created; + this.maturity = _maturity; + this.expiry = _expiry; + this.amount = _amount; + this.claimedAmount = _claimedAmount; + this.name = _name; + this.tokenSymbol = _tokenSymbol; + } - let currentCheckpoint = await securityToken.methods.currentCheckpointId().call(); - for (let index = 1; index <= currentCheckpoint; index++) { - let dividendIndexes = await currentDividendsModule.methods.getDividendIndex(index).call(); - for (const i of dividendIndexes) { - let dividend = await currentDividendsModule.methods.dividends(i).call(); - dividend.index = i; - result.push(dividend); + let dividends = []; + let dividendsData = await currentDividendsModule.methods.getDividendsData().call(); + let createdArray = dividendsData.createds; + let maturityArray = dividendsData.maturitys; + let expiryArray = dividendsData.expirys; + let amountArray = dividendsData.amounts; + let claimedAmountArray = dividendsData.claimedAmounts; + let nameArray = dividendsData.names; + for (let i = 0; i < nameArray.length; i++) { + let tokenSymbol = 'ETH'; + if (dividendsType === 'ERC20') { + let tokenAddress = await currentDividendsModule.methods.dividendTokens(i).call(); + let erc20token = new web3.eth.Contract(abis.erc20(), tokenAddress); + tokenSymbol = await erc20token.methods.symbol().call(); } + dividends.push( + new DividendData( + i, + createdArray[i], + maturityArray[i], + expiryArray[i], + amountArray[i], + claimedAmountArray[i], + web3.utils.hexToUtf8(nameArray[i]), + tokenSymbol + ) + ); } - return result; + return dividends; } function getExcludedFromDataFile() { - let excludedFromFile = require('fs').readFileSync('./CLI/data/dividendsExclusions_data.csv').toString().split("\n"); - let excluded = excludedFromFile.filter(function (address) { - return web3.utils.isAddress(address); - }); - return excluded; + let parsedData = csvParse(EXCLUSIONS_DATA_CSV); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, validData.length); + let [data] = common.transposeBatches(batches); + + return data; } function showExcluded(excluded) { - if (excluded.length > 0) { - console.log('Current default excluded addresses:') - excluded.map(function (address) { console.log(' ', address) }); + console.log('Current default excluded addresses:') + excluded.map(address => console.log(address)); + console.log(); +} + +async function getAllModulesByType(type) { + function ModuleInfo(_moduleType, _name, _address, _factoryAddress, _archived, _paused) { + this.name = _name; + this.type = _moduleType; + this.address = _address; + this.factoryAddress = _factoryAddress; + this.archived = _archived; + this.paused = _paused; + } + + let modules = []; + + let allModules = await securityToken.methods.getModulesByType(type).call(); + + for (let i = 0; i < allModules.length; i++) { + let details = await securityToken.methods.getModule(allModules[i]).call(); + let nameTemp = web3.utils.hexToUtf8(details[0]); + let pausedTemp = null; + if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname} /../../ build / contracts / ${nameTemp}.json`).toString()).abi; + let contractTemp = new web3.eth.Contract(abiTemp, details[1]); + pausedTemp = await contractTemp.methods.paused().call(); + } + modules.push(new ModuleInfo(type, nameTemp, details[1], details[2], details[3], pausedTemp)); + } + + return modules; +} + +async function initialize(_tokenSymbol) { + welcome(); + await setup(); + if (typeof _tokenSymbol === 'undefined') { + tokenSymbol = await selectToken(); } else { - console.log('There are not default excluded addresses.') + tokenSymbol = _tokenSymbol; } - console.log(); + let securityTokenAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (securityTokenAddress == '0x0000000000000000000000000000000000000000') { + console.log(chalk.red(`Selected Security Token ${tokenSymbol} does not exist.`)); + process.exit(0); + } + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI, securityTokenAddress); + securityToken.setProvider(web3.currentProvider); +} + +function welcome() { + common.logAsciiBull(); + console.log("**********************************************"); + console.log("Welcome to the Command-Line Dividends Manager."); + console.log("**********************************************"); + console.log("Issuer Account: " + Issuer.address + "\n"); +} + +async function setup() { + try { + 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); + + let moduleRegistryAddress = await contracts.moduleRegistry(); + let moduleRegistryABI = abis.moduleRegistry(); + moduleRegistry = new web3.eth.Contract(moduleRegistryABI, moduleRegistryAddress); + moduleRegistry.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 selectToken() { + let result = null; + + let userTokens = await securityTokenRegistry.methods.getTokensByOwner(Issuer.address).call(); + let tokenDataArray = await Promise.all(userTokens.map(async function (t) { + let tokenData = await securityTokenRegistry.methods.getSecurityTokenData(t).call(); + return { symbol: tokenData[0], address: t }; + })); + let options = tokenDataArray.map(function (t) { + return `${t.symbol} - Deployed at ${t.address} `; + }); + options.push('Enter token symbol manually'); + + let index = readlineSync.keyInSelect(options, 'Select a token:', { cancel: 'Exit' }); + let selected = index != -1 ? options[index] : 'Exit'; + switch (selected) { + case 'Enter token symbol manually': + result = readlineSync.question('Enter the token symbol: '); + break; + case 'Exit': + process.exit(); + break; + default: + result = tokenDataArray[index].symbol; + break; + } + + return result; } module.exports = { - executeApp: async function(type, remoteNetwork) { - return executeApp(type, remoteNetwork); + executeApp: async function (_tokenSymbol) { + await initialize(_tokenSymbol); + return executeApp(); } } diff --git a/CLI/commands/faucet.js b/CLI/commands/faucet.js index 556ffa402..70eadd57b 100644 --- a/CLI/commands/faucet.js +++ b/CLI/commands/faucet.js @@ -1,7 +1,6 @@ var readlineSync = require('readline-sync'); var BigNumber = require('bignumber.js'); var common = require('./common/common_functions'); -var global = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis') var chalk = require('chalk'); @@ -11,8 +10,7 @@ var chalk = require('chalk'); let polyToken; let usdToken; -async function executeApp(beneficiary, amount, remoteNetwork) { - await global.initialize(remoteNetwork); +async function executeApp(beneficiary, amount) { console.log("\n"); console.log("***************************") @@ -79,7 +77,7 @@ async function send_poly(beneficiary, amount) { beneficiary = readlineSync.question(`Enter beneficiary 10K USD Tokens ('${Issuer.address}'): `); if (beneficiary == "") beneficiary = Issuer.address; let getTokensAction = usdToken.methods.getTokens(web3.utils.toWei('10000'), beneficiary); - await common.sendTransaction(Issuer, getTokensAction, defaultGasPrice); + await common.sendTransaction(getTokensAction); let balance = await usdToken.methods.balanceOf(beneficiary).call(); let balanceInPoly = new BigNumber(balance).dividedBy(new BigNumber(10).pow(18)); console.log(chalk.green(`Congratulations! balance of ${beneficiary} address is ${balanceInPoly.toNumber()} USD Tokens`)); @@ -97,7 +95,7 @@ async function send_poly(beneficiary, amount) { async function transferTokens(to, amount) { try { let getTokensAction = polyToken.methods.getTokens(amount, to); - await common.sendTransaction(Issuer, getTokensAction, defaultGasPrice); + await common.sendTransaction(getTokensAction); } catch (err){ console.log(err.message); return; @@ -108,7 +106,7 @@ async function transferTokens(to, amount) { } module.exports = { - executeApp: async function(beneficiary, amount, remoteNetwork) { - return executeApp(beneficiary, amount, remoteNetwork); + executeApp: async function(beneficiary, amount) { + return executeApp(beneficiary, amount); } } \ No newline at end of file diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index b6c58cc38..b6abc7316 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -7,6 +7,12 @@ let stoInterfaceABI; let cappedSTOABI; let usdTieredSTOABI; let generalTransferManagerABI; +let manualApprovalTransferManagerABI; +let blacklistTransferManagerABI; +let countTransferManagerABI; +let percentageTransferManagerABI; +let lockUpTransferManagerABI; +let volumeRestrictionTMABI; let generalPermissionManagerABI; let polyTokenABI; let cappedSTOFactoryABI; @@ -15,29 +21,41 @@ let erc20DividendCheckpointABI; let etherDividendCheckpointABI; let moduleInterfaceABI; let ownableABI; +let iSTOABI; +let iTransferManagerABI; let moduleFactoryABI; +let erc20ABI; try { - polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; - securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityTokenRegistry.json').toString()).abi; - featureRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/FeatureRegistry.json').toString()).abi; - moduleRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleRegistry.json').toString()).abi; - securityTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityToken.json').toString()).abi; - stoInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi; - 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; - ownableABI = JSON.parse(require('fs').readFileSync('./build/contracts/Ownable.json').toString()).abi; - moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi; + polymathRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolymathRegistry.json`).toString()).abi; + securityTokenRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityTokenRegistry.json`).toString()).abi; + featureRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/FeatureRegistry.json`).toString()).abi; + moduleRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleRegistry.json`).toString()).abi; + securityTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityToken.json`).toString()).abi; + stoInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi; + cappedSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTO.json`).toString()).abi; + usdTieredSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTO.json`).toString()).abi; + generalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralTransferManager.json`).toString()).abi; + manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ManualApprovalTransferManager.json`).toString()).abi; + countTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CountTransferManager.json`).toString()).abi; + percentageTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PercentageTransferManager.json`).toString()).abi; + blacklistTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/BlacklistTransferManager.json`).toString()).abi; + volumeRestrictionTMABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/VolumeRestrictionTM.json`).toString()).abi; + lockUpTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/LockUpTransferManager.json`).toString()).abi; + generalPermissionManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralPermissionManager.json`).toString()).abi; + polyTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).abi; + cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTOFactory.json`).toString()).abi; + usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTOFactory.json`).toString()).abi; + erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ERC20DividendCheckpoint.json`).toString()).abi; + etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/EtherDividendCheckpoint.json`).toString()).abi; + moduleInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/IModule.json`).toString()).abi; + ownableABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/Ownable.json`).toString()).abi; + iSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi + iTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ITransferManager.json`).toString()).abi + moduleFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleFactory.json`).toString()).abi; + erc20ABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../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"); + console.log('\x1b[31m%s\x1b[0m', "Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); throw err; } @@ -69,6 +87,24 @@ module.exports = { generalTransferManager: function () { return generalTransferManagerABI; }, + manualApprovalTransferManager: function () { + return manualApprovalTransferManagerABI; + }, + blacklistTransferManager: function () { + return blacklistTransferManagerABI; + }, + countTransferManager: function () { + return countTransferManagerABI; + }, + percentageTransferManager: function () { + return percentageTransferManagerABI; + }, + lockUpTransferManager: function () { + return lockUpTransferManagerABI; + }, + volumeRestrictionTM: function () { + return volumeRestrictionTMABI; + }, generalPermissionManager: function () { return generalPermissionManagerABI; }, @@ -93,7 +129,16 @@ module.exports = { ownable: function () { return ownableABI; }, + ISTO: function () { + return iSTOABI; + }, + ITransferManager: function () { + return iTransferManagerABI; + }, moduleFactory: function () { return moduleFactoryABI; + }, + erc20: function () { + return erc20ABI; } -} \ No newline at end of file +} diff --git a/CLI/commands/helpers/contract_addresses.js b/CLI/commands/helpers/contract_addresses.js index 1328f8ba9..79f5cba10 100644 --- a/CLI/commands/helpers/contract_addresses.js +++ b/CLI/commands/helpers/contract_addresses.js @@ -7,13 +7,13 @@ function getPolymathRegistryAddress(networkId) { let result; switch (networkId) { case 1: // MAINNET - result = "0x06595656b93ce14834f0d22b7bbda4382d5ab510"; + result = "0xdfabf3e4793cd30affb47ab6fa4cf4eef26bbc27"; break; case 3: // ROPSTEN result = ""; break; case 15: // GANACHE - result = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).networks[networkId].address; + result = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolymathRegistry.json`).toString()).networks[networkId].address; break; case 42: // KOVAN result = "0x5b215a7d39ee305ad28da29bf2f0425c6c2a00b3"; @@ -52,35 +52,35 @@ module.exports = { let networkId = await web3.eth.net.getId(); return getPolymathRegistryAddress(networkId); }, - securityTokenRegistry: async function() { + securityTokenRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("SecurityTokenRegistry").call(); }, - moduleRegistry: async function() { + moduleRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("ModuleRegistry").call(); }, - featureRegistry: async function() { + featureRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("FeatureRegistry").call(); }, - polyToken: async function() { + polyToken: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("PolyToken").call(); }, - usdToken: async function() { + usdToken: async function () { let networkId = await web3.eth.net.getId(); if (networkId == 1) return "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; else if (networkId == 42) return "0xc4375b7de8af5a38a93548eb8453a498222c4ff2"; else - return JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).networks[networkId].address; + return JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).networks[networkId].address; }, - getModuleFactoryAddressByName: async function(stAddress, moduleType, moduleName) { + getModuleFactoryAddressByName: async function (stAddress, moduleType, moduleName) { let moduleRegistry = await getModuleRegistry(); let availableModules = await moduleRegistry.methods.getModulesByTypeAndToken(moduleType, stAddress).call(); - + let result = null; let counter = 0; let moduleFactoryABI = abis.moduleFactory(); diff --git a/CLI/commands/helpers/csv.js b/CLI/commands/helpers/csv.js new file mode 100644 index 000000000..a36dcec15 --- /dev/null +++ b/CLI/commands/helpers/csv.js @@ -0,0 +1,35 @@ +const csvParse = require('csv-parse/lib/sync'); +const fs = require('fs'); +const web3Utils = require('web3-utils'); + +function _cast(obj) { + if (/^(\-|\+)?([1-9]+[0-9]*)$/.test(obj)) { // Int + obj = parseInt(obj); + } + else if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(obj)) { // Float + obj = parseFloat(obj); + } + else if (/^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/.test(obj)) { // Datetime + var matches = /^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/.exec(obj); + var composedDate = new Date(matches[3], matches[1] - 1, matches[2]); + var timestampDate = composedDate.getTime(); + obj = timestampDate / 1000; + } + else if (obj.toLowerCase() === "true" || obj.toLowerCase() === "false") { // Boolean + obj = JSON.parse(obj.toLowerCase()); + } else if (web3Utils.isAddress(obj)) { + obj = web3Utils.toChecksumAddress(obj); + } + return obj; +} + +function parse(_csvFilePath, _batchSize) { + // Read file + let input = fs.readFileSync(_csvFilePath); + // Parse csv + let data = csvParse(input, { cast: _cast }); + + return data; +} + +module.exports = parse; \ No newline at end of file diff --git a/CLI/commands/helpers/time.js b/CLI/commands/helpers/time.js new file mode 100644 index 000000000..294f54f67 --- /dev/null +++ b/CLI/commands/helpers/time.js @@ -0,0 +1,94 @@ +const moment = require('moment'); +const chalk = require('chalk'); + +function increaseTime(duration) { + const id = Date.now(); + + return new Promise((resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_increaseTime", + params: [duration], + id: id + }, + err1 => { + if (err1) return reject(err1); + + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_mine", + id: id + 1 + }, + (err2, res) => { + return err2 ? reject(err2) : resolve(res); + } + ); + } + ); + }); +} + +function jumpToTime(timestamp) { + const id = Date.now(); + + return new Promise((resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_mine", + params: [timestamp], + id: id + }, + (err, res) => { + return err ? reject(err) : resolve(res); + } + ); + }); +} + +async function increaseTimeByDate(toTime) { + if (await web3.eth.net.getId() === 15) { + if (toTime.isValid()) { + let currentTime = (await web3.eth.getBlock('latest')).timestamp; + if (toTime.unix() > currentTime) { + await jumpToTime(toTime.unix()); + currentTime = (await web3.eth.getBlock('latest')).timestamp; + console.log(chalk.green(`Current datetime is ${currentTime} or ${moment.unix(currentTime).format("MM/DD/YYYY HH:mm:ss")}`)); + } else { + console.log(chalk.red(`It is not possible to go back in time. Please try again with a time in the future to travel to`)); + } + } else { + console.log(chalk.red(`Date format is not valid. Please use a valid Unix epoch time`)); + } + } else { + console.log(chalk.red(`Time traveling is only possible over develpment network`)); + } +} + +async function increaseTimeByDuration(duration) { + if (await web3.eth.net.getId() === 15) { + if (duration > 0) { + await increaseTime(duration); + currentTime = (await web3.eth.getBlock('latest')).timestamp; + console.log(chalk.green(`Current datetime is ${currentTime} or ${moment.unix(currentTime).format("MM/DD/YYYY HH:mm:ss")}`)); + } else { + console.log(chalk.red(`It is not possible to go back in time. Please try again with a time in the future to travel to`)); + } + } else { + console.log(chalk.red(`Time traveling is only possible over develpment network`)); + } +} + +function increaseTimeToDate(date) { + var toDate = moment(date, ['MM-DD-YYYY', 'MM-DD-YYYY HH:mm:ss']); + return increaseTimeByDate(toDate); +} + +function increaseTimeToEpochDate(epochDate) { + var toTime = moment.unix(epochDate); + return increaseTimeByDate(toTime); +} + +module.exports = { increaseTimeByDuration, increaseTimeToDate, increaseTimeToEpochDate }; diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 9a5229a7c..d59fd4bac 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -3,25 +3,21 @@ var readlineSync = require('readline-sync'); var BigNumber = require('bignumber.js'); var chalk = require('chalk'); var common = require('./common/common_functions'); -var global = require('./common/global'); +var gbl = require('./common/global'); // Load Contract artifacts var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); -const STO_KEY = 3; -const FUND_RAISE_TYPES = { - ETH: 0, - POLY: 1, - DAI: 2 -} +const ETH = 'ETH'; +const POLY = 'POLY'; +const STABLE = 'STABLE'; let securityTokenRegistry; let securityToken; let selectedSTO; let currentSTO; let polyToken; -let usdToken; let generalTransferManager; let raiseTypes = []; @@ -39,8 +35,7 @@ let displayCanBuy; let displayValidKYC; // Start Script -async function executeApp(investorAddress, investorPrivKey, symbol, currency, amount, remoteNetwork) { - await global.initialize(remoteNetwork); +async function executeApp(investorAddress, investorPrivKey, symbol, currency, amount) { common.logAsciiBull(); console.log("********************************************"); @@ -56,24 +51,25 @@ async function executeApp(investorAddress, investorPrivKey, symbol, currency, am } } if (investorAddress != "") { - User = { address: investorAddress, privateKey: investorPrivKey}; + User = { address: investorAddress, privateKey: investorPrivKey }; } else { User = Issuer; } 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) @@ -95,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.`)); @@ -117,7 +109,7 @@ async function inputSymbol(symbol) { if (STSymbol == "") process.exit(); STAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(STSymbol).call(); - if (STAddress == "0x0000000000000000000000000000000000000000"){ + if (STAddress == "0x0000000000000000000000000000000000000000") { console.log(`Token symbol provided is not a registered Security Token. Please enter another symbol.`); } else { let securityTokenABI = abis.securityToken(); @@ -129,12 +121,12 @@ async function inputSymbol(symbol) { let generalTransferManagerABI = abis.generalTransferManager(); generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, gtmModule[0]); - let stoModules = await securityToken.methods.getModulesByType(STO_KEY).call(); + let stoModules = await securityToken.methods.getModulesByType(gbl.constants.MODULES_TYPES.STO).call(); if (stoModules.length == 0) { console.log(chalk.red(`There is no STO module attached to the ${STSymbol.toUpperCase()} Token. No further actions can be taken.`)); process.exit(0); } else { - STOAddress = stoModules[0]; + STOAddress = stoModules[0]; let stoModuleData = await securityToken.methods.getModule(STOAddress).call(); selectedSTO = web3.utils.toAscii(stoModuleData[0]).replace(/\u0000/g, ''); let interfaceSTOABI = abis.stoInterface(); @@ -160,17 +152,22 @@ async function showTokenInfo() { // Show info async function showUserInfo(_user) { + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + console.log(` ******************* User Information ******************** - Address: ${_user}`); - if (await currentSTO.methods.fundRaiseTypes(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(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(FUND_RAISE_TYPES.DAI)) { - console.log(` - DAI balance:\t ${await usdBalance(_user)}`); + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()) { + let stableSymbolsAndBalance = await processAddressWithBalance(listOfStableCoins); + stableSymbolsAndBalance.forEach(stable => { + console.log(` - ${stable.symbol} balance:\t ${web3.utils.fromWei(stable.balance)}`); + }); } } @@ -185,27 +182,27 @@ async function showCappedSTOInfo() { let displayRaiseType; let displayFundsRaised; - for (const fundType in FUND_RAISE_TYPES) { - if (await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES[fundType]).call()) { + for (const fundType in gbl.constants.FUND_RAISE_TYPES) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { raiseTypes.push(fundType); displayRaiseType = fundType; - displayFundsRaised = await currentSTO.methods.fundsRaised(FUND_RAISE_TYPES[fundType]).call(); + displayFundsRaised = await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[fundType]).call(); } } - let now = Math.floor(Date.now()/1000); + let now = Math.floor(Date.now() / 1000); - await generalTransferManager.methods.whitelist(User.address).call({}, function(error, result){ + await generalTransferManager.methods.whitelist(User.address).call({}, function (error, result) { displayCanBuy = result.canBuyFromSTO; displayValidKYC = parseInt(result.expiryTime) > now; }); let timeTitle; let timeRemaining; - if(now < displayStartTime){ + if (now < displayStartTime) { timeTitle = "STO starts in: "; timeRemaining = displayStartTime - now; - }else{ + } else { timeTitle = "Time remaining:"; timeRemaining = displayEndTime - now; } @@ -219,7 +216,7 @@ async function showCappedSTOInfo() { - Start Time: ${new Date(displayStartTime * 1000)} - End Time: ${new Date(displayEndTime * 1000)} - Raise Type: ${displayRaiseType} - - Rate: 1 ${displayRaiseType} = ${displayRate} ${STSymbol.toUpperCase()} + - Rate: 1 ${displayRaiseType} = ${web3.utils.fromWei(displayRate)} ${STSymbol.toUpperCase()} --------------------------------------------------------------- - ${timeTitle} ${timeRemaining} - Funds raised: ${web3.utils.fromWei(displayFundsRaised)} ${displayRaiseType} @@ -228,7 +225,7 @@ async function showCappedSTOInfo() { - Investor count: ${displayInvestorCount} `); - if(!displayCanBuy) { + if (!displayCanBuy) { console.log(chalk.red(`Your address is not approved to participate in this token sale.\n`)); process.exit(0); } else if (!displayValidKYC) { @@ -243,30 +240,77 @@ async function showCappedSTOInfo() { } } -async function showUserInfoForUSDTieredSTO() -{ - for (const fundType in FUND_RAISE_TYPES) { - if (await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES[fundType]).call()) { - let displayInvestorInvested = web3.utils.fromWei(await currentSTO.methods.investorInvested(User.address, FUND_RAISE_TYPES[fundType]).call()); - console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); +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) { + let symbol = await checkSymbol(address); + list.push({ "symbol": symbol, "address": address }) + } + 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 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 = []; + 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()) { + 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()); + if ((fundType == STABLE) && (stableSymbols.length)) { + console.log(` - Invested in stable coin(s): ${displayInvestorInvested} USD`); + } else { + console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); + } } } let displayInvestorInvestedUSD = web3.utils.fromWei(await currentSTO.methods.investorInvestedUSD(User.address).call()); - console.log(` - Invested in USD: ${displayInvestorInvestedUSD} USD`); + console.log(` - Total invested in USD: ${displayInvestorInvestedUSD} USD`); - await generalTransferManager.methods.whitelist(User.address).call({}, function(error, result){ + await generalTransferManager.methods.whitelist(User.address).call({}, function (error, result) { displayCanBuy = result.canBuyFromSTO; - displayValidKYC = parseInt(result.expiryTime) > Math.floor(Date.now()/1000); + displayValidKYC = parseInt(result.expiryTime) > Math.floor(Date.now() / 1000); }); - console.log(` - Whitelisted: ${(displayCanBuy)? 'YES' : 'NO'}`); - console.log(` - Valid KYC: ${(displayValidKYC)? 'YES' : 'NO'}`); + console.log(` - Whitelisted: ${(displayCanBuy) ? 'YES' : 'NO'}`); + console.log(` - Valid KYC: ${(displayValidKYC) ? 'YES' : 'NO'}`); - let displayIsUserAccredited = await currentSTO.methods.accredited(User.address).call(); - console.log(` - Accredited: ${(displayIsUserAccredited)? "YES" : "NO"}`) + let investorData = await currentSTO.methods.investors(User.address).call(); + let displayIsUserAccredited = investorData.accredited == 1; + console.log(` - Accredited: ${(displayIsUserAccredited) ? "YES" : "NO"}`) - if (!await currentSTO.methods.accredited(User.address).call()) { - let displayOverrideNonAccreditedLimitUSD = web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSDOverride(User.address).call()) + if (!displayIsUserAccredited) { + let displayOverrideNonAccreditedLimitUSD = web3.utils.fromWei(investorData.nonAccreditedLimitUSDOverride); 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`); @@ -286,51 +330,62 @@ 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 = []; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); - for (const fundType in FUND_RAISE_TYPES) { - if (await currentSTO.methods.fundRaiseTypes(FUND_RAISE_TYPES[fundType]).call()) { + 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) + } } } let displayTiers = ""; let displayMintedPerTier = ""; for (let t = 0; t < tiersLength; t++) { - let ratePerTier = await currentSTO.methods.ratePerTier(t).call(); - let tokensPerTierTotal = await currentSTO.methods.tokensPerTierTotal(t).call(); - let mintedPerTierTotal = await currentSTO.methods.mintedPerTierTotal(t).call(); + let tier = await currentSTO.methods.tiers(t).call(); + let ratePerTier = tier.rate; + let tokensPerTierTotal = tier.tokenTotal; + let mintedPerTierTotal = tier.mintedTotal; + let mintedPerTierPerRaiseType = await currentSTO.methods.getTokensMintedByTier(t).call(); let displayMintedPerTierPerType = ""; let displayDiscountTokens = ""; for (const type of raiseTypes) { let displayDiscountMinted = ""; - if (type == 'POLY') { - let tokensPerTierDiscountPoly = await currentSTO.methods.tokensPerTierDiscountPoly(t).call(); - if (tokensPerTierDiscountPoly > 0) { - let ratePerTierDiscountPoly = await currentSTO.methods.ratePerTierDiscountPoly(t).call(); - let mintedPerTierDiscountPoly = await currentSTO.methods.mintedPerTierDiscountPoly(t).call(); - displayDiscountTokens = ` + let tokensPerTierDiscountPoly = tier.tokensDiscountPoly; + if (tokensPerTierDiscountPoly > 0) { + let ratePerTierDiscountPoly = tier.rateDiscountPoly; + let mintedPerTierDiscountPoly = tier.mintedDiscountPoly; + displayDiscountTokens = ` Tokens at discounted rate: ${web3.utils.fromWei(tokensPerTierDiscountPoly)} ${displayTokenSymbol} Discounted rate: ${web3.utils.fromWei(ratePerTierDiscountPoly, 'ether')} USD per Token`; - displayDiscountMinted = `(${web3.utils.fromWei(mintedPerTierDiscountPoly)} ${displayTokenSymbol} at discounted rate)`; - } + displayDiscountMinted = `(${web3.utils.fromWei(mintedPerTierDiscountPoly)} ${displayTokenSymbol} at discounted rate)`; } - let mintedPerTier = await currentSTO.methods.mintedPerTier(FUND_RAISE_TYPES[type], t).call(); - displayMintedPerTierPerType += ` + + let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for stable coin(s): ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } } displayTiers += ` - - Tier ${t+1}: + - Tier ${t + 1}: Tokens: ${web3.utils.fromWei(tokensPerTierTotal)} ${displayTokenSymbol} Rate: ${web3.utils.fromWei(ratePerTier)} USD per Token` - + displayDiscountTokens; + + displayDiscountTokens; - displayMintedPerTier += ` - - Tokens minted in Tier ${t+1}: ${web3.utils.fromWei(mintedPerTierTotal)} ${displayTokenSymbol}` - + displayMintedPerTierPerType; + displayMintedPerTier += ` + - Tokens minted in Tier ${t + 1}: ${web3.utils.fromWei(mintedPerTierTotal)} ${displayTokenSymbol}` + + displayMintedPerTierPerType; } let displayFundsRaisedUSD = web3.utils.fromWei(await currentSTO.methods.fundsRaisedUSD().call()); @@ -338,21 +393,37 @@ async function showUSDTieredSTOInfo() { let displayFundsRaisedPerType = ''; let displayTokensSoldPerType = ''; for (const type of raiseTypes) { - let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(FUND_RAISE_TYPES[type]).call()); - displayFundsRaisedPerType += ` + let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); + if ((type == STABLE) && (stableSymbols.length)) { + stableSymbols.forEach(async (stable) => { + let raised = await getStableCoinsRaised(currentSTO, stable.address); + displayFundsRaisedPerType += ` + ${stable.symbol}:\t\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) + } 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(FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` + let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for stable coin(s): ${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.map((obj) => { return obj.symbol }).toString().replace(`,`, ` - `)}` + } - let now = Math.floor(Date.now()/1000); + let now = Math.floor(Date.now() / 1000); let timeTitle; let timeRemaining; if (now < displayStartTime) { @@ -371,19 +442,19 @@ async function showUSDTieredSTOInfo() { - End Time: ${new Date(displayEndTime * 1000)} - Raise Type: ${displayRaiseType} - Tiers: ${tiersLength}` - + displayTiers + ` + + displayTiers + ` - Minimum Investment: ${displayMinimumInvestmentUSD} USD - Default NonAccredited Limit: ${displayNonAccreditedLimitUSD} USD ----------------------------------------------------------------------- - ${timeTitle} ${timeRemaining} - Tokens Sold: ${displayTokensSold} ${displayTokenSymbol}` - + displayTokensSoldPerType + ` + + displayTokensSoldPerType + ` - Current Tier: ${displayCurrentTier}` - + displayMintedPerTier + ` + + displayMintedPerTier + ` - Investor count: ${displayInvestorCount} - Funds Raised` - + displayFundsRaisedPerType + ` - USD: ${displayFundsRaisedUSD} USD + + displayFundsRaisedPerType + ` + Total USD: ${displayFundsRaisedUSD} USD `); if (!displayCanBuy) { @@ -401,6 +472,10 @@ async function showUSDTieredSTOInfo() { } } +async function getStableCoinsRaised(currentSTO, address) { + return await currentSTO.methods.stableCoinsRaised(address).call() +} + // Allow investor to buy tokens. async function investCappedSTO(currency, amount) { if (typeof currency !== 'undefined' && !raiseTypes.inlcudes(currency)) { @@ -416,7 +491,7 @@ async function investCappedSTO(currency, amount) { } if (amt == "") process.exit(); - let rate = await currentSTO.methods.rate().call(); + let rate = web3.utils.fromWei(await currentSTO.methods.rate().call()); let cost = new BigNumber(amt).div(rate); console.log(`This investment will cost ${cost} ${raiseTypes[0]}`); @@ -427,11 +502,11 @@ async function investCappedSTO(currency, amount) { let allowance = await polyToken.methods.allowance(STOAddress, User.address).call(); if (allowance < costWei) { let approveAction = polyToken.methods.approve(STOAddress, costWei); - await common.sendTransaction(User, approveAction, defaultGasPrice); + await common.sendTransaction(approveAction, { from: User }); } let actionBuyTokensWithPoly = currentSTO.methods.buyTokensWithPoly(costWei); - let receipt = await common.sendTransaction(User, actionBuyTokensWithPoly, defaultGasPrice); - logTokensPurchasedCappedSTO(receipt); + let receipt = await common.sendTransaction(actionBuyTokensWithPoly, { from: User }); + logTokensPurchasedCappedSTO(receipt, 'POLY'); } else { console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} POLY but have ${userBalance} POLY.`)); console.log(chalk.red(`Please purchase a smaller amount of tokens or access the POLY faucet to get the POLY to complete this txn.`)); @@ -439,15 +514,19 @@ async function investCappedSTO(currency, amount) { } } else { let actionBuyTokens = currentSTO.methods.buyTokens(User.address); - let receipt = await common.sendTransaction(User, actionBuyTokens, defaultGasPrice, costWei); - logTokensPurchasedCappedSTO(receipt); + let receipt = await common.sendTransaction(actionBuyTokens, { from: User, value: costWei }); + logTokensPurchasedCappedSTO(receipt, 'ETH'); } await showTokenInfo(); } // 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`)); @@ -457,15 +536,31 @@ async function investUsdTieredSTO(currency, amount) { } } else { for (const type of raiseTypes) { - let displayPrice = web3.utils.fromWei(await currentSTO.methods.convertToUSD(FUND_RAISE_TYPES[type], web3.utils.toWei("1")).call()); - console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + let displayPrice = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], web3.utils.toWei("1")).call()); + if (!((type == STABLE) && (stableSymbols.length))) { + console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + } } if (raiseTypes.length > 1) { - let index = readlineSync.keyInSelect(raiseTypes, 'Choose one of the allowed raise types: ', {cancel: false}); - raiseType = raiseTypes[index]; + const stableIndex = raiseTypes.indexOf(STABLE); + if (stableIndex > -1) { + raiseTypes.splice(stableIndex, 1) + stableSymbols.forEach((stable) => { + raiseTypes.push(stable.symbol) + }) + } + raiseType = raiseTypes[selectToken('Choose one of the allowed raise types: ')]; } else { - raiseType = raiseTypes[0]; - console.log(''); + if (raiseTypes[0] == STABLE) { + raiseTypes.splice(raiseTypes.indexOf(STABLE), 1) + stableSymbols.forEach((stable) => { + raiseTypes.push(stable.symbol) + }) + raiseType = raiseTypes[selectToken('Choose one of the allowed stable coin(s): ')]; + } else { + raiseType = raiseTypes[0]; + console.log(''); + } } } @@ -473,9 +568,16 @@ 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(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) { + limit: function (input) { return investorInvestedUSD != 0 || parseInt(input) > parseInt(web3.utils.fromWei(minimumInvestmentRaiseType)); }, limitMessage: `Amount must be greater than minimum investment (${web3.utils.fromWei(minimumInvestmentRaiseType)} ${raiseType} = ${web3.utils.fromWei(minimumInvestmentUSD)} USD)` @@ -487,41 +589,61 @@ async function investUsdTieredSTO(currency, amount) { let costWei = web3.utils.toWei(cost.toString()); - if (raiseType == 'POLY') { + 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.`)); + if (typeof amount !== 'undefined' || !readlineSync.keyInYNStrict(`Do you want the transaction to fail if this happens?`)) { + minTokenToBuy = 0; + } + + if (raiseType == POLY) { let userBalance = await polyBalance(User.address); if (parseInt(userBalance) >= parseInt(cost)) { let allowance = await polyToken.methods.allowance(STOAddress, User.address).call(); if (allowance < costWei) { let approveAction = polyToken.methods.approve(STOAddress, costWei); - await common.sendTransaction(User, approveAction, defaultGasPrice); + await common.sendTransaction(approveAction, { from: User }); } - let actionBuyWithPoly = currentSTO.methods.buyWithPOLY(User.address, costWei); - let receipt = await common.sendTransaction(User, actionBuyWithPoly,defaultGasPrice, 0, 2); + let actionBuyWithPoly = currentSTO.methods.buyWithPOLYRateLimited(User.address, costWei, minTokenToBuy); + let receipt = await common.sendTransaction(actionBuyWithPoly, { from: User, factor: 2 }); logTokensPurchasedUSDTieredSTO(receipt); } else { console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} POLY but have ${userBalance} POLY.`)); 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); - await common.sendTransaction(User, approveAction, defaultGasPrice); + let approveAction = stableCoin.methods.approve(STOAddress, costWei); + await common.sendTransaction(approveAction, { from: User }); } - let actionBuyWithUSD = currentSTO.methods.buyWithUSD(User.address, costWei); - let receipt = await common.sendTransaction(User, actionBuyWithUSD, defaultGasPrice, 0, 1.5); + 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(); - } + } } else { - let actionBuyWithETH = currentSTO.methods.buyWithETH(User.address); - let receipt = await common.sendTransaction(User, actionBuyWithETH, defaultGasPrice, costWei); + let actionBuyWithETH = currentSTO.methods.buyWithETHRateLimited(User.address, minTokenToBuy); + let receipt = await common.sendTransaction(actionBuyWithETH, { from: User, value: costWei }); logTokensPurchasedUSDTieredSTO(receipt); } @@ -529,13 +651,12 @@ async function investUsdTieredSTO(currency, amount) { await showUserInfoForUSDTieredSTO(); } -async function polyBalance(_user) { - let balance = await polyToken.methods.balanceOf(_user).call(); - return web3.utils.fromWei(balance); +function selectToken(msg) { + return readlineSync.keyInSelect(raiseTypes, msg, { cancel: false }); } -async function usdBalance(_user) { - let balance = await usdToken.methods.balanceOf(_user).call(); +async function polyBalance(_user) { + let balance = await polyToken.methods.balanceOf(_user).call(); return web3.utils.fromWei(balance); } @@ -551,7 +672,7 @@ function logTokensPurchasedUSDTieredSTO(receipt) { }; } -function logTokensPurchasedCappedSTO(receipt) { +function logTokensPurchasedCappedSTO(receipt, displayRaiseType) { console.log(chalk.green(`Congratulations! The token purchase was successfully completed.`)); let events = common.getMultipleEventsFromLogs(currentSTO._jsonInterface, receipt.logs, 'TokenPurchase'); for (event of events) { @@ -564,7 +685,7 @@ function logTokensPurchasedCappedSTO(receipt) { } module.exports = { - executeApp: async function(investorAddress, investorPrivKey, symbol, currency, amount, remoteNetwork) { - return executeApp(investorAddress, investorPrivKey, symbol, currency, amount, remoteNetwork); - } + executeApp: async function (investorAddress, investorPrivKey, symbol, currency, amount) { + return executeApp(investorAddress, investorPrivKey, symbol, currency, amount); + } } diff --git a/CLI/commands/module_manager.js b/CLI/commands/module_manager.js deleted file mode 100644 index b91541a9c..000000000 --- a/CLI/commands/module_manager.js +++ /dev/null @@ -1,447 +0,0 @@ -// Libraries for terminal prompts -var readlineSync = require('readline-sync'); -var chalk = require('chalk'); -var common = require('./common/common_functions'); -var global = require('./common/global'); - -// Load contract artifacts -var contracts = require('./helpers/contract_addresses'); -var abis = require('./helpers/contract_abis'); - -let securityTokenRegistry; -let securityToken; -let polyToken; - -// Init token info -let STSymbol; -let STAddress; -let STDetails; -let validSymbol = false; - -// Init Module Info -let pmModules; -let tmModules; -let stoModules; -let cpModules; -let numPM; -let numTM; -let numSTO; -let numCP; - -async function setup() { - try { - 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(chalk.red(`There was a problem getting the contracts. Make sure they are deployed to the selected network.`)); - process.exit(0); - } -} - -// Start function -async function executeApp(remoteNetwork) { - await global.initialize(remoteNetwork); - - common.logAsciiBull(); - console.log(chalk.yellow(`******************************************`)); - console.log(chalk.yellow(`Welcome to the Command-Line Module Manager`)); - console.log(chalk.yellow(`******************************************`)); - - await setup(); - await showUserInfo(Issuer.address); - - while (!validSymbol) { - await getSecurityToken(); - } -}; - -// get token contract based on input symbol -async function getSecurityToken() { - STSymbol = readlineSync.question(chalk.yellow(`\nEnter the symbol of the registered security token you issued: `)); - STAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(STSymbol).call(); - if (STAddress == "0x0000000000000000000000000000000000000000") { - console.log(chalk.red(`\nToken symbol provided is not a registered Security Token. Please enter another symbol.`)); - return; - } - STDetails = await securityTokenRegistry.methods.getSecurityTokenData(STAddress).call(); - if (STDetails[1] != Issuer.address) { - console.log(chalk.red(`\nYou did not issue the Security Token associated with the symbol provided. Please enter another symbol.`)); - return; - } - validSymbol = true; - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI, STAddress); - - await displayModules(); - -} - -// display module status -async function displayModules() { - - await showUserInfo(Issuer.address); - - // Security Token details - let displayTokenSymbol = await securityToken.methods.symbol().call(); - let displayTokenSupply = await securityToken.methods.totalSupply().call(); - let displayUserTokens = await securityToken.methods.balanceOf(Issuer.address).call(); - - console.log(` - ************** Security Token Information *************** - - Address: ${STAddress} - - Token symbol: ${displayTokenSymbol.toUpperCase()} - - Total supply: ${web3.utils.fromWei(displayTokenSupply)} ${displayTokenSymbol.toUpperCase()} - - User balance: ${web3.utils.fromWei(displayUserTokens)} ${displayTokenSymbol.toUpperCase()} - `); - - // Module Details - pmModules = await iterateModules(1); - tmModules = await iterateModules(2); - stoModules = await iterateModules(3); - cpModules = await iterateModules(4); - - // Module Counts - numPM = pmModules.length; - numTM = tmModules.length; - numSTO = stoModules.length; - numCP = cpModules.length; - - console.log(` - ****************** Module Information ******************* - - Permission Manager: ${(numPM > 0) ? numPM : 'None'} - - Transfer Manager: ${(numTM > 0) ? numTM : 'None'} - - STO: ${(numSTO > 0) ? numSTO : 'None'} - - Checkpoint: ${(numCP > 0) ? numCP : 'None'} - `); - - if (numPM) { - console.log(chalk.green(`\n Permission Manager Modules:`)); - for (i=0;i= 75) { - distribData.push(allocData); - allocData = []; - index = 0; - } - - } else { - let userArray = new Array() - //dont need this here, as if it is NOT an address this function will fail - //let checksummedAddress = web3.utils.toChecksumAddress(data[1]); - 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 = []; - - mint_tokens_for_affliliates(); - }); - - stream.pipe(csvStream); - } - - async function mint_tokens_for_affliliates() { - let tokenDeployed = false; - let tokenDeployedAddress; - // Let's check if token has already been deployed, if it has, skip to STO - await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call({}, function (error, result) { - if (result != "0x0000000000000000000000000000000000000000") { - console.log('\x1b[32m%s\x1b[0m', "Token deployed at address " + result + "."); - tokenDeployedAddress = result; - tokenDeployed = true; - } - }); - if (tokenDeployed) { - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI, tokenDeployedAddress); - } - let modules = await securityToken.methods.getModulesByType(STO_KEY).call(); - if (modules.length > 0) { - console.log("****************************************************************************************\n"); - console.log("*************" + chalk.red(" Minting of tokens is only allowed before the STO get attached ") + "************\n"); - console.log("****************************************************************************************\n"); - process.exit(0); - } - console.log(` - ------------------------------------------------------- - ------------ Mint the tokens to affiliates ------------ - ------------------------------------------------------- - `); - - let affiliatesFailedArray = []; - let affiliatesKYCInvalidArray = []; - //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 affiliatesVerifiedArray = []; - let tokensVerifiedArray = []; - //splitting the user arrays to be organized by input - for (let j = 0; j < distribData[i].length; j++) { - let investorAccount = distribData[i][j][0]; - let tokenAmount = web3.utils.toWei((distribData[i][j][1]).toString(),"ether"); - let verifiedTransaction = await securityToken.methods.verifyTransfer("0x0000000000000000000000000000000000000000", investorAccount, tokenAmount, web3.utils.fromAscii('')).call(); - if (verifiedTransaction) { - affiliatesVerifiedArray.push(investorAccount); - tokensVerifiedArray.push(tokenAmount); - } else { - let gtmModule = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); - let generalTransferManagerABI = abis.generalTransferManager(); - let generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, gtmModule[0]); - let validKYC = (await generalTransferManager.methods.whitelist(Issuer.address).call()).expiryTime > Math.floor(Date.now()/1000); - if (validKYC) { - affiliatesFailedArray.push(investorAccount); - } else { - affiliatesKYCInvalidArray.push(investorAccount); - } - } - } - let mintMultiAction = securityToken.methods.mintMulti(affiliatesVerifiedArray, tokensVerifiedArray); - let r = await common.sendTransaction(Issuer, mintMultiAction, defaultGasPrice); - console.log(`Batch ${i} - Attempting to send the Minted tokens to affiliates accounts:\n\n`, affiliatesVerifiedArray, "\n\n"); - console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------"); - console.log("Multi Mint transaction was successful.", r.gasUsed, "gas used. Spent:", web3.utils.fromWei(BigNumber(r.gasUsed * defaultGasPrice).toString(), "ether"), "Ether"); - console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n\n"); - - - } catch (err) { - console.log("ERROR:", err); - } - } - - console.log("Retrieving logs to determine investors have had their tokens correctly.\n\n") - - let totalInvestors = 0; - let updatedInvestors = 0; - - let investorData_Events = new Array(); - let investorObjectLookup = {}; - - let event_data = await securityToken.getPastEvents('Minted', { - fromBlock: 0, - toBlock: 'latest' - }, function (error, events) { - - }); - - for (var i = 0; i < event_data.length; i++) { - let combineArray = []; - - let investorAddress_Event = event_data[i].returnValues._to; - let amount_Event = event_data[i].returnValues._value; - let blockNumber = event_data[i].blockNumber - - combineArray.push(investorAddress_Event); - combineArray.push(amount_Event); - combineArray.push(blockNumber); - - investorData_Events.push(combineArray) - //we have already recorded it, so this is an update to our object - if (investorObjectLookup.hasOwnProperty(investorAddress_Event)) { - - //the block number form the event we are checking is bigger, so we gotta replace it - if (investorObjectLookup[investorAddress_Event].recordedBlockNumber < blockNumber) { - investorObjectLookup[investorAddress_Event] = { amount: amount_Event, recordedBlockNumber: blockNumber }; - updatedInvestors += 1; - // investorAddress_Events.push(investorAddress_Event); not needed, because we start the obj with zero events - - } else { - //do nothing. so if we find an event, and it was an older block, its old, we dont care - } - //we have never recorded this address as an object key, so we need to add it to our list of investors updated by the csv - } else { - investorObjectLookup[investorAddress_Event] = { amount: amount_Event, recordedBlockNumber: blockNumber }; - totalInvestors += 1; - // investorAddress_Events.push(investorAddress_Event); - } - } - let investorAddress_Events = Object.keys(investorObjectLookup) - - console.log(`******************** EVENT LOGS ANALYSIS COMPLETE ********************\n`); - console.log(`A total of ${totalInvestors} affiliated investors get the token\n`); - console.log(`This script in total sent ${fullFileData.length - badData.length - affiliatesFailedArray.length - affiliatesKYCInvalidArray.length} new investors and updated investors to the blockchain.\n`); - console.log(`There were ${badData.length} bad entries that didnt get sent to the blockchain in the script.\n`); - console.log(`There were ${affiliatesKYCInvalidArray.length} accounts with invalid KYC.\n`); - console.log(`There were ${affiliatesFailedArray.length} accounts that didn't get sent to the blockchain as they would fail.\n`); - - console.log("************************************************************************************************"); - console.log("OBJECT WITH EVERY USER AND THEIR MINTED TOKEN: \n\n", investorObjectLookup) - console.log("************************************************************************************************"); - console.log("LIST OF ALL INVESTORS WHO GOT THE MINTED TOKENS: \n\n", investorAddress_Events) - - let missingDistribs = []; - let failedVerificationDistribs = []; - let invalidKYCDistribs = []; - for (let l = 0; l < fullFileData.length; l++) { - if (affiliatesKYCInvalidArray.includes(fullFileData[l][0])) { - invalidKYCDistribs.push(fullFileData[l]); - } else if (affiliatesFailedArray.includes(fullFileData[l][0])) { - failedVerificationDistribs.push(fullFileData[l]); - } else if (!investorObjectLookup.hasOwnProperty(fullFileData[l][0])) { - missingDistribs.push(fullFileData[l]); - } - } - - if (invalidKYCDistribs.length > 0) { - console.log("**************************************************************************************************************************"); - console.log("The following data arrays have an invalid KYC. Please review if these accounts are whitelisted and their KYC is not expired\n"); - console.log(invalidKYCDistribs); - console.log("**************************************************************************************************************************"); - } - if (failedVerificationDistribs.length > 0) { - console.log("*********************************************************************************************************"); - console.log("-- The following data arrays failed at verifyTransfer. Please review if these accounts are whitelisted --\n"); - console.log(failedVerificationDistribs); - console.log("*********************************************************************************************************"); - } - if (missingDistribs.length > 0) { - console.log("******************************************************************************************"); - console.log("-- No Minted event was found for the following data arrays. Please review them manually --\n"); - console.log(missingDistribs); - console.log("******************************************************************************************"); - } - if (missingDistribs.length == 0 && failedVerificationDistribs.length == 0 && invalidKYCDistribs.length == 0) { - console.log("\n**************************************************************************************************************************"); - console.log("All accounts passed through from the CSV were successfully get the tokens, because we were able to read them all from events"); - console.log("****************************************************************************************************************************"); - } -} - -function isvalidToken(token) { - var tokenAmount = parseInt(token); - if((tokenAmount % 1 == 0)) { - return tokenAmount; - } - return false; -} diff --git a/CLI/commands/permission_manager.js b/CLI/commands/permission_manager.js index a2165f697..ef3e2ea60 100644 --- a/CLI/commands/permission_manager.js +++ b/CLI/commands/permission_manager.js @@ -1,7 +1,7 @@ var readlineSync = require('readline-sync'); var chalk = require('chalk'); var common = require('./common/common_functions'); -var global = require('./common/global'); +var gbl = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); @@ -12,16 +12,7 @@ let securityToken; let generalPermissionManager; let isNewDelegate = false; -const MODULES_TYPES = { - PERMISSION: 1, - TRANSFER: 2, - STO: 3, - DIVIDEND: 4, - BURN: 5 -} - -async function executeApp(remoteNetwork) { - await global.initialize(remoteNetwork); +async function executeApp() { common.logAsciiBull(); console.log("***********************************************"); @@ -75,9 +66,9 @@ async function addPermissionModule() { 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.getModuleFactoryAddressByName(securityToken.options.address, MODULES_TYPES.PERMISSION, 'GeneralPermissionManager'); + let permissionManagerFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.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 receipt = await common.sendTransaction(addModuleAction); let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); console.log(`Module deployed at address: ${event._module}`); generalPermissionManagerAddress = event._module; @@ -119,18 +110,23 @@ async function changePermissionAction(selectedDelegate) { async function deleteDelegate(address) { let deleteDelegateAction = generalPermissionManager.methods.deleteDelegate(address); - await common.sendTransaction(Issuer, deleteDelegateAction, defaultGasPrice, 0, 2); + await common.sendTransaction(deleteDelegateAction, {factor: 2}); } // Helper functions async function selectDelegate() { let result; let delegates = await getDelegates(); + let permissions = await getDelegatesAndPermissions(); let options = ['Add new delegate']; + options = options.concat(delegates.map(function(d) { + let perm = renderTable(permissions, d.address); + return `Account: ${d.address} - Details: ${d.details}` + Details: ${d.details} + Permisions: ${perm}` })); let index = readlineSync.keyInSelect(options, 'Select a delegate:', {cancel: false}); @@ -169,7 +165,7 @@ 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, 2); + let receipt = await common.sendTransaction(changePermissionAction, {factor: 2}); common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'ChangePermission'); console.log(`Permission changed succesfully,`); } @@ -210,9 +206,9 @@ async function addNewDelegate() { }); let addPermissionAction = generalPermissionManager.methods.addDelegate(newDelegate, web3.utils.asciiToHex(details)); - let receipt = await common.sendTransaction(Issuer, addPermissionAction, defaultGasPrice); + let receipt = await common.sendTransaction(addPermissionAction); let event = common.getEventFromLogs(generalPermissionManager._jsonInterface, receipt.logs, 'AddDelegate'); - console.log(`Delegate added succesfully: ${event._delegate} - ${event._details}`); + console.log(`Delegate added succesfully: ${event._delegate} - ${web3.utils.hexToAscii(event._details)}`); isNewDelegate = true; return event._delegate; } @@ -221,8 +217,8 @@ async function getModulesWithPermissions() { let modules = []; let moduleABI = abis.moduleInterface(); - for (const type in MODULES_TYPES) { - let modulesAttached = await securityToken.methods.getModulesByType(MODULES_TYPES[type]).call(); + for (const type in gbl.constants.MODULES_TYPES) { + let modulesAttached = await securityToken.methods.getModulesByType(gbl.constants.MODULES_TYPES[type]).call(); for (const m of modulesAttached) { let contractTemp = new web3.eth.Contract(moduleABI, m); let permissions = await contractTemp.methods.getPermissions().call(); @@ -239,8 +235,55 @@ async function getModulesWithPermissions() { return modules; } +async function getDelegatesAndPermissions() { + let moduleABI = abis.moduleInterface(); + let result = []; + for (const type in gbl.constants.MODULES_TYPES) { + let modulesAttached = await securityToken.methods.getModulesByType(gbl.constants.MODULES_TYPES[type]).call(); + for (const module of modulesAttached) { + let contractTemp = new web3.eth.Contract(moduleABI, module); + let permissions = await contractTemp.methods.getPermissions().call(); + if (permissions.length > 0) { + for (const permission of permissions) { + let allDelegates = await generalPermissionManager.methods.getAllDelegatesWithPerm(module, permission).call(); + let moduleName = web3.utils.hexToUtf8((await securityToken.methods.getModule(module).call())[0]); + let permissionName = web3.utils.hexToUtf8(permission); + for (delegateAddr of allDelegates) { + if (result[delegateAddr] == undefined) { + result[delegateAddr] = [] + } + if (result[delegateAddr][moduleName + '-' + module] == undefined) { + result[delegateAddr][moduleName + '-' + module] = [{permission: permissionName}] + } else { + result[delegateAddr][moduleName + '-' + module].push({permission: permissionName}) + } + } + } + } + } + } + return result +} + +function renderTable(permissions, address) { + let result = ``; + if (permissions[address] != undefined) { + Object.keys(permissions[address]).forEach((module) => { + result += ` + ${module.split('-')[0]} (${module.split('-')[1]}) -> `; + (permissions[address][module]).forEach((perm) => { + result += `${perm.permission}, `; + }) + result = result.slice(0, -2); + }) + } else { + result += `-`; + } + return result +} + module.exports = { - executeApp: async function(remoteNetwork) { - return executeApp(remoteNetwork); + executeApp: async function() { + return executeApp(); } } \ No newline at end of file diff --git a/CLI/commands/scripts/script.sh b/CLI/commands/scripts/script.sh deleted file mode 100755 index 2ee363a8f..000000000 --- a/CLI/commands/scripts/script.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/bash - -if [ $1 = "Whitelist" ]; -then -echo "Running the $1 script"; -node $PWD/CLI/commands/whitelist.js $2 $3 $4 -elif [ $1 = "Multimint" ]; -then -echo "Running the $1 script"; -node $PWD/CLI/commands/multi_mint.js $2 $3 $4 -elif [ $1 = "Accredit" ]; -then -echo "Running the $1 script"; -node $PWD/CLI/commands/accredit.js $2 $3 $4 -elif [ $1 = "NonAccreditedLimit" ]; -then -echo "Running the $1 script"; -node $PWD/CLI/commands/changeNonAccreditedLimit.js $2 $3 $4 -fi diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js new file mode 100644 index 000000000..f581b7339 --- /dev/null +++ b/CLI/commands/sto_manager.js @@ -0,0 +1,1109 @@ +const readlineSync = require('readline-sync'); +const chalk = require('chalk'); +const contracts = require('./helpers/contract_addresses'); +const abis = require('./helpers/contract_abis'); +const common = require('./common/common_functions'); +const gbl = require('./common/global'); +const csvParse = require('./helpers/csv'); +const { table } = require('table'); +const STABLE = 'STABLE'; + +/////////////////// +// Constants +const ACCREDIT_DATA_CSV = `${__dirname}/../data/STO/USDTieredSTO/accredited_data.csv`; +const NON_ACCREDIT_LIMIT_DATA_CSV = `${__dirname}/../data/STO/USDTieredSTO/nonAccreditedLimits_data.csv`; + +/////////////////// +// Crowdsale params +let tokenSymbol; + +//////////////////////// +// Artifacts +let securityTokenRegistry; +let moduleRegistry; +let polyToken; +let usdToken; +let securityToken; + +async function executeApp() { + let exit = false; + while (!exit) { + console.log('\n', chalk.blue('STO Manager - Main Menu'), '\n'); + + // Show non-archived attached STO modules + let stoModules = await getAllModulesByType(gbl.constants.MODULES_TYPES.STO); + let nonArchivedModules = stoModules.filter(m => !m.archived); + if (nonArchivedModules.length > 0) { + console.log(`STO modules attached:`); + nonArchivedModules.map(m => console.log(`- ${m.name} at ${m.address}`)) + } else { + console.log(`There are no STO modules attached`); + } + + let options = []; + if (nonArchivedModules.length > 0) { + options.push('Show existing STO information', 'Modify existing STO'); + } + options.push('Add new STO module'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'EXIT' }); + let optionSelected = index != -1 ? options[index] : 'EXIT'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Show existing STO information': + let stoToShow = selectExistingSTO(nonArchivedModules, true); + await showSTO(stoToShow.name, stoToShow.module); + break; + case 'Modify existing STO': + let stoToModify = selectExistingSTO(nonArchivedModules); + await modifySTO(stoToModify.name, stoToModify.module); + break; + case 'Add new STO module': + await addSTOModule(); + break; + case 'EXIT': + exit = true; + break; + } + } +}; + +function selectExistingSTO(stoModules, showPaused) { + let filteredModules = stoModules; + if (!showPaused) { + filteredModules = stoModules.filter(m => !m.paused); + } + let options = filteredModules.map(m => `${m.name} at ${m.address}`); + let index = readlineSync.keyInSelect(options, 'Select a module: ', { cancel: false }); + console.log('Selected:', options[index], '\n'); + let selectedName = filteredModules[index].name; + let stoABI; + switch (selectedName) { + case 'CappedSTO': + stoABI = abis.cappedSTO(); + break; + case 'USDTieredSTO': + stoABI = abis.usdTieredSTO(); + break; + } + + let stoModule = new web3.eth.Contract(stoABI, filteredModules[index].address); + return { name: selectedName, module: stoModule }; +} + +async function showSTO(selectedSTO, currentSTO) { + switch (selectedSTO) { + case 'CappedSTO': + await cappedSTO_status(currentSTO); + break; + case 'USDTieredSTO': + await usdTieredSTO_status(currentSTO); + await showAccreditedData(currentSTO); + break; + } +} + +async function modifySTO(selectedSTO, currentSTO) { + switch (selectedSTO) { + case 'CappedSTO': + console.log(chalk.red(` + ********************************* + This option is not yet available. + *********************************`)); + break; + case 'USDTieredSTO': + await usdTieredSTO_configure(currentSTO); + break; + } +} + +async function addSTOModule(stoConfig) { + console.log(chalk.blue('Launch STO - Configuration')); + + let optionSelected; + if (typeof stoConfig === 'undefined') { + let availableModules = await moduleRegistry.methods.getModulesByTypeAndToken(gbl.constants.MODULES_TYPES.STO, securityToken.options.address).call(); + let options = await Promise.all(availableModules.map(async function (m) { + let moduleFactoryABI = abis.moduleFactory(); + let moduleFactory = new web3.eth.Contract(moduleFactoryABI, m); + return web3.utils.hexToUtf8(await moduleFactory.methods.name().call()); + })); + let index = readlineSync.keyInSelect(options, 'What type of STO do you want?', { cancel: 'RETURN' }); + optionSelected = index != -1 ? options[index] : 'RETURN'; + } else { + optionSelected = stoConfig.type; + } + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'CappedSTO': + let cappedSTO = await cappedSTO_launch(stoConfig); + await cappedSTO_status(cappedSTO); + break; + case 'USDTieredSTO': + let usdTieredSTO = await usdTieredSTO_launch(stoConfig); + await usdTieredSTO_status(usdTieredSTO); + break; + } +} + +//////////////// +// Capped STO // +//////////////// +async function cappedSTO_launch(stoConfig) { + console.log(chalk.blue('Launch STO - Capped STO in No. of Tokens')); + + let cappedSTOFactoryABI = abis.cappedSTOFactory(); + let cappedSTOFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.MODULES_TYPES.STO, "CappedSTO"); + let cappedSTOFactory = new web3.eth.Contract(cappedSTOFactoryABI, cappedSTOFactoryAddress); + cappedSTOFactory.setProvider(web3.currentProvider); + let stoFee = new web3.utils.BN(await cappedSTOFactory.methods.getSetupCost().call()); + + let contractBalance = new web3.utils.BN(await polyToken.methods.balanceOf(securityToken._address).call()); + if (contractBalance.lt(stoFee)) { + let transferAmount = stoFee.sub(contractBalance); + let ownerBalance = new web3.utils.BN(await polyToken.methods.balanceOf(Issuer.address).call()); + if (ownerBalance.lt(transferAmount)) { + console.log(chalk.red(`\n**************************************************************************************************************************************************`)); + console.log(chalk.red(`Not enough balance to pay the CappedSTO fee, Requires ${web3.utils.fromWei(transferAmount)} POLY but have ${web3.utils.fromWei(ownerBalance)} POLY. Access POLY faucet to get the POLY to complete this txn`)); + console.log(chalk.red(`**************************************************************************************************************************************************\n`)); + return; + } else { + let transferAction = polyToken.methods.transfer(securityToken._address, transferAmount); + let receipt = await common.sendTransaction(transferAction, { factor: 2 }); + let event = common.getEventFromLogs(polyToken._jsonInterface, receipt.logs, 'Transfer'); + console.log(`Number of POLY sent: ${web3.utils.fromWei(new web3.utils.BN(event._value))}`) + } + } + + let oneMinuteFromNow = new web3.utils.BN((Math.floor(Date.now() / 1000) + 60)); + let oneMonthFromNow = new web3.utils.BN((Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60))); + + let cappedSTOconfig = {}; + let useConfigFile = typeof stoConfig !== 'undefined'; + if (!useConfigFile) { + cappedSTOconfig.cap = readlineSync.question('How many tokens do you plan to sell on the STO? (500.000): '); + if (cappedSTOconfig.cap == "") cappedSTOconfig.cap = 500000; + + cappedSTOconfig.raiseType = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise or leave empty for Ether raise (E): '); + if (cappedSTOconfig.raiseType.toUpperCase() == 'P') { + cappedSTOconfig.raiseType = [gbl.constants.FUND_RAISE_TYPES.POLY]; + } else { + cappedSTOconfig.raiseType = [gbl.constants.FUND_RAISE_TYPES.ETH]; + } + + cappedSTOconfig.rate = readlineSync.question(`Enter the rate (1 ${cappedSTOconfig.raiseType == gbl.constants.FUND_RAISE_TYPES.POLY ? 'POLY' : 'ETH'} = X ${tokenSymbol}) for the STO (1000): `); + if (cappedSTOconfig.rate == "") cappedSTOconfig.rate = 1000; + + cappedSTOconfig.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): '); + if (cappedSTOconfig.wallet == "") cappedSTOconfig.wallet = Issuer.address; + + cappedSTOconfig.startTime = readlineSync.question('Enter the start time for the STO (Unix Epoch time)\n(1 minutes from now = ' + oneMinuteFromNow + ' ): '); + + cappedSTOconfig.endTime = readlineSync.question('Enter the end time for the STO (Unix Epoch time)\n(1 month from now = ' + oneMonthFromNow + ' ): '); + } else { + cappedSTOconfig = stoConfig; + } + + if (cappedSTOconfig.startTime == "") cappedSTOconfig.startTime = oneMinuteFromNow; + if (cappedSTOconfig.endTime == "") cappedSTOconfig.endTime = oneMonthFromNow; + + let cappedSTOABI = abis.cappedSTO(); + let configureFunction = cappedSTOABI.find(o => o.name === 'configure' && o.type === 'function'); + let bytesSTO = web3.eth.abi.encodeFunctionCall(configureFunction, + [cappedSTOconfig.startTime, + cappedSTOconfig.endTime, + web3.utils.toWei(cappedSTOconfig.cap.toString()), + web3.utils.toWei(cappedSTOconfig.rate.toString()), + cappedSTOconfig.raiseType, + cappedSTOconfig.wallet] + ); + + let addModuleAction = securityToken.methods.addModule(cappedSTOFactoryAddress, bytesSTO, stoFee, 0); + let receipt = await common.sendTransaction(addModuleAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); + console.log(`STO deployed at address: ${event._module}`); + + let cappedSTO = new web3.eth.Contract(cappedSTOABI, event._module); + cappedSTO.setProvider(web3.currentProvider); + + return cappedSTO; +} + +async function cappedSTO_status(currentSTO) { + let displayStartTime = await currentSTO.methods.startTime().call(); + let displayEndTime = await currentSTO.methods.endTime().call(); + let displayRate = new web3.utils.BN(await currentSTO.methods.rate().call()); + let displayCap = new web3.utils.BN(await currentSTO.methods.cap().call()); + let displayWallet = await currentSTO.methods.wallet().call(); + let displayRaiseType = await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.ETH).call() ? 'ETH' : 'POLY'; + let displayFundsRaised = await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[displayRaiseType]).call(); + let displayWalletBalance = web3.utils.fromWei(await getBalance(displayWallet, gbl.constants.FUND_RAISE_TYPES[displayRaiseType])); + let displayTokensSold = new web3.utils.BN(await currentSTO.methods.totalTokensSold().call()); + let displayInvestorCount = await currentSTO.methods.investorCount().call(); + let displayTokenSymbol = await securityToken.methods.symbol().call(); + + let now = Math.floor(Date.now() / 1000); + let timeTitle; + let timeRemaining; + + if (now < displayStartTime) { + timeTitle = "STO starts in: "; + timeRemaining = displayStartTime - now; + } else { + timeTitle = "Time remaining:"; + timeRemaining = displayEndTime - now; + } + + timeRemaining = common.convertToDaysRemaining(timeRemaining); + + console.log(` + *************** STO Information *************** + - Address: ${currentSTO.options.address} + - Raise Cap: ${web3.utils.fromWei(displayCap)} ${displayTokenSymbol.toUpperCase()} + - Start Time: ${new Date(displayStartTime * 1000)} + - End Time: ${new Date(displayEndTime * 1000)} + - Raise Type: ${displayRaiseType} + - Rate: 1 ${displayRaiseType} = ${web3.utils.fromWei(displayRate)} ${displayTokenSymbol.toUpperCase()} + - Wallet: ${displayWallet} + - Wallet Balance: ${displayWalletBalance} ${displayRaiseType} + ----------------------------------------------- + - ${timeTitle} ${timeRemaining} + - Funds raised: ${web3.utils.fromWei(displayFundsRaised)} ${displayRaiseType} + - Tokens sold: ${web3.utils.fromWei(displayTokensSold)} ${displayTokenSymbol.toUpperCase()} + - Tokens remaining: ${web3.utils.fromWei(displayCap.sub(displayTokensSold))} ${displayTokenSymbol.toUpperCase()} + - Investor count: ${displayInvestorCount} + `); +} + +//////////////////// +// USD Tiered STO // +//////////////////// +function fundingConfigUSDTieredSTO() { + let funding = {}; + + 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')) { + funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.ETH); + } + if (selectedFunding.includes('P')) { + funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.POLY); + } + 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.STABLE]; + } + + return funding; +} + +async function addressesConfigUSDTieredSTO(usdTokenRaise) { + + let addresses, menu; + + do { + + 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; + + 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; + + let listOfAddress; + + if (usdTokenRaise) { + addresses.usdToken = readlineSync.question('Enter the address (or multiple addresses separated by commas) of the USD stable coin(s) (' + 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 = [] + addresses.usdToken = []; + } + + if ((usdTokenRaise) && (!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(",") + } + + } while (menu); + + 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": symbol, "address": address }) + } + return list +} + +function tiersConfigUSDTieredSTO(polyRaise) { + let tiers = {}; + + let defaultTiers = 3; + tiers.tiers = parseInt(readlineSync.question(`Enter the number of tiers for the STO? (${defaultTiers}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than zero', + defaultInput: defaultTiers + })); + + let defaultTokensPerTier = [190000000, 100000000, 200000000]; + let defaultRatePerTier = ['0.05', '0.10', '0.15']; + let defaultTokensPerTierDiscountPoly = [90000000, 50000000, 100000000]; + let defaultRatePerTierDiscountPoly = ['0.025', '0.05', '0.075']; + tiers.tokensPerTier = []; + tiers.ratePerTier = []; + tiers.tokensPerTierDiscountPoly = []; + tiers.ratePerTierDiscountPoly = []; + for (let i = 0; i < tiers.tiers; i++) { + tiers.tokensPerTier[i] = readlineSync.question(`How many tokens do you plan to sell on tier No. ${i + 1}? (${defaultTokensPerTier[i]}): `, { + limit: function (input) { + return parseFloat(input) > 0; + }, + limitMessage: 'Must be greater than zero', + defaultInput: defaultTokensPerTier[i] + }); + + tiers.ratePerTier[i] = readlineSync.question(`What is the USD per token rate for tier No. ${i + 1}? (${defaultRatePerTier[i]}): `, { + limit: function (input) { + return parseFloat(input) > 0; + }, + limitMessage: 'Must be greater than zero', + defaultInput: defaultRatePerTier[i] + }); + + if (polyRaise && readlineSync.keyInYNStrict(`Do you plan to have a discounted rate for POLY investments for tier No. ${i + 1}? `)) { + tiers.tokensPerTierDiscountPoly[i] = readlineSync.question(`How many of those tokens do you plan to sell at discounted rate on tier No. ${i + 1}? (${defaultTokensPerTierDiscountPoly[i]}): `, { + limit: function (input) { + return parseFloat(input) < parseFloat(tiers.tokensPerTier[i]); + }, + limitMessage: 'Must be less than the No. of tokens of the tier', + defaultInput: defaultTokensPerTierDiscountPoly[i] + }); + + tiers.ratePerTierDiscountPoly[i] = readlineSync.question(`What is the discounted rate for tier No. ${i + 1}? (${defaultRatePerTierDiscountPoly[i]}): `, { + limit: function (input) { + return parseFloat(input) < parseFloat(tiers.ratePerTier[i]); + }, + limitMessage: 'Must be less than the rate of the tier', + defaultInput: defaultRatePerTierDiscountPoly[i] + }); + } else { + tiers.tokensPerTierDiscountPoly[i] = 0; + tiers.ratePerTierDiscountPoly[i] = 0; + } + } + + return tiers; +} + +function limitsConfigUSDTieredSTO() { + let limits = {}; + + let defaultMinimumInvestment = 5; + limits.minimumInvestmentUSD = readlineSync.question(`What is the minimum investment in USD? (${defaultMinimumInvestment}): `, { + limit: function (input) { + return parseFloat(input) > 0; + }, + limitMessage: "Must be greater than zero", + defaultInput: defaultMinimumInvestment + }); + + let nonAccreditedLimit = 2500; + limits.nonAccreditedLimitUSD = readlineSync.question(`What is the default limit for non accredited investors in USD? (${nonAccreditedLimit}): `, { + limit: function (input) { + return parseFloat(input) >= parseFloat(limits.minimumInvestmentUSD); + }, + limitMessage: "Must be greater than minimum investment", + defaultInput: nonAccreditedLimit + }); + + return limits; +} + +function timesConfigUSDTieredSTO(stoConfig) { + let times = {}; + + let oneMinuteFromNow = Math.floor(Date.now() / 1000) + 60; + if (typeof stoConfig === 'undefined') { + times.startTime = parseInt(readlineSync.question('Enter the start time for the STO (Unix Epoch time)\n(1 minutes from now = ' + oneMinuteFromNow + ' ): ', { + limit: function (input) { + return parseInt(input) > Math.floor(Date.now() / 1000); + }, + limitMessage: "Must be a future time", + defaultInput: oneMinuteFromNow + })); + } else { + times.startTime = stoConfig.times.startTime; + } + if (times.startTime == "") times.startTime = oneMinuteFromNow; + + let oneMonthFromNow = Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60); + if (typeof stoConfig === 'undefined') { + times.endTime = parseInt(readlineSync.question('Enter the end time for the STO (Unix Epoch time)\n(1 month from now = ' + oneMonthFromNow + ' ): ', { + limit: function (input) { + return parseInt(input) > times.startTime; + }, + limitMessage: "Must be greater than Start Time", + defaultInput: oneMonthFromNow + })); + } else { + times.endTime = stoConfig.times.startTime; + } + if (times.endTime == "") times.endTime = oneMonthFromNow; + + return times; +} + +async function usdTieredSTO_launch(stoConfig) { + console.log(chalk.blue('Launch STO - USD pegged tiered STO')); + + let usdTieredSTOFactoryABI = abis.usdTieredSTOFactory(); + let usdTieredSTOFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.MODULES_TYPES.STO, 'USDTieredSTO'); + let usdTieredSTOFactory = new web3.eth.Contract(usdTieredSTOFactoryABI, usdTieredSTOFactoryAddress); + usdTieredSTOFactory.setProvider(web3.currentProvider); + let stoFee = new web3.utils.BN(await usdTieredSTOFactory.methods.getSetupCost().call()); + + let contractBalance = new web3.utils.BN(await polyToken.methods.balanceOf(securityToken._address).call()); + if (contractBalance.lt(stoFee)) { + let transferAmount = stoFee.sub(contractBalance); + let ownerBalance = new web3.utils.BN(await polyToken.methods.balanceOf(Issuer.address).call()); + if (ownerBalance.lt(transferAmount)) { + console.log(chalk.red(`\n**************************************************************************************************************************************************`)); + console.log(chalk.red(`Not enough balance to pay the USDTieredSTO fee, Requires ${web3.utils.fromWei(transferAmount)} POLY but have ${web3.utils.fromWei(ownerBalance)} POLY. Access POLY faucet to get the POLY to complete this txn`)); + console.log(chalk.red(`**************************************************************************************************************************************************\n`)); + return; + } else { + let transferAction = polyToken.methods.transfer(securityToken._address, transferAmount); + let receipt = await common.sendTransaction(transferAction, { factor: 2 }); + let event = common.getEventFromLogs(polyToken._jsonInterface, receipt.logs, 'Transfer'); + console.log(`Number of POLY sent: ${web3.utils.fromWei(new web3.utils.BN(event._value))}`) + } + } + + let useConfigFile = typeof stoConfig !== 'undefined'; + let funding = useConfigFile ? stoConfig.funding : fundingConfigUSDTieredSTO(); + 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); + + let usdTieredSTOABI = abis.usdTieredSTO(); + let configureFunction = usdTieredSTOABI.find(o => o.name === 'configure' && o.type === 'function'); + let bytesSTO = web3.eth.abi.encodeFunctionCall(configureFunction, + [times.startTime, + times.endTime, + tiers.ratePerTier.map(r => web3.utils.toWei(r.toString())), + tiers.ratePerTierDiscountPoly.map(rd => web3.utils.toWei(rd.toString())), + tiers.tokensPerTier.map(t => web3.utils.toWei(t.toString())), + tiers.tokensPerTierDiscountPoly.map(td => web3.utils.toWei(td.toString())), + web3.utils.toWei(limits.nonAccreditedLimitUSD.toString()), + web3.utils.toWei(limits.minimumInvestmentUSD.toString()), + funding.raiseType, + addresses.wallet, + addresses.reserveWallet, + addresses.usdToken] + ); + + let addModuleAction = securityToken.methods.addModule(usdTieredSTOFactoryAddress, bytesSTO, stoFee, 0); + let receipt = await common.sendTransaction(addModuleAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); + console.log(`STO deployed at address: ${event._module}`); + + let usdTieredSTO = new web3.eth.Contract(usdTieredSTOABI, event._module); + usdTieredSTO.setProvider(web3.currentProvider); + + return usdTieredSTO; +} + +async function usdTieredSTO_status(currentSTO) { + let displayStartTime = await currentSTO.methods.startTime().call(); + let displayEndTime = await currentSTO.methods.endTime().call(); + let displayCurrentTier = parseInt(await currentSTO.methods.currentTier().call()) + 1; + let test = await currentSTO.methods.nonAccreditedLimitUSD().call(); + let displayNonAccreditedLimitUSD = web3.utils.fromWei(await currentSTO.methods.nonAccreditedLimitUSD().call()); + let displayMinimumInvestmentUSD = web3.utils.fromWei(await currentSTO.methods.minimumInvestmentUSD().call()); + let displayWallet = await currentSTO.methods.wallet().call(); + let displayReserveWallet = await currentSTO.methods.reserveWallet().call(); + let displayTokensSold = web3.utils.fromWei(await currentSTO.methods.getTokensSold().call()); + 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 listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + 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); + } + } + + let displayTiers = ""; + let displayMintedPerTier = ""; + for (let t = 0; t < tiersLength; t++) { + let tier = await currentSTO.methods.tiers(t).call(); + let ratePerTier = tier.rate; + let tokensPerTierTotal = tier.tokenTotal; + let mintedPerTierTotal = tier.mintedTotal; + let mintedPerTierPerRaiseType = await currentSTO.methods.getTokensMintedByTier(t).call(); + + let displayMintedPerTierPerType = ""; + let displayDiscountTokens = ""; + for (const type of raiseTypes) { + let displayDiscountMinted = ""; + let tokensPerTierDiscountPoly = tier.tokensDiscountPoly; + if (tokensPerTierDiscountPoly > 0) { + let ratePerTierDiscountPoly = tier.rateDiscountPoly; + let mintedPerTierDiscountPoly = tier.mintedDiscountPoly; + displayDiscountTokens = ` + Tokens at discounted rate: ${web3.utils.fromWei(tokensPerTierDiscountPoly)} ${displayTokenSymbol} + Discounted rate: ${web3.utils.fromWei(ratePerTierDiscountPoly, 'ether')} USD per Token`; + + displayDiscountMinted = `(${web3.utils.fromWei(mintedPerTierDiscountPoly)} ${displayTokenSymbol} at discounted rate)`; + } + + let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for stable coin(s): ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` + Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } + } + + displayTiers += ` + - Tier ${t + 1}: + Tokens: ${web3.utils.fromWei(tokensPerTierTotal)} ${displayTokenSymbol} + Rate: ${web3.utils.fromWei(ratePerTier)} USD per Token` + + displayDiscountTokens; + + displayMintedPerTier += ` + - Tokens minted in Tier ${t + 1}: ${web3.utils.fromWei(mintedPerTierTotal)} ${displayTokenSymbol}` + + displayMintedPerTierPerType; + } + + let displayFundsRaisedUSD = web3.utils.fromWei(await currentSTO.methods.fundsRaisedUSD().call()); + + let displayWalletBalancePerType = ''; + let displayReserveWalletBalancePerType = ''; + let displayFundsRaisedPerType = ''; + let displayTokensSoldPerType = ''; + for (const type of raiseTypes) { + let balance = await getBalance(displayWallet, gbl.constants.FUND_RAISE_TYPES[type]); + let walletBalance = web3.utils.fromWei(balance); + if ((type == STABLE) && (stableSymbols.length)) { + stableSymbols.forEach(async (stable) => { + let raised = await checkStableBalance(displayWallet, stable.address); + displayWalletBalancePerType += ` + Balance ${stable.symbol}:\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) + } else { + let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); + 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()); + if ((type == STABLE) && (stableSymbols.length)) { + stableSymbols.forEach(async (stable) => { + let raised = await checkStableBalance(displayReserveWallet, stable.address); + displayReserveWalletBalancePerType += ` + Balance ${stable.symbol}:\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) + } 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()); + if ((type == STABLE) && (stableSymbols.length)) { + stableSymbols.forEach(async (stable) => { + let raised = await getStableCoinsRaised(currentSTO, stable.address); + displayFundsRaisedPerType += ` + ${stable.symbol}:\t\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) + } 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()); + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for stable coin(s): ${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.map((obj) => { return obj.symbol }).toString().replace(`,`, ` - `)}` + } + + let now = Math.floor(Date.now() / 1000); + let timeTitle; + let timeRemaining; + if (now < displayStartTime) { + timeTitle = "STO starts in: "; + timeRemaining = displayStartTime - now; + } else { + timeTitle = "Time remaining:"; + timeRemaining = displayEndTime - now; + } + + timeRemaining = common.convertToDaysRemaining(timeRemaining); + + console.log(` + *********************** STO Information *********************** + - Address: ${currentSTO.options.address} + - Start Time: ${new Date(displayStartTime * 1000)} + - End Time: ${new Date(displayEndTime * 1000)} + - Raise Type: ${displayRaiseType} + - Tiers: ${tiersLength}` + + displayTiers + ` + - Minimum Investment: ${displayMinimumInvestmentUSD} USD + - Non Accredited Limit: ${displayNonAccreditedLimitUSD} USD + - Wallet: ${displayWallet}` + + displayWalletBalancePerType + ` + - Reserve Wallet: ${displayReserveWallet}` + + displayReserveWalletBalancePerType + ` + + --------------------------------------------------------------- + - ${timeTitle} ${timeRemaining} + - Is Finalized: ${displayIsFinalized} + - Tokens Sold: ${displayTokensSold} ${displayTokenSymbol}` + + displayTokensSoldPerType + ` + - Current Tier: ${displayCurrentTier}` + + displayMintedPerTier + ` + - Investor count: ${displayInvestorCount} + - Funds Raised` + + displayFundsRaisedPerType + ` + Total USD: ${displayFundsRaisedUSD} USD + `); +} + +async function checkStableBalance(walletAddress, stableAddress) { + let stableCoin = common.connect(abis.erc20(), stableAddress); + try { + return await stableCoin.methods.balanceOf(walletAddress).call(); + } catch (e) { + return "" + } +} + +async function getStableCoinsRaised(currentSTO, address) { + return await currentSTO.methods.stableCoinsRaised(address).call() +} + +async function usdTieredSTO_configure(currentSTO) { + console.log(chalk.blue('STO Configuration - USD Tiered STO')); + + let isFinalized = await currentSTO.methods.isFinalized().call(); + if (isFinalized) { + console.log(chalk.red(`STO is finalized`)); + } else { + let options = []; + 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); + let startTime = await currentSTO.methods.startTime().call.call(); + if (now < startTime) { + options.push('Modify times configuration', 'Modify tiers configuration', 'Modify addresses configuration', + 'Modify limits configuration', 'Modify funding configuration'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let selected = index != -1 ? options[index] : 'Exit'; + switch (selected) { + case 'Finalize STO': + let reserveWallet = await currentSTO.methods.reserveWallet().call(); + let isVerified = await securityToken.methods.verifyTransfer('0x0000000000000000000000000000000000000000', reserveWallet, 0, web3.utils.fromAscii("")).call(); + if (isVerified) { + if (readlineSync.keyInYNStrict()) { + let finalizeAction = currentSTO.methods.finalize(); + await common.sendTransaction(finalizeAction); + } + } else { + console.log(chalk.red(`Reserve wallet (${reserveWallet}) is not able to receive remaining tokens. Check if this address is whitelisted.`)); + } + break; + case 'Change accredited account': + let investor = readlineSync.question('Enter the address to change accreditation: '); + let isAccredited = readlineSync.keyInYNStrict(`Is ${investor} accredited?`); + let investors = [investor]; + let accredited = [isAccredited]; + let changeAccreditedAction = currentSTO.methods.changeAccredited(investors, accredited); + // 2 GAS? + await common.sendTransaction(changeAccreditedAction); + break; + case 'Change accredited in batch': + await changeAccreditedInBatch(currentSTO); + break; + case 'Change non accredited limit for an account': + 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); + await common.sendTransaction(changeNonAccreditedLimitAction); + break; + case 'Change non accredited limits in batch': + await changeNonAccreditedLimitsInBatch(currentSTO); + break; + case 'Modify times configuration': + await modfifyTimes(currentSTO); + await usdTieredSTO_status(currentSTO); + break; + case 'Modify tiers configuration': + await modfifyTiers(currentSTO); + await usdTieredSTO_status(currentSTO); + break; + case 'Modify addresses configuration': + await modfifyAddresses(currentSTO); + await usdTieredSTO_status(currentSTO); + break; + case 'Modify limits configuration': + await modfifyLimits(currentSTO); + await usdTieredSTO_status(currentSTO); + break; + case 'Modify funding configuration': + await modfifyFunding(currentSTO); + await usdTieredSTO_status(currentSTO); + break; + } + } +} + +async function showAccreditedData(currentSTO) { + let accreditedData = await currentSTO.methods.getAccreditedData().call(); + let investorArray = accreditedData[0]; + let accreditedArray = accreditedData[1]; + let nonAccreditedLimitArray = accreditedData[2]; + + if (investorArray.length > 0) { + let dataTable = [['Investor', 'Is accredited', 'Non-accredited limit (USD)']]; + for (let i = 0; i < investorArray.length; i++) { + dataTable.push([ + investorArray[i], + accreditedArray[i] ? 'YES' : 'NO', + accreditedArray[i] ? 'N/A' : (nonAccreditedLimitArray[i] !== '0' ? web3.utils.fromWei(nonAccreditedLimitArray[i]) : 'default') + ]); + } + console.log(); + console.log(`************************************ ACCREDITED DATA *************************************`); + console.log(); + console.log(table(dataTable)); + } else { + console.log(); + console.log(chalk.yellow(`There is no accredited data to show`)); + console.log(); + } + +} + +async function changeAccreditedInBatch(currentSTO) { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ACCREDIT_DATA_CSV}): `, { + defaultInput: ACCREDIT_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && typeof row[1] === 'boolean'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')}`)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, isAccreditedArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to change accredited accounts:\n\n`, investorArray[batch], '\n'); + let action = currentSTO.methods.changeAccredited(investorArray[batch], isAccreditedArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Change accredited transaction was successful.')); + console.log(`${receipt.gasUsed} gas used. Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function changeNonAccreditedLimitsInBatch(currentSTO) { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${NON_ACCREDIT_LIMIT_DATA_CSV}): `, { + defaultInput: NON_ACCREDIT_LIMIT_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && !isNaN(row[1])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')}`)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, limitArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + limitArray[batch] = limitArray[batch].map(a => web3.utils.toWei(new web3.utils.BN(a))); + console.log(`Batch ${batch + 1} - Attempting to change non accredited limit to accounts:\n\n`, investorArray[batch], '\n'); + let action = currentSTO.methods.changeNonAccreditedLimit(investorArray[batch], limitArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Change non accredited limits transaction was successful.')); + console.log(`${receipt.gasUsed} gas used. Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modfifyTimes(currentSTO) { + let times = timesConfigUSDTieredSTO(); + let modifyTimesAction = currentSTO.methods.modifyTimes(times.startTime, times.endTime); + await common.sendTransaction(modifyTimesAction); +} + +async function modfifyLimits(currentSTO) { + let limits = limitsConfigUSDTieredSTO(); + let modifyLimitsAction = currentSTO.methods.modifyLimits(limits.nonAccreditedLimitUSD, limits.minimumInvestmentUSD); + await common.sendTransaction(modifyLimitsAction); +} + +async function modfifyFunding(currentSTO) { + let funding = fundingConfigUSDTieredSTO(); + let modifyFundingAction = currentSTO.methods.modifyFunding(funding.raiseType); + await common.sendTransaction(modifyFundingAction); +} + +async function modfifyAddresses(currentSTO) { + 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); +} + +async function modfifyTiers(currentSTO) { + let tiers = tiersConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.POLY).call()); + let modifyTiersAction = currentSTO.methods.modifyTiers( + tiers.ratePerTier, + tiers.ratePerTierDiscountPoly, + tiers.tokensPerTier, + tiers.tokensPerTierDiscountPoly, + ); + await common.sendTransaction(modifyTiersAction); +} + +////////////////////// +// HELPER FUNCTIONS // +////////////////////// +async function getBalance(from, type) { + switch (type) { + case gbl.constants.FUND_RAISE_TYPES.ETH: + 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.STABLE: + return await usdToken.methods.balanceOf(from).call(); + } +} + +async function getAllModulesByType(type) { + function ModuleInfo(_moduleType, _name, _address, _factoryAddress, _archived, _paused) { + this.name = _name; + this.type = _moduleType; + this.address = _address; + this.factoryAddress = _factoryAddress; + this.archived = _archived; + this.paused = _paused; + } + + let modules = []; + + let allModules = await securityToken.methods.getModulesByType(type).call(); + + for (let i = 0; i < allModules.length; i++) { + let details = await securityToken.methods.getModule(allModules[i]).call(); + let nameTemp = web3.utils.hexToUtf8(details[0]); + let pausedTemp = null; + if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; + let contractTemp = new web3.eth.Contract(abiTemp, details[1]); + pausedTemp = await contractTemp.methods.paused().call(); + } + modules.push(new ModuleInfo(type, nameTemp, details[1], details[2], details[3], pausedTemp)); + } + + return modules; +} + +async function initialize(_tokenSymbol) { + welcome(); + await setup(); + if (typeof _tokenSymbol === 'undefined') { + tokenSymbol = await selectToken(); + } else { + tokenSymbol = _tokenSymbol; + } + let securityTokenAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (securityTokenAddress == '0x0000000000000000000000000000000000000000') { + console.log(chalk.red(`Selected Security Token ${tokenSymbol} does not exist.`)); + process.exit(0); + } + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI, securityTokenAddress); + securityToken.setProvider(web3.currentProvider); +} + +function welcome() { + common.logAsciiBull(); + console.log("****************************************"); + console.log("Welcome to the Command-Line STO Manager."); + console.log("****************************************"); + console.log("The following script will allow you to manage STOs modules."); + console.log("Issuer Account: " + Issuer.address + "\n"); +} + +async function setup() { + try { + let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); + let securityTokenRegistryABI = abis.securityTokenRegistry(); + securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); + securityTokenRegistry.setProvider(web3.currentProvider); + + let moduleRegistryAddress = await contracts.moduleRegistry(); + let moduleRegistryABI = abis.moduleRegistry(); + moduleRegistry = new web3.eth.Contract(moduleRegistryABI, moduleRegistryAddress); + moduleRegistry.setProvider(web3.currentProvider); + + let polytokenAddress = await contracts.polyToken(); + let polytokenABI = abis.polyToken(); + polyToken = new web3.eth.Contract(polytokenABI, polytokenAddress); + polyToken.setProvider(web3.currentProvider); + + //TODO: Use proper DAI token here + let usdTokenAddress = await contracts.usdToken(); + usdToken = new web3.eth.Contract(polytokenABI, usdTokenAddress); + usdToken.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 selectToken() { + let result = null; + + let userTokens = await securityTokenRegistry.methods.getTokensByOwner(Issuer.address).call(); + let tokenDataArray = await Promise.all(userTokens.map(async function (t) { + let tokenData = await securityTokenRegistry.methods.getSecurityTokenData(t).call(); + return { symbol: tokenData[0], address: t }; + })); + let options = tokenDataArray.map(function (t) { + return `${t.symbol} - Deployed at ${t.address}`; + }); + options.push('Enter token symbol manually'); + + let index = readlineSync.keyInSelect(options, 'Select a token:', { cancel: 'EXIT' }); + let selected = index != -1 ? options[index] : 'EXIT'; + switch (selected) { + case 'Enter token symbol manually': + result = readlineSync.question('Enter the token symbol: '); + break; + case 'Exit': + process.exit(); + break; + default: + result = tokenDataArray[index].symbol; + break; + } + + return result; +} + +module.exports = { + executeApp: async function (_tokenSymbol) { + await initialize(_tokenSymbol); + return executeApp(); + }, + addSTOModule: async function (_tokenSymbol, stoConfig) { + await initialize(_tokenSymbol); + return addSTOModule(stoConfig) + } +} diff --git a/CLI/commands/strMigrator.js b/CLI/commands/strMigrator.js index 545455f46..7d4e0248f 100644 --- a/CLI/commands/strMigrator.js +++ b/CLI/commands/strMigrator.js @@ -4,335 +4,371 @@ var request = require('request-promise') var abis = require('./helpers/contract_abis'); var contracts = require('./helpers/contract_addresses'); var common = require('./common/common_functions'); -var global = require('./common/global'); +var gbl = require('./common/global'); let network; - -async function executeApp(toStrAddress, fromTrAddress, fromStrAddress, remoteNetwork) { - network = remoteNetwork; - await global.initialize(remoteNetwork); - - common.logAsciiBull(); - console.log("****************************************"); - console.log("Welcome to the Command-Line STR Migrator"); - console.log("****************************************"); - console.log("The following script will migrate tokens from old STR to new one."); - console.log("Issuer Account: " + Issuer.address + "\n"); - - try { - let toSecurityTokenRegistry = await step_instance_toSTR(toStrAddress); - let fromTickerRegistry = await step_instance_fromTR(fromTrAddress); - let tickers = await step_get_registered_tickers(fromTickerRegistry); - await step_register_tickers(tickers, toSecurityTokenRegistry); - let fromSecurityTokenRegistry = await step_instance_fromSTR(fromStrAddress); - let tokens = await step_get_deployed_tokens(fromSecurityTokenRegistry); - await step_launch_STs(tokens, toSecurityTokenRegistry); - } catch (err) { - console.log(err); - return; +let minNonce; + +async function executeApp(toStrAddress, fromTrAddress, fromStrAddress, singleTicker, tokenAddress, onlyTickers, remoteNetwork) { + network = remoteNetwork; + await global.initialize(remoteNetwork); + + common.logAsciiBull(); + console.log("****************************************"); + console.log("Welcome to the Command-Line STR Migrator"); + console.log("****************************************"); + console.log("The following script will migrate tokens from old STR to new one."); + console.log("Issuer Account: " + Issuer.address + "\n"); + + try { + minNonce = await common.getNonce(Issuer); + let toSecurityTokenRegistry = await step_instance_toSTR(toStrAddress); + if (typeof tokenAddress === 'undefined') { + let fromTickerRegistry = await step_instance_fromTR(fromTrAddress); + let tickers = await step_get_registered_tickers(fromTickerRegistry, singleTicker, onlyTickers); + await step_register_tickers(tickers, toSecurityTokenRegistry); + } + if (typeof onlyTickers === 'undefined') { + let fromSecurityTokenRegistry = await step_instance_fromSTR(fromStrAddress); + let tokens = await step_get_deployed_tokens(fromSecurityTokenRegistry, singleTicker); + await step_launch_STs(tokens, toSecurityTokenRegistry, tokenAddress); } + } catch (err) { + console.log(err); + return; + } } -async function step_instance_toSTR(toStrAddress){ - let _toStrAddress; - if (typeof toStrAddress !== 'undefined') { - if (web3.utils.isAddress(toStrAddress)) { - _toStrAddress = toStrAddress; - } else { - console.log(chalk.red("Entered toStrAddress is not a valid address.")); - return; - } +async function step_instance_toSTR(toStrAddress) { + let _toStrAddress; + if (typeof toStrAddress !== 'undefined') { + if (web3.utils.isAddress(toStrAddress)) { + _toStrAddress = toStrAddress; } else { - _toStrAddress = readlineSync.question('Enter the new SecurityTokenRegistry address to migrate TO: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); + console.log(chalk.red("Entered toStrAddress is not a valid address.")); + return; } - - console.log(`Creating SecurityTokenRegistry contract instance of address: ${_toStrAddress}...`); - let securityTokenRegistryABI = abis.securityTokenRegistry(); - let toSTR = new web3.eth.Contract(securityTokenRegistryABI, _toStrAddress); - toSTR.setProvider(web3.currentProvider); - - return toSTR; + } else { + // _toStrAddress = readlineSync.question('Enter the new SecurityTokenRegistry address to migrate TO: ', { + // limit: function(input) { + // return web3.utils.isAddress(input); + // }, + // limitMessage: "Must be a valid address" + // }); + _toStrAddress = "0x240f9f86b1465bf1b8eb29bc88cbf65573dfdd97"; + } + + console.log(`Creating SecurityTokenRegistry contract instance of address: ${_toStrAddress}...`); + let securityTokenRegistryABI = abis.securityTokenRegistry(); + let toSTR = new web3.eth.Contract(securityTokenRegistryABI, _toStrAddress); + toSTR.setProvider(web3.currentProvider); + + return toSTR; } -async function step_instance_fromTR(fromTrAddress){ - let _fromTrAddress; - if (typeof fromTrAddress !== 'undefined') { - if (web3.utils.isAddress(fromTrAddress)) { - _fromTrAddress = fromTrAddress; - } else { - console.log(chalk.red("Entered fromTrAddress is not a valid address.")); - return; - } +async function step_instance_fromTR(fromTrAddress) { + let _fromTrAddress; + if (typeof fromTrAddress !== 'undefined') { + if (web3.utils.isAddress(fromTrAddress)) { + _fromTrAddress = fromTrAddress; } else { - _fromTrAddress = readlineSync.question('Enter the old TikcerRegistry address to migrate FROM: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); + console.log(chalk.red("Entered fromTrAddress is not a valid address.")); + return; } - - console.log(`Creating TickerRegistry contract instance of address: ${_fromTrAddress}...`); - let tickerRegistryABI = await getABIfromEtherscan(_fromTrAddress); - let fromTR = new web3.eth.Contract(tickerRegistryABI, _fromTrAddress); - fromTR.setProvider(web3.currentProvider); - - return fromTR; + } else { + // _fromTrAddress = readlineSync.question('Enter the old TikcerRegistry address to migrate FROM: ', { + // limit: function(input) { + // return web3.utils.isAddress(input); + // }, + // limitMessage: "Must be a valid address" + // }); + _fromTrAddress = "0xc31714e6759a1ee26db1d06af1ed276340cd4233"; + } + + console.log(`Creating TickerRegistry contract instance of address: ${_fromTrAddress}...`); + let tickerRegistryABI = await getABIfromEtherscan(_fromTrAddress); + let fromTR = new web3.eth.Contract(tickerRegistryABI, _fromTrAddress); + fromTR.setProvider(web3.currentProvider); + + return fromTR; } -async function step_get_registered_tickers(tickerRegistry) { - let tickers = []; - let expiryTime = await tickerRegistry.methods.expiryLimit().call(); - - let events = await tickerRegistry.getPastEvents('LogRegisterTicker', { fromBlock: 0}); - if (events.length == 0) { - console.log("No ticker registration events were emitted."); - } else { - for (let event of events) { - //for (let index = 0; index < 2; index++) { - //const event = events[index]; - let details = await tickerRegistry.methods.getDetails(event.returnValues._symbol).call(); - let _symbol = event.returnValues._symbol; - let _owner = details[0]; - let _name = details[2]; - let _registrationDate = details[1]; - let _status = details[4]; - - console.log(`------------ Ticker Registered ------------`); - console.log(`Ticker: ${_symbol}`); - console.log(`Owner: ${_owner}`); - console.log(`Token name: ${_name}`); - console.log(`Timestamp: ${_registrationDate}`); - console.log(`Transaction hash: ${event.transactionHash}`); - console.log(`-------------------------------------------`); - console.log(`\n`); - - tickers.push({ - ticker: _symbol, - owner: _owner, - name: _name, - registrationDate: new web3.utils.BN(_registrationDate), - expiryDate: new web3.utils.BN(_registrationDate).add(new web3.utils.BN(expiryTime)), - status: _status - }); +async function step_get_registered_tickers(tickerRegistry, singleTicker, onlyTickers) { + let tickers = []; + let expiryTime = await tickerRegistry.methods.expiryLimit().call(); + + let logs = await getLogsFromEtherscan(tickerRegistry.options.address, 0, 'latest', 'LogRegisterTicker(address,string,string,bytes32,uint256)'); + if (logs.length == 0) { + console.log("No ticker registration events were emitted."); + } else { + for (let log of logs) { + let event = common.getEventFromLogs(tickerRegistry._jsonInterface, [log], 'LogRegisterTicker'); + if (typeof singleTicker === 'undefined' || event._symbol == singleTicker) { + let details = await tickerRegistry.methods.getDetails(event._symbol).call(); + let _status = details[4]; + if (typeof onlyTickers === 'undefined' || (onlyTickers && !_status)) { + let expiredTicker = details[0] == '0x0000000000000000000000000000000000000000'; + let _symbol = event._symbol; + let _owner = expiredTicker ? event._owner : details[0]; + let _name = expiredTicker ? event._name : details[2]; + let _registrationDate = expiredTicker ? event._timestamp : details[1]; + + + console.log(`------------ Ticker Registered ------------`); + console.log(`Ticker: ${_symbol}`); + console.log(`Owner: ${_owner}`); + console.log(`Token name: ${_name}`); + console.log(`Timestamp: ${_registrationDate}`); + console.log(`Transaction hash: ${log.transactionHash}`); + console.log(`-------------------------------------------`); + console.log(`\n`); + + tickers.push({ + ticker: _symbol, + owner: _owner, + name: _name, + registrationDate: new web3.utils.BN(_registrationDate), + expiryDate: new web3.utils.BN(_registrationDate).add(new web3.utils.BN(expiryTime)), + status: _status + }); } + } } + } - console.log(chalk.yellow(`${tickers.length} tickers found!`)); - return tickers; + console.log(chalk.yellow(`${tickers.length} tickers found!`)); + return tickers; } async function step_register_tickers(tickers, securityTokenRegistry) { - if (readlineSync.keyInYNStrict(`Do you want to migrate a single Ticker?`)) { - let tickerToMigrate = readlineSync.question(`Enter the ticker to migrate: `); - tickers = tickers.filter(t => t.ticker == tickerToMigrate); - } - - if (tickers.length == 0) { - console.log(chalk.yellow(`There are no tickers to migrate!`)); - } else if (readlineSync.keyInYNStrict(`Do you want to migrate ${tickers.length} Tickers?`)) { - let i = 0; - let succeed = []; - let failed = []; - let totalGas = new web3.utils.BN(0); - for (const t of tickers) { - console.log(`\n`); - console.log(`-------- Migrating ticker No ${++i}--------`); - console.log(`Ticker: ${t.ticker}`); - console.log(``); - try { - let modifyTickerAction = securityTokenRegistry.methods.modifyTicker(t.owner, t.ticker, t.name, t.registrationDate, t.expiryDate, false); - let receipt = await common.sendTransaction(Issuer, modifyTickerAction, defaultGasPrice); - totalGas = totalGas.add(new web3.utils.BN(receipt.gasUsed)); - succeed.push(t); - } catch (error) { - failed.push(t); - console.log(chalk.red(`Transaction failed!!! `)) - console.log(error); - } + if (tickers.length == 0) { + console.log(chalk.yellow(`There are no tickers to migrate!`)); + } else /*if (readlineSync.keyInYNStrict(`Do you want to migrate ${tickers.length} Tickers?`)) */ { + let i = 0; + let succeed = []; + let failed = []; + let totalGas = new web3.utils.BN(0); + let migrateAll = false; + for (const t of tickers) { + if (migrateAll || readlineSync.keyInYNStrict(`Do you want to migrate ${t.ticker}?`)) { + if (!migrateAll) { + migrateAll = readlineSync.keyInYNStrict(`Do you want to migrate all tickers from here?`) } - - logTickerResults(succeed, failed, totalGas); + console.log(`\n`); + console.log(`-------- Migrating ticker No ${++i}--------`); + console.log(`Ticker: ${t.ticker}`); + console.log(``); + try { + let modifyTickerAction = securityTokenRegistry.methods.modifyTicker(t.owner, t.ticker, t.name, t.registrationDate, t.expiryDate, false); + let receipt = await common.sendTransaction(modifyTickerAction, { minNonce: minNonce }); + console.log(minNonce); + minNonce = minNonce + 1; + //totalGas = totalGas.add(new web3.utils.BN(receipt.gasUsed)); + succeed.push(t); + } catch (error) { + failed.push(t); + console.log(chalk.red(`Transaction failed!!! `)) + console.log(error); + } + } } + + logTickerResults(succeed, failed, totalGas); + } } -async function step_instance_fromSTR(fromStrAddress){ - let _fromStrAddress; - if (typeof fromStrAddress !== 'undefined') { - if (web3.utils.isAddress(fromStrAddress)) { - _fromStrAddress = fromStrAddress; - } else { - console.log(chalk.red("Entered fromStrAddress is not a valid address.")); - return; - } +async function step_instance_fromSTR(fromStrAddress) { + let _fromStrAddress; + if (typeof fromStrAddress !== 'undefined') { + if (web3.utils.isAddress(fromStrAddress)) { + _fromStrAddress = fromStrAddress; } else { - _fromStrAddress = readlineSync.question('Enter the old SecurityTokenRegistry address to migrate FROM: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); + console.log(chalk.red("Entered fromStrAddress is not a valid address.")); + return; } - - console.log(`Creating SecurityTokenRegistry contract instance of address: ${_fromStrAddress}...`); - let securityTokenRegistryABI = await getABIfromEtherscan(_fromStrAddress); - let fromSTR = new web3.eth.Contract(securityTokenRegistryABI, _fromStrAddress); - fromSTR.setProvider(web3.currentProvider); - - return fromSTR; + } else { + // _fromStrAddress = readlineSync.question('Enter the old SecurityTokenRegistry address to migrate FROM: ', { + // limit: function(input) { + // return web3.utils.isAddress(input); + // }, + // limitMessage: "Must be a valid address" + // }); + _fromStrAddress = "0xef58491224958d978facf55d2120c55a24516b98"; + } + + console.log(`Creating SecurityTokenRegistry contract instance of address: ${_fromStrAddress}...`); + let securityTokenRegistryABI = await getABIfromEtherscan(_fromStrAddress); + let fromSTR = new web3.eth.Contract(securityTokenRegistryABI, _fromStrAddress); + fromSTR.setProvider(web3.currentProvider); + + return fromSTR; } -async function step_get_deployed_tokens(securityTokenRegistry) { - let tokens = []; - - let events = await securityTokenRegistry.getPastEvents('LogNewSecurityToken', { fromBlock: 0}); - if (events.length == 0) { - console.log("No security token events were emitted."); - } else { - for (let event of events) { - //for (let index = 0; index < 2; index++) { - //const event = events[index]; - let tokenAddress = event.returnValues._securityTokenAddress; - let securityTokenABI = JSON.parse(require('fs').readFileSync('./CLI/data/SecurityToken1-4-0.json').toString()).abi; - console.log(`Creating SecurityToken contract instance of address: ${tokenAddress}...`); - let token = new web3.eth.Contract(securityTokenABI, tokenAddress); - token.setProvider(web3.currentProvider); - - let tokenName = await token.methods.name().call(); - let tokenSymbol = await token.methods.symbol().call(); - let tokenOwner = await token.methods.owner().call(); - let tokenDetails = await token.methods.tokenDetails().call(); - let tokenDivisible = await token.methods.granularity().call() == 1; - let tokenDeployedAt = (await web3.eth.getBlock(event.blockNumber)).timestamp; - - let gmtAddress = (await token.methods.getModule(2, 0).call())[1]; - let gtmABI = JSON.parse(require('fs').readFileSync('./CLI/data/GeneralTransferManager1-4-0.json').toString()).abi; - let gmt = new web3.eth.Contract(gtmABI, gmtAddress); - let gtmEvents = await gmt.getPastEvents('LogModifyWhitelist', { fromBlock: event.blockNumber}); - - let mintedEvents = []; - if (gtmEvents.length > 0) { - mintedEvents = await token.getPastEvents('Minted', { fromBlock: event.blockNumber}); - } - - console.log(`--------- SecurityToken launched ---------`); - console.log(`Token address: ${event.returnValues._securityTokenAddress}`); - console.log(`Symbol: ${tokenSymbol}`); - console.log(`Name: ${tokenName}`); - console.log(`Owner: ${tokenOwner}`); - console.log(`Details: ${tokenDetails}`); - console.log(`Divisble: ${tokenDivisible}`); - console.log(`Deployed at: ${tokenDeployedAt}`); - console.log(`Transaction hash: ${event.transactionHash}`); - console.log(`------------------------------------------`); - console.log(``); - - - tokens.push({ - name: tokenName, - ticker: tokenSymbol, - owner: tokenOwner, - details: tokenDetails, - address: tokenAddress, - deployedAt: tokenDeployedAt, - divisble: tokenDivisible, - gmtEvents: gtmEvents, - mintedEvents: mintedEvents - }); +async function step_get_deployed_tokens(securityTokenRegistry, singleTicker) { + let tokens = []; + + //let events = await securityTokenRegistry.getPastEvents('LogNewSecurityToken', { fromBlock: 0}); + let logs = await getLogsFromEtherscan(securityTokenRegistry.options.address, 0, 'latest', 'LogNewSecurityToken(string,address,address)'); + if (logs.length == 0) { + console.log("No security token events were emitted."); + } else { + for (let log of logs) { + let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, [log], 'LogNewSecurityToken'); + if (typeof singleTicker === 'undefined' || event._ticker == singleTicker) { + let tokenAddress = event._securityTokenAddress; + let securityTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../data/SecurityToken1-4-0.json`).toString()).abi; + console.log(`Creating SecurityToken contract instance of address: ${tokenAddress}...`); + let token = new web3.eth.Contract(securityTokenABI, tokenAddress); + token.setProvider(web3.currentProvider); + + let tokenName = await token.methods.name().call(); + let tokenSymbol = await token.methods.symbol().call(); + let tokenOwner = await token.methods.owner().call(); + let tokenDetails = await token.methods.tokenDetails().call(); + let tokenDivisible = await token.methods.granularity().call() == 1; + let tokenDeployedAt = (await getBlockfromEtherscan(web3.utils.hexToNumber(log.blockNumber))).timeStamp; + + + let gmtAddress = (await token.methods.getModule(2, 0).call())[1]; + let gtmABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../data/GeneralTransferManager1-4-0.json`).toString()).abi; + let gmt = new web3.eth.Contract(gtmABI, gmtAddress); + //let gtmEvents = await gmt.getPastEvents('LogModifyWhitelist', { fromBlock: event.blockNumber}); + let gtmLogs = await getLogsFromEtherscan(gmt.options.address, 0, 'latest', 'LogModifyWhitelist(address,uint256,address,uint256,uint256,uint256,bool)'); + let gtmEvents = common.getMultipleEventsFromLogs(gmt._jsonInterface, gtmLogs, 'LogModifyWhitelist'); + + let mintedEvents = []; + if (gtmEvents.length > 0) { + //mintedEvents = await token.getPastEvents('Minted', { fromBlock: event.blockNumber}); + let mintedLogs = await getLogsFromEtherscan(token.options.address, 0, 'latest', 'Minted(address,uint256)'); + mintedEvents = common.getMultipleEventsFromLogs(token._jsonInterface, mintedLogs, 'Minted'); } + + console.log(`--------- SecurityToken launched ---------`); + console.log(`Token address: ${event._securityTokenAddress}`); + console.log(`Symbol: ${tokenSymbol}`); + console.log(`Name: ${tokenName}`); + console.log(`Owner: ${tokenOwner}`); + console.log(`Details: ${tokenDetails}`); + console.log(`Divisble: ${tokenDivisible}`); + console.log(`Deployed at: ${tokenDeployedAt}`); + console.log(`Transaction hash: ${log.transactionHash}`); + console.log(`------------------------------------------`); + console.log(``); + + + tokens.push({ + name: tokenName, + ticker: tokenSymbol, + owner: tokenOwner, + details: tokenDetails, + address: tokenAddress, + deployedAt: tokenDeployedAt, + divisble: tokenDivisible, + gmtEvents: gtmEvents, + mintedEvents: mintedEvents + }); + } } + } - console.log(chalk.yellow(`${tokens.length} security tokens found!`)); - return tokens; + console.log(chalk.yellow(`${tokens.length} security tokens found!`)); + return tokens; } -async function step_launch_STs(tokens, securityTokenRegistry) { - if (readlineSync.keyInYNStrict(`Do you want to migrate a single Security Token?`)) { - let tokenToMigrate = readlineSync.question(`Enter the Security Token symbol to migrate: `); - tokens = tokens.filter(t => t.ticker == tokenToMigrate); - } - - if (tickers.length == 0) { - console.log(chalk.yellow(`There are no security tokens to migrate!`)); - } else if (readlineSync.keyInYNStrict(`Do you want to migrate ${tokens.length} Security Tokens?`)) { - let i = 0; - let succeed = []; - let failed = []; - let totalGas = new web3.utils.BN(0); - let polymathRegistryAddress = await contracts.polymathRegistry(); - let STFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/STFactory.json').toString()).abi; - let STFactoryAddress = await securityTokenRegistry.methods.getSTFactoryAddress().call(); - let STFactory = new web3.eth.Contract(STFactoryABI, STFactoryAddress); - for (const t of tokens) { - console.log(`\n`); - console.log(`-------- Migrating token No ${++i}--------`); - console.log(`Token symbol: ${t.ticker}`); - console.log(`Token address: ${t.address}`); - console.log(``); - try { - // Deploying 2.0.0 Token - let deployTokenAction = STFactory.methods.deployToken(t.name, t.ticker, 18, t.details, Issuer.address, t.divisble, polymathRegistryAddress) - let deployTokenReceipt = await common.sendTransaction(Issuer, deployTokenAction, defaultGasPrice); - // Instancing Security Token - let newTokenAddress = deployTokenReceipt.logs[deployTokenReceipt.logs.length -1].address; //Last log is the ST creation - let newTokenABI = abis.securityToken(); - let newToken = new web3.eth.Contract(newTokenABI, newTokenAddress); - - // Checking if the old Security Token has activity - if (t.gmtEvents.length > 0) { - // Instancing GeneralTransferManager - let gmtABI = abis.generalTransferManager(); - let gmtAddress = (await newToken.methods.getModulesByName(web3.utils.toHex("GeneralTransferManager")).call())[0]; - let gmt = new web3.eth.Contract(gmtABI, gmtAddress); - // Whitelisting investors - for (const gmtEvent of t.gmtEvents) { - let modifyWhitelistAction = gmt.methods.modifyWhitelist( - gmtEvent.returnValues._investor, - new web3.utils.BN(gmtEvent.returnValues._fromTime), - new web3.utils.BN(gmtEvent.returnValues._toTime), - new web3.utils.BN(gmtEvent.returnValues._expiryTime), - gmtEvent.returnValues._canBuyFromSTO - ); - let modifyWhitelistReceipt = await common.sendTransaction(Issuer, modifyWhitelistAction, defaultGasPrice); - totalGas = totalGas.add(new web3.utils.BN(modifyWhitelistReceipt.gasUsed)); - } - // Minting tokens - for (const mintedEvent of t.mintedEvents) { - let mintAction = newToken.methods.mint(mintedEvent.returnValues.to, new web3.utils.BN(mintedEvent.returnValues.value)); - let mintReceipt = await common.sendTransaction(Issuer, mintAction, defaultGasPrice); - totalGas = totalGas.add(new web3.utils.BN(mintReceipt.gasUsed)); - } - } - - // Transferring onweship to the original owner - let transferOwnershipAction = newToken.methods.transferOwnership(t.owner); - let transferOwnershipReceipt = await common.sendTransaction(Issuer, transferOwnershipAction, defaultGasPrice); - totalGas = totalGas.add(new web3.utils.BN(transferOwnershipReceipt.gasUsed)); - - // Adding 2.0.0 Security Token to SecurityTokenRegistry - let modifySecurityTokenAction = securityTokenRegistry.methods.modifySecurityToken(t.name, t.ticker, t.owner, newTokenAddress, t.details, t.deployedAt); - let modifySecurityTokenReceipt = await common.sendTransaction(Issuer, modifySecurityTokenAction, defaultGasPrice); - totalGas = totalGas.add(new web3.utils.BN(modifySecurityTokenReceipt.gasUsed)); - - succeed.push(t); - } catch (error) { - failed.push(t); - console.log(chalk.red(`Transaction failed!!! `)) - console.log(error); - } +async function step_launch_STs(tokens, securityTokenRegistry, tokenAddress) { + if (tokens.length == 0) { + console.log(chalk.yellow(`There are no security tokens to migrate!`)); + } else /*if (readlineSync.keyInYNStrict(`Do you want to migrate ${tokens.length} Security Tokens?`))*/ { + let i = 0; + let succeed = []; + let failed = []; + let totalGas = new web3.utils.BN(0); + let polymathRegistryAddress = await contracts.polymathRegistry(); + let STFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/STFactory.json`).toString()).abi; + let STFactoryAddress = await securityTokenRegistry.methods.getSTFactoryAddress().call(); + let STFactory = new web3.eth.Contract(STFactoryABI, STFactoryAddress); + for (const t of tokens) { + console.log(`\n`); + console.log(`-------- Migrating token No ${++i}--------`); + console.log(`Token symbol: ${t.ticker}`); + console.log(`Token old address: ${t.address}`); + console.log(``); + try { + // Deploying 2.0.0 Token + let newTokenAddress; + if (tokens.length == 1 && typeof tokenAddress !== 'undefined') { + if (web3.utils.isAddress(tokenAddress)) { + newTokenAddress = tokenAddress; + } else { + console.log(chalk.red('Given tokenAddress is not an address!!')); + process.exit(0); + } + } else { + let deployTokenAction = STFactory.methods.deployToken(t.name, t.ticker, 18, t.details, Issuer.address, t.divisble, polymathRegistryAddress) + let deployTokenReceipt = await common.sendTransaction(deployTokenAction, { minNonce: minNonce }); + minNonce = minNonce + 1; + // Instancing Security Token + newTokenAddress = deployTokenReceipt.logs[deployTokenReceipt.logs.length - 1].address; //Last log is the ST creation + } + console.log(chalk.green(`The migrated to 2.0.0 Security Token address is ${newTokenAddress}`)); + let newTokenABI = abis.securityToken(); + let newToken = new web3.eth.Contract(newTokenABI, newTokenAddress); + + // Checking if the old Security Token has activity + if (t.gmtEvents.length > 0) { + // Instancing GeneralTransferManager + let gmtABI = abis.generalTransferManager(); + let gmtAddress = (await newToken.methods.getModulesByName(web3.utils.toHex("GeneralTransferManager")).call())[0]; + let gmt = new web3.eth.Contract(gmtABI, gmtAddress); + // Whitelisting investors + for (const gmtEvent of t.gmtEvents) { + let modifyWhitelistAction = gmt.methods.modifyWhitelist( + gmtEvent._investor, + new web3.utils.BN(gmtEvent._fromTime), + new web3.utils.BN(gmtEvent._toTime), + new web3.utils.BN(gmtEvent._expiryTime), + gmtEvent._canBuyFromSTO + ); + let modifyWhitelistReceipt = await common.sendTransaction(modifyWhitelistAction, { minNonce: minNonce }); + minNonce = minNonce + 1; + //totalGas = totalGas.add(new web3.utils.BN(modifyWhitelistReceipt.gasUsed)); + } + // Minting tokens + for (const mintedEvent of t.mintedEvents) { + let mintAction = newToken.methods.mint(mintedEvent.to, new web3.utils.BN(mintedEvent.amount)); + let mintReceipt = await common.sendTransaction(mintAction, { minNonce: minNonce }); + minNonce = minNonce + 1; + //totalGas = totalGas.add(new web3.utils.BN(mintReceipt.gasUsed)); + } } - logTokenResults(succeed, failed, totalGas); + // Transferring onweship to the original owner + let transferOwnershipAction = newToken.methods.transferOwnership(t.owner); + let transferOwnershipReceipt = await common.sendTransaction(transferOwnershipAction, { minNonce: minNonce }); + minNonce = minNonce + 1; + //totalGas = totalGas.add(new web3.utils.BN(transferOwnershipReceipt.gasUsed)); + + // Adding 2.0.0 Security Token to SecurityTokenRegistry + let modifySecurityTokenAction = securityTokenRegistry.methods.modifySecurityToken(t.name, t.ticker, t.owner, newTokenAddress, t.details, t.deployedAt); + let modifySecurityTokenReceipt = await common.sendTransaction(modifySecurityTokenAction, { minNonce: minNonce }); + minNonce = minNonce + 1; + //totalGas = totalGas.add(new web3.utils.BN(modifySecurityTokenReceipt.gasUsed)); + + succeed.push(t); + console.log('done'); + } catch (error) { + failed.push(t); + console.log(chalk.red(`Transaction failed!!! `)) + console.log(error); + } } + + logTokenResults(succeed, failed, totalGas); + } } function logTokenResults(succeed, failed, totalGas) { - console.log(` + console.log(` -------------------------------------------- --------- Token Migration Results ---------- -------------------------------------------- @@ -346,7 +382,7 @@ ${failed.map(token => chalk.red(`${token.symbol} at ${token.address}`)).join('\n } function logTickerResults(succeed, failed, totalGas) { - console.log(` + console.log(` -------------------------------------------- --------- Ticker Migration Results --------- -------------------------------------------- @@ -359,25 +395,62 @@ ${failed.map(ticker => chalk.red(`${ticker.ticker}`)).join('\n')} `); } +async function getLogsFromEtherscan(_address, _fromBlock, _toBlock, _eventSignature) { + let urlDomain = network == 'kovan' ? 'api-kovan' : 'api'; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: 'logs', + action: 'getLogs', + fromBlock: _fromBlock, + toBlock: _toBlock, + address: _address, + topic0: web3.utils.sha3(_eventSignature), + apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' + }, + method: 'GET', + json: true + }; + let data = await request(options); + return data.result; +} + async function getABIfromEtherscan(_address) { - let urlDomain = network == 'kovan' ? 'api-kovan' : 'api'; - const options = { - url: `https://${urlDomain}.etherscan.io/api`, - qs: { - module: 'contract', - action: 'getabi', - address: _address, - apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' - }, - method: 'GET', - json: true - }; - let data = await request(options); - return JSON.parse(data.result); + let urlDomain = remoteNetwork == 'kovan' ? 'api-kovan' : 'api'; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: 'contract', + action: 'getabi', + address: _address, + apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' + }, + method: 'GET', + json: true + }; + let data = await request(options); + return JSON.parse(data.result); +} + +async function getBlockfromEtherscan(_blockNumber) { + let urlDomain = network == 'kovan' ? 'api-kovan' : 'api'; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: 'block', + action: 'getblockreward', + blockno: _blockNumber, + apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' + }, + method: 'GET', + json: true + }; + let data = await request(options); + return data.result; } module.exports = { - executeApp: async function(toStrAddress, fromTrAddress, fromStrAddress, remoteNetwork) { - return executeApp(toStrAddress, fromTrAddress, fromStrAddress, remoteNetwork); - } + executeApp: async function (toStrAddress, fromTrAddress, fromStrAddress, singleTicker, tokenAddress, onlyTickers, remoteNetwork) { + return executeApp(toStrAddress, fromTrAddress, fromStrAddress, singleTicker, tokenAddress, onlyTickers, remoteNetwork); + } }; \ No newline at end of file diff --git a/CLI/commands/token_manager.js b/CLI/commands/token_manager.js new file mode 100644 index 000000000..fbe89b38f --- /dev/null +++ b/CLI/commands/token_manager.js @@ -0,0 +1,704 @@ +// Libraries for terminal prompts +const readlineSync = require('readline-sync'); +const chalk = require('chalk'); +const stoManager = require('./sto_manager'); +const transferManager = require('./transfer_manager'); +const common = require('./common/common_functions'); +const gbl = require('./common/global'); +const csvParse = require('./helpers/csv'); + +// Constants +const MULTIMINT_DATA_CSV = './CLI/data/ST/multi_mint_data.csv'; + +// Load contract artifacts +const contracts = require('./helpers/contract_addresses'); +const abis = require('./helpers/contract_abis'); + +let securityTokenRegistry; +let polyToken; +let featureRegistry; +let securityToken; + +let allModules; +let tokenSymbol + +async function setup() { + try { + 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); + + let featureRegistryAddress = await contracts.featureRegistry(); + let featureRegistryABI = abis.featureRegistry(); + featureRegistry = new web3.eth.Contract(featureRegistryABI, featureRegistryAddress); + featureRegistry.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.`)); + process.exit(0); + } +} + +// Start function +async function executeApp() { + await showUserInfo(Issuer.address); + while (securityToken) { + allModules = await getAllModules(); + await displayTokenData(); + await displayModules(); + await selectAction(); + } +}; + +async function displayTokenData() { + let displayTokenSymbol = await securityToken.methods.symbol().call(); + let displayTokenDetails = await securityToken.methods.tokenDetails().call(); + let displayVersion = await securityToken.methods.getVersion().call(); + let displayTokenSupply = await securityToken.methods.totalSupply().call(); + let displayInvestorsCount = await securityToken.methods.getInvestorCount().call(); + let displayCurrentCheckpointId = await securityToken.methods.currentCheckpointId().call(); + let displayTransferFrozen = await securityToken.methods.transfersFrozen().call(); + let displayMintingFrozen = await securityToken.methods.mintingFrozen().call(); + let displayUserTokens = await securityToken.methods.balanceOf(Issuer.address).call(); + + console.log(` +*************** Security Token Information **************** +- Address: ${securityToken.options.address} +- Token symbol: ${displayTokenSymbol.toUpperCase()} +- Token details: ${displayTokenDetails} +- Token version: ${displayVersion[0]}.${displayVersion[1]}.${displayVersion[2]} +- Total supply: ${web3.utils.fromWei(displayTokenSupply)} ${displayTokenSymbol.toUpperCase()} +- Investors count: ${displayInvestorsCount} +- Current checkpoint: ${displayCurrentCheckpointId} +- Transfer frozen: ${displayTransferFrozen ? 'YES' : 'NO'} +- Minting frozen: ${displayMintingFrozen ? 'YES' : 'NO'} +- User balance: ${web3.utils.fromWei(displayUserTokens)} ${displayTokenSymbol.toUpperCase()}`); +} + +async function displayModules() { + // Module Details + let pmModules = allModules.filter(m => m.type == gbl.constants.MODULES_TYPES.PERMISSION); + let tmModules = allModules.filter(m => m.type == gbl.constants.MODULES_TYPES.TRANSFER); + let stoModules = allModules.filter(m => m.type == gbl.constants.MODULES_TYPES.STO); + let cpModules = allModules.filter(m => m.type == gbl.constants.MODULES_TYPES.DIVIDENDS); + let burnModules = allModules.filter(m => m.type == gbl.constants.MODULES_TYPES.BURN); + + // Module Counts + let numPM = pmModules.length; + let numTM = tmModules.length; + let numSTO = stoModules.length; + let numCP = cpModules.length; + let numBURN = burnModules.length; + + console.log(` +******************* Module Information ******************** +- Permission Manager: ${(numPM > 0) ? numPM : 'None'} +- Transfer Manager: ${(numTM > 0) ? numTM : 'None'} +- STO: ${(numSTO > 0) ? numSTO : 'None'} +- Checkpoint: ${(numCP > 0) ? numCP : 'None'} +- Burn: ${(numBURN > 0) ? numBURN : 'None'} + `); + + if (numPM) { + console.log(`Permission Manager Modules:`); + pmModules.map(m => console.log(`- ${m.name} is ${(m.archived) ? chalk.yellow('archived') : 'unarchived'} at ${m.address}`)); + } + + if (numTM) { + console.log(`Transfer Manager Modules:`); + tmModules.map(m => console.log(`- ${m.name} is ${(m.archived) ? chalk.yellow('archived') : 'unarchived'} at ${m.address}`)); + } + + if (numSTO) { + console.log(`STO Modules:`); + stoModules.map(m => console.log(`- ${m.name} is ${(m.archived) ? chalk.yellow('archived') : 'unarchived'} at ${m.address}`)); + } + + if (numCP) { + console.log(`Checkpoint Modules:`); + cpModules.map(m => console.log(`- ${m.name} is ${(m.archived) ? chalk.yellow('archived') : 'unarchived'} at ${m.address}`)); + } + + if (numBURN) { + console.log(` Burn Modules:`); + burnModules.map(m => console.log(`- ${m.name} is ${(m.archived) ? chalk.yellow('archived') : 'unarchived'} at ${m.address}`)); + } +} + +async function selectAction() { + let options = ['Update token details'/*, 'Change granularity'*/]; + + let transferFrozen = await securityToken.methods.transfersFrozen().call(); + if (transferFrozen) { + options.push('Unfreeze transfers'); + } else { + options.push('Freeze transfers'); + } + + let isMintingFrozen = await securityToken.methods.mintingFrozen().call(); + if (!isMintingFrozen) { + let isFreezeMintingAllowed = await featureRegistry.methods.getFeatureStatus('freezeMintingAllowed').call(); + if (isFreezeMintingAllowed) { + options.push('Freeze minting permanently'); + } + } + + options.push('Create a checkpoint', 'List investors') + + let currentCheckpointId = await securityToken.methods.currentCheckpointId().call(); + if (currentCheckpointId > 0) { + options.push('List investors at checkpoint') + } + + if (!isMintingFrozen) { + options.push('Mint tokens'); + } + + options.push('Manage modules', 'Withdraw tokens from contract'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'Exit' }); + let selected = index == -1 ? 'Exit' : options[index]; + console.log('Selected:', selected); + switch (selected) { + case 'Update token details': + let updatedDetails = readlineSync.question('Enter new off-chain details of the token (i.e. Dropbox folder url): '); + await updateTokenDetails(updatedDetails); + break; + case 'Change granularity': + //let granularity = readlineSync.questionInt('Enter ') + //await changeGranularity(); + break; + case 'Freeze transfers': + await freezeTransfers(); + break; + case 'Unfreeze transfers': + await unfreezeTransfers(); + break; + case 'Freeze minting permanently': + await freezeMinting(); + break; + case 'Create a checkpoint': + await createCheckpoint(); + break; + case 'List investors': + await listInvestors(); + break; + case 'List investors at checkpoint': + let checkpointId = readlineSync.question('Enter the id of the checkpoint: ', { + limit: function (input) { + return parseInt(input) > 0 && parseInt(input) <= parseInt(currentCheckpointId); + }, + limitMessage: `Must be greater than 0 and less than ${currentCheckpointId}` + }); + await listInvestorsAtCheckpoint(checkpointId); + break; + case 'Mint tokens': + await mintTokens(); + break; + case 'Manage modules': + await listModuleOptions(); + break; + case 'Withdraw tokens from contract': + let tokenAddress = readlineSync.question(`Enter the ERC20 token address (POLY ${polyToken.options.address}): `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: polyToken.options.address + }); + let value = readlineSync.questionFloat('Enter the value to withdraw: ', { + limit: function (input) { + return input > 0; + }, + limitMessage: "Must be a greater than 0" + }); + await withdrawFromContract(tokenAddress, web3.utils.toWei(new web3.utils.BN(value))); + break; + case 'Exit': + process.exit(); + break; + } +} + +// Token actions +async function updateTokenDetails(updatedDetails) { + let updateTokenDetailsAction = securityToken.methods.updateTokenDetails(updatedDetails); + await common.sendTransaction(updateTokenDetailsAction); + console.log(chalk.green(`Token details have been updated successfully!`)); +} + +async function freezeTransfers() { + let freezeTransfersAction = securityToken.methods.freezeTransfers(); + await common.sendTransaction(freezeTransfersAction); + console.log(chalk.green(`Transfers have been frozen successfully!`)); +} + +async function unfreezeTransfers() { + let unfreezeTransfersAction = securityToken.methods.unfreezeTransfers(); + await common.sendTransaction(unfreezeTransfersAction); + console.log(chalk.green(`Transfers have been unfrozen successfully!`)); +} + +async function freezeMinting() { + let freezeMintingAction = securityToken.methods.freezeMinting(); + await common.sendTransaction(freezeMintingAction); + console.log(chalk.green(`Minting has been frozen successfully!.`)); +} + +async function createCheckpoint() { + let createCheckpointAction = securityToken.methods.createCheckpoint(); + let receipt = await common.sendTransaction(createCheckpointAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'CheckpointCreated'); + console.log(chalk.green(`Checkpoint ${event._checkpointId} has been created successfully!`)); +} + +async function listInvestors() { + let investors = await securityToken.methods.getInvestors().call(); + console.log(); + if (investors.length > 0) { + console.log('***** List of investors *****'); + investors.map(i => console.log(i)); + } else { + console.log(chalk.yellow('There are no investors yet')); + } +} + +async function listInvestorsAtCheckpoint(checkpointId) { + let investors = await securityToken.methods.getInvestorsAt(checkpointId).call(); + console.log(); + if (investors.length > 0) { + console.log(`*** List of investors at checkpoint ${checkpointId} ***`); + investors.map(i => console.log(i)); + } else { + console.log(chalk.yellow(`There are no investors at checkpoint ${checkpointId}`)); + } + +} + +async function mintTokens() { + let options = ['Modify whitelist', 'Mint tokens to a single address', `Mint tokens to multiple addresses from CSV`]; + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'Return' }); + let selected = index == -1 ? 'Return' : options[index]; + console.log('Selected:', selected); + switch (selected) { + case 'Modify whitelist': + let investor = readlineSync.question('Enter the address to whitelist: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let fromTime = readlineSync.questionInt('Enter the time (Unix Epoch time) when the sale lockup period ends and the investor can freely sell his tokens: '); + let toTime = readlineSync.questionInt('Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others: '); + let expiryTime = readlineSync.questionInt('Enter the time till investors KYC will be validated (after that investor need to do re-KYC): '); + let canBuyFromSTO = readlineSync.keyInYNStrict('Is the investor a restricted investor?'); + await modifyWhitelist(investor, fromTime, toTime, expiryTime, canBuyFromSTO); + break; + case 'Mint tokens to a single address': + console.log(chalk.yellow(`Investor should be previously whitelisted.`)); + let receiver = readlineSync.question(`Enter the address to receive the tokens: `); + let amount = readlineSync.question(`Enter the amount of tokens to mint: `); + await mintToSingleAddress(receiver, amount); + break; + case `Mint tokens to multiple addresses from CSV`: + console.log(chalk.yellow(`Investors should be previously whitelisted.`)); + await multiMint(); + break; + } +} + +/// Mint actions +async function modifyWhitelist(investor, fromTime, toTime, expiryTime, canBuyFromSTO) { + let gmtModules = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); + let generalTransferManagerAddress = gmtModules[0]; + let generalTransferManagerABI = abis.generalTransferManager(); + let generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, generalTransferManagerAddress); + + let modifyWhitelistAction = generalTransferManager.methods.modifyWhitelist(investor, fromTime, toTime, expiryTime, canBuyFromSTO); + let modifyWhitelistReceipt = await common.sendTransaction(modifyWhitelistAction); + let modifyWhitelistEvent = common.getEventFromLogs(generalTransferManager._jsonInterface, modifyWhitelistReceipt.logs, 'ModifyWhitelist'); + console.log(chalk.green(`${modifyWhitelistEvent._investor} has been whitelisted sucessfully!`)); +} + +async function mintToSingleAddress(_investor, _amount) { + try { + let mintAction = securityToken.methods.mint(_investor, web3.utils.toWei(_amount)); + let receipt = await common.sendTransaction(mintAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Minted'); + console.log(chalk.green(`${web3.utils.fromWei(event._value)} tokens have been minted to ${event._to} successfully.`)); + } + catch (e) { + console.log(e); + console.log(chalk.red(`Minting was not successful - Please make sure beneficiary address has been whitelisted`)); + } +} + +async function multiMint(_csvFilePath, _batchSize) { + let csvFilePath; + if (typeof _csvFilePath !== 'undefined') { + csvFilePath = _csvFilePath; + } else { + csvFilePath = readlineSync.question(`Enter the path for csv data file (${MULTIMINT_DATA_CSV}): `, { + defaultInput: MULTIMINT_DATA_CSV + }); + } + let batchSize; + if (typeof _batchSize !== 'undefined') { + batchSize = _batchSize; + } else { + batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + } + let parsedData = csvParse(csvFilePath); + let tokenDivisible = await securityToken.methods.granularity().call() == 1; + let validData = parsedData.filter(row => + web3.utils.isAddress(row[0]) && + (!isNaN(row[1]) && (tokenDivisible || parseFloat(row[1]) % 1 == 0)) + ); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let verifiedData = []; + let unverifiedData = []; + for (const row of validData) { + let investorAccount = row[0]; + let tokenAmount = web3.utils.toWei(row[1].toString()); + let verifiedTransaction = await securityToken.methods.verifyTransfer(gbl.constants.ADDRESS_ZERO, investorAccount, tokenAmount, web3.utils.fromAscii('')).call(); + if (verifiedTransaction) { + verifiedData.push(row); + } else { + unverifiedData.push(row); + } + } + + let batches = common.splitIntoBatches(verifiedData, batchSize); + let [investorArray, amountArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to mint tokens to accounts: \n\n`, investorArray[batch], '\n'); + amountArray[batch] = amountArray[batch].map(a => web3.utils.toWei(a.toString())); + let action = securityToken.methods.mintMulti(investorArray[batch], amountArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Multi mint transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + + if (unverifiedData.length > 0) { + console.log("*********************************************************************************************************"); + console.log('The following data arrays failed at verifyTransfer. Please review if these accounts are whitelisted\n'); + console.log(chalk.red(unverifiedData.map(d => `${d[0]}, ${d[1]}`).join('\n'))); + console.log("*********************************************************************************************************"); + } +} + +async function withdrawFromContract(erc20address, value) { + let withdrawAction = securityToken.methods.withdrawERC20(erc20address, value); + await common.sendTransaction(withdrawAction); + console.log(chalk.green(`Withdrawn has been successful!.`)); +} +/// + +async function listModuleOptions() { + let options = ['Add a module'] + + let unpausedModules = allModules.filter(m => m.paused == false); + if (unpausedModules.length > 0) { + options.push('Pause a module'); + } + + let pausedModules = allModules.filter(m => m.paused == true); + if (pausedModules.length > 0) { + options.push('Unpause a module'); + } + + let unarchivedModules = allModules.filter(m => !m.archived); + if (unarchivedModules.length > 0) { + options.push('Archive a module'); + } + + let archivedModules = allModules.filter(m => m.archived); + if (archivedModules.length > 0) { + options.push('Unarchive a module', 'Remove a module'); + } + + if (allModules.length > 0) { + options.push('Change module budget'); + } + + let index = readlineSync.keyInSelect(options, chalk.yellow('What do you want to do?'), { cancel: 'Return' }); + let selected = index == -1 ? 'Exit' : options[index]; + console.log('Selected:', selected); + switch (selected) { + case 'Add a module': + await addModule(); + break; + case 'Pause a module': + await pauseModule(unpausedModules); + break; + case 'Unpause a module': + await unpauseModule(pausedModules); + break; + case 'Archive a module': + await archiveModule(unarchivedModules); + break; + case 'Unarchive a module': + await unarchiveModule(archivedModules); + break; + case 'Remove a module': + await removeModule(archivedModules); + break; + case 'Change module budget': + await changeBudget(allModules); + break; + } +} + +// Modules a actions +async function addModule() { + let options = ['Permission Manager', 'Transfer Manager', 'Security Token Offering', 'Dividends', 'Burn']; + let index = readlineSync.keyInSelect(options, 'What type of module whould you like to add?', { cancel: 'Return' }); + switch (options[index]) { + case 'Permission Manager': + console.log(chalk.red(` + ********************************* + This option is not yet available. + *********************************`)); + break; + case 'Transfer Manager': + await transferManager.addTransferManagerModule(tokenSymbol) + break; + case 'Security Token Offering': + await stoManager.addSTOModule(tokenSymbol) + break; + case 'Dividends': + console.log(chalk.red(` + ********************************* + This option is not yet available. + *********************************`)); + break; + case 'Burn': + console.log(chalk.red(` + ********************************* + This option is not yet available. + *********************************`)); + break; + } +} + +async function pauseModule(modules) { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module whould you like to pause?'); + if (index != -1) { + console.log("\nSelected:", options[index]); + let moduleABI; + if (modules[index].type == gbl.constants.MODULES_TYPES.STO) { + moduleABI = abis.ISTO(); + } else if (modules[index].type == gbl.constants.MODULES_TYPES.STO) { + moduleABI = abis.ITransferManager(); + } else { + console.log(chalk.red(`Only STO and TM modules can be paused/unpaused`)); + process.exit(0); + } + let pausableModule = new web3.eth.Contract(moduleABI, modules[index].address); + let pauseAction = pausableModule.methods.pause(); + await common.sendTransaction(pauseAction); + console.log(chalk.green(`${modules[index].name} has been paused successfully!`)); + } +} + +async function unpauseModule(modules) { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module whould you like to pause?'); + if (index != -1) { + console.log("\nSelected: ", options[index]); + let moduleABI; + if (modules[index].type == gbl.constants.MODULES_TYPES.STO) { + moduleABI = abis.ISTO(); + } else if (modules[index].type == gbl.constants.MODULES_TYPES.STO) { + moduleABI = abis.ITransferManager(); + } else { + console.log(chalk.red(`Only STO and TM modules can be paused/unpaused`)); + process.exit(0); + } + let pausableModule = new web3.eth.Contract(moduleABI, modules[index].address); + let unpauseAction = pausableModule.methods.unpause(); + await common.sendTransaction(unpauseAction); + console.log(chalk.green(`${modules[index].name} has been unpaused successfully!`)); + } +} + +async function archiveModule(modules) { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module would you like to archive?'); + if (index != -1) { + console.log("\nSelected: ", options[index]); + let archiveModuleAction = securityToken.methods.archiveModule(modules[index].address); + await common.sendTransaction(archiveModuleAction, { factor: 2 }); + console.log(chalk.green(`${modules[index].name} has been archived successfully!`)); + } +} + +async function unarchiveModule(modules) { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module whould you like to unarchive?'); + if (index != -1) { + console.log("\nSelected: ", options[index]); + let unarchiveModuleAction = securityToken.methods.unarchiveModule(modules[index].address); + await common.sendTransaction(unarchiveModuleAction, { factor: 2 }); + console.log(chalk.green(`${modules[index].name} has been unarchived successfully!`)); + } +} + +async function removeModule(modules) { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module whould you like to remove?'); + if (index != -1) { + console.log("\nSelected: ", options[index]); + let removeModuleAction = securityToken.methods.removeModule(modules[index].address); + await common.sendTransaction(removeModuleAction, { factor: 2 }); + console.log(chalk.green(`${modules[index].name} has been removed successfully!`)); + } +} + +async function changeBudget() { + let options = modules.map(m => `${m.name} (${m.address})`); + let index = readlineSync.keyInSelect(options, 'Which module whould you like to remove?'); + if (index != -1) { + console.log("\nSelected: ", options[index]); + let increase = 0 == readlineSync.keyInSelect(['Increase', 'Decrease'], `Do you want to increase or decrease budget?`, { cancel: false }); + let amount = readlineSync.question(`Enter the amount of POLY to change in allowance`); + let changeModuleBudgetAction = securityToken.methods.changeModuleBudget(modules[index].address, web3.utils.toWei(amount), increase); + await common.sendTransaction(changeModuleBudgetAction); + console.log(chalk.green(`Module budget has been changed successfully!`)); + } +} + +// Helpers +async function showUserInfo(_user) { + console.log(` +******************** User Information ********************* +- Address: ${_user} +- POLY balance: ${web3.utils.fromWei(await polyToken.methods.balanceOf(_user).call())} +- ETH balance: ${web3.utils.fromWei(await web3.eth.getBalance(_user))} + `); +} + +async function getAllModules() { + function ModuleInfo(_moduleType, _name, _address, _factoryAddress, _archived, _paused) { + this.name = _name; + this.type = _moduleType; + this.address = _address; + this.factoryAddress = _factoryAddress; + this.archived = _archived; + this.paused = _paused; + } + + let modules = []; + + // Iterate over all module types + for (let type = 1; type <= 5; type++) { + let allModules = await securityToken.methods.getModulesByType(type).call(); + + // Iterate over all modules of each type + for (let i = 0; i < allModules.length; i++) { + try { + let details = await securityToken.methods.getModule(allModules[i]).call(); + let nameTemp = web3.utils.hexToUtf8(details[0]); + let pausedTemp = null; + if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { + let abiTemp = JSON.parse(require('fs').readFileSync(`./build/contracts/${nameTemp}.json`).toString()).abi; + let contractTemp = new web3.eth.Contract(abiTemp, details[1]); + pausedTemp = await contractTemp.methods.paused().call(); + } + modules.push(new ModuleInfo(type, nameTemp, details[1], details[2], details[3], pausedTemp)); + } catch (error) { + console.log(error); + console.log(chalk.red(` + ************************* + Unable to iterate over module type - unexpected error + *************************`)); + } + } + } + + return modules; +} + +async function initialize(_tokenSymbol) { + welcome(); + await setup(); + if (typeof _tokenSymbol === 'undefined') { + tokenSymbol = await selectToken(); + } else { + tokenSymbol = _tokenSymbol; + } + let securityTokenAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (securityTokenAddress == '0x0000000000000000000000000000000000000000') { + console.log(chalk.red(`Selected Security Token ${tokenSymbol} does not exist.`)); + process.exit(0); + } + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI, securityTokenAddress); + securityToken.setProvider(web3.currentProvider); +} + +function welcome() { + common.logAsciiBull(); + console.log(`*****************************************`); + console.log(`Welcome to the Command-Line Token Manager`); + console.log(`*****************************************`); + console.log("The following script will allow you to manage your ST-20 tokens"); + console.log("Issuer Account: " + Issuer.address + "\n"); +} + +async function selectToken() { + let result = null; + + let userTokens = await securityTokenRegistry.methods.getTokensByOwner(Issuer.address).call(); + let tokenDataArray = await Promise.all(userTokens.map(async function (t) { + let tokenData = await securityTokenRegistry.methods.getSecurityTokenData(t).call(); + return { symbol: tokenData[0], address: t }; + })); + let options = tokenDataArray.map(function (t) { + return `${t.symbol} - Deployed at ${t.address}`; + }); + options.push('Enter token symbol manually'); + + let index = readlineSync.keyInSelect(options, 'Select a token:', { cancel: 'Exit' }); + let selected = index != -1 ? options[index] : 'Exit'; + switch (selected) { + case 'Enter token symbol manually': + result = readlineSync.question('Enter the token symbol: '); + break; + case 'Exit': + process.exit(); + break; + default: + result = tokenDataArray[index].symbol; + break; + } + + return result; +} + +module.exports = { + executeApp: async function (_tokenSymbol) { + await initialize(_tokenSymbol); + return executeApp(); + }, + multiMint: async function (_tokenSymbol, _csvPath, _batchSize) { + await initialize(_tokenSymbol); + return multiMint(_csvPath, _batchSize); + } +} diff --git a/CLI/commands/transfer.js b/CLI/commands/transfer.js index 48fbb79e5..5bd6d1859 100644 --- a/CLI/commands/transfer.js +++ b/CLI/commands/transfer.js @@ -1,5 +1,4 @@ var common = require('./common/common_functions'); -var global = require('./common/global'); /////////////////////////////ARTIFACTS////////////////////////////////////////// var contracts = require('./helpers/contract_addresses'); @@ -14,13 +13,11 @@ let securityToken; //////////////////////////////////////////ENTRY INTO SCRIPT////////////////////////////////////////// -async function startScript(tokenSymbol, transferTo, transferAmount, remoteNetwork) { +async function startScript(tokenSymbol, transferTo, transferAmount) { _tokenSymbol = tokenSymbol; _transferTo = transferTo; _transferAmount = transferAmount; - await global.initialize(remoteNetwork); - try { let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); @@ -46,7 +43,7 @@ async function transfer() { try{ let transferAction = securityToken.methods.transfer(_transferTo,web3.utils.toWei(_transferAmount,"ether")); - let receipt = await common.sendTransaction(Issuer, transferAction, defaultGasPrice); + let receipt = await common.sendTransaction(transferAction); let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); console.log(` Account ${event.from} @@ -61,7 +58,7 @@ async function transfer() { }; module.exports = { - executeApp: async function(tokenSymbol, transferTo, transferAmount, remoteNetwork) { - return startScript(tokenSymbol, transferTo, transferAmount, remoteNetwork); + executeApp: async function(tokenSymbol, transferTo, transferAmount) { + return startScript(tokenSymbol, transferTo, transferAmount); } } diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index df06c68f4..39af92b78 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -1,144 +1,2754 @@ -var readlineSync = require('readline-sync'); -var chalk = require('chalk'); -var moment = require('moment'); -var common = require('./common/common_functions'); -var global = require('./common/global'); -var contracts = require('./helpers/contract_addresses'); -var abis = require('./helpers/contract_abis'); +const readlineSync = require('readline-sync'); +const chalk = require('chalk'); +const moment = require('moment'); +const common = require('./common/common_functions'); +const contracts = require('./helpers/contract_addresses'); +const abis = require('./helpers/contract_abis'); +const gbl = require('./common/global'); +const csvParse = require('./helpers/csv'); +const { table } = require('table'); + +/////////////////// +// Constants +const WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/GTM/whitelist_data.csv`; +const ADD_BLACKLIST_DATA_CSV = `${__dirname}/../data/Transfer/BlacklistTM/add_blacklist_data.csv`; +const MODIFY_BLACKLIST_DATA_CSV = `${__dirname}/../data/Transfer/BlacklistTM/modify_blacklist_data.csv`; +const DELETE_BLACKLIST_DATA_CSV = `${__dirname}/../data/Transfer/BlacklistTM/delete_blacklist_data.csv`; +const ADD_INVESTOR_BLACKLIST_DATA_CSV = `${__dirname}/../data/Transfer/BlacklistTM/add_investor_blacklist_data.csv`; +const REMOVE_INVESTOR_BLACKLIST_DATA_CSV = `${__dirname}/../data/Transfer/BlacklistTM/remove_investor_blacklist_data.csv`; +const PERCENTAGE_WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/PercentageTM/whitelist_data.csv`; +const ADD_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/add_manualapproval_data.csv`; +const MODIFY_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/modify_manualapproval_data.csv`; +const REVOKE_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/revoke_manualapproval_data.csv`; +const ADD_DAILY_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/add_daily_restriction_data.csv`; +const MODIFY_DAILY_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/modify_daily_restriction_data.csv`; +const REMOVE_DAILY_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/remove_daily_restriction_data.csv`; +const ADD_CUSTOM_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/add_custom_restriction_data.csv`; +const MODIFY_CUSTOM_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/modify_custom_restriction_data.csv`; +const REMOVE_CUSTOM_RESTRICTIONS_DATA_CSV = `${__dirname}/../data/Transfer/VRTM/remove_custom_restriction_data.csv`; +const ADD_LOCKUP_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/add_lockup_data.csv`; +const MODIFY_LOCKUP_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/modify_lockup_data.csv`; +const DELETE_LOCKUP_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/delete_lockup_data.csv`; +const ADD_LOCKUP_INVESTOR_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/add_lockup_investor_data.csv`; +const REMOVE_LOCKUP_INVESTOR_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/remove_lockup_investor_data.csv`; + +const RESTRICTION_TYPES = ['Fixed', 'Percentage']; + +const MATM_MENU_ADD = 'Add new manual approval'; +const MATM_MENU_MANAGE = 'Manage existing approvals'; +const MATM_MENU_EXPLORE = 'Explore account'; +const MATM_MENU_OPERATE = 'Operate with multiple approvals'; +const MATM_MENU_MANAGE_INCRESE = 'Increase allowance'; +const MATM_MENU_MANAGE_DECREASE = 'Decrease allowance'; +const MATM_MENU_MANAGE_TIME = 'Modify expiry time and/or description'; +const MATM_MENU_MANAGE_REVOKE = 'Revoke this approval'; +const MATM_MENU_OPERATE_ADD = 'Add multiple approvals in batch'; +const MATM_MENU_OPERATE_MODIFY = 'Modify multiple approvals in batch'; +const MATM_MENU_OPERATE_REVOKE = 'Revoke multiple approvals in batch'; // App flow let tokenSymbol; let securityToken; let securityTokenRegistry; +let moduleRegistry; +let currentTransferManager; + +async function executeApp() { + console.log('\n', chalk.blue('Transfer Manager - Main Menu', '\n')); + + let tmModules = await getAllModulesByType(gbl.constants.MODULES_TYPES.TRANSFER); + let nonArchivedModules = tmModules.filter(m => !m.archived); + if (nonArchivedModules.length > 0) { + console.log(`Transfer Manager modules attached:`); + nonArchivedModules.map(m => console.log(`- ${m.name} at ${m.address}`)) + } else { + console.log(`There are no Transfer Manager modules attached`); + } + + let options = ['Verify transfer', 'Transfer']; + let forcedTransferDisabled = await securityToken.methods.controllerDisabled().call(); + if (!forcedTransferDisabled) { + options.push('Forced transfers'); + } + if (nonArchivedModules.length > 0) { + options.push('Config existing modules'); + } + options.push('Add new Transfer Manager module'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'EXIT' }); + let optionSelected = index != -1 ? options[index] : 'EXIT'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Verify transfer': + let verifyTotalSupply = web3.utils.fromWei(await securityToken.methods.totalSupply().call()); + await logTotalInvestors(); + let verifyTransferFrom = readlineSync.question(`Enter the sender account (${Issuer.address}): `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: Issuer.address + }); + await logBalance(verifyTransferFrom, verifyTotalSupply); + let verifyTransferTo = readlineSync.question('Enter the receiver account: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + await logBalance(verifyTransferTo, verifyTotalSupply); + let verifyTransferAmount = readlineSync.question('Enter amount of tokens to verify: '); + let isVerified = await securityToken.methods.verifyTransfer(verifyTransferFrom, verifyTransferTo, web3.utils.toWei(verifyTransferAmount), web3.utils.fromAscii("")).call(); + if (isVerified) { + console.log(chalk.green(`\n${verifyTransferAmount} ${tokenSymbol} can be transferred from ${verifyTransferFrom} to ${verifyTransferTo}!`)); + } else { + console.log(chalk.red(`\n${verifyTransferAmount} ${tokenSymbol} can't be transferred from ${verifyTransferFrom} to ${verifyTransferTo}!`)); + } + break; + case 'Transfer': + let totalSupply = web3.utils.fromWei(await securityToken.methods.totalSupply().call()); + await logTotalInvestors(); + await logBalance(Issuer.address, totalSupply); + let transferTo = readlineSync.question('Enter beneficiary of tranfer: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + await logBalance(transferTo, totalSupply); + let transferAmount = readlineSync.question('Enter amount of tokens to transfer: '); + let isTranferVerified = await securityToken.methods.verifyTransfer(Issuer.address, transferTo, web3.utils.toWei(transferAmount), web3.utils.fromAscii("")).call(); + if (isTranferVerified) { + let transferAction = securityToken.methods.transfer(transferTo, web3.utils.toWei(transferAmount)); + let receipt = await common.sendTransaction(transferAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); + console.log(chalk.green(`${event.from} transferred ${web3.utils.fromWei(event.value)} ${tokenSymbol} to ${event.to} successfully!`)); + await logTotalInvestors(); + await logBalance(Issuer.address, totalSupply); + await logBalance(transferTo, totalSupply); + } else { + console.log(chalk.red(`Transfer failed at verification. Please review the transfer restrictions.`)); + } + break; + case 'Forced transfers': + await forcedTransfers(); + break; + case 'Config existing modules': + await configExistingModules(nonArchivedModules); + break; + case 'Add new Transfer Manager module': + await addTransferManagerModule(); + break; + case 'EXIT': + return; + } + + await executeApp(); +} + +async function forcedTransfers() { + let options = ['Disable controller', 'Set controller']; + let controller = await securityToken.methods.controller().call(); + if (controller == Issuer.address) { + options.push('Force Transfer'); + } + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Disable controller': + if (readlineSync.keyInYNStrict()) { + let disableControllerAction = securityToken.methods.disableController(); + await common.sendTransaction(disableControllerAction); + console.log(chalk.green(`Forced transfers have been disabled permanently`)); + } + break; + case 'Set controller': + let controllerAddress = readlineSync.question(`Enter the address for the controller (${Issuer.address}): `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: Issuer.address + }); + let setControllerAction = securityToken.methods.setController(controllerAddress); + let setControllerReceipt = await common.sendTransaction(setControllerAction); + let setControllerEvent = common.getEventFromLogs(securityToken._jsonInterface, setControllerReceipt.logs, 'SetController'); + console.log(chalk.green(`New controller is ${setControllerEvent._newController}`)); + break; + case 'Force Transfer': + let from = readlineSync.question('Enter the address from which to take tokens: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let fromBalance = web3.utils.fromWei(await securityToken.methods.balanceOf(from).call()); + console.log(chalk.yellow(`Balance of ${from}: ${fromBalance} ${tokenSymbol}`)); + let to = readlineSync.question('Enter address where to send tokens: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + }); + let toBalance = web3.utils.fromWei(await securityToken.methods.balanceOf(to).call()); + console.log(chalk.yellow(`Balance of ${to}: ${toBalance} ${tokenSymbol}`)); + let amount = readlineSync.question('Enter amount of tokens to transfer: ', { + limit: function (input) { + return parseInt(input) <= parseInt(fromBalance); + }, + limitMessage: `Amount must be less or equal than ${fromBalance} ${tokenSymbol}`, + }); + let data = readlineSync.question('Enter the data to indicate validation: '); + let log = readlineSync.question('Enter the data attached to the transfer by controller to emit in event: '); + let forceTransferAction = securityToken.methods.forceTransfer(from, to, web3.utils.toWei(amount), web3.utils.asciiToHex(data), web3.utils.asciiToHex(log)); + let forceTransferReceipt = await common.sendTransaction(forceTransferAction, { factor: 1.5 }); + let forceTransferEvent = common.getEventFromLogs(securityToken._jsonInterface, forceTransferReceipt.logs, 'ForceTransfer'); + console.log(chalk.green(` ${forceTransferEvent._controller} has successfully forced a transfer of ${web3.utils.fromWei(forceTransferEvent._value)} ${tokenSymbol} + from ${forceTransferEvent._from} to ${forceTransferEvent._to} + Verified transfer: ${forceTransferEvent._verifyTransfer} + Data: ${web3.utils.hexToAscii(forceTransferEvent._data)} + `)); + console.log(`Balance of ${from} after transfer: ${web3.utils.fromWei(await securityToken.methods.balanceOf(from).call())} ${tokenSymbol}`); + console.log(`Balance of ${to} after transfer: ${web3.utils.fromWei(await securityToken.methods.balanceOf(to).call())} ${tokenSymbol}`); + break; + case 'RETURN': + return; + } + + await forcedTransfers(); +} + +async function configExistingModules(tmModules) { + let options = tmModules.map(m => `${m.name} at ${m.address}`); + let index = readlineSync.keyInSelect(options, 'Which module do you want to config? ', { cancel: 'RETURN' }); + console.log('Selected:', index !== -1 ? options[index] : 'RETURN', '\n'); + let moduleNameSelected = index !== -1 ? tmModules[index].name : 'RETURN'; + + switch (moduleNameSelected) { + case 'GeneralTransferManager': + currentTransferManager = new web3.eth.Contract(abis.generalTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await generalTransferManager(); + break; + case 'ManualApprovalTransferManager': + currentTransferManager = new web3.eth.Contract(abis.manualApprovalTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await manualApprovalTransferManager(); + break; + case 'CountTransferManager': + currentTransferManager = new web3.eth.Contract(abis.countTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await countTransferManager(); + break; + case 'PercentageTransferManager': + currentTransferManager = new web3.eth.Contract(abis.percentageTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await percentageTransferManager(); + break; + case 'LockUpTransferManager': + currentTransferManager = new web3.eth.Contract(abis.lockUpTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await lockUpTransferManager(); + break; + case 'BlacklistTransferManager': + currentTransferManager = new web3.eth.Contract(abis.blacklistTransferManager(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await blacklistTransferManager(); + break; + case 'VolumeRestrictionTM': + currentTransferManager = new web3.eth.Contract(abis.volumeRestrictionTM(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await volumeRestrictionTM(); + break; + } +} + +async function addTransferManagerModule() { + let availableModules = await moduleRegistry.methods.getModulesByTypeAndToken(gbl.constants.MODULES_TYPES.TRANSFER, securityToken.options.address).call(); + let options = await Promise.all(availableModules.map(async function (m) { + let moduleFactoryABI = abis.moduleFactory(); + let moduleFactory = new web3.eth.Contract(moduleFactoryABI, m); + return web3.utils.hexToUtf8(await moduleFactory.methods.name().call()); + })); + + let index = readlineSync.keyInSelect(options, 'Which Transfer Manager module do you want to add? ', { cancel: 'RETURN' }); + if (index != -1 && readlineSync.keyInYNStrict(`Are you sure you want to add ${options[index]} module?`)) { + let bytes = web3.utils.fromAscii('', 16); + switch (options[index]) { + case 'CountTransferManager': + let maxHolderCount = readlineSync.question('Enter the maximum no. of holders the SecurityToken is allowed to have: '); + let configureCountTM = abis.countTransferManager().find(o => o.name === 'configure' && o.type === 'function'); + bytes = web3.eth.abi.encodeFunctionCall(configureCountTM, [maxHolderCount]); + break; + case 'PercentageTransferManager': + let maxHolderPercentage = toWeiPercentage(readlineSync.question('Enter the maximum amount of tokens in percentage that an investor can hold: ', { + limit: function (input) { + return (parseInt(input) > 0 && parseInt(input) <= 100); + }, + limitMessage: "Must be greater than 0 and less than 100" + })); + let allowPercentagePrimaryIssuance = readlineSync.keyInYNStrict(`Do you want to ignore transactions which are part of the primary issuance? `); + let configurePercentageTM = abis.percentageTransferManager().find(o => o.name === 'configure' && o.type === 'function'); + bytes = web3.eth.abi.encodeFunctionCall(configurePercentageTM, [maxHolderPercentage, allowPercentagePrimaryIssuance]); + break; + } + let selectedTMFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.MODULES_TYPES.TRANSFER, options[index]); + let addModuleAction = securityToken.methods.addModule(selectedTMFactoryAddress, bytes, 0, 0); + let receipt = await common.sendTransaction(addModuleAction); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); + console.log(chalk.green(`Module deployed at address: ${event._module}`)); + } +} + +async function generalTransferManager() { + console.log('\n', chalk.blue(`General Transfer Manager at ${currentTransferManager.options.address}`), '\n'); + + // Show current data + let displayIssuanceAddress = await currentTransferManager.methods.issuanceAddress().call(); + let displaySigningAddress = await currentTransferManager.methods.signingAddress().call(); + let displayAllowAllTransfers = await currentTransferManager.methods.allowAllTransfers().call(); + let displayAllowAllWhitelistTransfers = await currentTransferManager.methods.allowAllWhitelistTransfers().call(); + let displayAllowAllWhitelistIssuances = await currentTransferManager.methods.allowAllWhitelistIssuances().call(); + let displayAllowAllBurnTransfers = await currentTransferManager.methods.allowAllBurnTransfers().call(); + let displayDefaults = await currentTransferManager.methods.defaults().call(); + let displayInvestors = await currentTransferManager.methods.getInvestors().call(); + + console.log(`- Issuance address: ${displayIssuanceAddress}`); + console.log(`- Signing address: ${displaySigningAddress}`); + console.log(`- Allow all transfers: ${displayAllowAllTransfers ? `YES` : `NO`}`); + console.log(`- Allow all whitelist transfers: ${displayAllowAllWhitelistTransfers ? `YES` : `NO`}`); + console.log(`- Allow all whitelist issuances: ${displayAllowAllWhitelistIssuances ? `YES` : `NO`}`); + console.log(`- Allow all burn transfers: ${displayAllowAllBurnTransfers ? `YES` : `NO`}`); + console.log(`- Default times:`); + console.log(` - From time: ${displayDefaults.fromTime} (${moment.unix(displayDefaults.fromTime).format('MMMM Do YYYY, HH:mm:ss')})`); + console.log(` - To time: ${displayDefaults.toTime} (${moment.unix(displayDefaults.toTime).format('MMMM Do YYYY, HH:mm:ss')})`); + console.log(`- Investors: ${displayInvestors.length}`); + // ------------------ + + let options = []; + if (displayInvestors.length > 0) { + options.push(`Show investors`, `Show whitelist data`); + } + options.push('Modify whitelist', 'Modify whitelist from CSV', /*'Modify Whitelist Signed',*/ + 'Change the default times used when they are zero', `Change issuance address`, 'Change signing address'); + + if (displayAllowAllTransfers) { + options.push('Disallow all transfers'); + } else { + options.push('Allow all transfers'); + } + if (displayAllowAllWhitelistTransfers) { + options.push('Disallow all whitelist transfers'); + } else { + options.push('Allow all whitelist transfers'); + } + if (displayAllowAllWhitelistIssuances) { + options.push('Disallow all whitelist issuances'); + } else { + options.push('Allow all whitelist issuances'); + } + if (displayAllowAllBurnTransfers) { + options.push('Disallow all burn transfers'); + } else { + options.push('Allow all burn transfers'); + } -async function executeApp(remoteNetwork) { - await global.initialize(remoteNetwork); - + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case `Show investors`: + console.log('***** List of investors on whitelist *****'); + displayInvestors.map(i => console.log(i)); + break; + case `Show whitelist data`: + let investorsToShow = readlineSync.question(`Enter the addresses of the investors you want to show (i.e: addr1,addr2,addr3) or leave empty to show them all: `, { + limit: function (input) { + return input === '' || input.split(",").every(a => web3.utils.isAddress(a)); + }, + limitMessage: `All addresses must be valid` + }); + if (investorsToShow === '') { + let whitelistData = await currentTransferManager.methods.getAllInvestorsData().call(); + showWhitelistTable(whitelistData[0], whitelistData[1], whitelistData[2], whitelistData[3], whitelistData[4]); + } else { + let investorsArray = investorsToShow.split(','); + let whitelistData = await currentTransferManager.methods.getInvestorsData(investorsArray).call(); + showWhitelistTable(investorsArray, whitelistData[0], whitelistData[1], whitelistData[2], whitelistData[3]); + } + break; + case 'Change the default times used when they are zero': + let fromTimeDefault = readlineSync.questionInt(`Enter the default time (Unix Epoch time) used when fromTime is zero: `); + let toTimeDefault = readlineSync.questionInt(`Enter the default time (Unix Epoch time) used when fromTime is zero: `); + let changeDefaultsAction = currentTransferManager.methods.changeDefaults(fromTimeDefault, toTimeDefault); + let changeDefaultsReceipt = await common.sendTransaction(changeDefaultsAction); + let changeDefaultsEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeDefaultsReceipt.logs, 'ChangeDefaults'); + console.log(chalk.green(`Default times have been updated successfully!`)); + break; + case 'Modify whitelist': + let investor = readlineSync.question('Enter the address to whitelist: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let now = Math.floor(Date.now() / 1000); + let fromTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) when the sale lockup period ends and the investor can freely sell his tokens (now = ${now}): `, { defaultInput: now }); + let toTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others (now = ${now}): `, { defaultInput: now }); + let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); + let expiryTime = readlineSync.questionInt(`Enter the time till investors KYC will be validated (after that investor need to do re - KYC) (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let canBuyFromSTO = readlineSync.keyInYNStrict('Is the investor a restricted investor?'); + let modifyWhitelistAction = currentTransferManager.methods.modifyWhitelist(investor, fromTime, toTime, expiryTime, canBuyFromSTO); + let modifyWhitelistReceipt = await common.sendTransaction(modifyWhitelistAction); + let modifyWhitelistEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyWhitelistReceipt.logs, 'ModifyWhitelist'); + console.log(chalk.green(`${modifyWhitelistEvent._investor} has been whitelisted sucessfully!`)); + break; + case 'Modify whitelist from CSV': + await modifyWhitelistInBatch(); + break; + /* + case 'Modify Whitelist Signed': + let investorSigned = readlineSync.question('Enter the address to whitelist: ', { + limit: function(input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let fromTimeSigned = readlineSync.questionInt('Enter the time (Unix Epoch time) when the sale lockup period ends and the investor can freely sell his tokens: '); + let toTimeSigned = readlineSync.questionInt('Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others: '); + let expiryTimeSigned = readlineSync.questionInt('Enter the time till investors KYC will be validated (after that investor need to do re-KYC): '); + let vSigned = readlineSync.questionInt('Enter v: '); + let rSigned = readlineSync.question('Enter r: '); + let sSigned = readlineSync.question('Enter s: '); + let canBuyFromSTOSigned = readlineSync.keyInYNStrict('Is the investor a restricted investor?'); + let modifyWhitelistSignedAction = currentTransferManager.methods.modifyWhitelistSigned(investorSigned, fromTimeSigned, toTimeSigned, expiryTimeSigned, canBuyFromSTOSigned); + let modifyWhitelistSignedReceipt = await common.sendTransaction(Issuer, modifyWhitelistSignedAction, defaultGasPrice); + let modifyWhitelistSignedEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyWhitelistSignedReceipt.logs, 'ModifyWhitelist'); + console.log(chalk.green(`${ modifyWhitelistSignedEvent._investor } has been whitelisted sucessfully!`)); + break; + */ + case 'Change issuance address': + let issuanceAddress = readlineSync.question('Enter the new issuance address: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let changeIssuanceAddressAction = currentTransferManager.methods.changeIssuanceAddress(issuanceAddress); + let changeIssuanceAddressReceipt = await common.sendTransaction(changeIssuanceAddressAction); + let changeIssuanceAddressEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeIssuanceAddressReceipt.logs, 'ChangeIssuanceAddress'); + console.log(chalk.green(`${changeIssuanceAddressEvent._issuanceAddress} is the new address for the issuance!`)); + break; + case 'Change signing address': + let signingAddress = readlineSync.question('Enter the new signing address: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let changeSigningAddressAction = currentTransferManager.methods.changeSigningAddress(signingAddress); + let changeSigningAddressReceipt = await common.sendTransaction(changeSigningAddressAction); + let changeSigningAddressEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeSigningAddressReceipt.logs, 'ChangeSigningAddress'); + console.log(chalk.green(`${changeSigningAddressEvent._signingAddress} is the new address for the signing!`)); + break; + case 'Allow all transfers': + case 'Disallow all transfers': + let changeAllowAllTransfersAction = currentTransferManager.methods.changeAllowAllTransfers(!displayAllowAllTransfers); + let changeAllowAllTransfersReceipt = await common.sendTransaction(changeAllowAllTransfersAction); + let changeAllowAllTransfersEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeAllowAllTransfersReceipt.logs, 'AllowAllTransfers'); + if (changeAllowAllTransfersEvent._allowAllTransfers) { + console.log(chalk.green(`All transfers are allowed!`)); + } else { + console.log(chalk.green(`Transfers are restricted!`)); + } + break; + case 'Allow all whitelist transfers': + case 'Disallow all whitelist transfers': + let changeAllowAllWhitelistTransfersAction = currentTransferManager.methods.changeAllowAllWhitelistTransfers(!displayAllowAllWhitelistTransfers); + let changeAllowAllWhitelistTransfersReceipt = await common.sendTransaction(changeAllowAllWhitelistTransfersAction); + let changeAllowAllWhitelistTransfersEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeAllowAllWhitelistTransfersReceipt.logs, 'AllowAllWhitelistTransfers'); + if (changeAllowAllWhitelistTransfersEvent._allowAllWhitelistTransfers) { + console.log(chalk.green(`Time locks from whitelist are ignored for transfers!`)); + } else { + console.log(chalk.green(`Transfers are restricted by time locks from whitelist!`)); + } + break; + case 'Allow all whitelist issuances': + case 'Disallow all whitelist issuances': + let changeAllowAllWhitelistIssuancesAction = currentTransferManager.methods.changeAllowAllWhitelistIssuances(!displayAllowAllWhitelistIssuances); + let changeAllowAllWhitelistIssuancesReceipt = await common.sendTransaction(changeAllowAllWhitelistIssuancesAction); + let changeAllowAllWhitelistIssuancesEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeAllowAllWhitelistIssuancesReceipt.logs, 'AllowAllWhitelistIssuances'); + if (changeAllowAllWhitelistIssuancesEvent._allowAllWhitelistIssuances) { + console.log(chalk.green(`Time locks from whitelist are ignored for issuances!`)); + } else { + console.log(chalk.green(`Issuances are restricted by time locks from whitelist!`)); + } + break; + case 'Allow all burn transfers': + case 'Disallow all burn transfers': + let changeAllowAllBurnTransfersAction = currentTransferManager.methods.changeAllowAllBurnTransfers(!displayAllowAllBurnTransfers); + let changeAllowAllBurnTransfersReceipt = await common.sendTransaction(changeAllowAllBurnTransfersAction); + let changeAllowAllBurnTransfersEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeAllowAllBurnTransfersReceipt.logs, 'AllowAllBurnTransfers'); + if (changeAllowAllBurnTransfersEvent._allowAllWhitelistTransfers) { + console.log(chalk.green(`To burn tokens is allowed!`)); + } else { + console.log(chalk.green(`The burning mechanism is deactivated!`)); + } + break; + case 'RETURN': + return; + } + + await generalTransferManager(); +} + +function showWhitelistTable(investorsArray, fromTimeArray, toTimeArray, expiryTimeArray, canBuyFromSTOArray) { + let dataTable = [['Investor', 'From time', 'To time', 'KYC expiry date', 'Restricted']]; + for (let i = 0; i < investorsArray.length; i++) { + dataTable.push([ + investorsArray[i], + moment.unix(fromTimeArray[i]).format('MM/DD/YYYY HH:mm'), + moment.unix(toTimeArray[i]).format('MM/DD/YYYY HH:mm'), + moment.unix(expiryTimeArray[i]).format('MM/DD/YYYY HH:mm'), + canBuyFromSTOArray[i] ? 'YES' : 'NO' + ]); + } + console.log(); + console.log(table(dataTable)); +} + +async function modifyWhitelistInBatch(_csvFilePath, _batchSize) { + let csvFilePath; + if (typeof _csvFilePath === 'undefined') { + csvFilePath = readlineSync.question(`Enter the path for csv data file (${WHITELIST_DATA_CSV}): `, { + defaultInput: WHITELIST_DATA_CSV + }); + } else { + csvFilePath = _csvFilePath; + } + let batchSize; + if (typeof _batchSize === 'undefined') { + batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + } else { + batchSize = _batchSize; + } + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => + web3.utils.isAddress(row[0]) && + moment.unix(row[1]).isValid() && + moment.unix(row[2]).isValid() && + moment.unix(row[3]).isValid() && + typeof row[4] === 'boolean' + ); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, fromTimesArray, toTimesArray, expiryTimeArray, canBuyFromSTOArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify whitelist to accounts: \n\n`, investorArray[batch], '\n'); + let action = currentTransferManager.methods.modifyWhitelistMulti(investorArray[batch], fromTimesArray[batch], toTimesArray[batch], expiryTimeArray[batch], canBuyFromSTOArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify whitelist transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function manualApprovalTransferManager() { + console.log('\n', chalk.blue(`Manual Approval Transfer Manager at ${currentTransferManager.options.address} `), '\n'); + + let totalApprovals = await currentTransferManager.methods.getTotalApprovalsLength().call(); + console.log(`- Current active approvals: ${totalApprovals}`); + + let matmOptions = [ + MATM_MENU_ADD, + MATM_MENU_MANAGE, + MATM_MENU_EXPLORE, + MATM_MENU_OPERATE + ]; + + let index = readlineSync.keyInSelect(matmOptions, 'What do you want to do?', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? matmOptions[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + + switch (optionSelected) { + case MATM_MENU_ADD: + await matmAdd(); + break; + case MATM_MENU_MANAGE: + await matmManage(); + break; + case MATM_MENU_EXPLORE: + await matmExplore(); + break; + case MATM_MENU_OPERATE: + await matmOperate(); + break; + case 'RETURN': + return; + } + + await manualApprovalTransferManager(); +} + +async function matmAdd() { + let from = readlineSync.question('Enter the address from which transfers will be approved: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let to = readlineSync.question('Enter the address to which transfers will be approved: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + if (!await getManualApproval(from, to)) { + let description = readlineSync.question('Enter the description for the manual approval: ', { + limit: function (input) { + return input != "" && getBinarySize(input) < 33 + }, + limitMessage: "Description is required" + }); + let allowance = readlineSync.question('Enter the amount of tokens which will be approved: '); + let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); + let expiryTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) until which the transfer is allowed (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let addManualApprovalAction = currentTransferManager.methods.addManualApproval(from, to, web3.utils.toWei(allowance), expiryTime, web3.utils.fromAscii(description)); + let addManualApprovalReceipt = await common.sendTransaction(addManualApprovalAction); + let addManualApprovalEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addManualApprovalReceipt.logs, 'AddManualApproval'); + console.log(chalk.green(`Manual approval has been added successfully!`)); + } else { + console.log(chalk.red(`A manual approval already exists from ${from} to ${to}. Revoke it first if you want to add a new one or modify the existing one.`)); + } +} + +async function matmManage() { + + let manageOptions = [ + MATM_MENU_MANAGE_INCRESE, + MATM_MENU_MANAGE_DECREASE, + MATM_MENU_MANAGE_TIME, + MATM_MENU_MANAGE_REVOKE + ]; + + let getApprovals = await getApprovalsArray(); + + if (getApprovals.length > 0) { + let options = [] + getApprovals.forEach((item) => { + options.push(`${web3.utils.toAscii(item.description)}\n From: ${item.from}\n To: ${item.to}\n Amount: ${web3.utils.fromWei(item.allowance)} ${tokenSymbol}\n Expiry date: ${moment.unix(item.expiryTime).format('MM/DD/YYYY HH:mm')}\n`) + }) + + let index = readlineSync.keyInSelect(options, 'Select an existing approval: ', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + + if (optionSelected !== 'RETURN') { + let selectedApproval = getApprovals[index]; + + let index2 = readlineSync.keyInSelect(manageOptions, 'What do you want to do?', { + cancel: 'RETURN' + }); + let optionSelected2 = index2 != -1 ? manageOptions[index2] : 'RETURN'; + console.log('Selected:', optionSelected2, '\n'); + + if (optionSelected2 !== 'RETURN') { + switch (optionSelected2) { + case MATM_MENU_MANAGE_INCRESE: + await matmManageIncrese(selectedApproval); + break; + case MATM_MENU_MANAGE_DECREASE: + await matmManageDecrease(selectedApproval); + break; + case MATM_MENU_MANAGE_TIME: + await matmManageTimeOrDescription(selectedApproval); + break; + case MATM_MENU_MANAGE_REVOKE: + await matmManageRevoke(selectedApproval); + break; + } + } + } + } else { + console.log(chalk.yellow(`There are no existing approvals to show`)); + } +} + +async function matmExplore() { + let getApprovals = await getApprovalsArray(); + getApprovals.forEach((item) => { + printMatmRow(item.from, item.to, item.allowance, item.expiryTime, item.description); + }) +} + +async function matmOperate() { + let operateOptions = [ + MATM_MENU_OPERATE_ADD, + MATM_MENU_OPERATE_MODIFY, + MATM_MENU_OPERATE_REVOKE + ]; + + let index = readlineSync.keyInSelect(operateOptions, 'What do you want to do?', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? operateOptions[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + + switch (optionSelected) { + case MATM_MENU_OPERATE_ADD: + await addManualApproveInBatch(); + break; + case MATM_MENU_OPERATE_MODIFY: + await modifyManualApproveInBatch(); + break; + case MATM_MENU_OPERATE_REVOKE: + await revokeManualApproveInBatch(); + break; + } +} + +async function matmManageIncrese(selectedApproval) { + let allowance = readlineSync.question(`Enter a value to increase allowance (current allowance = ${web3.utils.fromWei(selectedApproval.allowance)}): `, { + limit: function (input) { + return parseFloat(input) > 0 + }, + limitMessage: "Amount must be bigger than 0" + }); + + if (readlineSync.keyInYNStrict(`Do you want to modify expiry time or description?`)) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + selectedApproval.expiryTime = expiryTime; + selectedApproval.description = web3.utils.fromAscii(description); + } + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(selectedApproval.expiryTime), web3.utils.toWei(allowance), selectedApproval.description, 1); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval allowance has been increased successfully!`)); +} + +async function matmManageDecrease(selectedApproval) { + let allowance = readlineSync.question(`Enter a value to decrease allowance (current allowance = ${web3.utils.fromWei(selectedApproval.allowance)}): `, { + limit: function (input) { + return parseFloat(input) > 0 + }, + limitMessage: "Amount must be bigger than 0" + }); + + if (readlineSync.keyInYNStrict(`Do you want to modify expiry time or description?`)) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + selectedApproval.expiryTime = expiryTime; + selectedApproval.description = web3.utils.fromAscii(description); + } + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(selectedApproval.expiryTime), web3.utils.toWei(allowance), selectedApproval.description, 0); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval allowance has been decreased successfully!`)); +} + +async function matmManageTimeOrDescription(selectedApproval) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(expiryTime), selectedApproval.allowance, web3.utils.fromAscii(description), 2); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval expiry time has been modified successfully!`)); +} + +function readExpiryTimeAndDescription(selectedApproval) { + let expiryTime = readlineSync.questionInt(`Enter the new expiry time (Unix Epoch time) until which the transfer is allowed or leave empty to keep the current (${selectedApproval.expiryTime}): `, { + limit: function (input) { + return parseFloat(input) > 0; + }, + limitMessage: "Enter Unix Epoch time", + defaultInput: selectedApproval.expiryTime + }); + let description = readlineSync.question(`Enter the new description for the manual approval or leave empty to keep the current (${web3.utils.toAscii(selectedApproval.description)}): `, { + limit: function (input) { + return input != "" && getBinarySize(input) < 33; + }, + limitMessage: "Description is required" + }); + return { expiryTime, description }; +} + +async function matmManageRevoke(selectedApproval) { + let modifyManualApprovalAction = currentTransferManager.methods.revokeManualApproval(selectedApproval.from, selectedApproval.to); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval has been revoked successfully!`)); +} + +async function getApprovalsArray() { + let address = readlineSync.question('Enter an address to filter or leave empty to get all the approvals: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: gbl.constants.ADDRESS_ZERO + }); + if (address == gbl.constants.ADDRESS_ZERO) { + return await getApprovals(); + } else { + let approvals = await getApprovalsToAnAddress(address); + if (!approvals.length) { + console.log(chalk.red(`\nThe address is not listed\n`)) + } + return approvals; + } +} + +function printMatmRow(from, to, allowance, time, description) { + console.log(`\nDescription: ${web3.utils.toAscii(description)}\nFrom ${from} to ${to}\nAllowance: ${web3.utils.fromWei(allowance)}\nExpiry time: ${moment.unix(time).format('MMMM Do YYYY HH:mm')}\n`); +} + +async function getApprovals() { + function ApprovalDetail(_from, _to, _allowance, _expiryTime, _description) { + this.from = _from; + this.to = _to; + this.allowance = _allowance; + this.expiryTime = _expiryTime; + this.description = _description; + } + + let results = []; + let approvalDetails = await currentTransferManager.methods.getAllApprovals().call(); + for (let i = 0; i < approvalDetails[0].length; i++) { + results.push(new ApprovalDetail(approvalDetails[0][i], approvalDetails[1][i], approvalDetails[2][i], approvalDetails[3][i], approvalDetails[4][i])); + } + return results; +} + +async function getApprovalsToAnAddress(address) { + function ApprovalDetail(_from, _to, _allowance, _expiryTime, _description) { + this.from = _from; + this.to = _to; + this.allowance = _allowance; + this.expiryTime = _expiryTime; + this.description = _description; + } + + let results = []; + let approvals = await currentTransferManager.methods.getActiveApprovalsToUser(address).call(); + for (let i = 0; i < approvals[0].length; i++) { + results.push(new ApprovalDetail(approvals[0][i], approvals[1][i], approvals[2][i], approvals[3][i], approvals[4][i])); + } + return results; +} + +async function getManualApproval(_from, _to) { + let result = null; + + let manualApproval = await currentTransferManager.methods.getApprovalDetails(_from, _to).call(); + if ((manualApproval[0] >= new Date()) && (manualApproval[1] != 0)) { + result = manualApproval; + } + return result; +} + +async function matmGenericCsv(path, f) { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${path}): `, { + defaultInput: path + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => f(row)); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + return common.splitIntoBatches(validData, batchSize); +} + +async function addManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1]) && + parseFloat(row[2]) > 0 && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && + getBinarySize(row[4]) < 33) + } + + let batches = await matmGenericCsv(ADD_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray, allowanceArray, expiryArray, descriptionArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add manual approvals: \n\n`, descriptionArray[batch], '\n'); + descriptionArray[batch] = descriptionArray[batch].map(d => web3.utils.fromAscii(d)); + allowanceArray[batch] = allowanceArray[batch].map(a => web3.utils.toWei(new web3.utils.BN(a))); + let action = await currentTransferManager.methods.addManualApprovalMulti(fromArray[batch], toArray[batch], allowanceArray[batch], expiryArray[batch], descriptionArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function revokeManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1])) + } + + let batches = await matmGenericCsv(REVOKE_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to revoke manual approvals`, '\n'); + let action = await currentTransferManager.methods.revokeManualApprovalMulti(fromArray[batch], toArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Revoke multip;e manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1]) && + moment.unix(row[2]).isValid() && + parseFloat(row[3]) > 0 && + typeof row[4] === 'string' && + getBinarySize(row[4]) < 33 && + typeof parseInt(row[5])) === 'number' + } + + let batches = await matmGenericCsv(MODIFY_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray, expiryArray, allowanceArray, descriptionArray, changesArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify manual approvals: \n\n`, descriptionArray[batch], '\n'); + descriptionArray[batch] = descriptionArray[batch].map(d => web3.utils.fromAscii(d)); + allowanceArray[batch] = allowanceArray[batch].map(a => web3.utils.toWei(new web3.utils.BN(a))); + changesArray[batch] = changesArray[batch].map(c => parseInt(c)); + let action = await currentTransferManager.methods.modifyManualApprovalMulti(fromArray[batch], toArray[batch], expiryArray[batch], allowanceArray[batch], descriptionArray[batch], changesArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +function getBinarySize(string) { + return Buffer.byteLength(string, 'utf8'); +} + +async function countTransferManager() { + console.log('\n', chalk.blue(`Count Transfer Manager at ${currentTransferManager.options.address}`), '\n'); + + // Show current data + let displayMaxHolderCount = await currentTransferManager.methods.maxHolderCount().call(); + + console.log(`- Max holder count: ${displayMaxHolderCount}`); + + let options = ['Change max holder count'] + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Change max holder count': + let maxHolderCount = readlineSync.question('Enter the maximum no. of holders the SecurityToken is allowed to have: '); + let changeHolderCountAction = currentTransferManager.methods.changeHolderCount(maxHolderCount); + let changeHolderCountReceipt = await common.sendTransaction(changeHolderCountAction); + let changeHolderCountEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeHolderCountReceipt.logs, 'ModifyHolderCount'); + console.log(chalk.green(`Max holder count has been set to ${changeHolderCountEvent._newHolderCount} sucessfully!`)); + break; + case 'RETURN': + return; + } + + await countTransferManager(); +} + +async function percentageTransferManager() { + console.log('\n', chalk.blue(`Percentage Transfer Manager at ${currentTransferManager.options.address}`), '\n'); + + // Show current data + let displayMaxHolderPercentage = await currentTransferManager.methods.maxHolderPercentage().call(); + let displayAllowPrimaryIssuance = await currentTransferManager.methods.allowPrimaryIssuance().call(); + + console.log(`- Max holder percentage: ${fromWeiPercentage(displayMaxHolderPercentage)}%`); + console.log(`- Allow primary issuance: ${displayAllowPrimaryIssuance ? `YES` : `NO`}`); + + let options = ['Change max holder percentage', 'Check if investor is whitelisted', 'Modify whitelist', 'Modify whitelist from CSV']; + if (displayAllowPrimaryIssuance) { + options.push('Disallow primary issuance'); + } else { + options.push('Allow primary issuance'); + } + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Change max holder percentage': + let maxHolderPercentage = toWeiPercentage(readlineSync.question('Enter the maximum amount of tokens in percentage that an investor can hold: ', { + limit: function (input) { + return (parseInt(input) > 0 && parseInt(input) <= 100); + }, + limitMessage: "Must be greater than 0 and less than 100" + })); + let changeHolderPercentageAction = currentTransferManager.methods.changeHolderPercentage(maxHolderPercentage); + let changeHolderPercentageReceipt = await common.sendTransaction(changeHolderPercentageAction); + let changeHolderPercentageEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeHolderPercentageReceipt.logs, 'ModifyHolderPercentage'); + console.log(chalk.green(`Max holder percentage has been set to ${fromWeiPercentage(changeHolderPercentageEvent._newHolderPercentage)} successfully!`)); + break; + case 'Check if investor is whitelisted': + let investorToCheck = readlineSync.question('Enter the address of the investor: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let isWhitelisted = await currentTransferManager.methods.whitelist(investorToCheck).call(); + if (isWhitelisted) { + console.log(chalk.green(`${investorToCheck} is whitelisted!`)); + } else { + console.log(chalk.yellow(`${investorToCheck} is not whitelisted!`)); + } + break; + case 'Modify whitelist': + let valid = !!readlineSync.keyInSelect(['Remove investor from whitelist', 'Add investor to whitelist'], 'How do you want to do? ', { cancel: false }); + let investorToWhitelist = readlineSync.question('Enter the address of the investor: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let modifyWhitelistAction = currentTransferManager.methods.modifyWhitelist(investorToWhitelist, valid); + let modifyWhitelistReceipt = await common.sendTransaction(modifyWhitelistAction); + let modifyWhitelistEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyWhitelistReceipt.logs, 'ModifyWhitelist'); + if (modifyWhitelistEvent._valid) { + console.log(chalk.green(`${modifyWhitelistEvent._investor} has been added to the whitelist sucessfully!`)); + } else { + console.log(chalk.green(`${modifyWhitelistEvent._investor} has been removed from the whitelist sucessfully!`)); + } + break; + case 'Modify whitelist from CSV': + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${PERCENTAGE_WHITELIST_DATA_CSV}): `, { + defaultInput: PERCENTAGE_WHITELIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && typeof row[1] === 'boolean'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')}`)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, isWhitelistedArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify whitelist accounts:\n\n`, investorArray[batch], '\n'); + let action = await currentTransferManager.methods.modifyWhitelistMulti(investorArray[batch], isWhitelistedArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify whitelist transaction was successful.')); + console.log(`${receipt.gasUsed} gas used. Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + break; + case 'Allow primary issuance': + case 'Disallow primary issuance': + let setAllowPrimaryIssuanceAction = currentTransferManager.methods.setAllowPrimaryIssuance(!displayAllowPrimaryIssuance); + let setAllowPrimaryIssuanceReceipt = await common.sendTransaction(setAllowPrimaryIssuanceAction); + let setAllowPrimaryIssuanceEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, setAllowPrimaryIssuanceReceipt.logs, 'SetAllowPrimaryIssuance'); + if (setAllowPrimaryIssuanceEvent._allowPrimaryIssuance) { + console.log(chalk.green(`Transactions which are part of the primary issuance will be ignored!`)); + } else { + console.log(chalk.green(`Transactions which are part of the primary issuance will NOT be ignored!`)); + } + break; + } + + await percentageTransferManager(); +} + +async function blacklistTransferManager() { + console.log('\n', chalk.blue(`Blacklist Transfer Manager at ${currentTransferManager.options.address}`), '\n'); + + let currentBlacklists = await currentTransferManager.methods.getAllBlacklists().call(); + console.log(`- Blacklists: ${currentBlacklists.length}`); + + let options = ['Add new blacklist']; + if (currentBlacklists.length > 0) { + options.push('Manage existing blacklist', 'Explore account'); + } + options.push('Delete investors from all blacklists', 'Operate with multiple blacklists'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: "RETURN" }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add new blacklist': + let name = readlineSync.question(`Enter the name of the blacklist type: `, { + limit: function (input) { + return input !== ""; + }, + limitMessage: `Invalid blacklist name` + }); + let minuteFromNow = Math.floor(Date.now() / 1000) + 60; + let startTime = readlineSync.questionInt(`Enter the start date (Unix Epoch time) of the blacklist type (a minute from now = ${minuteFromNow}): `, { defaultInput: minuteFromNow }); + let oneDayFromStartTime = startTime + 24 * 60 * 60; + let endTime = readlineSync.questionInt(`Enter the end date (Unix Epoch time) of the blacklist type (1 day from start time = ${oneDayFromStartTime}): `, { defaultInput: oneDayFromStartTime }); + let repeatPeriodTime = readlineSync.questionInt(`Enter the repeat period (days) of the blacklist type, 0 to disable (90 days): `, { defaultInput: 90 }); + if (readlineSync.keyInYNStrict(`Do you want to add an investor to this blacklist type? `)) { + let investor = readlineSync.question(`Enter the address of the investor: `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: `Must be a valid address` + }); + let addInvestorToNewBlacklistAction = currentTransferManager.methods.addInvestorToNewBlacklist( + startTime, + endTime, + web3.utils.toHex(name), + repeatPeriodTime, + investor + ); + let addInvestorToNewBlacklistReceipt = await common.sendTransaction(addInvestorToNewBlacklistAction); + let addNewBlacklistEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addInvestorToNewBlacklistReceipt.logs, 'AddBlacklistType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(addNewBlacklistEvent._blacklistName)} blacklist type has been added successfully!`)); + let addInvestorToNewBlacklistEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addInvestorToNewBlacklistReceipt.logs, 'AddInvestorToBlacklist'); + console.log(chalk.green(`${addInvestorToNewBlacklistEvent._investor} has been added to ${web3.utils.hexToUtf8(addInvestorToNewBlacklistEvent._blacklistName)} successfully!`)); + } else { + let addBlacklistTypeAction = currentTransferManager.methods.addBlacklistType(startTime, endTime, web3.utils.toHex(name), repeatPeriodTime); + let addBlacklistTypeReceipt = await common.sendTransaction(addBlacklistTypeAction); + let addBlacklistTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addBlacklistTypeReceipt.logs, 'AddBlacklistType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(addBlacklistTypeEvent._blacklistName)} blacklist type has been added successfully!`)); + } + break; + case 'Manage existing blacklist': + let options = currentBlacklists.map(b => web3.utils.hexToUtf8(b)); + let index = readlineSync.keyInSelect(options, 'Which blacklist type do you want to manage? ', { cancel: "RETURN" }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + if (index !== -1) { + await manageExistingBlacklist(currentBlacklists[index]); + } + break; + case 'Explore account': + let account = readlineSync.question(`Enter the address of the investor: `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: `Must be a valid address` + }); + let blacklistNamesToUser = await currentTransferManager.methods.getBlacklistNamesToUser(account).call(); + if (blacklistNamesToUser.length > 0) { + console.log(); + console.log(`**** Blacklists inlcuding ${account} ****`); + blacklistNamesToUser.map(n => console.log(web3.utils.hexToUtf8(n))); + } else { + console.log(chalk.yellow(`No blacklist includes ${account}`)); + } + console.log(); + break; + case 'Delete investors from all blacklists': + let investorsToRemove = readlineSync.question(`Enter the addresses of the investors separated by comma (i.e. addr1,addr2,addr3): `, { + limit: function (input) { + return (input !== '' && input.split(",").every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(','); + let deleteInvestorFromAllBlacklistAction; + if (investorsToRemove.length === 1) { + deleteInvestorFromAllBlacklistAction = currentTransferManager.methods.deleteInvestorFromAllBlacklist(investorsToRemove[0]); + } else { + deleteInvestorFromAllBlacklistAction = currentTransferManager.methods.deleteInvestorFromAllBlacklistMulti(investorsToRemove); + } + let deleteInvestorFromAllBlacklistReceipt = await common.sendTransaction(deleteInvestorFromAllBlacklistAction); + let deleteInvestorFromAllBlacklistEvents = common.getMultipleEventsFromLogs(currentTransferManager._jsonInterface, deleteInvestorFromAllBlacklistReceipt.logs, 'DeleteInvestorFromBlacklist'); + deleteInvestorFromAllBlacklistEvents.map(e => console.log(chalk.green(`${e._investor} has been removed from ${web3.utils.hexToUtf8(e._blacklistName)} successfully!`))); + break; + case 'Operate with multiple blacklists': + await operateWithMultipleBlacklists(currentBlacklists); + break; + case 'RETURN': + return; + } + + await blacklistTransferManager(); +} + +async function manageExistingBlacklist(blacklistName) { + // Show current data + let currentBlacklist = await currentTransferManager.methods.blacklists(blacklistName).call(); + let investors = await currentTransferManager.methods.getListOfAddresses(blacklistName).call(); + + console.log(); + console.log(`- Name: ${web3.utils.hexToUtf8(blacklistName)}`); + console.log(`- Start time: ${moment.unix(currentBlacklist.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- End time: ${moment.unix(currentBlacklist.endTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- Span: ${(currentBlacklist.endTime - currentBlacklist.startTime) / 60 / 60 / 24} days`); + console.log(`- Repeat period time: ${currentBlacklist.repeatPeriodTime} days`); + console.log(`- Investors: ${investors.length}`); + // ------------------ + + let options = [ + "Modify properties", + "Show investors", + "Add investors", + "Remove investor", + "Delete this blacklist type" + ]; + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Modify properties': + let minuteFromNow = Math.floor(Date.now() / 1000) + 60; + let startTime = readlineSync.questionInt(`Enter the start date (Unix Epoch time) of the blacklist type (a minute from now = ${minuteFromNow}): `, { defaultInput: minuteFromNow }); + let oneDayFromStartTime = startTime + 24 * 60 * 60; + let endTime = readlineSync.questionInt(`Enter the end date (Unix Epoch time) of the blacklist type (1 day from start time = ${oneDayFromStartTime}): `, { defaultInput: oneDayFromStartTime }); + let repeatPeriodTime = readlineSync.questionInt(`Enter the repeat period (days) of the blacklist type, 0 to disable (90 days): `, { defaultInput: 90 }); + let modifyBlacklistTypeAction = currentTransferManager.methods.modifyBlacklistType( + startTime, + endTime, + blacklistName, + repeatPeriodTime + ); + let modifyBlacklistTypeReceipt = await common.sendTransaction(modifyBlacklistTypeAction); + let modifyBlacklistTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyBlacklistTypeReceipt.logs, 'ModifyBlacklistType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(modifyBlacklistTypeEvent._blacklistName)} blacklist type has been modified successfully!`)); + break; + case 'Show investors': + if (investors.length > 0) { + console.log("************ List of investors ************"); + investors.map(i => console.log(i)); + } else { + console.log(chalk.yellow("There are no investors yet")); + } + break; + case 'Add investors': + let investorsToAdd = readlineSync.question(`Enter the addresses of the investors separated by comma (i.e. addr1,addr2,addr3): `, { + limit: function (input) { + return (input !== '' && input.split(",").every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(","); + let addInvestorToBlacklistAction; + if (investorsToAdd.length === 1) { + addInvestorToBlacklistAction = currentTransferManager.methods.addInvestorToBlacklist(investorsToAdd[0], blacklistName); + } else { + addInvestorToBlacklistAction = currentTransferManager.methods.addInvestorToBlacklistMulti(investorsToAdd, blacklistName); + } + let addInvestorToBlacklistReceipt = await common.sendTransaction(addInvestorToBlacklistAction); + let addInvestorToBlacklistEvents = common.getMultipleEventsFromLogs(currentTransferManager._jsonInterface, addInvestorToBlacklistReceipt.logs, 'AddInvestorToBlacklist'); + addInvestorToBlacklistEvents.map(e => console.log(chalk.green(`${e._investor} has been added to ${web3.utils.hexToUtf8(e._blacklistName)} successfully!`))); + break; + case "Remove investor": + let investorsToRemove = readlineSync.question(`Enter the address of the investor: `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: `Must be a valid address` + }); + let deleteInvestorFromBlacklistAction = currentTransferManager.methods.deleteInvestorFromBlacklist(investorsToRemove, blacklistName); + let deleteInvestorFromBlacklistReceipt = await common.sendTransaction(deleteInvestorFromBlacklistAction); + let deleteInvestorFromBlacklistEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, deleteInvestorFromBlacklistReceipt.logs, 'DeleteInvestorFromBlacklist'); + console.log(chalk.green(`${deleteInvestorFromBlacklistEvent._investor} has been removed from ${web3.utils.hexToUtf8(deleteInvestorFromBlacklistEvent._blacklistName)} successfully!`)); + break; + case "Delete this blacklist type": + let isEmpty = investors.length === 0; + if (!isEmpty) { + console.log(chalk.yellow(`This blacklist have investors added on it. To delete it you must remove them first.`)); + if (readlineSync.keyInYNStrict(`Do you want to remove them? `)) { + let data = investors.map(i => [i, blacklistName]) + let batches = common.splitIntoBatches(data, gbl.constants.DEFAULT_BATCH_SIZE); + let [investorArray, blacklistNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove the following investors:\n\n`, investorArray[batch], '\n'); + let action = currentTransferManager.methods.deleteMultiInvestorsFromBlacklistMulti(investorArray[batch], blacklistNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove investors from multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + isEmpty = true; + } + } + if (isEmpty) { + let deleteBlacklistTypeAction = currentTransferManager.methods.deleteBlacklistType(blacklistName); + let deleteBlacklistTypeReceipt = await common.sendTransaction(deleteBlacklistTypeAction); + let deleteBlacklistTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, deleteBlacklistTypeReceipt.logs, 'DeleteBlacklistType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(deleteBlacklistTypeEvent._blacklistName)} blacklist type has been deleted successfully!`)); + } + return; + case 'RETURN': + return; + } + + await manageExistingBlacklist(blacklistName); +} + +async function operateWithMultipleBlacklists(currentBlacklists) { + let options = ['Add multiple blacklists']; + if (currentBlacklists.length > 0) { + options.push('Modify multiple blacklists'); + } + options.push( + 'Delete multiple blacklists', + 'Add investors to multiple blacklists', + 'Remove investors from multiple blacklists' + ); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add multiple blacklists': + await addBlacklistsInBatch(); + break; + case 'Modify multiple blacklists': + await modifyBlacklistsInBatch(); + break; + case 'Delete multiple blacklists': + await deleteBlacklistsInBatch(); + break; + case 'Add investors to multiple blacklists': + await addInvestorsToBlacklistsInBatch(); + break; + case 'Remove investors from multiple blacklists': + await removeInvestorsFromBlacklistsInBatch(); + break; + } +} + +async function addBlacklistsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_BLACKLIST_DATA_CSV}): `, { + defaultInput: ADD_BLACKLIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => moment.unix(row[0]).isValid() && + moment.unix(row[1]).isValid() && + typeof row[2] === 'string' && + (!isNaN(row[3] && (parseFloat(row[3]) % 1 === 0)))); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [startTimeArray, endTimeArray, blacklistNameArray, repeatPeriodTimeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add the following blacklists:\n\n`, blacklistNameArray[batch], '\n'); + blacklistNameArray[batch] = blacklistNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.addBlacklistTypeMulti(startTimeArray[batch], endTimeArray[batch], blacklistNameArray[batch], repeatPeriodTimeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyBlacklistsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_BLACKLIST_DATA_CSV}): `, { + defaultInput: MODIFY_BLACKLIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => moment.unix(row[0]).isValid() && + moment.unix(row[1]).isValid() && + typeof row[2] === 'string' && + (!isNaN(row[3] && (parseFloat(row[3]) % 1 === 0)))); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [startTimeArray, endTimeArray, blacklistNameArray, repeatPeriodTimeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify the following blacklists:\n\n`, blacklistNameArray[batch], '\n'); + blacklistNameArray[batch] = blacklistNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.modifyBlacklistTypeMulti(startTimeArray[batch], endTimeArray[batch], blacklistNameArray[batch], repeatPeriodTimeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function deleteBlacklistsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${DELETE_BLACKLIST_DATA_CSV}): `, { + defaultInput: DELETE_BLACKLIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => typeof row[0] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + + let verifiedData = []; + let unverifiedData = []; + for (const row of validData) { + let blacklistName = row[0]; + let verifiedTransaction = (await currentTransferManager.methods.getListOfAddresses(web3.utils.toHex(blacklistName)).call()).length === 0; + if (verifiedTransaction) { + verifiedData.push(row); + } else { + unverifiedData.push(row); + } + } + + let batches = common.splitIntoBatches(verifiedData, batchSize); + let [blacklistNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to delete the following blacklists:\n\n`, blacklistNameArray[batch], '\n'); + blacklistNameArray[batch] = blacklistNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.deleteBlacklistTypeMulti(blacklistNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Delete multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + + if (unverifiedData.length > 0) { + console.log("*****************************************************************************************************************"); + console.log('The following data would failed as these blacklists have investors. They must be empty to be able to delete them.\n'); + console.log(chalk.red(unverifiedData.map(d => `${d[0]}`).join('\n'))); + console.log("*****************************************************************************************************************"); + } +} + +async function addInvestorsToBlacklistsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_INVESTOR_BLACKLIST_DATA_CSV}): `, { + defaultInput: ADD_INVESTOR_BLACKLIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + typeof row[1] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, blacklistNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add the following investors:\n\n`, investorArray[batch], '\n'); + blacklistNameArray[batch] = blacklistNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.addMultiInvestorToBlacklistMulti(investorArray[batch], blacklistNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add investors to multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +function makeBatchRequest(calls) { + let batch = new web3.BatchRequest(); + + let promises = calls.map(call => { + return new Promise((res, rej) => { + let req = call.request({ from: Issuer.address }, (err, data) => { + if (err) rej(err); + else res(data) + }); + batch.add(req) + }) + }) + batch.execute() + + return Promise.all(promises) +} + +async function removeInvestorsFromBlacklistsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_INVESTOR_BLACKLIST_DATA_CSV}): `, { + defaultInput: REMOVE_INVESTOR_BLACKLIST_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + typeof row[1] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, blacklistNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove the following investors:\n\n`, investorArray[batch], '\n'); + blacklistNameArray[batch] = blacklistNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.deleteMultiInvestorsFromBlacklistMulti(investorArray[batch], blacklistNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove investors from multiple blacklists transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function volumeRestrictionTM() { + console.log('\n', chalk.blue(`Volume Restriction Transfer Manager at ${currentTransferManager.options.address}`, '\n')); + + let globalDailyRestriction = await currentTransferManager.methods.defaultDailyRestriction().call(); + let hasGlobalDailyRestriction = parseInt(globalDailyRestriction.startTime) !== 0; + let globalCustomRestriction = await currentTransferManager.methods.defaultRestriction().call(); + let hasGlobalCustomRestriction = parseInt(globalCustomRestriction.startTime) !== 0; + + console.log(`- Default daily restriction: ${hasGlobalDailyRestriction ? '' : 'None'}`); + if (hasGlobalDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[globalDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${globalDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(globalDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(globalDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(globalDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${globalDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(globalDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Default custom restriction: ${hasGlobalCustomRestriction ? '' : 'None'}`); + if (hasGlobalCustomRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[globalCustomRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${globalCustomRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(globalCustomRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(globalCustomRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(globalCustomRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${globalCustomRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(globalCustomRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + let addressesAndRestrictions = await currentTransferManager.methods.getRestrictedData().call(); + console.log(`- Individual restrictions: ${addressesAndRestrictions.allAddresses.length}`); + let exemptedAddresses = await currentTransferManager.methods.getExemptAddress().call(); + console.log(`- Exempted addresses: ${exemptedAddresses.length}`); + + let options = []; + if (addressesAndRestrictions.allAddresses.length > 0) { + options.push('Show restrictions'); + } + if (exemptedAddresses.length > 0) { + options.push('Show exempted addresses'); + } + options.push( + 'Change exempt wallet', + 'Change default restrictions', + 'Change individual restrictions', + 'Explore account', + 'Operate with multiple restrictions' + ); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Show restrictions': + showRestrictionTable( + addressesAndRestrictions.allAddresses, + addressesAndRestrictions.allowedTokens, + addressesAndRestrictions.typeOfRestriction, + addressesAndRestrictions.rollingPeriodInDays, + addressesAndRestrictions.startTime, + addressesAndRestrictions.endTime, + ); + break; + case 'Show exempted addresses': + showExemptedAddresses(exemptedAddresses); + break; + case 'Change exempt wallet': + await changeExemptWallet(); + break; + case 'Change default restrictions': + await changeDefaultRestrictions(hasGlobalDailyRestriction, hasGlobalCustomRestriction); + break; + case 'Change individual restrictions': + await changeIndividualRestrictions(); + break; + case 'Explore account': + await exploreAccount(); + break; + case 'Operate with multiple restrictions': + await operateWithMultipleRestrictions(); + break; + case 'RETURN': + return; + } + + await volumeRestrictionTM(); +} + +function showRestrictionTable(investorArray, amountArray, typeArray, rollingPeriodArray, startTimeArray, endTimeTimeArray) { + let dataTable = [['Investor', 'Maximum transfer (# or %)', 'Rolling period (days)', 'Start date', 'End date']]; + for (let i = 0; i < investorArray.length; i++) { + dataTable.push([ + investorArray[i], + typeArray[i] === "0" ? `${web3.utils.fromWei(amountArray[i])} ${tokenSymbol}` : `${fromWeiPercentage(amountArray[i])}%`, + rollingPeriodArray[i], + moment.unix(startTimeArray[i]).format('MM/DD/YYYY HH:mm'), + moment.unix(endTimeTimeArray[i]).format('MM/DD/YYYY HH:mm') + ]); + } + console.log(); + console.log(table(dataTable)); +} + +function showExemptedAddresses(addresses) { + console.log("*********** Exepmpted addresses ***********"); + addresses.map(i => console.log(i)); +} + +async function changeExemptWallet() { + let options = [ + 'Add exempt wallet', + 'Remove exempt wallet' + ]; + + let change; + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add exempt wallet': + change = true; + break; + case 'Remove exempt wallet': + change = false; + break; + case 'RETURN': + return; + } + + let wallet = readlineSync.question('Enter the wallet to change: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let changeExemptWalletAction = currentTransferManager.methods.changeExemptWalletList(wallet, change); + let changeExemptWalletReceipt = await common.sendTransaction(changeExemptWalletAction); + let changeExemptWalletEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeExemptWalletReceipt.logs, 'ChangedExemptWalletList'); + console.log(chalk.green(`${changeExemptWalletEvent._wallet} has been ${changeExemptWalletEvent._change ? `added to` : `removed from`} exempt wallets successfully!`)); +} + +async function changeDefaultRestrictions(hasGlobalDailyRestriction, hasGlobalCustomRestriction) { + let options = []; + if (!hasGlobalDailyRestriction) { + options.push('Add global daily restriction'); + } else { + options.push('Modify global daily restriction', 'Remove global daily restriction'); + } + + if (!hasGlobalCustomRestriction) { + options.push('Add global custom restriction'); + } else { + options.push('Modify global custom restriction', 'Remove global custom restriction'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add global daily restriction': + let globalDailyRestrictoToAdd = inputRestrictionData(true); + let addGlobalDailyRestrictionAction = currentTransferManager.methods.addDefaultDailyRestriction( + globalDailyRestrictoToAdd.allowedTokens, + globalDailyRestrictoToAdd.startTime, + globalDailyRestrictoToAdd.endTime, + globalDailyRestrictoToAdd.restrictionType + ); + let addGlobalDailyRestrictionReceipt = await common.sendTransaction(addGlobalDailyRestrictionAction); + let addGlobalDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addGlobalDailyRestrictionReceipt.logs, 'AddDefaultDailyRestriction'); + console.log(chalk.green(`Global daily restriction has been added successfully!`)); + break; + case 'Modify global daily restriction': + let globalDailyRestrictoToModify = inputRestrictionData(true); + let modifyGlobalDailyRestrictionAction = currentTransferManager.methods.modifyDefaultDailyRestriction( + globalDailyRestrictoToModify.allowedTokens, + globalDailyRestrictoToModify.startTime, + globalDailyRestrictoToModify.endTime, + globalDailyRestrictoToModify.restrictionType + ); + let modifyGlobalDailyRestrictionReceipt = await common.sendTransaction(modifyGlobalDailyRestrictionAction); + let modifyGlobalDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyGlobalDailyRestrictionReceipt.logs, 'ModifyDefaultDailyRestriction'); + console.log(chalk.green(`Global daily restriction has been modified successfully!`)); + break; + case 'Remove global daily restriction': + let removeGlobalDailyRestrictionAction = currentTransferManager.methods.removeDefaultDailyRestriction(); + let removeGlobalDailyRestrictionReceipt = await common.sendTransaction(removeGlobalDailyRestrictionAction); + let removeGlobalDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeGlobalDailyRestrictionReceipt.logs, 'DefaultDailyRestrictionRemoved'); + console.log(chalk.green(`Global daily restriction has been removed successfully!`)); + break; + case 'Add global custom restriction': + let globalCustomRestrictoToAdd = inputRestrictionData(false); + let addGlobalCustomRestrictionAction = currentTransferManager.methods.addDefaultRestriction( + globalCustomRestrictoToAdd.allowedTokens, + globalCustomRestrictoToAdd.startTime, + globalCustomRestrictoToAdd.rollingPeriodInDays, + globalCustomRestrictoToAdd.endTime, + globalCustomRestrictoToAdd.restrictionType + ); + let addGlobalCustomRestrictionReceipt = await common.sendTransaction(addGlobalCustomRestrictionAction); + let addGlobalCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addGlobalCustomRestrictionReceipt.logs, 'AddDefaultRestriction'); + console.log(chalk.green(`Global custom restriction has been added successfully!`)); + break; + case 'Modify global custom restriction': + let globalCustomRestrictoToModify = inputRestrictionData(false); + let modifiyGlobalCustomRestrictionAction = currentTransferManager.methods.modifyDefaultRestriction( + globalCustomRestrictoToModify.allowedTokens, + globalCustomRestrictoToModify.startTime, + globalCustomRestrictoToModify.rollingPeriodInDays, + globalCustomRestrictoToModify.endTime, + globalCustomRestrictoToModify.restrictionType + ); + let modifyGlobalCustomRestrictionReceipt = await common.sendTransaction(modifiyGlobalCustomRestrictionAction); + let modifyGlobalCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyGlobalCustomRestrictionReceipt.logs, 'ModifyDefaultRestriction'); + console.log(chalk.green(`Global custom restriction has been modified successfully!`)); + break; + case 'Remove global custom restriction': + let removeGlobalCustomRestrictionAction = currentTransferManager.methods.removeDefaultRestriction(); + let removeGlobalCustomRestrictionReceipt = await common.sendTransaction(removeGlobalCustomRestrictionAction); + let removeGlobalCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeGlobalCustomRestrictionReceipt.logs, 'DefaultRestrictionRemoved'); + console.log(chalk.green(`Global custom restriction has been removed successfully!`)); + break; + } +} + +async function changeIndividualRestrictions() { + let holder = readlineSync.question('Enter the address of the token holder, whom restriction will be implied: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + + let currentDailyRestriction = await currentTransferManager.methods.individualDailyRestriction(holder).call(); + let hasDailyRestriction = parseInt(currentDailyRestriction.startTime) !== 0; + let currentCustomRestriction = await currentTransferManager.methods.individualRestriction(holder).call(); + let hasCustomRestriction = parseInt(currentCustomRestriction.startTime) !== 0; + + console.log(`*** Current individual restrictions for ${holder} ***`, '\n'); + + console.log(`- Daily restriction: ${hasDailyRestriction ? '' : 'None'}`); + if (hasDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[currentDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${currentDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(currentDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(currentDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(currentDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${currentDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(currentDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Custom restriction: ${hasCustomRestriction ? '' : 'None'} `); + if (hasCustomRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[currentCustomRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${currentCustomRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(currentCustomRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(currentCustomRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(currentCustomRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${currentCustomRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(currentCustomRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + let options = []; + if (!hasDailyRestriction) { + options.push('Add individual daily restriction'); + } else { + options.push('Modify individual daily restriction', 'Remove individual daily restriction'); + } + + if (!hasCustomRestriction) { + options.push('Add individual custom restriction'); + } else { + options.push('Modify individual custom restriction', 'Remove individual custom restriction'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add individual daily restriction': + let dailyRestrictonToAdd = inputRestrictionData(true); + let addDailyRestrictionAction = currentTransferManager.methods.addIndividualDailyRestriction( + holder, + dailyRestrictonToAdd.allowedTokens, + dailyRestrictonToAdd.startTime, + dailyRestrictonToAdd.endTime, + dailyRestrictonToAdd.restrictionType + ); + let addDailyRestrictionReceipt = await common.sendTransaction(addDailyRestrictionAction); + let addDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addDailyRestrictionReceipt.logs, 'AddIndividualDailyRestriction'); + console.log(chalk.green(`Daily restriction for ${addDailyRestrictionEvent._holder} has been added successfully!`)); + break; + case 'Modify individual daily restriction': + let dailyRestrictonToModify = inputRestrictionData(true); + let modifyDailyRestrictionAction = currentTransferManager.methods.modifyIndividualDailyRestriction( + holder, + dailyRestrictonToModify.allowedTokens, + dailyRestrictonToModify.startTime, + dailyRestrictonToModify.endTime, + dailyRestrictonToModify.restrictionType + ); + let modifyDailyRestrictionReceipt = await common.sendTransaction(modifyDailyRestrictionAction); + let modifyDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyDailyRestrictionReceipt.logs, 'ModifyIndividualDailyRestriction'); + console.log(chalk.green(`Daily restriction for ${modifyDailyRestrictionEvent._holder} has been modified successfully!`)); + break; + case 'Remove individual daily restriction': + let removeDailyRestrictionAction = currentTransferManager.methods.removeIndividualDailyRestriction(holder); + let removeDailyRestrictionReceipt = await common.sendTransaction(removeDailyRestrictionAction); + let removeDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeDailyRestrictionReceipt.logs, 'IndividualDailyRestrictionRemoved'); + console.log(chalk.green(`Daily restriction for ${removeDailyRestrictionEvent._holder} has been removed successfully!`)); + break; + case 'Add individual custom restriction': + let restrictonToAdd = inputRestrictionData(false); + let addCustomRestrictionAction = currentTransferManager.methods.addIndividualRestriction( + holder, + restrictonToAdd.allowedTokens, + restrictonToAdd.startTime, + restrictonToAdd.rollingPeriodInDays, + restrictonToAdd.endTime, + restrictonToAdd.restrictionType + ); + let addCustomRestrictionReceipt = await common.sendTransaction(addCustomRestrictionAction); + let addCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addCustomRestrictionReceipt.logs, 'AddIndividualRestriction'); + console.log(chalk.green(`Custom restriction for ${addCustomRestrictionEvent._holder} has been added successfully!`)); + break; + case 'Modify individual custom restriction': + let restrictonToModify = inputRestrictionData(false); + let modifyCustomRestrictionAction = currentTransferManager.methods.modifyIndividualRestriction( + holder, + restrictonToModify.allowedTokens, + restrictonToModify.startTime, + restrictonToModify.rollingPeriodInDays, + restrictonToModify.endTime, + restrictonToModify.restrictionType + ); + let modifyCustomRestrictionReceipt = await common.sendTransaction(modifyCustomRestrictionAction); + let modifyCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyCustomRestrictionReceipt.logs, 'ModifyIndividualRestriction'); + console.log(chalk.green(`Custom restriction for ${modifyCustomRestrictionEvent._holder} has been modified successfully!`)); + break; + case 'Remove individual custom restriction': + let removeCustomRestrictionAction = currentTransferManager.methods.removeIndividualRestriction(holder); + let removeCustomRestrictionReceipt = await common.sendTransaction(removeCustomRestrictionAction); + let removeCustomRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeCustomRestrictionReceipt.logs, 'IndividualRestrictionRemoved'); + console.log(chalk.green(`Custom restriction for ${removeCustomRestrictionEvent._holder} has been removed successfully!`)); + break; + case 'RETURN': + return; + } +} + +async function exploreAccount() { + let account = readlineSync.question('Enter the account to explore: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + + let appliyngDailyRestriction = null; + let applyingCustomRestriction = null; + let hasIndividualRestrictions = false; + let isExempted = await currentTransferManager.methods.exemptList(account).call(); + if (!isExempted) { + let individuallDailyRestriction = await currentTransferManager.methods.individualDailyRestriction(account).call(); + if (parseInt(individuallDailyRestriction.endTime) !== 0) { + appliyngDailyRestriction = individuallDailyRestriction; + } + let customRestriction = await currentTransferManager.methods.individualRestriction(account).call(); + if (parseInt(customRestriction.endTime) !== 0) { + applyingCustomRestriction = customRestriction; + } + + hasIndividualRestrictions = applyingCustomRestriction || appliyngDailyRestriction; + + if (!hasIndividualRestrictions) { + let globalDailyRestriction = await currentTransferManager.methods.defaultDailyRestriction().call(); + if (parseInt(globalDailyRestriction.endTime) !== 0) { + appliyngDailyRestriction = globalDailyRestriction; + } + let globalCustomRestriction = await currentTransferManager.methods.defaultRestriction().call(); + if (parseInt(globalCustomRestriction.endTime) === 0) { + applyingCustomRestriction = globalCustomRestriction; + } + } + } + + console.log(`*** Applying restrictions for ${account} ***`, '\n'); + + console.log(`- Daily restriction: ${appliyngDailyRestriction ? (!hasIndividualRestrictions ? 'global' : '') : 'None'}`); + if (appliyngDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[appliyngDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${appliyngDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(appliyngDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(appliyngDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(appliyngDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${appliyngDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(appliyngDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Other restriction: ${applyingCustomRestriction ? (!hasIndividualRestrictions ? 'global' : '') : 'None'} `); + if (applyingCustomRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[applyingCustomRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${applyingCustomRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(applyingCustomRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(applyingCustomRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(applyingCustomRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${applyingCustomRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(applyingCustomRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + if (applyingCustomRestriction || appliyngDailyRestriction) { + let bucketDetails; + if (hasIndividualRestrictions) { + bucketDetails = await currentTransferManager.methods.getIndividualBucketDetailsToUser(account).call(); + } else { + bucketDetails = await currentTransferManager.methods.getDefaultBucketDetailsToUser(account).call(); + } + let now = Math.floor(Date.now() / 1000) - gbl.constants.DURATION.days(1); + let tradedByUserLastDay = await currentTransferManager.methods.getTotalTradedByUser(account, now).call(); + console.log(); + console.log(`Last trade: ${bucketDetails[0]}`); + console.log(`Last daily trade: ${bucketDetails[3]}`); + console.log(`Days since rolling period started: ${bucketDetails[2]}`); + console.log(`Transacted amount since rolling period started: ${web3.utils.fromWei(bucketDetails[1])}`); + console.log(`Transacted amount within last 24 hours: ${web3.utils.fromWei(tradedByUserLastDay)}`); + console.log(); + } +} + +async function operateWithMultipleRestrictions() { + let options = [ + 'Add multiple individual daily restrictions', + 'Modify multiple individual daily restrictions', + 'Remove multiple individual daily restrictions', + 'Add multiple individual restrictions', + 'Modify multiple individual restrictions', + 'Remove multiple individual restrictions' + ]; + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add multiple individual daily restrictions': + await addDailyRestrictionsInBatch(); + break; + case 'Modify multiple individual daily restrictions': + await modifyDailyRestrictionsInBatch(); + break; + case 'Remove multiple individual daily restrictions': + await removeDailyRestrictionsInBatch(); + break; + case 'Add multiple individual restrictions': + await addCustomRestrictionsInBatch(); + break; + case 'Modify multiple individual restrictions': + await modifyCustomRestrictionsInBatch(); + break; + case 'Remove multiple individual restrictions': + await removeCustomRestrictionsInBatch(); + break; + } +} + +async function addDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: ADD_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && RESTRICTION_TYPES.includes(row[4])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.addIndividualDailyRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: MODIFY_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && RESTRICTION_TYPES.includes(row[4])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.modifyIndividualDailyRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function removeDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: REMOVE_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + let action = currentTransferManager.methods.removeIndividualDailyRestrictionMulti(holderArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function addCustomRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_CUSTOM_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: ADD_CUSTOM_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + (!isNaN(row[3]) && (parseFloat(row[3]) % 1 === 0)) && + moment.unix(row[4]).isValid() && + typeof row[5] === 'string' && RESTRICTION_TYPES.includes(row[5])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, rollingPeriodArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add custom restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.addIndividualRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], rollingPeriodArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple custom restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyCustomRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_CUSTOM_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: MODIFY_CUSTOM_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + (!isNaN(row[3]) && (parseFloat(row[3]) % 1 === 0)) && + moment.unix(row[4]).isValid() && + typeof row[5] === 'string' && RESTRICTION_TYPES.includes(row[5])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, rollingPeriodArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify custom restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.modifyIndividualRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], rollingPeriodArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple custom restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function removeCustomRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_CUSTOM_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: REMOVE_CUSTOM_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove custom restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + let action = currentTransferManager.methods.removeIndividualRestrictionMulti(holderArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove multiple custom restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +function inputRestrictionData(isDaily) { + let restriction = {}; + restriction.restrictionType = readlineSync.keyInSelect(RESTRICTION_TYPES, 'How do you want to set the allowance? ', { cancel: false }); + if (restriction.restrictionType == RESTRICTION_TYPES.indexOf('Fixed')) { + restriction.allowedTokens = web3.utils.toWei(readlineSync.question(`Enter the maximum amount of tokens allowed to be traded every ${isDaily ? 'day' : 'rolling period'}: `).toString()); + } else { + restriction.allowedTokens = toWeiPercentage(readlineSync.question(`Enter the maximum percentage of total supply allowed to be traded every ${isDaily ? 'day' : 'rolling period'}: `).toString()); + } + if (isDaily) { + restriction.rollingPeriodInDays = 1; + } else { + restriction.rollingPeriodInDays = readlineSync.questionInt(`Enter the rolling period in days (10 days): `, { defaultInput: 10 }); + } + restriction.startTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) at which restriction get into effect (now = 0): `, { defaultInput: 0 }); + let oneMonthFromNow = Math.floor(Date.now() / 1000) + gbl.constants.DURATION.days(30); + restriction.endTime = readlineSync.question(`Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others (1 month from now = ${oneMonthFromNow}): `, { + limit: function (input) { + return input > restriction.startTime + gbl.constants.DURATION.days(restriction.rollingPeriodInDays); + }, + limitMessage: 'Must be greater than startTime + rolling period', + defaultInput: oneMonthFromNow + }); + return restriction; +} + +async function lockUpTransferManager() { + console.log('\n', chalk.blue(`Lockup Transfer Manager at ${currentTransferManager.options.address}`), '\n'); + + let currentLockups = await currentTransferManager.methods.getAllLockups().call(); + console.log(`- Lockups: ${currentLockups.length}`); + + let options = ['Add new lockup']; + if (currentLockups.length > 0) { + options.push('Manage existing lockups', 'Explore investor'); + } + options.push('Operate with multiple lockups'); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add new lockup': + let name = readlineSync.question(`Enter the name of the lockup type: `, { + limit: function (input) { + return input !== ""; + }, + limitMessage: `Invalid lockup name` + }); + let lockupAmount = readlineSync.questionInt(`Enter the amount of tokens that will be locked: `); + let minuteFromNow = Math.floor(Date.now() / 1000) + 60; + let startTime = readlineSync.questionInt(`Enter the start time (Unix Epoch time) of the lockup type (a minute from now = ${minuteFromNow}): `, { defaultInput: minuteFromNow }); + let lockUpPeriodSeconds = readlineSync.questionInt(`Enter the total period (seconds) of the lockup type (ten minutes = 600): `, { defaultInput: 600 }); + let releaseFrequencySeconds = readlineSync.questionInt(`Enter how often to release a tranche of tokens in seconds (one minute = 60): `, { defaultInput: 60 }); + if (readlineSync.keyInYNStrict(`Do you want to add an investor to this lockup type? `)) { + let investor = readlineSync.question(`Enter the address of the investor: `, { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: `Must be a valid address` + }); + let addNewLockUpToUserAction = currentTransferManager.methods.addNewLockUpToUser( + investor, + web3.utils.toWei(lockupAmount.toString()), + startTime, + lockUpPeriodSeconds, + releaseFrequencySeconds, + web3.utils.toHex(name) + ); + let addNewLockUpToUserReceipt = await common.sendTransaction(addNewLockUpToUserAction); + let addNewLockUpToUserEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addNewLockUpToUserReceipt.logs, 'AddNewLockUpType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(addNewLockUpToUserEvent._lockupName)} lockup type has been added successfully!`)); + let addLockUpToUserEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addNewLockUpToUserReceipt.logs, 'AddLockUpToUser'); + console.log(chalk.green(`${addLockUpToUserEvent._userAddress} has been added to ${web3.utils.hexToUtf8(addLockUpToUserEvent._lockupName)} successfully!`)); + } else { + let addLockupTypeAction = currentTransferManager.methods.addNewLockUpType(web3.utils.toWei(lockupAmount.toString()), startTime, lockUpPeriodSeconds, releaseFrequencySeconds, web3.utils.toHex(name)); + let addLockupTypeReceipt = await common.sendTransaction(addLockupTypeAction); + let addLockupTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addLockupTypeReceipt.logs, 'AddNewLockUpType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(addLockupTypeEvent._lockupName)} lockup type has been added successfully!`)); + } + break; + case 'Manage existing lockups': + let options = currentLockups.map(b => web3.utils.hexToUtf8(b)); + let index = readlineSync.keyInSelect(options, 'Which lockup type do you want to manage? ', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + if (index !== -1) { + await manageExistingLockups(currentLockups[index]); + } + break; + case 'Explore investor': + let investorToExplore = readlineSync.question('Enter the address you want to explore: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let lockupsToInvestor = await currentTransferManager.methods.getLockupsNamesToUser(investorToExplore).call(); + if (lockupsToInvestor.length > 0) { + let lockedTokenToInvestor = await currentTransferManager.methods.getLockedTokenToUser(investorToExplore).call(); + console.log(chalk.green(`The address ${investorToExplore} has ${web3.utils.fromWei(lockedTokenToInvestor)} ${tokenSymbol} locked across the following ${lockupsToInvestor.length} lockups: `)); + lockupsToInvestor.map(l => console.log(chalk.green(`- ${web3.utils.hexToUtf8(l)}`))); + } else { + console.log(chalk.yellow(`The address ${investorToExplore} has no lockups`)); + } + break; + case 'Operate with multiple lockups': + await operateWithMultipleLockups(currentLockups); + break; + case 'RETURN': + return; + } + + await lockUpTransferManager(); +} + +async function manageExistingLockups(lockupName) { + console.log('\n', chalk.blue(`Lockup ${web3.utils.hexToUtf8(lockupName)}`), '\n'); + + // Show current data + let currentLockup = await currentTransferManager.methods.getLockUp(lockupName).call(); + let investors = await currentTransferManager.methods.getListOfAddresses(lockupName).call(); + + console.log(`- Amount: ${web3.utils.fromWei(currentLockup.lockupAmount)} ${tokenSymbol}`); + console.log(`- Currently unlocked: ${web3.utils.fromWei(currentLockup.unlockedAmount)} ${tokenSymbol}`); + console.log(`- Start time: ${moment.unix(currentLockup.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- Lockup period: ${currentLockup.lockUpPeriodSeconds} seconds`); + console.log(`- End time: ${moment.unix(currentLockup.endTime).add(parseInt(currentLockup.lockUpPeriodSeconds)).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(`- Release frequency: ${currentLockup.releaseFrequencySeconds} senconds`); + console.log(`- Investors: ${investors.length}`); + // ------------------ + + let options = [ + 'Modify properties', + 'Show investors', + 'Add this lockup to investors', + 'Remove this lockup from investors', + 'Delete this lockup type' + ]; + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Modify properties': + let lockupAmount = readlineSync.questionInt(`Enter the amount of tokens that will be locked: `); + let minuteFromNow = Math.floor(Date.now() / 1000) + 60; + let startTime = readlineSync.questionInt(`Enter the start time (Unix Epoch time) of the lockup type (a minute from now = ${minuteFromNow}): `, { defaultInput: minuteFromNow }); + let lockUpPeriodSeconds = readlineSync.questionInt(`Enter the total period (seconds) of the lockup type (ten minutes = 600): `, { defaultInput: 600 }); + let releaseFrequencySeconds = readlineSync.questionInt(`Enter how often to release a tranche of tokens in seconds (one minute = 60): `, { defaultInput: 60 }); + let modifyLockUpTypeAction = currentTransferManager.methods.modifyLockUpType(lockupAmount, startTime, lockUpPeriodSeconds, releaseFrequencySeconds, lockupName); + let modifyLockUpTypeReceipt = await common.sendTransaction(modifyLockUpTypeAction); + let modifyLockUpTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyLockUpTypeReceipt.logs, 'ModifyLockUpType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(modifyLockUpTypeEvent._lockupName)} lockup type has been modified successfully!`)); + break; + case 'Show investors': + if (investors.length > 0) { + console.log("************ List of investors ************"); + investors.map(i => console.log(i)); + } else { + console.log(chalk.yellow("There are no investors yet")); + } + break; + case 'Add this lockup to investors': + let investorsToAdd = readlineSync.question(`Enter the addresses of the investors separated by comma (i.e.addr1, addr2, addr3): `, { + limit: function (input) { + return (input !== '' && input.split(",").every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(","); + let addInvestorToLockupAction; + if (investorsToAdd.length === 1) { + addInvestorToLockupAction = currentTransferManager.methods.addLockUpByName(investorsToAdd[0], lockupName); + } else { + addInvestorToLockupAction = currentTransferManager.methods.addLockUpByNameMulti(investorsToAdd, investorsToAdd.map(i => lockupName)); + } + let addInvestorToLockupReceipt = await common.sendTransaction(addInvestorToLockupAction); + let addInvestorToLockupEvents = common.getMultipleEventsFromLogs(currentTransferManager._jsonInterface, addInvestorToLockupReceipt.logs, 'AddLockUpToUser'); + addInvestorToLockupEvents.map(e => console.log(chalk.green(`${e._userAddress} has been added to ${web3.utils.hexToUtf8(e._lockupName)} successfully!`))); + break; + case 'Remove this lockup from investors': + let investorsToRemove = readlineSync.question(`Enter the addresses of the investors separated by comma (i.e.addr1, addr2, addr3): `, { + limit: function (input) { + return (input !== '' && input.split(",").every(a => web3.utils.isAddress(a))); + }, + limitMessage: `All addresses must be valid` + }).split(","); + let removeLockupFromInvestorAction; + if (investorsToRemove.length === 1) { + removeLockupFromInvestorAction = currentTransferManager.methods.removeLockUpFromUser(investorsToRemove[0], lockupName); + } else { + removeLockupFromInvestorAction = currentTransferManager.methods.removeLockUpFromUserMulti(investorsToRemove, investorsToRemove.map(i => lockupName)); + } + let removeLockUpFromUserReceipt = await common.sendTransaction(removeLockupFromInvestorAction); + let removeLockUpFromUserEvents = common.getMultipleEventsFromLogs(currentTransferManager._jsonInterface, removeLockUpFromUserReceipt.logs, 'RemoveLockUpFromUser'); + removeLockUpFromUserEvents.map(e => console.log(chalk.green(`${e._userAddress} has been removed to ${web3.utils.hexToUtf8(e._lockupName)} successfully!`))); + break; + case 'Delete this lockup type': + let isEmpty = investors.length === 0; + if (!isEmpty) { + console.log(chalk.yellow(`This lockup have investors added to it. To delete it you must remove them first.`)); + if (readlineSync.keyInYNStrict(`Do you want to remove them? `)) { + let data = investors.map(i => [i, lockupName]) + let batches = common.splitIntoBatches(data, gbl.constants.DEFAULT_BATCH_SIZE); + let [investorArray, lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove the following investors:\n\n`, investorArray[batch], '\n'); + let action = currentTransferManager.methods.removeLockUpFromUserMulti(investorArray[batch], lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove lockups from multiple investors transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } + isEmpty = true; + } + } + if (isEmpty) { + let removeLockupTypeAction = currentTransferManager.methods.removeLockupType(lockupName); + let removeLockupTypeReceipt = await common.sendTransaction(removeLockupTypeAction); + let removeLockupTypeEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeLockupTypeReceipt.logs, 'RemoveLockUpType'); + console.log(chalk.green(`${web3.utils.hexToUtf8(removeLockupTypeEvent._lockupName)} lockup type has been deleted successfully!`)); + } + return; + case 'RETURN': + return; + } + + await manageExistingLockups(lockupName); +} + +async function operateWithMultipleLockups(currentLockups) { + let options = ['Add multiple lockups']; + if (currentLockups.length > 0) { + options.push('Modify multiple lockups'); + } + options.push( + 'Delete multiple lockups', + 'Add lockups to multiple investors', + 'Remove lockups from multiple investors' + ); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add multiple lockups': + await addLockupsInBatch(); + break; + case 'Modify multiple lockups': + await modifyLockupsInBatch(); + break; + case 'Delete multiple lockups': + await deleteLockupsInBatch(); + break; + case 'Add lockups to multiple investors': + await addLockupsToInvestorsInBatch(); + break; + case 'Remove lockups from multiple investors': + await removeLockupsFromInvestorsInBatch(); + break; + } +} + +async function addLockupsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_LOCKUP_DATA_CSV}): `, { + defaultInput: ADD_LOCKUP_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => !isNaN(row[0]) && + moment.unix(row[1]).isValid() && + (!isNaN(row[2] && (parseFloat(row[2]) % 1 === 0))) && + (!isNaN(row[3] && (parseFloat(row[3]) % 1 === 0))) && + typeof row[4] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [amountArray, startTimeArray, lockUpPeriodArray, releaseFrequencyArray, lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add the following lockups: \n\n`, lockupNameArray[batch], '\n'); + lockupNameArray[batch] = lockupNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.addNewLockUpTypeMulti(amountArray[batch], startTimeArray[batch], lockUpPeriodArray[batch], releaseFrequencyArray[batch], lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple lockups transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyLockupsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_LOCKUP_DATA_CSV}): `, { + defaultInput: MODIFY_LOCKUP_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => !isNaN(row[0]) && + moment.unix(row[1]).isValid() && + (!isNaN(row[2] && (parseFloat(row[2]) % 1 === 0))) && + (!isNaN(row[3] && (parseFloat(row[3]) % 1 === 0))) && + typeof row[4] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [amountArray, startTimeArray, lockUpPeriodArray, releaseFrequencyArray, lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify the following lockups: \n\n`, lockupNameArray[batch], '\n'); + lockupNameArray[batch] = lockupNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.modifyLockUpTypeMulti(amountArray[batch], startTimeArray[batch], lockUpPeriodArray[batch], releaseFrequencyArray[batch], lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple lockups transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function deleteLockupsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${DELETE_LOCKUP_DATA_CSV}): `, { + defaultInput: DELETE_LOCKUP_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => typeof row[0] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to delete the following lockups: \n\n`, lockupNameArray[batch], '\n'); + lockupNameArray[batch] = lockupNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.removeLockupTypeMulti(lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Delete multiple lockups transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function addLockupsToInvestorsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_LOCKUP_INVESTOR_DATA_CSV}): `, { + defaultInput: ADD_LOCKUP_INVESTOR_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + typeof row[1] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add lockups to the following investors: \n\n`, investorArray[batch], '\n'); + lockupNameArray[batch] = lockupNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.addLockUpByNameMulti(investorArray[batch], lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add lockups to multiple investors transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function removeLockupsFromInvestorsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_LOCKUP_INVESTOR_DATA_CSV}): `, { + defaultInput: REMOVE_LOCKUP_INVESTOR_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + typeof row[1] === 'string'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [investorArray, lockupNameArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove the following investors: \n\n`, investorArray[batch], '\n'); + lockupNameArray[batch] = lockupNameArray[batch].map(n => web3.utils.toHex(n)); + let action = currentTransferManager.methods.removeLockUpFromUserMulti(investorArray[batch], lockupNameArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove lockups from multiple investors transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +/* +// Copied from tests +function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, pk) { + let packedData = ethers.utils + .solidityKeccak256( + ["address", "address", "uint256", "uint256", "uint256", "bool", "uint256", "uint256"], + [tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo] + ) + .slice(2); + packedData = new Buffer(packedData, "hex"); + packedData = Buffer.concat([new Buffer(`\x19Ethereum Signed Message: \n${ packedData.length.toString() } `), packedData]); + packedData = web3.sha3(`0x${ packedData.toString("hex") } `, { encoding: "hex" }); + return ethUtil.ecsign(new Buffer(packedData.slice(2), "hex"), new Buffer(pk, "hex")); +} +*/ + +function toWeiPercentage(number) { + return web3.utils.toWei((parseFloat(number) / 100).toString()); +} + +function fromWeiPercentage(number) { + return web3.utils.fromWei(new web3.utils.BN(number).muln(100)).toString(); +} + +async function getAllModulesByType(type) { + function ModuleInfo(_moduleType, _name, _address, _factoryAddress, _archived, _paused) { + this.name = _name; + this.type = _moduleType; + this.address = _address; + this.factoryAddress = _factoryAddress; + this.archived = _archived; + this.paused = _paused; + } + + let modules = []; + + let allModules = await securityToken.methods.getModulesByType(type).call(); + + for (let i = 0; i < allModules.length; i++) { + let details = await securityToken.methods.getModule(allModules[i]).call(); + let nameTemp = web3.utils.hexToUtf8(details[0]); + let pausedTemp = null; + if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; + let contractTemp = new web3.eth.Contract(abiTemp, details[1]); + pausedTemp = await contractTemp.methods.paused().call(); + } + modules.push(new ModuleInfo(type, nameTemp, details[1], details[2], details[3], pausedTemp)); + } + + return modules; +} + +async function initialize(_tokenSymbol) { + welcome(); + await setup(); + if (typeof _tokenSymbol === 'undefined') { + tokenSymbol = await selectToken(); + } else { + tokenSymbol = _tokenSymbol; + } + let securityTokenAddress = await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call(); + if (securityTokenAddress == '0x0000000000000000000000000000000000000000') { + console.log(chalk.red(`Selected Security Token ${tokenSymbol} does not exist.`)); + process.exit(0); + } + let securityTokenABI = abis.securityToken(); + securityToken = new web3.eth.Contract(securityTokenABI, securityTokenAddress); + securityToken.setProvider(web3.currentProvider); +} + +function welcome() { common.logAsciiBull(); console.log("*********************************************"); console.log("Welcome to the Command-Line Transfer Manager."); console.log("*********************************************"); console.log("Issuer Account: " + Issuer.address + "\n"); +} - await setup(); - try { - await start_explorer(); - } catch (err) { - console.log(err); - return; - } -}; - -async function setup(){ +async function setup() { try { let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); securityTokenRegistry.setProvider(web3.currentProvider); + + let moduleRegistryAddress = await contracts.moduleRegistry(); + let moduleRegistryABI = abis.moduleRegistry(); + moduleRegistry = new web3.eth.Contract(moduleRegistryABI, moduleRegistryAddress); + moduleRegistry.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."); + 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 start_explorer() { - console.log('\n\x1b[34m%s\x1b[0m',"Transfer Manager - Main Menu"); +async function selectToken() { + let result = null; - if (!tokenSymbol) - tokenSymbol = readlineSync.question('Enter the token symbol: '); + let userTokens = await securityTokenRegistry.methods.getTokensByOwner(Issuer.address).call(); + let tokenDataArray = await Promise.all(userTokens.map(async function (t) { + let tokenData = await securityTokenRegistry.methods.getSecurityTokenData(t).call(); + return { symbol: tokenData[0], address: t }; + })); + let options = tokenDataArray.map(function (t) { + return `${t.symbol} - Deployed at ${t.address} `; + }); + options.push('Enter token symbol manually'); - 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.`)); - } else { - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI,result); + let index = readlineSync.keyInSelect(options, 'Select a token:', { cancel: 'EXIT' }); + let selected = index != -1 ? options[index] : 'EXIT'; + switch (selected) { + case 'Enter token symbol manually': + result = readlineSync.question('Enter the token symbol: '); + break; + case 'EXIT': + process.exit(); + break; + default: + result = tokenDataArray[index].symbol; + break; + } - let forcedTransferDisabled = await securityToken.methods.controllerDisabled().call(); + return result; +} - if (!forcedTransferDisabled) { - let options = ['Disable controller', 'Set controller']; - let controller = await securityToken.methods.controller().call(); - if (controller == Issuer.address) { - options.push('Force Transfer') - } - let index = readlineSync.keyInSelect(options, 'What do you want to do?'); - let optionSelected = options[index]; - console.log('Selected:', index != -1 ? optionSelected : 'Cancel', '\n'); - switch (optionSelected) { - case 'Disable controller': - if (readlineSync.keyInYNStrict()) { - let disableControllerAction = securityToken.methods.disableController(); - await common.sendTransaction(Issuer, disableControllerAction, defaultGasPrice); - console.log(chalk.green(`Forced transfers have been disabled permanently`)); - } - break; - case 'Set controller': - let controllerAddress = readlineSync.question(`Enter the address for the controller (${Issuer.address}): `, { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - let setControllerAction = securityToken.methods.setController(controllerAddress); - let setControllerReceipt = await common.sendTransaction(Issuer, setControllerAction, defaultGasPrice); - let setControllerEvent = common.getEventFromLogs(securityToken._jsonInterface, setControllerReceipt.logs, 'SetController'); - console.log(chalk.green(`New controller is ${setControllerEvent._newController}`)); - break; - case 'Force Transfer': - let from = readlineSync.question('Enter the address from which to take tokens: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - }); - let fromBalance = web3.utils.fromWei(await securityToken.methods.balanceOf(from).call()); - console.log(chalk.yellow(`Balance of ${from}: ${fromBalance} ${tokenSymbol}`)); - let to = readlineSync.question('Enter address where to send tokens: ', { - limit: function(input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - }); - let toBalance = web3.utils.fromWei(await securityToken.methods.balanceOf(to).call()); - console.log(chalk.yellow(`Balance of ${to}: ${toBalance} ${tokenSymbol}`)); - let amount = readlineSync.question('Enter amount of tokens to transfer: ', { - limit: function(input) { - return parseInt(input) <= parseInt(fromBalance); - }, - limitMessage: `Amount must be less or equal than ${fromBalance} ${tokenSymbol}`, - }); - let data = readlineSync.question('Enter the data to indicate validation: '); - let log = readlineSync.question('Enter the data attached to the transfer by controller to emit in event: '); - let forceTransferAction = securityToken.methods.forceTransfer(from, to, web3.utils.toWei(amount), web3.utils.asciiToHex(data), web3.utils.asciiToHex(log)); - let forceTransferReceipt = await common.sendTransaction(Issuer, forceTransferAction, defaultGasPrice, 0, 1.5); - let forceTransferEvent = common.getEventFromLogs(securityToken._jsonInterface, forceTransferReceipt.logs, 'ForceTransfer'); - console.log(chalk.green(` ${forceTransferEvent._controller} has successfully forced a transfer of ${web3.utils.fromWei(forceTransferEvent._value)} ${tokenSymbol} - from ${forceTransferEvent._from} to ${forceTransferEvent._to} - Verified transfer: ${forceTransferEvent._verifyTransfer} - Data: ${web3.utils.hexToAscii(forceTransferEvent._data)} - `)); - console.log(`Balance of ${from} after transfer: ${web3.utils.fromWei(await securityToken.methods.balanceOf(from).call())} ${tokenSymbol}`); - console.log(`Balance of ${to} after transfer: ${web3.utils.fromWei(await securityToken.methods.balanceOf(to).call())} ${tokenSymbol}`); - break; - default: - process.exit(0); - } - } else { - console.log(chalk.red(`Controller featueres are permanently disabled for this token.`)) - tokenSymbol = undefined; - } - } +async function logTotalInvestors() { + let investorsCount = await securityToken.methods.getInvestorCount().call(); + console.log(chalk.yellow(`Total investors at the moment: ${investorsCount} `)); +} - //Restart - await start_explorer(); +async function logBalance(from, totalSupply) { + let fromBalance = web3.utils.fromWei(await securityToken.methods.balanceOf(from).call()); + let percentage = totalSupply != '0' ? ` - ${parseFloat(fromBalance) / parseFloat(totalSupply) * 100}% of total supply` : ''; + console.log(chalk.yellow(`Balance of ${from}: ${fromBalance} ${tokenSymbol} ${percentage} `)); } module.exports = { - executeApp: async function(type, remoteNetwork) { - return executeApp(type, remoteNetwork); + executeApp: async function (_tokenSymbol) { + await initialize(_tokenSymbol); + return executeApp(); + }, + addTransferManagerModule: async function (_tokenSymbol) { + await initialize(_tokenSymbol); + return addTransferManagerModule(); + }, + modifyWhitelistInBatch: async function (_tokenSymbol, _csvFilePath, _batchSize) { + await initialize(_tokenSymbol); + let gmtModules = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); + let generalTransferManagerAddress = gmtModules[0]; + currentTransferManager = new web3.eth.Contract(abis.generalTransferManager(), generalTransferManagerAddress); + currentTransferManager.setProvider(web3.currentProvider); + return modifyWhitelistInBatch(_csvFilePath, _batchSize); } } \ No newline at end of file diff --git a/CLI/commands/transfer_ownership.js b/CLI/commands/transfer_ownership.js index 68ca7eb7a..99e12b80f 100644 --- a/CLI/commands/transfer_ownership.js +++ b/CLI/commands/transfer_ownership.js @@ -1,6 +1,5 @@ var chalk = require('chalk'); var common = require('./common/common_functions'); -var global = require('./common/global'); /////////////////////////////ARTIFACTS////////////////////////////////////////// var abis = require('./helpers/contract_abis') @@ -8,8 +7,7 @@ var abis = require('./helpers/contract_abis') let contract; //////////////////////////////////////////ENTRY INTO SCRIPT////////////////////////////////////////// -async function startScript(contractAddress, transferTo, remoteNetwork) { - await global.initialize(remoteNetwork); +async function startScript(contractAddress, transferTo) { if (!web3.utils.isAddress(contractAddress) || !web3.utils.isAddress(transferTo)) { console.log(chlak.red(`Please enter valid addresses`)); @@ -28,14 +26,14 @@ async function transferOwnership(transferTo) { console.log(chalk.red(`You are not the current owner ot this contract. Current owner is ${currentOwner}.`)); } else { let transferOwnershipAction = contract.methods.transferOwnership(transferTo); - let receipt = await common.sendTransaction(Issuer, transferOwnershipAction, defaultGasPrice); + let receipt = await common.sendTransaction(transferOwnershipAction); let event = common.getEventFromLogs(contract._jsonInterface, receipt.logs, 'OwnershipTransferred'); console.log(chalk.green(`Ownership transferred successfully. New owner is ${event.newOwner}`)); } }; module.exports = { - executeApp: async function(contractAddress, transferTo, remoteNetwork) { - return startScript(contractAddress, transferTo, remoteNetwork); + executeApp: async function(contractAddress, transferTo) { + return startScript(contractAddress, transferTo); } } diff --git a/CLI/commands/whitelist.js b/CLI/commands/whitelist.js deleted file mode 100644 index 4750adffa..000000000 --- a/CLI/commands/whitelist.js +++ /dev/null @@ -1,302 +0,0 @@ -var fs = require('fs'); -var csv = require('fast-csv'); -var BigNumber = require('bignumber.js'); -var common = require('./common/common_functions'); -var global = require('./common/global'); - -/////////////////////////////ARTIFACTS////////////////////////////////////////// -var contracts = require('./helpers/contract_addresses'); -var abis = require('./helpers/contract_abis'); - -////////////////////////////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 = 70; -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(); -let badData = new Array(); - -const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); - -//////////////////////////////////////////ENTRY INTO SCRIPT////////////////////////////////////////// - -startScript(); - -async function startScript() { - if (remoteNetwork == 'undefined') remoteNetwork = undefined; - await global.initialize(remoteNetwork); - - try { - 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/whitelist_data.csv"); - - let index = 0; - let batch = 0; - console.log(` - -------------------------------------------- - ----------- Parsing the csv file ----------- - -------------------------------------------- - `); - - var csvStream = csv() - .on("data", function (data) { - // console.log(data[1]) - // console.log(data[2]) - // console.log(data[3]) - let isAddress = web3.utils.isAddress(data[0]); - let sellValid = isValidDate(data[1]) - let buyValid = isValidDate(data[2]) - let kycExpiryDate = isValidDate(data[3]) - let canBuyFromSTO = (typeof JSON.parse(data[4].toLowerCase())) == "boolean" ? JSON.parse(data[4].toLowerCase()) : "not-valid"; - - if (isAddress && sellValid && buyValid && kycExpiryDate && (canBuyFromSTO != "not-valid") ) { - let userArray = new Array() - let checksummedAddress = web3.utils.toChecksumAddress(data[0]); - - userArray.push(checksummedAddress) - userArray.push(sellValid) - userArray.push(buyValid) - userArray.push(kycExpiryDate) - userArray.push(canBuyFromSTO) - // console.log(userArray) - allocData.push(userArray); - fullFileData.push(userArray); - index++; - if (index >= BATCH_SIZE) { - distribData.push(allocData); - // console.log("DIS", distribData); - allocData = []; - // console.log("ALLOC", allocData); - index = 0; - } - - } else { - let userArray = new Array() - //dont need this here, as if it is NOT an address this function will fail - //let checksummedAddress = web3.utils.toChecksumAddress(data[1]); - userArray.push(data[0]) - userArray.push(sellValid) - userArray.push(buyValid) - userArray.push(kycExpiryDate); - userArray.push(canBuyFromSTO); - badData.push(userArray); - fullFileData.push(userArray) - } - }) - .on("end", function () { - //Add last remainder batch - distribData.push(allocData); - allocData = []; - - setInvestors(); - }); - - stream.pipe(csvStream); -} - -////////////////////////MAIN FUNCTION COMMUNICATING TO BLOCKCHAIN -async function setInvestors() { - let tokenDeployed = false; - let tokenDeployedAddress; - // Let's check if token has already been deployed, if it has, skip to STO - await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call({}, function (error, result) { - if (result != "0x0000000000000000000000000000000000000000") { - console.log('\x1b[32m%s\x1b[0m', "Token deployed at address " + result + "."); - tokenDeployedAddress = result; - tokenDeployed = true; - } - }); - if (tokenDeployed) { - let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI, tokenDeployedAddress); - } - let gmtModules = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); - let generalTransferManagerAddress = gmtModules[0]; - let generalTransferManagerABI = abis.generalTransferManager(); - let generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, generalTransferManagerAddress); - - console.log(` - ------------------------------------------------------- - ----- Sending buy/sell restrictions 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 fromTimesArray = []; - let toTimesArray = []; - let expiryTimeArray = []; - let canBuyFromSTOArray = []; - - //splitting the user arrays to be organized by input - for (let j = 0; j < distribData[i].length; j++) { - investorArray.push(distribData[i][j][0]) - fromTimesArray.push(distribData[i][j][1]) - toTimesArray.push(distribData[i][j][2]) - expiryTimeArray.push(distribData[i][j][3]) - canBuyFromSTOArray.push(distribData[i][j][4]) - } - - //fromTimes is ability to sell coin FROM your account (2nd row in csv, 2nd parameter in modifyWhiteList() ) - //toTimes is ability to buy coins TOwards your account (3rd row in csv, 3rd parameter in modifyWhiteList() ) - //expiryTime is time at which KYC of investor get expired (4th row in csv, 4rd parameter in modifyWhiteList() ) - let modifyWhitelistMultiAction = generalTransferManager.methods.modifyWhitelistMulti(investorArray, fromTimesArray, toTimesArray, expiryTimeArray, canBuyFromSTOArray); - let r = await common.sendTransaction(Issuer, modifyWhitelistMultiAction, defaultGasPrice); - console.log(`Batch ${i} - Attempting to modifyWhitelist accounts:\n\n`, investorArray, "\n\n"); - console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------"); - console.log("Whitelist transaxction was successful.", r.gasUsed, "gas used. Spent:", web3.utils.fromWei(BigNumber(r.gasUsed * defaultGasPrice).toString(), "ether"), "Ether"); - console.log("---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n\n"); - - } catch (err) { - console.log("ERROR:", err); - } - } - - console.log("Retrieving logs to determine investors have had their times uploaded correctly.\n\n") - - let totalInvestors = 0; - let updatedInvestors = 0; - - let investorData_Events = new Array(); - let investorObjectLookup = {}; - - let event_data = await generalTransferManager.getPastEvents('ModifyWhitelist', { - fromBlock: 0, - toBlock: 'latest' - }, function (error, events) { - //console.log(error); - }); - - for (var i = 0; i < event_data.length; i++) { - let combineArray = []; - - let investorAddress_Event = event_data[i].returnValues._investor; - let fromTime_Event = event_data[i].returnValues._fromTime - let toTime_Event = event_data[i].returnValues._toTime - let expiryTime_Event = event_data[i].returnValues._expiryTime - let canBuyFromSTO_Event = event_data[i].returnValues._canBuyFromSTO - let blockNumber = event_data[i].blockNumber - - combineArray.push(investorAddress_Event); - combineArray.push(fromTime_Event); - combineArray.push(toTime_Event); - combineArray.push(expiryTime_Event); - combineArray.push(canBuyFromSTO_Event); - combineArray.push(blockNumber) - - investorData_Events.push(combineArray) - - //we have already recorded it, so this is an update to our object - if (investorObjectLookup.hasOwnProperty(investorAddress_Event)) { - - //the block number form the event we are checking is bigger, so we gotta replace it - if (investorObjectLookup[investorAddress_Event].recordedBlockNumber < blockNumber) { - investorObjectLookup[investorAddress_Event] = { fromTime: fromTime_Event, toTime: toTime_Event, expiryTime: expiryTime_Event, canBuyFromSTO: canBuyFromSTO_Event, recordedBlockNumber: blockNumber }; - updatedInvestors += 1; - // investorAddress_Events.push(investorAddress_Event); not needed, because we start the obj with zero events - - } else { - //do nothing. so if we find an event, and it was an older block, its old, we dont care - } - //we have never recorded this address as an object key, so we need to add it to our list of investors updated by the csv - } else { - investorObjectLookup[investorAddress_Event] = { fromTime: fromTime_Event, toTime: toTime_Event, expiryTime: expiryTime_Event, canBuyFromSTO: canBuyFromSTO_Event, recordedBlockNumber: blockNumber }; - totalInvestors += 1; - // investorAddress_Events.push(investorAddress_Event); - } - } - let investorAddress_Events = Object.keys(investorObjectLookup) - - console.log(`******************** EVENT LOGS ANALYSIS COMPLETE ********************\n`); - console.log(`A total of ${totalInvestors} investors have been whitelisted total, all time.\n`); - console.log(`This script in total sent ${fullFileData.length - badData.length} new investors and updated investors to the blockchain.\n`); - console.log(`There were ${badData.length} bad entries that didnt get sent to the blockchain in the script.\n`); - - // console.log("LIST OF ALL INVESTOR DATA FROM EVENTS:", investorData_Events) - // console.log(fullFileData) - console.log("************************************************************************************************"); - console.log("OBJECT WITH EVERY USER AND THEIR UPDATED TIMES: \n\n", investorObjectLookup) - console.log("************************************************************************************************"); - console.log("LIST OF ALL INVESTORS WHITELISTED: \n\n", investorAddress_Events) - - let missingDistribs = []; - for (let l = 0; l < fullFileData.length; l++) { - if (!investorObjectLookup.hasOwnProperty(fullFileData[l][0])) { - missingDistribs.push(fullFileData[l]) - } - } - - if (missingDistribs.length > 0) { - console.log("************************************************************************************************"); - console.log("-- No LogModifyWhitelist event was found for the following data arrays. Please review them manually --") - console.log(missingDistribs) - // for (var i = 0; i < missingDistribs.length; i++) { - // console.log('\x1b[31m%s\x1b[0m', `No Transfer event was found for account ${missingDistribs[i]}`); - // } - console.log("************************************************************************************************"); - } else { - console.log("\n************************************************************************************************"); - console.log("All accounts passed through from the CSV were successfully whitelisted, because we were able to read them all from events") - console.log("************************************************************************************************"); - } - // console.log(`Run 'node scripts/verify_airdrop.js ${polyDistribution.address} > scripts/data/review.csv' to get a log of all the accounts that were distributed the airdrop tokens.`) -} - -//will be deleted once DATES are updated -function isValidDayInput(days) { - let today = Date.now() / 1000 - let isValid = !isNaN(days) - if (isValid) { - let addedSeconds = days * 86400 - - let unixTimestamp = today + addedSeconds - console.log("unxitimestapm :" , (unixTimestamp)) - - return unixTimestamp - } else { - return false - } -} - -function isValidDate(date) { - var matches = /^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/.exec(date); - if (matches == null) return false; - var d = matches[2]; - var m = matches[1] - 1; //not clear why this is -1, but it works after checking - var y = matches[3]; - var composedDate = new Date(y, m, d); - var timestampDate = composedDate.getTime() - - //note, some reason these timestamps are being recorded +4 hours UTC - if (composedDate.getDate() == d && composedDate.getMonth() == m && composedDate.getFullYear() == y) { - return timestampDate / 1000 - } else { - return false - } -} diff --git a/CLI/data/dividendsExclusions_data.csv b/CLI/data/Checkpoint/dividendsExclusions_data.csv similarity index 100% rename from CLI/data/dividendsExclusions_data.csv rename to CLI/data/Checkpoint/dividendsExclusions_data.csv diff --git a/CLI/data/Checkpoint/exclusions_data.csv b/CLI/data/Checkpoint/exclusions_data.csv new file mode 100644 index 000000000..406cd94cf --- /dev/null +++ b/CLI/data/Checkpoint/exclusions_data.csv @@ -0,0 +1,5 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1 +0x49fc0b78238dab644698a90fa351b4c749e123d2 +0x10223927009b8add0960359dd90d1449415b7ca9 +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399 +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98 \ No newline at end of file diff --git a/CLI/data/Checkpoint/tax_withholding_data.csv b/CLI/data/Checkpoint/tax_withholding_data.csv new file mode 100644 index 000000000..10c2928c8 --- /dev/null +++ b/CLI/data/Checkpoint/tax_withholding_data.csv @@ -0,0 +1,10 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,0.5 +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,1 +0xac297053173b02b02a737d47f7b4a718e5b170ef,2 +0x49fc0b78238dab644698a90fa351b4c749e123d2,10 +0x10223927009b8add0960359dd90d1449415b7ca9,15 +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,50 +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,0 +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,23 +0x56be93088141b16ebaa9416122fd1d928da25ecf,45 +0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,67 \ No newline at end of file diff --git a/CLI/data/multi_mint_data.csv b/CLI/data/ST/multi_mint_data.csv similarity index 69% rename from CLI/data/multi_mint_data.csv rename to CLI/data/ST/multi_mint_data.csv index 0b157b164..8b27f06b0 100644 --- a/CLI/data/multi_mint_data.csv +++ b/CLI/data/ST/multi_mint_data.csv @@ -1,8 +1,8 @@ -0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,1000 +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,123.4 0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,1000 -0xac297053173b02b02a737d47f7b4a718e5b170ef,1000 +0xac297053173b02b02a737d47f7b4a718e5b170ef,234.5 0x49fc0b78238dab644698a90fa351b4c749e123d2,1000 -0x10223927009b8add0960359dd90d1449415b7ca9,1000 +0x10223927009b8add0960359dd90d1449415b7ca9,345.6 0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,1000 0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,1000 0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,1000 diff --git a/CLI/data/accredited_data.csv b/CLI/data/STO/USDTieredSTO/accredited_data.csv similarity index 100% rename from CLI/data/accredited_data.csv rename to CLI/data/STO/USDTieredSTO/accredited_data.csv diff --git a/CLI/data/nonAccreditedLimits_data.csv b/CLI/data/STO/USDTieredSTO/nonAccreditedLimits_data.csv similarity index 100% rename from CLI/data/nonAccreditedLimits_data.csv rename to CLI/data/STO/USDTieredSTO/nonAccreditedLimits_data.csv diff --git a/CLI/data/STO/capped_sto_data.yml b/CLI/data/STO/capped_sto_data.yml new file mode 100644 index 000000000..41653c9ff --- /dev/null +++ b/CLI/data/STO/capped_sto_data.yml @@ -0,0 +1,14 @@ +# type -> 'CappedSTO' for cappedSTO, 'USDTieredSTO' for USDTieredSTO +type: 'CappedSTO' +# cap -> Maximum No. of tokens for sale. +cap: 1000000 +# startTime -> Unix timestamp at which offering get started. '' for CLI default (one minute from now) +startTime: '' +# endTime -> Unix timestamp at which offering get ended. '' for CLI default (one month from now) +endTime: '' +# wallet -> Ethereum account address to hold the funds +wallet: '0x0a519b4b6501f92e8f516230b97aca83257b0c01' +# raiseType -> Type of currency used to collect the funds. [0] for ETH, [1] for POLY +raiseType: [0] +# rate -> Token units a buyer gets per unit of selected raise type. +rate: 1000 \ No newline at end of file diff --git a/CLI/data/STO/usd_tiered_sto_data.yml b/CLI/data/STO/usd_tiered_sto_data.yml new file mode 100644 index 000000000..29655c5bf --- /dev/null +++ b/CLI/data/STO/usd_tiered_sto_data.yml @@ -0,0 +1,37 @@ +# type -> 'CappedSTO' for cappedSTO, 'USDTieredSTO' for USDTieredSTO +type: 'USDTieredSTO' + +funding: + # fundigType -> Types of currency used to collect the funds, 0 for ETH, 1 for POLY, 2 for DAI, any combination of them for more than one (i.e. [0, 1, 2] for all of them) + raiseType: [0, 1] + +addresses: + # wallet -> Ethereum account address to hold the funds. + wallet: '0x0a519b4b6501f92e8f516230b97aca83257b0c01' + # reserveWallet -> Ethereum account address to receive unsold tokens. + reserveWallet: '0x0a519b4b6501f92e8f516230b97aca83257b0c01' + # usdToken -> Contract address of the stable coin. + usdToken: '0x0000000000000000000000000000000000000000' + + +tiers: + # tokensPerTiers -> Total tokens for each tier + tokensPerTier: !!seq [ 190000000, 100000000, 200000000 ] + # ratePerTiers -> Rate for each tier (in USD) + ratePerTier: !!seq [ 0.05, 0.10, 0.15 ] + # discountedTokensPerTiers -> Tokens for discounted rate for POLY investments. 0 for no discounted tokens + tokensPerTierDiscountPoly: !!seq [ 0, 0, 100000000 ] + # discountedRatePerTiers -> Discounted rate for POLY investments for each tier. 0 for no discounted rate + ratePerTierDiscountPoly: !!seq [ 0, 0, 0.075 ] + +limits: + # minimumInvestmentUSD -> Minimun investment in USD. + minimumInvestmentUSD: 5 + # nonAccreditedLimitUSD -> Limit in USD for non-accredited investors + nonAccreditedLimitUSD: 10000 + +times: + # startTime -> Unix timestamp at which offering get started. '' for CLI default (one minute from now) + startTime: '' + # endTime -> Unix timestamp at which offering get ended. '' for CLI default (one month from now) + endTime: '' \ No newline at end of file diff --git a/CLI/data/ticker_data.csv b/CLI/data/Ticker/ticker_data.csv similarity index 100% rename from CLI/data/ticker_data.csv rename to CLI/data/Ticker/ticker_data.csv diff --git a/CLI/data/Transfer/BlacklistTM/add_blacklist_data.csv b/CLI/data/Transfer/BlacklistTM/add_blacklist_data.csv new file mode 100644 index 000000000..1d925053d --- /dev/null +++ b/CLI/data/Transfer/BlacklistTM/add_blacklist_data.csv @@ -0,0 +1,4 @@ +1559401200,1560178800,"FirstTenDays",30 +1560535200,1560621600,"NoRepeat",0 +1566734400,1567252800,"Every90",90 +1567296000,1567303200,"TwoHours",2 diff --git a/CLI/data/Transfer/BlacklistTM/add_investor_blacklist_data.csv b/CLI/data/Transfer/BlacklistTM/add_investor_blacklist_data.csv new file mode 100644 index 000000000..cd0fe922c --- /dev/null +++ b/CLI/data/Transfer/BlacklistTM/add_investor_blacklist_data.csv @@ -0,0 +1,12 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,"FirstTenDays" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,"FirstTenDays" +0xac297053173b02b02a737d47f7b4a718e5b170ef,"FirstTenDays" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"FirstTenDays" +0x10223927009b8add0960359dd90d1449415b7ca9,"FirstTenDays" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"NoRepeat" +0x10223927009b8add0960359dd90d1449415b7ca9,"NoRepeat" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,"NoRepeat" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,"NoRepeat" +0x10223927009b8add0960359dd90d1449415b7ca9,"Every90" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,"Every90" +0x56be93088141b16ebaa9416122fd1d928da25ecf,"Every90" \ No newline at end of file diff --git a/CLI/data/Transfer/BlacklistTM/delete_blacklist_data.csv b/CLI/data/Transfer/BlacklistTM/delete_blacklist_data.csv new file mode 100644 index 000000000..752cb89c1 --- /dev/null +++ b/CLI/data/Transfer/BlacklistTM/delete_blacklist_data.csv @@ -0,0 +1,4 @@ +"FirstTenDays" +"Every90" +"NoRepeat" +"TwoHours" \ No newline at end of file diff --git a/CLI/data/Transfer/BlacklistTM/modify_blacklist_data.csv b/CLI/data/Transfer/BlacklistTM/modify_blacklist_data.csv new file mode 100644 index 000000000..4b5e45b4d --- /dev/null +++ b/CLI/data/Transfer/BlacklistTM/modify_blacklist_data.csv @@ -0,0 +1,4 @@ +1559412000,1560178800,"FirstTenDays",30 +1560535200,1561032000,"NoRepeat",0 +1566734400,1567252800,"Every90",90 +1567296000,1567303200,"TwoHours",3 diff --git a/CLI/data/Transfer/BlacklistTM/remove_investor_blacklist_data.csv b/CLI/data/Transfer/BlacklistTM/remove_investor_blacklist_data.csv new file mode 100644 index 000000000..cd0fe922c --- /dev/null +++ b/CLI/data/Transfer/BlacklistTM/remove_investor_blacklist_data.csv @@ -0,0 +1,12 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,"FirstTenDays" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,"FirstTenDays" +0xac297053173b02b02a737d47f7b4a718e5b170ef,"FirstTenDays" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"FirstTenDays" +0x10223927009b8add0960359dd90d1449415b7ca9,"FirstTenDays" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"NoRepeat" +0x10223927009b8add0960359dd90d1449415b7ca9,"NoRepeat" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,"NoRepeat" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,"NoRepeat" +0x10223927009b8add0960359dd90d1449415b7ca9,"Every90" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,"Every90" +0x56be93088141b16ebaa9416122fd1d928da25ecf,"Every90" \ No newline at end of file diff --git a/CLI/data/whitelist_data.csv b/CLI/data/Transfer/GTM/whitelist_data.csv similarity index 97% rename from CLI/data/whitelist_data.csv rename to CLI/data/Transfer/GTM/whitelist_data.csv index 3276dec64..236cac436 100644 --- a/CLI/data/whitelist_data.csv +++ b/CLI/data/Transfer/GTM/whitelist_data.csv @@ -7,4 +7,4 @@ 0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,5/5/2018,1/8/2018,10/10/2019,false 0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5/5/2018,1/8/2018,10/10/2019,false 0x56be93088141b16ebaa9416122fd1d928da25ecf,5/5/2018,1/8/2018,10/10/2019,false -0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,5/5/2018,1/8/2018,10/10/2019,true +0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,5/5/2018,1/8/2018,10/10/2019,true \ No newline at end of file diff --git a/CLI/data/Transfer/LockupTM/add_lockup_data.csv b/CLI/data/Transfer/LockupTM/add_lockup_data.csv new file mode 100644 index 000000000..f3d27ab2d --- /dev/null +++ b/CLI/data/Transfer/LockupTM/add_lockup_data.csv @@ -0,0 +1,4 @@ +1000,1560178800,600,1,"TenMinutes" +1000,1560621600,3600,60,"OneHour" +2000,1567252800,7200,3600,"TwoHours" +3000,1567303200,14400,4800,"4Hours" diff --git a/CLI/data/Transfer/LockupTM/add_lockup_investor_data.csv b/CLI/data/Transfer/LockupTM/add_lockup_investor_data.csv new file mode 100644 index 000000000..68c08a7d7 --- /dev/null +++ b/CLI/data/Transfer/LockupTM/add_lockup_investor_data.csv @@ -0,0 +1,12 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,"TenMinutes" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,"TenMinutes" +0xac297053173b02b02a737d47f7b4a718e5b170ef,"TenMinutes" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"TenMinutes" +0x10223927009b8add0960359dd90d1449415b7ca9,"TenMinutes" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"OneHour" +0x10223927009b8add0960359dd90d1449415b7ca9,"OneHour" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,"OneHour" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,"OneHour" +0x10223927009b8add0960359dd90d1449415b7ca9,"OneHour" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,"OneHour" +0x56be93088141b16ebaa9416122fd1d928da25ecf,"OneHour" \ No newline at end of file diff --git a/CLI/data/Transfer/LockupTM/delete_lockup_data.csv b/CLI/data/Transfer/LockupTM/delete_lockup_data.csv new file mode 100644 index 000000000..0d9203ffe --- /dev/null +++ b/CLI/data/Transfer/LockupTM/delete_lockup_data.csv @@ -0,0 +1,2 @@ +"TwoHours" +"4Hours" \ No newline at end of file diff --git a/CLI/data/Transfer/LockupTM/modify_lockup_data.csv b/CLI/data/Transfer/LockupTM/modify_lockup_data.csv new file mode 100644 index 000000000..2b520c4ff --- /dev/null +++ b/CLI/data/Transfer/LockupTM/modify_lockup_data.csv @@ -0,0 +1,4 @@ +1000,1560178800,600,10,"TenMinutes" +1000,1560623200,3600,60,"OneHour" +2000,1567252800,7200,3600,"TwoHours" +6000,1567303200,14400,4800,"4Hours" diff --git a/CLI/data/Transfer/LockupTM/remove_lockup_investor_data.csv b/CLI/data/Transfer/LockupTM/remove_lockup_investor_data.csv new file mode 100644 index 000000000..68c08a7d7 --- /dev/null +++ b/CLI/data/Transfer/LockupTM/remove_lockup_investor_data.csv @@ -0,0 +1,12 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,"TenMinutes" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,"TenMinutes" +0xac297053173b02b02a737d47f7b4a718e5b170ef,"TenMinutes" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"TenMinutes" +0x10223927009b8add0960359dd90d1449415b7ca9,"TenMinutes" +0x49fc0b78238dab644698a90fa351b4c749e123d2,"OneHour" +0x10223927009b8add0960359dd90d1449415b7ca9,"OneHour" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,"OneHour" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,"OneHour" +0x10223927009b8add0960359dd90d1449415b7ca9,"OneHour" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,"OneHour" +0x56be93088141b16ebaa9416122fd1d928da25ecf,"OneHour" \ No newline at end of file diff --git a/CLI/data/Transfer/MATM/add_manualapproval_data.csv b/CLI/data/Transfer/MATM/add_manualapproval_data.csv new file mode 100644 index 000000000..48d5f2a54 --- /dev/null +++ b/CLI/data/Transfer/MATM/add_manualapproval_data.csv @@ -0,0 +1,5 @@ +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x1DDF4FDBB8eaDB5dD6EeC6edB1A91a0926940a33,10,12/12/2019,Lorem ipsum dolor sit amet +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0xB849AC17d881183800300A31f022d3fe4B82D457,20,12/12/2019,Consectetur adipiscing elit +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0xD122e1951cfb337D5CC8bc5aDECC0eb66ffb4B80,25,12/12/2019,Pellentesque ultrices eros +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x1D9127821A244b64852E8Da431bb389B81710985,20,12/12/2019,Non eleifend ante tincidunt eget +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x42dc79375E511Fb7Ee17fF50417141bAE5E5698E,10,12/12/2019,Sed ante arcu \ No newline at end of file diff --git a/CLI/data/Transfer/MATM/modify_manualapproval_data.csv b/CLI/data/Transfer/MATM/modify_manualapproval_data.csv new file mode 100644 index 000000000..7e695f3b2 --- /dev/null +++ b/CLI/data/Transfer/MATM/modify_manualapproval_data.csv @@ -0,0 +1,5 @@ +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x1DDF4FDBB8eaDB5dD6EeC6edB1A91a0926940a33,12/12/2019,1,Lorem ipsum dolor sit amet,0 +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0xB849AC17d881183800300A31f022d3fe4B82D457,12/12/2019,2,Consectetur adipiscing elit,0 +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0xD122e1951cfb337D5CC8bc5aDECC0eb66ffb4B80,12/12/2019,3,Pellentesque ultrices eros,1 +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x1D9127821A244b64852E8Da431bb389B81710985,12/12/2019,4,Non eleifend ante tincidunt eget,2 +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x42dc79375E511Fb7Ee17fF50417141bAE5E5698E,12/12/2019,5,Sed ante arcu,1 \ No newline at end of file diff --git a/CLI/data/Transfer/MATM/revoke_manualapproval_data.csv b/CLI/data/Transfer/MATM/revoke_manualapproval_data.csv new file mode 100644 index 000000000..b398caf02 --- /dev/null +++ b/CLI/data/Transfer/MATM/revoke_manualapproval_data.csv @@ -0,0 +1,2 @@ +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0x1DDF4FDBB8eaDB5dD6EeC6edB1A91a0926940a33 +0x127b4F58A825Dfc6d4058AcdB7bB397d1F0411b5,0xB849AC17d881183800300A31f022d3fe4B82D457 \ No newline at end of file diff --git a/CLI/data/Transfer/PercentageTM/whitelist_data.csv b/CLI/data/Transfer/PercentageTM/whitelist_data.csv new file mode 100644 index 000000000..19d7f163c --- /dev/null +++ b/CLI/data/Transfer/PercentageTM/whitelist_data.csv @@ -0,0 +1,10 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,true +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,true +0xac297053173b02b02a737d47f7b4a718e5b170ef,true +0x49fc0b78238dab644698a90fa351b4c749e123d2,true +0x10223927009b8add0960359dd90d1449415b7ca9,true +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,true +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,true +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,false +0x56be93088141b16ebaa9416122fd1d928da25ecf,false +0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,false \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/add_custom_restriction_data.csv b/CLI/data/Transfer/VRTM/add_custom_restriction_data.csv new file mode 100644 index 000000000..1807e90e6 --- /dev/null +++ b/CLI/data/Transfer/VRTM/add_custom_restriction_data.csv @@ -0,0 +1,8 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,1000,8/1/2019,90,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,8/2/2019,30,10/12/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,8/1/2019,15,10/1/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,8/5/2019,90,10/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,0.25,8/3/2019,30,10/15/2019,"Percentage" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,0.1,8/10/2019,15,10/10/2019,"Percentage" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,1234,8/20/2019,10,10/22/2019,"Fixed" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5678,8/1/2019,2,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv new file mode 100644 index 000000000..1486579b7 --- /dev/null +++ b/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv @@ -0,0 +1,8 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,1000,8/1/2019,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,8/2/2019,10/12/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,8/1/2019,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,8/3/2019,10/1/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,0.25,8/1/2019,10/10/2019,"Percentage" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,0.1,8/1/2019,10/5/2019,"Percentage" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,1234,8/12/2019,10/10/2019,"Fixed" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5678,8/1/2019,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/modify_custom_restriction_data.csv b/CLI/data/Transfer/VRTM/modify_custom_restriction_data.csv new file mode 100644 index 000000000..b69f43361 --- /dev/null +++ b/CLI/data/Transfer/VRTM/modify_custom_restriction_data.csv @@ -0,0 +1,5 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,2000,8/1/2019,90,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,8/2/2019,30,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,8/1/2019,20,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,8/1/2019,90,20/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,25,8/1/2019,30,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv new file mode 100644 index 000000000..908c164e9 --- /dev/null +++ b/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv @@ -0,0 +1,5 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,2000,8/1/2019,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,8/1/2019,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,8/1/2019,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,8/1/2019,20/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,25,8/1/2019,10/10/2019,"Fixed" diff --git a/CLI/data/Transfer/VRTM/remove_custom_restriction_data.csv b/CLI/data/Transfer/VRTM/remove_custom_restriction_data.csv new file mode 100644 index 000000000..d927ee57b --- /dev/null +++ b/CLI/data/Transfer/VRTM/remove_custom_restriction_data.csv @@ -0,0 +1,2 @@ +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98 +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3 diff --git a/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv new file mode 100644 index 000000000..a7fef30c4 --- /dev/null +++ b/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv @@ -0,0 +1,6 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1 +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1 +0xac297053173b02b02a737d47f7b4a718e5b170ef +0x49fc0b78238dab644698a90fa351b4c749e123d2 +0x10223927009b8add0960359dd90d1449415b7ca9 +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399 diff --git a/CLI/data/capped_sto_data.yml b/CLI/data/capped_sto_data.yml deleted file mode 100644 index 6be0c0f66..000000000 --- a/CLI/data/capped_sto_data.yml +++ /dev/null @@ -1,35 +0,0 @@ -securityToken: - # symbol -> security token symbol - symbol: CAP - # name -> security token name - name: CAP Token - # divisible -> true for divisible token, false for non-divisible - divisible: true - -initialMint: - # multimint -> whitelist and mint tokens to a list of affiliates (true) or only to a single wallet (false) - multimint: true - # singleMint -> Only valid if multimint = false - singleMint: - # wallet -> address that will be whitelisted - wallet: '' - # allowedToBuy -> true to allow wallet to buy tokens from the STO - allowedToBuy: true - # tokenAmount -> tokens minted to wallet - tokenAmount: 500000 - -sto: - # type -> 0 for cappedSTO, 1 for USDTieredSTO, 2 for select STO later - type: 0 - # cap -> - cap: 500000 - # startTime -> start time for the STO (Unix Epoch time) - startTime: '' - # endTime -> end time for the STO (Unix Epoch time) - endTime: '' - # wallet -> address that will receive the funds from the STO - wallet: '' - # raiseType -> 0 for ETH, 1 for POLY - raiseType: 0 - # rate -> rate for the STO - rate: 1000 \ No newline at end of file diff --git a/CLI/data/usd_tiered_sto_data.yml b/CLI/data/usd_tiered_sto_data.yml deleted file mode 100644 index 305167bd7..000000000 --- a/CLI/data/usd_tiered_sto_data.yml +++ /dev/null @@ -1,57 +0,0 @@ -securityToken: - # symbol -> security token symbol - symbol: TIE - # name -> security token name - name: TIEred Token - # divisible -> true for divisible token, false for non-divisible - divisible: true - -initialMint: - # multimint -> whitelist and mint tokens to a list of affiliates (true) or only to a single wallet (false) - multimint: true - # singleMint -> Only valid if multimint = false - singleMint: - # wallet -> address that will be whitelisted - wallet: '' - # allowedToBuy -> true to allow wallet to buy tokens from the STO - allowedToBuy: true - # tokenAmount -> tokens minted to wallet - tokenAmount: 500000 - -sto: - # type -> 0 for cappedSTO, 1 for USDTieredSTO, 2 for select STO later - type: 1 - - # fundings - # fundigType -> P for POLY, E for ETH, B for both - fundingType: B - - # addresses - # wallet -> address that will receive the funds from the STO - wallet: '' - # reserveWallet -> address that will receive remaining tokens in the case the cap is not met - reserveWallet: '' - - # tiers - # numberOfTiers -> total of tiers for the STO - numberOfTiers: 3 - # tokensPerTiers -> total tokens for each tier - tokensPerTiers: !!seq [ 190000000, 100000000, 200000000 ] - # ratePerTiers -> rate for each tier (in USD) - ratePerTiers: !!seq [ 0.05, 0.10, 0.15 ] - # discountedTokensPerTiers -> tokens for discounted rate for POLY investments (relative to tokensPerTier). 0 for no discounted tokens - discountedTokensPerTiers: !!seq [ 0, 0, 100000000 ] - # discountedRatePerTiers -> discounted rate for POLY investments for each tier. 0 for no discounted rate - discountedRatePerTiers: !!seq [ 0, 0, 0.075 ] - - # limits - # minimumInvestmentUSD -> minimum investment (in USD) - minimumInvestmentUSD: 5 - # nonAccreditedLimitUSD -> limit for non accredited insvestors (in USD) - nonAccreditedLimitUSD: 10000 - - #times - # startTime -> start time for the STO (Unix Epoch time) - startTime: '' - # endTime -> end time for the STO (Unix Epoch time) - endTime: '' \ No newline at end of file diff --git a/CLI/package.json b/CLI/package.json index 9eee76624..cc2773b74 100644 --- a/CLI/package.json +++ b/CLI/package.json @@ -4,17 +4,20 @@ "description": "CLI for Polymath-core", "main": "polymath-cli.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "stable_coin": "scripts/stable_coin.sh" }, "author": "Polymath Inc", "license": "MIT", "dependencies": { "chalk": "^2.4.1", "commander": "^2.16.0", + "csv-parse": "^4.0.1", + "ethers": "^4.0.7", "moment": "^2.22.2", "readline-sync": "^1.4.9", "request": "^2.88.0", "request-promise": "^4.2.2", - "web3": "1.0.0-beta.34" + "table": "^5.1.1", + "web3": "1.0.0-beta.35" } } diff --git a/CLI/polymath-cli.js b/CLI/polymath-cli.js index 49f578df0..c46425716 100644 --- a/CLI/polymath-cli.js +++ b/CLI/polymath-cli.js @@ -1,156 +1,184 @@ #!/usr/bin/env node -const shell = require('shelljs'); -var faucet = require('./commands/faucet'); -var investor_portal = require('./commands/investor_portal'); -var module_manager = require('./commands/module_manager'); -var st20generator = require('./commands/ST20Generator'); -var transfer = require('./commands/transfer'); -var transfer_ownership = require('./commands/transfer_ownership'); -var dividends_manager = require('./commands/dividends_manager'); -var transfer_manager = require('./commands/transfer_manager'); -var contract_manager = require('./commands/contract_manager'); -var strMigrator = require('./commands/strMigrator'); -var permission_manager = require('./commands/permission_manager'); -var program = require('commander'); +const faucet = require('./commands/faucet'); +const investor_portal = require('./commands/investor_portal'); +const token_manager = require('./commands/token_manager'); +const st20generator = require('./commands/ST20Generator'); +const sto_manager = require('./commands/sto_manager'); +const transfer = require('./commands/transfer'); +const transfer_ownership = require('./commands/transfer_ownership'); +const dividends_manager = require('./commands/dividends_manager'); +const transfer_manager = require('./commands/transfer_manager'); +const contract_manager = require('./commands/contract_manager'); +const strMigrator = require('./commands/strMigrator'); +const permission_manager = require('./commands/permission_manager'); +const time = require('./commands/helpers/time'); +const gbl = require('./commands/common/global'); +const program = require('commander'); +const moment = require('moment'); const yaml = require('js-yaml'); const fs = require('fs'); program - .version('0.0.1') + .version('1.0.1') .description('CLI for Polymath-core') - .option('-r, --remote-node ', 'Use Infura to connect to a remote node on selected network'); + .option('-r, --remote-node ', 'Connect to a remote node'); program .command('st20generator') .alias('st') - .option('-c, --config ', "Uses configuration file to configure ST and STO") + .option('-t, --ticker ', 'Unique token ticker') + .option('-o, --transferOwnership ', `Transfers the ticker's ownership to newOwner account. If newOwner is 'false', this step is skipped`) + .option('-n, --tokenName ', 'Token name') + .option('-d, --details
', 'Off-chain details of the token') + .option('-D, --divisible
', 'If token is divisible or not [true]', /^(true|false)/) .description('Wizard-like script that will guide technical users in the creation and deployment of an ST-20 token') - .action(async function(cmd) { - let tokenConfig; - let mintingConfig; - let stoCofig; - if (cmd.config) { - let config = yaml.safeLoad(fs.readFileSync(`${__dirname}/data/${cmd.config}`, 'utf8')); - tokenConfig = config.securityToken; - mintingConfig = config.initialMint; - stoCofig = config.sto; + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + await st20generator.executeApp(cmd.ticker, cmd.transferOwnership, cmd.tokenName, cmd.details, cmd.divisible); + }); + +program + .command('sto_manager') + .alias('sto') + .option('-t, --securityToken ', 'Selects a ST to manage modules') + .option('-l, --launch ', 'Uses configuration file to configure and launch a STO') + .description('Wizard-like script that will guide technical users in the creation of an STO') + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + if (cmd.launch) { + let config = yaml.safeLoad(fs.readFileSync(`${__dirname}/${cmd.launch}`, 'utf8')); + await sto_manager.addSTOModule(cmd.securityToken, config) + } else { + await sto_manager.executeApp(cmd.securityToken); } - await st20generator.executeApp(tokenConfig, mintingConfig, stoCofig, program.remoteNode); }); program .command('faucet [beneficiary] [amount]') .alias('f') .description('Poly faucet for local private netwtorks') - .action(async function(beneficiary, amount) { - await faucet.executeApp(beneficiary, amount, program.remoteNode); + .action(async function (beneficiary, amount) { + await gbl.initialize(program.remoteNode); + await faucet.executeApp(beneficiary, amount); }); program .command('investor_portal [investor] [privateKey] [symbol] [currency] [amount]') .alias('i') .description('Participate in any STO you have been whitelisted for') - .action(async function(investor, privateKey, symbol, currency, amount) { - await investor_portal.executeApp(investor, privateKey, symbol, currency, amount, program.remoteNode); - }); - -program - .command('module_manager') - .alias('mm') - .description('View modules attached to a token and their status') - .action(async function() { - await module_manager.executeApp(program.remoteNode); + .action(async function (investor, privateKey, symbol, currency, amount) { + await gbl.initialize(program.remoteNode); + await investor_portal.executeApp(investor, privateKey, symbol, currency, amount); }); program - .command('multi_mint [batchSize]') - .alias('mi') - .description('Distribute tokens to previously whitelisted investors') - .action(async function(tokenSymbol, batchSize) { - shell.exec(`${__dirname}/commands/scripts/script.sh Multimint ${tokenSymbol} ${batchSize} ${program.remoteNode}`);; + .command('token_manager') + .alias('stm') + .option('-t, --securityToken ', 'Selects a ST to manage') + .option('-m, --multiMint ', 'Distribute tokens to previously whitelisted investors') + .option('-b, --batchSize ', 'Max number of records per transaction') + .description('Manage your Security Tokens, mint tokens, add modules and change config') + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + if (cmd.multiMint) { + let batchSize = cmd.batchSize ? cmd.batchSize : gbl.constants.DEFAULT_BATCH_SIZE; + await token_manager.multiMint(cmd.securityToken, cmd.multiMint, batchSize); + } else { + await token_manager.executeApp(cmd.securityToken); + } }); program .command('transfer ') .alias('t') .description('Transfer ST tokens to another account') - .action(async function(tokenSymbol, transferTo, transferAmount) { - await transfer.executeApp(tokenSymbol, transferTo, transferAmount, program.remoteNode); + .action(async function (tokenSymbol, transferTo, transferAmount) { + await gbl.initialize(program.remoteNode); + await transfer.executeApp(tokenSymbol, transferTo, transferAmount); }); program .command('transfer_ownership ') .alias('to') .description('Transfer Ownership of an own contract to another account') - .action(async function(contractAddress, transferTo) { - await transfer_ownership.executeApp(contractAddress, transferTo, program.remoteNode); - }); - -program - .command('whitelist [batchSize]') - .alias('w') - .description('Mass-update a whitelist of allowed/known investors') - .action(async function(tokenSymbol, batchSize) { - shell.exec(`${__dirname}/commands/scripts/script.sh Whitelist ${tokenSymbol} ${batchSize} ${program.remoteNode}`); + .action(async function (contractAddress, transferTo) { + await gbl.initialize(program.remoteNode); + await transfer_ownership.executeApp(contractAddress, transferTo); }); program .command('dividends_manager [dividendsType]') .alias('dm') .description('Runs dividends_manager') - .action(async function(dividendsType) { - await dividends_manager.executeApp(dividendsType, program.remoteNode); + .action(async function (dividendsType) { + await gbl.initialize(program.remoteNode); + await dividends_manager.executeApp(dividendsType); }); program .command('transfer_manager') .alias('tm') + .option('-t, --securityToken ', 'Selects a ST to manage transfer modules') + .option('-w, --whitelist ', 'Whitelists addresses according to a csv file') + .option('-b, --batchSize ', 'Max number of records per transaction') .description('Runs transfer_manager') - .action(async function() { - await transfer_manager.executeApp(program.remoteNode); + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + if (cmd.whitelist) { + let batchSize = cmd.batchSize ? cmd.batchSize : gbl.constants.DEFAULT_BATCH_SIZE; + await transfer_manager.modifyWhitelistInBatch(cmd.securityToken, cmd.whitelist, batchSize); + } else { + await transfer_manager.executeApp(cmd.securityToken); + } }); program .command('contract_manager') .alias('cm') .description('Runs contract_manager') - .action(async function() { - await contract_manager.executeApp(program.remoteNode); - }); - -program - .command('accredit [batchSize]') - .alias('a') - .description('Runs accredit') - .action(async function(tokenSymbol, batchSize) { - 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}`);; + .action(async function () { + await gbl.initialize(program.remoteNode); + await contract_manager.executeApp(); }); program .command('strMigrator [toStrAddress] [fromTrAddress] [fromStrAddress]') + .option('-tick, --singleTicker ', 'It only reads and migrates the ticker and token for given token symbol') + .option('-tok, --tokenAddress ', 'Migrated security token address. It skips all steps until modifySecurityToken') + .option('-ot, --onlyTickers', 'Only migrate tickers without a launched token') .alias('str') .description('Runs STR Migrator') - .action(async function(toStrAddress, fromTrAddress, fromStrAddress) { - await strMigrator.executeApp(toStrAddress, fromTrAddress, fromStrAddress, program.remoteNode); + .action(async function (toStrAddress, fromTrAddress, fromStrAddress, cmd) { + await strMigrator.executeApp(toStrAddress, fromTrAddress, fromStrAddress, cmd.singleTicker, cmd.tokenAddress, cmd.onlyTickers, program.remoteNode); }); program .command('permission_manager') .alias('pm') .description('Runs permission_manager') - .action(async function() { - await permission_manager.executeApp(program.remoteNode); + .action(async function () { + await gbl.initialize(program.remoteNode); + await permission_manager.executeApp(); }); + program + .command('time_travel') + .alias('tt') + .option('-p, --period ', 'Period of time in seconds to increase') + .option('-d, --toDate ', 'Human readable date ("MM/DD/YY [HH:mm:ss]") to travel to') + .option('-e, --toEpochTime ', 'Unix Epoch time to travel to') + .description('Increases time on EVM according to given value.') + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + if (cmd.period) { + await time.increaseTimeByDuration(parseInt(cmd.period)); + } else if (cmd.toDate) { + await time.increaseTimeToDate(cmd.toDate); + } else if (cmd.toEpochTime) { + await time.increaseTimeToEpochDate(cmd.toEpochTime); + } + }); program.parse(process.argv); if (typeof program.commands.length == 0) { diff --git a/CLI/scripts/stable_coin.sh b/CLI/scripts/stable_coin.sh new file mode 100755 index 000000000..98415d7cc --- /dev/null +++ b/CLI/scripts/stable_coin.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -o errexit + +# Token symbol read from args. +TOKEN_SYMBOL=$1 + +# Paths +CWD="$(pwd)" +WHITELIST='/data/Transfer/GTM/whitelist_data.csv' +MULTIMINT='/data/ST/multi_mint_data.csv' + +# Scripts + +node polymath-cli st -t $TOKEN_SYMBOL -o false -n $TOKEN_SYMBOL -d '' -D true +node polymath-cli tm -t $TOKEN_SYMBOL -w $CWD$WHITELIST +node polymath-cli stm -t $TOKEN_SYMBOL -m $CWD$MULTIMINT diff --git a/CLI/yarn.lock b/CLI/yarn.lock index 9a48fd1a5..82d60b57f 100644 --- a/CLI/yarn.lock +++ b/CLI/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@types/node@^10.3.2": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.0.tgz#ea6dcbddbc5b584c83f06c60e82736d8fbb0c235" + integrity sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ== + accepts@~1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" @@ -20,7 +25,22 @@ ajv@^5.3.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ansi-styles@^3.2.1: +ajv@^6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.1.tgz#6360f5ed0d80f232cc2b294c362d5dc2e538dd61" + integrity sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -58,6 +78,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" @@ -422,6 +447,11 @@ crypto-browserify@3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" +csv-parse@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.0.1.tgz#4ad438352cbf12d5317d0fb9d588e53473293851" + integrity sha512-ehkwejEj05wwO7Q9JD+YSI6dNMIauHIroNU1RALrmRrqPoZIwRnfBtgq5GkU6i2RxZOJqjo3dtI1NrVSXvaimA== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -618,6 +648,22 @@ eth-lib@0.2.7: elliptic "^6.4.0" xhr-request-promise "^0.1.2" +ethers@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.7.tgz#8c653618077a1fc60c5c1b2575da03561f0039f4" + integrity sha512-HGR2FcyeiRjg6qZUjtK5gkTp7puB+KPhBDa0s2zG4IMVcfU33rkhl9yBIqZBzpD5J0fE33gQ57+epZh90nBaeA== + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -695,6 +741,11 @@ fast-deep-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -988,6 +1039,11 @@ is-callable@^1.1.3: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-function@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" @@ -1046,6 +1102,11 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" +js-sha3@0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" @@ -1061,6 +1122,11 @@ json-schema-traverse@^0.3.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -1096,7 +1162,7 @@ keccakjs@^0.2.1: browserify-sha3 "^0.0.1" sha3 "^1.1.0" -lodash@^4.13.1: +lodash@^4.13.1, lodash@^4.17.11: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -1437,6 +1503,11 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + qs@6.5.2, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -1572,6 +1643,11 @@ safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + scrypt.js@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada" @@ -1680,6 +1756,15 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +slice-ansi@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.0.0.tgz#5373bdb8559b45676e8541c66916cdd6251612e7" + integrity sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + sshpk@^1.7.0: version "1.15.2" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.2.tgz#c946d6bd9b1a39d0e8635763f5242d6ed6dcb629" @@ -1715,6 +1800,14 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -1722,6 +1815,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" @@ -1762,6 +1862,16 @@ swarm-js@0.1.37: tar.gz "^1.0.5" xhr-request-promise "^0.1.2" +table@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/table/-/table-5.1.1.tgz#92030192f1b7b51b6eeab23ed416862e47b70837" + integrity sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw== + dependencies: + ajv "^6.6.1" + lodash "^4.17.11" + slice-ansi "2.0.0" + string-width "^2.1.1" + tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" @@ -1887,6 +1997,13 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" @@ -1943,87 +2060,87 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -web3-bzz@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.34.tgz#068d37777ab65e5c60f8ec8b9a50cfe45277929c" - integrity sha1-Bo03d3q2Xlxg+OyLmlDP5FJ3kpw= +web3-bzz@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.35.tgz#9d5e1362b3db2afd77d65619b7cd46dd5845c192" + integrity sha512-BhAU0qhlr8zltm4gs/+P1gki2VkxHJaM2Rrh4DGesDW0lzwufRoNvWFlwx1bKHoFPWNbSmm9PRkHOYOINL/Tgw== dependencies: got "7.1.0" swarm-js "0.1.37" underscore "1.8.3" -web3-core-helpers@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" - integrity sha1-sWjaANPhnhVrwVriAyA91N/uLQM= +web3-core-helpers@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.35.tgz#d681d218a0c6e3283ee1f99a078ab9d3eef037f1" + integrity sha512-APOu3sEsamyqWt//8o4yq9KF25/uqGm+pQShson/sC4gKzmfJB07fLo2ond0X30E8fIqAPeVCotPXQxGciGUmA== dependencies: underscore "1.8.3" - web3-eth-iban "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-eth-iban "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-core-method@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.34.tgz#ec163c8a2c490fa02a7ec15559fa7307fc7cc6dd" - integrity sha1-7BY8iixJD6AqfsFVWfpzB/x8xt0= +web3-core-method@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.35.tgz#fc10e2d546cf4886038e6130bd5726b0952a4e5f" + integrity sha512-jidImCide8q0GpfsO4L73qoHrbkeWgwU3uOH5DKtJtv0ccmG086knNMRgryb/o9ZgetDWLmDEsJnHjBSoIwcbA== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-core-promievent "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-core-promievent@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.34.tgz#a4f4fa6784bb293e82c60960ae5b56a94cd03edc" - integrity sha1-pPT6Z4S7KT6CxglgrltWqUzQPtw= +web3-core-promievent@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.35.tgz#4f1b24737520fa423fee3afee110fbe82bcb8691" + integrity sha512-GvqXqKq07OmHuVi5uNRg6k79a1/CI0ViCC+EtNv4CORHtDRmYEt5Bvdv6z6FJEiaaQkD0lKbFwNhLxutx7HItw== dependencies: any-promise "1.3.0" eventemitter3 "1.1.1" -web3-core-requestmanager@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.34.tgz#01f8f6cf2ae6b6f0b70c38bae1ef741b5bab215c" - integrity sha1-Afj2zyrmtvC3DDi64e90G1urIVw= +web3-core-requestmanager@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.35.tgz#2b77cbf6303720ad68899b39fa7f584dc03dbc8f" + integrity sha512-S+zW2h17ZZQU9oe3yaCJE0E7aJS4C3Kf4kGPDv+nXjW0gKhQQhgVhw1Doq/aYQGqNSWJp7f1VHkz5gQWwg6RRg== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-providers-http "1.0.0-beta.34" - web3-providers-ipc "1.0.0-beta.34" - web3-providers-ws "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-providers-http "1.0.0-beta.35" + web3-providers-ipc "1.0.0-beta.35" + web3-providers-ws "1.0.0-beta.35" -web3-core-subscriptions@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.34.tgz#9fed144033f221c3cf21060302ffdaf5ef2de2de" - integrity sha1-n+0UQDPyIcPPIQYDAv/a9e8t4t4= +web3-core-subscriptions@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.35.tgz#c1b76a2ad3c6e80f5d40b8ba560f01e0f4628758" + integrity sha512-gXzLrWvcGkGiWq1y33Z4Y80XI8XMrwowiQJkrPSjQ81K5PBKquOGwcMffLaKcwdmEy/NpsOXDeFo3eLE1Ghvvw== dependencies: eventemitter3 "1.1.1" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" -web3-core@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.34.tgz#121be8555e9fb00d2c5d05ddd3381d0c9e46987e" - integrity sha1-EhvoVV6fsA0sXQXd0zgdDJ5GmH4= +web3-core@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.35.tgz#0c44d3c50d23219b0b1531d145607a9bc7cd4b4f" + integrity sha512-ayGavbgVk4KL9Y88Uv411fBJ0SVgVfKhKEBweKYzmP0zOqneMzWt6YsyD1n6kRvjAbqA0AfUPEOKyMNjcx2tjw== dependencies: - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-requestmanager "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-requestmanager "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-abi@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" - integrity sha1-A0Uz46ovfln/MXk+rqaFwO1a9no= +web3-eth-abi@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.35.tgz#2eb9c1c7c7233db04010defcb192293e0db250e6" + integrity sha512-KUDC+EtFFYG8z01ZleKrASdjj327/rtWHzEt6RWsEj7bBa0bGp9nEh+nqdZx/Sdgz1O8tnfFzJlrRcXpfr1vGg== dependencies: bn.js "4.11.6" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-accounts@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz#e09142eeecc797ac3459b75e9b23946d3695f333" - integrity sha1-4JFC7uzHl6w0WbdemyOUbTaV8zM= +web3-eth-accounts@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.35.tgz#7d0e5a69f510dc93874471599eb7abfa9ddf3e63" + integrity sha512-duIgRsfht/0kAW/eQ0X9lKtVIykbETrnM2H7EnvplCzPHtQLodpib4o9JXfh9n6ZDgdDC7cuJoiVB9QJg089ew== dependencies: any-promise "1.3.0" crypto-browserify "3.12.0" @@ -2031,111 +2148,111 @@ web3-eth-accounts@1.0.0-beta.34: scrypt.js "0.2.0" underscore "1.8.3" uuid "2.0.1" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-contract@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz#9dbb38fae7643a808427a20180470ec7415c91e6" - integrity sha1-nbs4+udkOoCEJ6IBgEcOx0FckeY= +web3-eth-contract@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.35.tgz#5276242d8a3358d9f1ce92b71575c74f9015935c" + integrity sha512-foPohOg5O1UCGKGZOIs+kQK5IZdV2QQ7pAWwNxH8WHplUA+fre1MurXNpoxknUmH6mYplFhXjqgYq2MsrBpHrA== dependencies: underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-eth-iban@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" - integrity sha1-mvRYYFhnzPdOqXmq8yazi6alugw= + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-promievent "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-eth-abi "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-eth-iban@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.35.tgz#5aa10327a9abb26bcfc4ba79d7bad18a002b332c" + integrity sha512-H5wkcNcAIc+h/WoDIKv7ZYmrM2Xqu3O7jBQl1IWo73EDVQji+AoB2i3J8tuwI1yZRInRwrfpI3Zuwuf54hXHmQ== dependencies: bn.js "4.11.6" - web3-utils "1.0.0-beta.34" + web3-utils "1.0.0-beta.35" -web3-eth-personal@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.34.tgz#9afba167342ebde5420bcd5895c3f6c34388f205" - integrity sha1-mvuhZzQuveVCC81YlcP2w0OI8gU= +web3-eth-personal@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.35.tgz#ecac95b7a53d04a567447062d5cae5f49879e89f" + integrity sha512-AcM9nnlxu7ZRRxPvkrFB9eLxMM4A2cPfj2aCg21Wb2EpMnhR+b/O1cT33k7ApRowoMpM+T9M8vx2oPNwXfaCOQ== dependencies: - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.34.tgz#74086000850c6fe6f535ef49837d6d4bb6113268" - integrity sha1-dAhgAIUMb+b1Ne9Jg31tS7YRMmg= +web3-eth@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.35.tgz#c52c804afb95e6624b6f5e72a9af90fbf5005b68" + integrity sha512-04mcb2nGPXThawuuYICPOxv0xOHofvQKsjZeIq+89nyOC8DQMGTAErDkGyMHQYtjpth5XDhic0wuEsA80AmFZA== dependencies: underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-eth-accounts "1.0.0-beta.34" - web3-eth-contract "1.0.0-beta.34" - web3-eth-iban "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-net@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.34.tgz#427cea2f431881449c8e38d523290f173f9ff63d" - integrity sha1-QnzqL0MYgUScjjjVIykPFz+f9j0= - dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-providers-http@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz#e561b52bbb43766282007d40285bfe3550c27e7a" - integrity sha1-5WG1K7tDdmKCAH1AKFv+NVDCfno= - dependencies: - web3-core-helpers "1.0.0-beta.34" - xhr2 "0.1.4" - -web3-providers-ipc@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.34.tgz#a1b77f1a306d73649a9c039052e40cb71328d00a" - integrity sha1-obd/GjBtc2SanAOQUuQMtxMo0Ao= + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-eth-abi "1.0.0-beta.35" + web3-eth-accounts "1.0.0-beta.35" + web3-eth-contract "1.0.0-beta.35" + web3-eth-iban "1.0.0-beta.35" + web3-eth-personal "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-net@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.35.tgz#5c6688e0dea71fcd910ee9dc5437b94b7f6b3354" + integrity sha512-bbwaQ/KohGjIJ6HAKbZ6KrklCAaG6/B7hIbAbVLSFLxF+Yz9lmAgQYaDInpidpC/NLb3WOmcbRF+P77J4qMVIA== + dependencies: + web3-core "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-providers-http@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.35.tgz#92059d9d6de6e9f82f4fae30b743efd841afc1e1" + integrity sha512-DcIMFq52Fb08UpWyZ3ZlES6NsNqJnco4hBS/Ej6eOcASfuUayPI+GLkYVZsnF3cBYqlH+DOKuArcKSuIxK7jIA== + dependencies: + web3-core-helpers "1.0.0-beta.35" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.35.tgz#031afeb10fade2ebb0ef2fb82f5e58c04be842d9" + integrity sha512-iB0FG0HcpUnayfa8pn4guqEQ4Y1nrroi/jffdtQgFkrNt0sD3fMSwwC0AbmECqj3tDLl0e1slBR0RENll+ZF0g== dependencies: oboe "2.1.3" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" -web3-providers-ws@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.34.tgz#7de70f1b83f2de36476772156becfef6e3516eb3" - integrity sha1-fecPG4Py3jZHZ3IVa+z+9uNRbrM= +web3-providers-ws@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.35.tgz#5d38603fd450243a26aae0ff7f680644e77fa240" + integrity sha512-Cx64NgDStynKaUGDIIOfaCd0fZusL8h5avKTkdTjUu2aHhFJhZoVBGVLhoDtUaqZGWIZGcBJOoVf2JkGUOjDRQ== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" -web3-shh@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.34.tgz#975061d71eaec42ccee576f7bd8f70f03844afe0" - integrity sha1-l1Bh1x6uxCzO5Xb3vY9w8DhEr+A= +web3-shh@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.35.tgz#7e4a585f8beee0c1927390937c6537748a5d1a58" + integrity sha512-8qSonk/x0xabERS9Sr6AIADN/Ty+5KwARkkGIfSYHKqFpdMDz+76F7cUCxtoCZoS8K04xgZlDKYe0TJXLYA0Fw== dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-net "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-net "1.0.0-beta.35" -web3-utils@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" - integrity sha1-lBH8OarvOcpOBhafdiKX2f8CCXA= +web3-utils@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.35.tgz#ced9e1df47c65581c441c5f2af76b05a37a273d7" + integrity sha512-Dq6f0SOKj3BDFRgOPnE6ALbzBDCKVIW8mKWVf7tGVhTDHf+wQaWwQSC3aArFSqdExB75BPBPyDpuMTNszhljpA== dependencies: bn.js "4.11.6" eth-lib "0.1.27" @@ -2145,18 +2262,18 @@ web3-utils@1.0.0-beta.34: underscore "1.8.3" utf8 "2.1.1" -web3@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" - integrity sha1-NH5WG3hAmMtVYzFfSQR5odkfKrE= - dependencies: - web3-bzz "1.0.0-beta.34" - web3-core "1.0.0-beta.34" - web3-eth "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-shh "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" +web3@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.35.tgz#6475095bd451a96e50a32b997ddee82279292f11" + integrity sha512-xwDmUhvTcHQvvNnOPcPZZgCxKUsI2e+GbHy7JkTK3/Rmnutazy8x7fsAXT9myw7V1qpi3GgLoZ3fkglSUbg1Mg== + dependencies: + web3-bzz "1.0.0-beta.35" + web3-core "1.0.0-beta.35" + web3-eth "1.0.0-beta.35" + web3-eth-personal "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-shh "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" "websocket@git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible": version "1.0.26" diff --git a/README.md b/README.md index 28a86946a..51610e9d4 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,22 @@ ![Polymath logo](Polymath.png) # Polymath Core - The Polymath Core smart contracts provide a system for launching regulatory-compliant securities tokens on a decentralized blockchain. This particular repository is the implementation of a system that allows for the creation of ST-20-compatible tokens. This system has a modular design that promotes a variety of pluggable components for various types of issuances, legal requirements, and offering processes. +## Introduction to Security Tokens + +### What is a Security token? +A Security Token shares many of the characteristics of both fungible (erc20) and non-fungible tokens (erc721). Security tokens are designed to represent complete or fractional ownership interests in assets and/or entities. While utility tokens have no limitations on who can send or receive the token, security tokens are subject to many restrictions based on identity, jurisdiction and asset category. +### Security Tokens Vs. Utility Tokens? +The concept of utility tokens is fairly well understood in the blockchain space today. Utility tokens represent access to a network, and your token purchase represents the ability to buy goods or services from that network- Think of when you purchase a arcade token to allow you to play an arcade game machine. Utility tokens give you that same type of access to a product or service. On the other hand, security tokens represent complete or fractional ownership in an asset (such as shares in a company, a real-estate asset, artwork, etc). Such as having a stake in a company, real estate, or intellectual property can all be represented by security tokens. Security tokens offer the benefit of bringing significant transparency over traditional paper shares through the use of the blockchain and its associated public ledger. Security token structure, distribution, or changes that could affect investors are now accessible to all via the blockchain. # ST-20 Interface Overview ## Description An ST-20 token is an Ethereum-based token implemented on top of the ERC-20 protocol that adds the ability for tokens to control transfers based on specific rules. ST-20 tokens rely on Transfer Managers to determine the ruleset the token should apply in order to allow or deny a transfer, be it between the issuer and investors, in a peer to peer exchange, or a transaction with an exchange. ## How it works -ST-20 tokens must implement a `verifyTransfer` method which will be called when attempting to execute a `transfer` or `transferFrom` method. The `verifyTransfer` method will determine whether that transaction can be completed or not. The implementation of `verifyTransfer` can take many forms, but the default approach is a whitelist controlled by the `GeneralTransferManager`. - -### The ST-20 Interface - +ST-20 tokens must implement a verifyTransfer method which will be called when attempting to execute a transfer or transferFrom method. The verifyTransfer method will determine whether that transaction can be completed or not. The implementation of verifyTransfer can take many forms, but the default approach is a whitelist controlled by the GeneralTransferManager. +## The ST-20 Interface ``` contract IST20 { @@ -32,210 +35,144 @@ contract IST20 { function mint(address _investor, uint256 _amount) public returns (bool success); } ``` - - # The Polymath Core Architecture -The diagram below depicts a high-level view of the various modules, registries, and contracts implemented in Polymath Core: +The diagram below depicts a high-level view of the various modules, registries, and contracts implemented within Polymath Core 2.0.0: -![Polymath Core architecture](https://github.com/PolymathNetwork/polymath-core/blob/master/docs/images/PolymathCore.png) +![Polymath Core architecture](https://github.com/PolymathNetwork/polymath-core/blob/master/docs/images/Core%20Architecture%202.0.0%20%20Diagram.png) ## Components -### SecurityToken -`SecurityToken` is an implementation of the ST-20 protocol that allows the addition of different modules to control its behavior. Different modules can be attached to `SecurityToken`: -- [TransferManager modules](contracts/modules/TransferManager): These control the logic behind transfers and how they are allowed or disallowed. -By default, the ST (Security Token) gets a `GeneralTransferManager` module attached in order to determine if transfers should be allowed based on a whitelist approach. The `GeneralTransferManager` behaves differently depending who is trying to transfer the tokens. -a) In an offering setting (investors buying tokens from the issuer) the investor's address should be present on an internal whitelist managed by the issuer within the `GeneralTransferManager`. -b) In a peer to peer transfer, restrictions apply based on real-life lockups that are enforced on-chain. For example, if a particular holder has a 1-year sale restriction for the token, the transaction will fail until that year passes. -- [Security Token Offering (STO) modules](contracts/modules/STO): A `SecurityToken` can be attached to one (and only one) STO module that will dictate the logic of how those tokens will be sold/distributed. An STO is the equivalent to the Crowdsale contracts often found present in traditional ICOs. -- [Permission Manager modules](contracts/modules/PermissionManager): These modules manage permissions on different aspects of the issuance process. The issuer can use this module to manage permissions and designate administrators on his token. For example, the issuer might give a KYC firm permissions to add investors to the whitelist. -- [Checkpoint Modules](contracts/modules/Checkpoint): These modules allow the issuer to define checkpoints at which token balances and the total supply of a token can be consistently queried. This functionality is useful for dividend payment mechanisms and on-chain governance, both of which need to be able to determine token balances consistently as of a specified point in time. +### Polymath Registries -### TickerRegistry -The ticker registry manages the sign up process to the Polymath platform. Issuers can use this contract to register a token symbol (which are unique within the Polymath network). Token Symbol registrations have an expiration period (7 days by default) in which the issuer has to complete the process of deploying their SecurityToken. If they do not complete the process in time, their ticker symbol will be made available for someone else to register. +**Security Token Registry (STR)** - This registry tells us which tokens and tickers have been registered to it. This allows us to prevent people from reserving the same ticker as another issuer as well checking for inputs such as making sure it is a maximum of 10 characters and what the expiry date is on the respective ticker. Right now, if you reserve a ticker it last for 60 days. After it expires someone else can go ahead and reserve it or they you can re-register it. -### SecurityTokenRegistry -The security token registry keeps track of deployed STs on the Polymath Platform and uses the TickerRegistry to allow only registered symbols to be deployed. +With the **2.0.0 Core Release**, when you deploy a token you do it through the ST registry and it keeps a record of which tokens have been registered within it. -### ModuleRegistry -Modules allow custom add-in functionality in the issuance process and beyond. The module registry keeps track of modules added by Polymath or any other users. Modules can only be attached to STs if Polymath has previously verified them. If not, the only user able to utilize a module is its owner, and they should be using it "at their own risk". +**The Module Registry** - This registry keeps a record of all the different module factories. +**The Features Registry** - A registry of features that we may enable in the future but right now only Polymath has control of the features. Later, Polymath can easily turn access on and off. -# Stepping through an issuance with the CLI Tool -First, assure that you have [setup Polymath Core properly](#setup). +To be clear, each module has its own factory which is in charge of deploying an instance of that module for the issuers token. -The Polymath CLI (Command Line Interface) commands are operated from a *nix command prompt (unix or mac). +There are General factories which every token uses (if wanted). It works by sending the token to the factory where it asks for an instance of that said module and the token will add an instance of that module to itself. This allows for each token to have its unique modules associated with it. All of this is created through the factories and the module registry keeps a records of all the different modules factories that are registered. -It can be used in three differents ways: +As of now, Polymath is the only one that can add or register a module factory to the module registry. Polymath submits the modules to the registry, however, we are exploring different approaches to open up development to other parties such as potentially working with external developers to provide services to issuers through modules. -1. Connected to a full ethereum node: -You have to save your Parity account password to `$HOME/password.file` and run Parity with the following command to get started (make sure the node is fully synced before using the CLI tool): -```bash -parity --chain ropsten --rpcapi "eth,net,web3,personal,parity" --unlock YOUR_ETH_ACCOUNT --password $HOME/password.file -``` -2. Connected to a remote ethereum node: -You can access Ethereum via the Infura load-balanced nodes. You have to save your private key to `./privKey` file and run CLI command adding `--remote-node ` option. -```bash -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 one of the accounts generated by ganache into `./privKeyLocal`. +**Polymath has 3 main registries** +1. Security Token Registry +2. Features Registry +3. Module Registry +The Polymath Registry holds the addresses of the 3 registries above. -## Poly Faucet +As of the **2.0.0 release**, we have built it out so that the Module and Security Token Registry are upgradeable. This means that down the road if there is something in the logic that we need to change, we can do that without having to re-deploy the whole thing again. All we need to do is update it. -If you are working on a local private network, you should run the faucet command to get Poly necessary to pay fees for the other commands. +### Modules -```bash -node CLI/polymath-cli faucet -``` +**Security Token (ST-20)**: The SecurityToken is an implementation of the ST-20 protocol that allows the addition of different modules to control its behavior. Different modules can be attached to a SecurityToken. -## Generating ST-20 token +We have a ST-20 token which is an Ethereum-based token implemented on top of the ERC-20 protocol that adds the ability for tokens to control transfers based on specific rules. ST-20 tokens rely on Transfer Managers to determine the ruleset the token should apply in order to allow or deny a transfer, be it between the issuer and investors, in a peer to peer exchange, or a transaction with an exchange. -The ST-20 Generator command is a wizard-like script that will guide technical users in the creation and deployment of an ST-20 token. +To simplify, it breaks down to having a base token that gives the issuer the ability to add functionality through modules. -1. Edit `CLI/commands/helpers/contract_addresses.js` to make sure scripts are pointing to the correct contract addresses -2. On the terminal, run the following command: -```bash -node CLI/polymath-cli st20generator -``` -3. Follow the text prompts: - * You will be asked for a token symbol. Enter a new symbol to register or a symbol you have already registered. - * Enter a token name (long name seen by investors) to complete the token registration process. The token will be deployed to the blockchain. - * (Optional) If you want to issue tokens to an address you own enter the address and then how many tokens you want to issue. If you want to issue tokens to a list of affiliates press `Y` and it will update a whitelist with them and then tokens will be issued. - Make sure the `whitelist_data.csv` and `multi_mint_data.csv` files are present in the data folder and fulfilled with the right information. - * Choose between Capped STO and USD Tiered STO. - * Configure the selected STO. Enter start and end times, the issuance type, and exchange rate. -4. Once the process is finished, you can run the `node CLI/polymath-cli st20generator` command again and enter the token symbol to see the STO's live-progress. - -## Whitelisting investors - -After starting the STO you can run a command to mass-update a whitelist of allowed/known investors. -Make sure the `whitelist_data.csv` file is present in the data folder. -The command takes 2 parameters: -- The token symbol for the STO you want to invest in -- (Optional) The size of each batch +###### Example -```bash -node CLI/polymath-cli whitelist TOKEN_SYMBOL [BATCH_SIZE] -``` +We have modules that can deal with transfer management. Restricting transfers through a whitelist or just restricting a transfer between addresses that could make an account go over a specified limit or you can limit the amount of a token holders or you can even limit transfers to prevent dumping of tokens by having a lockup period for token holders. -## Initial minting +#### The Polymath Modules +**TransferManager modules:** These control the logic behind transfers and how they are allowed or disallowed. By default, the ST (Security Token) gets a `GeneralTransferManager` module attached in order to determine if transfers should be allowed based on a whitelist approach. -Before starting the STO you can run a command to distribute tokens to previously whitelisted investors. -Make sure the `multi_mint_data` file is present in the data folder. -The command takes 2 parameters: -- The token symbol for the STO you want to invest in -- (Optional) The size of each batch +The `GeneralTransferManager` behaves differently depending who is trying to transfer the tokens. +a) In an offering setting (investors buying tokens from the issuer) the investor's address should be present on an internal whitelist managed by the issuer within the GeneralTransferManager. -```bash -node CLI/polymath-cli multi_mint TOKEN_SYMBOL [BATCH_SIZE] -``` +b) In a peer to peer transfer, restrictions apply based on real-life lockups that are enforced on-chain. For example, if a particular holder has a 1-year sale restriction for the token, the transaction will fail until that year passes. -## Investing in the STO +**Security Token Offering (STO) modules:** A SecurityToken can be attached to one (and only one) STO module that will dictate the logic of how those tokens will be sold/distributed. An STO is the equivalent to the Crowdsale contracts often found present in traditional ICOs. -You can run the investor_portal command to participate in any STO you have been whitelisted for. -You will be asked for an account, the token symbol and amount for the STO you want to invest in. +**Permission Manager modules:** These modules manage permissions on different aspects of the issuance process. The issuer can use this module to manage permissions and designate administrators on his token. For example, the issuer might give a KYC firm permissions to add investors to the whitelist. -```bash -node CLI/polymath-cli investor_portal -``` +**Checkpoint Modules** +These modules allow the issuer to define checkpoints at which token balances and the total supply of a token can be consistently queried. This functionality is useful for dividend payment mechanisms and on-chain governance, both of which need to be able to determine token balances consistently as of a specified point in time. -## Transferring tokens +**Burn Modules** +These modules allow issuers or investors to burn or redeem their tokens in exchange of another token which can be on chain or offchain. -You can run the transfer command to transfer ST tokens to another account (as long as both are whitelisted and have been cleared of any lockup periods). -- The token symbol of the ST you want to transfer -- The account that will receive the tokens -- How many tokens to send +With the Core **2.0.0 Release**, Polymath has also introduced the `USDTieredSTO`. This new STO module allows a security token to be issued in return for investment (security token offering) in various currencies (ETH, POLY & a USD stable coin). The price of tokens is denominated in USD and the STO allows multiple tiers with different price points to be defined. Discounts for investments made in POLY can also be defined. -```bash -node CLI/polymath-cli transfer TOKEN_SYMBOL ACCOUNT_TO AMOUNT -``` - -## Managing modules +## CLI and CLI Documentation Wiki: -You can run the module manager command to view all the modules attached to a token and their status. -You will be asked for a token symbol. +The CLI is for users that want to easily walkthrough all the details of an STO issuance. The CLI documentation is located on our [Github Wiki](https://github.com/PolymathNetwork/polymath-core/wiki). -```bash -node CLI/polymath-cli module_manager -``` +You can easily navigate through it with the sidebar directory in order to run the CLI and set up and test the following: -## Dividends manager +1. Prerequisite Instructions / Deploy and setup the Polymath contracts +2. Launch the CLI on Ganache +3. Use the Faucet to get POLY +4. Deploy a token + Launch a USDTieredSTO +5. Whitelist investors +6. Work with the Dividends module +7. Using other CLI features -You can run this command to create dividends and paid them out proportionally to token holder balances as of the time that the dividend was created, or at the time of a specified checkpoint that was created previously. You can choose between Ether or ERC20 dividens. - -```bash -node CLI/polymath-cli dividends_manager -``` # Setting up Polymath Core -### v2.0.0 KOVAN - -New Kovan PolyTokenFaucet: 0xb347b9f5b56b431b2cf4e1d90a5995f7519ca792 - - ----------------------- Polymath Network Smart Contracts: ----------------------- - PolymathRegistry: 0x5b215a7d39ee305ad28da29bf2f0425c6c2a00b3 - SecurityTokenRegistry (Proxy): 0x91110c2f67e2881a8540417be9eadf5bc9f2f248 - ModuleRegistry (Proxy): 0xde6d19d7a68d453244227b6ccc5d8e6c2314627a - FeatureRegistry: 0x8967a7cfc4b455398be2356cd05cd43b7a39697e - - ETHOracle: 0xCE5551FC9d43E9D2CC255139169FC889352405C8 - POLYOracle: 0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C - - STFactory: 0x22f56100c6f18b656dbf1b156334206326fc672a - GeneralTransferManagerFactory: 0x650e9507e983077d6f822472a7dcc37626d55c7f - GeneralPermissionManagerFactory: 0xbf0bd6305b523ce055baa6dfaa9676d6b9e6090b - - CappedSTOFactory: 0xa4a24780b93a378eb25ec4bfbf93bc8e79d7eeeb - USDTieredSTOFactory: 0x9106d7fbbd2996ef787913876341d0070cbdfc95 - USDTieredSTOProxyFactory: 0xb004ff6893b95dc8a19b9e09b2920a44a609bae3 - - CountTransferManagerFactory: 0xc7cf0c1ddc85c18672951f9bfeb7163ecc8f0e2f - PercentageTransferManagerFactory: 0xfea5fcb254bcb4ada0f86903ff822d6372325cb1 - ManualApprovalTransferManagerFactory: 0x8e96e7199b9ba096d666033f058ebb0050786baf - EtherDividendCheckpointFactory: 0x18ae137fc6581e121f3d37ed85c423dbc3c9b964 - ERC20DividendCheckpointFactory: 0x8c724a1504643e02bb02b23cdd414da637872c80 - --------------------------------------------------------------------------------- - - - - ## Mainnet -### v1.3.0 (TORO Release) +### v2.0.0 | Contract | Address | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| TickerRegistry: | [0xc31714e6759a1ee26db1d06af1ed276340cd4233](https://etherscan.io/address/0xc31714e6759a1ee26db1d06af1ed276340cd4233) | -| SecurityTokenRegistry: | [0xef58491224958d978facf55d2120c55a24516b98](https://etherscan.io/address/0xef58491224958d978facf55d2120c55a24516b98) | -| ModuleRegistry: | [0x31d85fffd7e38bd42d2ae0409ac149e3ef0fd92c](https://etherscan.io/address/0x31d85fffd7e38bd42d2ae0409ac149e3ef0fd92c) | -| Polymath Registry: | [0x06595656b93ce14834f0d22b7bbda4382d5ab510](https://etherscan.io/address/0x06595656b93ce14834f0d22b7bbda4382d5ab510) | -| CappedSTOFactory: | [0x2aa1b133f464ac08f66c2f702581d014e4603d31](https://etherscan.io/address/0x2aa1b133f464ac08f66c2f702581d014e4603d31) | -| EthDividendsCheckpointFactory: | [0x0da7ed8789348ac40937cf6ae8ff521eee43816c](https://etherscan.io/address/0x0da7ed8789348ac40937cf6ae8ff521eee43816c) | -| ERC20 Dividends Checkpoint Factory: | [0x6950096964b7adae34d5a3d1792fe73afbe9ddbc](https://etherscan.io/address/0x6950096964b7adae34d5a3d1792fe73afbe9ddbc) | -| General Permission Manager Factory: | [0xeba0348e243f2de2f1687060f9c795ac279c66af](https://etherscan.io/address/0xeba0348e243f2de2f1687060f9c795ac279c66af) | -| Count Transfer Manager Factory: | [0xa662a05647a8e713be1bed193c094805d20471ff](https://etherscan.io/address/0xa662a05647a8e713be1bed193c094805d20471ff) | -| Percentage Transfer Manager Factory: | [0x3870ee581a0528d24a6216311fcfa78f95a00593](https://etherscan.io/address/0x3870ee581a0528d24a6216311fcfa78f95a00593) | - +| SecurityTokenRegistry (Proxy): | [0x240f9f86b1465bf1b8eb29bc88cbf65573dfdd97](https://etherscan.io/address/0x240f9f86b1465bf1b8eb29bc88cbf65573dfdd97) | +| ModuleRegistry (Proxy): | [0x4566d68ea96fc2213f2446f0dd0f482146cee96d](https://etherscan.io/address/0x4566d68ea96fc2213f2446f0dd0f482146cee96d) | +| Polymath Registry: | [0xdfabf3e4793cd30affb47ab6fa4cf4eef26bbc27](https://etherscan.io/address/0xdfabf3e4793cd30affb47ab6fa4cf4eef26bbc27) | +| Feature Registry: | [0xa3eacb03622bf1513880892b7270d965f693ffb5](https://etherscan.io/address/0xa3eacb03622bf1513880892b7270d965f693ffb5) | +| ETHOracle: | [0x60055e9a93aae267da5a052e95846fa9469c0e7a](https://etherscan.io/address/0x60055e9a93aae267da5a052e95846fa9469c0e7a) | +| POLYOracle: | [0x52cb4616E191Ff664B0bff247469ce7b74579D1B](https://etherscan.io/address/0x52cb4616E191Ff664B0bff247469ce7b74579D1B) | +| General Transfer Manager Factory: | [0xdc95598ef2bbfdb66d02d5f3eea98ea39fbc8b26](https://etherscan.io/address/0xdc95598ef2bbfdb66d02d5f3eea98ea39fbc8b26) | +| General Permission Manager Factory: | [0xf0aa1856360277c60052d6095c5b787b01388cdd](https://etherscan.io/address/0xf0aa1856360277c60052d6095c5b787b01388cdd) | +| CappedSTOFactory: | [0x77d89663e8819023a87bfe2bc9baaa6922c0e57c](https://etherscan.io/address/0x77d89663e8819023a87bfe2bc9baaa6922c0e57c) | +| USDTieredSTO Factory: | [0x5a3a30bddae1f857a19b1aed93b5cdb3c3da809a](https://etherscan.io/address/0x5a3a30bddae1f857a19b1aed93b5cdb3c3da809a) | +| EthDividendsCheckpointFactory: | [0x968c74c52f15b2de323eca8c677f6c9266bfefd6](https://etherscan.io/address/0x968c74c52f15b2de323eca8c677f6c9266bfefd6) | +| ERC20 Dividends Checkpoint Factory: | [0x82f9f1ab41bacb1433c79492e54bf13bccd7f9ae](https://etherscan.io/address/0x82f9f1ab41bacb1433c79492e54bf13bccd7f9ae) | +| Count Transfer Manager Factory: | [0xd9fd7e34d6e2c47a69e02131cf8554d52c3445d5](https://etherscan.io/address/0xd9fd7e34d6e2c47a69e02131cf8554d52c3445d5) | +| Percentage Transfer Manager Factory: | [0xe6267a9c0a227d21c95b782b1bd32bb41fc3b43b](https://etherscan.io/address/0xe6267a9c0a227d21c95b782b1bd32bb41fc3b43b) | +| Manual Approval Transfer Manager Factory (2.0.1): | [0x6af2afad53cb334e62b90ddbdcf3a086f654c298](https://etherscan.io/address/0x6af2afad53cb334e62b90ddbdcf3a086f654c298) | + + +New SecurityTokenRegistry (2.0.1): 0x538136ed73011a766bf0a126a27300c3a7a2e6a6 +(fixed bug with getTickersByOwner()) + +New ModuleRegistry (2.0.1): 0xbc18f144ccf87f2d98e6fa0661799fcdc3170119 +(fixed bug with missing transferOwnership function) + +New ManualApprovalTransferManager 0x6af2afad53cb334e62b90ddbdcf3a086f654c298 +(Fixed 0x0 from bug) ## KOVAN -### v1.3.0 (TORO Release) +### v2.0.0 +New Kovan PolyTokenFaucet: 0xb347b9f5b56b431b2cf4e1d90a5995f7519ca792 | Contract | Address | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| TickerRegistry: | [0xc9af1d88fe48c8a6aa8677a29a89b0a6ae78f5a8](https://kovan.etherscan.io/address/0xc9af1d88fe48c8a6aa8677a29a89b0a6ae78f5a8) | -| SecurityTokenRegistry: | [0xced6e4ec2ac5425743bf4edf4d4e476120b8fc72](https://kovan.etherscan.io/address/0xced6e4ec2ac5425743bf4edf4d4e476120b8fc72) | -| ModuleRegistry: | [0x961913dcbe2f36176bf25774337f3277796820eb](https://kovan.etherscan.io/address/0x961913dcbe2f36176bf25774337f3277796820eb) | -| Polymath Registry: | [0x05a6519e49e34239f78167abf293d94dae61b299](https://kovan.etherscan.io/address/0x05a6519e49e34239f78167abf293d94dae61b299) | -| CappedSTOFactory: | [0xde4f3cfb6b214e60c4e69e6dfc95ede3c4e3d709](https://kovan.etherscan.io/address/0xde4f3cfb6b214e60c4e69e6dfc95ede3c4e3d709) | -| EthDividendsCheckpointFactory: | [0x870a07d45b0f4c5653fc29a4cb0697a01e0224b1](https://kovan.etherscan.io/address/0x870a07d45b0f4c5653fc29a4cb0697a01e0224b1) | -| ERC20 Dividends Checkpoint Factory: | [0x7e823f5df6ed1bb6cc005c692febc6aedf3b8889](https://kovan.etherscan.io/address/0x7e823f5df6ed1bb6cc005c692febc6aedf3b8889) | -| General Permission Manager Factory: | [0x6f5fec2934a34d2e2374042cca6505f1c87ef79b](https://kovan.etherscan.io/address/0x6f5fec2934a34d2e2374042cca6505f1c87ef79b) | -| Count Transfer Manager Factory: | [0xb540b6fa752a91c7e7834523172309e543a99a06](https://kovan.etherscan.io/address/0xb540b6fa752a91c7e7834523172309e543a99a06) | -| Percentage Transfer Manager Factory: | [0xfe908f07e6db57aa6bbd8374e59aac86b60374b0](https://kovan.etherscan.io/address/0xfe908f07e6db57aa6bbd8374e59aac86b60374b0) | - +| SecurityTokenRegistry (Proxy): | [0xbefb81114d532bddddc724af20c3516fa75f0afb](https://kovan.etherscan.io/address/0xbefb81114d532bddddc724af20c3516fa75f0afb) | +| ModuleRegistry (Proxy): | [0x0fac8d8cce224eead73c1187df96570aa80a568b](https://kovan.etherscan.io/address/0x0fac8d8cce224eead73c1187df96570aa80a568b) | +| Polymath Registry: | [0x9903e7b5acfe5fa9713771a8d861eb1df8cd7046](https://kovan.etherscan.io/address/0x9903e7b5acfe5fa9713771a8d861eb1df8cd7046) | +| Feature Registry: | [0xa8f85006fdacb3d59ffae564c05433f0c949e911](https://kovan.etherscan.io/address/0xa8f85006fdacb3d59ffae564c05433f0c949e911) | +| ETHOracle: | [0xCE5551FC9d43E9D2CC255139169FC889352405C8](https://kovan.etherscan.io/address/0xCE5551FC9d43E9D2CC255139169FC889352405C8) | +| POLYOracle: | [0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C](https://kovan.etherscan.io/address/0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C) | +| General Transfer Manager Factory: | [0xfe7e2bb6c200d5222c82d0f8fecca5f8fe4ab8ce](https://kovan.etherscan.io/address/0xfe7e2bb6c200d5222c82d0f8fecca5f8fe4ab8ce) | +| General Permission Manager Factory: | [0xde5eaa8d73f43fc5e7badb203f03ecae2b29bd92](https://kovan.etherscan.io/address/0xde5eaa8d73f43fc5e7badb203f03ecae2b29bd92) | +| CappedSTOFactory: | [0xe14d7dd044cc6cfe37548b6791416c59f19bfc0d](https://kovan.etherscan.io/address/0xe14d7dd044cc6cfe37548b6791416c59f19bfc0d) | +| USDTieredSTO Factory: | [0xf9f0bb9f868d411dd9a9511a79d172449e3c15f5](https://kovan.etherscan.io/address/0xf9f0bb9f868d411dd9a9511a79d172449e3c15f5) | +| EthDividendsCheckpointFactory: | [0x2861425ba5abbf50089c473b28f6c40a8ea5262a](https://kovan.etherscan.io/address/0x2861425ba5abbf50089c473b28f6c40a8ea5262a) | +| ERC20 Dividends Checkpoint Factory: | [0xbf9495550417feaacc43f86d2244581b6d688431](https://kovan.etherscan.io/address/0xbf9495550417feaacc43f86d2244581b6d688431) | +| Count Transfer Manager Factory: | [0x3c3c1f40ae2bdca82b90541b2cfbd41caa941c0e](https://kovan.etherscan.io/address/0x3c3c1f40ae2bdca82b90541b2cfbd41caa941c0e) | +| Percentage Transfer Manager Factory: | [0x8cd00c3914b2967a8b79815037f51c76874236b8](https://kovan.etherscan.io/address/0x8cd00c3914b2967a8b79815037f51c76874236b8) | +| Manual Approval Transfer Manager Factory: | [0x9faa79e2ccf0eb49aa6ebde1795ad2e951ce78f8](https://kovan.etherscan.io/address/0x9faa79e2ccf0eb49aa6ebde1795ad2e951ce78f8) | + + +New ManualApprovalTransferManager 0x9faa79e2ccf0eb49aa6ebde1795ad2e951ce78f8 +(Fixed 0x0 from bug) ## Package version requirements for your machine: diff --git a/audit reports/Polymath Core 2.0.0 Audit Report .pdf b/audit reports/Polymath Core 2.0.0 Audit Report .pdf new file mode 100644 index 000000000..38821f1ad Binary files /dev/null and b/audit reports/Polymath Core 2.0.0 Audit Report .pdf differ diff --git a/contracts/FeatureRegistry.sol b/contracts/FeatureRegistry.sol index 936a19976..1fbbb2134 100644 --- a/contracts/FeatureRegistry.sol +++ b/contracts/FeatureRegistry.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./ReclaimTokens.sol"; import "./interfaces/IFeatureRegistry.sol"; @@ -7,8 +7,7 @@ import "./interfaces/IFeatureRegistry.sol"; * @title Registry for managing polymath feature switches */ contract FeatureRegistry is IFeatureRegistry, ReclaimTokens { - - mapping (bytes32 => bool) public featureStatus; + mapping(bytes32 => bool) public featureStatus; event ChangeFeatureStatus(string _nameKey, bool _newStatus); @@ -17,7 +16,7 @@ contract FeatureRegistry is IFeatureRegistry, ReclaimTokens { * @param _nameKey is the key for the feature status mapping * @return bool */ - function getFeatureStatus(string _nameKey) external view returns(bool) { + function getFeatureStatus(string calldata _nameKey) external view returns(bool) { bytes32 key = keccak256(bytes(_nameKey)); return featureStatus[key]; } @@ -28,7 +27,7 @@ contract FeatureRegistry is IFeatureRegistry, ReclaimTokens { * @param _nameKey is the key for the feature status mapping * @param _newStatus is the new feature status */ - function setFeatureStatus(string _nameKey, bool _newStatus) public onlyOwner { + function setFeatureStatus(string calldata _nameKey, bool _newStatus) external onlyOwner { bytes32 key = keccak256(bytes(_nameKey)); require(featureStatus[key] != _newStatus, "Status unchanged"); emit ChangeFeatureStatus(_nameKey, _newStatus); diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol index 9f2b50b60..a15e3c71b 100644 --- a/contracts/Migrations.sol +++ b/contracts/Migrations.sol @@ -1,8 +1,6 @@ -pragma solidity ^0.4.24; - +pragma solidity ^0.5.0; contract Migrations { - address public owner; uint public lastCompletedMigration; @@ -16,11 +14,11 @@ contract Migrations { owner = msg.sender; } - function setCompleted(uint _completed)public restricted { + function setCompleted(uint _completed) public restricted { lastCompletedMigration = _completed; } - function upgrade(address _newAddress)public restricted { + function upgrade(address _newAddress) public restricted { Migrations upgraded = Migrations(_newAddress); upgraded.setCompleted(lastCompletedMigration); } diff --git a/contracts/ModuleRegistry.sol b/contracts/ModuleRegistry.sol index b2f1fbba5..974d5310d 100644 --- a/contracts/ModuleRegistry.sol +++ b/contracts/ModuleRegistry.sol @@ -1,11 +1,11 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "./interfaces/IModuleRegistry.sol"; import "./interfaces/IModuleFactory.sol"; import "./interfaces/ISecurityTokenRegistry.sol"; import "./interfaces/IPolymathRegistry.sol"; import "./interfaces/IFeatureRegistry.sol"; -import "./interfaces/IERC20.sol"; import "./libraries/VersionUtils.sol"; import "./storage/EternalStorage.sol"; import "./libraries/Encoder.sol"; @@ -40,9 +40,9 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { ////////// // Emit when network becomes paused - event Pause(uint256 _timestammp); - // Emit when network becomes unpaused - event Unpause(uint256 _timestamp); + event Pause(address account); + // Emit when network becomes unpaused + event Unpause(address account); // Emit when Module is used by the SecurityToken event ModuleUsed(address indexed _moduleFactory, address indexed _securityToken); // Emit when the Module Factory gets registered on the ModuleRegistry contract @@ -51,6 +51,8 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { event ModuleVerified(address indexed _moduleFactory, bool _verified); // Emit when a ModuleFactory is removed by Polymath event ModuleRemoved(address indexed _moduleFactory, address indexed _decisionMaker); + // Emit when ownership gets transferred + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /////////////// //// Modifiers @@ -60,7 +62,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { - require(msg.sender == owner(),"sender must be owner"); + require(msg.sender == owner(), "sender must be owner"); _; } @@ -68,8 +70,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @notice Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPausedOrOwner() { - if (msg.sender == owner()) - _; + if (msg.sender == owner()) _; else { require(!isPaused(), "Already paused"); _; @@ -97,13 +98,12 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { ///////////////////////////// // Constructor - constructor () public - { + constructor() public { } function initialize(address _polymathRegistry, address _owner) external payable { - require(!getBool(Encoder.getKey("initialised")),"already initialized"); + require(!getBoolValue(Encoder.getKey("initialised")), "already initialized"); require(_owner != address(0) && _polymathRegistry != address(0), "0x address is invalid"); set(Encoder.getKey("polymathRegistry"), _polymathRegistry); set(Encoder.getKey("owner"), _owner); @@ -120,11 +120,14 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { */ function useModule(address _moduleFactory) external { // This if statement is required to be able to add modules from the token proxy contract during deployment - if (ISecurityTokenRegistry(getAddress(Encoder.getKey("securityTokenRegistry"))).isSecurityToken(msg.sender)) { - if (IFeatureRegistry(getAddress(Encoder.getKey("featureRegistry"))).getFeatureStatus("customModulesAllowed")) { - require(getBool(Encoder.getKey("verified", _moduleFactory)) || IOwnable(_moduleFactory).owner() == IOwnable(msg.sender).owner(),"ModuleFactory must be verified or SecurityToken owner must be ModuleFactory owner"); + if (ISecurityTokenRegistry(getAddressValue(Encoder.getKey("securityTokenRegistry"))).isSecurityToken(msg.sender)) { + if (IFeatureRegistry(getAddressValue(Encoder.getKey("featureRegistry"))).getFeatureStatus("customModulesAllowed")) { + require( + getBoolValue(Encoder.getKey("verified", _moduleFactory)) || IOwnable(_moduleFactory).owner() == IOwnable(msg.sender).owner(), + "ModuleFactory must be verified or SecurityToken owner must be ModuleFactory owner" + ); } else { - require(getBool(Encoder.getKey("verified", _moduleFactory)), "ModuleFactory must be verified"); + require(getBoolValue(Encoder.getKey("verified", _moduleFactory)), "ModuleFactory must be verified"); } require(_isCompatibleModule(_moduleFactory, msg.sender), "Version should within the compatible range of ST"); pushArray(Encoder.getKey("reputation", _moduleFactory), msg.sender); @@ -146,12 +149,15 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _moduleFactory is the address of the module factory to be registered */ function registerModule(address _moduleFactory) external whenNotPausedOrOwner { - if (IFeatureRegistry(getAddress(Encoder.getKey("featureRegistry"))).getFeatureStatus("customModulesAllowed")) { - require(msg.sender == IOwnable(_moduleFactory).owner() || msg.sender == owner(),"msg.sender must be the Module Factory owner or registry curator"); + if (IFeatureRegistry(getAddressValue(Encoder.getKey("featureRegistry"))).getFeatureStatus("customModulesAllowed")) { + require( + msg.sender == IOwnable(_moduleFactory).owner() || msg.sender == owner(), + "msg.sender must be the Module Factory owner or registry curator" + ); } else { require(msg.sender == owner(), "Only owner allowed to register modules"); } - require(getUint(Encoder.getKey("registry", _moduleFactory)) == 0, "Module factory should not be pre-registered"); + require(getUintValue(Encoder.getKey("registry", _moduleFactory)) == 0, "Module factory should not be pre-registered"); IModuleFactory moduleFactory = IModuleFactory(_moduleFactory); //Enforce type uniqueness uint256 i; @@ -171,7 +177,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { uint256(getArrayAddress(Encoder.getKey("moduleList", uint256(moduleType))).length) ); pushArray(Encoder.getKey("moduleList", uint256(moduleType)), _moduleFactory); - emit ModuleRegistered (_moduleFactory, IOwnable(_moduleFactory).owner()); + emit ModuleRegistered(_moduleFactory, IOwnable(_moduleFactory).owner()); } /** @@ -179,14 +185,14 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _moduleFactory is the address of the module factory to be deleted from the registry */ function removeModule(address _moduleFactory) external whenNotPausedOrOwner { - uint256 moduleType = getUint(Encoder.getKey("registry", _moduleFactory)); + uint256 moduleType = getUintValue(Encoder.getKey("registry", _moduleFactory)); require(moduleType != 0, "Module factory should be registered"); require( msg.sender == IOwnable(_moduleFactory).owner() || msg.sender == owner(), "msg.sender must be the Module Factory owner or registry curator" ); - uint256 index = getUint(Encoder.getKey("moduleListIndex", _moduleFactory)); + uint256 index = getUintValue(Encoder.getKey("moduleListIndex", _moduleFactory)); uint256 last = getArrayAddress(Encoder.getKey("moduleList", moduleType)).length - 1; address temp = getArrayAddress(Encoder.getKey("moduleList", moduleType))[last]; @@ -218,7 +224,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @return bool */ function verifyModule(address _moduleFactory, bool _verified) external onlyOwner { - require(getUint(Encoder.getKey("registry", _moduleFactory)) != uint256(0), "Module factory must be registered"); + require(getUintValue(Encoder.getKey("registry", _moduleFactory)) != uint256(0), "Module factory must be registered"); set(Encoder.getKey("verified", _moduleFactory), _verified); emit ModuleVerified(_moduleFactory, _verified); } @@ -230,7 +236,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @return list of tags * @return corresponding list of module factories */ - function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[], address[]) { + function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[] memory, address[] memory) { address[] memory modules = getModulesByTypeAndToken(_moduleType, _securityToken); return _tagsByModules(modules); } @@ -241,7 +247,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @return list of tags * @return corresponding list of module factories */ - function getTagsByType(uint8 _moduleType) external view returns(bytes32[], address[]) { + function getTagsByType(uint8 _moduleType) external view returns(bytes32[] memory, address[] memory) { address[] memory modules = getModulesByType(_moduleType); return _tagsByModules(modules); } @@ -252,7 +258,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @return list of tags * @return corresponding list of module factories */ - function _tagsByModules(address[] _modules) internal view returns(bytes32[], address[]) { + function _tagsByModules(address[] memory _modules) internal view returns(bytes32[] memory, address[] memory) { uint256 counter = 0; uint256 i; uint256 j; @@ -279,7 +285,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _factoryAddress is the address of the module factory * @return address array which contains the list of securityTokens that use that module factory */ - function getReputationByFactory(address _factoryAddress) external view returns(address[]) { + function getReputationByFactory(address _factoryAddress) external view returns(address[] memory) { return getArrayAddress(Encoder.getKey("reputation", _factoryAddress)); } @@ -288,7 +294,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _moduleType Type of Module * @return address array that contains the list of addresses of module factory contracts. */ - function getModulesByType(uint8 _moduleType) public view returns(address[]) { + function getModulesByType(uint8 _moduleType) public view returns(address[] memory) { return getArrayAddress(Encoder.getKey("moduleList", uint256(_moduleType))); } @@ -298,37 +304,38 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _securityToken is the address of SecurityToken * @return address array that contains the list of available addresses of module factory contracts. */ - function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) public view returns (address[]) { + function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) public view returns(address[] memory) { uint256 _len = getArrayAddress(Encoder.getKey("moduleList", uint256(_moduleType))).length; address[] memory _addressList = getArrayAddress(Encoder.getKey("moduleList", uint256(_moduleType))); - bool _isCustomModuleAllowed = IFeatureRegistry(getAddress(Encoder.getKey("featureRegistry"))).getFeatureStatus("customModulesAllowed"); + bool _isCustomModuleAllowed = IFeatureRegistry(getAddressValue(Encoder.getKey("featureRegistry"))).getFeatureStatus( + "customModulesAllowed" + ); uint256 counter = 0; for (uint256 i = 0; i < _len; i++) { if (_isCustomModuleAllowed) { - if (IOwnable(_addressList[i]).owner() == IOwnable(_securityToken).owner() || getBool(Encoder.getKey("verified", _addressList[i]))) - if(_isCompatibleModule(_addressList[i], _securityToken)) - counter++; - } - else if (getBool(Encoder.getKey("verified", _addressList[i]))) { - if(_isCompatibleModule(_addressList[i], _securityToken)) - counter++; + if (IOwnable(_addressList[i]).owner() == IOwnable(_securityToken).owner() || getBoolValue( + Encoder.getKey("verified", _addressList[i]) + )) if (_isCompatibleModule(_addressList[i], _securityToken)) counter++; + } else if (getBoolValue(Encoder.getKey("verified", _addressList[i]))) { + if (_isCompatibleModule(_addressList[i], _securityToken)) counter++; } } address[] memory _tempArray = new address[](counter); counter = 0; for (uint256 j = 0; j < _len; j++) { if (_isCustomModuleAllowed) { - if (IOwnable(_addressList[j]).owner() == IOwnable(_securityToken).owner() || getBool(Encoder.getKey("verified", _addressList[j]))) { - if(_isCompatibleModule(_addressList[j], _securityToken)) { + if (IOwnable(_addressList[j]).owner() == IOwnable(_securityToken).owner() || getBoolValue( + Encoder.getKey("verified", _addressList[j]) + )) { + if (_isCompatibleModule(_addressList[j], _securityToken)) { _tempArray[counter] = _addressList[j]; - counter ++; + counter++; } } - } - else if (getBool(Encoder.getKey("verified", _addressList[j]))) { - if(_isCompatibleModule(_addressList[j], _securityToken)) { + } else if (getBoolValue(Encoder.getKey("verified", _addressList[j]))) { + if (_isCompatibleModule(_addressList[j], _securityToken)) { _tempArray[counter] = _addressList[j]; - counter ++; + counter++; } } } @@ -343,7 +350,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { require(_tokenContract != address(0), "0x address is invalid"); IERC20 token = IERC20(_tokenContract); uint256 balance = token.balanceOf(address(this)); - require(token.transfer(owner(), balance),"token transfer failed"); + require(token.transfer(owner(), balance), "token transfer failed"); } /** @@ -352,7 +359,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { function pause() external whenNotPaused onlyOwner { set(Encoder.getKey("paused"), true); /*solium-disable-next-line security/no-block-members*/ - emit Pause(now); + emit Pause(msg.sender); } /** @@ -361,25 +368,35 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { function unpause() external whenPaused onlyOwner { set(Encoder.getKey("paused"), false); /*solium-disable-next-line security/no-block-members*/ - emit Unpause(now); + emit Unpause(msg.sender); } /** * @notice Stores the contract addresses of other key contracts from the PolymathRegistry */ function updateFromRegistry() external onlyOwner { - address _polymathRegistry = getAddress(Encoder.getKey("polymathRegistry")); + address _polymathRegistry = getAddressValue(Encoder.getKey("polymathRegistry")); set(Encoder.getKey("securityTokenRegistry"), IPolymathRegistry(_polymathRegistry).getAddress("SecurityTokenRegistry")); set(Encoder.getKey("featureRegistry"), IPolymathRegistry(_polymathRegistry).getAddress("FeatureRegistry")); set(Encoder.getKey("polyToken"), IPolymathRegistry(_polymathRegistry).getAddress("PolyToken")); } + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function transferOwnership(address _newOwner) external onlyOwner { + require(_newOwner != address(0), "Invalid address"); + emit OwnershipTransferred(owner(), _newOwner); + set(Encoder.getKey("owner"), _newOwner); + } + /** * @notice Gets the owner of the contract * @return address owner */ function owner() public view returns(address) { - return getAddress(Encoder.getKey("owner")); + return getAddressValue(Encoder.getKey("owner")); } /** @@ -387,6 +404,6 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @return bool */ function isPaused() public view returns(bool) { - return getBool(Encoder.getKey("paused")); + return getBoolValue(Encoder.getKey("paused")); } } diff --git a/contracts/Pausable.sol b/contracts/Pausable.sol index fe4a3ca28..184987439 100644 --- a/contracts/Pausable.sol +++ b/contracts/Pausable.sol @@ -1,12 +1,11 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Utility contract to allow pausing and unpausing of certain functions */ contract Pausable { - - event Pause(uint256 _timestammp); - event Unpause(uint256 _timestamp); + event Pause(address account); + event Unpause(address account); bool public paused = false; @@ -26,13 +25,13 @@ contract Pausable { _; } - /** + /** * @notice Called by the owner to pause, triggers stopped state */ function _pause() internal whenNotPaused { paused = true; /*solium-disable-next-line security/no-block-members*/ - emit Pause(now); + emit Pause(msg.sender); } /** @@ -41,7 +40,7 @@ contract Pausable { function _unpause() internal whenPaused { paused = false; /*solium-disable-next-line security/no-block-members*/ - emit Unpause(now); + emit Unpause(msg.sender); } } diff --git a/contracts/PolymathRegistry.sol b/contracts/PolymathRegistry.sol index 0d6a6ac92..f80144b43 100644 --- a/contracts/PolymathRegistry.sol +++ b/contracts/PolymathRegistry.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./ReclaimTokens.sol"; @@ -6,8 +6,7 @@ import "./ReclaimTokens.sol"; * @title Core functionality for registry upgradability */ contract PolymathRegistry is ReclaimTokens { - - mapping (bytes32 => address) public storedAddresses; + mapping(bytes32 => address) public storedAddresses; event ChangeAddress(string _nameKey, address indexed _oldAddress, address indexed _newAddress); @@ -16,9 +15,9 @@ contract PolymathRegistry is ReclaimTokens { * @param _nameKey is the key for the contract address mapping * @return address */ - function getAddress(string _nameKey) external view returns(address) { + function getAddress(string calldata _nameKey) external view returns(address) { bytes32 key = keccak256(bytes(_nameKey)); - require(storedAddresses[key] != address(0), "Invalid address key"); + require(storedAddresses[key] != address(0), "Invalid key"); return storedAddresses[key]; } @@ -27,11 +26,10 @@ contract PolymathRegistry is ReclaimTokens { * @param _nameKey is the key for the contract address mapping * @param _newAddress is the new contract address */ - function changeAddress(string _nameKey, address _newAddress) external onlyOwner { + function changeAddress(string calldata _nameKey, address _newAddress) external onlyOwner { bytes32 key = keccak256(bytes(_nameKey)); emit ChangeAddress(_nameKey, storedAddresses[key], _newAddress); storedAddresses[key] = _newAddress; } - } diff --git a/contracts/ReclaimTokens.sol b/contracts/ReclaimTokens.sol index 767442829..75b8cb192 100644 --- a/contracts/ReclaimTokens.sol +++ b/contracts/ReclaimTokens.sol @@ -1,13 +1,12 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; -import "./interfaces/IERC20.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; /** * @title Utility contract to allow owner to retreive any ERC20 sent to the contract */ contract ReclaimTokens is Ownable { - /** * @notice Reclaim all ERC20Basic compatible tokens * @param _tokenContract The address of the token contract @@ -16,6 +15,6 @@ contract ReclaimTokens is Ownable { require(_tokenContract != address(0), "Invalid address"); IERC20 token = IERC20(_tokenContract); uint256 balance = token.balanceOf(address(this)); - require(token.transfer(owner, balance), "Transfer failed"); + require(token.transfer(owner(), balance), "Transfer failed"); } } diff --git a/contracts/RegistryUpdater.sol b/contracts/RegistryUpdater.sol index ee325b1cf..70e6c6849 100644 --- a/contracts/RegistryUpdater.sol +++ b/contracts/RegistryUpdater.sol @@ -1,17 +1,16 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; import "./PolymathRegistry.sol"; contract RegistryUpdater is Ownable { - address public polymathRegistry; address public moduleRegistry; address public securityTokenRegistry; address public featureRegistry; address public polyToken; - constructor (address _polymathRegistry) public { + constructor(address _polymathRegistry) public { require(_polymathRegistry != address(0), "Invalid address"); polymathRegistry = _polymathRegistry; } diff --git a/contracts/STRGetter.sol b/contracts/STRGetter.sol new file mode 100644 index 000000000..29d5264fd --- /dev/null +++ b/contracts/STRGetter.sol @@ -0,0 +1,213 @@ +pragma solidity ^0.5.0; + +import "./storage/EternalStorage.sol"; +import "./libraries/Util.sol"; +import "./libraries/Encoder.sol"; +import "./interfaces/IOwnable.sol"; +import "./libraries/VersionUtils.sol"; + +contract STRGetter is EternalStorage { + + bytes32 constant STLAUNCHFEE = 0xd677304bb45536bb7fdfa6b9e47a3c58fe413f9e8f01474b0a4b9c6e0275baf2; + bytes32 constant TICKERREGFEE = 0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2; + bytes32 constant EXPIRYLIMIT = 0x604268e9a73dfd777dcecb8a614493dd65c638bad2f5e7d709d378bd2fb0baee; + + /** + * @notice Returns the list of tickers owned by the selected address + * @param _owner is the address which owns the list of tickers + */ + function getTickersByOwner(address _owner) external view returns(bytes32[] memory) { + uint counter = 0; + // accessing the data structure userTotickers[_owner].length + bytes32[] memory tickers = getArrayBytes32(Encoder.getKey("userToTickers", _owner)); + uint i; + for (i = 0; i < tickers.length; i++) { + string memory ticker = Util.bytes32ToString(tickers[i]); + /*solium-disable-next-line security/no-block-members*/ + if (getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now || getTickerStatus(ticker)) { + counter ++; + } + } + bytes32[] memory tempList = new bytes32[](counter); + counter = 0; + for (i = 0; i < tickers.length; i++) { + string memory ticker = Util.bytes32ToString(tickers[i]); + /*solium-disable-next-line security/no-block-members*/ + if (getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now || getTickerStatus(ticker)) { + tempList[counter] = tickers[i]; + counter ++; + } + } + return tempList; + } + + /** + * @notice Returns the list of tokens owned by the selected address + * @param _owner is the address which owns the list of tickers + * @dev Intention is that this is called off-chain so block gas limit is not relevant + */ + function getTokensByOwner(address _owner) external view returns(address[] memory) { + return _getTokens(false, _owner); + } + + /** + * @notice Returns the list of all tokens + * @dev Intention is that this is called off-chain so block gas limit is not relevant + */ + function getTokens() external view returns(address[] memory) { + return _getTokens(true, address(0)); + } + /** + * @notice Returns the list of tokens owned by the selected address + * @param _allTokens if _allTokens is true returns all tokens despite on the second parameter + * @param _owner is the address which owns the list of tickers + */ + function _getTokens(bool _allTokens, address _owner) internal view returns(address[] memory) { + // Loop over all active users, then all associated tickers of those users + // This ensures we find tokens, even if their owner has been modified + address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers")); + bytes32[] memory tickers; + address token; + uint256 count = 0; + uint256 i = 0; + uint256 j = 0; + for (i = 0; i < activeUsers.length; i++) { + tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); + for (j = 0; j < tickers.length; j++) { + token = getAddressValue(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); + if (token != address(0)) { + if (_allTokens || IOwnable(token).owner() == _owner) { + count = count + 1; + } + } + } + } + uint256 index = 0; + address[] memory result = new address[](count); + for (i = 0; i < activeUsers.length; i++) { + tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); + for (j = 0; j < tickers.length; j++) { + token = getAddressValue(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); + if (token != address(0)) { + if (_allTokens || IOwnable(token).owner() == _owner) { + result[index] = token; + index = index + 1; + } + } + } + } + return result; + } + + /** + * @notice Returns the owner and timestamp for a given ticker + * @param _ticker is the ticker symbol + * @return address + * @return uint256 + * @return uint256 + * @return string + * @return bool + */ + function getTickerDetails(string calldata _ticker) external view returns (address, uint256, uint256, string memory, bool) { + string memory ticker = Util.upper(_ticker); + bool tickerStatus = getTickerStatus(ticker); + uint256 expiryDate = getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)); + /*solium-disable-next-line security/no-block-members*/ + if ((tickerStatus == true) || (expiryDate > now)) { + return + ( + getTickerOwner(ticker), + getUintValue(Encoder.getKey("registeredTickers_registrationDate", ticker)), + expiryDate, + getStringValue(Encoder.getKey("registeredTickers_tokenName", ticker)), + tickerStatus + ); + } else { + return (address(0), uint256(0), uint256(0), "", false); + } + } + + /** + * @notice Returns the security token address by ticker symbol + * @param _ticker is the ticker of the security token + * @return address + */ + function getSecurityTokenAddress(string calldata _ticker) external view returns (address) { + string memory ticker = Util.upper(_ticker); + return getAddressValue(Encoder.getKey("tickerToSecurityToken", ticker)); + } + + /** + * @notice Returns the security token data by address + * @param _securityToken is the address of the security token. + * @return string is the ticker of the security Token. + * @return address is the issuer of the security Token. + * @return string is the details of the security token. + * @return uint256 is the timestamp at which security Token was deployed. + */ + function getSecurityTokenData(address _securityToken) external view returns (string memory, address, string memory, uint256) { + return ( + getStringValue(Encoder.getKey("securityTokens_ticker", _securityToken)), + IOwnable(_securityToken).owner(), + getStringValue(Encoder.getKey("securityTokens_tokenDetails", _securityToken)), + getUintValue(Encoder.getKey("securityTokens_deployedAt", _securityToken)) + ); + } + + /** + * @notice Returns the current STFactory Address + */ + function getSTFactoryAddress() public view returns(address) { + return getAddressValue(Encoder.getKey("protocolVersionST", getUintValue(Encoder.getKey("latestVersion")))); + } + + /** + * @notice Gets Protocol version + */ + function getProtocolVersion() public view returns(uint8[] memory) { + return VersionUtils.unpack(uint24(getUintValue(Encoder.getKey("latestVersion")))); + } + + /** + * @notice Gets the security token launch fee + * @return Fee amount + */ + function getSecurityTokenLaunchFee() public view returns(uint256) { + return getUintValue(STLAUNCHFEE); + } + + /** + * @notice Gets the ticker registration fee + * @return Fee amount + */ + function getTickerRegistrationFee() public view returns(uint256) { + return getUintValue(TICKERREGFEE); + } + + /** + * @notice Gets the expiry limit + * @return Expiry limit + */ + function getExpiryLimit() public view returns(uint256) { + return getUintValue(EXPIRYLIMIT); + } + + /** + * @notice Gets the status of the ticker + * @param _ticker Ticker whose status need to determine + * @return bool + */ + function getTickerStatus(string memory _ticker) public view returns(bool) { + return getBoolValue(Encoder.getKey("registeredTickers_status", _ticker)); + } + + /** + * @notice Gets the owner of the ticker + * @param _ticker Ticker whose owner need to determine + * @return address Address of the owner + */ + function getTickerOwner(string memory _ticker) public view returns(address) { + return getAddressValue(Encoder.getKey("registeredTickers_owner", _ticker)); + } + +} diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index 7d24793f3..88c9d31d5 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -1,19 +1,23 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "./interfaces/IOwnable.sol"; import "./interfaces/ISTFactory.sol"; -import "./interfaces/IERC20.sol"; import "./interfaces/ISecurityTokenRegistry.sol"; +import "./interfaces/IPolymathRegistry.sol"; import "./storage/EternalStorage.sol"; import "./libraries/Util.sol"; import "./libraries/Encoder.sol"; import "./libraries/VersionUtils.sol"; +import "./proxy/Proxy.sol"; +import "./interfaces/IOracle.sol"; +import "./libraries/DecimalMath.sol"; /** * @title Registry contract for issuers to register their tickers and security tokens */ -contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { +contract SecurityTokenRegistry is EternalStorage, Proxy { /** * @notice state variables @@ -70,16 +74,19 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { bytes32 constant PAUSED = 0xee35723ac350a69d2a92d3703f17439cbaadf2f093a21ba5bf5f1a53eb2a14d9; bytes32 constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; bytes32 constant POLYMATHREGISTRY = 0x90eeab7c36075577c7cc5ff366e389fefa8a18289b949bab3529ab4471139d4d; + bytes32 constant STRGETTER = 0x982f24b3bd80807ec3cb227ba152e15c07d66855fa8ae6ca536e689205c0e2e9; + + string constant POLY_ORACLE = "StablePolyUsdOracle"; // Emit when network becomes paused - event Pause(uint256 _timestammp); - // Emit when network becomes unpaused - event Unpause(uint256 _timestamp); + event Pause(address account); + // Emit when network becomes unpaused + event Unpause(address account); // Emit when the ticker is removed from the registry event TickerRemoved(string _ticker, uint256 _removedAt, address _removedBy); // Emit when the token ticker expiry is changed event ChangeExpiryLimit(uint256 _oldExpiry, uint256 _newExpiry); - // Emit when changeSecurityLaunchFee is called + // Emit when changeSecurityLaunchFee is called event ChangeSecurityLaunchFee(uint256 _oldFee, uint256 _newFee); // Emit when changeTickerRegistrationFee is called event ChangeTickerRegistrationFee(uint256 _oldFee, uint256 _newFee); @@ -96,7 +103,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { uint256 _addedAt, address _registrant, bool _fromAdmin, - uint256 _registrationFee + uint256 _usdFee, + uint256 _polyFee ); // Emit after ticker registration event RegisterTicker( @@ -106,7 +114,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { uint256 indexed _registrationDate, uint256 indexed _expiryDate, bool _fromAdmin, - uint256 _registrationFee + uint256 _usdFee, + uint256 _polyFee ); ///////////////////////////// @@ -117,7 +126,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { - require(msg.sender == owner(),"sender must be owner"); + require(msg.sender == owner(), "sender must be owner"); _; } @@ -125,8 +134,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @notice Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPausedOrOwner() { - if (msg.sender == owner()) - _; + if (msg.sender == owner()) _; else { require(!isPaused(), "Already paused"); _; @@ -141,7 +149,6 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { _; } - /** * @notice Modifier to make a function callable only when the contract is paused. */ @@ -150,7 +157,6 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { _; } - ///////////////////////////// // Initialization ///////////////////////////// @@ -159,29 +165,28 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @notice Initializes instance of STR * @param _polymathRegistry is the address of the Polymath Registry * @param _STFactory is the address of the Proxy contract for Security Tokens - * @param _stLaunchFee is the fee in POLY required to launch a token - * @param _tickerRegFee is the fee in POLY required to register a ticker - * @param _polyToken is the address of the POLY ERC20 token - * @param _owner is the owner of the STR + * @param _stLaunchFee is the fee in USD required to launch a token + * @param _tickerRegFee is the fee in USD required to register a ticker + * @param _owner is the owner of the STR, + * @param _getterContract Contract address of the contract which consists getter functions. */ function initialize( address _polymathRegistry, address _STFactory, uint256 _stLaunchFee, uint256 _tickerRegFee, - address _polyToken, - address _owner + address _owner, + address _getterContract ) external payable { - require(!getBool(INITIALIZE),"already initialized"); + require(!getBoolValue(INITIALIZE),"already initialized"); require( - _STFactory != address(0) && _polyToken != address(0) && _owner != address(0) && _polymathRegistry != address(0), + _STFactory != address(0) && _owner != address(0) && _polymathRegistry != address(0) && _getterContract != address(0), "Invalid address" ); require(_stLaunchFee != 0 && _tickerRegFee != 0, "Fees should not be 0"); - set(POLYTOKEN, _polyToken); set(STLAUNCHFEE, _stLaunchFee); set(TICKERREGFEE, _tickerRegFee); set(EXPIRYLIMIT, uint256(60 * 1 days)); @@ -190,6 +195,55 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { set(POLYMATHREGISTRY, _polymathRegistry); _setProtocolVersion(_STFactory, uint8(2), uint8(0), uint8(0)); set(INITIALIZE, true); + set(STRGETTER, _getterContract); + _updateFromRegistry(); + } + + /** + * @notice Used to update the polyToken contract address + */ + function updateFromRegistry() external onlyOwner { + _updateFromRegistry(); + } + + function _updateFromRegistry() internal { + address polymathRegistry = getAddressValue(POLYMATHREGISTRY); + set(POLYTOKEN, IPolymathRegistry(polymathRegistry).getAddress("PolyToken")); + } + + /** + * @notice Converts USD fees into POLY amounts + */ + function _takeFee(bytes32 _feeType) internal returns (uint256, uint256) { + (uint256 usdFee, uint256 polyFee) = getFees(_feeType); + if (polyFee > 0) + require(IERC20(getAddressValue(POLYTOKEN)).transferFrom(msg.sender, address(this), polyFee), "Insufficent allowance"); + return (usdFee, polyFee); + } + + /** + * @notice Returns the usd & poly fee for a particular feetype + * @param _feeType Key corresponding to fee type + */ + function getFees(bytes32 _feeType) public returns (uint256, uint256) { + address polymathRegistry = getAddressValue(POLYMATHREGISTRY); + uint256 polyRate = IOracle(IPolymathRegistry(polymathRegistry).getAddress(POLY_ORACLE)).getPrice(); + uint256 usdFee = getUintValue(_feeType); + uint256 polyFee = DecimalMath.div(usdFee, polyRate); + return (usdFee, polyFee); + } + + /** + * @notice Set the getter contract address + * @param _getterContract Address of the contract + */ + function setGetterRegistry(address _getterContract) public onlyOwner { + require(_getterContract != address(0)); + set(STRGETTER, _getterContract); + } + + function _implementation() internal view returns(address) { + return getAddressValue(Encoder.getKey("STRGetter")); } ///////////////////////////// @@ -204,13 +258,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _ticker is unique token ticker * @param _tokenName is the name of the token */ - function registerTicker(address _owner, string _ticker, string _tokenName) external whenNotPausedOrOwner { + function registerTicker(address _owner, string calldata _ticker, string calldata _tokenName) external whenNotPausedOrOwner { require(_owner != address(0), "Owner should not be 0x"); require(bytes(_ticker).length > 0 && bytes(_ticker).length <= 10, "Ticker length range (0,10]"); - // Attempt to charge the reg fee if it is > 0 POLY - uint256 tickerFee = getTickerRegistrationFee(); - if (tickerFee > 0) - require(IERC20(getAddress(POLYTOKEN)).transferFrom(msg.sender, address(this), tickerFee), "Insufficent allowance"); + // Attempt to charge the reg fee if it is > 0 USD + (uint256 _usdFee, uint256 _polyFee) = _takeFee(TICKERREGFEE); string memory ticker = Util.upper(_ticker); require(_tickerAvailable(ticker), "Ticker is reserved"); // Check whether ticker was previously registered (and expired) @@ -219,25 +271,28 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { _deleteTickerOwnership(previousOwner, ticker); } /*solium-disable-next-line security/no-block-members*/ - _addTicker(_owner, ticker, _tokenName, now, now.add(getExpiryLimit()), false, false, tickerFee); + _addTicker(_owner, ticker, _tokenName, now, now.add(getUintValue(EXPIRYLIMIT)), false, false, _usdFee, _polyFee); } /** * @notice Internal - Sets the details of the ticker */ function _addTicker( - address _owner, - string _ticker, - string _tokenName, - uint256 _registrationDate, - uint256 _expiryDate, - bool _status, - bool _fromAdmin, - uint256 _fee - ) internal { + address _owner, + string memory _ticker, + string memory _tokenName, + uint256 _registrationDate, + uint256 _expiryDate, + bool _status, + bool _fromAdmin, + uint256 _usdFee, + uint256 _polyFee + ) + internal + { _setTickerOwnership(_owner, _ticker); _storeTickerDetails(_ticker, _owner, _registrationDate, _expiryDate, _tokenName, _status); - emit RegisterTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _fromAdmin, _fee); + emit RegisterTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _fromAdmin, _usdFee, _polyFee); } /** @@ -252,12 +307,15 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function modifyTicker( address _owner, - string _ticker, - string _tokenName, + string calldata _ticker, + string calldata _tokenName, uint256 _registrationDate, uint256 _expiryDate, bool _status - ) external onlyOwner { + ) + external + onlyOwner + { require(bytes(_ticker).length > 0 && bytes(_ticker).length <= 10, "Ticker length range (0,10]"); require(_expiryDate != 0 && _registrationDate != 0, "Dates should not be 0"); require(_registrationDate <= _expiryDate, "Registration date should < expiry date"); @@ -271,12 +329,14 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function _modifyTicker( address _owner, - string _ticker, - string _tokenName, + string memory _ticker, + string memory _tokenName, uint256 _registrationDate, uint256 _expiryDate, bool _status - ) internal { + ) + internal + { address currentOwner = _tickerOwner(_ticker); if (currentOwner != address(0)) { _deleteTickerOwnership(currentOwner, _ticker); @@ -286,20 +346,20 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { } // If status is true, there must be a security token linked to the ticker already if (_status) { - require(getAddress(Encoder.getKey("tickerToSecurityToken", _ticker)) != address(0), "Token not registered"); + require(getAddressValue(Encoder.getKey("tickerToSecurityToken", _ticker)) != address(0), "Token not registered"); } - _addTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _status, true, uint256(0)); + _addTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _status, true, uint256(0), uint256(0)); } - function _tickerOwner(string _ticker) internal view returns(address) { - return getAddress(Encoder.getKey("registeredTickers_owner", _ticker)); + function _tickerOwner(string memory _ticker) internal view returns(address) { + return getAddressValue(Encoder.getKey("registeredTickers_owner", _ticker)); } /** * @notice Removes the ticker details, associated ownership & security token mapping * @param _ticker is the token ticker */ - function removeTicker(string _ticker) external onlyOwner { + function removeTicker(string calldata _ticker) external onlyOwner { string memory ticker = Util.upper(_ticker); address owner = _tickerOwner(ticker); require(owner != address(0), "Ticker doesn't exist"); @@ -315,19 +375,18 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _ticker is the token ticker * @return bool */ - function _tickerAvailable(string _ticker) internal view returns(bool) { + function _tickerAvailable(string memory _ticker) internal view returns(bool) { if (_tickerOwner(_ticker) != address(0)) { /*solium-disable-next-line security/no-block-members*/ - if ((now > getUint(Encoder.getKey("registeredTickers_expiryDate", _ticker))) && !_tickerStatus(_ticker)) { + if ((now > getUintValue(Encoder.getKey("registeredTickers_expiryDate", _ticker))) && !_tickerStatus(_ticker)) { return true; - } else - return false; + } else return false; } return true; } - function _tickerStatus(string _ticker) internal view returns(bool) { - return getBool(Encoder.getKey("registeredTickers_status", _ticker)); + function _tickerStatus(string memory _ticker) internal view returns(bool) { + return getBoolValue(Encoder.getKey("registeredTickers_status", _ticker)); } /** @@ -335,13 +394,13 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _owner is the address of the owner of the ticker * @param _ticker is the ticker symbol */ - function _setTickerOwnership(address _owner, string _ticker) internal { + function _setTickerOwnership(address _owner, string memory _ticker) internal { bytes32 _ownerKey = Encoder.getKey("userToTickers", _owner); uint256 length = uint256(getArrayBytes32(_ownerKey).length); pushArray(_ownerKey, Util.stringToBytes32(_ticker)); set(Encoder.getKey("tickerIndex", _ticker), length); bytes32 seenKey = Encoder.getKey("seenUsers", _owner); - if (!getBool(seenKey)) { + if (!getBoolValue(seenKey)) { pushArray(Encoder.getKey("activeUsers"), _owner); set(seenKey, true); } @@ -351,28 +410,25 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @notice Internal - Stores the ticker details */ function _storeTickerDetails( - string _ticker, + string memory _ticker, address _owner, uint256 _registrationDate, uint256 _expiryDate, - string _tokenName, + string memory _tokenName, bool _status - ) internal { + ) + internal + { bytes32 key = Encoder.getKey("registeredTickers_owner", _ticker); - if (getAddress(key) != _owner) - set(key, _owner); + if (getAddressValue(key) != _owner) set(key, _owner); key = Encoder.getKey("registeredTickers_registrationDate", _ticker); - if (getUint(key) != _registrationDate) - set(key, _registrationDate); + if (getUintValue(key) != _registrationDate) set(key, _registrationDate); key = Encoder.getKey("registeredTickers_expiryDate", _ticker); - if (getUint(key) != _expiryDate) - set(key, _expiryDate); + if (getUintValue(key) != _expiryDate) set(key, _expiryDate); key = Encoder.getKey("registeredTickers_tokenName", _ticker); - if (Encoder.getKey(getString(key)) != Encoder.getKey(_tokenName)) - set(key, _tokenName); + if (Encoder.getKey(getStringValue(key)) != Encoder.getKey(_tokenName)) set(key, _tokenName); key = Encoder.getKey("registeredTickers_status", _ticker); - if (getBool(key) != _status) - set(key, _status); + if (getBoolValue(key) != _status) set(key, _status); } /** @@ -380,13 +436,15 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _newOwner is the address of the new owner of the ticker * @param _ticker is the ticker symbol */ - function transferTickerOwnership(address _newOwner, string _ticker) external whenNotPausedOrOwner { + function transferTickerOwnership(address _newOwner, string calldata _ticker) external whenNotPausedOrOwner { string memory ticker = Util.upper(_ticker); require(_newOwner != address(0), "Invalid address"); bytes32 ownerKey = Encoder.getKey("registeredTickers_owner", ticker); - require(getAddress(ownerKey) == msg.sender, "Not authorised"); - if (_tickerStatus(ticker)) - require(IOwnable(getAddress(Encoder.getKey("tickerToSecurityToken", ticker))).owner() == _newOwner, "New owner does not match token owner"); + require(getAddressValue(ownerKey) == msg.sender, "Not authorised"); + if (_tickerStatus(ticker)) require( + IOwnable(getAddressValue(Encoder.getKey("tickerToSecurityToken", ticker))).owner() == _newOwner, + "New owner does not match token owner" + ); _deleteTickerOwnership(msg.sender, ticker); _setTickerOwnership(_newOwner, ticker); set(ownerKey, _newOwner); @@ -396,8 +454,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { /** * @notice Internal - Removes the owner of a ticker */ - function _deleteTickerOwnership(address _owner, string _ticker) internal { - uint256 index = uint256(getUint(Encoder.getKey("tickerIndex", _ticker))); + function _deleteTickerOwnership(address _owner, string memory _ticker) internal { + uint256 index = uint256(getUintValue(Encoder.getKey("tickerIndex", _ticker))); bytes32 ownerKey = Encoder.getKey("userToTickers", _owner); bytes32[] memory tickers = getArrayBytes32(ownerKey); assert(index < tickers.length); @@ -415,99 +473,10 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function changeExpiryLimit(uint256 _newExpiry) external onlyOwner { require(_newExpiry >= 1 days, "Expiry should >= 1 day"); - emit ChangeExpiryLimit(getUint(EXPIRYLIMIT), _newExpiry); + emit ChangeExpiryLimit(getUintValue(EXPIRYLIMIT), _newExpiry); set(EXPIRYLIMIT, _newExpiry); } - /** - * @notice Returns the list of tickers owned by the selected address - * @param _owner is the address which owns the list of tickers - */ - function getTickersByOwner(address _owner) external view returns(bytes32[]) { - uint counter = 0; - // accessing the data structure userTotickers[_owner].length - bytes32[] memory tickers = getArrayBytes32(Encoder.getKey("userToTickers", _owner)); - bytes32[] memory tempList = new bytes32[](tickers.length); - for (uint i = 0; i < tickers.length; i++) { - string memory ticker = Util.bytes32ToString(tickers[i]); - /*solium-disable-next-line security/no-block-members*/ - if (getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now || _tickerStatus(ticker)) { - tempList[counter] = tickers[i]; - counter ++; - } - } - return tempList; - } - - /** - * @notice Returns the list of tokens owned by the selected address - * @param _owner is the address which owns the list of tickers - * @dev Intention is that this is called off-chain so block gas limit is not relevant - */ - function getTokensByOwner(address _owner) external view returns(address[]) { - // Loop over all active users, then all associated tickers of those users - // This ensures we find tokens, even if their owner has been modified - address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers")); - bytes32[] memory tickers; - address token; - uint256 count = 0; - uint256 i = 0; - uint256 j = 0; - for (i = 0; i < activeUsers.length; i++) { - tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); - for (j = 0; j < tickers.length; j++) { - token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); - if (token != address(0)) { - if (IOwnable(token).owner() == _owner) { - count = count + 1; - } - } - } - } - uint256 index = 0; - address[] memory result = new address[](count); - for (i = 0; i < activeUsers.length; i++) { - tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); - for (j = 0; j < tickers.length; j++) { - token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); - if (token != address(0)) { - if (IOwnable(token).owner() == _owner) { - result[index] = token; - index = index + 1; - } - } - } - } - return result; - } - - /** - * @notice Returns the owner and timestamp for a given ticker - * @param _ticker is the ticker symbol - * @return address - * @return uint256 - * @return uint256 - * @return string - * @return bool - */ - function getTickerDetails(string _ticker) external view returns (address, uint256, uint256, string, bool) { - string memory ticker = Util.upper(_ticker); - bool tickerStatus = _tickerStatus(ticker); - uint256 expiryDate = getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)); - /*solium-disable-next-line security/no-block-members*/ - if ((tickerStatus == true) || (expiryDate > now)) { - return - ( - _tickerOwner(ticker), - getUint(Encoder.getKey("registeredTickers_registrationDate", ticker)), - expiryDate, - getString(Encoder.getKey("registeredTickers_tokenName", ticker)), - tickerStatus - ); - } else - return (address(0), uint256(0), uint256(0), "", false); - } - ///////////////////////////// // Security Token Management ///////////////////////////// @@ -519,35 +488,40 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _tokenDetails is the off-chain details of the token * @param _divisible is whether or not the token is divisible */ - function generateSecurityToken(string _name, string _ticker, string _tokenDetails, bool _divisible) external whenNotPausedOrOwner { + function generateSecurityToken( + string calldata _name, + string calldata _ticker, + string calldata _tokenDetails, + bool _divisible + ) + external + whenNotPausedOrOwner + { require(bytes(_name).length > 0 && bytes(_ticker).length > 0, "Ticker length > 0"); string memory ticker = Util.upper(_ticker); bytes32 statusKey = Encoder.getKey("registeredTickers_status", ticker); - require(!getBool(statusKey), "Already deployed"); + require(!getBoolValue(statusKey), "Already deployed"); set(statusKey, true); require(_tickerOwner(ticker) == msg.sender, "Not authorised"); /*solium-disable-next-line security/no-block-members*/ - require(getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker gets expired"); - - uint256 launchFee = getSecurityTokenLaunchFee(); - if (launchFee > 0) - require(IERC20(getAddress(POLYTOKEN)).transferFrom(msg.sender, address(this), launchFee), "Insufficient allowance"); + require(getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker gets expired"); + (uint256 _usdFee, uint256 _polyFee) = _takeFee(STLAUNCHFEE); - address newSecurityTokenAddress = ISTFactory(getSTFactoryAddress()).deployToken( + address newSecurityTokenAddress = ISTFactory(getAddressValue(Encoder.getKey("protocolVersionST", getUintValue(Encoder.getKey("latestVersion"))))).deployToken( _name, ticker, 18, _tokenDetails, msg.sender, _divisible, - getAddress(POLYMATHREGISTRY) + getAddressValue(POLYMATHREGISTRY) ); /*solium-disable-next-line security/no-block-members*/ _storeSecurityTokenData(newSecurityTokenAddress, ticker, _tokenDetails, now); set(Encoder.getKey("tickerToSecurityToken", ticker), newSecurityTokenAddress); /*solium-disable-next-line security/no-block-members*/ - emit NewSecurityToken(ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, launchFee); + emit NewSecurityToken(ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, _usdFee, _polyFee); } /** @@ -560,11 +534,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _deployedAt is the timestamp at which the security token is deployed */ function modifySecurityToken( - string _name, - string _ticker, + string calldata _name, + string calldata _ticker, address _owner, address _securityToken, - string _tokenDetails, + string calldata _tokenDetails, uint256 _deployedAt ) external @@ -575,23 +549,28 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { require(_deployedAt != 0 && _owner != address(0), "0 value params not allowed"); string memory ticker = Util.upper(_ticker); require(_securityToken != address(0), "ST address is 0x"); - uint256 registrationTime = getUint(Encoder.getKey("registeredTickers_registrationDate", ticker)); - uint256 expiryTime = getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)); + uint256 registrationTime = getUintValue(Encoder.getKey("registeredTickers_registrationDate", ticker)); + uint256 expiryTime = getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)); if (registrationTime == 0) { /*solium-disable-next-line security/no-block-members*/ registrationTime = now; - expiryTime = registrationTime.add(getExpiryLimit()); + expiryTime = registrationTime.add(getUintValue(EXPIRYLIMIT)); } set(Encoder.getKey("tickerToSecurityToken", ticker), _securityToken); _modifyTicker(_owner, ticker, _name, registrationTime, expiryTime, true); _storeSecurityTokenData(_securityToken, ticker, _tokenDetails, _deployedAt); - emit NewSecurityToken(ticker, _name, _securityToken, _owner, _deployedAt, msg.sender, true, getSecurityTokenLaunchFee()); + emit NewSecurityToken(ticker, _name, _securityToken, _owner, _deployedAt, msg.sender, true, uint256(0), uint256(0)); } /** * @notice Internal - Stores the security token details */ - function _storeSecurityTokenData(address _securityToken, string _ticker, string _tokenDetails, uint256 _deployedAt) internal { + function _storeSecurityTokenData( + address _securityToken, + string memory _ticker, + string memory _tokenDetails, + uint256 _deployedAt + ) internal { set(Encoder.getKey("securityTokens_ticker", _securityToken), _ticker); set(Encoder.getKey("securityTokens_tokenDetails", _securityToken), _tokenDetails); set(Encoder.getKey("securityTokens_deployedAt", _securityToken), _deployedAt); @@ -602,35 +581,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _securityToken is the address of the security token * @return bool */ - function isSecurityToken(address _securityToken) external view returns (bool) { - return (keccak256(bytes(getString(Encoder.getKey("securityTokens_ticker", _securityToken)))) != keccak256("")); - } - - /** - * @notice Returns the security token address by ticker symbol - * @param _ticker is the ticker of the security token - * @return address - */ - function getSecurityTokenAddress(string _ticker) external view returns (address) { - string memory ticker = Util.upper(_ticker); - return getAddress(Encoder.getKey("tickerToSecurityToken", ticker)); - } - - /** - * @notice Returns the security token data by address - * @param _securityToken is the address of the security token. - * @return string is the ticker of the security Token. - * @return address is the issuer of the security Token. - * @return string is the details of the security token. - * @return uint256 is the timestamp at which security Token was deployed. - */ - function getSecurityTokenData(address _securityToken) external view returns (string, address, string, uint256) { - return ( - getString(Encoder.getKey("securityTokens_ticker", _securityToken)), - IOwnable(_securityToken).owner(), - getString(Encoder.getKey("securityTokens_tokenDetails", _securityToken)), - getUint(Encoder.getKey("securityTokens_deployedAt", _securityToken)) - ); + function isSecurityToken(address _securityToken) external view returns(bool) { + return (keccak256(bytes(getStringValue(Encoder.getKey("securityTokens_ticker", _securityToken)))) != keccak256("")); } ///////////////////////////// @@ -643,7 +595,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function transferOwnership(address _newOwner) external onlyOwner { require(_newOwner != address(0), "Invalid address"); - emit OwnershipTransferred(getAddress(OWNER), _newOwner); + emit OwnershipTransferred(getAddressValue(OWNER), _newOwner); set(OWNER, _newOwner); } @@ -653,7 +605,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { function pause() external whenNotPaused onlyOwner { set(PAUSED, true); /*solium-disable-next-line security/no-block-members*/ - emit Pause(now); + emit Pause(msg.sender); } /** @@ -662,26 +614,26 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { function unpause() external whenPaused onlyOwner { set(PAUSED, false); /*solium-disable-next-line security/no-block-members*/ - emit Unpause(now); + emit Unpause(msg.sender); } /** - * @notice Sets the ticker registration fee in POLY tokens. Only Polymath. - * @param _tickerRegFee is the registration fee in POLY tokens (base 18 decimals) + * @notice Sets the ticker registration fee in USD tokens. Only Polymath. + * @param _tickerRegFee is the registration fee in USD tokens (base 18 decimals) */ function changeTickerRegistrationFee(uint256 _tickerRegFee) external onlyOwner { - uint256 fee = getUint(TICKERREGFEE); + uint256 fee = getUintValue(TICKERREGFEE); require(fee != _tickerRegFee, "Fee not changed"); emit ChangeTickerRegistrationFee(fee, _tickerRegFee); set(TICKERREGFEE, _tickerRegFee); } - /** - * @notice Sets the ticker registration fee in POLY tokens. Only Polymath. - * @param _stLaunchFee is the registration fee in POLY tokens (base 18 decimals) + /** + * @notice Sets the ticker registration fee in USD tokens. Only Polymath. + * @param _stLaunchFee is the registration fee in USD tokens (base 18 decimals) */ function changeSecurityLaunchFee(uint256 _stLaunchFee) external onlyOwner { - uint256 fee = getUint(STLAUNCHFEE); + uint256 fee = getUintValue(STLAUNCHFEE); require(fee != _stLaunchFee, "Fee not changed"); emit ChangeSecurityLaunchFee(fee, _stLaunchFee); set(STLAUNCHFEE, _stLaunchFee); @@ -721,23 +673,12 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { _version[1] = _minor; _version[2] = _patch; uint24 _packedVersion = VersionUtils.pack(_major, _minor, _patch); - require(VersionUtils.isValidVersion(getProtocolVersion(), _version),"In-valid version"); + require( + VersionUtils.isValidVersion(VersionUtils.unpack(uint24(getUintValue(Encoder.getKey("latestVersion")))), _version), + "In-valid version" + ); set(Encoder.getKey("latestVersion"), uint256(_packedVersion)); - set(Encoder.getKey("protocolVersionST", getUint(Encoder.getKey("latestVersion"))), _STFactoryAddress); - } - - /** - * @notice Returns the current STFactory Address - */ - function getSTFactoryAddress() public view returns(address) { - return getAddress(Encoder.getKey("protocolVersionST", getUint(Encoder.getKey("latestVersion")))); - } - - /** - * @notice Gets Protocol version - */ - function getProtocolVersion() public view returns(uint8[]) { - return VersionUtils.unpack(uint24(getUint(Encoder.getKey("latestVersion")))); + set(Encoder.getKey("protocolVersionST", getUintValue(Encoder.getKey("latestVersion"))), _STFactoryAddress); } /** @@ -749,36 +690,12 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { set(POLYTOKEN, _newAddress); } - /** - * @notice Gets the security token launch fee - * @return Fee amount - */ - function getSecurityTokenLaunchFee() public view returns(uint256) { - return getUint(STLAUNCHFEE); - } - - /** - * @notice Gets the ticker registration fee - * @return Fee amount - */ - function getTickerRegistrationFee() public view returns(uint256) { - return getUint(TICKERREGFEE); - } - - /** - * @notice Gets the expiry limit - * @return Expiry limit - */ - function getExpiryLimit() public view returns(uint256) { - return getUint(EXPIRYLIMIT); - } - /** * @notice Check whether the registry is paused or not * @return bool */ function isPaused() public view returns(bool) { - return getBool(PAUSED); + return getBoolValue(PAUSED); } /** @@ -786,7 +703,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @return address owner */ function owner() public view returns(address) { - return getAddress(OWNER); + return getAddressValue(OWNER); } } diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol new file mode 100644 index 000000000..958f8c51d --- /dev/null +++ b/contracts/datastore/DataStore.sol @@ -0,0 +1,403 @@ +pragma solidity ^0.5.0; + +import "../interfaces/ISecurityToken.sol"; +import "../interfaces/IOwnable.sol"; +import "../interfaces/IDataStore.sol"; +import "./DataStoreStorage.sol"; + +/** + * @title Data store contract that stores data for all the modules in a central contract. + */ +contract DataStore is DataStoreStorage, IDataStore { + //NB To modify a specific element of an array, First push a new element to the array and then delete the old element. + //Whenver an element is deleted from an Array, last element of that array is moved to the index of deleted element. + //Delegate with MANAGEDATA permission can modify data. + event SecurityTokenChanged(address indexed _oldSecurityToken, address indexed _newSecurityToken); + + function _isAuthorized() internal view { + require(msg.sender == address(securityToken) || + msg.sender == IOwnable(address(securityToken)).owner() || + securityToken.checkPermission(msg.sender, address(this), MANAGEDATA) || + securityToken.isModule(msg.sender, DATA_KEY), + "Unauthorized" + ); + } + + modifier validKey(bytes32 _key) { + require(_key != bytes32(0), "bad key"); + _; + } + + modifier validArrayLength(uint256 _keyLength, uint256 _dataLength) { + require(_keyLength == _dataLength, "bad length"); + _; + } + + modifier onlyOwner() { + require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); + _; + } + + /** + * @dev Changes security token atatched to this data store + * @param _securityToken address of the security token + */ + function setSecurityToken(address _securityToken) external onlyOwner { + require(_securityToken != address(0), "Invalid address"); + emit SecurityTokenChanged(address(securityToken), _securityToken); + securityToken = ISecurityToken(_securityToken); + } + + /** + * @dev Stores a uint256 data against a key + * @param _key Unique key to identify the data + * @param _data Data to be stored against the key + */ + function setUint256(bytes32 _key, uint256 _data) external { + _isAuthorized(); + _setData(_key, _data, false); + } + + function setBytes32(bytes32 _key, bytes32 _data) external { + _isAuthorized(); + _setData(_key, _data, false); + } + + function setAddress(bytes32 _key, address _data) external { + _isAuthorized(); + _setData(_key, _data, false); + } + + function setBool(bytes32 _key, bool _data) external { + _isAuthorized(); + _setData(_key, _data, false); + } + + function setString(bytes32 _key, string calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + function setBytes(bytes32 _key, bytes calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + /** + * @dev Stores a uint256 array against a key + * @param _key Unique key to identify the array + * @param _data Array to be stored against the key + */ + function setUint256Array(bytes32 _key, uint256[] calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + function setBytes32Array(bytes32 _key, bytes32[] calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + function setAddressArray(bytes32 _key, address[] calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + function setBoolArray(bytes32 _key, bool[] calldata _data) external { + _isAuthorized(); + _setData(_key, _data); + } + + /** + * @dev Inserts a uint256 element to the array identified by the key + * @param _key Unique key to identify the array + * @param _data Element to push into the array + */ + function insertUint256(bytes32 _key, uint256 _data) external { + _isAuthorized(); + _setData(_key, _data, true); + } + + function insertBytes32(bytes32 _key, bytes32 _data) external { + _isAuthorized(); + _setData(_key, _data, true); + } + + function insertAddress(bytes32 _key, address _data) external { + _isAuthorized(); + _setData(_key, _data, true); + } + + function insertBool(bytes32 _key, bool _data) external { + _isAuthorized(); + _setData(_key, _data, true); + } + + /** + * @dev Deletes an element from the array identified by the key. + * When an element is deleted from an Array, last element of that array is moved to the index of deleted element. + * @param _key Unique key to identify the array + * @param _index Index of the element to delete + */ + function deleteUint256(bytes32 _key, uint256 _index) external { + _isAuthorized(); + _deleteUint(_key, _index); + } + + function deleteBytes32(bytes32 _key, uint256 _index) external { + _isAuthorized(); + _deleteBytes32(_key, _index); + } + + function deleteAddress(bytes32 _key, uint256 _index) external { + _isAuthorized(); + _deleteAddress(_key, _index); + } + + function deleteBool(bytes32 _key, uint256 _index) external { + _isAuthorized(); + _deleteBool(_key, _index); + } + + /** + * @dev Stores multiple uint256 data against respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be stored against the respective keys + */ + function setUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], false); + } + } + + function setBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], false); + } + } + + function setAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], false); + } + } + + function setBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], false); + } + } + + /** + * @dev Inserts multiple uint256 elements to the array identified by the respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be inserted in arrays of the respective keys + */ + function insertUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], true); + } + } + + function insertBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], true); + } + } + + function insertAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], true); + } + } + + function insertBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external validArrayLength(_keys.length, _data.length) { + _isAuthorized(); + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i], true); + } + } + + function getUint256(bytes32 _key) external view returns(uint256) { + return uintData[_key]; + } + + function getBytes32(bytes32 _key) external view returns(bytes32) { + return bytes32Data[_key]; + } + + function getAddress(bytes32 _key) external view returns(address) { + return addressData[_key]; + } + + function getString(bytes32 _key) external view returns(string memory) { + return stringData[_key]; + } + + function getBytes(bytes32 _key) external view returns(bytes memory) { + return bytesData[_key]; + } + + function getBool(bytes32 _key) external view returns(bool) { + return boolData[_key]; + } + + function getUint256Array(bytes32 _key) external view returns(uint256[] memory) { + return uintArrayData[_key]; + } + + function getBytes32Array(bytes32 _key) external view returns(bytes32[] memory) { + return bytes32ArrayData[_key]; + } + + function getAddressArray(bytes32 _key) external view returns(address[] memory) { + return addressArrayData[_key]; + } + + function getBoolArray(bytes32 _key) external view returns(bool[] memory) { + return boolArrayData[_key]; + } + + function getUint256ArrayLength(bytes32 _key) external view returns(uint256) { + return uintArrayData[_key].length; + } + + function getBytes32ArrayLength(bytes32 _key) external view returns(uint256) { + return bytes32ArrayData[_key].length; + } + + function getAddressArrayLength(bytes32 _key) external view returns(uint256) { + return addressArrayData[_key].length; + } + + function getBoolArrayLength(bytes32 _key) external view returns(uint256) { + return boolArrayData[_key].length; + } + + function getUint256ArrayElement(bytes32 _key, uint256 _index) external view returns(uint256) { + return uintArrayData[_key][_index]; + } + + function getBytes32ArrayElement(bytes32 _key, uint256 _index) external view returns(bytes32) { + return bytes32ArrayData[_key][_index]; + } + + function getAddressArrayElement(bytes32 _key, uint256 _index) external view returns(address) { + return addressArrayData[_key][_index]; + } + + function getBoolArrayElement(bytes32 _key, uint256 _index) external view returns(bool) { + return boolArrayData[_key][_index]; + } + + function getUint256ArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(uint256[] memory array) { + uint256 size = _endIndex - _startIndex + 1; + array = new uint256[](size); + for(uint256 i; i < size; i++) + array[i] = uintArrayData[_key][i + _startIndex]; + } + + function getBytes32ArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(bytes32[] memory array) { + uint256 size = _endIndex - _startIndex + 1; + array = new bytes32[](size); + for(uint256 i; i < size; i++) + array[i] = bytes32ArrayData[_key][i + _startIndex]; + } + + function getAddressArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(address[] memory array) { + uint256 size = _endIndex - _startIndex + 1; + array = new address[](size); + for(uint256 i; i < size; i++) + array[i] = addressArrayData[_key][i + _startIndex]; + } + + function getBoolArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(bool[] memory array) { + uint256 size = _endIndex - _startIndex + 1; + array = new bool[](size); + for(uint256 i; i < size; i++) + array[i] = boolArrayData[_key][i + _startIndex]; + } + + function _setData(bytes32 _key, uint256 _data, bool _insert) internal validKey(_key) { + if (_insert) + uintArrayData[_key].push(_data); + else + uintData[_key] = _data; + } + + function _setData(bytes32 _key, bytes32 _data, bool _insert) internal validKey(_key) { + if (_insert) + bytes32ArrayData[_key].push(_data); + else + bytes32Data[_key] = _data; + } + + function _setData(bytes32 _key, address _data, bool _insert) internal validKey(_key) { + if (_insert) + addressArrayData[_key].push(_data); + else + addressData[_key] = _data; + } + + function _setData(bytes32 _key, bool _data, bool _insert) internal validKey(_key) { + if (_insert) + boolArrayData[_key].push(_data); + else + boolData[_key] = _data; + } + + function _setData(bytes32 _key, string memory _data) internal validKey(_key) { + stringData[_key] = _data; + } + + function _setData(bytes32 _key, bytes memory _data) internal validKey(_key) { + bytesData[_key] = _data; + } + + function _setData(bytes32 _key, uint256[] memory _data) internal validKey(_key) { + uintArrayData[_key] = _data; + } + + function _setData(bytes32 _key, bytes32[] memory _data) internal validKey(_key) { + bytes32ArrayData[_key] = _data; + } + + function _setData(bytes32 _key, address[] memory _data) internal validKey(_key) { + addressArrayData[_key] = _data; + } + + function _setData(bytes32 _key, bool[] memory _data) internal validKey(_key) { + boolArrayData[_key] = _data; + } + + function _deleteUint(bytes32 _key, uint256 _index) internal validKey(_key) { + require(uintArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + uintArrayData[_key][_index] = uintArrayData[_key][uintArrayData[_key].length - 1]; + uintArrayData[_key].length--; + } + + function _deleteBytes32(bytes32 _key, uint256 _index) internal validKey(_key) { + require(bytes32ArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + bytes32ArrayData[_key][_index] = bytes32ArrayData[_key][bytes32ArrayData[_key].length - 1]; + bytes32ArrayData[_key].length--; + } + + function _deleteAddress(bytes32 _key, uint256 _index) internal validKey(_key) { + require(addressArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + addressArrayData[_key][_index] = addressArrayData[_key][addressArrayData[_key].length - 1]; + addressArrayData[_key].length--; + } + + function _deleteBool(bytes32 _key, uint256 _index) internal validKey(_key) { + require(boolArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + boolArrayData[_key][_index] = boolArrayData[_key][boolArrayData[_key].length - 1]; + boolArrayData[_key].length--; + } +} diff --git a/contracts/datastore/DataStoreFactory.sol b/contracts/datastore/DataStoreFactory.sol new file mode 100644 index 000000000..f4a3cc6d2 --- /dev/null +++ b/contracts/datastore/DataStoreFactory.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.5.0; + +import "../proxy/DataStoreProxy.sol"; + +contract DataStoreFactory { + + address public implementation; + + constructor(address _implementation) public { + require(_implementation != address(0), "Address should not be 0x"); + implementation = _implementation; + } + + function generateDataStore(address _securityToken) public returns (address) { + DataStoreProxy dsProxy = new DataStoreProxy(_securityToken, implementation); + return address(dsProxy); + } +} diff --git a/contracts/datastore/DataStoreStorage.sol b/contracts/datastore/DataStoreStorage.sol new file mode 100644 index 000000000..64423f39d --- /dev/null +++ b/contracts/datastore/DataStoreStorage.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.0; + +import "../interfaces/ISecurityToken.sol"; + +contract DataStoreStorage { + ISecurityToken public securityToken; + + mapping (bytes32 => uint256) internal uintData; + mapping (bytes32 => bytes32) internal bytes32Data; + mapping (bytes32 => address) internal addressData; + mapping (bytes32 => string) internal stringData; + mapping (bytes32 => bytes) internal bytesData; + mapping (bytes32 => bool) internal boolData; + mapping (bytes32 => uint256[]) internal uintArrayData; + mapping (bytes32 => bytes32[]) internal bytes32ArrayData; + mapping (bytes32 => address[]) internal addressArrayData; + mapping (bytes32 => bool[]) internal boolArrayData; + + uint8 internal constant DATA_KEY = 6; + bytes32 internal constant MANAGEDATA = "MANAGEDATA"; +} diff --git a/contracts/external/IMedianizer.sol b/contracts/external/IMedianizer.sol index 755d54a0f..aebe8f77a 100644 --- a/contracts/external/IMedianizer.sol +++ b/contracts/external/IMedianizer.sol @@ -1,15 +1,14 @@ /* solium-disable */ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface to MakerDAO Medianizer contract */ interface IMedianizer { + function peek() external view returns(bytes32, bool); - function peek() constant external returns (bytes32, bool); - - function read() constant external returns (bytes32); + function read() external view returns(bytes32); function set(address wat) external; @@ -27,7 +26,7 @@ interface IMedianizer { function poke(bytes32) external; - function compute() constant external returns (bytes32, bool); + function compute() external view returns(bytes32, bool); function void() external; diff --git a/contracts/external/oraclizeAPI.sol b/contracts/external/oraclizeAPI.sol index c8d7a8f18..9989144c7 100644 --- a/contracts/external/oraclizeAPI.sol +++ b/contracts/external/oraclizeAPI.sol @@ -1,15 +1,20 @@ -// /* + +ORACLIZE_API + Copyright (c) 2015-2016 Oraclize SRL Copyright (c) 2016 Oraclize LTD + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -17,44 +22,55 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +pragma solidity >= 0.5.0; // Incompatible compiler version - please select a compiler within the stated pragma range, or use a different version of the oraclizeAPI! -// This api is currently targeted at 0.4.18, please import oraclizeAPI_pre0.4.sol or oraclizeAPI_0.4 where necessary -/* solium-disable */ -pragma solidity >=0.4.18;// Incompatible compiler version... please select one stated within pragma solidity or use different oraclizeAPI version +// Dummy contract only used to emit to end-user they are using wrong solc +contract solcChecker { +/* INCOMPATIBLE SOLC: import the following instead: "github.com/oraclize/ethereum-api/oraclizeAPI_0.4.sol" */ function f(bytes calldata x) external; +} contract OraclizeI { + address public cbAddress; - function query(uint _timestamp, string _datasource, string _arg) external payable returns (bytes32 _id); - function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) external payable returns (bytes32 _id); - function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) public payable returns (bytes32 _id); - function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) external payable returns (bytes32 _id); - function queryN(uint _timestamp, string _datasource, bytes _argN) public payable returns (bytes32 _id); - function queryN_withGasLimit(uint _timestamp, string _datasource, bytes _argN, uint _gaslimit) external payable returns (bytes32 _id); - function getPrice(string _datasource) public returns (uint _dsprice); - function getPrice(string _datasource, uint gaslimit) public returns (uint _dsprice); + function setProofType(byte _proofType) external; function setCustomGasPrice(uint _gasPrice) external; - function randomDS_getSessionPubKeyHash() external constant returns(bytes32); + function getPrice(string memory _datasource) public returns (uint _dsprice); + function randomDS_getSessionPubKeyHash() external view returns (bytes32 _sessionKeyHash); + function getPrice(string memory _datasource, uint _gasLimit) public returns (uint _dsprice); + function queryN(uint _timestamp, string memory _datasource, bytes memory _argN) public payable returns (bytes32 _id); + function query(uint _timestamp, string calldata _datasource, string calldata _arg) external payable returns (bytes32 _id); + function query2(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) public payable returns (bytes32 _id); + function query_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg, uint _gasLimit) external payable returns (bytes32 _id); + function queryN_withGasLimit(uint _timestamp, string calldata _datasource, bytes calldata _argN, uint _gasLimit) external payable returns (bytes32 _id); + function query2_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg1, string calldata _arg2, uint _gasLimit) external payable returns (bytes32 _id); } contract OraclizeAddrResolverI { - function getAddress() public returns (address _addr); + function getAddress() public returns (address _address); } - /* + Begin solidity-cborutils + https://github.com/smartcontractkit/solidity-cborutils + MIT License + Copyright (c) 2018 SmartContract ChainLink, Ltd. + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -62,760 +78,876 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ +*/ library Buffer { + struct buffer { bytes buf; uint capacity; } - function init(buffer memory buf, uint _capacity) internal pure { + function init(buffer memory _buf, uint _capacity) internal pure { uint capacity = _capacity; - if(capacity % 32 != 0) capacity += 32 - (capacity % 32); - // Allocate space for the buffer data - buf.capacity = capacity; + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + _buf.capacity = capacity; // Allocate space for the buffer data assembly { let ptr := mload(0x40) - mstore(buf, ptr) + mstore(_buf, ptr) mstore(ptr, 0) mstore(0x40, add(ptr, capacity)) } } - function resize(buffer memory buf, uint capacity) private pure { - bytes memory oldbuf = buf.buf; - init(buf, capacity); - append(buf, oldbuf); + function resize(buffer memory _buf, uint _capacity) private pure { + bytes memory oldbuf = _buf.buf; + init(_buf, _capacity); + append(_buf, oldbuf); } - function max(uint a, uint b) private pure returns(uint) { - if(a > b) { - return a; + function max(uint _a, uint _b) private pure returns (uint _max) { + if (_a > _b) { + return _a; } - return b; + return _b; } - /** - * @dev Appends a byte array to the end of the buffer. Resizes if doing so - * would exceed the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer. - */ - function append(buffer memory buf, bytes data) internal pure returns(buffer memory) { - if(data.length + buf.buf.length > buf.capacity) { - resize(buf, max(buf.capacity, data.length) * 2); + * @dev Appends a byte array to the end of the buffer. Resizes if doing so + * would exceed the capacity of the buffer. + * @param _buf The buffer to append to. + * @param _data The data to append. + * @return The original buffer. + * + */ + function append(buffer memory _buf, bytes memory _data) internal pure returns (buffer memory _buffer) { + if (_data.length + _buf.buf.length > _buf.capacity) { + resize(_buf, max(_buf.capacity, _data.length) * 2); } - uint dest; uint src; - uint len = data.length; + uint len = _data.length; assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Length of existing buffer data - let buflen := mload(bufptr) - // Start address = buffer address + buffer length + sizeof(buffer length) - dest := add(add(bufptr, buflen), 32) - // Update buffer length - mstore(bufptr, add(buflen, mload(data))) - src := add(data, 32) - } - - // Copy word-length chunks while possible - for(; len >= 32; len -= 32) { + let bufptr := mload(_buf) // Memory address of the buffer data + let buflen := mload(bufptr) // Length of existing buffer data + dest := add(add(bufptr, buflen), 32) // Start address = buffer address + buffer length + sizeof(buffer length) + mstore(bufptr, add(buflen, mload(_data))) // Update buffer length + src := add(_data, 32) + } + for(; len >= 32; len -= 32) { // Copy word-length chunks while possible assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } - - // Copy remaining bytes - uint mask = 256 ** (32 - len) - 1; + uint mask = 256 ** (32 - len) - 1; // Copy remaining bytes assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } - - return buf; + return _buf; } - /** - * @dev Appends a byte to the end of the buffer. Resizes if doing so would - * exceed the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer. - */ - function append(buffer memory buf, uint8 data) internal pure { - if(buf.buf.length + 1 > buf.capacity) { - resize(buf, buf.capacity * 2); + * + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param _buf The buffer to append to. + * @param _data The data to append. + * @return The original buffer. + * + */ + function append(buffer memory _buf, uint8 _data) internal pure { + if (_buf.buf.length + 1 > _buf.capacity) { + resize(_buf, _buf.capacity * 2); } - assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Length of existing buffer data - let buflen := mload(bufptr) - // Address = buffer address + buffer length + sizeof(buffer length) - let dest := add(add(bufptr, buflen), 32) - mstore8(dest, data) - // Update buffer length - mstore(bufptr, add(buflen, 1)) + let bufptr := mload(_buf) // Memory address of the buffer data + let buflen := mload(bufptr) // Length of existing buffer data + let dest := add(add(bufptr, buflen), 32) // Address = buffer address + buffer length + sizeof(buffer length) + mstore8(dest, _data) + mstore(bufptr, add(buflen, 1)) // Update buffer length } } - /** - * @dev Appends a byte to the end of the buffer. Resizes if doing so would - * exceed the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer. - */ - function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { - if(len + buf.buf.length > buf.capacity) { - resize(buf, max(buf.capacity, len) * 2); - } - - uint mask = 256 ** len - 1; + * + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param _buf The buffer to append to. + * @param _data The data to append. + * @return The original buffer. + * + */ + function appendInt(buffer memory _buf, uint _data, uint _len) internal pure returns (buffer memory _buffer) { + if (_len + _buf.buf.length > _buf.capacity) { + resize(_buf, max(_buf.capacity, _len) * 2); + } + uint mask = 256 ** _len - 1; assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Length of existing buffer data - let buflen := mload(bufptr) - // Address = buffer address + buffer length + sizeof(buffer length) + len - let dest := add(add(bufptr, buflen), len) - mstore(dest, or(and(mload(dest), not(mask)), data)) - // Update buffer length - mstore(bufptr, add(buflen, len)) - } - return buf; + let bufptr := mload(_buf) // Memory address of the buffer data + let buflen := mload(bufptr) // Length of existing buffer data + let dest := add(add(bufptr, buflen), _len) // Address = buffer address + buffer length + sizeof(buffer length) + len + mstore(dest, or(and(mload(dest), not(mask)), _data)) + mstore(bufptr, add(buflen, _len)) // Update buffer length + } + return _buf; } } library CBOR { + using Buffer for Buffer.buffer; uint8 private constant MAJOR_TYPE_INT = 0; - uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_BYTES = 2; - uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; - uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; - function encodeType(Buffer.buffer memory buf, uint8 major, uint value) private pure { - if(value <= 23) { - buf.append(uint8((major << 5) | value)); - } else if(value <= 0xFF) { - buf.append(uint8((major << 5) | 24)); - buf.appendInt(value, 1); - } else if(value <= 0xFFFF) { - buf.append(uint8((major << 5) | 25)); - buf.appendInt(value, 2); - } else if(value <= 0xFFFFFFFF) { - buf.append(uint8((major << 5) | 26)); - buf.appendInt(value, 4); - } else if(value <= 0xFFFFFFFFFFFFFFFF) { - buf.append(uint8((major << 5) | 27)); - buf.appendInt(value, 8); + function encodeType(Buffer.buffer memory _buf, uint8 _major, uint _value) private pure { + if (_value <= 23) { + _buf.append(uint8((_major << 5) | _value)); + } else if (_value <= 0xFF) { + _buf.append(uint8((_major << 5) | 24)); + _buf.appendInt(_value, 1); + } else if (_value <= 0xFFFF) { + _buf.append(uint8((_major << 5) | 25)); + _buf.appendInt(_value, 2); + } else if (_value <= 0xFFFFFFFF) { + _buf.append(uint8((_major << 5) | 26)); + _buf.appendInt(_value, 4); + } else if (_value <= 0xFFFFFFFFFFFFFFFF) { + _buf.append(uint8((_major << 5) | 27)); + _buf.appendInt(_value, 8); } } - function encodeIndefiniteLengthType(Buffer.buffer memory buf, uint8 major) private pure { - buf.append(uint8((major << 5) | 31)); + function encodeIndefiniteLengthType(Buffer.buffer memory _buf, uint8 _major) private pure { + _buf.append(uint8((_major << 5) | 31)); } - function encodeUInt(Buffer.buffer memory buf, uint value) internal pure { - encodeType(buf, MAJOR_TYPE_INT, value); + function encodeUInt(Buffer.buffer memory _buf, uint _value) internal pure { + encodeType(_buf, MAJOR_TYPE_INT, _value); } - function encodeInt(Buffer.buffer memory buf, int value) internal pure { - if(value >= 0) { - encodeType(buf, MAJOR_TYPE_INT, uint(value)); + function encodeInt(Buffer.buffer memory _buf, int _value) internal pure { + if (_value >= 0) { + encodeType(_buf, MAJOR_TYPE_INT, uint(_value)); } else { - encodeType(buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - value)); + encodeType(_buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - _value)); } } - function encodeBytes(Buffer.buffer memory buf, bytes value) internal pure { - encodeType(buf, MAJOR_TYPE_BYTES, value.length); - buf.append(value); + function encodeBytes(Buffer.buffer memory _buf, bytes memory _value) internal pure { + encodeType(_buf, MAJOR_TYPE_BYTES, _value.length); + _buf.append(_value); } - function encodeString(Buffer.buffer memory buf, string value) internal pure { - encodeType(buf, MAJOR_TYPE_STRING, bytes(value).length); - buf.append(bytes(value)); + function encodeString(Buffer.buffer memory _buf, string memory _value) internal pure { + encodeType(_buf, MAJOR_TYPE_STRING, bytes(_value).length); + _buf.append(bytes(_value)); } - function startArray(Buffer.buffer memory buf) internal pure { - encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + function startArray(Buffer.buffer memory _buf) internal pure { + encodeIndefiniteLengthType(_buf, MAJOR_TYPE_ARRAY); } - function startMap(Buffer.buffer memory buf) internal pure { - encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + function startMap(Buffer.buffer memory _buf) internal pure { + encodeIndefiniteLengthType(_buf, MAJOR_TYPE_MAP); } - function endSequence(Buffer.buffer memory buf) internal pure { - encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + function endSequence(Buffer.buffer memory _buf) internal pure { + encodeIndefiniteLengthType(_buf, MAJOR_TYPE_CONTENT_FREE); } } - /* + End solidity-cborutils - */ +*/ contract usingOraclize { - uint constant day = 60*60*24; - uint constant week = 60*60*24*7; - uint constant month = 60*60*24*30; + + using CBOR for Buffer.buffer; + + OraclizeI oraclize; + OraclizeAddrResolverI OAR; + + uint constant day = 60 * 60 * 24; + uint constant week = 60 * 60 * 24 * 7; + uint constant month = 60 * 60 * 24 * 30; + byte constant proofType_NONE = 0x00; - byte constant proofType_TLSNotary = 0x10; byte constant proofType_Ledger = 0x30; - byte constant proofType_Android = 0x40; byte constant proofType_Native = 0xF0; byte constant proofStorage_IPFS = 0x01; + byte constant proofType_Android = 0x40; + byte constant proofType_TLSNotary = 0x10; + + string oraclize_network_name; uint8 constant networkID_auto = 0; + uint8 constant networkID_morden = 2; uint8 constant networkID_mainnet = 1; uint8 constant networkID_testnet = 2; - uint8 constant networkID_morden = 2; uint8 constant networkID_consensys = 161; - OraclizeAddrResolverI OAR; + mapping(bytes32 => bytes32) oraclize_randomDS_args; + mapping(bytes32 => bool) oraclize_randomDS_sessionKeysHashVerified; - OraclizeI oraclize; modifier oraclizeAPI { - if((address(OAR)==0)||(getCodeSize(address(OAR))==0)) + if ((address(OAR) == address(0)) || (getCodeSize(address(OAR)) == 0)) { oraclize_setNetwork(networkID_auto); - - if(address(oraclize) != OAR.getAddress()) + } + if (address(oraclize) != OAR.getAddress()) { oraclize = OraclizeI(OAR.getAddress()); - + } _; } - modifier coupon(string code){ - oraclize = OraclizeI(OAR.getAddress()); + + modifier oraclize_randomDS_proofVerify(bytes32 _queryId, string memory _result, bytes memory _proof) { + // RandomDS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1) + require((_proof[0] == "L") && (_proof[1] == "P") && (uint8(_proof[2]) == uint8(1))); + bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName()); + require(proofVerified); _; } - function oraclize_setNetwork(uint8 networkID) internal returns(bool){ + function oraclize_setNetwork(uint8 _networkID) internal returns (bool _networkSet) { return oraclize_setNetwork(); - networkID; // silence the warning and remain backwards compatible + _networkID; // silence the warning and remain backwards compatible } - function oraclize_setNetwork() internal returns(bool){ - if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed)>0){ //mainnet + + function oraclize_setNetworkName(string memory _network_name) internal { + oraclize_network_name = _network_name; + } + + function oraclize_getNetworkName() internal view returns (string memory _networkName) { + return oraclize_network_name; + } + + function oraclize_setNetwork() internal returns (bool _networkSet) { + if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed) > 0) { //mainnet OAR = OraclizeAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed); oraclize_setNetworkName("eth_mainnet"); return true; } - if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1)>0){ //ropsten testnet + if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1) > 0) { //ropsten testnet OAR = OraclizeAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1); oraclize_setNetworkName("eth_ropsten3"); return true; } - if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e)>0){ //kovan testnet + if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e) > 0) { //kovan testnet OAR = OraclizeAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e); oraclize_setNetworkName("eth_kovan"); return true; } - if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48)>0){ //rinkeby testnet + if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48) > 0) { //rinkeby testnet OAR = OraclizeAddrResolverI(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48); oraclize_setNetworkName("eth_rinkeby"); return true; } - if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475)>0){ //ethereum-bridge + if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475) > 0) { //ethereum-bridge OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); return true; } - if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF)>0){ //ether.camp ide + if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF) > 0) { //ether.camp ide OAR = OraclizeAddrResolverI(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF); return true; } - if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA)>0){ //browser-solidity + if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA) > 0) { //browser-solidity OAR = OraclizeAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA); return true; } return false; } - function __callback(bytes32 myid, string result) public { - __callback(myid, result, new bytes(0)); + function __callback(bytes32 _myid, string memory _result) public { + __callback(_myid, _result, new bytes(0)); } - function __callback(bytes32 myid, string result, bytes proof) public { + + function __callback(bytes32 _myid, string memory _result, bytes memory _proof) public { return; - myid; result; proof; // Silence compiler warnings - } - - function oraclize_getPrice(string datasource) oraclizeAPI internal returns (uint){ - return oraclize.getPrice(datasource); - } - - function oraclize_getPrice(string datasource, uint gaslimit) oraclizeAPI internal returns (uint){ - return oraclize.getPrice(datasource, gaslimit); - } - - function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - return oraclize.query.value(price)(0, datasource, arg); - } - function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - return oraclize.query.value(price)(timestamp, datasource, arg); - } - function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - return oraclize.query_withGasLimit.value(price)(timestamp, datasource, arg, gaslimit); - } - function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - return oraclize.query_withGasLimit.value(price)(0, datasource, arg, gaslimit); - } - function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - return oraclize.query2.value(price)(0, datasource, arg1, arg2); - } - function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - return oraclize.query2.value(price)(timestamp, datasource, arg1, arg2); - } - function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - return oraclize.query2_withGasLimit.value(price)(timestamp, datasource, arg1, arg2, gaslimit); - } - function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - return oraclize.query2_withGasLimit.value(price)(0, datasource, arg1, arg2, gaslimit); - } - function oraclize_query(string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - bytes memory args = stra2cbor(argN); - return oraclize.queryN.value(price)(0, datasource, args); - } - function oraclize_query(uint timestamp, string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - bytes memory args = stra2cbor(argN); - return oraclize.queryN.value(price)(timestamp, datasource, args); - } - function oraclize_query(uint timestamp, string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - bytes memory args = stra2cbor(argN); - return oraclize.queryN_withGasLimit.value(price)(timestamp, datasource, args, gaslimit); - } - function oraclize_query(string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - bytes memory args = stra2cbor(argN); - return oraclize.queryN_withGasLimit.value(price)(0, datasource, args, gaslimit); - } - function oraclize_query(string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { + _myid; _result; _proof; // Silence compiler warnings + } + + function oraclize_getPrice(string memory _datasource) oraclizeAPI internal returns (uint _queryPrice) { + return oraclize.getPrice(_datasource); + } + + function oraclize_getPrice(string memory _datasource, uint _gasLimit) oraclizeAPI internal returns (uint _queryPrice) { + return oraclize.getPrice(_datasource, _gasLimit); + } + + function oraclize_query(string memory _datasource, string memory _arg) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + return oraclize.query.value(price)(0, _datasource, _arg); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + return oraclize.query.value(price)(_timestamp, _datasource, _arg); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource,_gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + return oraclize.query_withGasLimit.value(price)(_timestamp, _datasource, _arg, _gasLimit); + } + + function oraclize_query(string memory _datasource, string memory _arg, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + return oraclize.query_withGasLimit.value(price)(0, _datasource, _arg, _gasLimit); + } + + function oraclize_query(string memory _datasource, string memory _arg1, string memory _arg2) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + return oraclize.query2.value(price)(0, _datasource, _arg1, _arg2); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + return oraclize.query2.value(price)(_timestamp, _datasource, _arg1, _arg2); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + return oraclize.query2_withGasLimit.value(price)(_timestamp, _datasource, _arg1, _arg2, _gasLimit); + } + + function oraclize_query(string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + return oraclize.query2_withGasLimit.value(price)(0, _datasource, _arg1, _arg2, _gasLimit); + } + + function oraclize_query(string memory _datasource, string[] memory _argN) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + bytes memory args = stra2cbor(_argN); + return oraclize.queryN.value(price)(0, _datasource, args); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string[] memory _argN) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + bytes memory args = stra2cbor(_argN); + return oraclize.queryN.value(price)(_timestamp, _datasource, args); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + bytes memory args = stra2cbor(_argN); + return oraclize.queryN_withGasLimit.value(price)(_timestamp, _datasource, args, _gasLimit); + } + + function oraclize_query(string memory _datasource, string[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + bytes memory args = stra2cbor(_argN); + return oraclize.queryN_withGasLimit.value(price)(0, _datasource, args, _gasLimit); + } + + function oraclize_query(string memory _datasource, string[1] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](1); - dynargs[0] = args[0]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[1] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](1); - dynargs[0] = args[0]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](1); - dynargs[0] = args[0]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](1); - dynargs[0] = args[0]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { + function oraclize_query(string memory _datasource, string[2] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[2] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[3] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[3] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { + function oraclize_query(string memory _datasource, string[4] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[4] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, string[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, string[5] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(datasource, dynargs); - } - function oraclize_query(uint timestamp, string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_datasource, dynargs); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string[5] memory _args) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(timestamp, datasource, dynargs); - } - function oraclize_query(uint timestamp, string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_timestamp, _datasource, dynargs); + } + + function oraclize_query(uint _timestamp, string memory _datasource, string[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); - } - function oraclize_query(string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); + } + + function oraclize_query(string memory _datasource, string[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { string[] memory dynargs = new string[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(datasource, dynargs, gaslimit); - } - function oraclize_query(string datasource, bytes[] argN) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - bytes memory args = ba2cbor(argN); - return oraclize.queryN.value(price)(0, datasource, args); - } - function oraclize_query(uint timestamp, string datasource, bytes[] argN) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource); - if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price - bytes memory args = ba2cbor(argN); - return oraclize.queryN.value(price)(timestamp, datasource, args); - } - function oraclize_query(uint timestamp, string datasource, bytes[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - bytes memory args = ba2cbor(argN); - return oraclize.queryN_withGasLimit.value(price)(timestamp, datasource, args, gaslimit); - } - function oraclize_query(string datasource, bytes[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ - uint price = oraclize.getPrice(datasource, gaslimit); - if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price - bytes memory args = ba2cbor(argN); - return oraclize.queryN_withGasLimit.value(price)(0, datasource, args, gaslimit); - } - function oraclize_query(string datasource, bytes[1] args) oraclizeAPI internal returns (bytes32 id) { + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_datasource, dynargs, _gasLimit); + } + + function oraclize_query(string memory _datasource, bytes[] memory _argN) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + bytes memory args = ba2cbor(_argN); + return oraclize.queryN.value(price)(0, _datasource, args); + } + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[] memory _argN) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource); + if (price > 1 ether + tx.gasprice * 200000) { + return 0; // Unexpectedly high price + } + bytes memory args = ba2cbor(_argN); + return oraclize.queryN.value(price)(_timestamp, _datasource, args); + } + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + bytes memory args = ba2cbor(_argN); + return oraclize.queryN_withGasLimit.value(price)(_timestamp, _datasource, args, _gasLimit); + } + + function oraclize_query(string memory _datasource, bytes[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + uint price = oraclize.getPrice(_datasource, _gasLimit); + if (price > 1 ether + tx.gasprice * _gasLimit) { + return 0; // Unexpectedly high price + } + bytes memory args = ba2cbor(_argN); + return oraclize.queryN_withGasLimit.value(price)(0, _datasource, args, _gasLimit); + } + + function oraclize_query(string memory _datasource, bytes[1] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](1); - dynargs[0] = args[0]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[1] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[1] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](1); - dynargs[0] = args[0]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](1); - dynargs[0] = args[0]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](1); - dynargs[0] = args[0]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[2] args) oraclizeAPI internal returns (bytes32 id) { + function oraclize_query(string memory _datasource, bytes[2] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[2] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[2] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](2); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[3] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[3] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[3] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[3] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](3); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[4] args) oraclizeAPI internal returns (bytes32 id) { + function oraclize_query(string memory _datasource, bytes[4] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[4] args) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[4] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(timestamp, datasource, dynargs); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_query(uint timestamp, string datasource, bytes[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](4); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_query(string datasource, bytes[5] args) oraclizeAPI internal returns (bytes32 id) { - bytes[] memory dynargs = new bytes[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(datasource, dynargs); - } - function oraclize_query(uint timestamp, string datasource, bytes[5] args) oraclizeAPI internal returns (bytes32 id) { - bytes[] memory dynargs = new bytes[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(timestamp, datasource, dynargs); - } - function oraclize_query(uint timestamp, string datasource, bytes[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + + function oraclize_query(string memory _datasource, bytes[5] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(timestamp, datasource, dynargs, gaslimit); - } - function oraclize_query(string datasource, bytes[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_datasource, dynargs); + } + + function oraclize_query(uint _timestamp, string memory _datasource, bytes[5] memory _args) oraclizeAPI internal returns (bytes32 _id) { bytes[] memory dynargs = new bytes[](5); - dynargs[0] = args[0]; - dynargs[1] = args[1]; - dynargs[2] = args[2]; - dynargs[3] = args[3]; - dynargs[4] = args[4]; - return oraclize_query(datasource, dynargs, gaslimit); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_timestamp, _datasource, dynargs); } - function oraclize_cbAddress() oraclizeAPI internal returns (address){ - return oraclize.cbAddress(); + function oraclize_query(uint _timestamp, string memory _datasource, bytes[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + bytes[] memory dynargs = new bytes[](5); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit); } - function oraclize_setProof(byte proofP) oraclizeAPI internal { - return oraclize.setProofType(proofP); + + function oraclize_query(string memory _datasource, bytes[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) { + bytes[] memory dynargs = new bytes[](5); + dynargs[0] = _args[0]; + dynargs[1] = _args[1]; + dynargs[2] = _args[2]; + dynargs[3] = _args[3]; + dynargs[4] = _args[4]; + return oraclize_query(_datasource, dynargs, _gasLimit); } - function oraclize_setCustomGasPrice(uint gasPrice) oraclizeAPI internal { - return oraclize.setCustomGasPrice(gasPrice); + + function oraclize_setProof(byte _proofP) oraclizeAPI internal { + return oraclize.setProofType(_proofP); } - function oraclize_randomDS_getSessionPubKeyHash() oraclizeAPI internal returns (bytes32){ - return oraclize.randomDS_getSessionPubKeyHash(); + + function oraclize_cbAddress() oraclizeAPI internal returns (address _callbackAddress) { + return oraclize.cbAddress(); } - function getCodeSize(address _addr) constant internal returns(uint _size) { + function getCodeSize(address _addr) view internal returns (uint _size) { assembly { _size := extcodesize(_addr) } } - function parseAddr(string _a) internal pure returns (address){ + function oraclize_setCustomGasPrice(uint _gasPrice) oraclizeAPI internal { + return oraclize.setCustomGasPrice(_gasPrice); + } + + function oraclize_randomDS_getSessionPubKeyHash() oraclizeAPI internal returns (bytes32 _sessionKeyHash) { + return oraclize.randomDS_getSessionPubKeyHash(); + } + + function parseAddr(string memory _a) internal pure returns (address _parsedAddress) { bytes memory tmp = bytes(_a); uint160 iaddr = 0; uint160 b1; uint160 b2; - for (uint i=2; i<2+2*20; i+=2){ + for (uint i = 2; i < 2 + 2 * 20; i += 2) { iaddr *= 256; - b1 = uint160(tmp[i]); - b2 = uint160(tmp[i+1]); - if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87; - else if ((b1 >= 65)&&(b1 <= 70)) b1 -= 55; - else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48; - if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87; - else if ((b2 >= 65)&&(b2 <= 70)) b2 -= 55; - else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48; - iaddr += (b1*16+b2); + b1 = uint160(uint8(tmp[i])); + b2 = uint160(uint8(tmp[i + 1])); + if ((b1 >= 97) && (b1 <= 102)) { + b1 -= 87; + } else if ((b1 >= 65) && (b1 <= 70)) { + b1 -= 55; + } else if ((b1 >= 48) && (b1 <= 57)) { + b1 -= 48; + } + if ((b2 >= 97) && (b2 <= 102)) { + b2 -= 87; + } else if ((b2 >= 65) && (b2 <= 70)) { + b2 -= 55; + } else if ((b2 >= 48) && (b2 <= 57)) { + b2 -= 48; + } + iaddr += (b1 * 16 + b2); } return address(iaddr); } - function strCompare(string _a, string _b) internal pure returns (int) { + function strCompare(string memory _a, string memory _b) internal pure returns (int _returnCode) { bytes memory a = bytes(_a); bytes memory b = bytes(_b); uint minLength = a.length; - if (b.length < minLength) minLength = b.length; - for (uint i = 0; i < minLength; i ++) - if (a[i] < b[i]) + if (b.length < minLength) { + minLength = b.length; + } + for (uint i = 0; i < minLength; i ++) { + if (a[i] < b[i]) { return -1; - else if (a[i] > b[i]) + } else if (a[i] > b[i]) { return 1; - if (a.length < b.length) + } + } + if (a.length < b.length) { return -1; - else if (a.length > b.length) + } else if (a.length > b.length) { return 1; - else + } else { return 0; + } } - function indexOf(string _haystack, string _needle) internal pure returns (int) { + function indexOf(string memory _haystack, string memory _needle) internal pure returns (int _returnCode) { bytes memory h = bytes(_haystack); bytes memory n = bytes(_needle); - if(h.length < 1 || n.length < 1 || (n.length > h.length)) + if (h.length < 1 || n.length < 1 || (n.length > h.length)) { return -1; - else if(h.length > (2**128 -1)) + } else if (h.length > (2 ** 128 - 1)) { return -1; - else - { + } else { uint subindex = 0; - for (uint i = 0; i < h.length; i ++) - { - if (h[i] == n[0]) - { + for (uint i = 0; i < h.length; i++) { + if (h[i] == n[0]) { subindex = 1; - while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) - { + while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) { subindex++; } - if(subindex == n.length) + if (subindex == n.length) { return int(i); + } } } return -1; } } - function strConcat(string _a, string _b, string _c, string _d, string _e) internal pure returns (string) { + function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) { + return strConcat(_a, _b, "", "", ""); + } + + function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) { + return strConcat(_a, _b, _c, "", ""); + } + + function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) { + return strConcat(_a, _b, _c, _d, ""); + } + + function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) { bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); bytes memory _bc = bytes(_c); @@ -824,115 +956,141 @@ contract usingOraclize { string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); bytes memory babcde = bytes(abcde); uint k = 0; - for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i]; - for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i]; - for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i]; - for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i]; - for (i = 0; i < _be.length; i++) babcde[k++] = _be[i]; + uint i = 0; + for (i = 0; i < _ba.length; i++) { + babcde[k++] = _ba[i]; + } + for (i = 0; i < _bb.length; i++) { + babcde[k++] = _bb[i]; + } + for (i = 0; i < _bc.length; i++) { + babcde[k++] = _bc[i]; + } + for (i = 0; i < _bd.length; i++) { + babcde[k++] = _bd[i]; + } + for (i = 0; i < _be.length; i++) { + babcde[k++] = _be[i]; + } return string(babcde); } - function strConcat(string _a, string _b, string _c, string _d) internal pure returns (string) { - return strConcat(_a, _b, _c, _d, ""); - } - - function strConcat(string _a, string _b, string _c) internal pure returns (string) { - return strConcat(_a, _b, _c, "", ""); + function safeParseInt(string memory _a) internal pure returns (uint _parsedInt) { + return safeParseInt(_a, 0); } - function strConcat(string _a, string _b) internal pure returns (string) { - return strConcat(_a, _b, "", "", ""); + function safeParseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) { + bytes memory bresult = bytes(_a); + uint mint = 0; + bool decimals = false; + for (uint i = 0; i < bresult.length; i++) { + if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) { + if (decimals) { + if (_b == 0) break; + else _b--; + } + mint *= 10; + mint += uint(uint8(bresult[i])) - 48; + } else if (uint(uint8(bresult[i])) == 46) { + require(!decimals, 'More than one decimal encountered in string!'); + decimals = true; + } else { + revert("Non-numeral character encountered in string!"); + } + } + if (_b > 0) { + mint *= 10 ** _b; + } + return mint; } - // parseInt - function parseInt(string _a) internal pure returns (uint) { + function parseInt(string memory _a) internal pure returns (uint _parsedInt) { return parseInt(_a, 0); } - // parseInt(parseFloat*10^_b) - function parseInt(string _a, uint _b) internal pure returns (uint) { + function parseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) { bytes memory bresult = bytes(_a); uint mint = 0; bool decimals = false; - for (uint i=0; i= 48)&&(bresult[i] <= 57)){ - if (decimals){ - if (_b == 0) break; - else _b--; + for (uint i = 0; i < bresult.length; i++) { + if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) { + if (decimals) { + if (_b == 0) { + break; + } else { + _b--; + } } mint *= 10; - mint += uint(bresult[i]) - 48; - } else if (bresult[i] == 46) decimals = true; + mint += uint(uint8(bresult[i])) - 48; + } else if (uint(uint8(bresult[i])) == 46) { + decimals = true; + } + } + if (_b > 0) { + mint *= 10 ** _b; } - if (_b > 0) mint *= 10**_b; return mint; } - function uint2str(uint i) internal pure returns (string){ - if (i == 0) return "0"; - uint j = i; + function uint2str(uint _i) internal pure returns (string memory _uintAsString) { + if (_i == 0) { + return "0"; + } + uint j = _i; uint len; - while (j != 0){ + while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len - 1; - while (i != 0){ - bstr[k--] = byte(48 + i % 10); - i /= 10; + while (_i != 0) { + bstr[k--] = byte(uint8(48 + _i % 10)); + _i /= 10; } return string(bstr); } - using CBOR for Buffer.buffer; - function stra2cbor(string[] arr) internal pure returns (bytes) { + function stra2cbor(string[] memory _arr) internal pure returns (bytes memory _cborEncoding) { safeMemoryCleaner(); Buffer.buffer memory buf; Buffer.init(buf, 1024); buf.startArray(); - for (uint i = 0; i < arr.length; i++) { - buf.encodeString(arr[i]); + for (uint i = 0; i < _arr.length; i++) { + buf.encodeString(_arr[i]); } buf.endSequence(); return buf.buf; } - function ba2cbor(bytes[] arr) internal pure returns (bytes) { + function ba2cbor(bytes[] memory _arr) internal pure returns (bytes memory _cborEncoding) { safeMemoryCleaner(); Buffer.buffer memory buf; Buffer.init(buf, 1024); buf.startArray(); - for (uint i = 0; i < arr.length; i++) { - buf.encodeBytes(arr[i]); + for (uint i = 0; i < _arr.length; i++) { + buf.encodeBytes(_arr[i]); } buf.endSequence(); return buf.buf; } - string oraclize_network_name; - function oraclize_setNetworkName(string _network_name) internal { - oraclize_network_name = _network_name; - } - - function oraclize_getNetworkName() internal view returns (string) { - return oraclize_network_name; - } - - function oraclize_newRandomDSQuery(uint _delay, uint _nbytes, uint _customGasLimit) internal returns (bytes32){ + function oraclize_newRandomDSQuery(uint _delay, uint _nbytes, uint _customGasLimit) internal returns (bytes32 _queryId) { require((_nbytes > 0) && (_nbytes <= 32)); - // Convert from seconds to ledger timer ticks - _delay *= 10; + _delay *= 10; // Convert from seconds to ledger timer ticks bytes memory nbytes = new bytes(1); - nbytes[0] = byte(_nbytes); + nbytes[0] = byte(uint8(_nbytes)); bytes memory unonce = new bytes(32); bytes memory sessionKeyHash = new bytes(32); bytes32 sessionKeyHash_bytes32 = oraclize_randomDS_getSessionPubKeyHash(); assembly { mstore(unonce, 0x20) - // the following variables can be relaxed - // check relaxed random contract under ethereum-examples repo - // for an idea on how to override and replace comit hash vars + /* + The following variables can be relaxed. + Check the relaxed random contract at https://github.com/oraclize/ethereum-examples + for an idea on how to override and replace commit hash variables. + */ mstore(add(unonce, 0x20), xor(blockhash(sub(number, 1)), xor(coinbase, timestamp))) mstore(sessionKeyHash, 0x20) mstore(add(sessionKeyHash, 0x20), sessionKeyHash_bytes32) @@ -941,15 +1099,11 @@ contract usingOraclize { assembly { mstore(add(delay, 0x20), _delay) } - bytes memory delay_bytes8 = new bytes(8); copyBytes(delay, 24, 8, delay_bytes8, 0); - bytes[4] memory args = [unonce, nbytes, sessionKeyHash, delay]; bytes32 queryId = oraclize_query("random", args, _customGasLimit); - bytes memory delay_bytes8_left = new bytes(8); - assembly { let x := mload(add(delay_bytes8, 0x20)) mstore8(add(delay_bytes8_left, 0x27), div(x, 0x100000000000000000000000000000000000000000000000000000000000000)) @@ -960,248 +1114,214 @@ contract usingOraclize { mstore8(add(delay_bytes8_left, 0x22), div(x, 0x10000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x21), div(x, 0x100000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x20), div(x, 0x1000000000000000000000000000000000000000000000000)) - } - - oraclize_randomDS_setCommitment(queryId, keccak256(delay_bytes8_left, args[1], sha256(args[0]), args[2])); + oraclize_randomDS_setCommitment(queryId, keccak256(abi.encodePacked(delay_bytes8_left, args[1], sha256(args[0]), args[2]))); return queryId; } - function oraclize_randomDS_setCommitment(bytes32 queryId, bytes32 commitment) internal { - oraclize_randomDS_args[queryId] = commitment; + function oraclize_randomDS_setCommitment(bytes32 _queryId, bytes32 _commitment) internal { + oraclize_randomDS_args[_queryId] = _commitment; } - mapping(bytes32=>bytes32) oraclize_randomDS_args; - mapping(bytes32=>bool) oraclize_randomDS_sessionKeysHashVerified; - - function verifySig(bytes32 tosignh, bytes dersig, bytes pubkey) internal returns (bool){ + function verifySig(bytes32 _tosignh, bytes memory _dersig, bytes memory _pubkey) internal returns (bool _sigVerified) { bool sigok; address signer; - bytes32 sigr; bytes32 sigs; - bytes memory sigr_ = new bytes(32); - uint offset = 4+(uint(dersig[3]) - 0x20); - sigr_ = copyBytes(dersig, offset, 32, sigr_, 0); + uint offset = 4 + (uint(uint8(_dersig[3])) - 0x20); + sigr_ = copyBytes(_dersig, offset, 32, sigr_, 0); bytes memory sigs_ = new bytes(32); offset += 32 + 2; - sigs_ = copyBytes(dersig, offset+(uint(dersig[offset-1]) - 0x20), 32, sigs_, 0); - + sigs_ = copyBytes(_dersig, offset + (uint(uint8(_dersig[offset - 1])) - 0x20), 32, sigs_, 0); assembly { sigr := mload(add(sigr_, 32)) sigs := mload(add(sigs_, 32)) } - - - (sigok, signer) = safer_ecrecover(tosignh, 27, sigr, sigs); - if (address(keccak256(pubkey)) == signer) return true; - else { - (sigok, signer) = safer_ecrecover(tosignh, 28, sigr, sigs); - return (address(keccak256(pubkey)) == signer); + (sigok, signer) = safer_ecrecover(_tosignh, 27, sigr, sigs); + if (address(uint160(uint256(keccak256(_pubkey)))) == signer) { + return true; + } else { + (sigok, signer) = safer_ecrecover(_tosignh, 28, sigr, sigs); + return (address(uint160(uint256(keccak256(_pubkey)))) == signer); } } - function oraclize_randomDS_proofVerify__sessionKeyValidity(bytes proof, uint sig2offset) internal returns (bool) { + function oraclize_randomDS_proofVerify__sessionKeyValidity(bytes memory _proof, uint _sig2offset) internal returns (bool _proofVerified) { bool sigok; - - // Step 6: verify the attestation signature, APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) - bytes memory sig2 = new bytes(uint(proof[sig2offset+1])+2); - copyBytes(proof, sig2offset, sig2.length, sig2, 0); - + // Random DS Proof Step 6: Verify the attestation signature, APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) + bytes memory sig2 = new bytes(uint(uint8(_proof[_sig2offset + 1])) + 2); + copyBytes(_proof, _sig2offset, sig2.length, sig2, 0); bytes memory appkey1_pubkey = new bytes(64); - copyBytes(proof, 3+1, 64, appkey1_pubkey, 0); - - bytes memory tosign2 = new bytes(1+65+32); - tosign2[0] = byte(1); //role - copyBytes(proof, sig2offset-65, 65, tosign2, 1); + copyBytes(_proof, 3 + 1, 64, appkey1_pubkey, 0); + bytes memory tosign2 = new bytes(1 + 65 + 32); + tosign2[0] = byte(uint8(1)); //role + copyBytes(_proof, _sig2offset - 65, 65, tosign2, 1); bytes memory CODEHASH = hex"fd94fa71bc0ba10d39d464d0d8f465efeef0a2764e3887fcc9df41ded20f505c"; - copyBytes(CODEHASH, 0, 32, tosign2, 1+65); + copyBytes(CODEHASH, 0, 32, tosign2, 1 + 65); sigok = verifySig(sha256(tosign2), sig2, appkey1_pubkey); - - if (sigok == false) return false; - - - // Step 7: verify the APPKEY1 provenance (must be signed by Ledger) + if (!sigok) { + return false; + } + // Random DS Proof Step 7: Verify the APPKEY1 provenance (must be signed by Ledger) bytes memory LEDGERKEY = hex"7fb956469c5c9b89840d55b43537e66a98dd4811ea0a27224272c2e5622911e8537a2f8e86a46baec82864e98dd01e9ccc2f8bc5dfc9cbe5a91a290498dd96e4"; - - bytes memory tosign3 = new bytes(1+65); + bytes memory tosign3 = new bytes(1 + 65); tosign3[0] = 0xFE; - copyBytes(proof, 3, 65, tosign3, 1); - - bytes memory sig3 = new bytes(uint(proof[3+65+1])+2); - copyBytes(proof, 3+65, sig3.length, sig3, 0); - + copyBytes(_proof, 3, 65, tosign3, 1); + bytes memory sig3 = new bytes(uint(uint8(_proof[3 + 65 + 1])) + 2); + copyBytes(_proof, 3 + 65, sig3.length, sig3, 0); sigok = verifySig(sha256(tosign3), sig3, LEDGERKEY); - return sigok; } - modifier oraclize_randomDS_proofVerify(bytes32 _queryId, string _result, bytes _proof) { - // Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) - require((_proof[0] == "L") && (_proof[1] == "P") && (_proof[2] == 1)); - - bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName()); - require(proofVerified); - - _; - } - - function oraclize_randomDS_proofVerify__returnCode(bytes32 _queryId, string _result, bytes _proof) internal returns (uint8){ - // Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) - if ((_proof[0] != "L")||(_proof[1] != "P")||(_proof[2] != 1)) return 1; - + function oraclize_randomDS_proofVerify__returnCode(bytes32 _queryId, string memory _result, bytes memory _proof) internal returns (uint8 _returnCode) { + // Random DS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1) + if ((_proof[0] != "L") || (_proof[1] != "P") || (uint8(_proof[2]) != uint8(1))) { + return 1; + } bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName()); - if (proofVerified == false) return 2; - + if (!proofVerified) { + return 2; + } return 0; } - function matchBytes32Prefix(bytes32 content, bytes prefix, uint n_random_bytes) internal pure returns (bool){ + function matchBytes32Prefix(bytes32 _content, bytes memory _prefix, uint _nRandomBytes) internal pure returns (bool _matchesPrefix) { bool match_ = true; - - require(prefix.length == n_random_bytes); - - for (uint256 i=0; i< n_random_bytes; i++) { - if (content[i] != prefix[i]) match_ = false; + require(_prefix.length == _nRandomBytes); + for (uint256 i = 0; i< _nRandomBytes; i++) { + if (_content[i] != _prefix[i]) { + match_ = false; + } } - return match_; } - function oraclize_randomDS_proofVerify__main(bytes proof, bytes32 queryId, bytes result, string context_name) internal returns (bool){ - - // Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) - uint ledgerProofLength = 3+65+(uint(proof[3+65+1])+2)+32; + function oraclize_randomDS_proofVerify__main(bytes memory _proof, bytes32 _queryId, bytes memory _result, string memory _contextName) internal returns (bool _proofVerified) { + // Random DS Proof Step 2: The unique keyhash has to match with the sha256 of (context name + _queryId) + uint ledgerProofLength = 3 + 65 + (uint(uint8(_proof[3 + 65 + 1])) + 2) + 32; bytes memory keyhash = new bytes(32); - copyBytes(proof, ledgerProofLength, 32, keyhash, 0); - if (!(keccak256(keyhash) == keccak256(sha256(context_name, queryId)))) return false; - - bytes memory sig1 = new bytes(uint(proof[ledgerProofLength+(32+8+1+32)+1])+2); - copyBytes(proof, ledgerProofLength+(32+8+1+32), sig1.length, sig1, 0); - - // Step 3: we assume sig1 is valid (it will be verified during step 5) and we verify if 'result' is the prefix of sha256(sig1) - if (!matchBytes32Prefix(sha256(sig1), result, uint(proof[ledgerProofLength+32+8]))) return false; - - // Step 4: commitment match verification, keccak256(delay, nbytes, unonce, sessionKeyHash) == commitment in storage. + copyBytes(_proof, ledgerProofLength, 32, keyhash, 0); + if (!(keccak256(keyhash) == keccak256(abi.encodePacked(sha256(abi.encodePacked(_contextName, _queryId)))))) { + return false; + } + bytes memory sig1 = new bytes(uint(uint8(_proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1])) + 2); + copyBytes(_proof, ledgerProofLength + (32 + 8 + 1 + 32), sig1.length, sig1, 0); + // Random DS Proof Step 3: We assume sig1 is valid (it will be verified during step 5) and we verify if '_result' is the _prefix of sha256(sig1) + if (!matchBytes32Prefix(sha256(sig1), _result, uint(uint8(_proof[ledgerProofLength + 32 + 8])))) { + return false; + } + // Random DS Proof Step 4: Commitment match verification, keccak256(delay, nbytes, unonce, sessionKeyHash) == commitment in storage. // This is to verify that the computed args match with the ones specified in the query. - bytes memory commitmentSlice1 = new bytes(8+1+32); - copyBytes(proof, ledgerProofLength+32, 8+1+32, commitmentSlice1, 0); - + bytes memory commitmentSlice1 = new bytes(8 + 1 + 32); + copyBytes(_proof, ledgerProofLength + 32, 8 + 1 + 32, commitmentSlice1, 0); bytes memory sessionPubkey = new bytes(64); - uint sig2offset = ledgerProofLength+32+(8+1+32)+sig1.length+65; - copyBytes(proof, sig2offset-64, 64, sessionPubkey, 0); - + uint sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1.length + 65; + copyBytes(_proof, sig2offset - 64, 64, sessionPubkey, 0); bytes32 sessionPubkeyHash = sha256(sessionPubkey); - if (oraclize_randomDS_args[queryId] == keccak256(commitmentSlice1, sessionPubkeyHash)){ //unonce, nbytes and sessionKeyHash match - delete oraclize_randomDS_args[queryId]; + if (oraclize_randomDS_args[_queryId] == keccak256(abi.encodePacked(commitmentSlice1, sessionPubkeyHash))) { //unonce, nbytes and sessionKeyHash match + delete oraclize_randomDS_args[_queryId]; } else return false; - - - // Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) - bytes memory tosign1 = new bytes(32+8+1+32); - copyBytes(proof, ledgerProofLength, 32+8+1+32, tosign1, 0); - if (!verifySig(sha256(tosign1), sig1, sessionPubkey)) return false; - - // verify if sessionPubkeyHash was verified already, if not.. let's do it! - if (oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] == false){ - oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] = oraclize_randomDS_proofVerify__sessionKeyValidity(proof, sig2offset); + // Random DS Proof Step 5: Validity verification for sig1 (keyhash and args signed with the sessionKey) + bytes memory tosign1 = new bytes(32 + 8 + 1 + 32); + copyBytes(_proof, ledgerProofLength, 32 + 8 + 1 + 32, tosign1, 0); + if (!verifySig(sha256(tosign1), sig1, sessionPubkey)) { + return false; + } + // Verify if sessionPubkeyHash was verified already, if not.. let's do it! + if (!oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash]) { + oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] = oraclize_randomDS_proofVerify__sessionKeyValidity(_proof, sig2offset); } - return oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash]; } - - // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license - function copyBytes(bytes from, uint fromOffset, uint length, bytes to, uint toOffset) internal pure returns (bytes) { - uint minLength = length + toOffset; - - // Buffer too small - require(to.length >= minLength); // Should be a better way? - - // NOTE: the offset 32 is added to skip the `size` field of both bytes variables - uint i = 32 + fromOffset; - uint j = 32 + toOffset; - - while (i < (32 + fromOffset + length)) { + /* + The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license + */ + function copyBytes(bytes memory _from, uint _fromOffset, uint _length, bytes memory _to, uint _toOffset) internal pure returns (bytes memory _copiedBytes) { + uint minLength = _length + _toOffset; + require(_to.length >= minLength); // Buffer too small. Should be a better way? + uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables + uint j = 32 + _toOffset; + while (i < (32 + _fromOffset + _length)) { assembly { - let tmp := mload(add(from, i)) - mstore(add(to, j), tmp) + let tmp := mload(add(_from, i)) + mstore(add(_to, j), tmp) } i += 32; j += 32; } - - return to; - } - - // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license - // Duplicate Solidity's ecrecover, but catching the CALL return value - function safer_ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address) { - // We do our own memory management here. Solidity uses memory offset - // 0x40 to store the current end of memory. We write past it (as - // writes are memory extensions), but don't update the offset so - // Solidity will reuse it. The memory used here is only needed for - // this context. - - // FIXME: inline assembly can't access return values + return _to; + } + /* + The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license + Duplicate Solidity's ecrecover, but catching the CALL return value + */ + function safer_ecrecover(bytes32 _hash, uint8 _v, bytes32 _r, bytes32 _s) internal returns (bool _success, address _recoveredAddress) { + /* + We do our own memory management here. Solidity uses memory offset + 0x40 to store the current end of memory. We write past it (as + writes are memory extensions), but don't update the offset so + Solidity will reuse it. The memory used here is only needed for + this context. + FIXME: inline assembly can't access return values + */ bool ret; address addr; - assembly { let size := mload(0x40) - mstore(size, hash) - mstore(add(size, 32), v) - mstore(add(size, 64), r) - mstore(add(size, 96), s) - - // NOTE: we can reuse the request memory because we deal with - // the return code - ret := call(3000, 1, 0, size, 128, size, 32) + mstore(size, _hash) + mstore(add(size, 32), _v) + mstore(add(size, 64), _r) + mstore(add(size, 96), _s) + ret := call(3000, 1, 0, size, 128, size, 32) // NOTE: we can reuse the request memory because we deal with the return code. addr := mload(size) } - return (ret, addr); } - - // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license - function ecrecovery(bytes32 hash, bytes sig) internal returns (bool, address) { + /* + The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license + */ + function ecrecovery(bytes32 _hash, bytes memory _sig) internal returns (bool _success, address _recoveredAddress) { bytes32 r; bytes32 s; uint8 v; - - if (sig.length != 65) - return (false, 0); - - // The signature format is a compact form of: - // {bytes32 r}{bytes32 s}{uint8 v} - // Compact means, uint8 is not padded to 32 bytes. + if (_sig.length != 65) { + return (false, address(0)); + } + /* + The signature format is a compact form of: + {bytes32 r}{bytes32 s}{uint8 v} + Compact means, uint8 is not padded to 32 bytes. + */ assembly { - r := mload(add(sig, 32)) - s := mload(add(sig, 64)) - - // Here we are loading the last 32 bytes. We exploit the fact that - // 'mload' will pad with zeroes if we overread. - // There is no 'mload8' to do this, but that would be nicer. - v := byte(0, mload(add(sig, 96))) - - // Alternative solution: - // 'byte' is not working due to the Solidity parser, so lets - // use the second best option, 'and' - // v := and(mload(add(sig, 65)), 255) + r := mload(add(_sig, 32)) + s := mload(add(_sig, 64)) + /* + Here we are loading the last 32 bytes. We exploit the fact that + 'mload' will pad with zeroes if we overread. + There is no 'mload8' to do this, but that would be nicer. + */ + v := byte(0, mload(add(_sig, 96))) + /* + Alternative solution: + 'byte' is not working due to the Solidity parser, so lets + use the second best option, 'and' + v := and(mload(add(_sig, 65)), 255) + */ } - - // albeit non-transactional signatures are not specified by the YP, one would expect it - // to match the YP range of [27, 28] - // - // geth uses [0, 1] and some clients have followed. This might change, see: - // https://github.com/ethereum/go-ethereum/issues/2053 - if (v < 27) - v += 27; - - if (v != 27 && v != 28) - return (false, 0); - - return safer_ecrecover(hash, v, r, s); + /* + albeit non-transactional signatures are not specified by the YP, one would expect it + to match the YP range of [27, 28] + geth uses [0, 1] and some clients have followed. This might change, see: + https://github.com/ethereum/go-ethereum/issues/2053 + */ + if (v < 27) { + v += 27; + } + if (v != 27 && v != 28) { + return (false, address(0)); + } + return safer_ecrecover(_hash, v, r, s); } function safeMemoryCleaner() internal pure { @@ -1210,5 +1330,10 @@ contract usingOraclize { codecopy(fmem, codesize, sub(msize, fmem)) } } - } +/* + +END ORACLIZE_API + +*/ + diff --git a/contracts/helpers/PolyToken.sol b/contracts/helpers/PolyToken.sol index d8f3d48ec..5230276e3 100644 --- a/contracts/helpers/PolyToken.sol +++ b/contracts/helpers/PolyToken.sol @@ -1,6 +1,6 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "../interfaces/IERC20.sol"; +import "../interfaces/IPoly.sol"; /* Copyright (c) 2016 Smart Contract Solutions, Inc. @@ -30,7 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @dev Math operations with safety checks that throw on error */ library SafeMath { - function mul(uint256 a, uint256 b) internal pure returns (uint256) { + function mul(uint256 a, uint256 b) internal pure returns(uint256) { if (a == 0) { return 0; } @@ -39,32 +39,33 @@ library SafeMath { return c; } - function div(uint256 a, uint256 b) internal pure returns (uint256) { + function div(uint256 a, uint256 b) internal pure returns(uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } - function sub(uint256 a, uint256 b) internal pure returns (uint256) { + function sub(uint256 a, uint256 b) internal pure returns(uint256) { assert(b <= a); return a - b; } - function add(uint256 a, uint256 b) internal pure returns (uint256) { + function add(uint256 a, uint256 b) internal pure returns(uint256) { uint256 c = a + b; assert(c >= a); return c; } } + /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * @dev https://github.com/ethereum/EIPs/issues/20 */ -contract PolyToken is IERC20 { +contract PolyToken is IPoly { using SafeMath for uint256; // Poly Token parameters @@ -73,8 +74,8 @@ contract PolyToken is IERC20 { uint8 public constant decimals = 18; uint256 public constant decimalFactor = 10 ** uint256(decimals); uint256 public constant totalSupply = 1000000000 * decimalFactor; - mapping (address => uint256) balances; - mapping (address => mapping (address => uint256)) internal allowed; + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) internal allowed; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); @@ -83,7 +84,7 @@ contract PolyToken is IERC20 { * @dev Constructor for Poly creation * @dev Assigns the totalSupply to the PolyDistribution contract */ - constructor (address _polyDistributionContractAddress) public { + constructor(address _polyDistributionContractAddress) public { require(_polyDistributionContractAddress != address(0), "Invalid address"); balances[_polyDistributionContractAddress] = totalSupply; emit Transfer(address(0), _polyDistributionContractAddress, totalSupply); @@ -94,7 +95,7 @@ contract PolyToken is IERC20 { * @param _owner The address to query the the balance of * @return An uint256 representing the amount owned by the passed address */ - function balanceOf(address _owner) public view returns (uint256 balance) { + function balanceOf(address _owner) public view returns(uint256 balance) { return balances[_owner]; } @@ -104,7 +105,7 @@ contract PolyToken is IERC20 { * @param _spender address The address which will spend the tokens * @return A uint256 specifying the amount of tokens left available for the spender */ - function allowance(address _owner, address _spender) public view returns (uint256) { + function allowance(address _owner, address _spender) public view returns(uint256) { return allowed[_owner][_spender]; } @@ -113,7 +114,7 @@ contract PolyToken is IERC20 { * @param _to The address to transfer tokens to * @param _value The amount to be transferred */ - function transfer(address _to, uint256 _value) public returns (bool) { + function transfer(address _to, uint256 _value) public returns(bool) { require(_to != address(0), "Invalid address"); require(_value <= balances[msg.sender], "Insufficient tokens transferable"); @@ -130,7 +131,7 @@ contract PolyToken is IERC20 { * @param _to address The address to transfer tokens to * @param _value uint256 The amount of tokens to be transferred */ - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + function transferFrom(address _from, address _to, uint256 _value) public returns(bool) { require(_to != address(0), "Invalid address"); require(_value <= balances[_from], "Insufficient tokens transferable"); require(_value <= allowed[_from][msg.sender], "Insufficient tokens allowable"); @@ -152,7 +153,7 @@ contract PolyToken is IERC20 { * @param _spender The address which will spend the funds * @param _value The amount of tokens to be spent */ - function approve(address _spender, uint256 _value) public returns (bool) { + function approve(address _spender, uint256 _value) public returns(bool) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; @@ -168,7 +169,7 @@ contract PolyToken is IERC20 { * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ - function increaseApproval(address _spender, uint _addedValue) public returns (bool) { + function increaseApproval(address _spender, uint _addedValue) public returns(bool) { allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; @@ -184,7 +185,7 @@ contract PolyToken is IERC20 { * @param _spender The address which will spend the funds * @param _subtractedValue The amount of tokens to decrease the allowance by */ - function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) { + function decreaseApproval(address _spender, uint _subtractedValue) public returns(bool) { uint oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; diff --git a/contracts/interfaces/IBoot.sol b/contracts/interfaces/IBoot.sol new file mode 100644 index 000000000..34d3c3fc1 --- /dev/null +++ b/contracts/interfaces/IBoot.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.5.0; + +interface IBoot { + /** + * @notice This function returns the signature of configure function + * @return bytes4 Configure function signature + */ + function getInitFunction() external pure returns(bytes4); + +} \ No newline at end of file diff --git a/contracts/interfaces/ICheckPermission.sol b/contracts/interfaces/ICheckPermission.sol new file mode 100644 index 000000000..2e14fa5b9 --- /dev/null +++ b/contracts/interfaces/ICheckPermission.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.5.0; + +interface ICheckPermission { + /** + * @notice Validate permissions with PermissionManager if it exists, If no Permission return false + * @dev Note that IModule withPerm will allow ST owner all permissions anyway + * @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions) + * @param _delegate address of delegate + * @param _module address of PermissionManager module + * @param _perm the permissions + * @return success + */ + function checkPermission(address _delegate, address _module, bytes32 _perm) external view returns(bool); +} diff --git a/contracts/interfaces/IDataStore.sol b/contracts/interfaces/IDataStore.sol new file mode 100644 index 000000000..33df6b934 --- /dev/null +++ b/contracts/interfaces/IDataStore.sol @@ -0,0 +1,136 @@ +pragma solidity ^0.5.0; + +interface IDataStore { + /** + * @dev Changes security token atatched to this data store + * @param _securityToken address of the security token + */ + function setSecurityToken(address _securityToken) external; + + /** + * @dev Stores a uint256 data against a key + * @param _key Unique key to identify the data + * @param _data Data to be stored against the key + */ + function setUint256(bytes32 _key, uint256 _data) external; + + function setBytes32(bytes32 _key, bytes32 _data) external; + + function setAddress(bytes32 _key, address _data) external; + + function setString(bytes32 _key, string calldata _data) external; + + function setBytes(bytes32 _key, bytes calldata _data) external; + + function setBool(bytes32 _key, bool _data) external; + + /** + * @dev Stores a uint256 array against a key + * @param _key Unique key to identify the array + * @param _data Array to be stored against the key + */ + function setUint256Array(bytes32 _key, uint256[] calldata _data) external; + + function setBytes32Array(bytes32 _key, bytes32[] calldata _data) external ; + + function setAddressArray(bytes32 _key, address[] calldata _data) external; + + function setBoolArray(bytes32 _key, bool[] calldata _data) external; + + /** + * @dev Inserts a uint256 element to the array identified by the key + * @param _key Unique key to identify the array + * @param _data Element to push into the array + */ + function insertUint256(bytes32 _key, uint256 _data) external; + + function insertBytes32(bytes32 _key, bytes32 _data) external; + + function insertAddress(bytes32 _key, address _data) external; + + function insertBool(bytes32 _key, bool _data) external; + + /** + * @dev Deletes an element from the array identified by the key. + * When an element is deleted from an Array, last element of that array is moved to the index of deleted element. + * @param _key Unique key to identify the array + * @param _index Index of the element to delete + */ + function deleteUint256(bytes32 _key, uint256 _index) external; + + function deleteBytes32(bytes32 _key, uint256 _index) external; + + function deleteAddress(bytes32 _key, uint256 _index) external; + + function deleteBool(bytes32 _key, uint256 _index) external; + + /** + * @dev Stores multiple uint256 data against respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be stored against the respective keys + */ + function setUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external; + + function setBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external; + + function setAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external; + + function setBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external; + + /** + * @dev Inserts multiple uint256 elements to the array identified by the respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be inserted in arrays of the respective keys + */ + function insertUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external; + + function insertBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external; + + function insertAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external; + + function insertBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external; + + function getUint256(bytes32 _key) external view returns(uint256); + + function getBytes32(bytes32 _key) external view returns(bytes32); + + function getAddress(bytes32 _key) external view returns(address); + + function getString(bytes32 _key) external view returns(string memory); + + function getBytes(bytes32 _key) external view returns(bytes memory); + + function getBool(bytes32 _key) external view returns(bool); + + function getUint256Array(bytes32 _key) external view returns(uint256[] memory); + + function getBytes32Array(bytes32 _key) external view returns(bytes32[] memory); + + function getAddressArray(bytes32 _key) external view returns(address[] memory); + + function getBoolArray(bytes32 _key) external view returns(bool[] memory); + + function getUint256ArrayLength(bytes32 _key) external view returns(uint256); + + function getBytes32ArrayLength(bytes32 _key) external view returns(uint256); + + function getAddressArrayLength(bytes32 _key) external view returns(uint256); + + function getBoolArrayLength(bytes32 _key) external view returns(uint256); + + function getUint256ArrayElement(bytes32 _key, uint256 _index) external view returns(uint256); + + function getBytes32ArrayElement(bytes32 _key, uint256 _index) external view returns(bytes32); + + function getAddressArrayElement(bytes32 _key, uint256 _index) external view returns(address); + + function getBoolArrayElement(bytes32 _key, uint256 _index) external view returns(bool); + + function getUint256ArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(uint256[] memory); + + function getBytes32ArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(bytes32[] memory); + + function getAddressArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(address[] memory); + + function getBoolArrayElements(bytes32 _key, uint256 _startIndex, uint256 _endIndex) external view returns(bool[] memory); +} diff --git a/contracts/interfaces/IFeatureRegistry.sol b/contracts/interfaces/IFeatureRegistry.sol index 574f412a5..563a1a9b8 100644 --- a/contracts/interfaces/IFeatureRegistry.sol +++ b/contracts/interfaces/IFeatureRegistry.sol @@ -1,15 +1,14 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for managing polymath feature switches */ interface IFeatureRegistry { - /** * @notice Get the status of a feature * @param _nameKey is the key for the feature status mapping * @return bool */ - function getFeatureStatus(string _nameKey) external view returns(bool); + function getFeatureStatus(string calldata _nameKey) external view returns(bool); } diff --git a/contracts/interfaces/IModule.sol b/contracts/interfaces/IModule.sol index bc5140ae2..781ae2a65 100644 --- a/contracts/interfaces/IModule.sol +++ b/contracts/interfaces/IModule.sol @@ -1,19 +1,18 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface that every module contract should implement */ interface IModule { - /** * @notice This function returns the signature of configure function */ - function getInitFunction() external pure returns (bytes4); + function getInitFunction() external pure returns(bytes4); /** * @notice Return the permission flags that are associated with a module */ - function getPermissions() external view returns(bytes32[]); + function getPermissions() external view returns(bytes32[] memory); /** * @notice Used to withdraw the fee by the factory owner diff --git a/contracts/interfaces/IModuleFactory.sol b/contracts/interfaces/IModuleFactory.sol index 9c2ad6bf2..8a988c3a3 100644 --- a/contracts/interfaces/IModuleFactory.sol +++ b/contracts/interfaces/IModuleFactory.sol @@ -1,30 +1,29 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface that every module factory contract should implement */ interface IModuleFactory { - - event ChangeFactorySetupFee(uint256 _oldSetupCost, uint256 _newSetupCost, address _moduleFactory); - event ChangeFactoryUsageFee(uint256 _oldUsageCost, uint256 _newUsageCost, address _moduleFactory); - event ChangeFactorySubscriptionFee(uint256 _oldSubscriptionCost, uint256 _newMonthlySubscriptionCost, address _moduleFactory); + event ChangeSetupCost(uint256 _oldSetupCost, uint256 _newSetupCost); + event ChangeUsageCost(uint256 _oldUsageCost, uint256 _newUsageCost); event GenerateModuleFromFactory( address _module, bytes32 indexed _moduleName, address indexed _moduleFactory, address _creator, uint256 _setupCost, + uint256 _setupCostInPoly, uint256 _timestamp ); event ChangeSTVersionBound(string _boundType, uint8 _major, uint8 _minor, uint8 _patch); //Should create an instance of the Module, or throw - function deploy(bytes _data) external returns(address); + function deploy(bytes calldata _data) external returns(address); /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]); + function getTypes() external view returns(uint8[] memory); /** * @notice Get the name of the Module @@ -34,53 +33,52 @@ interface IModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns (string); + function getInstructions() external view returns(string memory); /** * @notice Get the tags related to the module factory */ - function getTags() external view returns (bytes32[]); + function getTags() external view returns(bytes32[] memory); /** * @notice Used to change the setup fee * @param _newSetupCost New setup fee */ - function changeFactorySetupFee(uint256 _newSetupCost) external; + function changeSetupCost(uint256 _newSetupCost) external; /** * @notice Used to change the usage fee * @param _newUsageCost New usage fee */ - function changeFactoryUsageFee(uint256 _newUsageCost) external; - - /** - * @notice Used to change the subscription fee - * @param _newSubscriptionCost New subscription fee - */ - function changeFactorySubscriptionFee(uint256 _newSubscriptionCost) external; + function changeUsageCost(uint256 _newUsageCost) external; /** * @notice Function use to change the lower and upper bound of the compatible version st * @param _boundType Type of bound * @param _newVersion New version array */ - function changeSTVersionBounds(string _boundType, uint8[] _newVersion) external; + function changeSTVersionBounds(string calldata _boundType, uint8[] calldata _newVersion) external; - /** + /** + * @notice Get the setup cost of the module in USD + */ + function getSetupCost() external view returns(uint256); + + /** * @notice Get the setup cost of the module */ - function getSetupCost() external view returns (uint256); + function getSetupCostInPoly() external returns (uint256); /** * @notice Used to get the lower bound * @return Lower bound */ - function getLowerSTVersionBounds() external view returns(uint8[]); + function getLowerSTVersionBounds() external view returns(uint8[] memory); - /** + /** * @notice Used to get the upper bound * @return Upper bound */ - function getUpperSTVersionBounds() external view returns(uint8[]); + function getUpperSTVersionBounds() external view returns(uint8[] memory); } diff --git a/contracts/interfaces/IModuleRegistry.sol b/contracts/interfaces/IModuleRegistry.sol index 0ca6044e4..0d4af763e 100644 --- a/contracts/interfaces/IModuleRegistry.sol +++ b/contracts/interfaces/IModuleRegistry.sol @@ -1,10 +1,9 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for the Polymath Module Registry contract */ interface IModuleRegistry { - /** * @notice Called by a security token to notify the registry it is using a module * @param _moduleFactory is the address of the relevant module factory @@ -36,7 +35,7 @@ interface IModuleRegistry { * @param _factoryAddress address of the Module Factory * @return address array which has the list of securityToken's uses that module factory */ - function getReputationByFactory(address _factoryAddress) external view returns(address[]); + function getReputationByFactory(address _factoryAddress) external view returns(address[] memory); /** * @notice Returns all the tags related to the a module type which are valid for the given token @@ -45,7 +44,7 @@ interface IModuleRegistry { * @return list of tags * @return corresponding list of module factories */ - function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[], address[]); + function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[] memory, address[] memory); /** * @notice Returns all the tags related to the a module type which are valid for the given token @@ -53,14 +52,14 @@ interface IModuleRegistry { * @return list of tags * @return corresponding list of module factories */ - function getTagsByType(uint8 _moduleType) external view returns(bytes32[], address[]); + function getTagsByType(uint8 _moduleType) external view returns(bytes32[] memory, address[] memory); /** * @notice Returns the list of addresses of Module Factory of a particular type * @param _moduleType Type of Module * @return address array that contains the list of addresses of module factory contracts. */ - function getModulesByType(uint8 _moduleType) external view returns(address[]); + function getModulesByType(uint8 _moduleType) external view returns(address[] memory); /** * @notice Returns the list of available Module factory addresses of a particular type for a given token. @@ -68,7 +67,7 @@ interface IModuleRegistry { * @param _securityToken is the address of SecurityToken * @return address array that contains the list of available addresses of module factory contracts. */ - function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns (address[]); + function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(address[] memory); /** * @notice Use to get the latest contract address of the regstries diff --git a/contracts/interfaces/IOracle.sol b/contracts/interfaces/IOracle.sol index 483a22cd0..704814a5a 100644 --- a/contracts/interfaces/IOracle.sol +++ b/contracts/interfaces/IOracle.sol @@ -1,7 +1,6 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; interface IOracle { - /** * @notice Returns address of oracle currency (0x0 for ETH) */ @@ -20,6 +19,6 @@ interface IOracle { /** * @notice Returns price - should throw if not valid */ - function getPrice() external view returns(uint256); + function getPrice() external returns(uint256); } diff --git a/contracts/interfaces/IOwnable.sol b/contracts/interfaces/IOwnable.sol index e4f427bd8..9f36abea6 100644 --- a/contracts/interfaces/IOwnable.sol +++ b/contracts/interfaces/IOwnable.sol @@ -1,5 +1,4 @@ -pragma solidity ^0.4.24; - +pragma solidity ^0.5.0; /** * @title Ownable @@ -10,7 +9,7 @@ interface IOwnable { /** * @dev Returns owner */ - function owner() external view returns (address); + function owner() external view returns(address); /** * @dev Allows the current owner to relinquish control of the contract. diff --git a/contracts/interfaces/IERC20.sol b/contracts/interfaces/IPoly.sol similarity index 62% rename from contracts/interfaces/IERC20.sol rename to contracts/interfaces/IPoly.sol index b1d36463f..0de0cc753 100644 --- a/contracts/interfaces/IERC20.sol +++ b/contracts/interfaces/IPoly.sol @@ -1,19 +1,19 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ -interface IERC20 { - function decimals() external view returns (uint8); - function totalSupply() external view returns (uint256); - function balanceOf(address _owner) external view returns (uint256); - function allowance(address _owner, address _spender) external view returns (uint256); - function transfer(address _to, uint256 _value) external returns (bool); - function transferFrom(address _from, address _to, uint256 _value) external returns (bool); - function approve(address _spender, uint256 _value) external returns (bool); - function decreaseApproval(address _spender, uint _subtractedValue) external returns (bool); - function increaseApproval(address _spender, uint _addedValue) external returns (bool); +interface IPoly { + function decimals() external view returns(uint8); + function totalSupply() external view returns(uint256); + function balanceOf(address _owner) external view returns(uint256); + function allowance(address _owner, address _spender) external view returns(uint256); + function transfer(address _to, uint256 _value) external returns(bool); + function transferFrom(address _from, address _to, uint256 _value) external returns(bool); + function approve(address _spender, uint256 _value) external returns(bool); + function decreaseApproval(address _spender, uint _subtractedValue) external returns(bool); + function increaseApproval(address _spender, uint _addedValue) external returns(bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } diff --git a/contracts/interfaces/IPolymathRegistry.sol b/contracts/interfaces/IPolymathRegistry.sol index 4601253fa..91a057d54 100644 --- a/contracts/interfaces/IPolymathRegistry.sol +++ b/contracts/interfaces/IPolymathRegistry.sol @@ -1,14 +1,11 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; - interface IPolymathRegistry { - /** * @notice Returns the contract address * @param _nameKey is the key for the contract address mapping * @return address */ - function getAddress(string _nameKey) external view returns(address); + function getAddress(string calldata _nameKey) external view returns(address); } - \ No newline at end of file diff --git a/contracts/interfaces/ISTFactory.sol b/contracts/interfaces/ISTFactory.sol index 7b7d6dd77..e4e2e4dc5 100644 --- a/contracts/interfaces/ISTFactory.sol +++ b/contracts/interfaces/ISTFactory.sol @@ -1,10 +1,9 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for security token proxy deployment */ interface ISTFactory { - /** * @notice Deploys the token and adds default modules like permission manager and transfer manager. * Future versions of the proxy can attach different modules or pass some other paramters. @@ -17,14 +16,14 @@ interface ISTFactory { * @param _polymathRegistry is the address of the Polymath Registry contract */ function deployToken( - string _name, - string _symbol, + string calldata _name, + string calldata _symbol, uint8 _decimals, - string _tokenDetails, + string calldata _tokenDetails, address _issuer, bool _divisible, address _polymathRegistry - ) - external - returns (address); + ) + external + returns(address); } diff --git a/contracts/interfaces/ISTO.sol b/contracts/interfaces/ISTO.sol new file mode 100644 index 000000000..248274e39 --- /dev/null +++ b/contracts/interfaces/ISTO.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.5.0; + +/** + * @title Interface to be implemented by all STO modules + */ +interface ISTO { + /** + * @notice Returns the total no. of tokens sold + */ + function getTokensSold() external view returns(uint256); + +} diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index f280d6c19..7e9929908 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -1,33 +1,106 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for all security tokens */ interface ISecurityToken { - // Standard ERC20 interface - function decimals() external view returns (uint8); - function totalSupply() external view returns (uint256); - function balanceOf(address _owner) external view returns (uint256); - function allowance(address _owner, address _spender) external view returns (uint256); - function transfer(address _to, uint256 _value) external returns (bool); - function transferFrom(address _from, address _to, uint256 _value) external returns (bool); - function approve(address _spender, uint256 _value) external returns (bool); - function decreaseApproval(address _spender, uint _subtractedValue) external returns (bool); - function increaseApproval(address _spender, uint _addedValue) external returns (bool); + function decimals() external view returns(uint8); + function totalSupply() external view returns(uint256); + function balanceOf(address _owner) external view returns(uint256); + function allowance(address _owner, address _spender) external view returns(uint256); + function transfer(address _to, uint256 _value) external returns(bool); + function transferFrom(address _from, address _to, uint256 _value) external returns(bool); + function approve(address _spender, uint256 _value) external returns(bool); + function decreaseApproval(address _spender, uint _subtractedValue) external returns(bool); + function increaseApproval(address _spender, uint _addedValue) external returns(bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); - //transfer, transferFrom must respect the result of verifyTransfer - function verifyTransfer(address _from, address _to, uint256 _value) external returns (bool success); + /** + * @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the + * cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped + * with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * @return bool It signifies whether the transaction will be executed or not. + * @return byte Ethereum status code (ESC) + * @return bytes32 Application specific reason code + */ + function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32); /** - * @notice Mints new tokens and assigns them to the target _investor. - * Can only be called by the STO attached to the token (Or by the ST owner if there's no STO attached yet) - * @param _investor Address the tokens will be minted to - * @param _value is the amount of tokens that will be minted to the investor + * @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the + * cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped + * with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * @return bool It signifies whether the transaction will be executed or not. + * @return byte Ethereum status code (ESC) + * @return bytes32 Application specific reason code + */ + function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32); + + /** + * @notice Used to attach a new document to the contract, or update the URI or hash of an existing attached document + * @dev Can only be executed by the owner of the contract. + * @param _name Name of the document. It should be unique always + * @param _uri Off-chain uri of the document from where it is accessible to investors/advisors to read. + * @param _documentHash hash (of the contents) of the document. + */ + function setDocument(bytes32 _name, string calldata _uri, bytes32 _documentHash) external; + + /** + * @notice Used to remove an existing document from the contract by giving the name of the document. + * @dev Can only be executed by the owner of the contract. + * @param _name Name of the document. It should be unique always + */ + function removeDocument(bytes32 _name) external; + + /** + * @notice Used to return the details of a document with a known name (`bytes32`). + * @param _name Name of the document + * @return string The URI associated with the document. + * @return bytes32 The hash (of the contents) of the document. + * @return uint256 the timestamp at which the document was last modified. + */ + function getDocument(bytes32 _name) external view returns (string memory, bytes32, uint256); + + /** + * @notice Used to retrieve a full list of documents attached to the smart contract. + * @return bytes32 List of all documents names present in the contract. */ - function mint(address _investor, uint256 _value) external returns (bool success); + function getAllDocuments() external view returns (bytes32[] memory); + + /** + * @notice In order to provide transparency over whether `controllerTransfer` / `controllerRedeem` are useable + * or not `isControllable` function will be used. + * @dev If `isControllable` returns `false` then it always return `false` and + * `controllerTransfer` / `controllerRedeem` will always revert. + * @return bool `true` when controller address is non-zero otherwise return `false`. + */ + function isControllable() external view returns (bool); + + /** + * @notice Checks if an address is a module of certain type + * @param _module Address to check + * @param _type type to check against + */ + function isModule(address _module, uint8 _type) external view returns(bool); + + /** + * @notice This function must be called to increase the total supply (Corresponds to mint function of ERC20). + * @dev It only be called by the token issuer or the operator defined by the issuer. ERC1594 doesn't have + * have the any logic related to operator but its superset ERC1400 have the operator logic and this function + * is allowed to call by the operator. + * @param _tokenHolder The account that will receive the created tokens (account should be whitelisted or KYCed). + * @param _value The amount of tokens need to be issued + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + */ + function issue(address _tokenHolder, uint256 _value, bytes calldata _data) external; /** * @notice Mints new tokens and assigns them to the target _investor. @@ -36,30 +109,42 @@ interface ISecurityToken { * @param _value is The amount of tokens that will be minted to the investor * @param _data Data to indicate validation */ - function mintWithData(address _investor, uint256 _value, bytes _data) external returns (bool success); + function mintWithData(address _investor, uint256 _value, bytes calldata _data) external returns(bool success); /** - * @notice Used to burn the securityToken on behalf of someone else - * @param _from Address for whom to burn tokens - * @param _value No. of tokens to be burned - * @param _data Data to indicate validation + * @notice This function redeem an amount of the token of a msg.sender. For doing so msg.sender may incentivize + * using different ways that could be implemented with in the `redeem` function definition. But those implementations + * are out of the scope of the ERC1594. + * @param _value The amount of tokens need to be redeemed + * @param _data The `bytes _data` it can be used in the token contract to authenticate the redemption. */ - function burnFromWithData(address _from, uint256 _value, bytes _data) external; + function redeem(uint256 _value, bytes calldata _data) external; /** - * @notice Used to burn the securityToken - * @param _value No. of tokens to be burned - * @param _data Data to indicate validation + * @notice This function redeem an amount of the token of a msg.sender. For doing so msg.sender may incentivize + * using different ways that could be implemented with in the `redeem` function definition. But those implementations + * are out of the scope of the ERC1594. + * @dev It is analogy to `transferFrom` + * @param _tokenHolder The account whose tokens gets redeemed. + * @param _value The amount of tokens need to be redeemed + * @param _data The `bytes _data` it can be used in the token contract to authenticate the redemption. */ - function burnWithData(uint256 _value, bytes _data) external; + function redeemFrom(address _tokenHolder, uint256 _value, bytes calldata _data) external; - event Minted(address indexed _to, uint256 _value); - event Burnt(address indexed _burner, uint256 _value); + // Issuance / Redemption Events + event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data); + event Redeemed(address indexed _operator, address indexed _from, uint256 _value, bytes _data); - // Permissions this to a Permission module, which has a key of 1 - // If no Permission return false - note that IModule withPerm will allow ST owner all permissions anyway - // this allows individual modules to override this logic if needed (to not allow ST owner all permissions) - function checkPermission(address _delegate, address _module, bytes32 _perm) external view returns (bool); + /** + * @notice Validate permissions with PermissionManager if it exists, If no Permission return false + * @dev Note that IModule withPerm will allow ST owner all permissions anyway + * @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions) + * @param _delegate address of delegate + * @param _module address of PermissionManager module + * @param _perm the permissions + * @return success + */ + function checkPermission(address _delegate, address _module, bytes32 _perm) external view returns(bool); /** * @notice Returns module list for a module type @@ -68,51 +153,55 @@ interface ISecurityToken { * @return address Module address * @return address Module factory address * @return bool Module archived - * @return uint8 Module type - * @return uint256 Module index - * @return uint256 Name index - + * @return uint8 Array of module types + * @return bytes32 Module label */ - function getModule(address _module) external view returns(bytes32, address, address, bool, uint8, uint256, uint256); + function getModule(address _module) external view returns (bytes32, address, address, bool, uint8[] memory, bytes32); /** * @notice Returns module list for a module name * @param _name Name of the module * @return address[] List of modules with this name */ - function getModulesByName(bytes32 _name) external view returns (address[]); + function getModulesByName(bytes32 _name) external view returns(address[] memory); /** * @notice Returns module list for a module type * @param _type Type of the module * @return address[] List of modules with this type */ - function getModulesByType(uint8 _type) external view returns (address[]); + function getModulesByType(uint8 _type) external view returns(address[] memory); /** * @notice Queries totalSupply at a specified checkpoint * @param _checkpointId Checkpoint ID to query as of */ - function totalSupplyAt(uint256 _checkpointId) external view returns (uint256); + function totalSupplyAt(uint256 _checkpointId) external view returns(uint256); /** * @notice Queries balance at a specified checkpoint * @param _investor Investor to query balance for * @param _checkpointId Checkpoint ID to query as of */ - function balanceOfAt(address _investor, uint256 _checkpointId) external view returns (uint256); + function balanceOfAt(address _investor, uint256 _checkpointId) external view returns(uint256); /** * @notice Creates a checkpoint that can be used to query historical balances / totalSuppy */ - function createCheckpoint() external returns (uint256); + function createCheckpoint() external returns(uint256); + + /** + * @notice Gets list of times that checkpoints were created + * @return List of checkpoint times + */ + function getCheckpointTimes() external view returns(uint256[] memory); /** * @notice Gets length of investors array * NB - this length may differ from investorCount if the list has not been pruned of zero-balance investors * @return Length */ - function getInvestors() external view returns (address[]); + function getInvestors() external view returns(address[] memory); /** * @notice returns an array of investors at a given checkpoint @@ -120,7 +209,7 @@ interface ISecurityToken { * @param _checkpointId Checkpoint id at which investor list is to be populated * @return list of investors */ - function getInvestorsAt(uint256 _checkpointId) external view returns(address[]); + function getInvestorsAt(uint256 _checkpointId) external view returns(address[] memory); /** * @notice generates subset of investors @@ -129,20 +218,25 @@ interface ISecurityToken { * @param _end Position of investor to stop iteration at * @return list of investors */ - function iterateInvestors(uint256 _start, uint256 _end) external view returns(address[]); - + function iterateInvestors(uint256 _start, uint256 _end) external view returns(address[] memory); + /** * @notice Gets current checkpoint ID * @return Id */ - function currentCheckpointId() external view returns (uint256); + function currentCheckpointId() external view returns(uint256); + + /** + * @notice Gets data store address + * @return data store address + */ + function dataStore() external view returns (address); /** - * @notice Gets an investor at a particular index - * @param _index Index to return address from - * @return Investor address + * @notice Allows owner to change data store + * @param _dataStore Address of the token data store */ - function investors(uint256 _index) external view returns (address); + function changeDataStore(address _dataStore) external; /** * @notice Allows the owner to withdraw unspent POLY stored by them on the ST or any ERC20 token. @@ -153,17 +247,18 @@ interface ISecurityToken { function withdrawERC20(address _tokenContract, uint256 _value) external; /** - * @notice Allows owner to approve more POLY to one of the modules + * @notice Allows owner to increase/decrease POLY approval of one of the modules * @param _module Module address - * @param _budget New budget + * @param _change Change in allowance + * @param _increase True if budget has to be increased, false if decrease */ - function changeModuleBudget(address _module, uint256 _budget) external; + function changeModuleBudget(address _module, uint256 _change, bool _increase) external; /** * @notice Changes the tokenDetails * @param _newTokenDetails New token details */ - function updateTokenDetails(string _newTokenDetails) external; + function updateTokenDetails(string calldata _newTokenDetails) external; /** * @notice Allows the owner to change token granularity @@ -171,14 +266,6 @@ interface ISecurityToken { */ function changeGranularity(uint256 _granularity) external; - /** - * @notice Removes addresses with zero balances from the investors list - * @param _start Index in investors list at which to start removing zero balances - * @param _iters Max number of iterations of the for loop - * NB - pruning this list will mean you may not be able to iterate over investors on-chain as of a historical checkpoint - */ - function pruneInvestors(uint256 _start, uint256 _iters) external; - /** * @notice Freezes all the transfers */ @@ -192,16 +279,25 @@ interface ISecurityToken { /** * @notice Ends token minting period permanently */ - function freezeMinting() external; + function freezeIssuance() external; - /** - * @notice Mints new tokens and assigns them to the target investors. - * Can only be called by the STO attached to the token or by the Issuer (Security Token contract owner) - * @param _investors A list of addresses to whom the minted tokens will be delivered - * @param _values A list of the amount of tokens to mint to corresponding addresses from _investor[] list - * @return Success - */ - function mintMulti(address[] _investors, uint256[] _values) external returns (bool success); + /** + * @notice Attachs a module to the SecurityToken + * @dev E.G.: On deployment (through the STR) ST gets a TransferManager module attached to it + * @dev to control restrictions on transfers. + * @param _moduleFactory is the address of the module factory to be added + * @param _data is data packed into bytes used to further configure the module (See STO usage) + * @param _maxCost max amount of POLY willing to pay to the module. + * @param _budget max amount of ongoing POLY willing to assign to the module. + * @param _label custom module label. + */ + function addModuleWithLabel( + address _moduleFactory, + bytes calldata _data, + uint256 _maxCost, + uint256 _budget, + bytes32 _label + ) external; /** * @notice Function used to attach a module to the security token @@ -213,13 +309,9 @@ interface ISecurityToken { * @param _moduleFactory is the address of the module factory to be added * @param _data is data packed into bytes used to further configure the module (See STO usage) * @param _maxCost max amount of POLY willing to pay to module. (WIP) + * @param _budget max amount of ongoing POLY willing to assign to the module. */ - function addModule( - address _moduleFactory, - bytes _data, - uint256 _maxCost, - uint256 _budget - ) external; + function addModule(address _moduleFactory, bytes calldata _data, uint256 _maxCost, uint256 _budget) external; /** * @notice Archives a module attached to the SecurityToken @@ -246,50 +338,65 @@ interface ISecurityToken { function setController(address _controller) external; /** - * @notice Used by a controller to execute a forced transfer - * @param _from address from which to take tokens - * @param _to address where to send tokens - * @param _value amount of tokens to transfer - * @param _data data to indicate validation - * @param _log data attached to the transfer by controller to emit in event + * @notice This function allows an authorised address to transfer tokens between any two token holders. + * The transfer must still respect the balances of the token holders (so the transfer must be for at most + * `balanceOf(_from)` tokens) and potentially also need to respect other transfer restrictions. + * @dev This function can only be executed by the `controller` address. + * @param _from Address The address which you want to send tokens from + * @param _to Address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data data to validate the transfer. (It is not used in this reference implementation + * because use of `_data` parameter is implementation specific). + * @param _operatorData data attached to the transfer by controller to emit in event. (It is more like a reason string + * for calling this function (aka force transfer) which provides the transparency on-chain). */ - function forceTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _log) external; + function controllerTransfer(address _from, address _to, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external; /** - * @notice Used by a controller to execute a foced burn - * @param _from address from which to take tokens - * @param _value amount of tokens to transfer - * @param _data data to indicate validation - * @param _log data attached to the transfer by controller to emit in event + * @notice This function allows an authorised address to redeem tokens for any token holder. + * The redemption must still respect the balances of the token holder (so the redemption must be for at most + * `balanceOf(_tokenHolder)` tokens) and potentially also need to respect other transfer restrictions. + * @dev This function can only be executed by the `controller` address. + * @param _tokenHolder The account whose tokens will be redeemed. + * @param _value uint256 the amount of tokens need to be redeemed. + * @param _data data to validate the transfer. (It is not used in this reference implementation + * because use of `_data` parameter is implementation specific). + * @param _operatorData data attached to the transfer by controller to emit in event. (It is more like a reason string + * for calling this function (aka force transfer) which provides the transparency on-chain). */ - function forceBurn(address _from, uint256 _value, bytes _data, bytes _log) external; + function controllerRedeem(address _tokenHolder, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external; /** * @notice Used by the issuer to permanently disable controller functionality * @dev enabled via feature switch "disableControllerAllowed" */ - function disableController() external; + function disableController() external; - /** + /** * @notice Used to get the version of the securityToken */ - function getVersion() external view returns(uint8[]); + function getVersion() external view returns(uint8[] memory); - /** + /** * @notice Gets the investor count */ - function getInvestorCount() external view returns(uint256); + function getInvestorCount() external view returns(uint256); - /** + /** + * @notice Gets the holder count (investors with non zero balance) + */ + function holderCount() external view returns(uint256); + + /** * @notice Overloaded version of the transfer function * @param _to receiver of transfer * @param _value value of transfer * @param _data data to indicate validation * @return bool success */ - function transferWithData(address _to, uint256 _value, bytes _data) external returns (bool success); + function transferWithData(address _to, uint256 _value, bytes calldata _data) external; - /** + /** * @notice Overloaded version of the transferFrom function * @param _from sender of transfer * @param _to receiver of transfer @@ -297,11 +404,20 @@ interface ISecurityToken { * @param _data data to indicate validation * @return bool success */ - function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external returns(bool); + function transferFromWithData(address _from, address _to, uint256 _value, bytes calldata _data) external; - /** + /** * @notice Provides the granularity of the token * @return uint256 */ - function granularity() external view returns(uint256); + function granularity() external view returns(uint256); + + /** + * @notice A security token issuer can specify that issuance has finished for the token + * (i.e. no new tokens can be minted or issued). + * @dev If a token returns FALSE for `isIssuable()` then it MUST always return FALSE in the future. + * If a token returns FALSE for `isIssuable()` then it MUST never allow additional tokens to be issued. + * @return bool `true` signifies the minting is allowed. While `false` denotes the end of minting + */ + function isIssuable() external view returns (bool); } diff --git a/contracts/interfaces/ISecurityTokenRegistry.sol b/contracts/interfaces/ISecurityTokenRegistry.sol index 8db01f8ad..18e1e6951 100644 --- a/contracts/interfaces/ISecurityTokenRegistry.sol +++ b/contracts/interfaces/ISecurityTokenRegistry.sol @@ -1,18 +1,17 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for the Polymath Security Token Registry contract */ interface ISecurityTokenRegistry { - - /** + /** * @notice Creates a new Security Token and saves it to the registry * @param _name Name of the token * @param _ticker Ticker ticker of the security token * @param _tokenDetails Off-chain details of the token * @param _divisible Whether the token is divisible or not */ - function generateSecurityToken(string _name, string _ticker, string _tokenDetails, bool _divisible) external; + function generateSecurityToken(string calldata _name, string calldata _ticker, string calldata _tokenDetails, bool _divisible) external; /** * @notice Adds a new custom Security Token and saves it to the registry. (Token should follow the ISecurityToken interface) @@ -24,14 +23,14 @@ interface ISecurityTokenRegistry { * @param _deployedAt Timestamp at which security token comes deployed on the ethereum blockchain */ function modifySecurityToken( - string _name, - string _ticker, + string calldata _name, + string calldata _ticker, address _owner, address _securityToken, - string _tokenDetails, + string calldata _tokenDetails, uint256 _deployedAt - ) - external; + ) + external; /** * @notice Registers the token ticker for its particular owner @@ -41,7 +40,7 @@ interface ISecurityTokenRegistry { * @param _ticker Token ticker * @param _tokenName Name of the token */ - function registerTicker(address _owner, string _ticker, string _tokenName) external; + function registerTicker(address _owner, string calldata _ticker, string calldata _tokenName) external; /** * @notice Changes the protocol version and the SecurityToken contract @@ -59,7 +58,7 @@ interface ISecurityTokenRegistry { * @param _securityToken Address of the Scurity token * @return bool */ - function isSecurityToken(address _securityToken) external view returns (bool); + function isSecurityToken(address _securityToken) external view returns(bool); /** * @dev Allows the current owner to transfer control of the contract to a newOwner. @@ -72,9 +71,9 @@ interface ISecurityTokenRegistry { * @param _ticker Symbol of the Scurity token * @return address */ - function getSecurityTokenAddress(string _ticker) external view returns (address); + function getSecurityTokenAddress(string calldata _ticker) external view returns(address); - /** + /** * @notice Get security token data by its address * @param _securityToken Address of the Scurity token. * @return string Symbol of the Security Token. @@ -82,7 +81,7 @@ interface ISecurityTokenRegistry { * @return string Details of the Token. * @return uint256 Timestamp at which Security Token get launched on Polymath platform. */ - function getSecurityTokenData(address _securityToken) external view returns (string, address, string, uint256); + function getSecurityTokenData(address _securityToken) external view returns(string memory, address, string memory, uint256); /** * @notice Get the current STFactory Address @@ -92,20 +91,26 @@ interface ISecurityTokenRegistry { /** * @notice Get Protocol version */ - function getProtocolVersion() external view returns(uint8[]); + function getProtocolVersion() external view returns(uint8[] memory); /** * @notice Used to get the ticker list as per the owner * @param _owner Address which owns the list of tickers */ - function getTickersByOwner(address _owner) external view returns(bytes32[]); + function getTickersByOwner(address _owner) external view returns(bytes32[] memory); /** * @notice Returns the list of tokens owned by the selected address * @param _owner is the address which owns the list of tickers * @dev Intention is that this is called off-chain so block gas limit is not relevant */ - function getTokensByOwner(address _owner) external view returns(address[]); + function getTokensByOwner(address _owner) external view returns(address[] memory); + + /** + * @notice Returns the list of all tokens + * @dev Intention is that this is called off-chain so block gas limit is not relevant + */ + function getTokens() external view returns(address[] memory); /** * @notice Returns the owner and timestamp for a given ticker @@ -116,7 +121,7 @@ interface ISecurityTokenRegistry { * @return string * @return bool */ - function getTickerDetails(string _ticker) external view returns (address, uint256, uint256, string, bool); + function getTickerDetails(string calldata _ticker) external view returns(address, uint256, uint256, string memory, bool); /** * @notice Modifies the ticker details. Only polymath account has the ability @@ -130,26 +135,26 @@ interface ISecurityTokenRegistry { */ function modifyTicker( address _owner, - string _ticker, - string _tokenName, + string calldata _ticker, + string calldata _tokenName, uint256 _registrationDate, uint256 _expiryDate, bool _status - ) - external; + ) + external; - /** + /** * @notice Removes the ticker details and associated ownership & security token mapping * @param _ticker Token ticker */ - function removeTicker(string _ticker) external; + function removeTicker(string calldata _ticker) external; /** * @notice Transfers the ownership of the ticker * @dev _newOwner Address whom ownership to transfer * @dev _ticker Ticker */ - function transferTickerOwnership(address _newOwner, string _ticker) external; + function transferTickerOwnership(address _newOwner, string calldata _ticker) external; /** * @notice Changes the expiry time for the token ticker @@ -161,19 +166,13 @@ interface ISecurityTokenRegistry { * @notice Sets the ticker registration fee in POLY tokens * @param _tickerRegFee Registration fee in POLY tokens (base 18 decimals) */ - function changeTickerRegistrationFee(uint256 _tickerRegFee) external; + function changeTickerRegistrationFee(uint256 _tickerRegFee) external; - /** + /** * @notice Sets the ticker registration fee in POLY tokens * @param _stLaunchFee Registration fee in POLY tokens (base 18 decimals) */ - function changeSecurityLaunchFee(uint256 _stLaunchFee) external; - - /** - * @notice Change the PolyToken address - * @param _newAddress Address of the polytoken - */ - function updatePolyTokenAddress(address _newAddress) external; + function changeSecurityLaunchFee(uint256 _stLaunchFee) external; /** * @notice Gets the security token launch fee diff --git a/contracts/interfaces/ITransferManager.sol b/contracts/interfaces/ITransferManager.sol new file mode 100644 index 000000000..3045b3579 --- /dev/null +++ b/contracts/interfaces/ITransferManager.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.5.0; + +/** + * @title Interface to be implemented by all Transfer Manager modules + */ +interface ITransferManager { + // If verifyTransfer returns: + // FORCE_VALID, the transaction will always be valid, regardless of other TM results + // INVALID, then the transfer should not be allowed regardless of other TM results + // VALID, then the transfer is valid for this TM + // NA, then the result from this TM is ignored + enum Result {INVALID, NA, VALID, FORCE_VALID} + + /** + * @notice Determines if the transfer between these two accounts can happen + */ + function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(Result); + + function verifyTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external view returns(Result, bytes32); + + /** + * @notice return the amount of tokens for a given user as per the partition + * @param _owner Whom token amount need to query + * @param _partition Identifier + */ + function getTokensByPartition(address _owner, bytes32 _partition) external view returns(uint256); +} diff --git a/contracts/interfaces/IUSDTieredSTOProxy.sol b/contracts/interfaces/IUSDTieredSTOProxy.sol index 3aec141a0..bcbf2b2d9 100644 --- a/contracts/interfaces/IUSDTieredSTOProxy.sol +++ b/contracts/interfaces/IUSDTieredSTOProxy.sol @@ -1,24 +1,22 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface for security token proxy deployment */ interface IUSDTieredSTOProxy { - - /** + /** * @notice Deploys the STO. * @param _securityToken Contract address of the securityToken - * @param _polyAddress Contract address of the PolyToken - * @param _factoryAddress Contract address of the factory + * @param _factoryAddress Contract address of the factory * @return address Address of the deployed STO */ - function deploySTO(address _securityToken, address _polyAddress, address _factoryAddress) external returns (address); - - /** + function deploySTO(address _securityToken, address _factoryAddress) external returns(address); + + /** * @notice Used to get the init function signature * @param _contractAddress Address of the STO contract * @return bytes4 */ - function getInitFunction(address _contractAddress) external returns (bytes4); + function getInitFunction(address _contractAddress) external returns(bytes4); -} \ No newline at end of file +} diff --git a/contracts/interfaces/token/IERC1594.sol b/contracts/interfaces/token/IERC1594.sol new file mode 100644 index 000000000..00e1e4d36 --- /dev/null +++ b/contracts/interfaces/token/IERC1594.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.5.0; + +/** + * @title Standard Interface of ERC1594 + */ +interface IERC1594 { + + // Transfers + function transferWithData(address _to, uint256 _value, bytes calldata _data) external; + function transferFromWithData(address _from, address _to, uint256 _value, bytes calldata _data) external; + + // Token Issuance + function isIssuable() external view returns (bool); + function issue(address _tokenHolder, uint256 _value, bytes calldata _data) external; + + // Token Redemption + function redeem(uint256 _value, bytes calldata _data) external; + function redeemFrom(address _tokenHolder, uint256 _value, bytes calldata _data) external; + + // Transfer Validity + function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32); + function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32); + + // Issuance / Redemption Events + event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data); + event Redeemed(address indexed _operator, address indexed _from, uint256 _value, bytes _data); + +} \ No newline at end of file diff --git a/contracts/interfaces/token/IERC1643.sol b/contracts/interfaces/token/IERC1643.sol new file mode 100644 index 000000000..90ff408ce --- /dev/null +++ b/contracts/interfaces/token/IERC1643.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.5.0; + +// @title IERC1643 Document Management (part of the ERC1400 Security Token Standards) +/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec + +interface IERC1643 { + + // Document Management + //function getDocument(bytes32 _name) external view returns (string memory, bytes32, uint256); + function setDocument(bytes32 _name, string calldata _uri, bytes32 _documentHash) external; + function removeDocument(bytes32 _name) external; + //function getAllDocuments() external view returns (bytes32[] memory); + + // Document Events + event DocumentRemoved(bytes32 indexed _name, string _uri, bytes32 _documentHash); + event DocumentUpdated(bytes32 indexed _name, string _uri, bytes32 _documentHash); + +} \ No newline at end of file diff --git a/contracts/interfaces/token/IERC1644.sol b/contracts/interfaces/token/IERC1644.sol new file mode 100644 index 000000000..d03e1cf30 --- /dev/null +++ b/contracts/interfaces/token/IERC1644.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.5.0; + +interface IERC1644 { + + // Controller Operation + function isControllable() external view returns (bool); + function controllerTransfer(address _from, address _to, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external; + function controllerRedeem(address _tokenHolder, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external; + + // Controller Events + event ControllerTransfer( + address _controller, + address indexed _from, + address indexed _to, + uint256 _value, + bytes _data, + bytes _operatorData + ); + + event ControllerRedemption( + address _controller, + address indexed _tokenHolder, + uint256 _value, + bytes _data, + bytes _operatorData + ); + +} \ No newline at end of file diff --git a/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol new file mode 100644 index 000000000..0e2f71d93 --- /dev/null +++ b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol @@ -0,0 +1,355 @@ +pragma solidity ^0.5.0; + +// ---------------------------------------------------------------------------- +// BokkyPooBah's DateTime Library v1.00 +// +// A gas-efficient Solidity date and time library +// +// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary +// +// Tested date range 1970/01/01 to 2345/12/31 +// +// Conventions: +// Unit | Range | Notes +// :-------- |:-------------:|:----- +// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC +// year | 1970 ... 2345 | +// month | 1 ... 12 | +// day | 1 ... 31 | +// hour | 0 ... 23 | +// minute | 0 ... 59 | +// second | 0 ... 59 | +// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday +// +// +// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. +// +// GNU Lesser General Public License 3.0 +// https://www.gnu.org/licenses/lgpl-3.0.en.html +// ---------------------------------------------------------------------------- + +library BokkyPooBahsDateTimeLibrary { + uint constant SECONDS_PER_DAY = 24 * 60 * 60; + uint constant SECONDS_PER_HOUR = 60 * 60; + uint constant SECONDS_PER_MINUTE = 60; + int constant OFFSET19700101 = 2440588; + + uint constant DOW_MON = 1; + uint constant DOW_TUE = 2; + uint constant DOW_WED = 3; + uint constant DOW_THU = 4; + uint constant DOW_FRI = 5; + uint constant DOW_SAT = 6; + uint constant DOW_SUN = 7; + + // ------------------------------------------------------------------------ + // Calculate the number of days from 1970/01/01 to year/month/day using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and subtracting the offset 2440588 so that 1970/01/01 is day 0 + // + // days = day + // - 32075 + // + 1461 * (year + 4800 + (month - 14) / 12) / 4 + // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 + // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 + // - offset + // ------------------------------------------------------------------------ + function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { + require(year >= 1970); + int _year = int(year); + int _month = int(month); + int _day = int(day); + + int __days = _day - 32075 + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 - OFFSET19700101; + + _days = uint(__days); + } + + // ------------------------------------------------------------------------ + // Calculate year/month/day from the number of days since 1970/01/01 using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and adding the offset 2440588 so that 1970/01/01 is day 0 + // + // int L = days + 68569 + offset + // int N = 4 * L / 146097 + // L = L - (146097 * N + 3) / 4 + // year = 4000 * (L + 1) / 1461001 + // L = L - 1461 * year / 4 + 31 + // month = 80 * L / 2447 + // dd = L - 2447 * month / 80 + // L = month / 11 + // month = month + 2 - 12 * L + // year = 100 * (N - 49) + year + L + // ------------------------------------------------------------------------ + function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { + int __days = int(_days); + + int L = __days + 68569 + OFFSET19700101; + int N = 4 * L / 146097; + L = L - (146097 * N + 3) / 4; + int _year = 4000 * (L + 1) / 1461001; + L = L - 1461 * _year / 4 + 31; + int _month = 80 * L / 2447; + int _day = L - 2447 * _month / 80; + L = _month / 11; + _month = _month + 2 - 12 * L; + _year = 100 * (N - 49) + _year + L; + + year = uint(_year); + month = uint(_month); + day = uint(_day); + } + + function timestampFromDate(uint year, uint month, uint day) internal pure returns(uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; + } + function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns( + uint timestamp + ) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; + } + function timestampToDate(uint timestamp) internal pure returns(uint year, uint month, uint day) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function timestampToDateTime(uint timestamp) internal pure returns( + uint year, + uint month, + uint day, + uint hour, + uint minute, + uint second + ) + { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + secs = secs % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + second = secs % SECONDS_PER_MINUTE; + } + + function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { + if (year >= 1970 && month > 0 && month <= 12) { + uint daysInMonth = _getDaysInMonth(year, month); + if (day > 0 && day <= daysInMonth) { + valid = true; + } + } + } + function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { + if (isValidDate(year, month, day)) { + if (hour < 24 && minute < 60 && second < 60) { + valid = true; + } + } + } + function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + leapYear = _isLeapYear(year); + } + function _isLeapYear(uint year) internal pure returns(bool leapYear) { + leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); + } + function isWeekDay(uint timestamp) internal pure returns(bool weekDay) { + weekDay = getDayOfWeek(timestamp) <= DOW_FRI; + } + function isWeekEnd(uint timestamp) internal pure returns(bool weekEnd) { + weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; + } + function getDaysInMonth(uint timestamp) internal pure returns(uint daysInMonth) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + daysInMonth = _getDaysInMonth(year, month); + } + function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { + if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { + daysInMonth = 31; + } else if (month != 2) { + daysInMonth = 30; + } else { + daysInMonth = _isLeapYear(year) ? 29 : 28; + } + } + // 1 = Monday, 7 = Sunday + function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { + uint _days = timestamp / SECONDS_PER_DAY; + dayOfWeek = (_days + 3) % 7 + 1; + } + + function getYear(uint timestamp) internal pure returns (uint year) { + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getMonth(uint timestamp) internal pure returns (uint month) { + uint year; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getDay(uint timestamp) internal pure returns (uint day) { + uint year; + uint month; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getHour(uint timestamp) internal pure returns(uint hour) { + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + } + function getMinute(uint timestamp) internal pure returns(uint minute) { + uint secs = timestamp % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + } + function getSecond(uint timestamp) internal pure returns(uint second) { + second = timestamp % SECONDS_PER_MINUTE; + } + + function addYears(uint timestamp, uint _years) internal pure returns(uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year += _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + + function addMonths(uint timestamp, uint _months) internal pure returns(uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + month += _months; + year += (month - 1) / 12; + month = (month - 1) % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + + function addDays(uint timestamp, uint _days) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp + _days * SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + + function addHours(uint timestamp, uint _hours) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; + require(newTimestamp >= timestamp); + } + + function addMinutes(uint timestamp, uint _minutes) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; + require(newTimestamp >= timestamp); + } + + function addSeconds(uint timestamp, uint _seconds) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp + _seconds; + require(newTimestamp >= timestamp); + } + + function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year -= _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + + function subMonths(uint timestamp, uint _months) internal pure returns(uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint yearMonth = year * 12 + (month - 1) - _months; + year = yearMonth / 12; + month = yearMonth % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + + function subDays(uint timestamp, uint _days) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp - _days * SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; + require(newTimestamp <= timestamp); + } + function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; + require(newTimestamp <= timestamp); + } + function subSeconds(uint timestamp, uint _seconds) internal pure returns(uint newTimestamp) { + newTimestamp = timestamp - _seconds; + require(newTimestamp <= timestamp); + } + + function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _years = toYear - fromYear; + } + + function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns(uint _months) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; + } + + function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns(uint _days) { + require(fromTimestamp <= toTimestamp); + _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; + } + + function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns(uint _hours) { + require(fromTimestamp <= toTimestamp); + _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; + } + + function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns(uint _minutes) { + require(fromTimestamp <= toTimestamp); + _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; + } + + function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns(uint _seconds) { + require(fromTimestamp <= toTimestamp); + _seconds = toTimestamp - fromTimestamp; + } +} diff --git a/contracts/libraries/DecimalMath.sol b/contracts/libraries/DecimalMath.sol index 242daffab..ab942bad2 100644 --- a/contracts/libraries/DecimalMath.sol +++ b/contracts/libraries/DecimalMath.sol @@ -1,16 +1,15 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; library DecimalMath { - using SafeMath for uint256; - /** + /** * @notice This function multiplies two decimals represented as (decimal * 10**DECIMALS) * @return uint256 Result of multiplication represented as (decimal * 10**DECIMALS) */ - function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + function mul(uint256 x, uint256 y) internal pure returns(uint256 z) { z = SafeMath.add(SafeMath.mul(x, y), (10 ** 18) / 2) / (10 ** 18); } @@ -18,8 +17,8 @@ library DecimalMath { * @notice This function divides two decimals represented as (decimal * 10**DECIMALS) * @return uint256 Result of division represented as (decimal * 10**DECIMALS) */ - function div(uint256 x, uint256 y) internal pure returns (uint256 z) { + function div(uint256 x, uint256 y) internal pure returns(uint256 z) { z = SafeMath.add(SafeMath.mul(x, (10 ** 18)), y / 2) / y; } -} \ No newline at end of file +} diff --git a/contracts/libraries/Encoder.sol b/contracts/libraries/Encoder.sol index 27d9a4ddf..b3cf9124d 100644 --- a/contracts/libraries/Encoder.sol +++ b/contracts/libraries/Encoder.sol @@ -1,28 +1,27 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; library Encoder { - - function getKey(string _key) internal pure returns (bytes32) { + function getKey(string memory _key) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key))); } - function getKey(string _key1, address _key2) internal pure returns (bytes32) { + function getKey(string memory _key1, address _key2) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } - function getKey(string _key1, string _key2) internal pure returns (bytes32) { + function getKey(string memory _key1, string memory _key2) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } - function getKey(string _key1, uint256 _key2) internal pure returns (bytes32) { + function getKey(string memory _key1, uint256 _key2) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } - function getKey(string _key1, bytes32 _key2) internal pure returns (bytes32) { + function getKey(string memory _key1, bytes32 _key2) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } - function getKey(string _key1, bool _key2) internal pure returns (bytes32) { + function getKey(string memory _key1, bool _key2) internal pure returns(bytes32) { return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } diff --git a/contracts/libraries/KindMath.sol b/contracts/libraries/KindMath.sol index 850926088..e9de30c65 100644 --- a/contracts/libraries/KindMath.sol +++ b/contracts/libraries/KindMath.sol @@ -1,53 +1,48 @@ -pragma solidity ^0.4.24; - -// Copied from OpenZeppelin and modified to be friendlier +pragma solidity ^0.5.0; /** * @title KindMath - * @dev Math operations with safety checks that throw on error + * @notice ref. https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol + * @dev Math operations with safety checks that returns boolean */ library KindMath { /** - * @dev Multiplies two numbers, throws on overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { + * @dev Multiplies two numbers, return false on overflow. + */ + function checkMul(uint256 a, uint256 b) internal pure returns (bool) { // Gas optimization: this is cheaper than requireing 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { - return 0; + return true; } - c = a * b; - require(c / a == b, "mul overflow"); - return c; - } - - /** - * @dev Integer division of two numbers, truncating the quotient. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - // require(b > 0); // Solidity automatically throws when dividing by 0 - // uint256 c = a / b; - // require(a == b * c + a % b); // There is no case in which this doesn't hold - return a / b; + uint256 c = a * b; + if (c / a == b) + return true; + else + return false; } /** - * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). + * @dev Subtracts two numbers, return false on overflow (i.e. if subtrahend is greater than minuend). */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - require(b <= a, "sub overflow"); - return a - b; + function checkSub(uint256 a, uint256 b) internal pure returns (bool) { + if (b <= a) + return true; + else + return false; } /** - * @dev Adds two numbers, throws on overflow. + * @dev Adds two numbers, return false on overflow. */ - function add(uint256 a, uint256 b) internal pure returns (uint256 c) { - c = a + b; - require(c >= a, "add overflow"); - return c; + function checkAdd(uint256 a, uint256 b) internal pure returns (bool) { + uint256 c = a + b; + if (c < a) + return false; + else + return true; } } diff --git a/contracts/libraries/TokenLib.sol b/contracts/libraries/TokenLib.sol index 42e5bebff..b432f8bb4 100644 --- a/contracts/libraries/TokenLib.sol +++ b/contracts/libraries/TokenLib.sol @@ -1,53 +1,38 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "../modules/PermissionManager/IPermissionManager.sol"; +import "../interfaces/IPoly.sol"; +import "../interfaces/IDataStore.sol"; +import "../tokens/SecurityTokenStorage.sol"; +import "../interfaces/ITransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../modules/PermissionManager/IPermissionManager.sol"; library TokenLib { using SafeMath for uint256; - // Struct for module data - struct ModuleData { - bytes32 name; - address module; - address moduleFactory; - bool isArchived; - uint8[] moduleTypes; - uint256[] moduleIndexes; - uint256 nameIndex; - } - - // Structures to maintain checkpoints of balances for governance / dividends - struct Checkpoint { - uint256 checkpointId; - uint256 value; - } - - struct InvestorDataStorage { - // List of investors who have ever held a non-zero token balance - mapping (address => bool) investorListed; - // List of token holders - address[] investors; - // Total number of non-zero token holders - uint256 investorCount; - } + bytes32 internal constant WHITELIST = "WHITELIST"; + bytes32 internal constant INVESTORSKEY = 0xdf3a8dd24acdd05addfc6aeffef7574d2de3f844535ec91e8e0f3e45dba96731; //keccak256(abi.encodePacked("INVESTORS")) // Emit when Module is archived from the SecurityToken - event ModuleArchived(uint8[] _types, address _module, uint256 _timestamp); + event ModuleArchived(uint8[] _types, address _module); // Emit when Module is unarchived from the SecurityToken - event ModuleUnarchived(uint8[] _types, address _module, uint256 _timestamp); + event ModuleUnarchived(uint8[] _types, address _module); + // Emit when Module get removed from the securityToken + event ModuleRemoved(uint8[] _types, address _module); + // Emit when the budget allocated to a module is changed + event ModuleBudgetChanged(uint8[] _moduleTypes, address _module, uint256 _oldBudget, uint256 _budget); /** * @notice Archives a module attached to the SecurityToken * @param _moduleData Storage data * @param _module Address of module to archive */ - function archiveModule(ModuleData storage _moduleData, address _module) public { + function archiveModule(SecurityTokenStorage.ModuleData storage _moduleData, address _module) public { require(!_moduleData.isArchived, "Module archived"); require(_moduleData.module != address(0), "Module missing"); /*solium-disable-next-line security/no-block-members*/ - emit ModuleArchived(_moduleData.moduleTypes, _module, now); + emit ModuleArchived(_moduleData.moduleTypes, _module); _moduleData.isArchived = true; } @@ -56,35 +41,100 @@ library TokenLib { * @param _moduleData Storage data * @param _module Address of module to unarchive */ - function unarchiveModule(ModuleData storage _moduleData, address _module) public { + function unarchiveModule(SecurityTokenStorage.ModuleData storage _moduleData, address _module) public { require(_moduleData.isArchived, "Module unarchived"); /*solium-disable-next-line security/no-block-members*/ - emit ModuleUnarchived(_moduleData.moduleTypes, _module, now); + emit ModuleUnarchived(_moduleData.moduleTypes, _module); _moduleData.isArchived = false; } /** - * @notice Validates permissions with PermissionManager if it exists. If there's no permission return false - * @dev Note that IModule withPerm will allow ST owner all permissions by default - * @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions) - * @param _modules is the modules to check permissions on - * @param _delegate is the address of the delegate - * @param _module is the address of the PermissionManager module - * @param _perm is the permissions data - * @return success - */ - function checkPermission(address[] storage _modules, address _delegate, address _module, bytes32 _perm) public view returns(bool) { - if (_modules.length == 0) { - return false; + * @notice Removes a module attached to the SecurityToken + * @param _module address of module to unarchive + */ + function removeModule( + address _module, + mapping(uint8 => address[]) storage _modules, + mapping(address => SecurityTokenStorage.ModuleData) storage _modulesToData, + mapping(bytes32 => address[]) storage _names + ) + public + { + require(_modulesToData[_module].isArchived, "Not archived"); + require(_modulesToData[_module].module != address(0), "Module missing"); + /*solium-disable-next-line security/no-block-members*/ + emit ModuleRemoved(_modulesToData[_module].moduleTypes, _module); + // Remove from module type list + uint8[] memory moduleTypes = _modulesToData[_module].moduleTypes; + for (uint256 i = 0; i < moduleTypes.length; i++) { + _removeModuleWithIndex(moduleTypes[i], _modulesToData[_module].moduleIndexes[i], _modules, _modulesToData); + /* modulesToData[_module].moduleType[moduleTypes[i]] = false; */ } + // Remove from module names list + uint256 index = _modulesToData[_module].nameIndex; + bytes32 name = _modulesToData[_module].name; + uint256 length = _names[name].length; + _names[name][index] = _names[name][length - 1]; + _names[name].length = length - 1; + if ((length - 1) != index) { + _modulesToData[_names[name][index]].nameIndex = index; + } + // Remove from modulesToData + delete _modulesToData[_module]; + } + + /** + * @notice Internal - Removes a module attached to the SecurityToken by index + */ + function _removeModuleWithIndex( + uint8 _type, + uint256 _index, + mapping(uint8 => address[]) storage _modules, + mapping(address => SecurityTokenStorage.ModuleData) storage _modulesToData + ) + internal + { + uint256 length = _modules[_type].length; + _modules[_type][_index] = _modules[_type][length - 1]; + _modules[_type].length = length - 1; - for (uint8 i = 0; i < _modules.length; i++) { - if (IPermissionManager(_modules[i]).checkPermission(_delegate, _module, _perm)) { - return true; + if ((length - 1) != _index) { + //Need to find index of _type in moduleTypes of module we are moving + uint8[] memory newTypes = _modulesToData[_modules[_type][_index]].moduleTypes; + for (uint256 i = 0; i < newTypes.length; i++) { + if (newTypes[i] == _type) { + _modulesToData[_modules[_type][_index]].moduleIndexes[i] = _index; + } } } + } - return false; + /** + * @notice allows owner to increase/decrease POLY approval of one of the modules + * @param _module module address + * @param _change change in allowance + * @param _increase true if budget has to be increased, false if decrease + */ + function changeModuleBudget( + address _module, + uint256 _change, + bool _increase, + address _polyToken, + mapping(address => SecurityTokenStorage.ModuleData) storage _modulesToData + ) + public + { + require(_modulesToData[_module].module != address(0), "Module missing"); + uint256 currentAllowance = IPoly(_polyToken).allowance(address(this), _module); + uint256 newAllowance; + if (_increase) { + require(IPoly(_polyToken).increaseApproval(_module, _change), "IncreaseApproval fail"); + newAllowance = currentAllowance.add(_change); + } else { + require(IPoly(_polyToken).decreaseApproval(_module, _change), "Insufficient allowance"); + newAllowance = currentAllowance.sub(_change); + } + emit ModuleBudgetChanged(_modulesToData[_module].moduleTypes, _module, currentAllowance, newAllowance); } /** @@ -94,7 +144,7 @@ library TokenLib { * @param _currentValue is the Current value of checkpoint * @return uint256 */ - function getValueAt(Checkpoint[] storage _checkpoints, uint256 _checkpointId, uint256 _currentValue) public view returns(uint256) { + function getValueAt(SecurityTokenStorage.Checkpoint[] storage _checkpoints, uint256 _checkpointId, uint256 _currentValue) public view returns(uint256) { //Checkpoint id 0 is when the token is first created - everyone has a zero balance if (_checkpointId == 0) { return 0; @@ -133,7 +183,7 @@ library TokenLib { * @param _checkpoints is the affected checkpoint object array * @param _newValue is the new value that needs to be stored */ - function adjustCheckpoints(TokenLib.Checkpoint[] storage _checkpoints, uint256 _newValue, uint256 _currentCheckpointId) public { + function adjustCheckpoints(SecurityTokenStorage.Checkpoint[] storage _checkpoints, uint256 _newValue, uint256 _currentCheckpointId) public { //No checkpoints set yet if (_currentCheckpointId == 0) { return; @@ -143,48 +193,113 @@ library TokenLib { return; } //New checkpoint, so record balance - _checkpoints.push( - TokenLib.Checkpoint({ - checkpointId: _currentCheckpointId, - value: _newValue - }) - ); + _checkpoints.push(SecurityTokenStorage.Checkpoint({checkpointId: _currentCheckpointId, value: _newValue})); } /** * @notice Keeps track of the number of non-zero token holders - * @param _investorData Date releated to investor metrics + * @param _holderCount Number of current token holders * @param _from Sender of transfer * @param _to Receiver of transfer * @param _value Value of transfer * @param _balanceTo Balance of the _to address * @param _balanceFrom Balance of the _from address + * @param _dataStore address of data store */ function adjustInvestorCount( - InvestorDataStorage storage _investorData, + uint256 _holderCount, address _from, address _to, uint256 _value, uint256 _balanceTo, - uint256 _balanceFrom - ) public { + uint256 _balanceFrom, + address _dataStore + ) + public + returns(uint256) + { if ((_value == 0) || (_from == _to)) { - return; + return _holderCount; } // Check whether receiver is a new token holder if ((_balanceTo == 0) && (_to != address(0))) { - _investorData.investorCount = (_investorData.investorCount).add(1); + _holderCount = _holderCount.add(1); + IDataStore dataStore = IDataStore(_dataStore); + if (!_isExistingInvestor(_to, dataStore)) { + dataStore.insertAddress(INVESTORSKEY, _to); + //KYC data can not be present if added is false and hence we can set packed KYC as uint256(1) to set added as true + dataStore.setUint256(_getKey(WHITELIST, _to), uint256(1)); + } } // Check whether sender is moving all of their tokens if (_value == _balanceFrom) { - _investorData.investorCount = (_investorData.investorCount).sub(1); + _holderCount = _holderCount.sub(1); } - //Also adjust investor list - if (!_investorData.investorListed[_to] && (_to != address(0))) { - _investorData.investors.push(_to); - _investorData.investorListed[_to] = true; + + return _holderCount; + } + + /** + * @notice Validate transfer with TransferManager module if it exists + * @dev TransferManager module has a key of 2 + * @param from sender of transfer + * @param to receiver of transfer + * @param value value of transfer + * @param data data to indicate validation + * @param modules Array of addresses for transfer managers + * @param modulesToData Mapping of the modules details + * @param transfersFrozen whether the transfer are frozen or not. + * @return bool + */ + function verifyTransfer( + address[] storage modules, + mapping(address => SecurityTokenStorage.ModuleData) storage modulesToData, + address from, + address to, + uint256 value, + bytes memory data, + bool transfersFrozen + ) + public + view + returns(bool, bytes32) + { + if (!transfersFrozen) { + bool isInvalid = false; + bool isValid = false; + bool isForceValid = false; + // Use the local variables to avoid the stack too deep error + transfersFrozen = false; // bool unarchived = false; + bytes32 appCode; + for (uint256 i = 0; i < modules.length; i++) { + if (!modulesToData[modules[i]].isArchived) { + transfersFrozen = true; + (ITransferManager.Result valid, bytes32 reason) = ITransferManager(modules[i]).verifyTransfer(from, to, value, data); + if (valid == ITransferManager.Result.INVALID) { + isInvalid = true; + appCode = reason; + } else if (valid == ITransferManager.Result.VALID) { + isValid = true; + } else if (valid == ITransferManager.Result.FORCE_VALID) { + isForceValid = true; + } + } + } + // If no unarchived modules, return true by default + // Use the local variables to avoid the stack too deep error + isValid = transfersFrozen ? (isForceValid ? true : (isInvalid ? false : isValid)) : true; + return (isValid, isValid ? bytes32(hex"51"): appCode); } + return (false, bytes32(hex"54")); + } + function _getKey(bytes32 _key1, address _key2) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(_key1, _key2))); } + function _isExistingInvestor(address _investor, IDataStore dataStore) internal view returns(bool) { + uint256 data = dataStore.getUint256(_getKey(WHITELIST, _investor)); + //extracts `added` from packed `whitelistData` + return uint8(data) == 0 ? false : true; + } } diff --git a/contracts/libraries/Util.sol b/contracts/libraries/Util.sol index 5d4cce83e..b502ca305 100644 --- a/contracts/libraries/Util.sol +++ b/contracts/libraries/Util.sol @@ -1,20 +1,19 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Utility contract for reusable code */ library Util { - - /** + /** * @notice Changes a string to upper case * @param _base String to change */ - function upper(string _base) internal pure returns (string) { + function upper(string memory _base) internal pure returns(string memory) { bytes memory _baseBytes = bytes(_base); for (uint i = 0; i < _baseBytes.length; i++) { bytes1 b1 = _baseBytes[i]; if (b1 >= 0x61 && b1 <= 0x7A) { - b1 = bytes1(uint8(b1)-32); + b1 = bytes1(uint8(b1) - 32); } _baseBytes[i] = b1; } @@ -26,7 +25,7 @@ library Util { * @param _source String that need to convert into bytes32 */ /// Notice - Maximum Length for _source will be 32 chars otherwise returned bytes32 value will have lossy value. - function stringToBytes32(string memory _source) internal pure returns (bytes32) { + function stringToBytes32(string memory _source) internal pure returns(bytes32) { return bytesToBytes32(bytes(_source), 0); } @@ -36,7 +35,7 @@ library Util { * @param _offset Offset from which to begin conversion */ /// Notice - Maximum length for _source will be 32 chars otherwise returned bytes32 value will have lossy value. - function bytesToBytes32(bytes _b, uint _offset) internal pure returns (bytes32) { + function bytesToBytes32(bytes memory _b, uint _offset) internal pure returns(bytes32) { bytes32 result; for (uint i = 0; i < _b.length; i++) { @@ -49,10 +48,11 @@ library Util { * @notice Changes the bytes32 into string * @param _source that need to convert into string */ - function bytes32ToString(bytes32 _source) internal pure returns (string result) { + function bytes32ToString(bytes32 _source) internal pure returns(string memory) { bytes memory bytesString = new bytes(32); uint charCount = 0; - for (uint j = 0; j < 32; j++) { + uint j = 0; + for (j = 0; j < 32; j++) { byte char = byte(bytes32(uint(_source) * 2 ** (8 * j))); if (char != 0) { bytesString[charCount] = char; @@ -71,12 +71,11 @@ library Util { * @param _data Passed data * @return bytes4 sig */ - function getSig(bytes _data) internal pure returns (bytes4 sig) { + function getSig(bytes memory _data) internal pure returns(bytes4 sig) { uint len = _data.length < 4 ? _data.length : 4; - for (uint i = 0; i < len; i++) { - sig = bytes4(uint(sig) + uint(_data[i]) * (2 ** (8 * (len - 1 - i)))); + for (uint256 i = 0; i < len; i++) { + sig |= bytes4(_data[i] & 0xFF) >> (i * 8); } + return sig; } - - } diff --git a/contracts/libraries/VersionUtils.sol b/contracts/libraries/VersionUtils.sol index 71376aa45..e878389bb 100644 --- a/contracts/libraries/VersionUtils.sol +++ b/contracts/libraries/VersionUtils.sol @@ -1,48 +1,39 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Helper library use to compare or validate the semantic versions */ library VersionUtils { - /** * @notice This function is used to validate the version submitted * @param _current Array holds the present version of ST * @param _new Array holds the latest version of the ST * @return bool */ - function isValidVersion(uint8[] _current, uint8[] _new) internal pure returns(bool) { + function isValidVersion(uint8[] memory _current, uint8[] memory _new) internal pure returns(bool) { bool[] memory _temp = new bool[](_current.length); uint8 counter = 0; - for (uint8 i = 0; i < _current.length; i++) { - if (_current[i] < _new[i]) - _temp[i] = true; - else - _temp[i] = false; + uint8 i = 0; + for (i = 0; i < _current.length; i++) { + if (_current[i] < _new[i]) _temp[i] = true; + else _temp[i] = false; } for (i = 0; i < _current.length; i++) { if (i == 0) { - if (_current[i] <= _new[i]) - if(_temp[0]) { - counter = counter + 3; - break; - } else - counter++; - else - return false; + if (_current[i] <= _new[i]) if (_temp[0]) { + counter = counter + 3; + break; + } else counter++; + else return false; } else { - if (_temp[i-1]) - counter++; - else if (_current[i] <= _new[i]) - counter++; - else - return false; + if (_temp[i - 1]) counter++; + else if (_current[i] <= _new[i]) counter++; + else return false; } } - if (counter == _current.length) - return true; + if (counter == _current.length) return true; } /** @@ -51,29 +42,22 @@ library VersionUtils { * @param _version2 Array holds the latest version of the ST * @return bool */ - function compareLowerBound(uint8[] _version1, uint8[] _version2) internal pure returns(bool) { + function compareLowerBound(uint8[] memory _version1, uint8[] memory _version2) internal pure returns(bool) { require(_version1.length == _version2.length, "Input length mismatch"); uint counter = 0; for (uint8 j = 0; j < _version1.length; j++) { - if (_version1[j] == 0) - counter ++; + if (_version1[j] == 0) counter++; } if (counter != _version1.length) { counter = 0; for (uint8 i = 0; i < _version1.length; i++) { - if (_version2[i] > _version1[i]) - return true; - else if (_version2[i] < _version1[i]) - return false; - else - counter++; + if (_version2[i] > _version1[i]) return true; + else if (_version2[i] < _version1[i]) return false; + else counter++; } - if (counter == _version1.length - 1) - return true; - else - return false; - } else - return true; + if (counter == _version1.length - 1) return true; + else return false; + } else return true; } /** @@ -82,32 +66,24 @@ library VersionUtils { * @param _version2 Array holds the latest version of the ST * @return bool */ - function compareUpperBound(uint8[] _version1, uint8[] _version2) internal pure returns(bool) { + function compareUpperBound(uint8[] memory _version1, uint8[] memory _version2) internal pure returns(bool) { require(_version1.length == _version2.length, "Input length mismatch"); uint counter = 0; for (uint8 j = 0; j < _version1.length; j++) { - if (_version1[j] == 0) - counter ++; + if (_version1[j] == 0) counter++; } if (counter != _version1.length) { counter = 0; for (uint8 i = 0; i < _version1.length; i++) { - if (_version1[i] > _version2[i]) - return true; - else if (_version1[i] < _version2[i]) - return false; - else - counter++; + if (_version1[i] > _version2[i]) return true; + else if (_version1[i] < _version2[i]) return false; + else counter++; } - if (counter == _version1.length - 1) - return true; - else - return false; - } else - return true; + if (counter == _version1.length - 1) return true; + else return false; + } else return true; } - /** * @notice Used to pack the uint8[] array data into uint24 value * @param _major Major version @@ -122,7 +98,7 @@ library VersionUtils { * @notice Used to convert packed data into uint8 array * @param _packedVersion Packed data */ - function unpack(uint24 _packedVersion) internal pure returns (uint8[]) { + function unpack(uint24 _packedVersion) internal pure returns(uint8[] memory) { uint8[] memory _unpackVersion = new uint8[](3); _unpackVersion[0] = uint8(_packedVersion >> 16); _unpackVersion[1] = uint8(_packedVersion >> 8); @@ -131,4 +107,25 @@ library VersionUtils { } + /** + * @notice Used to packed the KYC data + */ + function packKYC(uint64 _a, uint64 _b, uint64 _c, uint8 _d) internal pure returns(uint256) { + // this function packs 3 uint64 and a uint8 together in a uint256 to save storage cost + // a is rotated left by 136 bits, b is rotated left by 72 bits and c is rotated left by 8 bits. + // rotation pads empty bits with zeroes so now we can safely do a bitwise OR operation to pack + // all the variables together. + return (uint256(_a) << 136) | (uint256(_b) << 72) | (uint256(_c) << 8) | uint256(_d); + } + + /** + * @notice Used to convert packed data into KYC data + * @param _packedVersion Packed data + */ + function unpackKYC(uint256 _packedVersion) internal pure returns(uint64 fromTime, uint64 toTime, uint64 expiryTime, uint8 added) { + fromTime = uint64(_packedVersion >> 136); + toTime = uint64(_packedVersion >> 72); + expiryTime = uint64(_packedVersion >> 8); + added = uint8(_packedVersion); + } } diff --git a/contracts/libraries/VolumeRestrictionLib.sol b/contracts/libraries/VolumeRestrictionLib.sol new file mode 100644 index 000000000..589256de1 --- /dev/null +++ b/contracts/libraries/VolumeRestrictionLib.sol @@ -0,0 +1,130 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../storage/modules/TransferManager/VolumeRestrictionTMStorage.sol"; + +library VolumeRestrictionLib { + + using SafeMath for uint256; + + function _checkLengthOfArray( + address[] memory _holders, + uint256[] memory _allowedTokens, + uint256[] memory _startTimes, + uint256[] memory _rollingPeriodInDays, + uint256[] memory _endTimes, + uint256[] memory _restrictionTypes + ) + internal + pure + { + require( + _holders.length == _allowedTokens.length && + _allowedTokens.length == _startTimes.length && + _startTimes.length == _rollingPeriodInDays.length && + _rollingPeriodInDays.length == _endTimes.length && + _endTimes.length == _restrictionTypes.length, + "Length mismatch" + ); + } + + function deleteHolderFromList(VolumeRestrictionTMStorage.RestrictedData storage data, address _holder, uint8 _typeOfPeriod) public { + // Deleting the holder if holder's type of Period is `Both` type otherwise + // it will assign the given type `_typeOfPeriod` to the _holder typeOfPeriod + // `_typeOfPeriod` it always be contrary to the removing restriction + // if removing restriction is individual then typeOfPeriod is TypeOfPeriod.OneDay + // in uint8 its value is 1. if removing restriction is daily individual then typeOfPeriod + // is TypeOfPeriod.MultipleDays in uint8 its value is 0. + if (data.restrictedHolders[_holder].typeOfPeriod != uint8(VolumeRestrictionTMStorage.TypeOfPeriod.Both)) { + uint128 index = data.restrictedHolders[_holder].index; + uint256 _len = data.restrictedAddresses.length; + if (index != _len) { + data.restrictedHolders[data.restrictedAddresses[_len - 1]].index = index; + data.restrictedAddresses[index - 1] = data.restrictedAddresses[_len - 1]; + } + delete data.restrictedHolders[_holder]; + data.restrictedAddresses.length--; + } else { + data.restrictedHolders[_holder].typeOfPeriod = _typeOfPeriod; + } + } + + function addRestrictionData(VolumeRestrictionTMStorage.RestrictedData storage data, address _holder, uint8 _callFrom, uint256 _endTime) public { + uint128 index = data.restrictedHolders[_holder].index; + if (data.restrictedHolders[_holder].seen == 0) { + data.restrictedAddresses.push(_holder); + index = uint128(data.restrictedAddresses.length); + } + uint8 _type = _getTypeOfPeriod(data.restrictedHolders[_holder].typeOfPeriod, _callFrom, _endTime); + data.restrictedHolders[_holder] = VolumeRestrictionTMStorage.RestrictedHolder(uint8(1), _type, index); + } + + function _getTypeOfPeriod(uint8 _currentTypeOfPeriod, uint8 _callFrom, uint256 _endTime) internal pure returns(uint8) { + if (_currentTypeOfPeriod != _callFrom && _endTime != uint256(0)) + return uint8(VolumeRestrictionTMStorage.TypeOfPeriod.Both); + else + return _callFrom; + } + + function getRestrictionData( + VolumeRestrictionTMStorage.RestrictedData storage _holderData, + VolumeRestrictionTMStorage.IndividualRestrictions storage _individualRestrictions + ) public view returns( + address[] memory allAddresses, + uint256[] memory allowedTokens, + uint256[] memory startTime, + uint256[] memory rollingPeriodInDays, + uint256[] memory endTime, + uint8[] memory typeOfRestriction + ) + { + uint256 counter = 0; + uint256 i = 0; + for (i = 0; i < _holderData.restrictedAddresses.length; i++) { + counter = counter + (_holderData.restrictedHolders[_holderData.restrictedAddresses[i]].typeOfPeriod == uint8(2) ? 2 : 1); + } + allAddresses = new address[](counter); + allowedTokens = new uint256[](counter); + startTime = new uint256[](counter); + rollingPeriodInDays = new uint256[](counter); + endTime = new uint256[](counter); + typeOfRestriction = new uint8[](counter); + counter = 0; + for (i = 0; i < _holderData.restrictedAddresses.length; i++) { + allAddresses[counter] = _holderData.restrictedAddresses[i]; + if (_holderData.restrictedHolders[_holderData.restrictedAddresses[i]].typeOfPeriod == uint8(VolumeRestrictionTMStorage.TypeOfPeriod.MultipleDays)) { + _setValues(_individualRestrictions.individualRestriction[_holderData.restrictedAddresses[i]], allowedTokens, startTime, rollingPeriodInDays, endTime, typeOfRestriction, counter); + } + else if (_holderData.restrictedHolders[_holderData.restrictedAddresses[i]].typeOfPeriod == uint8(VolumeRestrictionTMStorage.TypeOfPeriod.OneDay)) { + _setValues(_individualRestrictions.individualDailyRestriction[_holderData.restrictedAddresses[i]], allowedTokens, startTime, rollingPeriodInDays, endTime, typeOfRestriction, counter); + } + else if (_holderData.restrictedHolders[_holderData.restrictedAddresses[i]].typeOfPeriod == uint8(VolumeRestrictionTMStorage.TypeOfPeriod.Both)) { + _setValues(_individualRestrictions.individualRestriction[_holderData.restrictedAddresses[i]], allowedTokens, startTime, rollingPeriodInDays, endTime, typeOfRestriction, counter); + counter++; + allAddresses[counter] = _holderData.restrictedAddresses[i]; + _setValues(_individualRestrictions.individualDailyRestriction[_holderData.restrictedAddresses[i]], allowedTokens, startTime, rollingPeriodInDays, endTime, typeOfRestriction, counter); + } + counter++; + } + } + + function _setValues( + VolumeRestrictionTMStorage.VolumeRestriction memory restriction, + uint256[] memory allowedTokens, + uint256[] memory startTime, + uint256[] memory rollingPeriodInDays, + uint256[] memory endTime, + uint8[] memory typeOfRestriction, + uint256 index + ) + internal + pure + { + allowedTokens[index] = restriction.allowedTokens; + startTime[index] = restriction.startTime; + rollingPeriodInDays[index] = restriction.rollingPeriodInDays; + endTime[index] = restriction.endTime; + typeOfRestriction[index] = uint8(restriction.typeOfRestriction); + } + +} diff --git a/contracts/mocks/MockBurnFactory.sol b/contracts/mocks/MockBurnFactory.sol index 6c0e47c3d..edb84d26d 100644 --- a/contracts/mocks/MockBurnFactory.sol +++ b/contracts/mocks/MockBurnFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./MockRedemptionManager.sol"; import "../modules/Experimental/Burn/TrackedRedemptionFactory.sol"; @@ -9,26 +9,38 @@ import "../modules/Experimental/Burn/TrackedRedemptionFactory.sol"; contract MockBurnFactory is TrackedRedemptionFactory { - /** - * @notice Constructor - * @param _polyAddress Address of the polytoken - */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - TrackedRedemptionFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _polymathRegistry Address of the Polymath Registry + */ + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry + ) + public + TrackedRedemptionFactory(_setupCost, _usageCost, _polymathRegistry) { + } /** * @notice Used to launch the Module with the help of factory * @return Address Contract address of the Module */ - function deploy(bytes /*_data*/) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Unable to pay setup cost"); + function deploy( + bytes calldata /*_data*/ + ) + external + returns(address) + { + address polyToken = _takeFee(); //Check valid bytes - can only call module init function - MockRedemptionManager mockRedemptionManager = new MockRedemptionManager(msg.sender, address(polyToken)); + MockRedemptionManager mockRedemptionManager = new MockRedemptionManager(msg.sender, polyToken); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(mockRedemptionManager), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(address(mockRedemptionManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return address(mockRedemptionManager); } diff --git a/contracts/mocks/MockFactory.sol b/contracts/mocks/MockFactory.sol index 627efb71c..a9a8d5695 100644 --- a/contracts/mocks/MockFactory.sol +++ b/contracts/mocks/MockFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../modules/STO/DummySTOFactory.sol"; @@ -7,22 +7,30 @@ import "../modules/STO/DummySTOFactory.sol"; */ contract MockFactory is DummySTOFactory { - bool public switchTypes = false; - /** + + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath Registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - DummySTOFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + DummySTOFactory(_setupCost, _usageCost, _logicContract, _polymathRegistry) { - } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { if (!switchTypes) { uint8[] memory types = new uint8[](0); return types; @@ -32,7 +40,7 @@ contract MockFactory is DummySTOFactory { res[1] = 1; return res; } - + } function changeTypes() external onlyOwner { diff --git a/contracts/mocks/MockModuleRegistry.sol b/contracts/mocks/MockModuleRegistry.sol index 5711108c1..8dccc7bf6 100644 --- a/contracts/mocks/MockModuleRegistry.sol +++ b/contracts/mocks/MockModuleRegistry.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../ModuleRegistry.sol"; @@ -6,14 +6,12 @@ import "../ModuleRegistry.sol"; * @title Registry contract for issuers to register their security tokens */ contract MockModuleRegistry is ModuleRegistry { - /// @notice It is dummy functionality /// Alert! Alert! Do not use it for the mainnet release - function addMoreReputation(address _moduleFactory, address[] _tokens) public onlyOwner { + function addMoreReputation(address _moduleFactory, address[] memory _tokens) public onlyOwner { for (uint8 i = 0; i < _tokens.length; i++) { pushArray(Encoder.getKey("reputation", _moduleFactory), _tokens[i]); } } - } diff --git a/contracts/mocks/MockOracle.sol b/contracts/mocks/MockOracle.sol index d7cf14a2a..511c1ef5b 100644 --- a/contracts/mocks/MockOracle.sol +++ b/contracts/mocks/MockOracle.sol @@ -1,9 +1,8 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../interfaces/IOracle.sol"; contract MockOracle is IOracle { - address public currency; bytes32 public currencySymbol; bytes32 public denominatedCurrency; @@ -44,7 +43,7 @@ contract MockOracle is IOracle { /** * @notice Returns price - should throw if not valid */ - function getPrice() external view returns(uint256) { + function getPrice() external returns(uint256) { return price; } diff --git a/contracts/mocks/MockPolyOracle.sol b/contracts/mocks/MockPolyOracle.sol index 681bb2681..15041a42d 100644 --- a/contracts/mocks/MockPolyOracle.sol +++ b/contracts/mocks/MockPolyOracle.sol @@ -1,11 +1,10 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../oracles/PolyOracle.sol"; contract MockPolyOracle is PolyOracle { - - constructor() payable public { + constructor() public payable { OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); } -} \ No newline at end of file +} diff --git a/contracts/mocks/MockRedemptionManager.sol b/contracts/mocks/MockRedemptionManager.sol index 02fbb2e81..6ba5994d4 100644 --- a/contracts/mocks/MockRedemptionManager.sol +++ b/contracts/mocks/MockRedemptionManager.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../modules/Experimental/Burn/TrackedRedemption.sol"; @@ -6,19 +6,16 @@ import "../modules/Experimental/Burn/TrackedRedemption.sol"; * @title Burn module for burning tokens and keeping track of burnt amounts */ contract MockRedemptionManager is TrackedRedemption { - - mapping (address => uint256) tokenToRedeem; + mapping(address => uint256) tokenToRedeem; event RedeemedTokenByOwner(address _investor, address _byWhoom, uint256 _value, uint256 _timestamp); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public - TrackedRedemption(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public TrackedRedemption(_securityToken, _polyToken) { + } /** @@ -38,7 +35,7 @@ contract MockRedemptionManager is TrackedRedemption { require(tokenToRedeem[msg.sender] >= _value, "Insufficient tokens redeemable"); tokenToRedeem[msg.sender] = tokenToRedeem[msg.sender].sub(_value); redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value); - ISecurityToken(securityToken).burnWithData(_value, ""); + ISecurityToken(securityToken).redeem(_value, ""); /*solium-disable-next-line security/no-block-members*/ emit RedeemedTokenByOwner(msg.sender, address(this), _value, now); } diff --git a/contracts/mocks/MockWrongTypeFactory.sol b/contracts/mocks/MockWrongTypeFactory.sol index f04a3a2de..27f256023 100644 --- a/contracts/mocks/MockWrongTypeFactory.sol +++ b/contracts/mocks/MockWrongTypeFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./MockBurnFactory.sol"; import "../modules/ModuleFactory.sol"; @@ -9,20 +9,27 @@ import "../libraries/Util.sol"; */ contract MockWrongTypeFactory is MockBurnFactory { - - /** - * @notice Constructor - * @param _polyAddress Address of the polytoken - */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - MockBurnFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _polymathRegistry Address of the Polymath Registry + */ + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry + ) + public + MockBurnFactory(_setupCost, _usageCost, _polymathRegistry) { + } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory types = new uint8[](1); types[0] = 4; return types; diff --git a/contracts/mocks/PolyTokenFaucet.sol b/contracts/mocks/PolyTokenFaucet.sol index 76f1a5596..4ebd9863f 100644 --- a/contracts/mocks/PolyTokenFaucet.sol +++ b/contracts/mocks/PolyTokenFaucet.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; @@ -8,7 +8,6 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; */ contract PolyTokenFaucet { - using SafeMath for uint256; uint256 totalSupply_; string public name = "Polymath Network"; @@ -18,19 +17,19 @@ contract PolyTokenFaucet { mapping(address => uint256) balances; mapping(address => mapping(address => uint256)) allowed; - event Transfer(address indexed _from, address indexed _to, uint256 _value); - event Approval(address indexed _owner, address indexed _spender, uint256 _value); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); constructor() public { decimals = 18; - totalSupply_ = 1000000 * uint256(10)**decimals; + totalSupply_ = 1000000 * uint256(10) ** decimals; balances[msg.sender] = totalSupply_; emit Transfer(address(0), msg.sender, totalSupply_); } /* Token faucet - Not part of the ERC20 standard */ - function getTokens(uint256 _amount, address _recipient) public returns (bool) { - require(_amount <= 1000000 * uint256(10)**decimals, "Amount should not exceed 1 million"); + function getTokens(uint256 _amount, address _recipient) public returns(bool) { + require(_amount <= 1000000 * uint256(10) ** decimals, "Amount should not exceed 1 million"); require(_recipient != address(0), "Recipient address can not be empty"); balances[_recipient] = balances[_recipient].add(_amount); totalSupply_ = totalSupply_.add(_amount); @@ -44,7 +43,7 @@ contract PolyTokenFaucet { * @param _value The amount of token to be transferred * @return Whether the transfer was successful or not */ - function transfer(address _to, uint256 _value) public returns (bool) { + function transfer(address _to, uint256 _value) public returns(bool) { balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); @@ -58,7 +57,7 @@ contract PolyTokenFaucet { * @param _value The amount of token to be transferred * @return Whether the transfer was successful or not */ - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + function transferFrom(address _from, address _to, uint256 _value) public returns(bool) { require(_to != address(0), "Invalid address"); require(_value <= balances[_from], "Insufficient tokens transferable"); require(_value <= allowed[_from][msg.sender], "Insufficient tokens allowable"); @@ -75,7 +74,7 @@ contract PolyTokenFaucet { * @param _owner The address from which the balance will be retrieved * @return The balance */ - function balanceOf(address _owner) public view returns (uint256 balance) { + function balanceOf(address _owner) public view returns(uint256 balance) { return balances[_owner]; } @@ -85,7 +84,7 @@ contract PolyTokenFaucet { * @param _value The amount of tokens to be approved for transfer * @return Whether the approval was successful or not */ - function approve(address _spender, uint256 _value) public returns (bool) { + function approve(address _spender, uint256 _value) public returns(bool) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; @@ -96,11 +95,11 @@ contract PolyTokenFaucet { * @param _spender The address of the account able to transfer the tokens * @return Amount of remaining tokens allowed to be spent */ - function allowance(address _owner, address _spender) public view returns (uint256 remaining) { + function allowance(address _owner, address _spender) public view returns(uint256 remaining) { return allowed[_owner][_spender]; } - function totalSupply() public view returns (uint256) { + function totalSupply() public view returns(uint256) { return totalSupply_; } @@ -113,15 +112,8 @@ contract PolyTokenFaucet { * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ - function increaseApproval( - address _spender, - uint _addedValue - ) - public - returns (bool) - { - allowed[msg.sender][_spender] = ( - allowed[msg.sender][_spender].add(_addedValue)); + function increaseApproval(address _spender, uint _addedValue) public returns(bool) { + allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].add(_addedValue)); emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } @@ -136,13 +128,7 @@ contract PolyTokenFaucet { * @param _spender The address which will spend the funds. * @param _subtractedValue The amount of tokens to decrease the allowance by. */ - function decreaseApproval( - address _spender, - uint _subtractedValue - ) - public - returns (bool) - { + function decreaseApproval(address _spender, uint _subtractedValue) public returns(bool) { uint oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; diff --git a/contracts/mocks/SecurityTokenRegistryMock.sol b/contracts/mocks/SecurityTokenRegistryMock.sol index d0a6de099..2ec9d8edd 100644 --- a/contracts/mocks/SecurityTokenRegistryMock.sol +++ b/contracts/mocks/SecurityTokenRegistryMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../SecurityTokenRegistry.sol"; @@ -6,11 +6,10 @@ import "../SecurityTokenRegistry.sol"; * @title Registry contract for issuers to register their security tokens */ contract SecurityTokenRegistryMock is SecurityTokenRegistry { - /// @notice It is a dummy function - /// Alert! Alert! Do not use it for the mainnet release - function changeTheDeployedAddress(string _ticker, address _newSecurityTokenAddress) public { + /// Alert! Alert! Do NOT use it for the mainnet release + function changeTheDeployedAddress(string memory _ticker, address _newSecurityTokenAddress) public { set(Encoder.getKey("tickerToSecurityToken", _ticker), _newSecurityTokenAddress); - } - + } + } diff --git a/contracts/mocks/TestSTOFactory.sol b/contracts/mocks/TestSTOFactory.sol index a8bcbbeb6..2117f3796 100644 --- a/contracts/mocks/TestSTOFactory.sol +++ b/contracts/mocks/TestSTOFactory.sol @@ -1,15 +1,23 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../modules/STO/DummySTOFactory.sol"; contract TestSTOFactory is DummySTOFactory { - /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath Registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - DummySTOFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + DummySTOFactory(_setupCost, _usageCost, _logicContract, _polymathRegistry) { version = "1.0.0"; name = "TestSTO"; @@ -22,14 +30,14 @@ contract TestSTOFactory is DummySTOFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Test STO - you can mint tokens at will"; } /** * @notice Gets the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](4); availableTags[0] = "Test"; availableTags[1] = "Non-refundable"; diff --git a/contracts/modules/Burn/IBurn.sol b/contracts/modules/Burn/IBurn.sol index 7a84a802b..64a9b7de9 100644 --- a/contracts/modules/Burn/IBurn.sol +++ b/contracts/modules/Burn/IBurn.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface to be implemented by all checkpoint modules diff --git a/contracts/modules/Checkpoint/DividendCheckpoint.sol b/contracts/modules/Checkpoint/DividendCheckpoint.sol index 452a48b13..ebb4e944e 100644 --- a/contracts/modules/Checkpoint/DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/DividendCheckpoint.sol @@ -1,273 +1,399 @@ -/** - * DISCLAIMER: Under certain conditions, the function pushDividendPayment - * may fail due to block gas limits. - * If the total number of investors that ever held tokens is greater than ~15,000 then - * the function may fail. If this happens investors can pull their dividends, or the Issuer - * can use pushDividendPaymentToAddresses to provide an explict address list in batches - */ -pragma solidity ^0.4.24; - -import "./ICheckpoint.sol"; -import "../Module.sol"; -import "../../interfaces/ISecurityToken.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "openzeppelin-solidity/contracts/math/Math.sol"; - -/** - * @title Checkpoint module for issuing ether dividends - * @dev abstract contract - */ -contract DividendCheckpoint is ICheckpoint, Module { - using SafeMath for uint256; - - uint256 public EXCLUDED_ADDRESS_LIMIT = 50; - bytes32 public constant DISTRIBUTE = "DISTRIBUTE"; - bytes32 public constant MANAGE = "MANAGE"; - bytes32 public constant CHECKPOINT = "CHECKPOINT"; - - struct Dividend { - uint256 checkpointId; - uint256 created; // Time at which the dividend was created - uint256 maturity; // Time after which dividend can be claimed - set to 0 to bypass - uint256 expiry; // Time until which dividend can be claimed - after this time any remaining amount can be withdrawn by issuer - - // set to very high value to bypass - uint256 amount; // Dividend amount in WEI - uint256 claimedAmount; // Amount of dividend claimed so far - uint256 totalSupply; // Total supply at the associated checkpoint (avoids recalculating this) - bool reclaimed; // True if expiry has passed and issuer has reclaimed remaining dividend - uint256 dividendWithheld; - uint256 dividendWithheldReclaimed; - mapping (address => bool) claimed; // List of addresses which have claimed dividend - mapping (address => bool) dividendExcluded; // List of addresses which cannot claim dividends - bytes32 name; // Name/title - used for identification - } - - // List of all dividends - Dividend[] public dividends; - - // List of addresses which cannot claim dividends - address[] public excluded; - - // Mapping from address to withholding tax as a percentage * 10**16 - mapping (address => uint256) public withholdingTax; - - // Total amount of ETH withheld per investor - mapping (address => uint256) public investorWithheld; - - event SetDefaultExcludedAddresses(address[] _excluded, uint256 _timestamp); - event SetWithholding(address[] _investors, uint256[] _withholding, uint256 _timestamp); - event SetWithholdingFixed(address[] _investors, uint256 _withholding, uint256 _timestamp); - - modifier validDividendIndex(uint256 _dividendIndex) { - require(_dividendIndex < dividends.length, "Invalid dividend"); - require(!dividends[_dividendIndex].reclaimed, "Dividend reclaimed"); - /*solium-disable-next-line security/no-block-members*/ - require(now >= dividends[_dividendIndex].maturity, "Dividend maturity in future"); - /*solium-disable-next-line security/no-block-members*/ - require(now < dividends[_dividendIndex].expiry, "Dividend expiry in past"); - _; - } - - /** - * @notice Init function i.e generalise function to maintain the structure of the module contract - * @return bytes4 - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(0); - } - - /** - * @notice Return the default excluded addresses - * @return List of excluded addresses - */ - function getDefaultExcluded() external view returns (address[]) { - return excluded; - } - - /** - * @notice Creates a checkpoint on the security token - * @return Checkpoint ID - */ - function createCheckpoint() public withPerm(CHECKPOINT) returns (uint256) { - return ISecurityToken(securityToken).createCheckpoint(); - } - - /** - * @notice Function to clear and set list of excluded addresses used for future dividends - * @param _excluded Addresses of investors - */ - function setDefaultExcluded(address[] _excluded) public withPerm(MANAGE) { - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many excluded addresses"); - for (uint256 j = 0; j < _excluded.length; j++) { - require (_excluded[j] != address(0), "Invalid address"); - for (uint256 i = j + 1; i < _excluded.length; i++) { - require (_excluded[j] != _excluded[i], "Duplicate exclude address"); - } - } - excluded = _excluded; - /*solium-disable-next-line security/no-block-members*/ - emit SetDefaultExcludedAddresses(excluded, now); - } - - /** - * @notice Function to set withholding tax rates for investors - * @param _investors Addresses of investors - * @param _withholding Withholding tax for individual investors (multiplied by 10**16) - */ - function setWithholding(address[] _investors, uint256[] _withholding) public withPerm(MANAGE) { - require(_investors.length == _withholding.length, "Mismatched input lengths"); - /*solium-disable-next-line security/no-block-members*/ - emit SetWithholding(_investors, _withholding, now); - for (uint256 i = 0; i < _investors.length; i++) { - require(_withholding[i] <= 10**18, "Incorrect withholding tax"); - withholdingTax[_investors[i]] = _withholding[i]; - } - } - - /** - * @notice Function to set withholding tax rates for investors - * @param _investors Addresses of investor - * @param _withholding Withholding tax for all investors (multiplied by 10**16) - */ - function setWithholdingFixed(address[] _investors, uint256 _withholding) public withPerm(MANAGE) { - require(_withholding <= 10**18, "Incorrect withholding tax"); - /*solium-disable-next-line security/no-block-members*/ - emit SetWithholdingFixed(_investors, _withholding, now); - for (uint256 i = 0; i < _investors.length; i++) { - withholdingTax[_investors[i]] = _withholding; - } - } - - /** - * @notice Issuer can push dividends to provided addresses - * @param _dividendIndex Dividend to push - * @param _payees Addresses to which to push the dividend - */ - function pushDividendPaymentToAddresses( - uint256 _dividendIndex, - address[] _payees - ) - public - withPerm(DISTRIBUTE) - validDividendIndex(_dividendIndex) - { - Dividend storage dividend = dividends[_dividendIndex]; - for (uint256 i = 0; i < _payees.length; i++) { - if ((!dividend.claimed[_payees[i]]) && (!dividend.dividendExcluded[_payees[i]])) { - _payDividend(_payees[i], dividend, _dividendIndex); - } - } - } - - /** - * @notice Issuer can push dividends using the investor list from the security token - * @param _dividendIndex Dividend to push - * @param _start Index in investor list at which to start pushing dividends - * @param _iterations Number of addresses to push dividends for - */ - function pushDividendPayment( - uint256 _dividendIndex, - uint256 _start, - uint256 _iterations - ) - public - withPerm(DISTRIBUTE) - validDividendIndex(_dividendIndex) - { - Dividend storage dividend = dividends[_dividendIndex]; - address[] memory investors = ISecurityToken(securityToken).getInvestors(); - uint256 numberInvestors = Math.min256(investors.length, _start.add(_iterations)); - for (uint256 i = _start; i < numberInvestors; i++) { - address payee = investors[i]; - if ((!dividend.claimed[payee]) && (!dividend.dividendExcluded[payee])) { - _payDividend(payee, dividend, _dividendIndex); - } - } - } - - /** - * @notice Investors can pull their own dividends - * @param _dividendIndex Dividend to pull - */ - function pullDividendPayment(uint256 _dividendIndex) public validDividendIndex(_dividendIndex) - { - Dividend storage dividend = dividends[_dividendIndex]; - require(!dividend.claimed[msg.sender], "Dividend already claimed"); - require(!dividend.dividendExcluded[msg.sender], "msg.sender excluded from Dividend"); - _payDividend(msg.sender, dividend, _dividendIndex); - } - - /** - * @notice Internal function for paying dividends - * @param _payee Address of investor - * @param _dividend Storage with previously issued dividends - * @param _dividendIndex Dividend to pay - */ - function _payDividend(address _payee, Dividend storage _dividend, uint256 _dividendIndex) internal; - - /** - * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends - * @param _dividendIndex Dividend to reclaim - */ - function reclaimDividend(uint256 _dividendIndex) external; - - /** - * @notice Calculate amount of dividends claimable - * @param _dividendIndex Dividend to calculate - * @param _payee Affected investor address - * @return claim, withheld amounts - */ - function calculateDividend(uint256 _dividendIndex, address _payee) public view returns(uint256, uint256) { - require(_dividendIndex < dividends.length, "Invalid dividend"); - Dividend storage dividend = dividends[_dividendIndex]; - if (dividend.claimed[_payee] || dividend.dividendExcluded[_payee]) { - return (0, 0); - } - uint256 balance = ISecurityToken(securityToken).balanceOfAt(_payee, dividend.checkpointId); - uint256 claim = balance.mul(dividend.amount).div(dividend.totalSupply); - uint256 withheld = claim.mul(withholdingTax[_payee]).div(uint256(10**18)); - return (claim, withheld); - } - - /** - * @notice Get the index according to the checkpoint id - * @param _checkpointId Checkpoint id to query - * @return uint256[] - */ - function getDividendIndex(uint256 _checkpointId) public view returns(uint256[]) { - uint256 counter = 0; - for(uint256 i = 0; i < dividends.length; i++) { - if (dividends[i].checkpointId == _checkpointId) { - counter++; - } - } - - uint256[] memory index = new uint256[](counter); - counter = 0; - for(uint256 j = 0; j < dividends.length; j++) { - if (dividends[j].checkpointId == _checkpointId) { - index[counter] = j; - counter++; - } - } - return index; - } - - /** - * @notice Allows issuer to withdraw withheld tax - * @param _dividendIndex Dividend to withdraw from - */ - function withdrawWithholding(uint256 _dividendIndex) external; - - /** - * @notice Return the permissions flag that are associated with this module - * @return bytes32 array - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](2); - allPermissions[0] = DISTRIBUTE; - allPermissions[1] = MANAGE; - return allPermissions; - } - -} +/** + * DISCLAIMER: Under certain conditions, the function pushDividendPayment + * may fail due to block gas limits. + * If the total number of investors that ever held tokens is greater than ~15,000 then + * the function may fail. If this happens investors can pull their dividends, or the Issuer + * can use pushDividendPaymentToAddresses to provide an explict address list in batches + */ +pragma solidity ^0.5.0; + +import "./ICheckpoint.sol"; +import "../../storage/modules/Checkpoint/DividendCheckpointStorage.sol"; +import "../Module.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/math/Math.sol"; + +/** + * @title Checkpoint module for issuing ether dividends + * @dev abstract contract + */ +contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module { + using SafeMath for uint256; + + event SetDefaultExcludedAddresses(address[] _excluded); + event SetWithholding(address[] _investors, uint256[] _withholding); + event SetWithholdingFixed(address[] _investors, uint256 _withholding); + event SetWallet(address indexed _oldWallet, address indexed _newWallet); + + modifier validDividendIndex(uint256 _dividendIndex) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + require(!dividends[_dividendIndex].reclaimed, "Dividend reclaimed"); + /*solium-disable-next-line security/no-block-members*/ + require(now >= dividends[_dividendIndex].maturity, "Dividend maturity in future"); + /*solium-disable-next-line security/no-block-members*/ + require(now < dividends[_dividendIndex].expiry, "Dividend expiry in past"); + _; + } + + /** + * @notice Function used to intialize the contract variables + * @param _wallet Ethereum account address to receive reclaimed dividends and tax + */ + function configure( + address payable _wallet + ) public onlyFactory { + _setWallet(_wallet); + } + + /** + * @notice Init function i.e generalise function to maintain the structure of the module contract + * @return bytes4 + */ + function getInitFunction() public pure returns(bytes4) { + return this.configure.selector; + } + + /** + * @notice Function used to change wallet address + * @param _wallet Ethereum account address to receive reclaimed dividends and tax + */ + function changeWallet(address payable _wallet) external onlyOwner { + _setWallet(_wallet); + } + + function _setWallet(address payable _wallet) internal { + require(_wallet != address(0)); + emit SetWallet(wallet, _wallet); + wallet = _wallet; + } + + /** + * @notice Return the default excluded addresses + * @return List of excluded addresses + */ + function getDefaultExcluded() external view returns(address[] memory) { + return excluded; + } + + /** + * @notice Creates a checkpoint on the security token + * @return Checkpoint ID + */ + function createCheckpoint() public withPerm(CHECKPOINT) returns(uint256) { + return ISecurityToken(securityToken).createCheckpoint(); + } + + /** + * @notice Function to clear and set list of excluded addresses used for future dividends + * @param _excluded Addresses of investors + */ + function setDefaultExcluded(address[] memory _excluded) public withPerm(MANAGE) { + require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many excluded addresses"); + for (uint256 j = 0; j < _excluded.length; j++) { + require(_excluded[j] != address(0), "Invalid address"); + for (uint256 i = j + 1; i < _excluded.length; i++) { + require(_excluded[j] != _excluded[i], "Duplicate exclude address"); + } + } + excluded = _excluded; + /*solium-disable-next-line security/no-block-members*/ + emit SetDefaultExcludedAddresses(excluded); + } + + /** + * @notice Function to set withholding tax rates for investors + * @param _investors Addresses of investors + * @param _withholding Withholding tax for individual investors (multiplied by 10**16) + */ + function setWithholding(address[] memory _investors, uint256[] memory _withholding) public withPerm(MANAGE) { + require(_investors.length == _withholding.length, "Mismatched input lengths"); + /*solium-disable-next-line security/no-block-members*/ + emit SetWithholding(_investors, _withholding); + for (uint256 i = 0; i < _investors.length; i++) { + require(_withholding[i] <= 10 ** 18, "Incorrect withholding tax"); + withholdingTax[_investors[i]] = _withholding[i]; + } + } + + /** + * @notice Function to set withholding tax rates for investors + * @param _investors Addresses of investor + * @param _withholding Withholding tax for all investors (multiplied by 10**16) + */ + function setWithholdingFixed(address[] memory _investors, uint256 _withholding) public withPerm(MANAGE) { + require(_withholding <= 10 ** 18, "Incorrect withholding tax"); + /*solium-disable-next-line security/no-block-members*/ + emit SetWithholdingFixed(_investors, _withholding); + for (uint256 i = 0; i < _investors.length; i++) { + withholdingTax[_investors[i]] = _withholding; + } + } + + /** + * @notice Issuer can push dividends to provided addresses + * @param _dividendIndex Dividend to push + * @param _payees Addresses to which to push the dividend + */ + function pushDividendPaymentToAddresses( + uint256 _dividendIndex, + address payable[] memory _payees + ) + public + withPerm(DISTRIBUTE) + validDividendIndex(_dividendIndex) + { + Dividend storage dividend = dividends[_dividendIndex]; + for (uint256 i = 0; i < _payees.length; i++) { + if ((!dividend.claimed[_payees[i]]) && (!dividend.dividendExcluded[_payees[i]])) { + _payDividend(_payees[i], dividend, _dividendIndex); + } + } + } + + /** + * @notice Issuer can push dividends using the investor list from the security token + * @param _dividendIndex Dividend to push + * @param _start Index in investor list at which to start pushing dividends + * @param _iterations Number of addresses to push dividends for + */ + function pushDividendPayment( + uint256 _dividendIndex, + uint256 _start, + uint256 _iterations + ) public + withPerm(DISTRIBUTE) + validDividendIndex(_dividendIndex) + { + Dividend storage dividend = dividends[_dividendIndex]; + uint256 checkpointId = dividend.checkpointId; + address[] memory investors = ISecurityToken(securityToken).getInvestorsAt(checkpointId); + uint256 numberInvestors = Math.min(investors.length, _start.add(_iterations)); + for (uint256 i = _start; i < numberInvestors; i++) { + address payable payee = address(uint160(investors[i])); + if ((!dividend.claimed[payee]) && (!dividend.dividendExcluded[payee])) { + _payDividend(payee, dividend, _dividendIndex); + } + } + } + + /** + * @notice Investors can pull their own dividends + * @param _dividendIndex Dividend to pull + */ + function pullDividendPayment(uint256 _dividendIndex) public validDividendIndex(_dividendIndex) { + Dividend storage dividend = dividends[_dividendIndex]; + require(!dividend.claimed[msg.sender], "Dividend already claimed"); + require(!dividend.dividendExcluded[msg.sender], "msg.sender excluded from Dividend"); + _payDividend(msg.sender, dividend, _dividendIndex); + } + + /** + * @notice Internal function for paying dividends + * @param _payee Address of investor + * @param _dividend Storage with previously issued dividends + * @param _dividendIndex Dividend to pay + */ + function _payDividend(address payable _payee, Dividend storage _dividend, uint256 _dividendIndex) internal; + + /** + * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends + * @param _dividendIndex Dividend to reclaim + */ + function reclaimDividend(uint256 _dividendIndex) external; + + /** + * @notice Calculate amount of dividends claimable + * @param _dividendIndex Dividend to calculate + * @param _payee Affected investor address + * @return claim, withheld amounts + */ + function calculateDividend(uint256 _dividendIndex, address _payee) public view returns(uint256, uint256) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + Dividend storage dividend = dividends[_dividendIndex]; + if (dividend.claimed[_payee] || dividend.dividendExcluded[_payee]) { + return (0, 0); + } + uint256 balance = ISecurityToken(securityToken).balanceOfAt(_payee, dividend.checkpointId); + uint256 claim = balance.mul(dividend.amount).div(dividend.totalSupply); + uint256 withheld = claim.mul(withholdingTax[_payee]).div(uint256(10 ** 18)); + return (claim, withheld); + } + + /** + * @notice Get the index according to the checkpoint id + * @param _checkpointId Checkpoint id to query + * @return uint256[] + */ + function getDividendIndex(uint256 _checkpointId) public view returns(uint256[] memory) { + uint256 counter = 0; + for (uint256 i = 0; i < dividends.length; i++) { + if (dividends[i].checkpointId == _checkpointId) { + counter++; + } + } + + uint256[] memory index = new uint256[](counter); + counter = 0; + for (uint256 j = 0; j < dividends.length; j++) { + if (dividends[j].checkpointId == _checkpointId) { + index[counter] = j; + counter++; + } + } + return index; + } + + /** + * @notice Allows issuer to withdraw withheld tax + * @param _dividendIndex Dividend to withdraw from + */ + function withdrawWithholding(uint256 _dividendIndex) external; + + /** + * @notice Get static dividend data + * @return uint256[] timestamp of dividends creation + * @return uint256[] timestamp of dividends maturity + * @return uint256[] timestamp of dividends expiry + * @return uint256[] amount of dividends + * @return uint256[] claimed amount of dividends + * @return bytes32[] name of dividends + */ + function getDividendsData() external view returns ( + uint256[] memory createds, + uint256[] memory maturitys, + uint256[] memory expirys, + uint256[] memory amounts, + uint256[] memory claimedAmounts, + bytes32[] memory names) + { + createds = new uint256[](dividends.length); + maturitys = new uint256[](dividends.length); + expirys = new uint256[](dividends.length); + amounts = new uint256[](dividends.length); + claimedAmounts = new uint256[](dividends.length); + names = new bytes32[](dividends.length); + for (uint256 i = 0; i < dividends.length; i++) { + (createds[i], maturitys[i], expirys[i], amounts[i], claimedAmounts[i], names[i]) = getDividendData(i); + } + } + + /** + * @notice Get static dividend data + * @return uint256 timestamp of dividend creation + * @return uint256 timestamp of dividend maturity + * @return uint256 timestamp of dividend expiry + * @return uint256 amount of dividend + * @return uint256 claimed amount of dividend + * @return bytes32 name of dividend + */ + function getDividendData(uint256 _dividendIndex) public view returns ( + uint256 created, + uint256 maturity, + uint256 expiry, + uint256 amount, + uint256 claimedAmount, + bytes32 name) + { + created = dividends[_dividendIndex].created; + maturity = dividends[_dividendIndex].maturity; + expiry = dividends[_dividendIndex].expiry; + amount = dividends[_dividendIndex].amount; + claimedAmount = dividends[_dividendIndex].claimedAmount; + name = dividends[_dividendIndex].name; + } + + /** + * @notice Retrieves list of investors, their claim status and whether they are excluded + * @param _dividendIndex Dividend to withdraw from + * @return address[] list of investors + * @return bool[] whether investor has claimed + * @return bool[] whether investor is excluded + * @return uint256[] amount of withheld tax (estimate if not claimed) + * @return uint256[] amount of claim (estimate if not claimeed) + * @return uint256[] investor balance + */ + function getDividendProgress(uint256 _dividendIndex) external view returns ( + address[] memory investors, + bool[] memory resultClaimed, + bool[] memory resultExcluded, + uint256[] memory resultWithheld, + uint256[] memory resultAmount, + uint256[] memory resultBalance) + { + require(_dividendIndex < dividends.length, "Invalid dividend"); + //Get list of Investors + Dividend storage dividend = dividends[_dividendIndex]; + uint256 checkpointId = dividend.checkpointId; + investors = ISecurityToken(securityToken).getInvestorsAt(checkpointId); + resultClaimed = new bool[](investors.length); + resultExcluded = new bool[](investors.length); + resultWithheld = new uint256[](investors.length); + resultAmount = new uint256[](investors.length); + resultBalance = new uint256[](investors.length); + for (uint256 i; i < investors.length; i++) { + resultClaimed[i] = dividend.claimed[investors[i]]; + resultExcluded[i] = dividend.dividendExcluded[investors[i]]; + resultBalance[i] = ISecurityToken(securityToken).balanceOfAt(investors[i], dividend.checkpointId); + if (!resultExcluded[i]) { + if (resultClaimed[i]) { + resultWithheld[i] = dividend.withheld[investors[i]]; + resultAmount[i] = resultBalance[i].mul(dividend.amount).div(dividend.totalSupply).sub(resultWithheld[i]); + } else { + (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, investors[i]); + resultWithheld[i] = withheld; + resultAmount[i] = claim.sub(withheld); + } + } + } + } + + /** + * @notice Retrieves list of investors, their balances, and their current withholding tax percentage + * @param _checkpointId Checkpoint Id to query for + * @return address[] list of investors + * @return uint256[] investor balances + * @return uint256[] investor withheld percentages + */ + function getCheckpointData(uint256 _checkpointId) external view returns (address[] memory investors, uint256[] memory balances, uint256[] memory withholdings) { + require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId(), "Invalid checkpoint"); + investors = ISecurityToken(securityToken).getInvestorsAt(_checkpointId); + balances = new uint256[](investors.length); + withholdings = new uint256[](investors.length); + for (uint256 i; i < investors.length; i++) { + balances[i] = ISecurityToken(securityToken).balanceOfAt(investors[i], _checkpointId); + withholdings[i] = withholdingTax[investors[i]]; + } + } + + /** + * @notice Checks whether an address is excluded from claiming a dividend + * @param _dividendIndex Dividend to withdraw from + * @return bool whether the address is excluded + */ + function isExcluded(address _investor, uint256 _dividendIndex) external view returns (bool) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + return dividends[_dividendIndex].dividendExcluded[_investor]; + } + + /** + * @notice Checks whether an address has claimed a dividend + * @param _dividendIndex Dividend to withdraw from + * @return bool whether the address has claimed + */ + function isClaimed(address _investor, uint256 _dividendIndex) external view returns (bool) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + return dividends[_dividendIndex].claimed[_investor]; + } + + /** + * @notice Return the permissions flag that are associated with this module + * @return bytes32 array + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](2); + allPermissions[0] = DISTRIBUTE; + allPermissions[1] = MANAGE; + return allPermissions; + } + +} diff --git a/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol b/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol index 1f7f25cd5..68d24c38e 100644 --- a/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol @@ -1,282 +1,279 @@ -pragma solidity ^0.4.24; - -import "./DividendCheckpoint.sol"; -import "../../interfaces/IOwnable.sol"; -import "../../interfaces/IERC20.sol"; - -/** - * @title Checkpoint module for issuing ERC20 dividends - */ -contract ERC20DividendCheckpoint is DividendCheckpoint { - using SafeMath for uint256; - - // Mapping to token address for each dividend - mapping (uint256 => address) public dividendTokens; - event ERC20DividendDeposited( - address indexed _depositor, - uint256 _checkpointId, - uint256 _created, - uint256 _maturity, - uint256 _expiry, - address indexed _token, - uint256 _amount, - uint256 _totalSupply, - uint256 _dividendIndex, - bytes32 indexed _name - ); - event ERC20DividendClaimed( - address indexed _payee, - uint256 _dividendIndex, - address indexed _token, - uint256 _amount, - uint256 _withheld - ); - event ERC20DividendReclaimed( - address indexed _claimer, - uint256 _dividendIndex, - address indexed _token, - uint256 _claimedAmount - ); - event ERC20DividendWithholdingWithdrawn( - address indexed _claimer, - uint256 _dividendIndex, - address indexed _token, - uint256 _withheldAmount - ); - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { - } - - /** - * @notice Creates a dividend and checkpoint for the dividend - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _token Address of ERC20 token in which dividend is to be denominated - * @param _amount Amount of specified token for dividend - * @param _name Name/Title for identification - */ - function createDividend( - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - bytes32 _name - ) - external - withPerm(MANAGE) - { - createDividendWithExclusions(_maturity, _expiry, _token, _amount, excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _token Address of ERC20 token in which dividend is to be denominated - * @param _amount Amount of specified token for dividend - * @param _checkpointId Checkpoint id from which to create dividends - * @param _name Name/Title for identification - */ - function createDividendWithCheckpoint( - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - uint256 _checkpointId, - bytes32 _name - ) - external - withPerm(MANAGE) - { - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, _checkpointId, excluded, _name); - } - - /** - * @notice Creates a dividend and checkpoint for the dividend - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _token Address of ERC20 token in which dividend is to be denominated - * @param _amount Amount of specified token for dividend - * @param _excluded List of addresses to exclude - * @param _name Name/Title for identification - */ - function createDividendWithExclusions( - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - address[] _excluded, - bytes32 _name - ) - public - withPerm(MANAGE) - { - uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, checkpointId, _excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _token Address of ERC20 token in which dividend is to be denominated - * @param _amount Amount of specified token for dividend - * @param _checkpointId Checkpoint id from which to create dividends - * @param _excluded List of addresses to exclude - * @param _name Name/Title for identification - */ - function createDividendWithCheckpointAndExclusions( - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - uint256 _checkpointId, - address[] _excluded, - bytes32 _name - ) - public - withPerm(MANAGE) - { - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, _checkpointId, _excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _token Address of ERC20 token in which dividend is to be denominated - * @param _amount Amount of specified token for dividend - * @param _checkpointId Checkpoint id from which to create dividends - * @param _excluded List of addresses to exclude - * @param _name Name/Title for identification - */ - function _createDividendWithCheckpointAndExclusions( - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - uint256 _checkpointId, - address[] _excluded, - bytes32 _name - ) - internal - { - ISecurityToken securityTokenInstance = ISecurityToken(securityToken); - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); - require(_expiry > _maturity, "Expiry before maturity"); - /*solium-disable-next-line security/no-block-members*/ - require(_expiry > now, "Expiry in past"); - require(_amount > 0, "No dividend sent"); - require(_token != address(0), "Invalid token"); - require(_checkpointId <= securityTokenInstance.currentCheckpointId(), "Invalid checkpoint"); - require(IERC20(_token).transferFrom(msg.sender, address(this), _amount), "insufficent allowance"); - require(_name[0] != 0); - uint256 dividendIndex = dividends.length; - uint256 currentSupply = securityTokenInstance.totalSupplyAt(_checkpointId); - uint256 excludedSupply = 0; - dividends.push( - Dividend( - _checkpointId, - now, /*solium-disable-line security/no-block-members*/ - _maturity, - _expiry, - _amount, - 0, - 0, - false, - 0, - 0, - _name - ) - ); - - for (uint256 j = 0; j < _excluded.length; j++) { - require (_excluded[j] != address(0), "Invalid address"); - require(!dividends[dividendIndex].dividendExcluded[_excluded[j]], "duped exclude address"); - excludedSupply = excludedSupply.add(securityTokenInstance.balanceOfAt(_excluded[j], _checkpointId)); - dividends[dividendIndex].dividendExcluded[_excluded[j]] = true; - } - - dividends[dividendIndex].totalSupply = currentSupply.sub(excludedSupply); - dividendTokens[dividendIndex] = _token; - _emitERC20DividendDepositedEvent(_checkpointId, _maturity, _expiry, _token, _amount, currentSupply, dividendIndex, _name); - } - - /** - * @notice Emits the ERC20DividendDeposited event. - * Seperated into a different function as a workaround for stack too deep error - */ - function _emitERC20DividendDepositedEvent( - uint256 _checkpointId, - uint256 _maturity, - uint256 _expiry, - address _token, - uint256 _amount, - uint256 currentSupply, - uint256 dividendIndex, - bytes32 _name - ) - internal - { - /*solium-disable-next-line security/no-block-members*/ - emit ERC20DividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, _token, _amount, currentSupply, dividendIndex, _name); - } - - /** - * @notice Internal function for paying dividends - * @param _payee Address of investor - * @param _dividend Storage with previously issued dividends - * @param _dividendIndex Dividend to pay - */ - function _payDividend(address _payee, Dividend storage _dividend, uint256 _dividendIndex) internal { - (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); - _dividend.claimed[_payee] = true; - _dividend.claimedAmount = claim.add(_dividend.claimedAmount); - uint256 claimAfterWithheld = claim.sub(withheld); - if (claimAfterWithheld > 0) { - require(IERC20(dividendTokens[_dividendIndex]).transfer(_payee, claimAfterWithheld), "transfer failed"); - _dividend.dividendWithheld = _dividend.dividendWithheld.add(withheld); - investorWithheld[_payee] = investorWithheld[_payee].add(withheld); - emit ERC20DividendClaimed(_payee, _dividendIndex, dividendTokens[_dividendIndex], claim, withheld); - } - } - - /** - * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends - * @param _dividendIndex Dividend to reclaim - */ - function reclaimDividend(uint256 _dividendIndex) external withPerm(MANAGE) { - require(_dividendIndex < dividends.length, "Invalid dividend"); - /*solium-disable-next-line security/no-block-members*/ - require(now >= dividends[_dividendIndex].expiry, "Dividend expiry in future"); - require(!dividends[_dividendIndex].reclaimed, "already claimed"); - dividends[_dividendIndex].reclaimed = true; - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); - address owner = IOwnable(securityToken).owner(); - require(IERC20(dividendTokens[_dividendIndex]).transfer(owner, remainingAmount), "transfer failed"); - emit ERC20DividendReclaimed(owner, _dividendIndex, dividendTokens[_dividendIndex], remainingAmount); - } - - /** - * @notice Allows issuer to withdraw withheld tax - * @param _dividendIndex Dividend to withdraw from - */ - function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) { - require(_dividendIndex < dividends.length, "Invalid dividend"); - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); - dividend.dividendWithheldReclaimed = dividend.dividendWithheld; - address owner = IOwnable(securityToken).owner(); - require(IERC20(dividendTokens[_dividendIndex]).transfer(owner, remainingWithheld), "transfer failed"); - emit ERC20DividendWithholdingWithdrawn(owner, _dividendIndex, dividendTokens[_dividendIndex], remainingWithheld); - } - -} +pragma solidity ^0.5.0; + +import "./DividendCheckpoint.sol"; +import "../../storage/modules/Checkpoint/ERC20DividendCheckpointStorage.sol"; +import "../../interfaces/IOwnable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; + +/** + * @title Checkpoint module for issuing ERC20 dividends + */ +contract ERC20DividendCheckpoint is ERC20DividendCheckpointStorage, DividendCheckpoint { + using SafeMath for uint256; + + event ERC20DividendDeposited( + address indexed _depositor, + uint256 _checkpointId, + uint256 _created, + uint256 _maturity, + uint256 _expiry, + address indexed _token, + uint256 _amount, + uint256 _totalSupply, + uint256 _dividendIndex, + bytes32 indexed _name + ); + event ERC20DividendClaimed(address indexed _payee, uint256 indexed _dividendIndex, address indexed _token, uint256 _amount, uint256 _withheld); + event ERC20DividendReclaimed(address indexed _claimer, uint256 indexed _dividendIndex, address indexed _token, uint256 _claimedAmount); + event ERC20DividendWithholdingWithdrawn( + address indexed _claimer, + uint256 indexed _dividendIndex, + address indexed _token, + uint256 _withheldAmount + ); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + */ + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + + } + + /** + * @notice Creates a dividend and checkpoint for the dividend + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _token Address of ERC20 token in which dividend is to be denominated + * @param _amount Amount of specified token for dividend + * @param _name Name/Title for identification + */ + function createDividend( + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + bytes32 _name + ) + external + withPerm(MANAGE) + { + createDividendWithExclusions(_maturity, _expiry, _token, _amount, excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _token Address of ERC20 token in which dividend is to be denominated + * @param _amount Amount of specified token for dividend + * @param _checkpointId Checkpoint id from which to create dividends + * @param _name Name/Title for identification + */ + function createDividendWithCheckpoint( + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + uint256 _checkpointId, + bytes32 _name + ) + external + withPerm(MANAGE) + { + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, _checkpointId, excluded, _name); + } + + /** + * @notice Creates a dividend and checkpoint for the dividend + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _token Address of ERC20 token in which dividend is to be denominated + * @param _amount Amount of specified token for dividend + * @param _excluded List of addresses to exclude + * @param _name Name/Title for identification + */ + function createDividendWithExclusions( + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + address[] memory _excluded, + bytes32 _name + ) + public + withPerm(MANAGE) + { + uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, checkpointId, _excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _token Address of ERC20 token in which dividend is to be denominated + * @param _amount Amount of specified token for dividend + * @param _checkpointId Checkpoint id from which to create dividends + * @param _excluded List of addresses to exclude + * @param _name Name/Title for identification + */ + function createDividendWithCheckpointAndExclusions( + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + uint256 _checkpointId, + address[] memory _excluded, + bytes32 _name + ) + public + withPerm(MANAGE) + { + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, _checkpointId, _excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _token Address of ERC20 token in which dividend is to be denominated + * @param _amount Amount of specified token for dividend + * @param _checkpointId Checkpoint id from which to create dividends + * @param _excluded List of addresses to exclude + * @param _name Name/Title for identification + */ + function _createDividendWithCheckpointAndExclusions( + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + uint256 _checkpointId, + address[] memory _excluded, + bytes32 _name + ) + internal + { + ISecurityToken securityTokenInstance = ISecurityToken(securityToken); + require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); + require(_expiry > _maturity, "Expiry before maturity"); + /*solium-disable-next-line security/no-block-members*/ + require(_expiry > now, "Expiry in past"); + require(_amount > 0, "No dividend sent"); + require(_token != address(0), "Invalid token"); + require(_checkpointId <= securityTokenInstance.currentCheckpointId(), "Invalid checkpoint"); + require(IERC20(_token).transferFrom(msg.sender, address(this), _amount), "insufficent allowance"); + require(_name[0] != 0); + uint256 dividendIndex = dividends.length; + uint256 currentSupply = securityTokenInstance.totalSupplyAt(_checkpointId); + uint256 excludedSupply = 0; + dividends.push( + Dividend( + _checkpointId, + now, /*solium-disable-line security/no-block-members*/ + _maturity, + _expiry, + _amount, + 0, + 0, + false, + 0, + 0, + _name + ) + ); + + for (uint256 j = 0; j < _excluded.length; j++) { + require(_excluded[j] != address(0), "Invalid address"); + require(!dividends[dividendIndex].dividendExcluded[_excluded[j]], "duped exclude address"); + excludedSupply = excludedSupply.add(securityTokenInstance.balanceOfAt(_excluded[j], _checkpointId)); + dividends[dividendIndex].dividendExcluded[_excluded[j]] = true; + } + + dividends[dividendIndex].totalSupply = currentSupply.sub(excludedSupply); + dividendTokens[dividendIndex] = _token; + _emitERC20DividendDepositedEvent(_checkpointId, _maturity, _expiry, _token, _amount, currentSupply, dividendIndex, _name); + } + + /** + * @notice Emits the ERC20DividendDeposited event. + * Seperated into a different function as a workaround for stack too deep error + */ + function _emitERC20DividendDepositedEvent( + uint256 _checkpointId, + uint256 _maturity, + uint256 _expiry, + address _token, + uint256 _amount, + uint256 currentSupply, + uint256 dividendIndex, + bytes32 _name + ) + internal + { + /*solium-disable-next-line security/no-block-members*/ + emit ERC20DividendDeposited( + msg.sender, + _checkpointId, + now, + _maturity, + _expiry, + _token, + _amount, + currentSupply, + dividendIndex, + _name + ); + } + + /** + * @notice Internal function for paying dividends + * @param _payee Address of investor + * @param _dividend Storage with previously issued dividends + * @param _dividendIndex Dividend to pay + */ + function _payDividend(address payable _payee, Dividend storage _dividend, uint256 _dividendIndex) internal { + (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); + _dividend.claimed[_payee] = true; + _dividend.claimedAmount = claim.add(_dividend.claimedAmount); + uint256 claimAfterWithheld = claim.sub(withheld); + if (claimAfterWithheld > 0) { + require(IERC20(dividendTokens[_dividendIndex]).transfer(_payee, claimAfterWithheld), "transfer failed"); + if (withheld > 0) { + _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); + _dividend.withheld[_payee] = withheld; + } + emit ERC20DividendClaimed(_payee, _dividendIndex, dividendTokens[_dividendIndex], claim, withheld); + } + } + + /** + * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends + * @param _dividendIndex Dividend to reclaim + */ + function reclaimDividend(uint256 _dividendIndex) external withPerm(MANAGE) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + /*solium-disable-next-line security/no-block-members*/ + require(now >= dividends[_dividendIndex].expiry, "Dividend expiry in future"); + require(!dividends[_dividendIndex].reclaimed, "already claimed"); + dividends[_dividendIndex].reclaimed = true; + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); + require(IERC20(dividendTokens[_dividendIndex]).transfer(wallet, remainingAmount), "transfer failed"); + emit ERC20DividendReclaimed(wallet, _dividendIndex, dividendTokens[_dividendIndex], remainingAmount); + } + + /** + * @notice Allows issuer to withdraw withheld tax + * @param _dividendIndex Dividend to withdraw from + */ + function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingWithheld = dividend.totalWithheld.sub(dividend.totalWithheldWithdrawn); + dividend.totalWithheldWithdrawn = dividend.totalWithheld; + require(IERC20(dividendTokens[_dividendIndex]).transfer(wallet, remainingWithheld), "transfer failed"); + emit ERC20DividendWithholdingWithdrawn(wallet, _dividendIndex, dividendTokens[_dividendIndex], remainingWithheld); + } + +} diff --git a/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol b/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol index 5fd158c26..3410e2f36 100644 --- a/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol +++ b/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol @@ -1,48 +1,64 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ERC20DividendCheckpoint.sol"; +import "../../proxy/ERC20DividendCheckpointProxy.sol"; +import "../../libraries/Util.sol"; +import "../../interfaces/IBoot.sol"; import "../ModuleFactory.sol"; /** * @title Factory for deploying ERC20DividendCheckpoint module */ contract ERC20DividendCheckpointFactory is ModuleFactory { + address public logicContract; /** * @notice Constructor - * @param _polyAddress Address of the polytoken * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid logic contract"); + version = "2.1.0"; name = "ERC20DividendCheckpoint"; title = "ERC20 Dividend Checkpoint"; description = "Create ERC20 dividends for token holders at a specific checkpoint"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** * @notice Used to launch the Module with the help of factory * @return Address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "insufficent allowance"); - address erc20DividendCheckpoint = new ERC20DividendCheckpoint(msg.sender, address(polyToken)); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address erc20DividendCheckpoint = address(new ERC20DividendCheckpointProxy(msg.sender, address(polyToken), logicContract)); + //Checks that _data is valid (not calling anything it shouldn't) + require(Util.getSig(_data) == IBoot(erc20DividendCheckpoint).getInitFunction(), "Invalid data"); + /*solium-disable-next-line security/no-low-level-calls*/ + bool success; + (success, ) = erc20DividendCheckpoint.call(_data); + require(success, "Unsuccessful call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(erc20DividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(erc20DividendCheckpoint, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return erc20DividendCheckpoint; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 4; return res; @@ -51,14 +67,14 @@ contract ERC20DividendCheckpointFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Create ERC20 dividend to be paid out to token holders based on their balances at dividend creation time"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](3); availableTags[0] = "ERC20"; availableTags[1] = "Dividend"; diff --git a/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol b/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol index 4def51468..0c8cae750 100644 --- a/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol @@ -1,219 +1,218 @@ -pragma solidity ^0.4.24; - -import "./DividendCheckpoint.sol"; -import "../../interfaces/IOwnable.sol"; - -/** - * @title Checkpoint module for issuing ether dividends - */ -contract EtherDividendCheckpoint is DividendCheckpoint { - using SafeMath for uint256; - event EtherDividendDeposited( - address indexed _depositor, - uint256 _checkpointId, - uint256 _created, - uint256 _maturity, - uint256 _expiry, - uint256 _amount, - uint256 _totalSupply, - uint256 _dividendIndex, - bytes32 indexed _name - ); - event EtherDividendClaimed(address indexed _payee, uint256 _dividendIndex, uint256 _amount, uint256 _withheld); - event EtherDividendReclaimed(address indexed _claimer, uint256 _dividendIndex, uint256 _claimedAmount); - event EtherDividendClaimFailed(address indexed _payee, uint256 _dividendIndex, uint256 _amount, uint256 _withheld); - event EtherDividendWithholdingWithdrawn(address indexed _claimer, uint256 _dividendIndex, uint256 _withheldAmount); - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { - } - - /** - * @notice Creates a dividend and checkpoint for the dividend, using global list of excluded addresses - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _name Name/title for identification - */ - function createDividend(uint256 _maturity, uint256 _expiry, bytes32 _name) external payable withPerm(MANAGE) { - createDividendWithExclusions(_maturity, _expiry, excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint, using global list of excluded addresses - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _checkpointId Id of the checkpoint from which to issue dividend - * @param _name Name/title for identification - */ - function createDividendWithCheckpoint( - uint256 _maturity, - uint256 _expiry, - uint256 _checkpointId, - bytes32 _name - ) - external - payable - withPerm(MANAGE) - { - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, excluded, _name); - } - - /** - * @notice Creates a dividend and checkpoint for the dividend, specifying explicit excluded addresses - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _excluded List of addresses to exclude - * @param _name Name/title for identification - */ - function createDividendWithExclusions( - uint256 _maturity, - uint256 _expiry, - address[] _excluded, - bytes32 _name - ) - public - payable - withPerm(MANAGE) - { - uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, checkpointId, _excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _checkpointId Id of the checkpoint from which to issue dividend - * @param _excluded List of addresses to exclude - * @param _name Name/title for identification - */ - function createDividendWithCheckpointAndExclusions( - uint256 _maturity, - uint256 _expiry, - uint256 _checkpointId, - address[] _excluded, - bytes32 _name - ) - public - payable - withPerm(MANAGE) - { - _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, _excluded, _name); - } - - /** - * @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses - * @param _maturity Time from which dividend can be paid - * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer - * @param _checkpointId Id of the checkpoint from which to issue dividend - * @param _excluded List of addresses to exclude - * @param _name Name/title for identification - */ - function _createDividendWithCheckpointAndExclusions( - uint256 _maturity, - uint256 _expiry, - uint256 _checkpointId, - address[] _excluded, - bytes32 _name - ) - internal - { - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); - require(_expiry > _maturity, "Expiry is before maturity"); - /*solium-disable-next-line security/no-block-members*/ - require(_expiry > now, "Expiry is in the past"); - require(msg.value > 0, "No dividend sent"); - require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId()); - require(_name[0] != 0); - uint256 dividendIndex = dividends.length; - uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); - uint256 excludedSupply = 0; - dividends.push( - Dividend( - _checkpointId, - now, /*solium-disable-line security/no-block-members*/ - _maturity, - _expiry, - msg.value, - 0, - 0, - false, - 0, - 0, - _name - ) - ); - - for (uint256 j = 0; j < _excluded.length; j++) { - require (_excluded[j] != address(0), "Invalid address"); - require(!dividends[dividendIndex].dividendExcluded[_excluded[j]], "duped exclude address"); - excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[j], _checkpointId)); - dividends[dividendIndex].dividendExcluded[_excluded[j]] = true; - } - dividends[dividendIndex].totalSupply = currentSupply.sub(excludedSupply); - /*solium-disable-next-line security/no-block-members*/ - emit EtherDividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, msg.value, currentSupply, dividendIndex, _name); - } - - /** - * @notice Internal function for paying dividends - * @param _payee address of investor - * @param _dividend storage with previously issued dividends - * @param _dividendIndex Dividend to pay - */ - function _payDividend(address _payee, Dividend storage _dividend, uint256 _dividendIndex) internal { - (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); - _dividend.claimed[_payee] = true; - uint256 claimAfterWithheld = claim.sub(withheld); - if (claimAfterWithheld > 0) { - /*solium-disable-next-line security/no-send*/ - if (_payee.send(claimAfterWithheld)) { - _dividend.claimedAmount = _dividend.claimedAmount.add(claim); - _dividend.dividendWithheld = _dividend.dividendWithheld.add(withheld); - investorWithheld[_payee] = investorWithheld[_payee].add(withheld); - emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld); - } else { - _dividend.claimed[_payee] = false; - emit EtherDividendClaimFailed(_payee, _dividendIndex, claim, withheld); - } - } - } - - /** - * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends - * @param _dividendIndex Dividend to reclaim - */ - function reclaimDividend(uint256 _dividendIndex) external withPerm(MANAGE) { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - /*solium-disable-next-line security/no-block-members*/ - require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); - require(!dividends[_dividendIndex].reclaimed, "Dividend is already claimed"); - Dividend storage dividend = dividends[_dividendIndex]; - dividend.reclaimed = true; - uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); - address owner = IOwnable(securityToken).owner(); - owner.transfer(remainingAmount); - emit EtherDividendReclaimed(owner, _dividendIndex, remainingAmount); - } - - /** - * @notice Allows issuer to withdraw withheld tax - * @param _dividendIndex Dividend to withdraw from - */ - function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); - dividend.dividendWithheldReclaimed = dividend.dividendWithheld; - address owner = IOwnable(securityToken).owner(); - owner.transfer(remainingWithheld); - emit EtherDividendWithholdingWithdrawn(owner, _dividendIndex, remainingWithheld); - } - -} +pragma solidity ^0.5.0; + +import "./DividendCheckpoint.sol"; +import "../../interfaces/IOwnable.sol"; + +/** + * @title Checkpoint module for issuing ether dividends + */ +contract EtherDividendCheckpoint is DividendCheckpoint { + using SafeMath for uint256; + + event EtherDividendDeposited( + address indexed _depositor, + uint256 _checkpointId, + uint256 _created, + uint256 _maturity, + uint256 _expiry, + uint256 _amount, + uint256 _totalSupply, + uint256 indexed _dividendIndex, + bytes32 indexed _name + ); + event EtherDividendClaimed(address indexed _payee, uint256 indexed _dividendIndex, uint256 _amount, uint256 _withheld); + event EtherDividendReclaimed(address indexed _claimer, uint256 indexed _dividendIndex, uint256 _claimedAmount); + event EtherDividendClaimFailed(address indexed _payee, uint256 indexed _dividendIndex, uint256 _amount, uint256 _withheld); + event EtherDividendWithholdingWithdrawn(address indexed _claimer, uint256 indexed _dividendIndex, uint256 _withheldAmount); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + */ + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + + } + + /** + * @notice Creates a dividend and checkpoint for the dividend, using global list of excluded addresses + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _name Name/title for identification + */ + function createDividend(uint256 _maturity, uint256 _expiry, bytes32 _name) external payable withPerm(MANAGE) { + createDividendWithExclusions(_maturity, _expiry, excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint, using global list of excluded addresses + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _checkpointId Id of the checkpoint from which to issue dividend + * @param _name Name/title for identification + */ + function createDividendWithCheckpoint( + uint256 _maturity, + uint256 _expiry, + uint256 _checkpointId, + bytes32 _name + ) + external + payable + withPerm(MANAGE) + { + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, excluded, _name); + } + + /** + * @notice Creates a dividend and checkpoint for the dividend, specifying explicit excluded addresses + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _excluded List of addresses to exclude + * @param _name Name/title for identification + */ + function createDividendWithExclusions( + uint256 _maturity, + uint256 _expiry, + address[] memory _excluded, + bytes32 _name + ) + public + payable + withPerm(MANAGE) + { + uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, checkpointId, _excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _checkpointId Id of the checkpoint from which to issue dividend + * @param _excluded List of addresses to exclude + * @param _name Name/title for identification + */ + function createDividendWithCheckpointAndExclusions( + uint256 _maturity, + uint256 _expiry, + uint256 _checkpointId, + address[] memory _excluded, + bytes32 _name + ) + public + payable + withPerm(MANAGE) + { + _createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, _excluded, _name); + } + + /** + * @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses + * @param _maturity Time from which dividend can be paid + * @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer + * @param _checkpointId Id of the checkpoint from which to issue dividend + * @param _excluded List of addresses to exclude + * @param _name Name/title for identification + */ + function _createDividendWithCheckpointAndExclusions( + uint256 _maturity, + uint256 _expiry, + uint256 _checkpointId, + address[] memory _excluded, + bytes32 _name + ) + internal + { + require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); + require(_expiry > _maturity, "Expiry is before maturity"); + /*solium-disable-next-line security/no-block-members*/ + require(_expiry > now, "Expiry is in the past"); + require(msg.value > 0, "No dividend sent"); + require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId()); + require(_name[0] != 0); + uint256 dividendIndex = dividends.length; + uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); + uint256 excludedSupply = 0; + dividends.push( + Dividend( + _checkpointId, + now, /*solium-disable-line security/no-block-members*/ + _maturity, + _expiry, + msg.value, + 0, + 0, + false, + 0, + 0, + _name + ) + ); + + for (uint256 j = 0; j < _excluded.length; j++) { + require(_excluded[j] != address(0), "Invalid address"); + require(!dividends[dividendIndex].dividendExcluded[_excluded[j]], "duped exclude address"); + excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[j], _checkpointId)); + dividends[dividendIndex].dividendExcluded[_excluded[j]] = true; + } + dividends[dividendIndex].totalSupply = currentSupply.sub(excludedSupply); + /*solium-disable-next-line security/no-block-members*/ + emit EtherDividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, msg.value, currentSupply, dividendIndex, _name); + } + + /** + * @notice Internal function for paying dividends + * @param _payee address of investor + * @param _dividend storage with previously issued dividends + * @param _dividendIndex Dividend to pay + */ + function _payDividend(address payable _payee, Dividend storage _dividend, uint256 _dividendIndex) internal { + (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); + _dividend.claimed[_payee] = true; + uint256 claimAfterWithheld = claim.sub(withheld); + if (claimAfterWithheld > 0) { + /*solium-disable-next-line security/no-send*/ + if (_payee.send(claimAfterWithheld)) { + _dividend.claimedAmount = _dividend.claimedAmount.add(claim); + if (withheld > 0) { + _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); + _dividend.withheld[_payee] = withheld; + } + emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld); + } else { + _dividend.claimed[_payee] = false; + emit EtherDividendClaimFailed(_payee, _dividendIndex, claim, withheld); + } + } + } + + /** + * @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends + * @param _dividendIndex Dividend to reclaim + */ + function reclaimDividend(uint256 _dividendIndex) external withPerm(MANAGE) { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + /*solium-disable-next-line security/no-block-members*/ + require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); + require(!dividends[_dividendIndex].reclaimed, "Dividend is already claimed"); + Dividend storage dividend = dividends[_dividendIndex]; + dividend.reclaimed = true; + uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); + wallet.transfer(remainingAmount); + emit EtherDividendReclaimed(wallet, _dividendIndex, remainingAmount); + } + + /** + * @notice Allows issuer to withdraw withheld tax + * @param _dividendIndex Dividend to withdraw from + */ + function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingWithheld = dividend.totalWithheld.sub(dividend.totalWithheldWithdrawn); + dividend.totalWithheldWithdrawn = dividend.totalWithheld; + wallet.transfer(remainingWithheld); + emit EtherDividendWithholdingWithdrawn(wallet, _dividendIndex, remainingWithheld); + } + +} diff --git a/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol b/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol index 315760be1..38f1b16b4 100644 --- a/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol +++ b/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol @@ -1,48 +1,64 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./EtherDividendCheckpoint.sol"; +import "../../proxy/EtherDividendCheckpointProxy.sol"; +import "../../libraries/Util.sol"; +import "../../interfaces/IBoot.sol"; import "../ModuleFactory.sol"; /** * @title Factory for deploying EtherDividendCheckpoint module */ contract EtherDividendCheckpointFactory is ModuleFactory { + address public logicContract; /** * @notice Constructor - * @param _polyAddress Address of the polytoken * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid logic contract"); + version = "2.1.0"; name = "EtherDividendCheckpoint"; title = "Ether Dividend Checkpoint"; description = "Create ETH dividends for token holders at a specific checkpoint"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** * @notice Used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Insufficent allowance or balance"); - address ethDividendCheckpoint = new EtherDividendCheckpoint(msg.sender, address(polyToken)); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address ethDividendCheckpoint = address(new EtherDividendCheckpointProxy(msg.sender, address(polyToken), logicContract)); + //Checks that _data is valid (not calling anything it shouldn't) + require(Util.getSig(_data) == IBoot(ethDividendCheckpoint).getInitFunction(), "Invalid data"); + /*solium-disable-next-line security/no-low-level-calls*/ + bool success; + (success, ) = ethDividendCheckpoint.call(_data); + require(success, "Unsuccessful call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(ethDividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(ethDividendCheckpoint, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return ethDividendCheckpoint; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 4; return res; @@ -51,14 +67,14 @@ contract EtherDividendCheckpointFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Create a dividend which will be paid out to token holders proportionally according to their balances at the point the dividend is created"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](3); availableTags[0] = "ETH"; availableTags[1] = "Checkpoint"; diff --git a/contracts/modules/Checkpoint/ICheckpoint.sol b/contracts/modules/Checkpoint/ICheckpoint.sol index f2a7417b2..f7b10b77d 100644 --- a/contracts/modules/Checkpoint/ICheckpoint.sol +++ b/contracts/modules/Checkpoint/ICheckpoint.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface to be implemented by all checkpoint modules diff --git a/contracts/modules/Experimental/Burn/TrackedRedemption.sol b/contracts/modules/Experimental/Burn/TrackedRedemption.sol index bc06a99e9..4f6c1d4ce 100644 --- a/contracts/modules/Experimental/Burn/TrackedRedemption.sol +++ b/contracts/modules/Experimental/Burn/TrackedRedemption.sol @@ -1,8 +1,7 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../../Burn/IBurn.sol"; import "../../Module.sol"; -import "../../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** @@ -11,24 +10,22 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; contract TrackedRedemption is IBurn, Module { using SafeMath for uint256; - mapping (address => uint256) redeemedTokens; + mapping(address => uint256) redeemedTokens; - event Redeemed(address _investor, uint256 _value, uint256 _timestamp); + event Redeemed(address _investor, uint256 _value); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(0); } @@ -37,16 +34,16 @@ contract TrackedRedemption is IBurn, Module { * @param _value The number of tokens to redeem */ function redeemTokens(uint256 _value) public { - ISecurityToken(securityToken).burnFromWithData(msg.sender, _value, ""); + ISecurityToken(securityToken).redeemFrom(msg.sender, _value, ""); redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value); /*solium-disable-next-line security/no-block-members*/ - emit Redeemed(msg.sender, _value, now); + emit Redeemed(msg.sender, _value); } /** * @notice Returns the permissions flag that are associated with CountTransferManager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](0); return allPermissions; } diff --git a/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol b/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol index d1fa010b7..603d65600 100644 --- a/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol +++ b/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./TrackedRedemption.sol"; import "../../ModuleFactory.sol"; @@ -7,16 +7,19 @@ import "../../ModuleFactory.sol"; * @title Factory for deploying GeneralTransferManager module */ contract TrackedRedemptionFactory is ModuleFactory { - /** * @notice Constructor - * @param _polyAddress Address of the polytoken * @param _setupCost Setup cost of module * @param _usageCost Usage cost of module - * @param _subscriptionCost Monthly cost of module + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { version = "1.0.0"; name = "TrackedRedemption"; @@ -30,19 +33,23 @@ contract TrackedRedemptionFactory is ModuleFactory { * @notice Used to launch the Module with the help of factory * @return Address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Insufficent allowance or balance"); - address trackedRedemption = new TrackedRedemption(msg.sender, address(polyToken)); + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + address trackedRedemption = address(new TrackedRedemption(msg.sender, polyToken)); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(trackedRedemption), getName(), address(this), msg.sender, setupCost, now); - return address(trackedRedemption); + emit GenerateModuleFromFactory(trackedRedemption, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return trackedRedemption; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 5; return res; @@ -51,14 +58,14 @@ contract TrackedRedemptionFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Allows an investor to redeem security tokens which are tracked by this module"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "Redemption"; availableTags[1] = "Tracked"; diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 097a44f50..64301c681 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -1,14 +1,13 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./../../Checkpoint/ICheckpoint.sol"; -import "../../TransferManager/ITransferManager.sol"; -import "../../../interfaces/ISecurityToken.sol"; +import "../../TransferManager/TransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** * @title Burn module for burning tokens and keeping track of burnt amounts */ -contract ScheduledCheckpoint is ICheckpoint, ITransferManager { +contract ScheduledCheckpoint is ICheckpoint, TransferManager { using SafeMath for uint256; struct Schedule { @@ -24,25 +23,23 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { bytes32[] public names; - mapping (bytes32 => Schedule) public schedules; + mapping(bytes32 => Schedule) public schedules; - event AddSchedule(bytes32 _name, uint256 _startTime, uint256 _interval, uint256 _timestamp); - event RemoveSchedule(bytes32 _name, uint256 _timestamp); + event AddSchedule(bytes32 _name, uint256 _startTime, uint256 _interval); + event RemoveSchedule(bytes32 _name); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(0); } @@ -61,7 +58,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { schedules[_name].interval = _interval; schedules[_name].index = names.length; names.push(_name); - emit AddSchedule(_name, _startTime, _interval, now); + emit AddSchedule(_name, _startTime, _interval); } /** @@ -77,38 +74,60 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { schedules[names[index]].index = index; } delete schedules[_name]; - emit RemoveSchedule(_name, now); + emit RemoveSchedule(_name); } - /** * @notice Used to create checkpoints that correctly reflect balances - * @param _isTransfer whether or not an actual transfer is occuring * @return always returns Result.NA */ - function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { - require(_isTransfer == false || msg.sender == securityToken, "Sender is not owner"); - if (paused || !_isTransfer) { - return Result.NA; + function executeTransfer( + address, /* _from */ + address, /* _to */ + uint256, /* _amount */ + bytes calldata /* _data */ + ) + external + onlySecurityToken + returns(Result) + { + if (!paused) { + _updateAll(); } - _updateAll(); - return Result.NA; + return (Result.NA); + } + + /** + * @notice Used to create checkpoints that correctly reflect balances + * @return always returns Result.NA + */ + function verifyTransfer( + address, /* _from */ + address, /* _to */ + uint256, /* _amount */ + bytes memory /* _data */ + ) + public + view + returns(Result, bytes32) + { + return (Result.NA, bytes32(0)); } /** * @notice gets schedule details * @param _name name of the schedule */ - function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, uint256[], uint256[], uint256[]) { - return ( - schedules[_name].name, - schedules[_name].startTime, - schedules[_name].nextTime, - schedules[_name].interval, - schedules[_name].checkpointIds, - schedules[_name].timestamps, - schedules[_name].periods - ); + function getSchedule(bytes32 _name) external view returns( + bytes32, + uint256, + uint256, + uint256, + uint256[] memory, + uint256[] memory, + uint256[] memory + ){ + return (schedules[_name].name, schedules[_name].startTime, schedules[_name].nextTime, schedules[_name].interval, schedules[_name].checkpointIds, schedules[_name].timestamps, schedules[_name].periods); } /** @@ -134,7 +153,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { /** * @notice manually triggers update outside of transfer request for all schedules (can be used to reduce user gas costs) */ - function updateAll() onlyOwner external { + function updateAll() external onlyOwner { _updateAll(); } @@ -145,10 +164,17 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } } + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + /** * @notice Return the permissions flag that are associated with CountTransferManager */ - function getPermissions() view external returns(bytes32[]) { + function getPermissions() external view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](0); return allPermissions; } diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol index 1b5daac29..86f4687a7 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./ScheduledCheckpoint.sol"; import "../../ModuleFactory.sol"; @@ -7,16 +7,19 @@ import "../../ModuleFactory.sol"; * @title Factory for deploying EtherDividendCheckpoint module */ contract ScheduledCheckpointFactory is ModuleFactory { - /** * @notice Constructor - * @param _polyAddress Address of the polytoken * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { version = "1.0.0"; name = "ScheduledCheckpoint"; @@ -30,70 +33,39 @@ contract ScheduledCheckpointFactory is ModuleFactory { * @notice used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - address scheduledCheckpoint = new ScheduledCheckpoint(msg.sender, address(polyToken)); - emit GenerateModuleFromFactory(scheduledCheckpoint, getName(), address(this), msg.sender, setupCost, now); + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + address scheduledCheckpoint = address(new ScheduledCheckpoint(msg.sender, polyToken)); + emit GenerateModuleFromFactory(scheduledCheckpoint, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return scheduledCheckpoint; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](2); res[0] = 4; res[1] = 2; return res; } - /** - * @notice Get the name of the Module - */ - function getName() public view returns(bytes32) { - return name; - } - - /** - * @notice Get the description of the Module - */ - function getDescription() external view returns(string) { - return description; - } - - /** - * @notice Get the title of the Module - */ - function getTitle() external view returns(string) { - return title; - } - - /** - * @notice Get the version of the Module - */ - function getVersion() external view returns(string) { - return version; - } - - /** - * @notice Get the setup cost of the module - */ - function getSetupCost() external view returns (uint256) { - return setupCost; - } - /** * @notice Get the Instructions that helped to used the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Schedule a series of future checkpoints by specifying a start time and interval of each checkpoint"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "Scheduled"; availableTags[1] = "Checkpoint"; diff --git a/contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol b/contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol new file mode 100644 index 000000000..059588a6e --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol @@ -0,0 +1,429 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/** + * @title Transfer Manager module to automate blacklist and restrict transfers + */ +contract BlacklistTransferManager is TransferManager { + using SafeMath for uint256; + + bytes32 public constant ADMIN = "ADMIN"; + + struct BlacklistsDetails { + uint256 startTime; + uint256 endTime; + uint256 repeatPeriodTime; + } + + //hold the different blacklist details corresponds to its name + mapping(bytes32 => BlacklistsDetails) public blacklists; + + //hold the different name of blacklist corresponds to a investor + mapping(address => bytes32[]) investorToBlacklist; + + //get list of the addresses for a particular blacklist + mapping(bytes32 => address[]) blacklistToInvestor; + + //mapping use to store the indexes for different blacklist types for a investor + mapping(address => mapping(bytes32 => uint256)) investorToIndex; + + //mapping use to store the indexes for different investor for a blacklist type + mapping(bytes32 => mapping(address => uint256)) blacklistToIndex; + + bytes32[] allBlacklists; + + // Emit when new blacklist type is added + event AddBlacklistType( + uint256 _startTime, + uint256 _endTime, + bytes32 _blacklistName, + uint256 _repeatPeriodTime + ); + + // Emit when there is a change in the blacklist type + event ModifyBlacklistType( + uint256 _startTime, + uint256 _endTime, + bytes32 _blacklistName, + uint256 _repeatPeriodTime + ); + + // Emit when the added blacklist type is deleted + event DeleteBlacklistType( + bytes32 _blacklistName + ); + + // Emit when new investor is added to the blacklist type + event AddInvestorToBlacklist( + address indexed _investor, + bytes32 _blacklistName + ); + + // Emit when investor is deleted from the blacklist type + event DeleteInvestorFromBlacklist( + address indexed _investor, + bytes32 _blacklistName + ); + + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + + /** + * @notice Used to verify the transfer transaction + * @param _from Address of the sender + * @dev Restrict the blacklist address to transfer tokens + * if the current time is between the timeframe define for the + * blacklist type associated with the _from address + */ + function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(Result) { + (Result success, ) = verifyTransfer(_from, _to, _amount, _data); + return success; + } + + + /** + * @notice Used to verify the transfer transaction (View) + * @param _from Address of the sender + * @dev Restrict the blacklist address to transfer tokens + * if the current time is between the timeframe define for the + * blacklist type associated with the _from address + */ + function verifyTransfer( + address _from, + address /* _to */, + uint256 /* _amount */, + bytes memory/* _data */ + ) + public + view + returns(Result, bytes32) + { + if (!paused) { + if (investorToBlacklist[_from].length != 0) { + for (uint256 i = 0; i < investorToBlacklist[_from].length; i++) { + uint256 endTimeTemp = blacklists[investorToBlacklist[_from][i]].endTime; + uint256 startTimeTemp = blacklists[investorToBlacklist[_from][i]].startTime; + uint256 repeatPeriodTimeTemp = blacklists[investorToBlacklist[_from][i]].repeatPeriodTime * 1 days; + /*solium-disable-next-line security/no-block-members*/ + if (now > startTimeTemp) { + // Find the repeating parameter that will be used to calculate the new startTime and endTime + // based on the new current time value + /*solium-disable-next-line security/no-block-members*/ + uint256 repeater = (now.sub(startTimeTemp)).div(repeatPeriodTimeTemp); + /*solium-disable-next-line security/no-block-members*/ + if (startTimeTemp.add(repeatPeriodTimeTemp.mul(repeater)) <= now && endTimeTemp.add(repeatPeriodTimeTemp.mul(repeater)) >= now) { + return (Result.INVALID, bytes32(uint256(address(this)) << 96)); + } + } + } + } + } + return (Result.NA, bytes32(0)); + } + + + /** + * @notice Used to add the blacklist type + * @param _startTime Start date of the blacklist type + * @param _endTime End date of the blacklist type + * @param _blacklistName Name of the blacklist type + * @param _repeatPeriodTime Repeat period of the blacklist type + */ + function addBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) public withPerm(ADMIN) { + _addBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + } + + /** + * @notice Used to add the multiple blacklist type + * @param _startTimes Start date of the blacklist type + * @param _endTimes End date of the blacklist type + * @param _blacklistNames Name of the blacklist type + * @param _repeatPeriodTimes Repeat period of the blacklist type + */ + function addBlacklistTypeMulti( + uint256[] calldata _startTimes, + uint256[] calldata _endTimes, + bytes32[] calldata _blacklistNames, + uint256[] calldata _repeatPeriodTimes + ) + external + withPerm(ADMIN) + { + require (_startTimes.length == _endTimes.length && _endTimes.length == _blacklistNames.length && _blacklistNames.length == _repeatPeriodTimes.length, "Input array's length mismatch"); + for (uint256 i = 0; i < _startTimes.length; i++){ + _addBlacklistType(_startTimes[i], _endTimes[i], _blacklistNames[i], _repeatPeriodTimes[i]); + } + } + + /** + * @notice Internal function + */ + function _validParams(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal view { + require(_blacklistName != bytes32(0), "Invalid blacklist name"); + require(_startTime >= now && _startTime < _endTime, "Invalid start or end date"); + require(_repeatPeriodTime.mul(1 days) >= _endTime.sub(_startTime) || _repeatPeriodTime == 0); + } + + /** + * @notice Used to modify the details of a given blacklist type + * @param _startTime Start date of the blacklist type + * @param _endTime End date of the blacklist type + * @param _blacklistName Name of the blacklist type + * @param _repeatPeriodTime Repeat period of the blacklist type + */ + function modifyBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) public withPerm(ADMIN) { + require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn't exist"); + _validParams(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + blacklists[_blacklistName] = BlacklistsDetails(_startTime, _endTime, _repeatPeriodTime); + emit ModifyBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + } + + /** + * @notice Used to modify the details of a given multpile blacklist types + * @param _startTimes Start date of the blacklist type + * @param _endTimes End date of the blacklist type + * @param _blacklistNames Name of the blacklist type + * @param _repeatPeriodTimes Repeat period of the blacklist type + */ + function modifyBlacklistTypeMulti( + uint256[] calldata _startTimes, + uint256[] calldata _endTimes, + bytes32[] calldata _blacklistNames, + uint256[] calldata _repeatPeriodTimes + ) + external + withPerm(ADMIN) + { + require (_startTimes.length == _endTimes.length && _endTimes.length == _blacklistNames.length && _blacklistNames.length == _repeatPeriodTimes.length, "Input array's length mismatch"); + for (uint256 i = 0; i < _startTimes.length; i++){ + modifyBlacklistType(_startTimes[i], _endTimes[i], _blacklistNames[i], _repeatPeriodTimes[i]); + } + } + + /** + * @notice Used to delete the blacklist type + * @param _blacklistName Name of the blacklist type + */ + function deleteBlacklistType(bytes32 _blacklistName) public withPerm(ADMIN) { + require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn’t exist"); + require(blacklistToInvestor[_blacklistName].length == 0, "Investors are associated with the blacklist"); + // delete blacklist type + delete(blacklists[_blacklistName]); + uint256 i = 0; + for (i = 0; i < allBlacklists.length; i++) { + if (allBlacklists[i] == _blacklistName) { + break; + } + } + if (i != allBlacklists.length -1) { + allBlacklists[i] = allBlacklists[allBlacklists.length -1]; + } + allBlacklists.length--; + emit DeleteBlacklistType(_blacklistName); + } + + /** + * @notice Used to delete the multiple blacklist type + * @param _blacklistNames Name of the blacklist type + */ + function deleteBlacklistTypeMulti(bytes32[] calldata _blacklistNames) external withPerm(ADMIN) { + for(uint256 i = 0; i < _blacklistNames.length; i++){ + deleteBlacklistType(_blacklistNames[i]); + } + } + + /** + * @notice Used to assign the blacklist type to the investor + * @param _investor Address of the investor + * @param _blacklistName Name of the blacklist + */ + function addInvestorToBlacklist(address _investor, bytes32 _blacklistName) public withPerm(ADMIN) { + require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn't exist"); + require(_investor != address(0), "Invalid investor address"); + uint256 index = investorToIndex[_investor][_blacklistName]; + if (index < investorToBlacklist[_investor].length) + require(investorToBlacklist[_investor][index] != _blacklistName, "Blacklist already added to investor"); + uint256 investorIndex = investorToBlacklist[_investor].length; + // Add blacklist index to the investor + investorToIndex[_investor][_blacklistName] = investorIndex; + uint256 blacklistIndex = blacklistToInvestor[_blacklistName].length; + // Add investor index to the blacklist + blacklistToIndex[_blacklistName][_investor] = blacklistIndex; + investorToBlacklist[_investor].push(_blacklistName); + blacklistToInvestor[_blacklistName].push(_investor); + emit AddInvestorToBlacklist(_investor, _blacklistName); + } + + /** + * @notice Used to assign the blacklist type to the multiple investor + * @param _investors Address of the investor + * @param _blacklistName Name of the blacklist + */ + function addInvestorToBlacklistMulti(address[] calldata _investors, bytes32 _blacklistName) external withPerm(ADMIN){ + for(uint256 i = 0; i < _investors.length; i++){ + addInvestorToBlacklist(_investors[i], _blacklistName); + } + } + + /** + * @notice Used to assign the multiple blacklist type to the multiple investor + * @param _investors Address of the investor + * @param _blacklistNames Name of the blacklist + */ + function addMultiInvestorToBlacklistMulti(address[] calldata _investors, bytes32[] calldata _blacklistNames) external withPerm(ADMIN){ + require (_investors.length == _blacklistNames.length, "Input array's length mismatch"); + for(uint256 i = 0; i < _investors.length; i++){ + addInvestorToBlacklist(_investors[i], _blacklistNames[i]); + } + } + + /** + * @notice Used to assign the new blacklist type to the investor + * @param _startTime Start date of the blacklist type + * @param _endTime End date of the blacklist type + * @param _blacklistName Name of the blacklist type + * @param _repeatPeriodTime Repeat period of the blacklist type + * @param _investor Address of the investor + */ + function addInvestorToNewBlacklist(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime, address _investor) external withPerm(ADMIN){ + _addBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + addInvestorToBlacklist(_investor, _blacklistName); + } + + /** + * @notice Used to delete the investor from all the associated blacklist types + * @param _investor Address of the investor + */ + function deleteInvestorFromAllBlacklist(address _investor) public withPerm(ADMIN) { + require(_investor != address(0), "Invalid investor address"); + require(investorToBlacklist[_investor].length != 0, "Investor is not associated to any blacklist type"); + uint256 index = investorToBlacklist[_investor].length - 1; + for (uint256 i = index; i >= 0 && i <= index; i--){ + deleteInvestorFromBlacklist(_investor, investorToBlacklist[_investor][i]); + } + } + + /** + * @notice Used to delete the multiple investor from all the associated blacklist types + * @param _investor Address of the investor + */ + function deleteInvestorFromAllBlacklistMulti(address[] calldata _investor) external withPerm(ADMIN) { + for(uint256 i = 0; i < _investor.length; i++){ + deleteInvestorFromAllBlacklist(_investor[i]); + } + } + + /** + * @notice Used to delete the investor from the blacklist + * @param _investor Address of the investor + * @param _blacklistName Name of the blacklist + */ + function deleteInvestorFromBlacklist(address _investor, bytes32 _blacklistName) public withPerm(ADMIN) { + require(_investor != address(0), "Invalid investor address"); + require(_blacklistName != bytes32(0),"Invalid blacklist name"); + require(investorToBlacklist[_investor][investorToIndex[_investor][_blacklistName]] == _blacklistName, "Investor not associated to the blacklist"); + // delete the investor from the blacklist type + uint256 _blacklistIndex = blacklistToIndex[_blacklistName][_investor]; + uint256 _len = blacklistToInvestor[_blacklistName].length; + if ( _blacklistIndex < _len -1) { + blacklistToInvestor[_blacklistName][_blacklistIndex] = blacklistToInvestor[_blacklistName][_len - 1]; + blacklistToIndex[_blacklistName][blacklistToInvestor[_blacklistName][_blacklistIndex]] = _blacklistIndex; + } + blacklistToInvestor[_blacklistName].length--; + // delete the investor index from the blacklist + delete(blacklistToIndex[_blacklistName][_investor]); + // delete the blacklist from the investor + uint256 _investorIndex = investorToIndex[_investor][_blacklistName]; + _len = investorToBlacklist[_investor].length; + if ( _investorIndex < _len -1) { + investorToBlacklist[_investor][_investorIndex] = investorToBlacklist[_investor][_len - 1]; + investorToIndex[_investor][investorToBlacklist[_investor][_investorIndex]] = _investorIndex; + } + investorToBlacklist[_investor].length--; + // delete the blacklist index from the invetsor + delete(investorToIndex[_investor][_blacklistName]); + emit DeleteInvestorFromBlacklist(_investor, _blacklistName); + } + + /** + * @notice Used to delete the multiple investor from the blacklist + * @param _investors address of the investor + * @param _blacklistNames name of the blacklist + */ + function deleteMultiInvestorsFromBlacklistMulti(address[] calldata _investors, bytes32[] calldata _blacklistNames) external withPerm(ADMIN) { + require (_investors.length == _blacklistNames.length, "Input array's length mismatch"); + for(uint256 i = 0; i < _investors.length; i++){ + deleteInvestorFromBlacklist(_investors[i], _blacklistNames[i]); + } + } + + function _addBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal { + require(blacklists[_blacklistName].endTime == 0, "Blacklist type already exist"); + _validParams(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + blacklists[_blacklistName] = BlacklistsDetails(_startTime, _endTime, _repeatPeriodTime); + allBlacklists.push(_blacklistName); + emit AddBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + } + + /** + * @notice get the list of the investors of a blacklist type + * @param _blacklistName Name of the blacklist type + * @return address List of investors associated with the blacklist + */ + function getListOfAddresses(bytes32 _blacklistName) external view returns(address[] memory) { + require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn't exist"); + return blacklistToInvestor[_blacklistName]; + } + + /** + * @notice get the list of the investors of a blacklist type + * @param _user Address of the user + * @return bytes32 List of blacklist names associated with the given address + */ + function getBlacklistNamesToUser(address _user) external view returns(bytes32[] memory) { + return investorToBlacklist[_user]; + } + + /** + * @notice get the list of blacklist names + * @return bytes32 Array of blacklist names + */ + function getAllBlacklists() external view returns(bytes32[] memory) { + return allBlacklists; + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + + /** + * @notice Return the permissions flag that are associated with blacklist transfer manager + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } +} diff --git a/contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol b/contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol new file mode 100644 index 000000000..2c01246ff --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol @@ -0,0 +1,68 @@ +pragma solidity ^0.5.0; + +import "./BlacklistTransferManager.sol"; +import "../../ModuleFactory.sol"; +import "../../../libraries/Util.sol"; + +/** + * @title Factory for deploying BlacklistManager module + */ +contract BlacklistTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _polymathRegistry Address of the Polymath registry + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + version = "2.1.0"; + name = "BlacklistTransferManager"; + title = "Blacklist Transfer Manager"; + description = "Automate blacklist to restrict selling"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes calldata /* _data */) external returns(address) { + address polyToken = _takeFee(); + address blacklistTransferManager = address(new BlacklistTransferManager(msg.sender, address(polyToken))); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(blacklistTransferManager, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return blacklistTransferManager; + } + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; + } + + /** + * @notice Get the Instructions that helped to used the module + */ + function getInstructions() public view returns(string memory) { + return "Allows an issuer to blacklist the addresses."; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Blacklist"; + availableTags[1] = "Restricted transfer"; + return availableTags; + } + + +} diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol new file mode 100644 index 000000000..a2741d346 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "../../../interfaces/ISecurityToken.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract KYCTransferManager is TransferManager { + + using SafeMath for uint256; + + bytes32 public constant KYC_NUMBER = "KYC_NUMBER"; //We will standardize what key to use for what. + + bytes32 public constant KYC_PROVIDER = "KYC_PROVIDER"; + + bytes32 public constant KYC_ARRAY = "KYC_ARRAY"; + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) + external + returns (Result) + { + (Result success,)= verifyTransfer(_from, _to, _amount, _data); + return success; + } + + function verifyTransfer(address /*_from*/, address _to, uint256 /*_amount*/, bytes memory /* _data */) public view returns(Result, bytes32) { + if (!paused && checkKYC(_to)) { + return (Result.VALID, bytes32(uint256(address(this)) << 96)); + } + return (Result.NA, bytes32(0)); + } + + function modifyKYC( address _investor, bool _kycStatus) public withPerm(KYC_PROVIDER) { + _modifyKYC(_investor, _kycStatus); + } + + function _modifyKYC(address _investor, bool _kycStatus) internal { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + bytes32 key = _getKYCKey(_investor); + uint256 kycNumber = dataStore.getUint256(key); //index in address array + 1 + uint256 kycTotal = dataStore.getAddressArrayLength(KYC_ARRAY); + if(_kycStatus) { + require(kycNumber == 0, "KYC exists"); + dataStore.setUint256(key, kycTotal + 1); + dataStore.insertAddress(KYC_ARRAY, _investor); + } else { + require(kycNumber != 0, "KYC does not exist"); + address lastAddress = dataStore.getAddressArrayElement(KYC_ARRAY, kycTotal - 1); + dataStore.deleteAddress(KYC_ARRAY, kycNumber - 1); + + //Corrects the index of last element as delete fucntions move last element to index. + dataStore.setUint256(_getKYCKey(lastAddress), kycNumber); + } + //Alternatively, we can just emit an event and not maintain the KYC array on chain. + //I am maintaining the array to showcase how it can be done in cases where it might be needed. + } + + /** + * @notice Return the permissions flag that are associated with general trnasfer manager + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = KYC_PROVIDER; + return allPermissions; + } + + function getKYCAddresses() public view returns(address[] memory) { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + return dataStore.getAddressArray(KYC_ARRAY); + } + + function checkKYC(address _investor) public view returns (bool kyc) { + bytes32 key = _getKYCKey(_investor); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + if (dataStore.getUint256(key) > 0) + kyc = true; + } + + function _getKYCKey(address _identity) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(KYC_NUMBER, _identity))); + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + +} diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol new file mode 100644 index 000000000..f8de5dbef --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.5.0; + +import "./KYCTransferManager.sol"; +import "./../../ModuleFactory.sol"; + + +contract KYCTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + version = "1.0.0"; + name = "KYCTransferManager"; + title = "KYC Transfer Manager"; + description = "Manages KYC"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + + /** + * @notice Used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes calldata /* _data */) external returns(address) { + address polyToken = _takeFee(); + address kycTransferManager = address(new KYCTransferManager(msg.sender, polyToken)); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(kycTransferManager, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return kycTransferManager; + } + + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](2); + res[0] = 2; + res[1] = 6; + return res; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string memory) { + /*solium-disable-next-line max-len*/ + return "pray it works"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](1); + availableTags[0] = "KYC"; + return availableTags; + } + +} diff --git a/contracts/modules/Experimental/TransferManager/LockUpTransferManager.sol b/contracts/modules/Experimental/TransferManager/LockUpTransferManager.sol new file mode 100644 index 000000000..5519e44b6 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/LockUpTransferManager.sol @@ -0,0 +1,677 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/math/Math.sol"; + +contract LockUpTransferManager is TransferManager { + + using SafeMath for uint256; + + // permission definition + bytes32 public constant ADMIN = "ADMIN"; + + // a per-user lockup + struct LockUp { + uint256 lockupAmount; // Amount to be locked + uint256 startTime; // when this lockup starts (seconds) + uint256 lockUpPeriodSeconds; // total period of lockup (seconds) + uint256 releaseFrequencySeconds; // how often to release a tranche of tokens (seconds) + } + + // mapping use to store the lockup details corresponds to lockup name + mapping (bytes32 => LockUp) public lockups; + // mapping user addresses to an array of lockups name for that user + mapping (address => bytes32[]) internal userToLockups; + // get list of the addresses for a particular lockupName + mapping (bytes32 => address[]) internal lockupToUsers; + // holds lockup index corresponds to user address. userAddress => lockupName => lockupIndex + mapping (address => mapping(bytes32 => uint256)) internal userToLockupIndex; + // holds the user address index corresponds to the lockup. lockupName => userAddress => userIndex + mapping (bytes32 => mapping(address => uint256)) internal lockupToUserIndex; + + bytes32[] lockupArray; + + event AddLockUpToUser( + address indexed _userAddress, + bytes32 indexed _lockupName + ); + + event RemoveLockUpFromUser( + address indexed _userAddress, + bytes32 indexed _lockupName + ); + + event ModifyLockUpType( + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 indexed _lockupName + ); + + event AddNewLockUpType( + bytes32 indexed _lockupName, + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds + ); + + event RemoveLockUpType(bytes32 indexed _lockupName); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** @notice Used to verify the transfer transaction and prevent locked up tokens from being transferred + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(Result) { + (Result success,) = verifyTransfer(_from, _to, _amount, _data); + return success; + } + + /** @notice Used to verify the transfer transaction and prevent locked up tokens from being transferred + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function verifyTransfer( + address _from, + address /* _to*/, + uint256 _amount, + bytes memory /* _data */ + ) + public + view + returns(Result, bytes32) + { + // only attempt to verify the transfer if the token is unpaused, this isn't a mint txn, and there exists a lockup for this user + if (!paused && _from != address(0) && userToLockups[_from].length != 0) { + // check if this transfer is valid + return _checkIfValidTransfer(_from, _amount); + } + return (Result.NA, bytes32(0)); + } + + + /** + * @notice Use to add the new lockup type + * @param _lockupAmount Amount of tokens that need to lock. + * @param _startTime When this lockup starts (seconds) + * @param _lockUpPeriodSeconds Total period of lockup (seconds) + * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) + * @param _lockupName Name of the lockup + */ + function addNewLockUpType( + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + public + withPerm(ADMIN) + { + _addNewLockUpType( + _lockupAmount, + _startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + _lockupName + ); + } + + /** + * @notice Use to add the new lockup type + * @param _lockupAmounts Array of amount of tokens that need to lock. + * @param _startTimes Array of startTimes when this lockup starts (seconds) + * @param _lockUpPeriodsSeconds Array of total period of lockup (seconds) + * @param _releaseFrequenciesSeconds Array of how often to release a tranche of tokens (seconds) + * @param _lockupNames Array of names of the lockup + */ + function addNewLockUpTypeMulti( + uint256[] calldata _lockupAmounts, + uint256[] calldata _startTimes, + uint256[] calldata _lockUpPeriodsSeconds, + uint256[] calldata _releaseFrequenciesSeconds, + bytes32[] calldata _lockupNames + ) + external + withPerm(ADMIN) + { + require( + _lockupNames.length == _lockUpPeriodsSeconds.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _releaseFrequenciesSeconds.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _startTimes.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _lockupAmounts.length, + "Length mismatch" + ); + for (uint256 i = 0; i < _lockupNames.length; i++) { + _addNewLockUpType( + _lockupAmounts[i], + _startTimes[i], + _lockUpPeriodsSeconds[i], + _releaseFrequenciesSeconds[i], + _lockupNames[i] + ); + } + } + + /** + * @notice Add the lockup to a user + * @param _userAddress Address of the user + * @param _lockupName Name of the lockup + */ + function addLockUpByName( + address _userAddress, + bytes32 _lockupName + ) + public + withPerm(ADMIN) + { + _addLockUpByName(_userAddress, _lockupName); + } + + /** + * @notice Add the lockup to a user + * @param _userAddresses Address of the user + * @param _lockupNames Name of the lockup + */ + function addLockUpByNameMulti( + address[] calldata _userAddresses, + bytes32[] calldata _lockupNames + ) + external + withPerm(ADMIN) + { + require(_userAddresses.length == _lockupNames.length, "Length mismatch"); + for (uint256 i = 0; i < _userAddresses.length; i++) { + _addLockUpByName(_userAddresses[i], _lockupNames[i]); + } + } + + /** + * @notice Lets the admin create a volume restriction lockup for a given address. + * @param _userAddress Address of the user whose tokens should be locked up + * @param _lockupAmount Amount of tokens that need to lock. + * @param _startTime When this lockup starts (seconds) + * @param _lockUpPeriodSeconds Total period of lockup (seconds) + * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) + * @param _lockupName Name of the lockup + */ + function addNewLockUpToUser( + address _userAddress, + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + external + withPerm(ADMIN) + { + _addNewLockUpToUser( + _userAddress, + _lockupAmount, + _startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + _lockupName + ); + } + + /** + * @notice Lets the admin create multiple volume restriction lockups for multiple given addresses. + * @param _userAddresses Array of address of the user whose tokens should be locked up + * @param _lockupAmounts Array of the amounts that need to be locked for the different addresses. + * @param _startTimes Array of When this lockup starts (seconds) + * @param _lockUpPeriodsSeconds Array of total periods of lockup (seconds) + * @param _releaseFrequenciesSeconds Array of how often to release a tranche of tokens (seconds) + * @param _lockupNames Array of names of the lockup + */ + function addNewLockUpToUserMulti( + address[] memory _userAddresses, + uint256[] memory _lockupAmounts, + uint256[] memory _startTimes, + uint256[] memory _lockUpPeriodsSeconds, + uint256[] memory _releaseFrequenciesSeconds, + bytes32[] memory _lockupNames + ) + public + withPerm(ADMIN) + { + require( + _userAddresses.length == _lockUpPeriodsSeconds.length && /*solium-disable-line operator-whitespace*/ + _userAddresses.length == _releaseFrequenciesSeconds.length && /*solium-disable-line operator-whitespace*/ + _userAddresses.length == _startTimes.length && /*solium-disable-line operator-whitespace*/ + _userAddresses.length == _lockupAmounts.length && + _userAddresses.length == _lockupNames.length, + "Length mismatch" + ); + for (uint256 i = 0; i < _userAddresses.length; i++) { + _addNewLockUpToUser(_userAddresses[i], _lockupAmounts[i], _startTimes[i], _lockUpPeriodsSeconds[i], _releaseFrequenciesSeconds[i], _lockupNames[i]); + } + } + + /** + * @notice Lets the admin remove a user's lock up + * @param _userAddress Address of the user whose tokens are locked up + * @param _lockupName Name of the lockup need to be removed. + */ + function removeLockUpFromUser(address _userAddress, bytes32 _lockupName) external withPerm(ADMIN) { + _removeLockUpFromUser(_userAddress, _lockupName); + } + + /** + * @notice Used to remove the lockup type + * @param _lockupName Name of the lockup + */ + function removeLockupType(bytes32 _lockupName) external withPerm(ADMIN) { + _removeLockupType(_lockupName); + } + + /** + * @notice Used to remove the multiple lockup type + * @param _lockupNames Array of the lockup names. + */ + function removeLockupTypeMulti(bytes32[] calldata _lockupNames) external withPerm(ADMIN) { + for (uint256 i = 0; i < _lockupNames.length; i++) { + _removeLockupType(_lockupNames[i]); + } + } + + /** + * @notice Use to remove the lockup for multiple users + * @param _userAddresses Array of addresses of the user whose tokens are locked up + * @param _lockupNames Array of the names of the lockup that needs to be removed. + */ + function removeLockUpFromUserMulti(address[] calldata _userAddresses, bytes32[] calldata _lockupNames) external withPerm(ADMIN) { + require(_userAddresses.length == _lockupNames.length, "Length mismatch"); + for (uint256 i = 0; i < _userAddresses.length; i++) { + _removeLockUpFromUser(_userAddresses[i], _lockupNames[i]); + } + } + + /** + * @notice Lets the admin modify a lockup. + * @param _lockupAmount Amount of tokens that needs to be locked + * @param _startTime When this lockup starts (seconds) + * @param _lockUpPeriodSeconds Total period of lockup (seconds) + * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) + * @param _lockupName name of the lockup that needs to be modified. + */ + function modifyLockUpType( + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + external + withPerm(ADMIN) + { + _modifyLockUpType( + _lockupAmount, + _startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + _lockupName + ); + } + + /** + * @notice Lets the admin modify a volume restriction lockup for a multiple address. + * @param _lockupAmounts Array of the amount of tokens that needs to be locked for the respective addresses. + * @param _startTimes Array of the start time of the lockups (seconds) + * @param _lockUpPeriodsSeconds Array of unix timestamp for the list of lockups (seconds). + * @param _releaseFrequenciesSeconds How often to release a tranche of tokens (seconds) + * @param _lockupNames Array of the lockup names that needs to be modified + */ + function modifyLockUpTypeMulti( + uint256[] memory _lockupAmounts, + uint256[] memory _startTimes, + uint256[] memory _lockUpPeriodsSeconds, + uint256[] memory _releaseFrequenciesSeconds, + bytes32[] memory _lockupNames + ) + public + withPerm(ADMIN) + { + require( + _lockupNames.length == _lockUpPeriodsSeconds.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _releaseFrequenciesSeconds.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _startTimes.length && /*solium-disable-line operator-whitespace*/ + _lockupNames.length == _lockupAmounts.length, + "Length mismatch" + ); + for (uint256 i = 0; i < _lockupNames.length; i++) { + _modifyLockUpType( + _lockupAmounts[i], + _startTimes[i], + _lockUpPeriodsSeconds[i], + _releaseFrequenciesSeconds[i], + _lockupNames[i] + ); + } + } + + /** + * @notice Get a specific element in a user's lockups array given the user's address and the element index + * @param _lockupName The name of the lockup + */ + function getLockUp(bytes32 _lockupName) external view returns ( + uint256 lockupAmount, + uint256 startTime, + uint256 lockUpPeriodSeconds, + uint256 releaseFrequencySeconds, + uint256 unlockedAmount + ) { + if (lockups[_lockupName].lockupAmount != 0) { + return ( + lockups[_lockupName].lockupAmount, + lockups[_lockupName].startTime, + lockups[_lockupName].lockUpPeriodSeconds, + lockups[_lockupName].releaseFrequencySeconds, + _getUnlockedAmountForLockup(_lockupName) + ); + } + return (uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)); + } + + /** + * @notice get the list of the users of a lockup type + * @param _lockupName Name of the lockup type + * @return address List of users associated with the blacklist + */ + function getListOfAddresses(bytes32 _lockupName) external view returns(address[] memory) { + require(lockups[_lockupName].startTime != 0, "Invalid blacklist"); + return lockupToUsers[_lockupName]; + } + + /** + * @notice get the list of lockups names + * @return bytes32 Array of lockups names + */ + function getAllLockups() external view returns(bytes32[] memory) { + return lockupArray; + } + + /** + * @notice get the list of the lockups for a given user + * @param _user Address of the user + * @return bytes32 List of lockups names associated with the given address + */ + function getLockupsNamesToUser(address _user) external view returns(bytes32[] memory) { + return userToLockups[_user]; + } + + /** + * @notice Use to get the total locked tokens for a given user + * @param _userAddress Address of the user + * @return uint256 Total locked tokens amount + */ + function getLockedTokenToUser(address _userAddress) public view returns(uint256) { + require(_userAddress != address(0), "Invalid address"); + bytes32[] memory userLockupNames = userToLockups[_userAddress]; + uint256 totalRemainingLockedAmount = 0; + + for (uint256 i = 0; i < userLockupNames.length; i++) { + // Find out the remaining locked amount for a given lockup + uint256 remainingLockedAmount = lockups[userLockupNames[i]].lockupAmount.sub(_getUnlockedAmountForLockup(userLockupNames[i])); + // aggregating all the remaining locked amount for all the lockups for a given address + totalRemainingLockedAmount = totalRemainingLockedAmount.add(remainingLockedAmount); + } + return totalRemainingLockedAmount; + } + + /** + * @notice Checks whether the transfer is allowed + * @param _userAddress Address of the user whose lock ups should be checked + * @param _amount Amount of tokens that need to transact + */ + function _checkIfValidTransfer(address _userAddress, uint256 _amount) internal view returns (Result, bytes32) { + uint256 totalRemainingLockedAmount = getLockedTokenToUser(_userAddress); + // Present balance of the user + uint256 currentBalance = IERC20(securityToken).balanceOf(_userAddress); + if ((currentBalance.sub(_amount)) >= totalRemainingLockedAmount) { + return (Result.NA, bytes32(0)); + } + return (Result.INVALID, bytes32(uint256(address(this)) << 96)); + } + + /** + * @notice Provide the unlock amount for the given lockup for a particular user + */ + function _getUnlockedAmountForLockup(bytes32 _lockupName) internal view returns (uint256) { + /*solium-disable-next-line security/no-block-members*/ + if (lockups[_lockupName].startTime > now) { + return 0; + } else if (lockups[_lockupName].startTime.add(lockups[_lockupName].lockUpPeriodSeconds) <= now) { + return lockups[_lockupName].lockupAmount; + } else { + // Calculate the no. of periods for a lockup + uint256 noOfPeriods = (lockups[_lockupName].lockUpPeriodSeconds).div(lockups[_lockupName].releaseFrequencySeconds); + // Calculate the transaction time lies in which period + /*solium-disable-next-line security/no-block-members*/ + uint256 elapsedPeriod = (now.sub(lockups[_lockupName].startTime)).div(lockups[_lockupName].releaseFrequencySeconds); + // Find out the unlocked amount for a given lockup + uint256 unLockedAmount = (lockups[_lockupName].lockupAmount.mul(elapsedPeriod)).div(noOfPeriods); + return unLockedAmount; + } + } + + function _removeLockupType(bytes32 _lockupName) internal { + require(lockups[_lockupName].startTime != 0, "Invalid lockup"); + require(lockupToUsers[_lockupName].length == 0, "Not empty"); + // delete lockup type + delete(lockups[_lockupName]); + uint256 i = 0; + for (i = 0; i < lockupArray.length; i++) { + if (lockupArray[i] == _lockupName) { + break; + } + } + if (i != lockupArray.length -1) { + lockupArray[i] = lockupArray[lockupArray.length -1]; + } + lockupArray.length--; + emit RemoveLockUpType(_lockupName); + } + + function _modifyLockUpType( + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + internal + { + /*solium-disable-next-line security/no-block-members*/ + uint256 startTime = _startTime; + + if (_startTime == 0) { + startTime = now; + } + require(startTime >= now, "Invalid start time"); + require(lockups[_lockupName].lockupAmount != 0, "Doesn't exist"); + + _checkLockUpParams( + _lockupAmount, + _lockUpPeriodSeconds, + _releaseFrequencySeconds + ); + + lockups[_lockupName] = LockUp( + _lockupAmount, + startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds + ); + + emit ModifyLockUpType( + _lockupAmount, + startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + _lockupName + ); + } + + function _removeLockUpFromUser(address _userAddress, bytes32 _lockupName) internal { + require(_userAddress != address(0), "Invalid address"); + require(_lockupName != bytes32(0), "Invalid lockup name"); + require( + userToLockups[_userAddress][userToLockupIndex[_userAddress][_lockupName]] == _lockupName, + "Not empty" + ); + + // delete the user from the lockup type + uint256 _lockupIndex = lockupToUserIndex[_lockupName][_userAddress]; + uint256 _len = lockupToUsers[_lockupName].length; + if ( _lockupIndex != _len) { + lockupToUsers[_lockupName][_lockupIndex] = lockupToUsers[_lockupName][_len - 1]; + lockupToUserIndex[_lockupName][lockupToUsers[_lockupName][_lockupIndex]] = _lockupIndex; + } + lockupToUsers[_lockupName].length--; + // delete the user index from the lockup + delete(lockupToUserIndex[_lockupName][_userAddress]); + // delete the lockup from the user + uint256 _userIndex = userToLockupIndex[_userAddress][_lockupName]; + _len = userToLockups[_userAddress].length; + if ( _userIndex != _len) { + userToLockups[_userAddress][_userIndex] = userToLockups[_userAddress][_len - 1]; + userToLockupIndex[_userAddress][userToLockups[_userAddress][_userIndex]] = _userIndex; + } + userToLockups[_userAddress].length--; + // delete the lockup index from the user + delete(userToLockupIndex[_userAddress][_lockupName]); + emit RemoveLockUpFromUser(_userAddress, _lockupName); + } + + function _addNewLockUpToUser( + address _userAddress, + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + internal + { + require(_userAddress != address(0), "Invalid address"); + _addNewLockUpType( + _lockupAmount, + _startTime, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + _lockupName + ); + _addLockUpByName(_userAddress, _lockupName); + } + + function _addLockUpByName( + address _userAddress, + bytes32 _lockupName + ) + internal + { + require(_userAddress != address(0), "Invalid address"); + require(lockups[_lockupName].startTime >= now, "Lockup expired"); + + userToLockupIndex[_userAddress][_lockupName] = userToLockups[_userAddress].length; + lockupToUserIndex[_lockupName][_userAddress] = lockupToUsers[_lockupName].length; + userToLockups[_userAddress].push(_lockupName); + lockupToUsers[_lockupName].push(_userAddress); + emit AddLockUpToUser(_userAddress, _lockupName); + } + + function _addNewLockUpType( + uint256 _lockupAmount, + uint256 _startTime, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds, + bytes32 _lockupName + ) + internal + { + uint256 startTime = _startTime; + require(_lockupName != bytes32(0), "Invalid name"); + require(lockups[_lockupName].lockupAmount == 0, "Already exist"); + /*solium-disable-next-line security/no-block-members*/ + if (_startTime == 0) { + startTime = now; + } + require(startTime >= now, "Invalid start time"); + _checkLockUpParams(_lockupAmount, _lockUpPeriodSeconds, _releaseFrequencySeconds); + lockups[_lockupName] = LockUp(_lockupAmount, startTime, _lockUpPeriodSeconds, _releaseFrequencySeconds); + lockupArray.push(_lockupName); + emit AddNewLockUpType(_lockupName, _lockupAmount, startTime, _lockUpPeriodSeconds, _releaseFrequencySeconds); + } + + /** + * @notice Parameter checking function for creating or editing a lockup. + * This function will cause an exception if any of the parameters are bad. + * @param _lockupAmount Amount that needs to be locked + * @param _lockUpPeriodSeconds Total period of lockup (seconds) + * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) + */ + function _checkLockUpParams( + uint256 _lockupAmount, + uint256 _lockUpPeriodSeconds, + uint256 _releaseFrequencySeconds + ) + internal + pure + { + require( + _lockUpPeriodSeconds != 0 && + _releaseFrequencySeconds != 0 && + _lockupAmount != 0, + "Cannot be zero" + ); + } + + /** + * @notice return the amount of tokens for a given user as per the partition + * @param _owner Whom token amount need to query + * @param _partition Identifier + */ + function getTokensByPartition(address _owner, bytes32 _partition) external view returns(uint256){ + uint256 _currentBalance = IERC20(securityToken).balanceOf(_owner); + if (_partition == LOCKED) { + return Math.min(getLockedTokenToUser(_owner), _currentBalance); + } else if (_partition == UNLOCKED) { + if (_currentBalance < getLockedTokenToUser(_owner)) { + return 0; + } + return _currentBalance.sub(getLockedTokenToUser(_owner)); + } + return 0; + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + /** + * @notice Returns the permissions flag that are associated with Percentage transfer Manager + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } +} diff --git a/contracts/modules/Experimental/TransferManager/LockUpTransferManagerFactory.sol b/contracts/modules/Experimental/TransferManager/LockUpTransferManagerFactory.sol new file mode 100644 index 000000000..6b47972f1 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/LockUpTransferManagerFactory.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.5.0; + +import "../../ModuleFactory.sol"; +import "./LockUpTransferManager.sol"; + +/** + * @title Factory for deploying LockUpTransferManager module + */ +contract LockUpTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _polymathRegistry Address of the Polymath registry + */ + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + version = "1.0.0"; + name = "LockUpTransferManager"; + title = "LockUp Transfer Manager"; + description = "Manage transfers using lock ups over time"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + /** + * @notice Used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + LockUpTransferManager lockUpTransferManager = new LockUpTransferManager(msg.sender, polyToken); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(address(lockUpTransferManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return address(lockUpTransferManager); + } + + /** + * @notice Type of the Module factory + * @return uint8 + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string memory) { + return "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters."; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() external view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "LockUp"; + availableTags[1] = "Transfer Restriction"; + return availableTags; + } + +} diff --git a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol deleted file mode 100644 index 80f44cdb6..000000000 --- a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol +++ /dev/null @@ -1,411 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../TransferManager/ITransferManager.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - - -contract LockupVolumeRestrictionTM is ITransferManager { - - using SafeMath for uint256; - - // permission definition - bytes32 public constant ADMIN = "ADMIN"; - - // a per-user lockup - struct LockUp { - uint lockUpPeriodSeconds; // total period of lockup (seconds) - uint releaseFrequencySeconds; // how often to release a tranche of tokens (seconds) - uint startTime; // when this lockup starts (seconds) - uint totalAmount; // total amount of locked up tokens - uint alreadyWithdrawn; // amount already withdrawn for this lockup - } - - // maps user addresses to an array of lockups for that user - mapping (address => LockUp[]) internal lockUps; - - event AddNewLockUp( - address indexed userAddress, - uint lockUpPeriodSeconds, - uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed addedIndex - ); - - event RemoveLockUp( - address indexed userAddress, - uint lockUpPeriodSeconds, - uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed removedIndex - ); - - event ModifyLockUp( - address indexed userAddress, - uint lockUpPeriodSeconds, - uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed modifiedIndex - ); - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor (address _securityToken, address _polyAddress) - public - Module(_securityToken, _polyAddress) - { - } - - - /** @notice Used to verify the transfer transaction and prevent locked up tokens from being transferred - * @param _from Address of the sender - * @param _amount The amount of tokens to transfer - * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable - */ - function verifyTransfer(address _from, address /* _to*/, uint256 _amount, bytes /* _data */, bool _isTransfer) public returns(Result) { - // only attempt to verify the transfer if the token is unpaused, this isn't a mint txn, and there exists a lockup for this user - if (!paused && _from != address(0) && lockUps[_from].length != 0) { - // check if this transfer is valid - return _checkIfValidTransfer(_from, _amount, _isTransfer); - } - return Result.NA; - } - - /** - * @notice Lets the admin create a volume restriction lockup for a given address. - * @param _userAddress Address of the user whose tokens should be locked up - * @param _lockUpPeriodSeconds Total period of lockup (seconds) - * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) - * @param _startTime When this lockup starts (seconds) - * @param _totalAmount Total amount of locked up tokens - */ - function addLockUp( - address _userAddress, - uint _lockUpPeriodSeconds, - uint _releaseFrequencySeconds, - uint _startTime, - uint _totalAmount - ) public withPerm(ADMIN) { - uint256 startTime = _startTime; - _checkLockUpParams(_lockUpPeriodSeconds, _releaseFrequencySeconds, _totalAmount); - - // if a startTime of 0 is passed in, then start now. - if (startTime == 0) { - /*solium-disable-next-line security/no-block-members*/ - startTime = now; - } - - lockUps[_userAddress].push(LockUp(_lockUpPeriodSeconds, _releaseFrequencySeconds, startTime, _totalAmount, 0)); - - emit AddNewLockUp( - _userAddress, - _lockUpPeriodSeconds, - _releaseFrequencySeconds, - startTime, - _totalAmount, - lockUps[_userAddress].length - 1 - ); - } - - /** - * @notice Lets the admin create multiple volume restriction lockups for multiple given addresses. - * @param _userAddresses Array of address of the user whose tokens should be locked up - * @param _lockUpPeriodsSeconds Array of total periods of lockup (seconds) - * @param _releaseFrequenciesSeconds Array of how often to release a tranche of tokens (seconds) - * @param _startTimes Array of When this lockup starts (seconds) - * @param _totalAmounts Array of total amount of locked up tokens - */ - function addLockUpMulti( - address[] _userAddresses, - uint[] _lockUpPeriodsSeconds, - uint[] _releaseFrequenciesSeconds, - uint[] _startTimes, - uint[] _totalAmounts - ) external withPerm(ADMIN) { - require( - _userAddresses.length == _lockUpPeriodsSeconds.length && /*solium-disable-line operator-whitespace*/ - _userAddresses.length == _releaseFrequenciesSeconds.length && /*solium-disable-line operator-whitespace*/ - _userAddresses.length == _startTimes.length && - _userAddresses.length == _totalAmounts.length, - "Input array length mismatch" - ); - - for (uint i = 0; i < _userAddresses.length; i++) { - addLockUp(_userAddresses[i], _lockUpPeriodsSeconds[i], _releaseFrequenciesSeconds[i], _startTimes[i], _totalAmounts[i]); - } - - } - - /** - * @notice Lets the admin remove a user's lock up - * @param _userAddress Address of the user whose tokens are locked up - * @param _lockUpIndex The index of the LockUp to remove for the given userAddress - */ - function removeLockUp(address _userAddress, uint _lockUpIndex) public withPerm(ADMIN) { - LockUp[] storage userLockUps = lockUps[_userAddress]; - require(_lockUpIndex < userLockUps.length, "Array out of bounds exception"); - - LockUp memory toRemove = userLockUps[_lockUpIndex]; - - emit RemoveLockUp( - _userAddress, - toRemove.lockUpPeriodSeconds, - toRemove.releaseFrequencySeconds, - toRemove.startTime, - toRemove.totalAmount, - _lockUpIndex - ); - - if (_lockUpIndex < userLockUps.length - 1) { - // move the last element in the array into the index that is desired to be removed. - userLockUps[_lockUpIndex] = userLockUps[userLockUps.length - 1]; - } - // delete the last element - userLockUps.length--; - } - - /** - * @notice Lets the admin modify a volume restriction lockup for a given address. - * @param _userAddress Address of the user whose tokens should be locked up - * @param _lockUpIndex The index of the LockUp to edit for the given userAddress - * @param _lockUpPeriodSeconds Total period of lockup (seconds) - * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) - * @param _startTime When this lockup starts (seconds) - * @param _totalAmount Total amount of locked up tokens - */ - function modifyLockUp( - address _userAddress, - uint _lockUpIndex, - uint _lockUpPeriodSeconds, - uint _releaseFrequencySeconds, - uint _startTime, - uint _totalAmount - ) public withPerm(ADMIN) { - require(_lockUpIndex < lockUps[_userAddress].length, "Array out of bounds exception"); - - uint256 startTime = _startTime; - // if a startTime of 0 is passed in, then start now. - if (startTime == 0) { - /*solium-disable-next-line security/no-block-members*/ - startTime = now; - } - - _checkLockUpParams(_lockUpPeriodSeconds, _releaseFrequencySeconds, _totalAmount); - - // Get the lockup from the master list and edit it - lockUps[_userAddress][_lockUpIndex] = LockUp( - _lockUpPeriodSeconds, - _releaseFrequencySeconds, - startTime, - _totalAmount, - lockUps[_userAddress][_lockUpIndex].alreadyWithdrawn - ); - - emit ModifyLockUp( - _userAddress, - _lockUpPeriodSeconds, - _releaseFrequencySeconds, - startTime, - _totalAmount, - _lockUpIndex - ); - } - - /** - * @notice Get the length of the lockups array for a specific user address - * @param _userAddress Address of the user whose tokens should be locked up - */ - function getLockUpsLength(address _userAddress) public view returns (uint) { - return lockUps[_userAddress].length; - } - - /** - * @notice Get a specific element in a user's lockups array given the user's address and the element index - * @param _userAddress Address of the user whose tokens should be locked up - * @param _lockUpIndex The index of the LockUp to edit for the given userAddress - */ - function getLockUp( - address _userAddress, - uint _lockUpIndex) - public view returns ( - uint lockUpPeriodSeconds, - uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint alreadyWithdrawn - ) { - require(_lockUpIndex < lockUps[_userAddress].length, "Array out of bounds exception"); - LockUp storage userLockUp = lockUps[_userAddress][_lockUpIndex]; - return ( - userLockUp.lockUpPeriodSeconds, - userLockUp.releaseFrequencySeconds, - userLockUp.startTime, - userLockUp.totalAmount, - userLockUp.alreadyWithdrawn - ); - } - - /** - * @notice Takes a userAddress as input, and returns a uint that represents the number of tokens allowed to be withdrawn right now - * @param userAddress Address of the user whose lock ups should be checked - */ - function _checkIfValidTransfer(address userAddress, uint amount, bool isTransfer) internal returns (Result) { - // get lock up array for this user - LockUp[] storage userLockUps = lockUps[userAddress]; - - // maps the index of userLockUps to the amount allowed in this transfer - uint[] memory allowedAmountPerLockup = new uint[](userLockUps.length); - - uint[3] memory tokenSums = [ - uint256(0), // allowed amount right now - uint256(0), // total locked up, ever - uint256(0) // already withdrawn, ever - ]; - - // loop over the user's lock ups - for (uint i = 0; i < userLockUps.length; i++) { - LockUp storage aLockUp = userLockUps[i]; - - uint allowedAmountForThisLockup = 0; - - // check if lockup has entirely passed - /*solium-disable-next-line security/no-block-members*/ - if (now >= aLockUp.startTime.add(aLockUp.lockUpPeriodSeconds)) { - // lockup has passed, or not started yet. allow all. - allowedAmountForThisLockup = aLockUp.totalAmount.sub(aLockUp.alreadyWithdrawn); - /*solium-disable-next-line security/no-block-members*/ - } else if (now >= aLockUp.startTime) { - // lockup is active. calculate how many to allow to be withdrawn right now - // calculate how many periods have elapsed already - /*solium-disable-next-line security/no-block-members*/ - uint elapsedPeriods = (now.sub(aLockUp.startTime)).div(aLockUp.releaseFrequencySeconds); - // calculate the total number of periods, overall - uint totalPeriods = aLockUp.lockUpPeriodSeconds.div(aLockUp.releaseFrequencySeconds); - // calculate how much should be released per period - uint amountPerPeriod = aLockUp.totalAmount.div(totalPeriods); - // calculate the number of tokens that should be released, - // multiplied by the number of periods that have elapsed already - // and add it to the total tokenSums[0] - allowedAmountForThisLockup = amountPerPeriod.mul(elapsedPeriods).sub(aLockUp.alreadyWithdrawn); - - } - // tokenSums[0] is allowed sum - tokenSums[0] = tokenSums[0].add(allowedAmountForThisLockup); - // tokenSums[1] is total locked up - tokenSums[1] = tokenSums[1].add(aLockUp.totalAmount); - // tokenSums[2] is total already withdrawn - tokenSums[2] = tokenSums[2].add(aLockUp.alreadyWithdrawn); - - allowedAmountPerLockup[i] = allowedAmountForThisLockup; - } - - // tokenSums[0] is allowed sum - if (amount <= tokenSums[0]) { - // transfer is valid and will succeed. - if (!isTransfer) { - // if this isn't a real transfer, don't subtract the withdrawn amounts from the lockups. it's a "read only" txn - return Result.VALID; - } - - // we are going to write the withdrawn balances back to the lockups, so make sure that the person calling this function is the securityToken itself, since its public - require(msg.sender == securityToken, "Sender is not securityToken"); - - // subtract amounts so they are now known to be withdrawen - for (i = 0; i < userLockUps.length; i++) { - aLockUp = userLockUps[i]; - - // tokenSums[0] is allowed sum - if (allowedAmountPerLockup[i] >= tokenSums[0]) { - aLockUp.alreadyWithdrawn = aLockUp.alreadyWithdrawn.add(tokenSums[0]); - // we withdrew the entire tokenSums[0] from the lockup. We are done. - break; - } else { - // we have to split the tokenSums[0] across mutiple lockUps - aLockUp.alreadyWithdrawn = aLockUp.alreadyWithdrawn.add(allowedAmountPerLockup[i]); - // subtract the amount withdrawn from this lockup - tokenSums[0] = tokenSums[0].sub(allowedAmountPerLockup[i]); - } - - } - return Result.VALID; - } - - return _checkIfUnlockedTokenTransferIsPossible(userAddress, amount, tokenSums[1], tokenSums[2]); - } - - function _checkIfUnlockedTokenTransferIsPossible( - address userAddress, - uint amount, - uint totalSum, - uint alreadyWithdrawnSum - ) internal view returns (Result) { - // the amount the user wants to withdraw is greater than their allowed amounts according to the lockups. however, if the user has like, 10 tokens, but only 4 are locked up, we should let the transfer go through for those 6 that aren't locked up - uint currentUserBalance = ISecurityToken(securityToken).balanceOf(userAddress); - uint stillLockedAmount = totalSum.sub(alreadyWithdrawnSum); - if (currentUserBalance >= stillLockedAmount && amount <= currentUserBalance.sub(stillLockedAmount)) { - // the user has more tokens in their balance than are actually locked up. they should be allowed to withdraw the difference - return Result.VALID; - } - return Result.INVALID; - } - - - /** - * @notice Parameter checking function for creating or editing a lockup. This function will cause an exception if any of the parameters are bad. - * @param lockUpPeriodSeconds Total period of lockup (seconds) - * @param releaseFrequencySeconds How often to release a tranche of tokens (seconds) - * @param totalAmount Total amount of locked up tokens - */ - function _checkLockUpParams(uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint totalAmount) internal view { - require(lockUpPeriodSeconds != 0, "lockUpPeriodSeconds cannot be zero"); - require(releaseFrequencySeconds != 0, "releaseFrequencySeconds cannot be zero"); - require(totalAmount != 0, "totalAmount cannot be zero"); - - // check that the total amount to be released isn't too granular - require( - totalAmount % ISecurityToken(securityToken).granularity() == 0, - "The total amount to be released is more granular than allowed by the token" - ); - - // check that releaseFrequencySeconds evenly divides lockUpPeriodSeconds - require( - lockUpPeriodSeconds % releaseFrequencySeconds == 0, - "lockUpPeriodSeconds must be evenly divisible by releaseFrequencySeconds" - ); - - // check that totalPeriods evenly divides totalAmount - uint totalPeriods = lockUpPeriodSeconds.div(releaseFrequencySeconds); - require( - totalAmount % totalPeriods == 0, - "The total amount being locked up must be evenly divisible by the number of total periods" - ); - - // make sure the amount to be released per period is not too granular for the token - uint amountPerPeriod = totalAmount.div(totalPeriods); - require( - amountPerPeriod % ISecurityToken(securityToken).granularity() == 0, - "The amount to be released per period is more granular than allowed by the token" - ); - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(0); - } - - /** - * @notice Returns the permissions flag that are associated with Percentage transfer Manager - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = ADMIN; - return allPermissions; - } -} diff --git a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTMFactory.sol deleted file mode 100644 index 5be77b6e0..000000000 --- a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTMFactory.sol +++ /dev/null @@ -1,70 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../ModuleFactory.sol"; -import "./LockupVolumeRestrictionTM.sol"; - -/** - * @title Factory for deploying ManualApprovalTransferManager module - */ -contract LockupVolumeRestrictionTMFactory is ModuleFactory { - - /** - * @notice Constructor - * @param _polyAddress Address of the polytoken - * @param _setupCost Setup cost of the module - * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module - */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) - { - version = "1.0.0"; - name = "LockupVolumeRestrictionTM"; - title = "Lockup Volume Restriction Transfer Manager"; - description = "Manage transfers using lock ups over time"; - compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); - compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); - } - - /** - * @notice Used to launch the Module with the help of factory - * @return address Contract address of the Module - */ - function deploy(bytes /* _data */) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - LockupVolumeRestrictionTM lockupVolumeRestrictionTransferManager = new LockupVolumeRestrictionTM(msg.sender, address(polyToken)); - /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(lockupVolumeRestrictionTransferManager), getName(), address(this), msg.sender, now); - return address(lockupVolumeRestrictionTransferManager); - } - - /** - * @notice Type of the Module factory - * @return uint8 - */ - function getTypes() external view returns(uint8[]) { - uint8[] memory res = new uint8[](1); - res[0] = 2; - return res; - } - - /** - * @notice Returns the instructions associated with the module - */ - function getInstructions() external view returns(string) { - return "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters."; - } - - /** - * @notice Get the tags related to the module factory - */ - function getTags() external view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](2); - availableTags[0] = "Volume"; - availableTags[1] = "Transfer Restriction"; - return availableTags; - } - - -} diff --git a/contracts/modules/Experimental/TransferManager/SignedTransferManager.sol b/contracts/modules/Experimental/TransferManager/SignedTransferManager.sol new file mode 100644 index 000000000..bcf42edb8 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/SignedTransferManager.sol @@ -0,0 +1,194 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; + +/** + * @title Transfer Manager module for verifing transations with a signed message + */ +contract SignedTransferManager is TransferManager { + using SafeMath for uint256; + using ECDSA for bytes32; + + bytes32 constant public ADMIN = "ADMIN"; + + //Keeps track of if the signature has been used or invalidated + //mapping(bytes => bool) invalidSignatures; + bytes32 constant public INVALID_SIG = "INVALIDSIG"; + + //keep tracks of the address that allows to sign messages + //mapping(address => bool) public signers; + bytes32 constant public SIGNER = "SIGNER"; + + // Emit when signer stats was changed + event SignersUpdated(address[] _signers, bool[] _signersStats); + + // Emit when a signature has been deemed invalid + event SignatureUsed(bytes _data); + + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() external pure returns (bytes4) { + return bytes4(0); + } + + /** + * @notice function to check if a signature is still valid + * @param _data signature + */ + function checkSignatureValidity(bytes calldata _data) external view returns(bool) { + address targetAddress; + uint256 nonce; + uint256 expiry; + bytes memory signature; + (targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes)); + if (targetAddress != address(this) || expiry < now || signature.length == 0 || _checkSignatureIsInvalid(signature)) + return false; + return true; + } + + function checkSigner(address _signer) external view returns(bool) { + return _checkSigner(_signer); + } + + /** + * @notice function to remove or add signer(s) onto the signer mapping + * @param _signers address array of signers + * @param _signersStats bool array of signers stats + */ + function updateSigners(address[] calldata _signers, bool[] calldata _signersStats) external withPerm(ADMIN) { + require(_signers.length == _signersStats.length, "Array length mismatch"); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + for(uint256 i=0; i<_signers.length; i++) { + require(_signers[i] != address(0), "Invalid address"); + dataStore.setBool(keccak256(abi.encodePacked(SIGNER, _signers[i])), _signersStats[i]); + } + emit SignersUpdated(_signers, _signersStats); + } + + /** + * @notice allow verify transfer with signature + * @param _from address transfer from + * @param _to address transfer to + * @param _amount transfer amount + * @param _data signature + * Sig needs to be valid (not used or deemed as invalid) + * Signer needs to be in the signers mapping + */ + function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external onlySecurityToken returns(Result) { + (Result success, ) = verifyTransfer(_from, _to, _amount, _data); + if (success == Result.VALID) { + bytes memory signature; + (,,,signature) = abi.decode(_data, (address, uint256, uint256, bytes)); + _invalidateSignature(signature); + } + return success; + } + + /** + * @notice allow verify transfer with signature + * @param _from address transfer from + * @param _to address transfer to + * @param _amount transfer amount + * @param _data signature + * Sig needs to be valid (not used or deemed as invalid) + * Signer needs to be in the signers mapping + */ + function verifyTransfer(address _from, address _to, uint256 _amount, bytes memory _data) public view returns(Result, bytes32) { + if (!paused) { + + if (_data.length == 0) + return (Result.NA, bytes32(0)); + + address targetAddress; + uint256 nonce; + uint256 expiry; + bytes memory signature; + (targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes)); + + if (address(this) != targetAddress || signature.length == 0 || _checkSignatureIsInvalid(signature) || expiry < now) + return (Result.NA, bytes32(0)); + + bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, expiry, _from, _to, _amount)); + address signer = hash.toEthSignedMessageHash().recover(signature); + + if (!_checkSigner(signer)) + return (Result.NA, bytes32(0)); + return (Result.VALID, bytes32(uint256(address(this)) << 96)); + } + return (Result.NA, bytes32(0)); + } + + /** + * @notice allow signers to deem a signature invalid + * @param _from address transfer from + * @param _to address transfer to + * @param _amount transfer amount + * @param _data signature + * Sig needs to be valid (not used or deemed as invalid) + * Signer needs to be in the signers mapping + */ + function invalidateSignature(address _from, address _to, uint256 _amount, bytes calldata _data) external { + require(_checkSigner(msg.sender), "Unauthorized Signer"); + + address targetAddress; + uint256 nonce; + uint256 expiry; + bytes memory signature; + (targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes)); + + require(!_checkSignatureIsInvalid(signature), "Signature already invalid"); + require(targetAddress == address(this), "Signature not for this module"); + + bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, expiry, _from, _to, _amount)); + require(hash.toEthSignedMessageHash().recover(signature) == msg.sender, "Incorrect Signer"); + + _invalidateSignature(signature); + } + + /** + * @notice Return the permissions flag that are associated with ManualApproval transfer manager + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256) { + return 0; + } + + function _checkSignatureIsInvalid(bytes memory _data) internal view returns(bool) { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + return dataStore.getBool(keccak256(abi.encodePacked(INVALID_SIG, _data))); + } + + function _checkSigner(address _signer) internal view returns(bool) { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + return dataStore.getBool(keccak256(abi.encodePacked(SIGNER, _signer))); + } + + function _invalidateSignature(bytes memory _data) internal { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + dataStore.setBool(keccak256(abi.encodePacked(INVALID_SIG, _data)), true); + emit SignatureUsed(_data); + } +} diff --git a/contracts/modules/Experimental/TransferManager/SignedTransferManagerFactory.sol b/contracts/modules/Experimental/TransferManager/SignedTransferManagerFactory.sol new file mode 100644 index 000000000..8f3f90ced --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/SignedTransferManagerFactory.sol @@ -0,0 +1,74 @@ +pragma solidity ^0.5.0; + +import "./SignedTransferManager.sol"; +import "../../ModuleFactory.sol"; + +/** + * @title Factory for deploying SignedTransferManager module + */ +contract SignedTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + version = "1.0.0"; + name = "SignedTransferManager"; + title = "Signed Transfer Manager"; + description = "Manage transfers using a signature"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + // function deploy(bytes calldata /* _data */) external returns(address) { + // if (setupCost > 0) + // require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); + // address signedTransferManager = new SignedTransferManager(msg.sender, address(polyToken)); + // emit GenerateModuleFromFactory(address(signedTransferManager), getName(), address(this), msg.sender, setupCost, now); + // return address(signedTransferManager); + // } + + function deploy(bytes calldata /* _data */) external returns(address) { + address polyToken = _takeFee(); + SignedTransferManager signedTransferManager = new SignedTransferManager(msg.sender, polyToken); + emit GenerateModuleFromFactory(address(signedTransferManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return address(signedTransferManager); + } + + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](2); + res[0] = 2; + res[1] = 6; + return res; + } + + /** + * @notice Get the Instructions that helped to used the module + */ + function getInstructions() external view returns(string memory) { + return "Allows an issuer to maintain a list of signers who can validate transfer request using signatures. A mapping is used to track valid signers which can be managed by the issuer. verifytransfer function takes in a signature and if the signature is valid, it will verify the transfer. invalidSigature function allow the signer to make a signature invalid after it is signed. Init function takes no parameters."; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Signed"; + availableTags[1] = "Transfer Restriction"; + return availableTags; + } + + +} diff --git a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol deleted file mode 100644 index b92272167..000000000 --- a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol +++ /dev/null @@ -1,331 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../TransferManager/ITransferManager.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -/** - * @title Transfer Manager for limiting volume of tokens in a single trade - */ - -contract SingleTradeVolumeRestrictionTM is ITransferManager { - using SafeMath for uint256; - - bytes32 constant public ADMIN = "ADMIN"; - - bool public isTransferLimitInPercentage; - - uint256 public globalTransferLimitInTokens; - - // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 - uint256 public globalTransferLimitInPercentage; - - // Ignore transactions which are part of the primary issuance - bool public allowPrimaryIssuance = true; - - //mapping to store the wallets that are exempted from the volume restriction - mapping(address => bool) public exemptWallets; - - //addresses on this list have special transfer restrictions apart from global - mapping(address => uint) public specialTransferLimitsInTokens; - - mapping(address => uint) public specialTransferLimitsInPercentages; - - event ExemptWalletAdded(address _wallet); - event ExemptWalletRemoved(address _wallet); - event TransferLimitInTokensSet(address _wallet, uint256 _amount); - event TransferLimitInPercentageSet(address _wallet, uint _percentage); - event TransferLimitInPercentageRemoved(address _wallet); - event TransferLimitInTokensRemoved(address _wallet); - event GlobalTransferLimitInTokensSet(uint256 _amount, uint256 _oldAmount); - event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); - event TransferLimitChangedToTokens(); - event TransferLimitChangedtoPercentage(); - event SetAllowPrimaryIssuance(bool _allowPrimaryIssuance, uint256 _timestamp); - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor(address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { - - } - - /** @notice Used to verify the transfer transaction and prevent an account from sending more tokens than allowed in a single transfer - * @param _from Address of the sender - * @param _amount The amount of tokens to transfer - */ - function verifyTransfer( - address _from, - address /* _to */, - uint256 _amount, - bytes /* _data */, - bool /* _isTransfer */ - ) - public - returns(Result) - { - bool validTransfer; - - if (exemptWallets[_from] || paused) return Result.NA; - - if (_from == address(0) && allowPrimaryIssuance) { - return Result.NA; - } - - if (isTransferLimitInPercentage) { - if(specialTransferLimitsInPercentages[_from] > 0) { - validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimitsInPercentages[_from]; - } else { - validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; - } - } else { - if (specialTransferLimitsInTokens[_from] > 0) { - validTransfer = _amount <= specialTransferLimitsInTokens[_from]; - } else { - validTransfer = _amount <= globalTransferLimitInTokens; - } - } - if (validTransfer) return Result.NA; - return Result.INVALID; - } - - /** - * @notice Used to intialize the variables of the contract - * @param _isTransferLimitInPercentage true if the transfer limit is in percentage else false - * @param _globalTransferLimitInPercentageOrToken transfer limit per single transaction. - */ - function configure( - bool _isTransferLimitInPercentage, - uint256 _globalTransferLimitInPercentageOrToken, - bool _allowPrimaryIssuance - ) public onlyFactory { - isTransferLimitInPercentage = _isTransferLimitInPercentage; - if (isTransferLimitInPercentage) { - changeGlobalLimitInPercentage(_globalTransferLimitInPercentageOrToken); - } else { - changeGlobalLimitInTokens(_globalTransferLimitInPercentageOrToken); - } - allowPrimaryIssuance = _allowPrimaryIssuance; - } - - /** - * @notice Sets whether or not to consider primary issuance transfers - * @param _allowPrimaryIssuance whether to allow all primary issuance transfers - */ - function setAllowPrimaryIssuance(bool _allowPrimaryIssuance) public withPerm(ADMIN) { - require(_allowPrimaryIssuance != allowPrimaryIssuance, "Must change setting"); - allowPrimaryIssuance = _allowPrimaryIssuance; - /*solium-disable-next-line security/no-block-members*/ - emit SetAllowPrimaryIssuance(_allowPrimaryIssuance, now); - } - - /** - * @notice Changes the manager to use transfer limit as Percentages - * @param _newGlobalTransferLimitInPercentage uint256 new global Transfer Limit In Percentage. - * @dev specialTransferLimits set for wallets have to re-configured - */ - function changeTransferLimitToPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { - require(!isTransferLimitInPercentage, "Transfer limit already in percentage"); - isTransferLimitInPercentage = true; - changeGlobalLimitInPercentage(_newGlobalTransferLimitInPercentage); - emit TransferLimitChangedtoPercentage(); - } - - /** - * @notice Changes the manager to use transfer limit as tokens - * @param _newGlobalTransferLimit uint256 new global Transfer Limit in tokens. - * @dev specialTransferLimits set for wallets have to re-configured - */ - function changeTransferLimitToTokens(uint _newGlobalTransferLimit) public withPerm(ADMIN) { - require(isTransferLimitInPercentage, "Transfer limit already in tokens"); - isTransferLimitInPercentage = false; - changeGlobalLimitInTokens(_newGlobalTransferLimit); - emit TransferLimitChangedToTokens(); - } - /** - * @notice Changes the global transfer limit - * @param _newGlobalTransferLimitInTokens new transfer limit in tokens - * @dev This function can be used only when The manager is configured to use limits in tokens - */ - function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimitInTokens) public withPerm(ADMIN) { - require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); - require(_newGlobalTransferLimitInTokens > 0, "Transfer limit has to greater than zero"); - emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimitInTokens, globalTransferLimitInTokens); - globalTransferLimitInTokens = _newGlobalTransferLimitInTokens; - - } - - /** - * @notice Changes the global transfer limit - * @param _newGlobalTransferLimitInPercentage new transfer limit in percentage. - * Multiply the percentage by 10^16. Eg 22% will be 22*10^16 - * @dev This function can be used only when The manager is configured to use limits in percentage - */ - function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { - require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); - require(_newGlobalTransferLimitInPercentage > 0 && _newGlobalTransferLimitInPercentage <= 100 * 10 ** 16, "Limit not within [0,100]"); - emit GlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage, globalTransferLimitInPercentage); - globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; - - } - - /** - * @notice Adds an exempt wallet - * @param _wallet exempt wallet address - */ - function addExemptWallet(address _wallet) public withPerm(ADMIN) { - require(_wallet != address(0), "Wallet address cannot be a zero address"); - exemptWallets[_wallet] = true; - emit ExemptWalletAdded(_wallet); - } - - /** - * @notice Removes an exempt wallet - * @param _wallet exempt wallet address - */ - function removeExemptWallet(address _wallet) public withPerm(ADMIN) { - require(_wallet != address(0), "Wallet address cannot be a zero address"); - exemptWallets[_wallet] = false; - emit ExemptWalletRemoved(_wallet); - } - - /** - * @notice Adds an array of exempt wallet - * @param _wallets array of exempt wallet addresses - */ - function addExemptWalletMulti(address[] _wallets) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint256 i = 0; i < _wallets.length; i++) { - addExemptWallet(_wallets[i]); - } - } - - /** - * @notice Removes an array of exempt wallet - * @param _wallets array of exempt wallet addresses - */ - function removeExemptWalletMulti(address[] _wallets) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint256 i = 0; i < _wallets.length; i++) { - removeExemptWallet(_wallets[i]); - } - } - - /** - * @notice Sets transfer limit per wallet - * @param _wallet wallet address - * @param _transferLimit transfer limit for the wallet in tokens - * @dev the manager has to be configured to use limits in tokens - */ - function setTransferLimitInTokens(address _wallet, uint _transferLimit) public withPerm(ADMIN) { - require(_transferLimit > 0, "Transfer limit has to be greater than 0"); - require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); - specialTransferLimitsInTokens[_wallet] = _transferLimit; - emit TransferLimitInTokensSet(_wallet, _transferLimit); - } - - /** - * @notice Sets transfer limit for a wallet - * @param _wallet wallet address - * @param _transferLimitInPercentage transfer limit for the wallet in percentage. - * Multiply the percentage by 10^16. Eg 22% will be 22*10^16 - * @dev The manager has to be configured to use percentages - */ - function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { - require(isTransferLimitInPercentage, "Transfer limit not in percentage"); - require(_transferLimitInPercentage > 0 && _transferLimitInPercentage <= 100 * 10 ** 16, "Transfer limit not in required range"); - specialTransferLimitsInPercentages[_wallet] = _transferLimitInPercentage; - emit TransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); - } - - - /** - * @notice Removes transfer limit set in percentage for a wallet - * @param _wallet wallet address - */ - function removeTransferLimitInPercentage(address _wallet) public withPerm(ADMIN) { - require(specialTransferLimitsInPercentages[_wallet] > 0, "Wallet Address does not have a transfer limit"); - specialTransferLimitsInPercentages[_wallet] = 0; - emit TransferLimitInPercentageRemoved(_wallet); - } - - /** - * @notice Removes transfer limit set in tokens for a wallet - * @param _wallet wallet address - */ - function removeTransferLimitInTokens(address _wallet) public withPerm(ADMIN) { - require(specialTransferLimitsInTokens[_wallet] > 0, "Wallet Address does not have a transfer limit"); - specialTransferLimitsInTokens[_wallet] = 0; - emit TransferLimitInTokensRemoved(_wallet); - } - - /** - * @notice Sets transfer limits for an array of wallet - * @param _wallets array of wallet addresses - * @param _transferLimits array of transfer limits for each wallet in tokens - * @dev The manager has to be configured to use tokens as limit - */ - function setTransferLimitInTokensMulti(address[] _wallets, uint[] _transferLimits) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - require(_wallets.length == _transferLimits.length, "Wallets don't match to transfer limits"); - for (uint256 i = 0; i < _wallets.length; i++ ) { - setTransferLimitInTokens(_wallets[i], _transferLimits[i]); - } - } - - /** - * @notice Sets transfer limits for an array of wallet - * @param _wallets array of wallet addresses - * @param _transferLimitsInPercentage array of transfer limits for each wallet in percentages - * The percentage has to be multipled by 10 ** 16. Eg: 20% would be 20 * 10 ** 16 - * @dev The manager has to be configured to use percentage as limit - */ - function setTransferLimitInPercentageMulti(address[] _wallets, uint[] _transferLimitsInPercentage) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - require(_wallets.length == _transferLimitsInPercentage.length, "Wallets don't match to percentage limits"); - for (uint256 i = 0; i < _wallets.length; i++) { - setTransferLimitInPercentage(_wallets[i], _transferLimitsInPercentage[i]); - } - } - - /** - * @notice Removes transfer limits set in tokens for an array of wallet - * @param _wallets array of wallet addresses - */ - function removeTransferLimitInTokensMulti(address[] _wallets) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint i = 0; i < _wallets.length; i++) { - removeTransferLimitInTokens(_wallets[i]); - } - } - - /** - * @notice Removes transfer limits set in percentage for an array of wallet - * @param _wallets array of wallet addresses - */ - function removeTransferLimitInPercentageMulti(address[] _wallets) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint i = 0; i < _wallets.length; i++) { - removeTransferLimitInPercentage(_wallets[i]); - } - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(keccak256("configure(bool,uint256,bool)")); - } - - /** - * @notice Returns the permissions flag that are associated with SingleTradeVolumeRestrictionManager - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = ADMIN; - return allPermissions; - } -} diff --git a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol deleted file mode 100644 index e6d8ed2be..000000000 --- a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol +++ /dev/null @@ -1,79 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../ModuleFactory.sol"; -import "./SingleTradeVolumeRestrictionTM.sol"; -import "../../../libraries/Util.sol"; - -/** - * @title Factory for deploying SingleTradeVolumeRestrictionManager - */ -contract SingleTradeVolumeRestrictionTMFactory is ModuleFactory { - - - /** - * @notice Constructor - * @param _polyAddress Address of the polytoken - * @param _setupCost Setup cost of the module - * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module - */ - constructor(address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) - { - version = "1.0.0"; - name = "SingleTradeVolumeRestrictionTM"; - title = "Single Trade Volume Restriction Manager"; - description = "Imposes volume restriction on a single trade"; - compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); - compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); - } - - /** - * @notice Used to launch the Module with the help of factory - * @return address Contract address of the Module - */ - function deploy(bytes _data) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - SingleTradeVolumeRestrictionTM singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionTM(msg.sender, address(polyToken)); - - require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); - /*solium-disable-next-line security/no-low-level-calls*/ - require(address(singleTradeVolumeRestrictionManager).call(_data), "Unsuccessful call"); - /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, setupCost, now); - return address(singleTradeVolumeRestrictionManager); - } - - /** - * @notice Get the types of the Module factory - * @return uint8[] - */ - function getTypes() external view returns(uint8[]) { - uint8[] memory res = new uint8[](1); - res[0] = 2; - return res; - } - - /** - * @notice Get the Instructions that help to use the module - * @return string - */ - function getInstructions() external view returns(string) { - /*solium-disable-next-line max-len*/ - return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; - } - - /** - * @notice Get the tags related to the module factory - * @return bytes32[] - */ - function getTags() external view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](3); - availableTags[0] = "Single Trade"; - availableTags[1] = "Transfer"; - availableTags[2] = "Volume"; - return availableTags; - } - -} diff --git a/contracts/modules/Experimental/Wallet/IWallet.sol b/contracts/modules/Experimental/Wallet/IWallet.sol new file mode 100644 index 000000000..013bcc540 --- /dev/null +++ b/contracts/modules/Experimental/Wallet/IWallet.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.5.0; + +import "../../../Pausable.sol"; +import "../../Module.sol"; + +/** + * @title Interface to be implemented by all Wallet modules + * @dev abstract contract + */ +contract IWallet is Module, Pausable { + + function unpause() public onlyOwner { + super._unpause(); + } + + function pause() public onlyOwner { + super._pause(); + } +} diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol b/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol new file mode 100644 index 000000000..3dc85999d --- /dev/null +++ b/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol @@ -0,0 +1,565 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../../storage/modules/Wallet/VestingEscrowWalletStorage.sol"; +import "./IWallet.sol"; + +/** + * @title Wallet for core vesting escrow functionality + */ +contract VestingEscrowWallet is VestingEscrowWalletStorage, IWallet { + using SafeMath for uint256; + + bytes32 public constant ADMIN = "ADMIN"; + + // States used to represent the status of the schedule + enum State {CREATED, STARTED, COMPLETED} + + // Emit when new schedule is added + event AddSchedule( + address indexed _beneficiary, + bytes32 _templateName, + uint256 _startTime + ); + // Emit when schedule is modified + event ModifySchedule( + address indexed _beneficiary, + bytes32 _templateName, + uint256 _startTime + ); + // Emit when all schedules are revoked for user + event RevokeAllSchedules(address indexed _beneficiary); + // Emit when schedule is revoked + event RevokeSchedule(address indexed _beneficiary, bytes32 _templateName); + // Emit when tokes are deposited to wallet + event DepositTokens(uint256 _numberOfTokens, address _sender); + // Emit when all unassigned tokens are sent to treasury + event SendToTreasury(uint256 _numberOfTokens, address _sender); + // Emit when is sent tokes to user + event SendTokens(address indexed _beneficiary, uint256 _numberOfTokens); + // Emit when template is added + event AddTemplate(bytes32 _name, uint256 _numberOfTokens, uint256 _duration, uint256 _frequency); + // Emit when template is removed + event RemoveTemplate(bytes32 _name); + // Emit when the treasury wallet gets changed + event TreasuryWalletChanged(address _newWallet, address _oldWallet); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of the configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(keccak256("configure(address)")); + } + + /** + * @notice Used to initialize the treasury wallet address + * @param _treasuryWallet Address of the treasury wallet + */ + function configure(address _treasuryWallet) public onlyFactory { + require(_treasuryWallet != address(0), "Invalid address"); + treasuryWallet = _treasuryWallet; + } + + /** + * @notice Used to change the treasury wallet address + * @param _newTreasuryWallet Address of the treasury wallet + */ + function changeTreasuryWallet(address _newTreasuryWallet) public onlyOwner { + require(_newTreasuryWallet != address(0)); + emit TreasuryWalletChanged(_newTreasuryWallet, treasuryWallet); + treasuryWallet = _newTreasuryWallet; + } + + /** + * @notice Used to deposit tokens from treasury wallet to the vesting escrow wallet + * @param _numberOfTokens Number of tokens that should be deposited + */ + function depositTokens(uint256 _numberOfTokens) external withPerm(ADMIN) { + _depositTokens(_numberOfTokens); + } + + function _depositTokens(uint256 _numberOfTokens) internal { + require(_numberOfTokens > 0, "Should be > 0"); + require( + ISecurityToken(securityToken).transferFrom(msg.sender, address(this), _numberOfTokens), + "Failed transferFrom due to insufficent Allowance provided" + ); + unassignedTokens = unassignedTokens.add(_numberOfTokens); + emit DepositTokens(_numberOfTokens, msg.sender); + } + + /** + * @notice Sends unassigned tokens to the treasury wallet + * @param _amount Amount of tokens that should be send to the treasury wallet + */ + function sendToTreasury(uint256 _amount) external withPerm(ADMIN) { + require(_amount > 0, "Amount cannot be zero"); + require(_amount <= unassignedTokens, "Amount is greater than unassigned tokens"); + uint256 amount = unassignedTokens; + unassignedTokens = 0; + require(ISecurityToken(securityToken).transfer(treasuryWallet, amount), "Transfer failed"); + emit SendToTreasury(amount, msg.sender); + } + + /** + * @notice Pushes available tokens to the beneficiary's address + * @param _beneficiary Address of the beneficiary who will receive tokens + */ + function pushAvailableTokens(address _beneficiary) public withPerm(ADMIN) { + _sendTokens(_beneficiary); + } + + /** + * @notice Used to withdraw available tokens by beneficiary + */ + function pullAvailableTokens() external { + _sendTokens(msg.sender); + } + + /** + * @notice Adds template that can be used for creating schedule + * @param _name Name of the template will be created + * @param _numberOfTokens Number of tokens that should be assigned to schedule + * @param _duration Duration of the vesting schedule + * @param _frequency Frequency of the vesting schedule + */ + function addTemplate(bytes32 _name, uint256 _numberOfTokens, uint256 _duration, uint256 _frequency) external withPerm(ADMIN) { + _addTemplate(_name, _numberOfTokens, _duration, _frequency); + } + + function _addTemplate(bytes32 _name, uint256 _numberOfTokens, uint256 _duration, uint256 _frequency) internal { + require(_name != bytes32(0), "Invalid name"); + require(!_isTemplateExists(_name), "Already exists"); + _validateTemplate(_numberOfTokens, _duration, _frequency); + templateNames.push(_name); + templates[_name] = Template(_numberOfTokens, _duration, _frequency, templateNames.length - 1); + emit AddTemplate(_name, _numberOfTokens, _duration, _frequency); + } + + /** + * @notice Removes template with a given name + * @param _name Name of the template that will be removed + */ + function removeTemplate(bytes32 _name) external withPerm(ADMIN) { + require(_isTemplateExists(_name), "Template not found"); + require(templateToUsers[_name].length == 0, "Template is used"); + uint256 index = templates[_name].index; + if (index != templateNames.length - 1) { + templateNames[index] = templateNames[templateNames.length - 1]; + templates[templateNames[index]].index = index; + } + templateNames.length--; + // delete template data + delete templates[_name]; + emit RemoveTemplate(_name); + } + + /** + * @notice Returns count of the templates those can be used for creating schedule + * @return Count of the templates + */ + function getTemplateCount() external view returns(uint256) { + return templateNames.length; + } + + /** + * @notice Gets the list of the template names those can be used for creating schedule + * @return bytes32 Array of all template names were created + */ + function getAllTemplateNames() external view returns(bytes32[] memory) { + return templateNames; + } + + /** + * @notice Adds vesting schedules for each of the beneficiary's address + * @param _beneficiary Address of the beneficiary for whom it is scheduled + * @param _templateName Name of the template that will be created + * @param _numberOfTokens Total number of tokens for created schedule + * @param _duration Duration of the created vesting schedule + * @param _frequency Frequency of the created vesting schedule + * @param _startTime Start time of the created vesting schedule + */ + function addSchedule( + address _beneficiary, + bytes32 _templateName, + uint256 _numberOfTokens, + uint256 _duration, + uint256 _frequency, + uint256 _startTime + ) + external + withPerm(ADMIN) + { + _addSchedule(_beneficiary, _templateName, _numberOfTokens, _duration, _frequency, _startTime); + } + + function _addSchedule( + address _beneficiary, + bytes32 _templateName, + uint256 _numberOfTokens, + uint256 _duration, + uint256 _frequency, + uint256 _startTime + ) + internal + { + _addTemplate(_templateName, _numberOfTokens, _duration, _frequency); + _addScheduleFromTemplate(_beneficiary, _templateName, _startTime); + } + + /** + * @notice Adds vesting schedules from template for the beneficiary + * @param _beneficiary Address of the beneficiary for whom it is scheduled + * @param _templateName Name of the exists template + * @param _startTime Start time of the created vesting schedule + */ + function addScheduleFromTemplate(address _beneficiary, bytes32 _templateName, uint256 _startTime) external withPerm(ADMIN) { + _addScheduleFromTemplate(_beneficiary, _templateName, _startTime); + } + + function _addScheduleFromTemplate(address _beneficiary, bytes32 _templateName, uint256 _startTime) internal { + require(_beneficiary != address(0), "Invalid address"); + require(_isTemplateExists(_templateName), "Template not found"); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + require( + schedules[_beneficiary].length == 0 || + schedules[_beneficiary][index].templateName != _templateName, + "Already added" + ); + require(_startTime >= now, "Date in the past"); + uint256 numberOfTokens = templates[_templateName].numberOfTokens; + if (numberOfTokens > unassignedTokens) { + _depositTokens(numberOfTokens.sub(unassignedTokens)); + } + unassignedTokens = unassignedTokens.sub(numberOfTokens); + if (!beneficiaryAdded[_beneficiary]) { + beneficiaries.push(_beneficiary); + beneficiaryAdded[_beneficiary] = true; + } + schedules[_beneficiary].push(Schedule(_templateName, 0, _startTime)); + userToTemplates[_beneficiary].push(_templateName); + userToTemplateIndex[_beneficiary][_templateName] = schedules[_beneficiary].length - 1; + templateToUsers[_templateName].push(_beneficiary); + templateToUserIndex[_templateName][_beneficiary] = templateToUsers[_templateName].length - 1; + emit AddSchedule(_beneficiary, _templateName, _startTime); + } + + /** + * @notice Modifies vesting schedules for each of the beneficiary + * @param _beneficiary Address of the beneficiary for whom it is modified + * @param _templateName Name of the template was used for schedule creation + * @param _startTime Start time of the created vesting schedule + */ + function modifySchedule(address _beneficiary, bytes32 _templateName, uint256 _startTime) public withPerm(ADMIN) { + _modifySchedule(_beneficiary, _templateName, _startTime); + } + + function _modifySchedule(address _beneficiary, bytes32 _templateName, uint256 _startTime) internal { + _checkSchedule(_beneficiary, _templateName); + require(_startTime > now, "Date in the past"); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + Schedule storage schedule = schedules[_beneficiary][index]; + /*solium-disable-next-line security/no-block-members*/ + require(now < schedule.startTime, "Schedule started"); + schedule.startTime = _startTime; + emit ModifySchedule(_beneficiary, _templateName, _startTime); + } + + /** + * @notice Revokes vesting schedule with given template name for given beneficiary + * @param _beneficiary Address of the beneficiary for whom it is revoked + * @param _templateName Name of the template was used for schedule creation + */ + function revokeSchedule(address _beneficiary, bytes32 _templateName) external withPerm(ADMIN) { + _checkSchedule(_beneficiary, _templateName); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + _sendTokensPerSchedule(_beneficiary, index); + uint256 releasedTokens = _getReleasedTokens(_beneficiary, index); + unassignedTokens = unassignedTokens.add(templates[_templateName].numberOfTokens.sub(releasedTokens)); + _deleteUserToTemplates(_beneficiary, _templateName); + _deleteTemplateToUsers(_beneficiary, _templateName); + emit RevokeSchedule(_beneficiary, _templateName); + } + + function _deleteUserToTemplates(address _beneficiary, bytes32 _templateName) internal { + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + Schedule[] storage userSchedules = schedules[_beneficiary]; + if (index != userSchedules.length - 1) { + userSchedules[index] = userSchedules[userSchedules.length - 1]; + userToTemplates[_beneficiary][index] = userToTemplates[_beneficiary][userToTemplates[_beneficiary].length - 1]; + userToTemplateIndex[_beneficiary][userSchedules[index].templateName] = index; + } + userSchedules.length--; + userToTemplates[_beneficiary].length--; + delete userToTemplateIndex[_beneficiary][_templateName]; + } + + function _deleteTemplateToUsers(address _beneficiary, bytes32 _templateName) internal { + uint256 templateIndex = templateToUserIndex[_templateName][_beneficiary]; + if (templateIndex != templateToUsers[_templateName].length - 1) { + templateToUsers[_templateName][templateIndex] = templateToUsers[_templateName][templateToUsers[_templateName].length - 1]; + templateToUserIndex[_templateName][templateToUsers[_templateName][templateIndex]] = templateIndex; + } + templateToUsers[_templateName].length--; + delete templateToUserIndex[_templateName][_beneficiary]; + } + + /** + * @notice Revokes all vesting schedules for given beneficiary's address + * @param _beneficiary Address of the beneficiary for whom all schedules will be revoked + */ + function revokeAllSchedules(address _beneficiary) public withPerm(ADMIN) { + _revokeAllSchedules(_beneficiary); + } + + function _revokeAllSchedules(address _beneficiary) internal { + require(_beneficiary != address(0), "Invalid address"); + _sendTokens(_beneficiary); + Schedule[] storage userSchedules = schedules[_beneficiary]; + for (uint256 i = 0; i < userSchedules.length; i++) { + uint256 releasedTokens = _getReleasedTokens(_beneficiary, i); + Template memory template = templates[userSchedules[i].templateName]; + unassignedTokens = unassignedTokens.add(template.numberOfTokens.sub(releasedTokens)); + delete userToTemplateIndex[_beneficiary][userSchedules[i].templateName]; + _deleteTemplateToUsers(_beneficiary, userSchedules[i].templateName); + } + delete schedules[_beneficiary]; + delete userToTemplates[_beneficiary]; + emit RevokeAllSchedules(_beneficiary); + } + + /** + * @notice Returns beneficiary's schedule created using template name + * @param _beneficiary Address of the beneficiary who will receive tokens + * @param _templateName Name of the template was used for schedule creation + * @return beneficiary's schedule data (numberOfTokens, duration, frequency, startTime, claimedTokens, State) + */ + function getSchedule(address _beneficiary, bytes32 _templateName) external view returns(uint256, uint256, uint256, uint256, uint256, State) { + _checkSchedule(_beneficiary, _templateName); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + Schedule memory schedule = schedules[_beneficiary][index]; + return ( + templates[schedule.templateName].numberOfTokens, + templates[schedule.templateName].duration, + templates[schedule.templateName].frequency, + schedule.startTime, + schedule.claimedTokens, + _getScheduleState(_beneficiary, _templateName) + ); + } + + function _getScheduleState(address _beneficiary, bytes32 _templateName) internal view returns(State) { + _checkSchedule(_beneficiary, _templateName); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + Schedule memory schedule = schedules[_beneficiary][index]; + if (now < schedule.startTime) { + return State.CREATED; + } else if (now > schedule.startTime && now < schedule.startTime.add(templates[_templateName].duration)) { + return State.STARTED; + } else { + return State.COMPLETED; + } + } + + /** + * @notice Returns list of the template names for given beneficiary's address + * @param _beneficiary Address of the beneficiary + * @return List of the template names that were used for schedule creation + */ + function getTemplateNames(address _beneficiary) external view returns(bytes32[] memory) { + require(_beneficiary != address(0), "Invalid address"); + return userToTemplates[_beneficiary]; + } + + /** + * @notice Returns count of the schedules were created for given beneficiary + * @param _beneficiary Address of the beneficiary + * @return Count of beneficiary's schedules + */ + function getScheduleCount(address _beneficiary) external view returns(uint256) { + require(_beneficiary != address(0), "Invalid address"); + return schedules[_beneficiary].length; + } + + function _getAvailableTokens(address _beneficiary, uint256 _index) internal view returns(uint256) { + Schedule memory schedule = schedules[_beneficiary][_index]; + uint256 releasedTokens = _getReleasedTokens(_beneficiary, _index); + return releasedTokens.sub(schedule.claimedTokens); + } + + function _getReleasedTokens(address _beneficiary, uint256 _index) internal view returns(uint256) { + Schedule memory schedule = schedules[_beneficiary][_index]; + Template memory template = templates[schedule.templateName]; + /*solium-disable-next-line security/no-block-members*/ + if (now > schedule.startTime) { + uint256 periodCount = template.duration.div(template.frequency); + /*solium-disable-next-line security/no-block-members*/ + uint256 periodNumber = (now.sub(schedule.startTime)).div(template.frequency); + if (periodNumber > periodCount) { + periodNumber = periodCount; + } + return template.numberOfTokens.mul(periodNumber).div(periodCount); + } else { + return 0; + } + } + + /** + * @notice Used to bulk send available tokens for each of the beneficiaries + * @param _fromIndex Start index of array of beneficiary's addresses + * @param _toIndex End index of array of beneficiary's addresses + */ + function pushAvailableTokensMulti(uint256 _fromIndex, uint256 _toIndex) external withPerm(ADMIN) { + require(_toIndex <= beneficiaries.length - 1, "Array out of bound"); + for (uint256 i = _fromIndex; i <= _toIndex; i++) { + if (schedules[beneficiaries[i]].length !=0) + pushAvailableTokens(beneficiaries[i]); + } + } + + /** + * @notice Used to bulk add vesting schedules for each of beneficiary + * @param _beneficiaries Array of the beneficiary's addresses + * @param _templateNames Array of the template names + * @param _numberOfTokens Array of number of tokens should be assigned to schedules + * @param _durations Array of the vesting duration + * @param _frequencies Array of the vesting frequency + * @param _startTimes Array of the vesting start time + */ + function addScheduleMulti( + address[] memory _beneficiaries, + bytes32[] memory _templateNames, + uint256[] memory _numberOfTokens, + uint256[] memory _durations, + uint256[] memory _frequencies, + uint256[] memory _startTimes + ) + public + withPerm(ADMIN) + { + require( + _beneficiaries.length == _templateNames.length && /*solium-disable-line operator-whitespace*/ + _beneficiaries.length == _numberOfTokens.length && /*solium-disable-line operator-whitespace*/ + _beneficiaries.length == _durations.length && /*solium-disable-line operator-whitespace*/ + _beneficiaries.length == _frequencies.length && /*solium-disable-line operator-whitespace*/ + _beneficiaries.length == _startTimes.length, + "Arrays sizes mismatch" + ); + for (uint256 i = 0; i < _beneficiaries.length; i++) { + _addSchedule(_beneficiaries[i], _templateNames[i], _numberOfTokens[i], _durations[i], _frequencies[i], _startTimes[i]); + } + } + + /** + * @notice Used to bulk add vesting schedules from template for each of the beneficiary + * @param _beneficiaries Array of beneficiary's addresses + * @param _templateNames Array of the template names were used for schedule creation + * @param _startTimes Array of the vesting start time + */ + function addScheduleFromTemplateMulti( + address[] calldata _beneficiaries, + bytes32[] calldata _templateNames, + uint256[] calldata _startTimes + ) + external + withPerm(ADMIN) + { + require(_beneficiaries.length == _templateNames.length && _beneficiaries.length == _startTimes.length, "Arrays sizes mismatch"); + for (uint256 i = 0; i < _beneficiaries.length; i++) { + _addScheduleFromTemplate(_beneficiaries[i], _templateNames[i], _startTimes[i]); + } + } + + /** + * @notice Used to bulk revoke vesting schedules for each of the beneficiaries + * @param _beneficiaries Array of the beneficiary's addresses + */ + function revokeSchedulesMulti(address[] calldata _beneficiaries) external withPerm(ADMIN) { + for (uint256 i = 0; i < _beneficiaries.length; i++) { + _revokeAllSchedules(_beneficiaries[i]); + } + } + + /** + * @notice Used to bulk modify vesting schedules for each of the beneficiaries + * @param _beneficiaries Array of the beneficiary's addresses + * @param _templateNames Array of the template names + * @param _startTimes Array of the vesting start time + */ + function modifyScheduleMulti( + address[] memory _beneficiaries, + bytes32[] memory _templateNames, + uint256[] memory _startTimes + ) + public + withPerm(ADMIN) + { + require( + _beneficiaries.length == _templateNames.length && /*solium-disable-line operator-whitespace*/ + _beneficiaries.length == _startTimes.length, + "Arrays sizes mismatch" + ); + for (uint256 i = 0; i < _beneficiaries.length; i++) { + _modifySchedule(_beneficiaries[i], _templateNames[i], _startTimes[i]); + } + } + + function _checkSchedule(address _beneficiary, bytes32 _templateName) internal view { + require(_beneficiary != address(0), "Invalid address"); + uint256 index = userToTemplateIndex[_beneficiary][_templateName]; + require( + index < schedules[_beneficiary].length && + schedules[_beneficiary][index].templateName == _templateName, + "Schedule not found" + ); + } + + function _isTemplateExists(bytes32 _name) internal view returns(bool) { + return templates[_name].numberOfTokens > 0; + } + + function _validateTemplate(uint256 _numberOfTokens, uint256 _duration, uint256 _frequency) internal view { + require(_numberOfTokens > 0, "Zero amount"); + require(_duration % _frequency == 0, "Invalid frequency"); + uint256 periodCount = _duration.div(_frequency); + require(_numberOfTokens % periodCount == 0); + uint256 amountPerPeriod = _numberOfTokens.div(periodCount); + require(amountPerPeriod % ISecurityToken(securityToken).granularity() == 0, "Invalid granularity"); + } + + function _sendTokens(address _beneficiary) internal { + for (uint256 i = 0; i < schedules[_beneficiary].length; i++) { + _sendTokensPerSchedule(_beneficiary, i); + } + } + + function _sendTokensPerSchedule(address _beneficiary, uint256 _index) internal { + uint256 amount = _getAvailableTokens(_beneficiary, _index); + if (amount > 0) { + schedules[_beneficiary][_index].claimedTokens = schedules[_beneficiary][_index].claimedTokens.add(amount); + require(ISecurityToken(securityToken).transfer(_beneficiary, amount), "Transfer failed"); + emit SendTokens(_beneficiary, amount); + } + } + + /** + * @notice Return the permissions flag that are associated with VestingEscrowWallet + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } + +} diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol b/contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol new file mode 100644 index 000000000..da0be5eb5 --- /dev/null +++ b/contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.5.0; + +import "../../../proxy/VestingEscrowWalletProxy.sol"; +import "../../../interfaces/IBoot.sol"; +import "../../ModuleFactory.sol"; +import "../../../libraries/Util.sol"; + +/** + * @title Factory for deploying VestingEscrowWallet module + */ +contract VestingEscrowWalletFactory is ModuleFactory { + + address public logicContract; + /** + * @notice Constructor + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _logicContract, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + require(_logicContract != address(0), "Invalid address"); + version = "1.0.0"; + name = "VestingEscrowWallet"; + title = "Vesting Escrow Wallet"; + description = "Manage vesting schedules to employees / affiliates"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; + } + + /** + * @notice Used to launch the Module with the help of factory + * _data Data used for the intialization of the module factory variables + * @return address Contract address of the Module + */ + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address vestingEscrowWallet = address(new VestingEscrowWalletProxy(msg.sender, address(polyToken), logicContract)); + //Checks that _data is valid (not calling anything it shouldn't) + require(Util.getSig(_data) == IBoot(vestingEscrowWallet).getInitFunction(), "Invalid data"); + bool success; + /*solium-disable-next-line security/no-low-level-calls*/ + (success, ) = vestingEscrowWallet.call(_data); + require(success, "Unsuccessfull call"); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(vestingEscrowWallet, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return vestingEscrowWallet; + } + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](1); + res[0] = 6; + return res; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string memory) { + /*solium-disable-next-line max-len*/ + return "Issuer can deposit tokens to the contract and create the vesting schedule for the given address (Affiliate/Employee). These address can withdraw tokens according to there vesting schedule."; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() external view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Vested"; + availableTags[1] = "Escrow Wallet"; + return availableTags; + } +} diff --git a/contracts/modules/Module.sol b/contracts/modules/Module.sol index aa3e6d865..b0d233201 100644 --- a/contracts/modules/Module.sol +++ b/contracts/modules/Module.sol @@ -1,59 +1,54 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../interfaces/IModule.sol"; +import "../interfaces/IDataStore.sol"; import "../interfaces/ISecurityToken.sol"; -import "../interfaces/IERC20.sol"; +import "../interfaces/ICheckPermission.sol"; +import "../storage/modules/ModuleStorage.sol"; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; /** * @title Interface that any module contract should implement * @notice Contract is abstract */ -contract Module is IModule { - - address public factory; - - address public securityToken; - - bytes32 public constant FEE_ADMIN = "FEE_ADMIN"; - - IERC20 public polyToken; - +contract Module is IModule, ModuleStorage { /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public { - securityToken = _securityToken; - factory = msg.sender; - polyToken = IERC20(_polyAddress); + constructor (address _securityToken, address _polyAddress) public + ModuleStorage(_securityToken, _polyAddress) + { } //Allows owner, factory or permissioned delegate modifier withPerm(bytes32 _perm) { bool isOwner = msg.sender == Ownable(securityToken).owner(); bool isFactory = msg.sender == factory; - require(isOwner||isFactory||ISecurityToken(securityToken).checkPermission(msg.sender, address(this), _perm), "Permission check failed"); + require( + isOwner || isFactory || ICheckPermission(securityToken).checkPermission(msg.sender, address(this), _perm), + "Permission check failed" + ); _; } - modifier onlyOwner { + modifier onlyOwner() { require(msg.sender == Ownable(securityToken).owner(), "Sender is not owner"); _; } - modifier onlyFactory { + modifier onlyFactory() { require(msg.sender == factory, "Sender is not factory"); _; } - modifier onlyFactoryOwner { + modifier onlyFactoryOwner() { require(msg.sender == Ownable(factory).owner(), "Sender is not factory owner"); _; } - modifier onlyFactoryOrOwner { + modifier onlyFactoryOrOwner() { require((msg.sender == Ownable(securityToken).owner()) || (msg.sender == factory), "Sender is not factory or owner"); _; } @@ -65,4 +60,8 @@ contract Module is IModule { require(polyToken.transferFrom(securityToken, Ownable(factory).owner(), _amount), "Unable to take fee"); return true; } + + function getDataStore() public view returns(address) { + return ISecurityToken(securityToken).dataStore(); + } } diff --git a/contracts/modules/ModuleFactory.sol b/contracts/modules/ModuleFactory.sol index aca262621..15e9f6cd9 100644 --- a/contracts/modules/ModuleFactory.sol +++ b/contracts/modules/ModuleFactory.sol @@ -1,89 +1,69 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "../interfaces/IERC20.sol"; +import "../RegistryUpdater.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IModuleFactory.sol"; +import "../interfaces/IOracle.sol"; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; import "../libraries/VersionUtils.sol"; +import "../interfaces/IPolymathRegistry.sol"; +import "../libraries/DecimalMath.sol"; /** * @title Interface that any module factory contract should implement * @notice Contract is abstract */ contract ModuleFactory is IModuleFactory, Ownable { - - IERC20 public polyToken; - uint256 public usageCost; - uint256 public monthlySubscriptionCost; - + // Fee to create underlying module in USD uint256 public setupCost; + uint256 public usageCost; + address public polymathRegistry; string public description; string public version; bytes32 public name; string public title; + string constant POLY_ORACLE = "StablePolyUsdOracle"; + // @notice Allow only two variables to be stored - // 1. lowerBound + // 1. lowerBound // 2. upperBound - // @dev (0.0.0 will act as the wildcard) + // @dev (0.0.0 will act as the wildcard) // @dev uint24 consists packed value of uint8 _major, uint8 _minor, uint8 _patch mapping(string => uint24) compatibleSTVersionRange; - event ChangeFactorySetupFee(uint256 _oldSetupCost, uint256 _newSetupCost, address _moduleFactory); - event ChangeFactoryUsageFee(uint256 _oldUsageCost, uint256 _newUsageCost, address _moduleFactory); - event ChangeFactorySubscriptionFee(uint256 _oldSubscriptionCost, uint256 _newMonthlySubscriptionCost, address _moduleFactory); - event GenerateModuleFromFactory( - address _module, - bytes32 indexed _moduleName, - address indexed _moduleFactory, - address _creator, - uint256 _timestamp - ); - event ChangeSTVersionBound(string _boundType, uint8 _major, uint8 _minor, uint8 _patch); - /** * @notice Constructor - * @param _polyAddress Address of the polytoken */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public { - polyToken = IERC20(_polyAddress); + constructor(uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public { setupCost = _setupCost; usageCost = _usageCost; - monthlySubscriptionCost = _subscriptionCost; + polymathRegistry = _polymathRegistry; } /** * @notice Used to change the fee of the setup cost - * @param _newSetupCost new setup cost + * @param _newSetupCost new setup cost in USD */ - function changeFactorySetupFee(uint256 _newSetupCost) public onlyOwner { - emit ChangeFactorySetupFee(setupCost, _newSetupCost, address(this)); + function changeSetupCost(uint256 _newSetupCost) public onlyOwner { + emit ChangeSetupCost(setupCost, _newSetupCost); setupCost = _newSetupCost; } /** * @notice Used to change the fee of the usage cost - * @param _newUsageCost new usage cost + * @param _newUsageCost new usage cost in USD */ - function changeFactoryUsageFee(uint256 _newUsageCost) public onlyOwner { - emit ChangeFactoryUsageFee(usageCost, _newUsageCost, address(this)); + function changeUsageCost(uint256 _newUsageCost) public onlyOwner { + emit ChangeUsageCost(usageCost, _newUsageCost); usageCost = _newUsageCost; } - /** - * @notice Used to change the fee of the subscription cost - * @param _newSubscriptionCost new subscription cost - */ - function changeFactorySubscriptionFee(uint256 _newSubscriptionCost) public onlyOwner { - emit ChangeFactorySubscriptionFee(monthlySubscriptionCost, _newSubscriptionCost, address(this)); - monthlySubscriptionCost = _newSubscriptionCost; - - } - /** * @notice Updates the title of the ModuleFactory * @param _newTitle New Title that will replace the old one. */ - function changeTitle(string _newTitle) public onlyOwner { + function changeTitle(string memory _newTitle) public onlyOwner { require(bytes(_newTitle).length > 0, "Invalid title"); title = _newTitle; } @@ -92,7 +72,7 @@ contract ModuleFactory is IModuleFactory, Ownable { * @notice Updates the description of the ModuleFactory * @param _newDesc New description that will replace the old one. */ - function changeDescription(string _newDesc) public onlyOwner { + function changeDescription(string memory _newDesc) public onlyOwner { require(bytes(_newDesc).length > 0, "Invalid description"); description = _newDesc; } @@ -102,7 +82,7 @@ contract ModuleFactory is IModuleFactory, Ownable { * @param _newName New name that will replace the old one. */ function changeName(bytes32 _newName) public onlyOwner { - require(_newName != bytes32(0),"Invalid name"); + require(_newName != bytes32(0), "Invalid name"); name = _newName; } @@ -110,7 +90,7 @@ contract ModuleFactory is IModuleFactory, Ownable { * @notice Updates the version of the ModuleFactory * @param _newVersion New name that will replace the old one. */ - function changeVersion(string _newVersion) public onlyOwner { + function changeVersion(string memory _newVersion) public onlyOwner { require(bytes(_newVersion).length > 0, "Invalid version"); version = _newVersion; } @@ -120,14 +100,15 @@ contract ModuleFactory is IModuleFactory, Ownable { * @param _boundType Type of bound * @param _newVersion new version array */ - function changeSTVersionBounds(string _boundType, uint8[] _newVersion) external onlyOwner { + function changeSTVersionBounds(string calldata _boundType, uint8[] calldata _newVersion) external onlyOwner { require( - keccak256(abi.encodePacked(_boundType)) == keccak256(abi.encodePacked("lowerBound")) || - keccak256(abi.encodePacked(_boundType)) == keccak256(abi.encodePacked("upperBound")), + keccak256(abi.encodePacked(_boundType)) == keccak256(abi.encodePacked("lowerBound")) || keccak256( + abi.encodePacked(_boundType) + ) == keccak256(abi.encodePacked("upperBound")), "Must be a valid bound type" ); require(_newVersion.length == 3); - if (compatibleSTVersionRange[_boundType] != uint24(0)) { + if (compatibleSTVersionRange[_boundType] != uint24(0)) { uint8[] memory _currentVersion = VersionUtils.unpack(compatibleSTVersionRange[_boundType]); require(VersionUtils.isValidVersion(_currentVersion, _newVersion), "Failed because of in-valid version"); } @@ -139,7 +120,7 @@ contract ModuleFactory is IModuleFactory, Ownable { * @notice Used to get the lower bound * @return lower bound */ - function getLowerSTVersionBounds() external view returns(uint8[]) { + function getLowerSTVersionBounds() external view returns(uint8[] memory) { return VersionUtils.unpack(compatibleSTVersionRange["lowerBound"]); } @@ -147,22 +128,43 @@ contract ModuleFactory is IModuleFactory, Ownable { * @notice Used to get the upper bound * @return upper bound */ - function getUpperSTVersionBounds() external view returns(uint8[]) { + function getUpperSTVersionBounds() external view returns(uint8[] memory) { return VersionUtils.unpack(compatibleSTVersionRange["upperBound"]); } /** * @notice Get the setup cost of the module */ - function getSetupCost() external view returns (uint256) { + function getSetupCost() public view returns (uint256) { return setupCost; } - /** - * @notice Get the name of the Module - */ + /** + * @notice Get the setup cost of the module + */ + function getSetupCostInPoly() public returns (uint256) { + uint256 polyRate = IOracle(IPolymathRegistry(polymathRegistry).getAddress(POLY_ORACLE)).getPrice(); + return DecimalMath.div(setupCost, polyRate); + } + + /** + * @notice Get the name of the Module + */ function getName() public view returns(bytes32) { return name; } + + /** + * @notice Calculates fee in POLY + */ + function _takeFee() internal returns(address) { + uint256 setupCostInPoly = getSetupCostInPoly(); + address polyToken = IPolymathRegistry(polymathRegistry).getAddress("PolyToken"); + if (setupCostInPoly > 0) { + require(IERC20(polyToken).transferFrom(msg.sender, owner(), setupCostInPoly), "Insufficient allowance for module fee"); + } + return polyToken; + } + } diff --git a/contracts/modules/PermissionManager/GeneralPermissionManager.sol b/contracts/modules/PermissionManager/GeneralPermissionManager.sol index f85abf675..7a37476b3 100644 --- a/contracts/modules/PermissionManager/GeneralPermissionManager.sol +++ b/contracts/modules/PermissionManager/GeneralPermissionManager.sol @@ -1,41 +1,30 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./IPermissionManager.sol"; import "../Module.sol"; +import "../../storage/modules/PermissionManager/GeneralPermissionManagerStorage.sol"; +import "../../interfaces/ISecurityToken.sol"; /** * @title Permission Manager module for core permissioning functionality */ -contract GeneralPermissionManager is IPermissionManager, Module { - - // Mapping used to hold the permissions on the modules provided to delegate, module add => delegate add => permission bytes32 => bool - mapping (address => mapping (address => mapping (bytes32 => bool))) public perms; - // Mapping hold the delagate details - mapping (address => bytes32) public delegateDetails; - // Array to track all delegates - address[] public allDelegates; - - - // Permission flag - bytes32 public constant CHANGE_PERMISSION = "CHANGE_PERMISSION"; +contract GeneralPermissionManager is GeneralPermissionManagerStorage, IPermissionManager, Module { /// Event emitted after any permission get changed for the delegate - event ChangePermission(address indexed _delegate, address _module, bytes32 _perm, bool _valid, uint256 _timestamp); + event ChangePermission(address indexed _delegate, address _module, bytes32 _perm, bool _valid); /// Used to notify when delegate is added in permission manager contract - event AddDelegate(address indexed _delegate, bytes32 _details, uint256 _timestamp); - + event AddDelegate(address indexed _delegate, bytes32 _details); /// @notice constructor - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** * @notice Init function i.e generalise function to maintain the structure of the module contract * @return bytes4 */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(0); } @@ -49,8 +38,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { function checkPermission(address _delegate, address _module, bytes32 _perm) external view returns(bool) { if (delegateDetails[_delegate] != bytes32(0)) { return perms[_module][_delegate][_perm]; - } else - return false; + } else return false; } /** @@ -65,7 +53,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { delegateDetails[_delegate] = _details; allDelegates.push(_delegate); /*solium-disable-next-line security/no-block-members*/ - emit AddDelegate(_delegate, _details, now); + emit AddDelegate(_delegate, _details); } /** @@ -93,8 +81,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { if (delegateDetails[_potentialDelegate] != bytes32(0)) { return true; - } else - return false; + } else return false; } /** @@ -105,15 +92,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { * @param _valid Bool flag use to switch on/off the permission * @return bool */ - function changePermission( - address _delegate, - address _module, - bytes32 _perm, - bool _valid - ) - public - withPerm(CHANGE_PERMISSION) - { + function changePermission(address _delegate, address _module, bytes32 _perm, bool _valid) public withPerm(CHANGE_PERMISSION) { require(_delegate != address(0), "invalid address"); _changePermission(_delegate, _module, _perm, _valid); } @@ -128,18 +107,18 @@ contract GeneralPermissionManager is IPermissionManager, Module { */ function changePermissionMulti( address _delegate, - address[] _modules, - bytes32[] _perms, - bool[] _valids + address[] calldata _modules, + bytes32[] calldata _perms, + bool[] calldata _valids ) - external - withPerm(CHANGE_PERMISSION) + external + withPerm(CHANGE_PERMISSION) { require(_delegate != address(0), "invalid address"); require(_modules.length > 0, "0 length is not allowed"); require(_modules.length == _perms.length, "Array length mismatch"); require(_valids.length == _perms.length, "Array length mismatch"); - for(uint256 i = 0; i < _perms.length; i++) { + for (uint256 i = 0; i < _perms.length; i++) { _changePermission(_delegate, _modules[i], _perms[i], _valids[i]); } } @@ -150,7 +129,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { * @param _perm Permission flag * @return address[] */ - function getAllDelegatesWithPerm(address _module, bytes32 _perm) external view returns(address[]) { + function getAllDelegatesWithPerm(address _module, bytes32 _perm) external view returns(address[] memory) { uint256 counter = 0; uint256 i = 0; for (i = 0; i < allDelegates.length; i++) { @@ -161,7 +140,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { address[] memory allDelegatesWithPerm = new address[](counter); counter = 0; for (i = 0; i < allDelegates.length; i++) { - if (perms[_module][allDelegates[i]][_perm]){ + if (perms[_module][allDelegates[i]][_perm]) { allDelegatesWithPerm[counter] = allDelegates[i]; counter++; } @@ -177,18 +156,21 @@ contract GeneralPermissionManager is IPermissionManager, Module { * @return address[] the address array of Modules this delegate has permission * @return bytes32[] the permission array of the corresponding Modules */ - function getAllModulesAndPermsFromTypes(address _delegate, uint8[] _types) external view returns(address[], bytes32[]) { + function getAllModulesAndPermsFromTypes(address _delegate, uint8[] calldata _types) external view returns( + address[] memory, + bytes32[] memory + ) { uint256 counter = 0; // loop through _types and get their modules from securityToken->getModulesByType for (uint256 i = 0; i < _types.length; i++) { address[] memory _currentTypeModules = ISecurityToken(securityToken).getModulesByType(_types[i]); // loop through each modules to get their perms from IModule->getPermissions - for (uint256 j = 0; j < _currentTypeModules.length; j++){ + for (uint256 j = 0; j < _currentTypeModules.length; j++) { bytes32[] memory _allModulePerms = IModule(_currentTypeModules[j]).getPermissions(); // loop through each perm, if it is true, push results into arrays for (uint256 k = 0; k < _allModulePerms.length; k++) { if (perms[_currentTypeModules[j]][_delegate][_allModulePerms[k]]) { - counter ++; + counter++; } } } @@ -198,11 +180,11 @@ contract GeneralPermissionManager is IPermissionManager, Module { bytes32[] memory _allPerms = new bytes32[](counter); counter = 0; - for (i = 0; i < _types.length; i++){ - _currentTypeModules = ISecurityToken(securityToken).getModulesByType(_types[i]); - for (j = 0; j < _currentTypeModules.length; j++) { - _allModulePerms = IModule(_currentTypeModules[j]).getPermissions(); - for (k = 0; k < _allModulePerms.length; k++) { + for (uint256 i = 0; i < _types.length; i++) { + address[] memory _currentTypeModules = ISecurityToken(securityToken).getModulesByType(_types[i]); + for (uint256 j = 0; j < _currentTypeModules.length; j++) { + bytes32[] memory _allModulePerms = IModule(_currentTypeModules[j]).getPermissions(); + for (uint256 k = 0; k < _allModulePerms.length; k++) { if (perms[_currentTypeModules[j]][_delegate][_allModulePerms[k]]) { _allModules[counter] = _currentTypeModules[j]; _allPerms[counter] = _allModulePerms[k]; @@ -212,7 +194,7 @@ contract GeneralPermissionManager is IPermissionManager, Module { } } - return(_allModules, _allPerms); + return (_allModules, _allPerms); } /** @@ -223,32 +205,25 @@ contract GeneralPermissionManager is IPermissionManager, Module { * @param _valid Bool flag use to switch on/off the permission * @return bool */ - function _changePermission( - address _delegate, - address _module, - bytes32 _perm, - bool _valid - ) - internal - { + function _changePermission(address _delegate, address _module, bytes32 _perm, bool _valid) internal { perms[_module][_delegate][_perm] = _valid; /*solium-disable-next-line security/no-block-members*/ - emit ChangePermission(_delegate, _module, _perm, _valid, now); + emit ChangePermission(_delegate, _module, _perm, _valid); } /** * @notice Used to get all delegates * @return address[] */ - function getAllDelegates() external view returns(address[]) { + function getAllDelegates() external view returns(address[] memory) { return allDelegates; } - + /** * @notice Returns the Permission flag related the `this` contract * @return Array of permission flags */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = CHANGE_PERMISSION; return allPermissions; diff --git a/contracts/modules/PermissionManager/GeneralPermissionManagerFactory.sol b/contracts/modules/PermissionManager/GeneralPermissionManagerFactory.sol index bfdc73801..646c739c8 100644 --- a/contracts/modules/PermissionManager/GeneralPermissionManagerFactory.sol +++ b/contracts/modules/PermissionManager/GeneralPermissionManagerFactory.sol @@ -1,45 +1,62 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./GeneralPermissionManager.sol"; import "../ModuleFactory.sol"; +import "../../proxy/GeneralPermissionManagerProxy.sol"; /** * @title Factory for deploying GeneralPermissionManager module */ contract GeneralPermissionManagerFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { + require(_logicContract != address(0), "Invalid address"); version = "1.0.0"; name = "GeneralPermissionManager"; title = "General Permission Manager"; description = "Manage permissions within the Security Token and attached modules"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** * @notice Used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom due to insufficent Allowance provided"); - address permissionManager = new GeneralPermissionManager(msg.sender, address(polyToken)); + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + address permissionManager = address(new GeneralPermissionManagerProxy(msg.sender, polyToken, logicContract)); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(permissionManager), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(permissionManager, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return permissionManager; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 1; return res; @@ -48,7 +65,7 @@ contract GeneralPermissionManagerFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { /*solium-disable-next-line max-len*/ return "Add and remove permissions for the SecurityToken and associated modules. Permission types should be encoded as bytes32 values and attached using withPerm modifier to relevant functions. No initFunction required."; } @@ -56,7 +73,7 @@ contract GeneralPermissionManagerFactory is ModuleFactory { /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](0); return availableTags; } diff --git a/contracts/modules/PermissionManager/IPermissionManager.sol b/contracts/modules/PermissionManager/IPermissionManager.sol index d9b78944d..f1fdc69b5 100644 --- a/contracts/modules/PermissionManager/IPermissionManager.sol +++ b/contracts/modules/PermissionManager/IPermissionManager.sol @@ -1,10 +1,9 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Interface to be implemented by all permission manager modules */ interface IPermissionManager { - /** * @notice Used to check the permission on delegate corresponds to module contract address * @param _delegate Ethereum address of the delegate @@ -42,13 +41,7 @@ interface IPermissionManager { * @param _valid Bool flag use to switch on/off the permission * @return bool */ - function changePermission( - address _delegate, - address _module, - bytes32 _perm, - bool _valid - ) - external; + function changePermission(address _delegate, address _module, bytes32 _perm, bool _valid) external; /** * @notice Used to change one or more permissions for a single delegate at once @@ -60,11 +53,10 @@ interface IPermissionManager { */ function changePermissionMulti( address _delegate, - address[] _modules, - bytes32[] _perms, - bool[] _valids - ) - external; + address[] calldata _modules, + bytes32[] calldata _perms, + bool[] calldata _valids + ) external; /** * @notice Used to return all delegates with a given permission and module @@ -72,9 +64,9 @@ interface IPermissionManager { * @param _perm Permission flag * @return address[] */ - function getAllDelegatesWithPerm(address _module, bytes32 _perm) external view returns(address[]); + function getAllDelegatesWithPerm(address _module, bytes32 _perm) external view returns(address[] memory); - /** + /** * @notice Used to return all permission of a single or multiple module * @dev possible that function get out of gas is there are lot of modules and perm related to them * @param _delegate Ethereum address of the delegate @@ -82,18 +74,21 @@ interface IPermissionManager { * @return address[] the address array of Modules this delegate has permission * @return bytes32[] the permission array of the corresponding Modules */ - function getAllModulesAndPermsFromTypes(address _delegate, uint8[] _types) external view returns(address[], bytes32[]); + function getAllModulesAndPermsFromTypes(address _delegate, uint8[] calldata _types) external view returns( + address[] memory, + bytes32[] memory + ); /** * @notice Used to get the Permission flag related the `this` contract * @return Array of permission flags */ - function getPermissions() external view returns(bytes32[]); + function getPermissions() external view returns(bytes32[] memory); /** * @notice Used to get all delegates * @return address[] */ - function getAllDelegates() external view returns(address[]); + function getAllDelegates() external view returns(address[] memory); } diff --git a/contracts/modules/STO/CappedSTO.sol b/contracts/modules/STO/CappedSTO.sol index 60373ae37..69cd1cfe3 100644 --- a/contracts/modules/STO/CappedSTO.sol +++ b/contracts/modules/STO/CappedSTO.sol @@ -1,25 +1,16 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ISTO.sol"; -import "../../interfaces/ISecurityToken.sol"; -import "openzeppelin-solidity/contracts/ReentrancyGuard.sol"; +import "./STO.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../storage/modules/STO/CappedSTOStorage.sol"; /** * @title STO module for standard capped crowdsale */ -contract CappedSTO is ISTO, ReentrancyGuard { +contract CappedSTO is CappedSTOStorage, STO, ReentrancyGuard { using SafeMath for uint256; - // Determine whether users can invest on behalf of a beneficiary - bool public allowBeneficialInvestments = false; - // How many token units a buyer gets per wei / base unit of POLY - uint256 public rate; - //How many tokens this STO will be allowed to sell to investors - uint256 public cap; - - mapping (address => uint256) public investors; - /** * Event for token purchase logging * @param purchaser who paid for the tokens @@ -31,16 +22,15 @@ contract CappedSTO is ISTO, ReentrancyGuard { event SetAllowBeneficialInvestments(bool _allowed); - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } ////////////////////////////////// /** * @notice fallback function ***DO NOT OVERRIDE*** */ - function () external payable { + function() external payable { buyTokens(msg.sender); } @@ -48,8 +38,8 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @notice Function used to intialize the contract variables * @param _startTime Unix timestamp at which offering get started * @param _endTime Unix timestamp at which offering get ended - * @param _cap Maximum No. of tokens for sale - * @param _rate Token units a buyer gets per wei / base unit of POLY + * @param _cap Maximum No. of token base units for sale + * @param _rate Token units a buyer gets multiplied by 10^18 per wei / base unit of POLY * @param _fundRaiseTypes Type of currency used to collect the funds * @param _fundsReceiver Ethereum account address to hold the funds */ @@ -58,12 +48,13 @@ contract CappedSTO is ISTO, ReentrancyGuard { uint256 _endTime, uint256 _cap, uint256 _rate, - FundRaiseType[] _fundRaiseTypes, - address _fundsReceiver + FundRaiseType[] memory _fundRaiseTypes, + address payable _fundsReceiver ) - public - onlyFactory + public + onlyFactory { + require(endTime == 0, "Already configured"); require(_rate > 0, "Rate of token should be greater than 0"); require(_fundsReceiver != address(0), "Zero address is not permitted"); /*solium-disable-next-line security/no-block-members*/ @@ -81,7 +72,7 @@ contract CappedSTO is ISTO, ReentrancyGuard { /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(keccak256("configure(uint256,uint256,uint256,uint256,uint8[],address)")); } @@ -108,9 +99,10 @@ contract CappedSTO is ISTO, ReentrancyGuard { require(fundRaiseTypes[uint8(FundRaiseType.ETH)], "Mode of investment is not ETH"); uint256 weiAmount = msg.value; - _processTx(_beneficiary, weiAmount); + uint256 refund = _processTx(_beneficiary, weiAmount); + weiAmount = weiAmount.sub(refund); - _forwardFunds(); + _forwardFunds(refund); _postValidatePurchase(_beneficiary, weiAmount); } @@ -118,33 +110,33 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @notice low level token purchase * @param _investedPOLY Amount of POLY invested */ - function buyTokensWithPoly(uint256 _investedPOLY) public nonReentrant{ + function buyTokensWithPoly(uint256 _investedPOLY) public nonReentrant { require(!paused, "Should not be paused"); require(fundRaiseTypes[uint8(FundRaiseType.POLY)], "Mode of investment is not POLY"); - _processTx(msg.sender, _investedPOLY); - _forwardPoly(msg.sender, wallet, _investedPOLY); - _postValidatePurchase(msg.sender, _investedPOLY); + uint256 refund = _processTx(msg.sender, _investedPOLY); + _forwardPoly(msg.sender, wallet, _investedPOLY.sub(refund)); + _postValidatePurchase(msg.sender, _investedPOLY.sub(refund)); } /** * @notice Checks whether the cap has been reached. * @return bool Whether the cap was reached */ - function capReached() public view returns (bool) { + function capReached() public view returns(bool) { return totalTokensSold >= cap; } /** * @notice Return the total no. of tokens sold */ - function getTokensSold() public view returns (uint256) { + function getTokensSold() external view returns (uint256) { return totalTokensSold; } /** * @notice Return the permissions flag that are associated with STO */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](0); return allPermissions; } @@ -153,23 +145,17 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @notice Return the STO details * @return Unixtimestamp at which offering gets start. * @return Unixtimestamp at which offering ends. - * @return Number of tokens this STO will be allowed to sell to investors. + * @return Number of token base units this STO will be allowed to sell to investors. + * @return Token units a buyer gets(multiplied by 10^18) per wei / base unit of POLY * @return Amount of funds raised * @return Number of individual investors this STO have. - * @return Amount of tokens get sold. + * @return Amount of tokens get sold. * @return Boolean value to justify whether the fund raise type is POLY or not, i.e true for POLY. */ function getSTODetails() public view returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool) { - return ( - startTime, - endTime, - cap, - rate, - (fundRaiseTypes[uint8(FundRaiseType.POLY)]) ? fundsRaised[uint8(FundRaiseType.POLY)]: fundsRaised[uint8(FundRaiseType.ETH)], - investorCount, - totalTokensSold, - (fundRaiseTypes[uint8(FundRaiseType.POLY)]) - ); + return (startTime, endTime, cap, rate, (fundRaiseTypes[uint8(FundRaiseType.POLY)]) ? fundsRaised[uint8( + FundRaiseType.POLY + )] : fundsRaised[uint8(FundRaiseType.ETH)], investorCount, totalTokensSold, (fundRaiseTypes[uint8(FundRaiseType.POLY)])); } // ----------------------------------------- @@ -180,11 +166,12 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @param _beneficiary Address performing the token purchase * @param _investedAmount Value in wei involved in the purchase */ - function _processTx(address _beneficiary, uint256 _investedAmount) internal { - + function _processTx(address _beneficiary, uint256 _investedAmount) internal returns(uint256 refund) { _preValidatePurchase(_beneficiary, _investedAmount); // calculate token amount to be created - uint256 tokens = _getTokenAmount(_investedAmount); + uint256 tokens; + (tokens, refund) = _getTokenAmount(_investedAmount); + _investedAmount = _investedAmount.sub(refund); // update state if (fundRaiseTypes[uint8(FundRaiseType.POLY)]) { @@ -209,7 +196,9 @@ contract CappedSTO is ISTO, ReentrancyGuard { function _preValidatePurchase(address _beneficiary, uint256 _investedAmount) internal view { require(_beneficiary != address(0), "Beneficiary address should not be 0x"); require(_investedAmount != 0, "Amount invested should not be equal to 0"); - require(totalTokensSold.add(_getTokenAmount(_investedAmount)) <= cap, "Investment more than cap is not allowed"); + uint256 tokens; + (tokens, ) = _getTokenAmount(_investedAmount); + require(totalTokensSold.add(tokens) <= cap, "Investment more than cap is not allowed"); /*solium-disable-next-line security/no-block-members*/ require(now >= startTime && now <= endTime, "Offering is closed/Not yet started"); } @@ -218,8 +207,11 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @notice Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid conditions are not met. */ - function _postValidatePurchase(address /*_beneficiary*/, uint256 /*_investedAmount*/) internal pure { - // optional override + function _postValidatePurchase( + address _beneficiary, + uint256 /*_investedAmount*/ + ) internal view { + require(_canBuy(_beneficiary), "Unauthorized"); } /** @@ -229,7 +221,7 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @param _tokenAmount Number of tokens to be emitted */ function _deliverTokens(address _beneficiary, uint256 _tokenAmount) internal { - require(ISecurityToken(securityToken).mint(_beneficiary, _tokenAmount), "Error in minting the tokens"); + ISecurityToken(securityToken).issue(_beneficiary, _tokenAmount, ""); } /** @@ -250,24 +242,34 @@ contract CappedSTO is ISTO, ReentrancyGuard { * @notice Overrides for extensions that require an internal state to check for validity (current user contributions, etc.) */ - function _updatePurchasingState(address /*_beneficiary*/, uint256 /*_investedAmount*/) internal pure { - // optional override + function _updatePurchasingState( + address, /*_beneficiary*/ + uint256 _investedAmount + ) internal pure { + _investedAmount = 0; //yolo } /** * @notice Overrides to extend the way in which ether is converted to tokens. * @param _investedAmount Value in wei to be converted into tokens * @return Number of tokens that can be purchased with the specified _investedAmount + * @return Remaining amount that should be refunded to the investor */ - function _getTokenAmount(uint256 _investedAmount) internal view returns (uint256) { - return _investedAmount.mul(rate); + function _getTokenAmount(uint256 _investedAmount) internal view returns(uint256 _tokens, uint256 _refund) { + _tokens = _investedAmount.mul(rate); + _tokens = _tokens.div(uint256(10) ** 18); + uint256 granularity = ISecurityToken(securityToken).granularity(); + _tokens = _tokens.div(granularity); + _tokens = _tokens.mul(granularity); + _refund = _investedAmount.sub((_tokens.mul(uint256(10) ** 18)).div(rate)); } /** * @notice Determines how ETH is stored/forwarded on purchases. */ - function _forwardFunds() internal { - wallet.transfer(msg.value); + function _forwardFunds(uint256 _refund) internal { + wallet.transfer(msg.value.sub(_refund)); + msg.sender.transfer(_refund); } /** diff --git a/contracts/modules/STO/CappedSTOFactory.sol b/contracts/modules/STO/CappedSTOFactory.sol index 89f3311d2..1f6a63311 100644 --- a/contracts/modules/STO/CappedSTOFactory.sol +++ b/contracts/modules/STO/CappedSTOFactory.sol @@ -1,51 +1,67 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./CappedSTO.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; +import "../../proxy/CappedSTOProxy.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying CappedSTO module */ contract CappedSTOFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid address"); + version = "2.1.0"; name = "CappedSTO"; title = "Capped STO"; - description = "Use to collects the funds and once the cap is reached then investment will be no longer entertained"; + description = "This smart contract creates a maximum number of tokens (i.e. hard cap) which the total aggregate of tokens acquired by all investors cannot exceed. Security tokens are sent to the investor upon reception of the funds (ETH or POLY), and any security tokens left upon termination of the offering will not be minted."; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } - /** + /** * @notice Used to launch the Module with the help of factory + * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); //Check valid bytes - can only call module init function - CappedSTO cappedSTO = new CappedSTO(msg.sender, address(polyToken)); + address cappedSTO = address(new CappedSTOProxy(msg.sender, polyToken, logicContract)); //Checks that _data is valid (not calling anything it shouldn't) - require(Util.getSig(_data) == cappedSTO.getInitFunction(), "Invalid data"); + require(Util.getSig(_data) == IBoot(cappedSTO).getInitFunction(), "Invalid data"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(cappedSTO).call(_data), "Unsuccessfull call"); + (success, ) = cappedSTO.call(_data); + require(success, "Unsuccessfull call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(cappedSTO), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(address(cappedSTO), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return address(cappedSTO); } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 3; return res; @@ -54,7 +70,7 @@ contract CappedSTOFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { /*solium-disable-next-line max-len*/ return "Initialises a capped STO. Init parameters are _startTime (time STO starts), _endTime (time STO ends), _cap (cap in tokens for STO), _rate (POLY/ETH to token rate), _fundRaiseType (whether you are raising in POLY or ETH), _polyToken (address of POLY token), _fundsReceiver (address which will receive funds)"; } @@ -62,7 +78,7 @@ contract CappedSTOFactory is ModuleFactory { /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](4); availableTags[0] = "Capped"; availableTags[1] = "Non-refundable"; diff --git a/contracts/modules/STO/DummySTO.sol b/contracts/modules/STO/DummySTO.sol index 1b44d2e9b..f49aee7ae 100644 --- a/contracts/modules/STO/DummySTO.sol +++ b/contracts/modules/STO/DummySTO.sol @@ -1,32 +1,22 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ISTO.sol"; +import "./STO.sol"; import "../../interfaces/ISecurityToken.sol"; +import "../../storage/modules/STO/DummySTOStorage.sol"; /** * @title STO module for sample implementation of a different crowdsale module */ -contract DummySTO is ISTO { - - bytes32 public constant ADMIN = "ADMIN"; - - uint256 public investorCount; - - uint256 public cap; - string public someString; +contract DummySTO is DummySTOStorage, STO { event GenerateTokens(address _investor, uint256 _amount); - mapping (address => uint256) public investors; - /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** @@ -36,7 +26,7 @@ contract DummySTO is ISTO { * @param _cap Maximum No. of tokens for sale * @param _someString Any string that contails the details */ - function configure(uint256 _startTime, uint256 _endTime, uint256 _cap, string _someString) public onlyFactory { + function configure(uint256 _startTime, uint256 _endTime, uint256 _cap, string memory _someString) public onlyFactory { startTime = _startTime; endTime = _endTime; cap = _cap; @@ -46,8 +36,8 @@ contract DummySTO is ISTO { /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(keccak256("configure(uint256,uint256,uint256,string)")); + function getInitFunction() public pure returns(bytes4) { + return this.configure.selector; } /** @@ -58,33 +48,34 @@ contract DummySTO is ISTO { function generateTokens(address _investor, uint256 _amount) public withPerm(ADMIN) { require(!paused, "Should not be paused"); require(_amount > 0, "Amount should be greater than 0"); - ISecurityToken(securityToken).mint(_investor, _amount); + require(_canBuy(_investor), "Unauthorized"); + ISecurityToken(securityToken).issue(_investor, _amount, ""); if (investors[_investor] == 0) { investorCount = investorCount + 1; } //TODO: Add SafeMath maybe investors[_investor] = investors[_investor] + _amount; - emit GenerateTokens (_investor, _amount); + emit GenerateTokens(_investor, _amount); } /** * @notice Returns the total no. of investors */ - function getNumberInvestors() public view returns (uint256) { + function getNumberInvestors() public view returns(uint256) { return investorCount; } /** * @notice Returns the total no. of investors */ - function getTokensSold() public view returns (uint256) { + function getTokensSold() external view returns(uint256) { return 0; } /** * @notice Returns the permissions flag that are associated with STO */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = ADMIN; return allPermissions; diff --git a/contracts/modules/STO/DummySTOFactory.sol b/contracts/modules/STO/DummySTOFactory.sol index 04640ac58..57cd7eea6 100644 --- a/contracts/modules/STO/DummySTOFactory.sol +++ b/contracts/modules/STO/DummySTOFactory.sol @@ -1,20 +1,32 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./DummySTO.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; +import "../../proxy/DummySTOProxy.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying DummySTO module */ contract DummySTOFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { version = "1.0.0"; name = "DummySTO"; @@ -22,29 +34,33 @@ contract DummySTOFactory is ModuleFactory { description = "Dummy STO"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } + /** * @notice Used to launch the Module with the help of factory + * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); //Check valid bytes - can only call module init function - DummySTO dummySTO = new DummySTO(msg.sender, address(polyToken)); + address dummySTO = address(new DummySTOProxy(msg.sender, polyToken, logicContract)); //Checks that _data is valid (not calling anything it shouldn't) - require(Util.getSig(_data) == dummySTO.getInitFunction(), "Invalid data"); + require(Util.getSig(_data) == IBoot(dummySTO).getInitFunction(), "Invalid data"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(dummySTO).call(_data), "Unsuccessfull call"); + (success, ) = dummySTO.call(_data); + require(success, "Unsuccessfull call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(dummySTO), getName(), address(this), msg.sender, setupCost, now); - return address(dummySTO); + emit GenerateModuleFromFactory(dummySTO, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return dummySTO; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 3; return res; @@ -53,14 +69,14 @@ contract DummySTOFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Dummy STO - you can mint tokens at will"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](4); availableTags[0] = "Dummy"; availableTags[1] = "Non-refundable"; diff --git a/contracts/modules/STO/PreSaleSTO.sol b/contracts/modules/STO/PreSaleSTO.sol index 7378fb06b..147c85b8d 100644 --- a/contracts/modules/STO/PreSaleSTO.sol +++ b/contracts/modules/STO/PreSaleSTO.sol @@ -1,29 +1,23 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ISTO.sol"; -import "../../interfaces/ISecurityToken.sol"; +import "./STO.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../storage/modules/STO/PreSaleSTOStorage.sol"; /** * @title STO module for private presales */ -contract PreSaleSTO is ISTO { +contract PreSaleSTO is PreSaleSTOStorage, STO { using SafeMath for uint256; - bytes32 public constant PRE_SALE_ADMIN = "PRE_SALE_ADMIN"; - event TokensAllocated(address _investor, uint256 _amount); - mapping (address => uint256) public investors; - /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** @@ -38,28 +32,28 @@ contract PreSaleSTO is ISTO { /** * @notice This function returns the signature of the configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(keccak256("configure(uint256)")); } /** * @notice Returns the total no. of investors */ - function getNumberInvestors() public view returns (uint256) { + function getNumberInvestors() public view returns(uint256) { return investorCount; } /** * @notice Returns the total no. of tokens sold */ - function getTokensSold() public view returns (uint256) { + function getTokensSold() external view returns(uint256) { return totalTokensSold; } /** * @notice Returns the permissions flag that are associated with STO */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = PRE_SALE_ADMIN; return allPermissions; @@ -84,7 +78,8 @@ contract PreSaleSTO is ISTO { /*solium-disable-next-line security/no-block-members*/ require(now <= endTime, "Already passed Endtime"); require(_amount > 0, "No. of tokens provided should be greater the zero"); - ISecurityToken(securityToken).mint(_investor, _amount); + require(_canBuy(_investor), "Unauthorized"); + ISecurityToken(securityToken).issue(_investor, _amount, ""); if (investors[_investor] == uint256(0)) { investorCount = investorCount.add(1); } @@ -103,10 +98,10 @@ contract PreSaleSTO is ISTO { * @param _polyContributed Array of amount of POLY contributed by each investor */ function allocateTokensMulti( - address[] _investors, - uint256[] _amounts, - uint256[] _etherContributed, - uint256[] _polyContributed + address[] memory _investors, + uint256[] memory _amounts, + uint256[] memory _etherContributed, + uint256[] memory _polyContributed ) public withPerm(PRE_SALE_ADMIN) diff --git a/contracts/modules/STO/PreSaleSTOFactory.sol b/contracts/modules/STO/PreSaleSTOFactory.sol index 035c4bad5..cfa617faa 100644 --- a/contracts/modules/STO/PreSaleSTOFactory.sol +++ b/contracts/modules/STO/PreSaleSTOFactory.sol @@ -1,27 +1,41 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./PreSaleSTO.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; +import "../../proxy/PreSaleSTOProxy.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying PreSaleSTO module */ contract PreSaleSTOFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { + require(_logicContract != address(0), "Invalid address"); version = "1.0.0"; name = "PreSaleSTO"; title = "PreSale STO"; description = "Allows Issuer to configure pre-sale token allocations"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** @@ -29,41 +43,41 @@ contract PreSaleSTOFactory is ModuleFactory { * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if (setupCost > 0) { - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); - } + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); //Check valid bytes - can only call module init function - PreSaleSTO preSaleSTO = new PreSaleSTO(msg.sender, address(polyToken)); + address preSaleSTO = address(new PreSaleSTOProxy(msg.sender, polyToken, logicContract)); //Checks that _data is valid (not calling anything it shouldn't) - require(Util.getSig(_data) == preSaleSTO.getInitFunction(), "Invalid data"); + require(Util.getSig(_data) == IBoot(preSaleSTO).getInitFunction(), "Invalid data"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(preSaleSTO).call(_data), "Unsuccessfull call"); + (success, ) = preSaleSTO.call(_data); + require(success, "Unsuccessfull call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(preSaleSTO), getName(), address(this), msg.sender, setupCost, now); - return address(preSaleSTO); + emit GenerateModuleFromFactory(preSaleSTO, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return preSaleSTO; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 3; return res; } - + /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Configure and track pre-sale token allocations"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](1); availableTags[0] = "Presale"; return availableTags; diff --git a/contracts/modules/STO/ProxyFactory/USDTieredSTOProxyFactory.sol b/contracts/modules/STO/ProxyFactory/USDTieredSTOProxyFactory.sol deleted file mode 100644 index d9a48b87d..000000000 --- a/contracts/modules/STO/ProxyFactory/USDTieredSTOProxyFactory.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.4.24; - -import "../USDTieredSTO.sol"; -import "../../../interfaces/IUSDTieredSTOProxy.sol"; - -contract USDTieredSTOProxyFactory is IUSDTieredSTOProxy { - - constructor() public { - - } - - /** - * @notice Deploys the STO. - * @param _securityToken Contract address of the securityToken - * @param _polyAddress Contract address of the PolyToken. - * @param _factoryAddress Contract address of the factory - * @return address Address of the deployed STO - */ - function deploySTO(address _securityToken, address _polyAddress, address _factoryAddress) external returns (address) { - address newSecurityTokenAddress = new USDTieredSTO(_securityToken, _polyAddress, _factoryAddress); - return newSecurityTokenAddress; - } - - /** - * @notice Used to get the init function signature - * @param _contractAddress Address of the STO contract - * @return bytes4 - */ - function getInitFunction(address _contractAddress) external returns (bytes4) { - return USDTieredSTO(_contractAddress).getInitFunction(); - } - -} diff --git a/contracts/modules/STO/ISTO.sol b/contracts/modules/STO/STO.sol similarity index 65% rename from contracts/modules/STO/ISTO.sol rename to contracts/modules/STO/STO.sol index 6ed1d5ba1..7cc72f135 100644 --- a/contracts/modules/STO/ISTO.sol +++ b/contracts/modules/STO/STO.sol @@ -1,32 +1,19 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../../Pausable.sol"; import "../Module.sol"; -import "../../interfaces/IERC20.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import "../../storage/modules/STO/STOStorage.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../interfaces/ISTO.sol"; /** - * @title Interface to be implemented by all STO modules + * @title Base abstract contract to be extended by all STO modules */ -contract ISTO is Module, Pausable { +contract STO is ISTO, STOStorage, Module, Pausable { using SafeMath for uint256; - enum FundRaiseType { ETH, POLY, DAI } - mapping (uint8 => bool) public fundRaiseTypes; - mapping (uint8 => uint256) public fundsRaised; - - // Start time of the STO - uint256 public startTime; - // End time of the STO - uint256 public endTime; - // Time STO was paused - uint256 public pausedTime; - // Number of individual investors - uint256 public investorCount; - // Address where ETH & POLY funds are delivered - address public wallet; - // Final amount of tokens sold - uint256 public totalTokensSold; + enum FundRaiseType {ETH, POLY, SC} // Event event SetFundRaiseTypes(FundRaiseType[] _fundRaiseTypes); @@ -46,14 +33,14 @@ contract ISTO is Module, Pausable { /** * @notice Returns funds raised by the STO */ - function getRaised(FundRaiseType _fundRaiseType) public view returns (uint256) { + function getRaised(FundRaiseType _fundRaiseType) public view returns(uint256) { return fundsRaised[uint8(_fundRaiseType)]; } /** * @notice Returns the total no. of tokens sold */ - function getTokensSold() public view returns (uint256); + function getTokensSold() external view returns (uint256); /** * @notice Pause (overridden function) @@ -71,16 +58,25 @@ contract ISTO is Module, Pausable { super._unpause(); } - function _setFundRaiseType(FundRaiseType[] _fundRaiseTypes) internal { + function _setFundRaiseType(FundRaiseType[] memory _fundRaiseTypes) internal { // FundRaiseType[] parameter type ensures only valid values for _fundRaiseTypes require(_fundRaiseTypes.length > 0, "Raise type is not specified"); fundRaiseTypes[uint8(FundRaiseType.ETH)] = false; fundRaiseTypes[uint8(FundRaiseType.POLY)] = false; - fundRaiseTypes[uint8(FundRaiseType.DAI)] = false; + fundRaiseTypes[uint8(FundRaiseType.SC)] = false; for (uint8 j = 0; j < _fundRaiseTypes.length; j++) { fundRaiseTypes[uint8(_fundRaiseTypes[j])] = true; } emit SetFundRaiseTypes(_fundRaiseTypes); } + function _canBuy(address _investor) internal view returns(bool) { + IDataStore dataStore = IDataStore(getDataStore()); + uint256 flags = dataStore.getUint256(_getKey(INVESTORFLAGS, _investor)); + return(flags & (uint256(1) << 1) == 0); + } + + function _getKey(bytes32 _key1, address _key2) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(_key1, _key2))); + } } diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 894744451..421aa978e 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -1,88 +1,20 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ISTO.sol"; -import "../../interfaces/ISecurityToken.sol"; +import "./STO.sol"; import "../../interfaces/IOracle.sol"; import "../../RegistryUpdater.sol"; import "../../libraries/DecimalMath.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "openzeppelin-solidity/contracts/ReentrancyGuard.sol"; +import "../../storage/modules/STO/USDTieredSTOStorage.sol"; /** * @title STO module for standard capped crowdsale */ -contract USDTieredSTO is ISTO, ReentrancyGuard { +contract USDTieredSTO is USDTieredSTOStorage, STO { using SafeMath for uint256; - ///////////// - // Storage // - ///////////// - - string public POLY_ORACLE = "PolyUsdOracle"; - string public ETH_ORACLE = "EthUsdOracle"; - mapping (bytes32 => mapping (bytes32 => string)) oracleKeys; - - IERC20 public usdToken; - - // Determine whether users can invest on behalf of a beneficiary - bool public allowBeneficialInvestments = false; - - // Address where ETH, POLY & DAI funds are delivered - address public wallet; - - // Address of issuer reserve wallet for unsold tokens - address public reserveWallet; - - // How many token units a buyer gets per USD per tier (multiplied by 10**18) - uint256[] public ratePerTier; - - // How many token units a buyer gets per USD per tier (multiplied by 10**18) when investing in POLY up to tokensPerTierDiscountPoly - uint256[] public ratePerTierDiscountPoly; - - // How many tokens are available in each tier (relative to totalSupply) - uint256[] public tokensPerTierTotal; - - // How many token units are available in each tier (relative to totalSupply) at the ratePerTierDiscountPoly rate - uint256[] public tokensPerTierDiscountPoly; - - // How many tokens have been minted in each tier (relative to totalSupply) - uint256[] public mintedPerTierTotal; - - // How many tokens have been minted in each tier (relative to totalSupply) for each fund raise type - mapping (uint8 => uint256[]) public mintedPerTier; - - // How many tokens have been minted in each tier (relative to totalSupply) at discounted POLY rate - uint256[] public mintedPerTierDiscountPoly; - - // Current tier - uint8 public currentTier; - - // Amount of USD funds raised - uint256 public fundsRaisedUSD; - - // Amount in USD invested by each address - mapping (address => uint256) public investorInvestedUSD; - - // Amount in fund raise type invested by each investor - mapping (address => mapping (uint8 => uint256)) public investorInvested; - - // List of accredited investors - mapping (address => bool) public accredited; - - // Default limit in USD for non-accredited investors multiplied by 10**18 - uint256 public nonAccreditedLimitUSD; - - // Overrides for default limit in USD for non-accredited investors multiplied by 10**18 - mapping (address => uint256) public nonAccreditedLimitUSDOverride; - - // Minimum investable amount in USD - uint256 public minimumInvestmentUSD; - - // Whether or not the STO has been finalized - bool public isFinalized; - - // Final amount of tokens returned to issuer - uint256 public finalAmountReturned; + string internal constant POLY_ORACLE = "PolyUsdOracle"; + string internal constant ETH_ORACLE = "EthUsdOracle"; //////////// // Events // @@ -97,7 +29,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 _tokens, uint256 _usdAmount, uint256 _tierPrice, - uint8 _tier + uint256 _tier ); event FundsReceived( address indexed _purchaser, @@ -108,29 +40,10 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 _spentValue, uint256 _rate ); - event FundsReceivedPOLY( - address indexed _purchaser, - address indexed _beneficiary, - uint256 _usdAmount, - uint256 _receivedValue, - uint256 _spentValue, - uint256 _rate - ); - event ReserveTokenMint(address indexed _owner, address indexed _wallet, uint256 _tokens, uint8 _latestTier); - - event SetAddresses( - address indexed _wallet, - address indexed _reserveWallet, - address indexed _usdToken - ); - event SetLimits( - uint256 _nonAccreditedLimitUSD, - uint256 _minimumInvestmentUSD - ); - event SetTimes( - uint256 _startTime, - uint256 _endTime - ); + event ReserveTokenMint(address indexed _owner, address indexed _wallet, uint256 _tokens, uint256 _latestTier); + event SetAddresses(address indexed _wallet, address indexed _reserveWallet, address[] _usdTokens); + event SetLimits(uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD); + event SetTimes(uint256 _startTime, uint256 _endTime); event SetTiers( uint256[] _ratePerTier, uint256[] _ratePerTierDiscountPoly, @@ -142,20 +55,20 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Modifiers // /////////////// - modifier validETH { - require(_getOracle(bytes32("ETH"), bytes32("USD")) != address(0), "Invalid ETHUSD Oracle"); - require(fundRaiseTypes[uint8(FundRaiseType.ETH)], "Fund raise in ETH should be allowed"); + modifier validETH() { + require(_getOracle(bytes32("ETH"), bytes32("USD")) != address(0), "Invalid Oracle"); + require(fundRaiseTypes[uint8(FundRaiseType.ETH)], "ETH not allowed"); _; } - modifier validPOLY { - require(_getOracle(bytes32("POLY"), bytes32("USD")) != address(0), "Invalid POLYUSD Oracle"); - require(fundRaiseTypes[uint8(FundRaiseType.POLY)], "Fund raise in POLY should be allowed"); + modifier validPOLY() { + require(_getOracle(bytes32("POLY"), bytes32("USD")) != address(0), "Invalid Oracle"); + require(fundRaiseTypes[uint8(FundRaiseType.POLY)], "POLY not allowed"); _; } - modifier validDAI { - require(fundRaiseTypes[uint8(FundRaiseType.DAI)], "Fund raise in DAI should be allowed"); + modifier validSC(address _usdToken) { + require(fundRaiseTypes[uint8(FundRaiseType.SC)] && usdTokenEnabled[_usdToken], "USD not allowed"); _; } @@ -163,11 +76,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // STO Configuration // /////////////////////// - constructor (address _securityToken, address _polyAddress, address _factory) public Module(_securityToken, _polyAddress) { - oracleKeys[bytes32("ETH")][bytes32("USD")] = ETH_ORACLE; - oracleKeys[bytes32("POLY")][bytes32("USD")] = POLY_ORACLE; - require(_factory != address(0), "In-valid address"); - factory = _factory; + constructor(address _securityToken, address _polyAddress) public Module(_securityToken, _polyAddress) { + } /** @@ -181,85 +91,132 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _fundRaiseTypes Types of currency used to collect the funds * @param _wallet Ethereum account address to hold the funds * @param _reserveWallet Ethereum account address to receive unsold tokens - * @param _usdToken Contract address of the stable coin + * @param _usdTokens Contract address of the stable coins */ function configure( uint256 _startTime, uint256 _endTime, - uint256[] _ratePerTier, - uint256[] _ratePerTierDiscountPoly, - uint256[] _tokensPerTierTotal, - uint256[] _tokensPerTierDiscountPoly, + uint256[] memory _ratePerTier, + uint256[] memory _ratePerTierDiscountPoly, + uint256[] memory _tokensPerTierTotal, + uint256[] memory _tokensPerTierDiscountPoly, uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD, - FundRaiseType[] _fundRaiseTypes, - address _wallet, + FundRaiseType[] memory _fundRaiseTypes, + address payable _wallet, address _reserveWallet, - address _usdToken - ) public onlyFactory { - modifyTimes(_startTime, _endTime); - // NB - modifyTiers must come before modifyFunding - modifyTiers(_ratePerTier, _ratePerTierDiscountPoly, _tokensPerTierTotal, _tokensPerTierDiscountPoly); - // NB - modifyFunding must come before modifyAddresses - modifyFunding(_fundRaiseTypes); - modifyAddresses(_wallet, _reserveWallet, _usdToken); - modifyLimits(_nonAccreditedLimitUSD, _minimumInvestmentUSD); - } - - function modifyFunding(FundRaiseType[] _fundRaiseTypes) public onlyFactoryOrOwner { + address[] memory _usdTokens + ) + public + onlyFactory + { + oracleKeys[bytes32("ETH")][bytes32("USD")] = ETH_ORACLE; + oracleKeys[bytes32("POLY")][bytes32("USD")] = POLY_ORACLE; + require(endTime == 0, "Already configured"); + _modifyTimes(_startTime, _endTime); + _modifyTiers(_ratePerTier, _ratePerTierDiscountPoly, _tokensPerTierTotal, _tokensPerTierDiscountPoly); + // NB - _setFundRaiseType must come before modifyAddresses + _setFundRaiseType(_fundRaiseTypes); + _modifyAddresses(_wallet, _reserveWallet, _usdTokens); + _modifyLimits(_nonAccreditedLimitUSD, _minimumInvestmentUSD); + } + + /** + * @dev Modifies fund raise types + * @param _fundRaiseTypes Array of fund raise types to allow + */ + function modifyFunding(FundRaiseType[] calldata _fundRaiseTypes) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO shouldn't be started"); + require(now < startTime, "STO already started"); _setFundRaiseType(_fundRaiseTypes); - uint256 length = getNumberOfTiers(); - mintedPerTierTotal = new uint256[](length); - mintedPerTierDiscountPoly = new uint256[](length); - for (uint8 i = 0; i < _fundRaiseTypes.length; i++) { - mintedPerTier[uint8(_fundRaiseTypes[i])] = new uint256[](length); - } } - function modifyLimits( - uint256 _nonAccreditedLimitUSD, - uint256 _minimumInvestmentUSD - ) public onlyFactoryOrOwner { + /** + * @dev modifies max non accredited invets limit and overall minimum investment limit + * @param _nonAccreditedLimitUSD max non accredited invets limit + * @param _minimumInvestmentUSD overall minimum investment limit + */ + function modifyLimits(uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO shouldn't be started"); + require(now < startTime, "STO already started"); + _modifyLimits(_nonAccreditedLimitUSD, _minimumInvestmentUSD); + } + + /** + * @dev modifiers STO tiers. All tiers must be passed, can not edit specific tiers. + * @param _ratePerTier Array of rates per tier + * @param _ratePerTierDiscountPoly Array of discounted poly rates per tier + * @param _tokensPerTierTotal Array of total tokens per tier + * @param _tokensPerTierDiscountPoly Array of discounted tokens per tier + */ + function modifyTiers( + uint256[] calldata _ratePerTier, + uint256[] calldata _ratePerTierDiscountPoly, + uint256[] calldata _tokensPerTierTotal, + uint256[] calldata _tokensPerTierDiscountPoly + ) + external + onlyOwner + { + /*solium-disable-next-line security/no-block-members*/ + require(now < startTime, "STO already started"); + _modifyTiers(_ratePerTier, _ratePerTierDiscountPoly, _tokensPerTierTotal, _tokensPerTierDiscountPoly); + } + + /** + * @dev Modifies STO start and end times + * @param _startTime start time of sto + * @param _endTime end time of sto + */ + function modifyTimes(uint256 _startTime, uint256 _endTime) external onlyOwner { + /*solium-disable-next-line security/no-block-members*/ + require(now < startTime, "STO already started"); + _modifyTimes(_startTime, _endTime); + } + + /** + * @dev Modifies addresses used as wallet, reserve wallet and usd token + * @param _wallet Address of wallet where funds are sent + * @param _reserveWallet Address of wallet where unsold tokens are sent + * @param _usdTokens Address of usd tokens + */ + function modifyAddresses(address payable _wallet, address _reserveWallet, address[] calldata _usdTokens) external onlyOwner { + _modifyAddresses(_wallet, _reserveWallet, _usdTokens); + } + + function _modifyLimits(uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD) internal { minimumInvestmentUSD = _minimumInvestmentUSD; nonAccreditedLimitUSD = _nonAccreditedLimitUSD; emit SetLimits(minimumInvestmentUSD, nonAccreditedLimitUSD); } - function modifyTiers( - uint256[] _ratePerTier, - uint256[] _ratePerTierDiscountPoly, - uint256[] _tokensPerTierTotal, - uint256[] _tokensPerTierDiscountPoly - ) public onlyFactoryOrOwner { - /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO shouldn't be started"); - require(_tokensPerTierTotal.length > 0, "Length should be > 0"); - require(_ratePerTier.length == _tokensPerTierTotal.length, "Mismatch b/w rates & tokens / tier"); - require(_ratePerTierDiscountPoly.length == _tokensPerTierTotal.length, "Mismatch b/w discount rates & tokens / tier"); - require(_tokensPerTierDiscountPoly.length == _tokensPerTierTotal.length, "Mismatch b/w discount tokens / tier & tokens / tier"); - for (uint8 i = 0; i < _ratePerTier.length; i++) { - require(_ratePerTier[i] > 0, "Rate > 0"); - require(_tokensPerTierTotal[i] > 0, "Tokens per tier > 0"); - require(_tokensPerTierDiscountPoly[i] <= _tokensPerTierTotal[i], "Discounted tokens / tier <= tokens / tier"); - require(_ratePerTierDiscountPoly[i] <= _ratePerTier[i], "Discounted rate / tier <= rate / tier"); + function _modifyTiers( + uint256[] memory _ratePerTier, + uint256[] memory _ratePerTierDiscountPoly, + uint256[] memory _tokensPerTierTotal, + uint256[] memory _tokensPerTierDiscountPoly + ) + internal + { + require( + _tokensPerTierTotal.length > 0 && + _ratePerTier.length == _tokensPerTierTotal.length && + _ratePerTierDiscountPoly.length == _tokensPerTierTotal.length && + _tokensPerTierDiscountPoly.length == _tokensPerTierTotal.length, + "Invalid Input" + ); + delete tiers; + for (uint256 i = 0; i < _ratePerTier.length; i++) { + require(_ratePerTier[i] > 0, "Invalid rate"); + require(_tokensPerTierTotal[i] > 0, "Invalid token amount"); + require(_tokensPerTierDiscountPoly[i] <= _tokensPerTierTotal[i], "Too many discounted tokens"); + require(_ratePerTierDiscountPoly[i] <= _ratePerTier[i], "Invalid discount"); + tiers.push(Tier(_ratePerTier[i], _ratePerTierDiscountPoly[i], _tokensPerTierTotal[i], _tokensPerTierDiscountPoly[i], 0, 0)); } - ratePerTier = _ratePerTier; - ratePerTierDiscountPoly = _ratePerTierDiscountPoly; - tokensPerTierTotal = _tokensPerTierTotal; - tokensPerTierDiscountPoly = _tokensPerTierDiscountPoly; emit SetTiers(_ratePerTier, _ratePerTierDiscountPoly, _tokensPerTierTotal, _tokensPerTierDiscountPoly); } - function modifyTimes( - uint256 _startTime, - uint256 _endTime - ) public onlyFactoryOrOwner { - /*solium-disable-next-line security/no-block-members*/ - require((startTime == 0) || (now < startTime), "Invalid startTime"); + function _modifyTimes(uint256 _startTime, uint256 _endTime) internal { /*solium-disable-next-line security/no-block-members*/ require((_endTime > _startTime) && (_startTime > now), "Invalid times"); startTime = _startTime; @@ -267,21 +224,24 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { emit SetTimes(_startTime, _endTime); } - function modifyAddresses( - address _wallet, - address _reserveWallet, - address _usdToken - ) public onlyFactoryOrOwner { - /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO shouldn't be started"); - require(_wallet != address(0) && _reserveWallet != address(0), "Invalid address"); - if (fundRaiseTypes[uint8(FundRaiseType.DAI)]) { - require(_usdToken != address(0), "Invalid address"); - } + function _modifyAddresses(address payable _wallet, address _reserveWallet, address[] memory _usdTokens) internal { + require(_wallet != address(0) && _reserveWallet != address(0), "Invalid wallet"); wallet = _wallet; reserveWallet = _reserveWallet; - usdToken = IERC20(_usdToken); - emit SetAddresses(_wallet, _reserveWallet, _usdToken); + _modifyUSDTokens(_usdTokens); + } + + function _modifyUSDTokens(address[] memory _usdTokens) internal { + uint256 i; + for(i = 0; i < usdTokens.length; i++) { + usdTokenEnabled[usdTokens[i]] = false; + } + usdTokens = _usdTokens; + for(i = 0; i < _usdTokens.length; i++) { + require(_usdTokens[i] != address(0), "Invalid USD token"); + usdTokenEnabled[_usdTokens[i]] = true; + } + emit SetAddresses(wallet, reserveWallet, _usdTokens); } //////////////////// @@ -293,53 +253,56 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @notice Reserve address must be whitelisted to successfully finalize */ function finalize() public onlyOwner { - require(!isFinalized, "STO is already finalized"); + require(!isFinalized, "STO already finalized"); isFinalized = true; uint256 tempReturned; uint256 tempSold; uint256 remainingTokens; - for (uint8 i = 0; i < tokensPerTierTotal.length; i++) { - remainingTokens = tokensPerTierTotal[i].sub(mintedPerTierTotal[i]); + for (uint256 i = 0; i < tiers.length; i++) { + remainingTokens = tiers[i].tokenTotal.sub(tiers[i].mintedTotal); tempReturned = tempReturned.add(remainingTokens); - tempSold = tempSold.add(mintedPerTierTotal[i]); + tempSold = tempSold.add(tiers[i].mintedTotal); if (remainingTokens > 0) { - mintedPerTierTotal[i] = tokensPerTierTotal[i]; + tiers[i].mintedTotal = tiers[i].tokenTotal; } } - require(ISecurityToken(securityToken).mint(reserveWallet, tempReturned), "Error in minting"); + ISecurityToken(securityToken).issue(reserveWallet, tempReturned, ""); emit ReserveTokenMint(msg.sender, reserveWallet, tempReturned, currentTier); finalAmountReturned = tempReturned; totalTokensSold = tempSold; } - /** - * @notice Modifies the list of accredited addresses - * @param _investors Array of investor addresses to modify - * @param _accredited Array of bools specifying accreditation status - */ - function changeAccredited(address[] _investors, bool[] _accredited) public onlyOwner { - require(_investors.length == _accredited.length, "Array length mismatch"); - for (uint256 i = 0; i < _investors.length; i++) { - accredited[_investors[i]] = _accredited[i]; - emit SetAccredited(_investors[i], _accredited[i]); - } - } - /** * @notice Modifies the list of overrides for non-accredited limits in USD * @param _investors Array of investor addresses to modify * @param _nonAccreditedLimit Array of uints specifying non-accredited limits */ - function changeNonAccreditedLimit(address[] _investors, uint256[] _nonAccreditedLimit) public onlyOwner { + function changeNonAccreditedLimit(address[] memory _investors, uint256[] memory _nonAccreditedLimit) public onlyOwner { //nonAccreditedLimitUSDOverride require(_investors.length == _nonAccreditedLimit.length, "Array length mismatch"); for (uint256 i = 0; i < _investors.length; i++) { - require(_nonAccreditedLimit[i] > 0, "Limit can not be 0"); nonAccreditedLimitUSDOverride[_investors[i]] = _nonAccreditedLimit[i]; emit SetNonAccreditedLimit(_investors[i], _nonAccreditedLimit[i]); } } + /** + * @notice Returns investor accredited & non-accredited override informatiomn + * @return investors list of all configured investors + * @return accredited whether investor is accredited + * @return override any USD overrides for non-accredited limits for the investor + */ + function getAccreditedData() external view returns (address[] memory investors, bool[] memory accredited, uint256[] memory overrides) { + IDataStore dataStore = IDataStore(getDataStore()); + investors = dataStore.getAddressArray(INVESTORSKEY); + accredited = new bool[](investors.length); + overrides = new uint256[](investors.length); + for (uint256 i = 0; i < investors.length; i++) { + accredited[i] = _getIsAccredited(investors[i], dataStore); + overrides[i] = nonAccreditedLimitUSDOverride[investors[i]]; + } + } + /** * @notice Function to set allowBeneficialInvestments (allow beneficiary to be different to funder) * @param _allowBeneficialInvestments Boolean to allow or disallow beneficial investments @@ -357,17 +320,33 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { /** * @notice fallback function - assumes ETH being invested */ - function () external payable { - buyWithETH(msg.sender); + function() external payable { + buyWithETHRateLimited(msg.sender, 0); + } + + // Buy functions without rate restriction + function buyWithETH(address _beneficiary) external payable returns (uint256, uint256, uint256) { + return buyWithETHRateLimited(_beneficiary, 0); + } + + function buyWithPOLY(address _beneficiary, uint256 _investedPOLY) external returns (uint256, uint256, uint256) { + return buyWithPOLYRateLimited(_beneficiary, _investedPOLY, 0); + } + + function buyWithUSD(address _beneficiary, uint256 _investedSC, IERC20 _usdToken) external returns (uint256, uint256, uint256) { + return buyWithUSDRateLimited(_beneficiary, _investedSC, 0, _usdToken); } /** * @notice Purchase tokens using ETH * @param _beneficiary Address where security tokens will be sent + * @param _minTokens Minumum number of tokens to buy or else revert */ - function buyWithETH(address _beneficiary) public payable validETH { + function buyWithETHRateLimited(address _beneficiary, uint256 _minTokens) public payable validETH returns (uint256, uint256, uint256) { uint256 rate = getRate(FundRaiseType.ETH); + uint256 initialMinted = getTokensMinted(); (uint256 spentUSD, uint256 spentValue) = _buyTokens(_beneficiary, msg.value, rate, FundRaiseType.ETH); + require(getTokensMinted().sub(initialMinted) >= _minTokens, "Insufficient tokens minted"); // Modify storage investorInvested[_beneficiary][uint8(FundRaiseType.ETH)] = investorInvested[_beneficiary][uint8(FundRaiseType.ETH)].add(spentValue); fundsRaised[uint8(FundRaiseType.ETH)] = fundsRaised[uint8(FundRaiseType.ETH)].add(spentValue); @@ -376,44 +355,53 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Refund excess ETH to investor wallet msg.sender.transfer(msg.value.sub(spentValue)); emit FundsReceived(msg.sender, _beneficiary, spentUSD, FundRaiseType.ETH, msg.value, spentValue, rate); + return (spentUSD, spentValue, getTokensMinted().sub(initialMinted)); } /** * @notice Purchase tokens using POLY * @param _beneficiary Address where security tokens will be sent * @param _investedPOLY Amount of POLY invested + * @param _minTokens Minumum number of tokens to buy or else revert */ - function buyWithPOLY(address _beneficiary, uint256 _investedPOLY) public validPOLY { - _buyWithTokens(_beneficiary, _investedPOLY, FundRaiseType.POLY); + function buyWithPOLYRateLimited(address _beneficiary, uint256 _investedPOLY, uint256 _minTokens) public validPOLY returns (uint256, uint256, uint256) { + return _buyWithTokens(_beneficiary, _investedPOLY, FundRaiseType.POLY, _minTokens, polyToken); } /** - * @notice Purchase tokens using POLY + * @notice Purchase tokens using Stable coins * @param _beneficiary Address where security tokens will be sent - * @param _investedDAI Amount of POLY invested + * @param _investedSC Amount of Stable coins invested + * @param _minTokens Minumum number of tokens to buy or else revert + * @param _usdToken Address of USD stable coin to buy tokens with */ - function buyWithUSD(address _beneficiary, uint256 _investedDAI) public validDAI { - _buyWithTokens(_beneficiary, _investedDAI, FundRaiseType.DAI); + function buyWithUSDRateLimited(address _beneficiary, uint256 _investedSC, uint256 _minTokens, IERC20 _usdToken) + public validSC(address(_usdToken)) returns (uint256, uint256, uint256) + { + return _buyWithTokens(_beneficiary, _investedSC, FundRaiseType.SC, _minTokens, _usdToken); } - function _buyWithTokens(address _beneficiary, uint256 _tokenAmount, FundRaiseType _fundRaiseType) internal { - require(_fundRaiseType == FundRaiseType.POLY || _fundRaiseType == FundRaiseType.DAI, "POLY & DAI supported"); + function _buyWithTokens(address _beneficiary, uint256 _tokenAmount, FundRaiseType _fundRaiseType, uint256 _minTokens, IERC20 _token) internal returns (uint256, uint256, uint256) { + uint256 initialMinted = getTokensMinted(); uint256 rate = getRate(_fundRaiseType); (uint256 spentUSD, uint256 spentValue) = _buyTokens(_beneficiary, _tokenAmount, rate, _fundRaiseType); + require(getTokensMinted().sub(initialMinted) >= _minTokens, "Insufficient tokens minted"); // Modify storage investorInvested[_beneficiary][uint8(_fundRaiseType)] = investorInvested[_beneficiary][uint8(_fundRaiseType)].add(spentValue); fundsRaised[uint8(_fundRaiseType)] = fundsRaised[uint8(_fundRaiseType)].add(spentValue); - // Forward DAI to issuer wallet - IERC20 token = _fundRaiseType == FundRaiseType.POLY ? polyToken : usdToken; - require(token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); + if(address(_token) != address(polyToken)) + stableCoinsRaised[address(_token)] = stableCoinsRaised[address(_token)].add(spentValue); + // Forward coins to issuer wallet + require(_token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); emit FundsReceived(msg.sender, _beneficiary, spentUSD, _fundRaiseType, _tokenAmount, spentValue, rate); + return (spentUSD, spentValue, getTokensMinted().sub(initialMinted)); } /** * @notice Low level token purchase * @param _beneficiary Address where security tokens will be sent - * @param _investmentValue Amount of POLY, ETH or DAI invested - * @param _fundRaiseType Fund raise type (POLY, ETH, DAI) + * @param _investmentValue Amount of POLY, ETH or Stable coins invested + * @param _fundRaiseType Fund raise type (POLY, ETH, SC) */ function _buyTokens( address _beneficiary, @@ -422,42 +410,32 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { FundRaiseType _fundRaiseType ) internal - nonReentrant whenNotPaused - returns(uint256, uint256) + returns(uint256 spentUSD, uint256 spentValue) { if (!allowBeneficialInvestments) { - require(_beneficiary == msg.sender, "Beneficiary does not match funder"); + require(_beneficiary == msg.sender, "Beneficiary != funder"); } - require(isOpen(), "STO is not open"); - require(_investmentValue > 0, "No funds were sent"); + require(_canBuy(_beneficiary), "Unauthorized"); - uint256 investedUSD = DecimalMath.mul(_rate, _investmentValue); - uint256 originalUSD = investedUSD; + uint256 originalUSD = DecimalMath.mul(_rate, _investmentValue); + uint256 allowedUSD = _buyTokensChecks(_beneficiary, _investmentValue, originalUSD); - // Check for minimum investment - require(investedUSD.add(investorInvestedUSD[_beneficiary]) >= minimumInvestmentUSD, "Total investment < minimumInvestmentUSD"); - - // Check for non-accredited cap - if (!accredited[_beneficiary]) { - uint256 investorLimitUSD = (nonAccreditedLimitUSDOverride[_beneficiary] == 0) ? nonAccreditedLimitUSD : nonAccreditedLimitUSDOverride[_beneficiary]; - require(investorInvestedUSD[_beneficiary] < investorLimitUSD, "Non-accredited investor has reached limit"); - if (investedUSD.add(investorInvestedUSD[_beneficiary]) > investorLimitUSD) - investedUSD = investorLimitUSD.sub(investorInvestedUSD[_beneficiary]); - } - uint256 spentUSD; - // Iterate over each tier and process payment - for (uint8 i = currentTier; i < ratePerTier.length; i++) { + for (uint256 i = currentTier; i < tiers.length; i++) { + bool gotoNextTier; + uint256 tempSpentUSD; // Update current tier if needed if (currentTier != i) currentTier = i; // If there are tokens remaining, process investment - if (mintedPerTierTotal[i] < tokensPerTierTotal[i]) - spentUSD = spentUSD.add(_calculateTier(_beneficiary, i, investedUSD.sub(spentUSD), _fundRaiseType)); - // If all funds have been spent, exit the loop - if (investedUSD == spentUSD) - break; + if (tiers[i].mintedTotal < tiers[i].tokenTotal) { + (tempSpentUSD, gotoNextTier) = _calculateTier(_beneficiary, i, allowedUSD.sub(spentUSD), _fundRaiseType); + spentUSD = spentUSD.add(tempSpentUSD); + // If all funds have been spent, exit the loop + if (!gotoNextTier) + break; + } } // Modify storage @@ -468,53 +446,70 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { fundsRaisedUSD = fundsRaisedUSD.add(spentUSD); } - // Calculate spent in base currency (ETH, DAI or POLY) - uint256 spentValue; - if (spentUSD == 0) { - spentValue = 0; - } else { - spentValue = DecimalMath.mul(DecimalMath.div(spentUSD, originalUSD), _investmentValue); - } + spentValue = DecimalMath.div(spentUSD, _rate); + } + + function _buyTokensChecks( + address _beneficiary, + uint256 _investmentValue, + uint256 investedUSD + ) + internal + view + returns(uint256 netInvestedUSD) + { + require(isOpen(), "STO not open"); + require(_investmentValue > 0, "No funds sent"); - // Return calculated amounts - return (spentUSD, spentValue); + // Check for minimum investment + require(investedUSD.add(investorInvestedUSD[_beneficiary]) >= minimumInvestmentUSD, "Investment < min"); + netInvestedUSD = investedUSD; + // Check for non-accredited cap + if (!_isAccredited(_beneficiary)) { + uint256 investorLimitUSD = (nonAccreditedLimitUSDOverride[_beneficiary] == 0) ? nonAccreditedLimitUSD : nonAccreditedLimitUSDOverride[_beneficiary]; + require(investorInvestedUSD[_beneficiary] < investorLimitUSD, "Over Non-accredited investor limit"); + if (investedUSD.add(investorInvestedUSD[_beneficiary]) > investorLimitUSD) + netInvestedUSD = investorLimitUSD.sub(investorInvestedUSD[_beneficiary]); + } } function _calculateTier( address _beneficiary, - uint8 _tier, + uint256 _tier, uint256 _investedUSD, FundRaiseType _fundRaiseType - ) + ) internal - returns(uint256) + returns(uint256 spentUSD, bool gotoNextTier) { // First purchase any discounted tokens if POLY investment - uint256 spentUSD; uint256 tierSpentUSD; uint256 tierPurchasedTokens; uint256 investedUSD = _investedUSD; + Tier storage tierData = tiers[_tier]; // Check whether there are any remaining discounted tokens - if ((_fundRaiseType == FundRaiseType.POLY) && (tokensPerTierDiscountPoly[_tier] > mintedPerTierDiscountPoly[_tier])) { - uint256 discountRemaining = tokensPerTierDiscountPoly[_tier].sub(mintedPerTierDiscountPoly[_tier]); - uint256 totalRemaining = tokensPerTierTotal[_tier].sub(mintedPerTierTotal[_tier]); + if ((_fundRaiseType == FundRaiseType.POLY) && (tierData.tokensDiscountPoly > tierData.mintedDiscountPoly)) { + uint256 discountRemaining = tierData.tokensDiscountPoly.sub(tierData.mintedDiscountPoly); + uint256 totalRemaining = tierData.tokenTotal.sub(tierData.mintedTotal); if (totalRemaining < discountRemaining) - (spentUSD, tierPurchasedTokens) = _purchaseTier(_beneficiary, ratePerTierDiscountPoly[_tier], totalRemaining, investedUSD, _tier); + (spentUSD, tierPurchasedTokens, gotoNextTier) = _purchaseTier(_beneficiary, tierData.rateDiscountPoly, totalRemaining, investedUSD, _tier); else - (spentUSD, tierPurchasedTokens) = _purchaseTier(_beneficiary, ratePerTierDiscountPoly[_tier], discountRemaining, investedUSD, _tier); + (spentUSD, tierPurchasedTokens, gotoNextTier) = _purchaseTier(_beneficiary, tierData.rateDiscountPoly, discountRemaining, investedUSD, _tier); investedUSD = investedUSD.sub(spentUSD); - mintedPerTierDiscountPoly[_tier] = mintedPerTierDiscountPoly[_tier].add(tierPurchasedTokens); - mintedPerTier[uint8(FundRaiseType.POLY)][_tier] = mintedPerTier[uint8(FundRaiseType.POLY)][_tier].add(tierPurchasedTokens); - mintedPerTierTotal[_tier] = mintedPerTierTotal[_tier].add(tierPurchasedTokens); + tierData.mintedDiscountPoly = tierData.mintedDiscountPoly.add(tierPurchasedTokens); + tierData.minted[uint8(_fundRaiseType)] = tierData.minted[uint8(_fundRaiseType)].add(tierPurchasedTokens); + tierData.mintedTotal = tierData.mintedTotal.add(tierPurchasedTokens); } // Now, if there is any remaining USD to be invested, purchase at non-discounted rate - if ((investedUSD > 0) && (tokensPerTierTotal[_tier].sub(mintedPerTierTotal[_tier]) > 0)) { - (tierSpentUSD, tierPurchasedTokens) = _purchaseTier(_beneficiary, ratePerTier[_tier], tokensPerTierTotal[_tier].sub(mintedPerTierTotal[_tier]), investedUSD, _tier); + if (investedUSD > 0 && + tierData.tokenTotal.sub(tierData.mintedTotal) > 0 && + (_fundRaiseType != FundRaiseType.POLY || tierData.tokensDiscountPoly <= tierData.mintedDiscountPoly) + ) { + (tierSpentUSD, tierPurchasedTokens, gotoNextTier) = _purchaseTier(_beneficiary, tierData.rate, tierData.tokenTotal.sub(tierData.mintedTotal), investedUSD, _tier); spentUSD = spentUSD.add(tierSpentUSD); - mintedPerTier[uint8(_fundRaiseType)][_tier] = mintedPerTier[uint8(_fundRaiseType)][_tier].add(tierPurchasedTokens); - mintedPerTierTotal[_tier] = mintedPerTierTotal[_tier].add(tierPurchasedTokens); + tierData.minted[uint8(_fundRaiseType)] = tierData.minted[uint8(_fundRaiseType)].add(tierPurchasedTokens); + tierData.mintedTotal = tierData.mintedTotal.add(tierPurchasedTokens); } - return spentUSD; } function _purchaseTier( @@ -522,14 +517,15 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 _tierPrice, uint256 _tierRemaining, uint256 _investedUSD, - uint8 _tier + uint256 _tier ) internal - returns(uint256, uint256) + returns(uint256 spentUSD, uint256 purchasedTokens, bool gotoNextTier) { uint256 maximumTokens = DecimalMath.div(_investedUSD, _tierPrice); - uint256 spentUSD; - uint256 purchasedTokens; + uint256 granularity = ISecurityToken(securityToken).granularity(); + maximumTokens = maximumTokens.div(granularity); + maximumTokens = maximumTokens.mul(granularity); if (maximumTokens > _tierRemaining) { spentUSD = DecimalMath.mul(_tierRemaining, _tierPrice); // In case of rounding issues, ensure that spentUSD is never more than investedUSD @@ -537,13 +533,26 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { spentUSD = _investedUSD; } purchasedTokens = _tierRemaining; + gotoNextTier = true; } else { - spentUSD = _investedUSD; + spentUSD = DecimalMath.mul(maximumTokens, _tierPrice); purchasedTokens = maximumTokens; } - require(ISecurityToken(securityToken).mint(_beneficiary, purchasedTokens), "Error in minting"); - emit TokenPurchase(msg.sender, _beneficiary, purchasedTokens, spentUSD, _tierPrice, _tier); - return (spentUSD, purchasedTokens); + if (purchasedTokens > 0) { + ISecurityToken(securityToken).issue(_beneficiary, purchasedTokens, ""); + emit TokenPurchase(msg.sender, _beneficiary, purchasedTokens, spentUSD, _tierPrice, _tier); + } + } + + function _isAccredited(address _investor) internal view returns(bool) { + IDataStore dataStore = IDataStore(getDataStore()); + return _getIsAccredited(_investor, dataStore); + } + + function _getIsAccredited(address _investor, IDataStore dataStore) internal view returns(bool) { + uint256 flags = dataStore.getUint256(_getKey(INVESTORFLAGS, _investor)); + uint256 flag = flags & uint256(1); //isAccredited is flag 0 so we don't need to bit shift flags. + return flag > 0 ? true : false; } ///////////// @@ -555,15 +564,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @return bool Whether the STO is accepting investments */ function isOpen() public view returns(bool) { - if (isFinalized) - return false; /*solium-disable-next-line security/no-block-members*/ - if (now < startTime) - return false; - /*solium-disable-next-line security/no-block-members*/ - if (now >= endTime) - return false; - if (capReached()) + if (isFinalized || now < startTime || now >= endTime || capReached()) return false; return true; } @@ -576,18 +578,20 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { if (isFinalized) { return (finalAmountReturned == 0); } - return (mintedPerTierTotal[mintedPerTierTotal.length - 1] == tokensPerTierTotal[tokensPerTierTotal.length - 1]); + return (tiers[tiers.length - 1].mintedTotal == tiers[tiers.length - 1].tokenTotal); } - function getRate(FundRaiseType _fundRaiseType) public view returns (uint256) { + /** + * @dev returns current conversion rate of funds + * @param _fundRaiseType Fund raise type to get rate of + */ + function getRate(FundRaiseType _fundRaiseType) public returns (uint256) { if (_fundRaiseType == FundRaiseType.ETH) { return IOracle(_getOracle(bytes32("ETH"), bytes32("USD"))).getPrice(); } else if (_fundRaiseType == FundRaiseType.POLY) { return IOracle(_getOracle(bytes32("POLY"), bytes32("USD"))).getPrice(); - } else if (_fundRaiseType == FundRaiseType.DAI) { - return 1 * 10**18; - } else { - revert("Incorrect funding"); + } else if (_fundRaiseType == FundRaiseType.SC) { + return 10**18; } } @@ -597,9 +601,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _amount Value to convert to USD * @return uint256 Value in USD */ - function convertToUSD(FundRaiseType _fundRaiseType, uint256 _amount) public view returns(uint256) { - uint256 rate = getRate(_fundRaiseType); - return DecimalMath.mul(_amount, rate); + function convertToUSD(FundRaiseType _fundRaiseType, uint256 _amount) public returns(uint256) { + return DecimalMath.mul(_amount, getRate(_fundRaiseType)); } /** @@ -608,9 +611,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _amount Value to convert from USD * @return uint256 Value in ETH or POLY */ - function convertFromUSD(FundRaiseType _fundRaiseType, uint256 _amount) public view returns(uint256) { - uint256 rate = getRate(_fundRaiseType); - return DecimalMath.div(_amount, rate); + function convertFromUSD(FundRaiseType _fundRaiseType, uint256 _amount) public returns(uint256) { + return DecimalMath.div(_amount, getRate(_fundRaiseType)); } /** @@ -620,31 +622,53 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { function getTokensSold() public view returns (uint256) { if (isFinalized) return totalTokensSold; - else - return getTokensMinted(); + return getTokensMinted(); } /** * @notice Return the total no. of tokens minted * @return uint256 Total number of tokens minted */ - function getTokensMinted() public view returns (uint256) { - uint256 tokensMinted; - for (uint8 i = 0; i < mintedPerTierTotal.length; i++) { - tokensMinted = tokensMinted.add(mintedPerTierTotal[i]); + function getTokensMinted() public view returns (uint256 tokensMinted) { + for (uint256 i = 0; i < tiers.length; i++) { + tokensMinted = tokensMinted.add(tiers[i].mintedTotal); } - return tokensMinted; } /** - * @notice Return the total no. of tokens sold for ETH + * @notice Return the total no. of tokens sold for the given fund raise type + * param _fundRaiseType The fund raising currency (e.g. ETH, POLY, SC) to calculate sold tokens for * @return uint256 Total number of tokens sold for ETH */ - function getTokensSoldFor(FundRaiseType _fundRaiseType) public view returns (uint256) { - uint256 tokensSold; - for (uint8 i = 0; i < mintedPerTier[uint8(_fundRaiseType)].length; i++) { - tokensSold = tokensSold.add(mintedPerTier[uint8(_fundRaiseType)][i]); + function getTokensSoldFor(FundRaiseType _fundRaiseType) external view returns (uint256 tokensSold) { + for (uint256 i = 0; i < tiers.length; i++) { + tokensSold = tokensSold.add(tiers[i].minted[uint8(_fundRaiseType)]); } + } + + /** + * @notice Return array of minted tokens in each fund raise type for given tier + * param _tier The tier to return minted tokens for + * @return uint256[] array of minted tokens in each fund raise type + */ + function getTokensMintedByTier(uint256 _tier) external view returns(uint256[] memory) { + uint256[] memory tokensMinted = new uint256[](3); + tokensMinted[0] = tiers[_tier].minted[uint8(FundRaiseType.ETH)]; + tokensMinted[1] = tiers[_tier].minted[uint8(FundRaiseType.POLY)]; + tokensMinted[2] = tiers[_tier].minted[uint8(FundRaiseType.SC)]; + return tokensMinted; + } + + /** + * @notice Return the total no. of tokens sold in a given tier + * param _tier The tier to calculate sold tokens for + * @return uint256 Total number of tokens sold in the tier + */ + function getTokensSoldByTier(uint256 _tier) external view returns (uint256) { + uint256 tokensSold; + tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.ETH)]); + tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.POLY)]); + tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.SC)]); return tokensSold; } @@ -652,27 +676,70 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @notice Return the total no. of tiers * @return uint256 Total number of tiers */ - function getNumberOfTiers() public view returns (uint256) { - return tokensPerTierTotal.length; + function getNumberOfTiers() external view returns (uint256) { + return tiers.length; + } + + /** + * @notice Return the usd tokens accepted by the STO + * @return address[] usd tokens + */ + function getUsdTokens() external view returns (address[] memory) { + return usdTokens; } /** * @notice Return the permissions flag that are associated with STO */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](0); + function getPermissions() public view returns(bytes32[] memory allPermissions) { return allPermissions; } + /** + * @notice Return the STO details + * @return Unixtimestamp at which offering gets start. + * @return Unixtimestamp at which offering ends. + * @return Currently active tier + * @return Array of Number of tokens this STO will be allowed to sell at different tiers. + * @return Array Rate at which tokens are sold at different tiers + * @return Amount of funds raised + * @return Number of individual investors this STO have. + * @return Amount of tokens sold. + * @return Array of bools to show if funding is allowed in ETH, POLY, SC respectively + */ + function getSTODetails() external view returns(uint256, uint256, uint256, uint256[] memory, uint256[] memory, uint256, uint256, uint256, bool[] memory) { + uint256[] memory cap = new uint256[](tiers.length); + uint256[] memory rate = new uint256[](tiers.length); + for(uint256 i = 0; i < tiers.length; i++) { + cap[i] = tiers[i].tokenTotal; + rate[i] = tiers[i].rate; + } + bool[] memory _fundRaiseTypes = new bool[](3); + _fundRaiseTypes[0] = fundRaiseTypes[uint8(FundRaiseType.ETH)]; + _fundRaiseTypes[1] = fundRaiseTypes[uint8(FundRaiseType.POLY)]; + _fundRaiseTypes[2] = fundRaiseTypes[uint8(FundRaiseType.SC)]; + return ( + startTime, + endTime, + currentTier, + cap, + rate, + fundsRaisedUSD, + investorCount, + getTokensSold(), + _fundRaiseTypes + ); + } + /** * @notice This function returns the signature of configure function * @return bytes4 Configure function signature */ - function getInitFunction() public pure returns (bytes4) { - return 0xb0ff041e; + function getInitFunction() public pure returns(bytes4) { + return this.configure.selector; } - function _getOracle(bytes32 _currency, bytes32 _denominatedCurrency) internal view returns (address) { + function _getOracle(bytes32 _currency, bytes32 _denominatedCurrency) internal view returns(address) { return PolymathRegistry(RegistryUpdater(securityToken).polymathRegistry()).getAddress(oracleKeys[_currency][_denominatedCurrency]); } diff --git a/contracts/modules/STO/USDTieredSTOFactory.sol b/contracts/modules/STO/USDTieredSTOFactory.sol index adc3ba628..cec5420c0 100644 --- a/contracts/modules/STO/USDTieredSTOFactory.sol +++ b/contracts/modules/STO/USDTieredSTOFactory.sol @@ -1,6 +1,7 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "../../interfaces/IUSDTieredSTOProxy.sol"; +import "../../interfaces/IBoot.sol"; +import "../../proxy/USDTieredSTOProxy.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; @@ -8,19 +9,27 @@ import "../../libraries/Util.sol"; * @title Factory for deploying CappedSTO module */ contract USDTieredSTOFactory is ModuleFactory { - - address public USDTieredSTOProxyAddress; + address public logicContract; /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost, address _proxyFactoryAddress) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - require(_proxyFactoryAddress != address(0), "0x address is not allowed"); - USDTieredSTOProxyAddress = _proxyFactoryAddress; - version = "1.0.0"; + require(_logicContract != address(0), "0x address is not allowed"); + logicContract = _logicContract; + version = "2.1.0"; name = "USDTieredSTO"; title = "USD Tiered STO"; /*solium-disable-next-line max-len*/ @@ -29,29 +38,28 @@ contract USDTieredSTOFactory is ModuleFactory { compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); } - /** + /** * @notice Used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); - require(USDTieredSTOProxyAddress != address(0), "Proxy contract should be pre-set"); - //Check valid bytes - can only call module init function - address usdTieredSTO = IUSDTieredSTOProxy(USDTieredSTOProxyAddress).deploySTO(msg.sender, address(polyToken), address(this)); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address usdTieredSTO = address(new USDTieredSTOProxy(msg.sender, polyToken, logicContract)); //Checks that _data is valid (not calling anything it shouldn't) - require(Util.getSig(_data) == IUSDTieredSTOProxy(USDTieredSTOProxyAddress).getInitFunction(usdTieredSTO), "Invalid data"); + require(Util.getSig(_data) == IBoot(usdTieredSTO).getInitFunction(), "Invalid data"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(usdTieredSTO).call(_data), "Unsuccessfull call"); + (success, ) = usdTieredSTO.call(_data); + require(success, "Unsuccessfull call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(usdTieredSTO, getName(), address(this), msg.sender, setupCost, now); - return address(usdTieredSTO); + emit GenerateModuleFromFactory(usdTieredSTO, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return usdTieredSTO; } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 3; return res; @@ -60,14 +68,14 @@ contract USDTieredSTOFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Initialises a USD tiered STO."; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](4); availableTags[0] = "USD"; availableTags[1] = "Tiered"; diff --git a/contracts/modules/TransferManager/CountTransferManager.sol b/contracts/modules/TransferManager/CountTransferManager.sol index b54870251..b4c1cb5d8 100644 --- a/contracts/modules/TransferManager/CountTransferManager.sol +++ b/contracts/modules/TransferManager/CountTransferManager.sol @@ -1,56 +1,72 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ITransferManager.sol"; +import "./TransferManager.sol"; +import "../../storage/modules/TransferManager/CountTransferManagerStorage.sol"; +import "../../interfaces/ISecurityToken.sol"; /** * @title Transfer Manager for limiting maximum number of token holders */ -contract CountTransferManager is ITransferManager { - - // The maximum number of concurrent token holders - uint256 public maxHolderCount; - - bytes32 public constant ADMIN = "ADMIN"; +contract CountTransferManager is CountTransferManagerStorage, TransferManager { event ModifyHolderCount(uint256 _oldHolderCount, uint256 _newHolderCount); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) - public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** @notice Used to verify the transfer transaction and prevent a transfer if it passes the allowed amount of token holders + * @param _from Address of the sender * @param _to Address of the receiver + * @param _amount Amount to send */ - function verifyTransfer( - address /* _from */, + function executeTransfer( + address _from, address _to, - uint256 /* _amount */, - bytes /* _data */, - bool /* _isTransfer */ + uint256 _amount, + bytes calldata _data ) - public + external returns(Result) + { + (Result success,) = verifyTransfer(_from, _to, _amount, _data); + return success; + } + + /** + * @notice Used to verify the transfer transaction and prevent a transfer if it passes the allowed amount of token holders + * @param _from Address of the sender + * @param _to Address of the receiver + * @param _amount Amount to send + */ + function verifyTransfer( + address _from, + address _to, + uint256 _amount, + bytes memory /* _data */ + ) + public + view + returns(Result, bytes32) { if (!paused) { - if (maxHolderCount < ISecurityToken(securityToken).getInvestorCount()) { + if (maxHolderCount < ISecurityToken(securityToken).holderCount()) { // Allow transfers to existing maxHolders - if (ISecurityToken(securityToken).balanceOf(_to) != 0) { - return Result.NA; + if (ISecurityToken(securityToken).balanceOf(_to) != 0 || ISecurityToken(securityToken).balanceOf(_from) == _amount) { + return (Result.NA, bytes32(0)); } - return Result.INVALID; + return (Result.INVALID, bytes32(uint256(address(this)) << 96)); } - return Result.NA; + return (Result.NA, bytes32(0)); } - return Result.NA; + return (Result.NA, bytes32(0)); } + /** * @notice Used to initialize the variables of the contract * @param _maxHolderCount Maximum no. of holders this module allows the SecurityToken to have @@ -71,14 +87,21 @@ contract CountTransferManager is ITransferManager { /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(keccak256("configure(uint256)")); } + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + /** * @notice Returns the permissions flag that are associated with CountTransferManager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = ADMIN; return allPermissions; diff --git a/contracts/modules/TransferManager/CountTransferManagerFactory.sol b/contracts/modules/TransferManager/CountTransferManagerFactory.sol index d9b1328f7..06873ac5a 100644 --- a/contracts/modules/TransferManager/CountTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/CountTransferManagerFactory.sol @@ -1,27 +1,40 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./CountTransferManager.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; +import "../../proxy/CountTransferManagerProxy.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying CountTransferManager module */ contract CountTransferManagerFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid address"); + version = "2.1.0"; name = "CountTransferManager"; title = "Count Transfer Manager"; description = "Restrict the number of investors"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** @@ -29,23 +42,23 @@ contract CountTransferManagerFactory is ModuleFactory { * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom due to insufficent Allowance provided"); - CountTransferManager countTransferManager = new CountTransferManager(msg.sender, address(polyToken)); - require(Util.getSig(_data) == countTransferManager.getInitFunction(), "Provided data is not valid"); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address countTransferManager = address(new CountTransferManagerProxy(msg.sender, polyToken, logicContract)); + require(Util.getSig(_data) == IBoot(countTransferManager).getInitFunction(), "Provided data is not valid"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(countTransferManager).call(_data), "Unsuccessful call"); + (success, ) = countTransferManager.call(_data); + require(success, "Unsuccessful call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(countTransferManager), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(address(countTransferManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return address(countTransferManager); - } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 2; return res; @@ -54,14 +67,14 @@ contract CountTransferManagerFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Allows an issuer to restrict the total number of non-zero token holders"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "Count"; availableTags[1] = "Transfer Restriction"; diff --git a/contracts/modules/TransferManager/GeneralTransferManager.sol b/contracts/modules/TransferManager/GeneralTransferManager.sol index 814d54d7b..f8731d46d 100644 --- a/contracts/modules/TransferManager/GeneralTransferManager.sol +++ b/contracts/modules/TransferManager/GeneralTransferManager.sol @@ -1,46 +1,18 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ITransferManager.sol"; +import "./TransferManager.sol"; +import "../../libraries/Encoder.sol"; +import "../../libraries/VersionUtils.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; +import "../../storage/modules/TransferManager/GeneralTransferManagerStorage.sol"; /** * @title Transfer Manager module for core transfer validation functionality */ -contract GeneralTransferManager is ITransferManager { - +contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManager { using SafeMath for uint256; - - //Address from which issuances come - address public issuanceAddress = address(0); - - //Address which can sign whitelist changes - address public signingAddress = address(0); - - bytes32 public constant WHITELIST = "WHITELIST"; - bytes32 public constant FLAGS = "FLAGS"; - - //from and to timestamps that an investor can send / receive tokens respectively - struct TimeRestriction { - uint256 fromTime; - uint256 toTime; - uint256 expiryTime; - bool canBuyFromSTO; - } - - // An address can only send / receive tokens once their corresponding uint256 > block.number - // (unless allowAllTransfers == true or allowAllWhitelistTransfers == true) - mapping (address => TimeRestriction) public whitelist; - // Map of used nonces by customer - mapping(address => mapping(uint256 => bool)) public nonceMap; - - //If true, there are no transfer restrictions, for any addresses - bool public allowAllTransfers = false; - //If true, time lock is ignored for transfers (address must still be on whitelist) - bool public allowAllWhitelistTransfers = false; - //If true, time lock is ignored for issuances (address must still be on whitelist) - bool public allowAllWhitelistIssuances = true; - //If true, time lock is ignored for burn transactions - bool public allowAllBurnTransfers = false; + using ECDSA for bytes32; // Emit when Issuance address get changed event ChangeIssuanceAddress(address _issuanceAddress); @@ -55,34 +27,57 @@ contract GeneralTransferManager is ITransferManager { // Emit when there is change in the flag variable called signingAddress event ChangeSigningAddress(address _signingAddress); // Emit when investor details get modified related to their whitelisting - event ModifyWhitelist( - address _investor, + event ChangeDefaults(uint64 _defaultFromTime, uint64 _defaultToTime); + + // _fromTime is the time from which the _investor can send tokens + // _toTime is the time from which the _investor can receive tokens + // if allowAllWhitelistIssuances is TRUE, then _toTime is ignored when receiving tokens from the issuance address + // if allowAllWhitelistTransfers is TRUE, then _toTime and _fromTime is ignored when sending or receiving tokens + // in any case, any investor sending or receiving tokens, must have a _expiryTime in the future + event ModifyKYCData( + address indexed _investor, uint256 _dateAdded, - address _addedBy, + address indexed _addedBy, uint256 _fromTime, uint256 _toTime, - uint256 _expiryTime, - bool _canBuyFromSTO + uint256 _expiryTime + ); + + event ModifyInvestorFlag( + address indexed _investor, + uint8 indexed _flag, + bool _value ); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) + constructor(address _securityToken, address _polyToken) public - Module(_securityToken, _polyAddress) + Module(_securityToken, _polyToken) { + } /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(0); } + /** + * @notice Used to change the default times used when fromTime / toTime are zero + * @param _defaultFromTime default for zero fromTime + * @param _defaultToTime default for zero toTime + */ + function changeDefaults(uint64 _defaultFromTime, uint64 _defaultToTime) public withPerm(FLAGS) { + defaults.fromTime = _defaultFromTime; + defaults.toTime = _defaultToTime; + emit ChangeDefaults(_defaultFromTime, _defaultToTime); + } + /** * @notice Used to change the Issuance Address * @param _issuanceAddress new address for the issuance @@ -155,78 +150,195 @@ contract GeneralTransferManager is ITransferManager { * @param _from Address of the sender * @param _to Address of the receiver */ - function verifyTransfer(address _from, address _to, uint256 /*_amount*/, bytes /* _data */, bool /* _isTransfer */) public returns(Result) { + function executeTransfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external returns(Result) { + (Result success,) = verifyTransfer(_from, _to, _amount, _data); + return success; + } + + /** + * @notice Default implementation of verifyTransfer used by SecurityToken + * If the transfer request comes from the STO, it only checks that the investor is in the whitelist + * If the transfer request comes from a token holder, it checks that: + * a) Both are on the whitelist + * b) Seller's sale lockup period is over + * c) Buyer's purchase lockup is over + * @param _from Address of the sender + * @param _to Address of the receiver + */ + function verifyTransfer( + address _from, + address _to, + uint256, /*_amount*/ + bytes memory /* _data */ + ) + public + view + returns(Result, bytes32) + { + Result success; if (!paused) { + uint64 fromTime; + uint64 fromExpiry; + uint64 toExpiry; + uint64 toTime; if (allowAllTransfers) { //All transfers allowed, regardless of whitelist - return Result.VALID; + return (Result.VALID, getAddressBytes32()); } if (allowAllBurnTransfers && (_to == address(0))) { - return Result.VALID; + return (Result.VALID, getAddressBytes32()); } + + (fromTime, fromExpiry, toTime, toExpiry) = _getValuesForTransfer(_from, _to); + if (allowAllWhitelistTransfers) { //Anyone on the whitelist can transfer, regardless of time - return (_onWhitelist(_to) && _onWhitelist(_from)) ? Result.VALID : Result.NA; + success = (_validExpiry(toExpiry) && _validExpiry(fromExpiry)) ? Result.VALID : Result.NA; + return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0)); } - if (allowAllWhitelistIssuances && _from == issuanceAddress) { - if (!whitelist[_to].canBuyFromSTO && _isSTOAttached()) { - return Result.NA; + // Using the local variables to avoid the stack too deep error + (fromTime, toTime) = _adjustTimes(fromTime, toTime); + if (_from == issuanceAddress) { + // if allowAllWhitelistIssuances is true, so time stamp ignored + if (allowAllWhitelistIssuances) { + success = _validExpiry(toExpiry) ? Result.VALID : Result.NA; + return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0)); + } else { + success = (_validExpiry(toExpiry) && _validLockTime(toTime)) ? Result.VALID : Result.NA; + return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0)); } - return _onWhitelist(_to) ? Result.VALID : Result.NA; } + //Anyone on the whitelist can transfer provided the blocknumber is large enough /*solium-disable-next-line security/no-block-members*/ - return ((_onWhitelist(_from) && whitelist[_from].fromTime <= now) && - (_onWhitelist(_to) && whitelist[_to].toTime <= now)) ? Result.VALID : Result.NA; /*solium-disable-line security/no-block-members*/ + success = (_validExpiry(fromExpiry) && _validLockTime(fromTime) && _validExpiry(toExpiry) && + _validLockTime(toTime)) ? Result.VALID : Result.NA; /*solium-disable-line security/no-block-members*/ + return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0)); } - return Result.NA; + return (Result.NA, bytes32(0)); } + /** - * @notice Adds or removes addresses from the whitelist. + * @notice Add or remove KYC info of an investor. * @param _investor is the address to whitelist * @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens * @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others * @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC - * @param _canBuyFromSTO is used to know whether the investor is restricted investor or not. */ - function modifyWhitelist( + function modifyKYCData( address _investor, uint256 _fromTime, uint256 _toTime, - uint256 _expiryTime, - bool _canBuyFromSTO + uint256 _expiryTime ) public withPerm(WHITELIST) { - //Passing a _time == 0 into this function, is equivalent to removing the _investor from the whitelist - whitelist[_investor] = TimeRestriction(_fromTime, _toTime, _expiryTime, _canBuyFromSTO); - /*solium-disable-next-line security/no-block-members*/ - emit ModifyWhitelist(_investor, now, msg.sender, _fromTime, _toTime, _expiryTime, _canBuyFromSTO); + _modifyKYCData(_investor, _fromTime, _toTime, _expiryTime); + } + + function _modifyKYCData(address _investor, uint256 _fromTime, uint256 _toTime, uint256 _expiryTime) internal { + require(_investor != address(0), "Invalid investor"); + IDataStore dataStore = IDataStore(getDataStore()); + if (!_isExistingInvestor(_investor, dataStore)) { + dataStore.insertAddress(INVESTORSKEY, _investor); + } + uint256 _data = VersionUtils.packKYC(uint64(_fromTime), uint64(_toTime), uint64(_expiryTime), uint8(1)); + dataStore.setUint256(_getKey(WHITELIST, _investor), _data); + emit ModifyKYCData(_investor, now, msg.sender, _fromTime, _toTime, _expiryTime); } /** - * @notice Adds or removes addresses from the whitelist. - * @param _investors List of the addresses to whitelist - * @param _fromTimes An array of the moment when the sale lockup period ends and the investor can freely sell his tokens - * @param _toTimes An array of the moment when the purchase lockup period ends and the investor can freely purchase tokens from others - * @param _expiryTimes An array of the moment till investors KYC will be validated. After that investor need to do re-KYC - * @param _canBuyFromSTO An array of boolean values + * @notice Add or remove KYC info of an investor. + * @param _investors is the address to whitelist + * @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens + * @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others + * @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC */ - function modifyWhitelistMulti( - address[] _investors, - uint256[] _fromTimes, - uint256[] _toTimes, - uint256[] _expiryTimes, - bool[] _canBuyFromSTO - ) public withPerm(WHITELIST) { - require(_investors.length == _fromTimes.length, "Mismatched input lengths"); - require(_fromTimes.length == _toTimes.length, "Mismatched input lengths"); - require(_toTimes.length == _expiryTimes.length, "Mismatched input lengths"); - require(_canBuyFromSTO.length == _toTimes.length, "Mismatched input length"); + function modifyKYCDataMulti( + address[] memory _investors, + uint256[] memory _fromTime, + uint256[] memory _toTime, + uint256[] memory _expiryTime + ) + public + withPerm(WHITELIST) + { + require( + _investors.length == _fromTime.length && + _fromTime.length == _toTime.length && + _toTime.length == _expiryTime.length, + "Mismatched input lengths" + ); for (uint256 i = 0; i < _investors.length; i++) { - modifyWhitelist(_investors[i], _fromTimes[i], _toTimes[i], _expiryTimes[i], _canBuyFromSTO[i]); + _modifyKYCData(_investors[i], _fromTime[i], _toTime[i], _expiryTime[i]); + } + } + + /** + * @notice Used to modify investor Flag. + * @dev Flags are properties about investors that can be true or false like isAccredited + * @param _investor is the address of the investor. + * @param _flag index of flag to change. flag is used to know specifics about investor like isAccredited. + * @param _value value of the flag. a flag can be true or false. + */ + function modifyInvestorFlag( + address _investor, + uint8 _flag, + bool _value + ) + public + withPerm(WHITELIST) + { + _modifyInvestorFlag(_investor, _flag, _value); + } + + + function _modifyInvestorFlag(address _investor, uint8 _flag, bool _value) internal { + require(_investor != address(0), "Invalid investor"); + IDataStore dataStore = IDataStore(getDataStore()); + if (!_isExistingInvestor(_investor, dataStore)) { + dataStore.insertAddress(INVESTORSKEY, _investor); + //KYC data can not be present if added is false and hence we can set packed KYC as uint256(1) to set added as true + dataStore.setUint256(_getKey(WHITELIST, _investor), uint256(1)); + } + //NB Flags are packed together in a uint256 to save gas. We can have a maximum of 256 flags. + uint256 flags = dataStore.getUint256(_getKey(INVESTORFLAGS, _investor)); + if (_value) + flags = flags | (ONE << _flag); + else + flags = flags & ~(ONE << _flag); + dataStore.setUint256(_getKey(INVESTORFLAGS, _investor), flags); + emit ModifyInvestorFlag(_investor, _flag, _value); + } + + /** + * @notice Used to modify investor data. + * @param _investors List of the addresses to modify data about. + * @param _flag index of flag to change. flag is used to know specifics about investor like isAccredited. + * @param _value value of the flag. a flag can be true or false. + */ + function modifyInvestorFlagMulti( + address[] memory _investors, + uint8[] memory _flag, + bool[] memory _value + ) + public + withPerm(WHITELIST) + { + require( + _investors.length == _flag.length && + _flag.length == _value.length, + "Mismatched input lengths" + ); + for (uint256 i = 0; i < _investors.length; i++) { + _modifyInvestorFlag(_investors[i], _flag[i], _value[i]); } } @@ -236,27 +348,23 @@ contract GeneralTransferManager is ITransferManager { * @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens * @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others * @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC - * @param _canBuyFromSTO is used to know whether the investor is restricted investor or not. * @param _validFrom is the time that this signature is valid from * @param _validTo is the time that this signature is valid until * @param _nonce nonce of signature (avoid replay attack) - * @param _v issuer signature - * @param _r issuer signature - * @param _s issuer signature + * @param _signature issuer signature */ - function modifyWhitelistSigned( + function modifyKYCDataSigned( address _investor, uint256 _fromTime, uint256 _toTime, uint256 _expiryTime, - bool _canBuyFromSTO, uint256 _validFrom, uint256 _validTo, uint256 _nonce, - uint8 _v, - bytes32 _r, - bytes32 _s - ) public { + bytes memory _signature + ) + public + { /*solium-disable-next-line security/no-block-members*/ require(_validFrom <= now, "ValidFrom is too early"); /*solium-disable-next-line security/no-block-members*/ @@ -264,51 +372,176 @@ contract GeneralTransferManager is ITransferManager { require(!nonceMap[_investor][_nonce], "Already used signature"); nonceMap[_investor][_nonce] = true; bytes32 hash = keccak256( - abi.encodePacked(this, _investor, _fromTime, _toTime, _expiryTime, _canBuyFromSTO, _validFrom, _validTo, _nonce) + abi.encodePacked(this, _investor, _fromTime, _toTime, _expiryTime, _validFrom, _validTo, _nonce) ); - _checkSig(hash, _v, _r, _s); - //Passing a _time == 0 into this function, is equivalent to removing the _investor from the whitelist - whitelist[_investor] = TimeRestriction(_fromTime, _toTime, _expiryTime, _canBuyFromSTO); - /*solium-disable-next-line security/no-block-members*/ - emit ModifyWhitelist(_investor, now, msg.sender, _fromTime, _toTime, _expiryTime, _canBuyFromSTO); + _checkSig(hash, _signature); + _modifyKYCData(_investor, _fromTime, _toTime, _expiryTime); } /** * @notice Used to verify the signature */ - function _checkSig(bytes32 _hash, uint8 _v, bytes32 _r, bytes32 _s) internal view { + function _checkSig(bytes32 _hash, bytes memory _signature) internal view { //Check that the signature is valid //sig should be signing - _investor, _fromTime, _toTime & _expiryTime and be signed by the issuer address - address signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), _v, _r, _s); + address signer = _hash.toEthSignedMessageHash().recover(_signature); require(signer == Ownable(securityToken).owner() || signer == signingAddress, "Incorrect signer"); } /** - * @notice Internal function used to check whether the investor is in the whitelist or not - & also checks whether the KYC of investor get expired or not - * @param _investor Address of the investor + * @notice Internal function used to check whether the KYC of investor is valid + * @param _expiryTime Expiry time of the investor + */ + function _validExpiry(uint64 _expiryTime) internal view returns(bool) { + return (_expiryTime >= uint64(now)); /*solium-disable-line security/no-block-members*/ + } + + /** + * @notice Internal function used to check whether the lock time of investor is valid + * @param _lockTime Lock time of the investor */ - function _onWhitelist(address _investor) internal view returns(bool) { - return (((whitelist[_investor].fromTime != 0) || (whitelist[_investor].toTime != 0)) && - (whitelist[_investor].expiryTime >= now)); /*solium-disable-line security/no-block-members*/ + function _validLockTime(uint64 _lockTime) internal view returns(bool) { + return (_lockTime <= uint64(now)); /*solium-disable-line security/no-block-members*/ } /** - * @notice Internal function use to know whether the STO is attached or not + * @notice Internal function to adjust times using default values */ - function _isSTOAttached() internal view returns(bool) { - bool attached = ISecurityToken(securityToken).getModulesByType(3).length > 0; - return attached; + function _adjustTimes(uint64 _fromTime, uint64 _toTime) internal view returns(uint64, uint64) { + uint64 adjustedFromTime = _fromTime; + uint64 adjustedToTime = _toTime; + if (_fromTime == 0) { + adjustedFromTime = defaults.fromTime; + } + if (_toTime == 0) { + adjustedToTime = defaults.toTime; + } + return (adjustedFromTime, adjustedToTime); + } + + function _getKey(bytes32 _key1, address _key2) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(_key1, _key2))); + } + + function _getKYCValues(address _investor, IDataStore dataStore) internal view returns( + uint64 fromTime, + uint64 toTime, + uint64 expiryTime, + uint8 added + ) + { + uint256 data = dataStore.getUint256(_getKey(WHITELIST, _investor)); + (fromTime, toTime, expiryTime, added) = VersionUtils.unpackKYC(data); + } + + function _isExistingInvestor(address _investor, IDataStore dataStore) internal view returns(bool) { + uint256 data = dataStore.getUint256(_getKey(WHITELIST, _investor)); + //extracts `added` from packed `_whitelistData` + return uint8(data) == 0 ? false : true; + } + + function _getValuesForTransfer(address _from, address _to) internal view returns(uint64 fromTime, uint64 fromExpiry, uint64 toTime, uint64 toExpiry) { + IDataStore dataStore = IDataStore(getDataStore()); + (fromTime, , fromExpiry, ) = _getKYCValues(_from, dataStore); + (, toTime, toExpiry, ) = _getKYCValues(_to, dataStore); } + /** + * @dev Returns list of all investors + */ + function getAllInvestors() public view returns(address[] memory investors) { + IDataStore dataStore = IDataStore(getDataStore()); + investors = dataStore.getAddressArray(INVESTORSKEY); + } + + /** + * @dev Returns list of investors in a range + */ + function getInvestors(uint256 _fromIndex, uint256 _toIndex) public view returns(address[] memory investors) { + IDataStore dataStore = IDataStore(getDataStore()); + investors = dataStore.getAddressArrayElements(INVESTORSKEY, _fromIndex, _toIndex); + } + + function getAllInvestorFlags() public view returns(address[] memory investors, uint256[] memory flags) { + investors = getAllInvestors(); + flags = new uint256[](investors.length); + for (uint256 i = 0; i < investors.length; i++) { + flags[i] = _getInvestorFlags(investors[i]); + } + } + + function getInvestorFlag(address _investor, uint8 _flag) public view returns(bool value) { + uint256 flag = (_getInvestorFlags(_investor) >> _flag) & ONE; + value = flag > 0 ? true : false; + } + + function getInvestorFlags(address _investor) public view returns(uint256 flags) { + flags = _getInvestorFlags(_investor); + } + + function _getInvestorFlags(address _investor) public view returns(uint256 flags) { + IDataStore dataStore = IDataStore(getDataStore()); + flags = dataStore.getUint256(_getKey(INVESTORFLAGS, _investor)); + } + + /** + * @dev Returns list of all investors data + */ + function getAllKYCData() external view returns( + address[] memory investors, + uint256[] memory fromTimes, + uint256[] memory toTimes, + uint256[] memory expiryTimes + ) { + investors = getAllInvestors(); + (fromTimes, toTimes, expiryTimes) = _kycData(investors); + return (investors, fromTimes, toTimes, expiryTimes); + } + + /** + * @dev Returns list of specified investors data + */ + function getKYCData(address[] calldata _investors) external view returns( + uint256[] memory, + uint256[] memory, + uint256[] memory + ) { + return _kycData(_investors); + } + + function _kycData(address[] memory _investors) internal view returns( + uint256[] memory, + uint256[] memory, + uint256[] memory + ) { + uint256[] memory fromTimes = new uint256[](_investors.length); + uint256[] memory toTimes = new uint256[](_investors.length); + uint256[] memory expiryTimes = new uint256[](_investors.length); + for (uint256 i = 0; i < _investors.length; i++) { + (fromTimes[i], toTimes[i], expiryTimes[i], ) = _getKYCValues(_investors[i], IDataStore(getDataStore())); + } + return (fromTimes, toTimes, expiryTimes); + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + /** * @notice Return the permissions flag that are associated with general trnasfer manager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](2); allPermissions[0] = WHITELIST; allPermissions[1] = FLAGS; return allPermissions; } + function getAddressBytes32() public view returns(bytes32) { + return bytes32(uint256(address(this)) << 96); + } + } diff --git a/contracts/modules/TransferManager/GeneralTransferManagerFactory.sol b/contracts/modules/TransferManager/GeneralTransferManagerFactory.sol index c15eb52aa..78de01e46 100644 --- a/contracts/modules/TransferManager/GeneralTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/GeneralTransferManagerFactory.sol @@ -1,56 +1,71 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./GeneralTransferManager.sol"; +import "../../proxy/GeneralTransferManagerProxy.sol"; import "../ModuleFactory.sol"; /** * @title Factory for deploying GeneralTransferManager module */ contract GeneralTransferManagerFactory is ModuleFactory { + address public logicContract; /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid logic contract"); + version = "2.1.0"; name = "GeneralTransferManager"; title = "General Transfer Manager"; description = "Manage transfers using a time based whitelist"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } - - /** + /** * @notice Used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - address generalTransferManager = new GeneralTransferManager(msg.sender, address(polyToken)); + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + GeneralTransferManagerProxy generalTransferManager = new GeneralTransferManagerProxy(msg.sender, polyToken, logicContract); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(generalTransferManager), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(address(generalTransferManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return address(generalTransferManager); } - /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { - uint8[] memory res = new uint8[](1); + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](2); res[0] = 2; + res[1] = 6; return res; } /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { /*solium-disable-next-line max-len*/ return "Allows an issuer to maintain a time based whitelist of authorised token holders.Addresses are added via modifyWhitelist and take a fromTime (the time from which they can send tokens) and a toTime (the time from which they can receive tokens). There are additional flags, allowAllWhitelistIssuances, allowAllWhitelistTransfers & allowAllTransfers which allow you to set corresponding contract level behaviour. Init function takes no parameters."; } @@ -58,12 +73,11 @@ contract GeneralTransferManagerFactory is ModuleFactory { /** * @notice Get the tags related to the module factory */ - function getTags() public view returns(bytes32[]) { + function getTags() public view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "General"; availableTags[1] = "Transfer Restriction"; return availableTags; } - } diff --git a/contracts/modules/TransferManager/ITransferManager.sol b/contracts/modules/TransferManager/ITransferManager.sol deleted file mode 100644 index a8cfa4475..000000000 --- a/contracts/modules/TransferManager/ITransferManager.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.24; - -import "../../Pausable.sol"; -import "../Module.sol"; - -/** - * @title Interface to be implemented by all Transfer Manager modules - * @dev abstract contract - */ -contract ITransferManager is Module, Pausable { - - //If verifyTransfer returns: - // FORCE_VALID, the transaction will always be valid, regardless of other TM results - // INVALID, then the transfer should not be allowed regardless of other TM results - // VALID, then the transfer is valid for this TM - // NA, then the result from this TM is ignored - enum Result {INVALID, NA, VALID, FORCE_VALID} - - function verifyTransfer(address _from, address _to, uint256 _amount, bytes _data, bool _isTransfer) public returns(Result); - - function unpause() public onlyOwner { - super._unpause(); - } - - function pause() public onlyOwner { - super._pause(); - } -} diff --git a/contracts/modules/TransferManager/ManualApprovalTransferManager.sol b/contracts/modules/TransferManager/ManualApprovalTransferManager.sol index fd888680a..6501fc9a6 100644 --- a/contracts/modules/TransferManager/ManualApprovalTransferManager.sol +++ b/contracts/modules/TransferManager/ManualApprovalTransferManager.sol @@ -1,52 +1,31 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ITransferManager.sol"; +import "./TransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../storage/modules/TransferManager/ManualApprovalTransferManagerStorage.sol"; /** - * @title Transfer Manager module for manually approving or blocking transactions between accounts + * @title Transfer Manager module for manually approving transactions between accounts */ -contract ManualApprovalTransferManager is ITransferManager { +contract ManualApprovalTransferManager is ManualApprovalTransferManagerStorage, TransferManager { using SafeMath for uint256; - //Address from which issuances come - address public issuanceAddress = address(0); - - //Address which can sign whitelist changes - address public signingAddress = address(0); - - bytes32 public constant TRANSFER_APPROVAL = "TRANSFER_APPROVAL"; - - //Manual approval is an allowance (that has been approved) with an expiry time - struct ManualApproval { - uint256 allowance; - uint256 expiryTime; - } - - //Manual blocking allows you to specify a list of blocked address pairs with an associated expiry time for the block - struct ManualBlocking { - uint256 expiryTime; - } - - //Store mappings of address => address with ManualApprovals - mapping (address => mapping (address => ManualApproval)) public manualApprovals; - - //Store mappings of address => address with ManualBlockings - mapping (address => mapping (address => ManualBlocking)) public manualBlockings; - event AddManualApproval( address indexed _from, address indexed _to, uint256 _allowance, uint256 _expiryTime, + bytes32 _description, address indexed _addedBy ); - event AddManualBlocking( + event ModifyManualApproval( address indexed _from, address indexed _to, uint256 _expiryTime, - address indexed _addedBy + uint256 _allowance, + bytes32 _description, + address indexed _edittedBy ); event RevokeManualApproval( @@ -55,87 +34,230 @@ contract ManualApprovalTransferManager is ITransferManager { address indexed _addedBy ); - event RevokeManualBlocking( - address indexed _from, - address indexed _to, - address indexed _addedBy - ); - /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) - public - Module(_securityToken, _polyAddress) - { + constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { + } /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(0); } - /** @notice Used to verify the transfer transaction and allow a manually approved transqaction to bypass other restrictions + /** + * @notice Used to verify the transfer transaction and allow a manually approved transqaction to bypass other restrictions * @param _from Address of the sender * @param _to Address of the receiver * @param _amount The amount of tokens to transfer - * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable */ - function verifyTransfer(address _from, address _to, uint256 _amount, bytes /* _data */, bool _isTransfer) public returns(Result) { - // function must only be called by the associated security token if _isTransfer == true - require(_isTransfer == false || msg.sender == securityToken, "Sender is not the owner"); - // manual blocking takes precidence over manual approval - if (!paused) { - /*solium-disable-next-line security/no-block-members*/ - if (manualBlockings[_from][_to].expiryTime >= now) { - return Result.INVALID; - } - /*solium-disable-next-line security/no-block-members*/ - if ((manualApprovals[_from][_to].expiryTime >= now) && (manualApprovals[_from][_to].allowance >= _amount)) { - if (_isTransfer) { - manualApprovals[_from][_to].allowance = manualApprovals[_from][_to].allowance.sub(_amount); - } - return Result.VALID; + function executeTransfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) + external + onlySecurityToken + returns(Result) + { + + (Result success, bytes32 esc) = verifyTransfer(_from, _to, _amount, _data); + if (esc != bytes32(0)) { + uint256 index = approvalIndex[_from][_to] - 1; + ManualApproval storage approval = approvals[index]; + approval.allowance = approval.allowance.sub(_amount); + } + return (success); + } + + + /** + * @notice Used to verify the transfer transaction and allow a manually approved transqaction to bypass other restrictions + * @param _from Address of the sender + * @param _to Address of the receiver + * @param _amount The amount of tokens to transfer + */ + function verifyTransfer( + address _from, + address _to, + uint256 _amount, + bytes memory /* _data */ + ) + public + view + returns(Result, bytes32) + { + if (!paused && approvalIndex[_from][_to] != 0) { + uint256 index = approvalIndex[_from][_to] - 1; + ManualApproval memory approval = approvals[index]; + if ((approval.expiryTime >= now) && (approval.allowance >= _amount)) { + return (Result.VALID, bytes32(uint256(address(this)) << 96)); } } - return Result.NA; + return (Result.NA, bytes32(0)); } + /** * @notice Adds a pair of addresses to manual approvals * @param _from is the address from which transfers are approved * @param _to is the address to which transfers are approved * @param _allowance is the approved amount of tokens * @param _expiryTime is the time until which the transfer is allowed + * @param _description Description about the manual approval */ - function addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); + function addManualApproval( + address _from, + address _to, + uint256 _allowance, + uint256 _expiryTime, + bytes32 _description + ) + external + withPerm(TRANSFER_APPROVAL) + { + _addManualApproval(_from, _to, _allowance, _expiryTime, _description); + } + + function _addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime, bytes32 _description) internal { require(_to != address(0), "Invalid to address"); - /*solium-disable-next-line security/no-block-members*/ require(_expiryTime > now, "Invalid expiry time"); - require(manualApprovals[_from][_to].allowance == 0, "Approval already exists"); - manualApprovals[_from][_to] = ManualApproval(_allowance, _expiryTime); - emit AddManualApproval(_from, _to, _allowance, _expiryTime, msg.sender); + require(_allowance > 0, "Invalid allowance"); + if (approvalIndex[_from][_to] != 0) { + uint256 index = approvalIndex[_from][_to] - 1; + require(approvals[index].expiryTime < now || approvals[index].allowance == 0, "Approval already exists"); + _revokeManualApproval(_from, _to); + } + approvals.push(ManualApproval(_from, _to, _allowance, _expiryTime, _description)); + approvalIndex[_from][_to] = approvals.length; + emit AddManualApproval(_from, _to, _allowance, _expiryTime, _description, msg.sender); + } + + /** + * @notice Adds mutiple manual approvals in batch + * @param _from is the address array from which transfers are approved + * @param _to is the address array to which transfers are approved + * @param _allowances is the array of approved amounts + * @param _expiryTimes is the array of the times until which eath transfer is allowed + * @param _descriptions is the description array for these manual approvals + */ + function addManualApprovalMulti( + address[] calldata _from, + address[] calldata _to, + uint256[] calldata _allowances, + uint256[] calldata _expiryTimes, + bytes32[] calldata _descriptions + ) + external + withPerm(TRANSFER_APPROVAL) + { + _checkInputLengthArray(_from, _to, _allowances, _expiryTimes, _descriptions); + for (uint256 i = 0; i < _from.length; i++){ + _addManualApproval(_from[i], _to[i], _allowances[i], _expiryTimes[i], _descriptions[i]); + } } /** - * @notice Adds a pair of addresses to manual blockings - * @param _from is the address from which transfers are blocked - * @param _to is the address to which transfers are blocked - * @param _expiryTime is the time until which the transfer is blocked + * @notice Modify the existing manual approvals + * @param _from is the address from which transfers are approved + * @param _to is the address to which transfers are approved + * @param _expiryTime is the time until which the transfer is allowed + * @param _changedAllowance is the changed allowance + * @param _description Description about the manual approval + * @param _change uint values which tells whether the allowances will be increased (1) or decreased (0) + * or any value when there is no change in allowances */ - function addManualBlocking(address _from, address _to, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); + function modifyManualApproval( + address _from, + address _to, + uint256 _expiryTime, + uint256 _changedAllowance, + bytes32 _description, + uint8 _change + ) + external + withPerm(TRANSFER_APPROVAL) + { + _modifyManualApproval(_from, _to, _expiryTime, _changedAllowance, _description, _change); + } + + function _modifyManualApproval( + address _from, + address _to, + uint256 _expiryTime, + uint256 _changedAllowance, + bytes32 _description, + uint8 _change + ) + internal + { require(_to != address(0), "Invalid to address"); /*solium-disable-next-line security/no-block-members*/ require(_expiryTime > now, "Invalid expiry time"); - require(manualBlockings[_from][_to].expiryTime == 0, "Blocking already exists"); - manualBlockings[_from][_to] = ManualBlocking(_expiryTime); - emit AddManualBlocking(_from, _to, _expiryTime, msg.sender); + require(approvalIndex[_from][_to] != 0, "Approval not present"); + uint256 index = approvalIndex[_from][_to] - 1; + ManualApproval storage approval = approvals[index]; + require(approval.allowance != 0 && approval.expiryTime > now, "Not allowed"); + uint256 currentAllowance = approval.allowance; + uint256 newAllowance; + if (_change == 1) { + // Allowance get increased + newAllowance = currentAllowance.add(_changedAllowance); + approval.allowance = newAllowance; + } else if (_change == 0) { + // Allowance get decreased + if (_changedAllowance > currentAllowance) { + newAllowance = 0; + approval.allowance = newAllowance; + } else { + newAllowance = currentAllowance.sub(_changedAllowance); + approval.allowance = newAllowance; + } + } else { + // No change in the Allowance + newAllowance = currentAllowance; + } + // Greedy storage technique + if (approval.expiryTime != _expiryTime) { + approval.expiryTime = _expiryTime; + } + if (approval.description != _description) { + approval.description = _description; + } + emit ModifyManualApproval(_from, _to, _expiryTime, newAllowance, _description, msg.sender); + } + + /** + * @notice Adds mutiple manual approvals in batch + * @param _from is the address array from which transfers are approved + * @param _to is the address array to which transfers are approved + * @param _expiryTimes is the array of the times until which eath transfer is allowed + * @param _changedAllowances is the array of approved amounts + * @param _descriptions is the description array for these manual approvals + * @param _changes Array of uint values which tells whether the allowances will be increased (1) or decreased (0) + * or any value when there is no change in allowances + */ + function modifyManualApprovalMulti( + address[] memory _from, + address[] memory _to, + uint256[] memory _expiryTimes, + uint256[] memory _changedAllowances, + bytes32[] memory _descriptions, + uint8[] memory _changes + ) + public + withPerm(TRANSFER_APPROVAL) + { + _checkInputLengthArray(_from, _to, _changedAllowances, _expiryTimes, _descriptions); + require(_changes.length == _changedAllowances.length, "Input length array mismatch"); + for (uint256 i = 0; i < _from.length; i++) { + _modifyManualApproval(_from[i], _to[i], _expiryTimes[i], _changedAllowances[i], _descriptions[i], _changes[i]); + } } /** @@ -143,29 +265,164 @@ contract ManualApprovalTransferManager is ITransferManager { * @param _from is the address from which transfers are approved * @param _to is the address to which transfers are approved */ - function revokeManualApproval(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); - require(_to != address(0), "Invalid to address"); - delete manualApprovals[_from][_to]; + function revokeManualApproval(address _from, address _to) external withPerm(TRANSFER_APPROVAL) { + _revokeManualApproval(_from, _to); + } + + function _revokeManualApproval(address _from, address _to) internal { + require(approvalIndex[_from][_to] != 0, "Approval not exist"); + + // find the record in active approvals array & delete it + uint256 index = approvalIndex[_from][_to] - 1; + if (index != approvals.length -1) { + approvals[index] = approvals[approvals.length -1]; + approvalIndex[approvals[index].from][approvals[index].to] = index + 1; + } + delete approvalIndex[_from][_to]; + approvals.length--; emit RevokeManualApproval(_from, _to, msg.sender); } /** - * @notice Removes a pairs of addresses from manual approvals - * @param _from is the address from which transfers are approved - * @param _to is the address to which transfers are approved + * @notice Removes mutiple pairs of addresses from manual approvals + * @param _from is the address array from which transfers are approved + * @param _to is the address array to which transfers are approved */ - function revokeManualBlocking(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); - require(_to != address(0), "Invalid to address"); - delete manualBlockings[_from][_to]; - emit RevokeManualBlocking(_from, _to, msg.sender); + function revokeManualApprovalMulti(address[] calldata _from, address[] calldata _to) external withPerm(TRANSFER_APPROVAL) { + require(_from.length == _to.length, "Input array length mismatch"); + for(uint256 i = 0; i < _from.length; i++){ + _revokeManualApproval(_from[i], _to[i]); + } + } + + function _checkInputLengthArray( + address[] memory _from, + address[] memory _to, + uint256[] memory _expiryTimes, + uint256[] memory _allowances, + bytes32[] memory _descriptions + ) + internal + pure + { + require(_from.length == _to.length && + _to.length == _allowances.length && + _allowances.length == _expiryTimes.length && + _expiryTimes.length == _descriptions.length, + "Input array length mismatch" + ); + } + + /** + * @notice Returns the all active approvals corresponds to an address + * @param _user Address of the holder corresponds to whom list of manual approvals + * need to return + * @return address[] addresses from + * @return address[] addresses to + * @return uint256[] allowances provided to the approvals + * @return uint256[] expiry times provided to the approvals + * @return bytes32[] descriptions provided to the approvals + */ + function getActiveApprovalsToUser(address _user) external view returns(address[] memory, address[] memory, uint256[] memory, uint256[] memory, bytes32[] memory) { + uint256 counter = 0; + for (uint256 i = 0; i < approvals.length; i++) { + if ((approvals[i].from == _user || approvals[i].to == _user) + && approvals[i].expiryTime >= now) + counter ++; + } + + address[] memory from = new address[](counter); + address[] memory to = new address[](counter); + uint256[] memory allowance = new uint256[](counter); + uint256[] memory expiryTime = new uint256[](counter); + bytes32[] memory description = new bytes32[](counter); + + counter = 0; + for (uint256 i = 0; i < approvals.length; i++) { + if ((approvals[i].from == _user || approvals[i].to == _user) + && approvals[i].expiryTime >= now) { + + from[counter]=approvals[i].from; + to[counter]=approvals[i].to; + allowance[counter]=approvals[i].allowance; + expiryTime[counter]=approvals[i].expiryTime; + description[counter]=approvals[i].description; + counter ++; + } + } + return (from, to, allowance, expiryTime, description); + } + + /** + * @notice Get the details of the approval corresponds to _from & _to addresses + * @param _from Address of the sender + * @param _to Address of the receiver + * @return uint256 expiryTime of the approval + * @return uint256 allowance provided to the approval + * @return uint256 Description provided to the approval + */ + function getApprovalDetails(address _from, address _to) external view returns(uint256, uint256, bytes32) { + if (approvalIndex[_from][_to] != 0) { + uint256 index = approvalIndex[_from][_to] - 1; + if (index < approvals.length) { + ManualApproval storage approval = approvals[index]; + return( + approval.expiryTime, + approval.allowance, + approval.description + ); + } + } + return (uint256(0), uint256(0), bytes32(0)); + } + + /** + * @notice Returns the current number of active approvals + */ + function getTotalApprovalsLength() external view returns(uint256) { + return approvals.length; + } + + /** + * @notice Get the details of all approvals + * @return address[] addresses from + * @return address[] addresses to + * @return uint256[] allowances provided to the approvals + * @return uint256[] expiry times provided to the approvals + * @return bytes32[] descriptions provided to the approvals + */ + function getAllApprovals() external view returns(address[] memory, address[] memory, uint256[] memory, uint256[] memory, bytes32[] memory) { + address[] memory from = new address[](approvals.length); + address[] memory to = new address[](approvals.length); + uint256[] memory allowance = new uint256[](approvals.length); + uint256[] memory expiryTime = new uint256[](approvals.length); + bytes32[] memory description = new bytes32[](approvals.length); + + for (uint256 i = 0; i < approvals.length; i++) { + + from[i]=approvals[i].from; + to[i]=approvals[i].to; + allowance[i]=approvals[i].allowance; + expiryTime[i]=approvals[i].expiryTime; + description[i]=approvals[i].description; + + } + + return (from, to, allowance, expiryTime, description); + + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; } /** * @notice Returns the permissions flag that are associated with ManualApproval transfer manager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = TRANSFER_APPROVAL; return allPermissions; diff --git a/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol b/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol index f634b33e9..9a1f2e7df 100644 --- a/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol @@ -1,48 +1,62 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ManualApprovalTransferManager.sol"; import "../ModuleFactory.sol"; +import "../../proxy/ManualApprovalTransferManagerProxy.sol"; /** * @title Factory for deploying ManualApprovalTransferManager module */ contract ManualApprovalTransferManagerFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { - version = "1.0.0"; + require(_logicContract != address(0), "Invalid address"); + version = "2.1.0"; name = "ManualApprovalTransferManager"; title = "Manual Approval Transfer Manager"; - description = "Manage transfers using single approvals / blocking"; + description = "Manage transfers using single approvals"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } - /** + /** * @notice used to launch the Module with the help of factory * @return address Contract address of the Module */ - function deploy(bytes /* _data */) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - address manualTransferManager = new ManualApprovalTransferManager(msg.sender, address(polyToken)); + function deploy( + bytes calldata /* _data */ + ) + external + returns(address) + { + address polyToken = _takeFee(); + ManualApprovalTransferManagerProxy manualTransferManager = new ManualApprovalTransferManagerProxy(msg.sender, polyToken, logicContract); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(manualTransferManager), getName(), address(this), msg.sender, setupCost, now); + emit GenerateModuleFromFactory(address(manualTransferManager), getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); return address(manualTransferManager); } /** * @notice Type of the Module factory */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 2; return res; @@ -51,20 +65,19 @@ contract ManualApprovalTransferManagerFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { /*solium-disable-next-line max-len*/ - return "Allows an issuer to set manual approvals or blocks for specific pairs of addresses and amounts. Init function takes no parameters."; + return "Allows an issuer to set manual approvals for specific pairs of addresses and amounts. Init function takes no parameters."; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "ManualApproval"; availableTags[1] = "Transfer Restriction"; return availableTags; } - } diff --git a/contracts/modules/TransferManager/PercentageTransferManager.sol b/contracts/modules/TransferManager/PercentageTransferManager.sol index 162b090de..8217a1b17 100644 --- a/contracts/modules/TransferManager/PercentageTransferManager.sol +++ b/contracts/modules/TransferManager/PercentageTransferManager.sol @@ -1,52 +1,35 @@ /** - * DISCLAIMER: Under certain conditions, the limit could be bypassed if a large token holder - * redeems a huge portion of their tokens. It will cause the total supply to drop - * which can result in some other token holders having a percentage of tokens + * DISCLAIMER: Under certain conditions, the limit could be bypassed if a large token holder + * redeems a huge portion of their tokens. It will cause the total supply to drop + * which can result in some other token holders having a percentage of tokens * higher than the intended limit. */ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./ITransferManager.sol"; +import "./TransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../storage/modules/TransferManager/PercentageTransferManagerStorage.sol"; /** * @title Transfer Manager module for limiting percentage of token supply a single address can hold */ -contract PercentageTransferManager is ITransferManager { +contract PercentageTransferManager is PercentageTransferManagerStorage, TransferManager { using SafeMath for uint256; - // Permission key for modifying the whitelist - bytes32 public constant WHITELIST = "WHITELIST"; - bytes32 public constant ADMIN = "ADMIN"; - - // Maximum percentage that any holder can have, multiplied by 10**16 - e.g. 20% is 20 * 10**16 - uint256 public maxHolderPercentage; - - // Ignore transactions which are part of the primary issuance - bool public allowPrimaryIssuance = true; - - // Addresses on this list are always able to send / receive tokens - mapping (address => bool) public whitelist; - event ModifyHolderPercentage(uint256 _oldHolderPercentage, uint256 _newHolderPercentage); - event ModifyWhitelist( - address _investor, - uint256 _dateAdded, - address _addedBy, - bool _valid - ); - event SetAllowPrimaryIssuance(bool _allowPrimaryIssuance, uint256 _timestamp); + event ModifyWhitelist(address _investor, uint256 _dateAdded, address _addedBy, bool _valid); + event SetAllowPrimaryIssuance(bool _allowPrimaryIssuance); /** * @notice Constructor * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken */ - constructor (address _securityToken, address _polyAddress) + constructor(address _securityToken, address _polyToken) public - Module(_securityToken, _polyAddress) + Module(_securityToken, _polyToken) { + } /** @notice Used to verify the transfer transaction and prevent a given account to end up with more tokens than allowed @@ -54,22 +37,50 @@ contract PercentageTransferManager is ITransferManager { * @param _to Address of the receiver * @param _amount The amount of tokens to transfer */ - function verifyTransfer(address _from, address _to, uint256 _amount, bytes /* _data */, bool /* _isTransfer */) public returns(Result) { + function executeTransfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) + external + returns(Result) + { + (Result success,) = verifyTransfer(_from, _to, _amount, _data); + return success; + } + + /** + * @notice Used to verify the transfer transaction and prevent a given account to end up with more tokens than allowed + * @param _from Address of the sender + * @param _to Address of the receiver + * @param _amount The amount of tokens to transfer + */ + function verifyTransfer( + address _from, + address _to, + uint256 _amount, + bytes memory /*_data*/ + ) + public + view + returns(Result, bytes32) + { if (!paused) { if (_from == address(0) && allowPrimaryIssuance) { - return Result.NA; + return (Result.NA, bytes32(0)); } // If an address is on the whitelist, it is allowed to hold more than maxHolderPercentage of the tokens. if (whitelist[_to]) { - return Result.NA; + return (Result.NA, bytes32(0)); } - uint256 newBalance = ISecurityToken(securityToken).balanceOf(_to).add(_amount); - if (newBalance.mul(uint256(10)**18).div(ISecurityToken(securityToken).totalSupply()) > maxHolderPercentage) { - return Result.INVALID; + uint256 newBalance = IERC20(securityToken).balanceOf(_to).add(_amount); + if (newBalance.mul(uint256(10) ** 18).div(IERC20(securityToken).totalSupply()) > maxHolderPercentage) { + return (Result.INVALID, bytes32(uint256(address(this)) << 96)); } - return Result.NA; + return (Result.NA, bytes32(0)); } - return Result.NA; + return (Result.NA, bytes32(0)); } /** @@ -84,7 +95,7 @@ contract PercentageTransferManager is ITransferManager { /** * @notice This function returns the signature of configure function */ - function getInitFunction() public pure returns (bytes4) { + function getInitFunction() public pure returns(bytes4) { return bytes4(keccak256("configure(uint256,bool)")); } @@ -113,7 +124,7 @@ contract PercentageTransferManager is ITransferManager { * @param _investors Array of the addresses to whitelist * @param _valids Array of boolean value to decide whether or not the address it to be added or removed from the whitelist */ - function modifyWhitelistMulti(address[] _investors, bool[] _valids) public withPerm(WHITELIST) { + function modifyWhitelistMulti(address[] memory _investors, bool[] memory _valids) public withPerm(WHITELIST) { require(_investors.length == _valids.length, "Input array length mis-match"); for (uint i = 0; i < _investors.length; i++) { modifyWhitelist(_investors[i], _valids[i]); @@ -128,13 +139,20 @@ contract PercentageTransferManager is ITransferManager { require(_allowPrimaryIssuance != allowPrimaryIssuance, "Must change setting"); allowPrimaryIssuance = _allowPrimaryIssuance; /*solium-disable-next-line security/no-block-members*/ - emit SetAllowPrimaryIssuance(_allowPrimaryIssuance, now); + emit SetAllowPrimaryIssuance(_allowPrimaryIssuance); } + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + /** * @notice Return the permissions flag that are associated with Percentage transfer Manager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() public view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](2); allPermissions[0] = WHITELIST; allPermissions[1] = ADMIN; diff --git a/contracts/modules/TransferManager/PercentageTransferManagerFactory.sol b/contracts/modules/TransferManager/PercentageTransferManagerFactory.sol index 1702b6f08..3154022e3 100644 --- a/contracts/modules/TransferManager/PercentageTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/PercentageTransferManagerFactory.sol @@ -1,27 +1,41 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "./PercentageTransferManager.sol"; import "../ModuleFactory.sol"; import "../../libraries/Util.sol"; +import "../../proxy/PercentageTransferManagerProxy.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying PercentageTransferManager module */ contract PercentageTransferManagerFactory is ModuleFactory { + address public logicContract; + /** * @notice Constructor - * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + constructor( + uint256 _setupCost, + uint256 _usageCost, + address _logicContract, + address _polymathRegistry + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) { + require(_logicContract != address(0), "Invalid address"); version = "1.0.0"; name = "PercentageTransferManager"; title = "Percentage Transfer Manager"; description = "Restrict the number of investors"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } /** @@ -29,24 +43,24 @@ contract PercentageTransferManagerFactory is ModuleFactory { * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ - function deploy(bytes _data) external returns(address) { - if(setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - PercentageTransferManager percentageTransferManager = new PercentageTransferManager(msg.sender, address(polyToken)); - require(Util.getSig(_data) == percentageTransferManager.getInitFunction(), "Provided data is not valid"); + function deploy(bytes calldata _data) external returns(address) { + address polyToken = _takeFee(); + address percentageTransferManager = address(new PercentageTransferManagerProxy(msg.sender, polyToken, logicContract)); + require(Util.getSig(_data) == IBoot(percentageTransferManager).getInitFunction(), "Provided data is not valid"); + bool success; /*solium-disable-next-line security/no-low-level-calls*/ - require(address(percentageTransferManager).call(_data), "Unsuccessful call"); + (success, ) = percentageTransferManager.call(_data); + require(success, "Unsuccessful call"); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(percentageTransferManager), getName(), address(this), msg.sender, setupCost, now); - return address(percentageTransferManager); - + emit GenerateModuleFromFactory(percentageTransferManager, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return percentageTransferManager; } /** * @notice Type of the Module factory * @return uint8 */ - function getTypes() external view returns(uint8[]) { + function getTypes() external view returns(uint8[] memory) { uint8[] memory res = new uint8[](1); res[0] = 2; return res; @@ -55,14 +69,14 @@ contract PercentageTransferManagerFactory is ModuleFactory { /** * @notice Returns the instructions associated with the module */ - function getInstructions() external view returns(string) { + function getInstructions() external view returns(string memory) { return "Allows an issuer to restrict the total number of non-zero token holders"; } /** * @notice Get the tags related to the module factory */ - function getTags() external view returns(bytes32[]) { + function getTags() external view returns(bytes32[] memory) { bytes32[] memory availableTags = new bytes32[](2); availableTags[0] = "Percentage"; availableTags[1] = "Transfer Restriction"; diff --git a/contracts/modules/TransferManager/TransferManager.sol b/contracts/modules/TransferManager/TransferManager.sol new file mode 100644 index 000000000..bcf96e206 --- /dev/null +++ b/contracts/modules/TransferManager/TransferManager.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.5.0; + +import "../../Pausable.sol"; +import "../Module.sol"; +import "../../interfaces/ITransferManager.sol"; + +/** + * @title Base abstract contract to be implemented by all Transfer Manager modules + */ +contract TransferManager is ITransferManager, Module, Pausable { + + bytes32 public constant LOCKED = "LOCKED"; + bytes32 public constant UNLOCKED = "UNLOCKED"; + + modifier onlySecurityToken() { + require(msg.sender == securityToken, "Sender is not owner"); + _; + } + + function unpause() public onlyOwner { + super._unpause(); + } + + function pause() public onlyOwner { + super._pause(); + } +} diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol new file mode 100644 index 000000000..c57cc4929 --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -0,0 +1,1144 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; +import "../../libraries/VolumeRestrictionLib.sol"; +import "./TransferManager.sol"; + +contract VolumeRestrictionTM is VolumeRestrictionTMStorage, TransferManager { + using SafeMath for uint256; + + // permission definition + bytes32 public constant ADMIN = "ADMIN"; + + // Emit when the token holder is added/removed from the exemption list + event ChangedExemptWalletList(address indexed _wallet, bool _change); + // Emit when the new individual restriction is added corresponds to new token holders + event AddIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new daily (Individual) restriction is added + event AddIndividualDailyRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the individual restriction is modified for a given address + event ModifyIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when individual daily restriction get modified + event ModifyIndividualDailyRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new global restriction is added + event AddDefaultRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new daily (Default) restriction is added + event AddDefaultDailyRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when default restriction get modified + event ModifyDefaultRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when daily default restriction get modified + event ModifyDefaultDailyRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the individual restriction gets removed + event IndividualRestrictionRemoved(address indexed _holder); + // Emit when individual daily restriction removed + event IndividualDailyRestrictionRemoved(address indexed _holder); + // Emit when the default restriction gets removed + event DefaultRestrictionRemoved(); + // Emit when the daily default restriction gets removed + event DefaultDailyRestrictionRemoved(); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + + } + + /** + * @notice Used to verify the transfer/transferFrom transaction and prevent tranaction + * whose volume of tokens will voilate the maximum volume transfer restriction + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function executeTransfer(address _from, address /*_to */, uint256 _amount, bytes calldata /*_data*/) external onlySecurityToken returns (Result success) { + uint256 fromTimestamp; + uint256 sumOfLastPeriod; + uint256 daysCovered; + uint256 dailyTime; + uint256 endTime; + bool isGlobal; + (success, fromTimestamp, sumOfLastPeriod, daysCovered, dailyTime, endTime, isGlobal) = _verifyTransfer(_from, _amount); + if (fromTimestamp != 0 || dailyTime != 0) { + _updateStorage( + _from, + _amount, + fromTimestamp, + sumOfLastPeriod, + daysCovered, + dailyTime, + endTime, + isGlobal + ); + } + return success; + } + + /** + * @notice Used to verify the transfer/transferFrom transaction and prevent tranaction + * whose volume of tokens will voilate the maximum volume transfer restriction + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function verifyTransfer( + address _from, + address /*_to*/ , + uint256 _amount, + bytes memory /*_data*/ + ) + public + view + returns (Result, bytes32) + { + + (Result success,,,,,,) = _verifyTransfer(_from, _amount); + if (success == Result.INVALID) + return (success, bytes32(uint256(address(this)) << 96)); + return (Result.NA, bytes32(0)); + } + + /** + * @notice Used to verify the transfer/transferFrom transaction and prevent tranaction + * whose volume of tokens will voilate the maximum volume transfer restriction + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function _verifyTransfer( + address _from, + uint256 _amount + ) + internal + view + returns (Result, uint256, uint256, uint256, uint256, uint256, bool) + { + // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction + if (!paused && _from != address(0) && exemptions.exemptIndex[_from] == 0) { + // Checking the individual restriction if the `_from` comes in the individual category + if ((individualRestrictions.individualRestriction[_from].endTime >= now && individualRestrictions.individualRestriction[_from].startTime <= now) + || (individualRestrictions.individualDailyRestriction[_from].endTime >= now && individualRestrictions.individualDailyRestriction[_from].startTime <= now)) { + + return _individualRestrictionCheck(_from, _amount); + // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically + } else if ((globalRestrictions.defaultRestriction.endTime >= now && globalRestrictions.defaultRestriction.startTime <= now) + || (globalRestrictions.defaultDailyRestriction.endTime >= now && globalRestrictions.defaultDailyRestriction.startTime <= now)) { + + return _defaultRestrictionCheck(_from, _amount); + } + } + return (Result.NA, 0, 0, 0, 0, 0, false); + } + + /** + * @notice Add/Remove wallet address from the exempt list + * @param _wallet Ethereum wallet/contract address that need to be exempted + * @param _change Boolean value used to add (i.e true) or remove (i.e false) from the list + */ + function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { + require(_wallet != address(0)); + require((exemptions.exemptIndex[_wallet] == 0) == _change); + if (_change) { + exemptions.exemptAddresses.push(_wallet); + exemptions.exemptIndex[_wallet] = exemptions.exemptAddresses.length; + } else { + exemptions.exemptAddresses[exemptions.exemptIndex[_wallet] - 1] = exemptions.exemptAddresses[exemptions.exemptAddresses.length - 1]; + exemptions.exemptIndex[exemptions.exemptAddresses[exemptions.exemptIndex[_wallet] - 1]] = exemptions.exemptIndex[_wallet]; + delete exemptions.exemptIndex[_wallet]; + exemptions.exemptAddresses.length --; + } + emit ChangedExemptWalletList(_wallet, _change); + } + + /** + * @notice Use to add the new individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + public + withPerm(ADMIN) + { + require( + individualRestrictions.individualRestriction[_holder].endTime < now, + "Not Allowed" + ); + if (_startTime == 0) { + _startTime = now; + } + require(_holder != address(0) && exemptions.exemptIndex[_holder] == 0, "Invalid address"); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType, now); + + if (individualRestrictions.individualRestriction[_holder].endTime != 0) { + removeIndividualRestriction(_holder); + } + individualRestrictions.individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + VolumeRestrictionLib.addRestrictionData(holderData, _holder, uint8(TypeOfPeriod.MultipleDays), individualRestrictions.individualRestriction[_holder].endTime); + emit AddIndividualRestriction( + _holder, + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new individual daily restriction for all token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + public + withPerm(ADMIN) + { + if (_startTime == 0) { + _startTime = now; + } + require( + individualRestrictions.individualDailyRestriction[_holder].endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType, now); + if (individualRestrictions.individualDailyRestriction[_holder].endTime != 0) { + removeIndividualDailyRestriction(_holder); + } + individualRestrictions.individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + VolumeRestrictionLib.addRestrictionData(holderData, _holder, uint8(TypeOfPeriod.OneDay), individualRestrictions.individualRestriction[_holder].endTime); + emit AddIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new individual daily restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualDailyRestrictionMulti( + address[] memory _holders, + uint256[] memory _allowedTokens, + uint256[] memory _startTimes, + uint256[] memory _endTimes, + uint256[] memory _restrictionTypes + ) + public + { + //NB - we duplicate _startTimes below to allow function reuse + VolumeRestrictionLib._checkLengthOfArray(_holders, _allowedTokens, _startTimes, _startTimes, _endTimes, _restrictionTypes); + for (uint256 i = 0; i < _holders.length; i++) { + addIndividualDailyRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to add the new individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualRestrictionMulti( + address[] memory _holders, + uint256[] memory _allowedTokens, + uint256[] memory _startTimes, + uint256[] memory _rollingPeriodInDays, + uint256[] memory _endTimes, + uint256[] memory _restrictionTypes + ) + public + { + VolumeRestrictionLib._checkLengthOfArray(_holders, _allowedTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + for (uint256 i = 0; i < _holders.length; i++) { + addIndividualRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to add the new default restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addDefaultRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + require( + globalRestrictions.defaultRestriction.endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType, now); + globalRestrictions.defaultRestriction = VolumeRestriction( + _allowedTokens, + startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddDefaultRestriction( + _allowedTokens, + startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new default daily restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addDefaultDailyRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + require( + globalRestrictions.defaultDailyRestriction.endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType, now); + globalRestrictions.defaultDailyRestriction = VolumeRestriction( + _allowedTokens, + startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddDefaultDailyRestriction( + _allowedTokens, + startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _holder Address of the user + */ + function removeIndividualRestriction(address _holder) public withPerm(ADMIN) { + require(_holder != address(0)); + require(individualRestrictions.individualRestriction[_holder].endTime != 0); + individualRestrictions.individualRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + VolumeRestrictionLib.deleteHolderFromList(holderData, _holder, uint8(TypeOfPeriod.OneDay)); + bucketData.userToBucket[_holder].lastTradedDayTime = 0; + bucketData.userToBucket[_holder].sumOfLastPeriod = 0; + bucketData.userToBucket[_holder].daysCovered = 0; + emit IndividualRestrictionRemoved(_holder); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _holders Array of address of the user + */ + function removeIndividualRestrictionMulti(address[] calldata _holders) external { + for (uint256 i = 0; i < _holders.length; i++) { + removeIndividualRestriction(_holders[i]); + } + } + + /** + * @notice use to remove the individual daily restriction for a given address + * @param _holder Address of the user + */ + function removeIndividualDailyRestriction(address _holder) public withPerm(ADMIN) { + require(_holder != address(0)); + require(individualRestrictions.individualDailyRestriction[_holder].endTime != 0); + individualRestrictions.individualDailyRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + VolumeRestrictionLib.deleteHolderFromList(holderData, _holder, uint8(TypeOfPeriod.MultipleDays)); + bucketData.userToBucket[_holder].dailyLastTradedDayTime = 0; + emit IndividualDailyRestrictionRemoved(_holder); + } + + /** + * @notice use to remove the individual daily restriction for a given address + * @param _holders Array of address of the user + */ + function removeIndividualDailyRestrictionMulti(address[] calldata _holders) external { + for (uint256 i = 0; i < _holders.length; i++) { + removeIndividualDailyRestriction(_holders[i]); + } + } + + /** + * @notice Use to remove the default restriction + */ + function removeDefaultRestriction() public withPerm(ADMIN) { + require(globalRestrictions.defaultRestriction.endTime != 0); + globalRestrictions.defaultRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + emit DefaultRestrictionRemoved(); + } + + /** + * @notice Use to remove the daily default restriction + */ + function removeDefaultDailyRestriction() external withPerm(ADMIN) { + require(globalRestrictions.defaultDailyRestriction.endTime != 0); + globalRestrictions.defaultDailyRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + emit DefaultDailyRestrictionRemoved(); + } + + /** + * @notice Use to modify the existing individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + public + withPerm(ADMIN) + { + if (_startTime == 0) { + _startTime = now; + } + require(individualRestrictions.individualRestriction[_holder].startTime > now, "Not Allowed"); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType, now); + individualRestrictions.individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyIndividualRestriction( + _holder, + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the existing individual daily restriction for a given token holder + * @dev Changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with + * morning and end on midnight while after the change day may start with afternoon and end with other day afternoon + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + public + withPerm(ADMIN) + { + if (_startTime == 0) { + _startTime = now; + } + uint checkTime = (individualRestrictions.individualDailyRestriction[_holder].startTime <= now ? individualRestrictions.individualDailyRestriction[_holder].startTime : now); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType, checkTime); + individualRestrictions.individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the existing individual daily restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualDailyRestrictionMulti( + address[] memory _holders, + uint256[] memory _allowedTokens, + uint256[] memory _startTimes, + uint256[] memory _endTimes, + uint256[] memory _restrictionTypes + ) + public + { + //NB - we duplicate _startTimes below to allow function reuse + VolumeRestrictionLib._checkLengthOfArray(_holders, _allowedTokens, _startTimes, _startTimes, _endTimes, _restrictionTypes); + for (uint256 i = 0; i < _holders.length; i++) { + modifyIndividualDailyRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to modify the existing individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualRestrictionMulti( + address[] memory _holders, + uint256[] memory _allowedTokens, + uint256[] memory _startTimes, + uint256[] memory _rollingPeriodInDays, + uint256[] memory _endTimes, + uint256[] memory _restrictionTypes + ) + public + { + VolumeRestrictionLib._checkLengthOfArray(_holders, _allowedTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + for (uint256 i = 0; i < _holders.length; i++) { + modifyIndividualRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to modify the global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyDefaultRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require(globalRestrictions.defaultRestriction.startTime > now, "Not Allowed"); + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType, now); + globalRestrictions.defaultRestriction = VolumeRestriction( + _allowedTokens, + startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDefaultRestriction( + _allowedTokens, + startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the daily default restriction for all token holder + * @dev Changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with + * morning and end on midnight while after the change day may start with afternoon and end with other day afternoon. + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyDefaultDailyRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + // If old startTime is already passed then new startTime should be greater than or equal to the + // old startTime otherwise any past startTime can be allowed in compare to earlier startTime. + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType, + (globalRestrictions.defaultDailyRestriction.startTime <= now ? globalRestrictions.defaultDailyRestriction.startTime : now) + ); + globalRestrictions.defaultDailyRestriction = VolumeRestriction( + _allowedTokens, + startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDefaultDailyRestriction( + _allowedTokens, + startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the default restriction + */ + function _defaultRestrictionCheck(address _from, uint256 _amount) internal view returns ( + Result success, + uint256 fromTimestamp, + uint256 sumOfLastPeriod, + uint256 daysCovered, + uint256 dailyTime, + uint256 endTime, + bool isGlobal + ) { + // using the variable to avoid stack too deep error + BucketDetails storage bucketDetails = bucketData.defaultUserToBucket[_from]; + daysCovered = globalRestrictions.defaultRestriction.rollingPeriodInDays; + bool allowedDefault = true; + bool allowedDaily; + if (globalRestrictions.defaultRestriction.endTime >= now && globalRestrictions.defaultRestriction.startTime <= now) { + if (bucketDetails.lastTradedDayTime < globalRestrictions.defaultRestriction.startTime) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = globalRestrictions.defaultRestriction.startTime; + } else { + // Picking up the last timestamp + fromTimestamp = bucketDetails.lastTradedDayTime; + } + + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( + fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), + _from, + daysCovered, + bucketDetails + ); + // validation of the transaction amount + if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, globalRestrictions.defaultRestriction)) { + allowedDefault = false; + } + } + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, bucketDetails.dailyLastTradedDayTime, globalRestrictions.defaultDailyRestriction); + + success = ((allowedDaily && allowedDefault) == true ? Result.NA : Result.INVALID); + endTime = globalRestrictions.defaultDailyRestriction.endTime; + isGlobal = true; + } + + /** + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the individual restriction + */ + function _individualRestrictionCheck(address _from, uint256 _amount) internal view returns ( + Result success, + uint256 fromTimestamp, + uint256 sumOfLastPeriod, + uint256 daysCovered, + uint256 dailyTime, + uint256 endTime, + bool allowedDaily + ) { + // using the variable to avoid stack too deep error + BucketDetails memory bucketDetails = bucketData.userToBucket[_from]; + VolumeRestriction memory dailyRestriction = individualRestrictions.individualDailyRestriction[_from]; + VolumeRestriction memory restriction = individualRestrictions.individualRestriction[_from]; + daysCovered = restriction.rollingPeriodInDays; + bool allowedIndividual = true; + if (restriction.endTime >= now && restriction.startTime <= now) { + if (bucketDetails.lastTradedDayTime < restriction.startTime) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = restriction.startTime; + } else { + // Picking up the last timestamp + fromTimestamp = bucketDetails.lastTradedDayTime; + } + + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( + fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), + _from, + daysCovered, + bucketDetails + ); + // validation of the transaction amount + if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, restriction)) { + allowedIndividual = false; + } + } + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, bucketDetails.dailyLastTradedDayTime, dailyRestriction); + success = ((allowedDaily && allowedIndividual) ? Result.NA : Result.INVALID); + endTime = dailyRestriction.endTime; + allowedDaily = false; + } + + function _dailyTxCheck( + address from, + uint256 amount, + uint256 dailyLastTradedDayTime, + VolumeRestriction memory restriction + ) + internal + view + returns(bool, uint256) + { + // Checking whether the daily restriction is added or not if yes then calculate + // the total amount get traded on a particular day (~ _fromTime) + if ( now <= restriction.endTime && now >= restriction.startTime) { + uint256 txSumOfDay = 0; + if (dailyLastTradedDayTime == 0 || dailyLastTradedDayTime < restriction.startTime) + // This if condition will be executed when the individual daily restriction executed first time + dailyLastTradedDayTime = restriction.startTime.add(BokkyPooBahsDateTimeLibrary.diffDays(restriction.startTime, now).mul(1 days)); + else if (now.sub(dailyLastTradedDayTime) >= 1 days) + dailyLastTradedDayTime = dailyLastTradedDayTime.add(BokkyPooBahsDateTimeLibrary.diffDays(dailyLastTradedDayTime, now).mul(1 days)); + // Assgining total sum traded on dailyLastTradedDayTime timestamp + txSumOfDay = bucketData.bucket[from][dailyLastTradedDayTime]; + return (_checkValidAmountToTransact(txSumOfDay, amount, restriction), dailyLastTradedDayTime); + } + return (true, dailyLastTradedDayTime); + } + + /// Internal function for the bucket check + function _bucketCheck( + uint256 _fromTime, + uint256 _diffDays, + address _from, + uint256 _rollingPeriodInDays, + BucketDetails memory _bucketDetails + ) + internal + view + returns (uint256, uint256, uint256) + { + uint256 counter = _bucketDetails.daysCovered; + uint256 sumOfLastPeriod = _bucketDetails.sumOfLastPeriod; + uint256 i = 0; + if (_diffDays >= _rollingPeriodInDays) { + // If the difference of days is greater than the rollingPeriod then sumOfLastPeriod will always be zero + sumOfLastPeriod = 0; + counter = counter.add(_diffDays); + } else { + for (i = 0; i < _diffDays; i++) { + counter++; + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (counter >= _rollingPeriodInDays) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + // The below line subtracts (the traded volume on days no longer covered by rolling period) from sumOfLastPeriod. + // Every loop execution subtracts one day's trade volume. + // Loop starts from the first day covered in sumOfLastPeriod upto the day that is covered by rolling period. + uint256 temp = _bucketDetails.daysCovered.sub(counter.sub(_rollingPeriodInDays)); + temp = _bucketDetails.lastTradedDayTime.sub(temp.mul(1 days)); + sumOfLastPeriod = sumOfLastPeriod.sub(bucketData.bucket[_from][temp]); + } + // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand + // the alogrithm + //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + } + } + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + _fromTime = _fromTime.add(_diffDays.mul(1 days)); + return (sumOfLastPeriod, _fromTime, counter); + } + + function _checkValidAmountToTransact( + uint256 _sumOfLastPeriod, + uint256 _amountToTransact, + VolumeRestriction memory _restriction + ) + internal + view + returns (bool) + { + uint256 _allowedAmount = 0; + if (_restriction.typeOfRestriction == RestrictionType.Percentage) { + _allowedAmount = (_restriction.allowedTokens.mul(IERC20(securityToken).totalSupply())) / uint256(10) ** 18; + } else { + _allowedAmount = _restriction.allowedTokens; + } + // Validation on the amount to transact + return (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)); + } + + function _updateStorage( + address _from, + uint256 _amount, + uint256 _lastTradedDayTime, + uint256 _sumOfLastPeriod, + uint256 _daysCovered, + uint256 _dailyLastTradedDayTime, + uint256 _endTime, + bool isDefault + ) + internal + { + + if (isDefault){ + BucketDetails storage defaultUserToBucketDetails = bucketData.defaultUserToBucket[_from]; + _updateStorageActual(_from, _amount, _lastTradedDayTime, _sumOfLastPeriod, _daysCovered, _dailyLastTradedDayTime, _endTime, defaultUserToBucketDetails); + } + else { + BucketDetails storage userToBucketDetails = bucketData.userToBucket[_from]; + _updateStorageActual(_from, _amount, _lastTradedDayTime, _sumOfLastPeriod, _daysCovered, _dailyLastTradedDayTime, _endTime, userToBucketDetails); + } + } + + function _updateStorageActual( + address _from, + uint256 _amount, + uint256 _lastTradedDayTime, + uint256 _sumOfLastPeriod, + uint256 _daysCovered, + uint256 _dailyLastTradedDayTime, + uint256 _endTime, + BucketDetails storage details + ) + internal + { + // Cheap storage technique + if (details.lastTradedDayTime != _lastTradedDayTime) { + // Assigning the latest transaction timestamp of the day + details.lastTradedDayTime = _lastTradedDayTime; + } + if (details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { + // Assigning the latest transaction timestamp of the day + details.dailyLastTradedDayTime = _dailyLastTradedDayTime; + } + if (details.daysCovered != _daysCovered) { + details.daysCovered = _daysCovered; + } + + if (_amount != 0) { + if (_lastTradedDayTime !=0) { + details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + // Increasing the total amount of the day by `_amount` + bucketData.bucket[_from][_lastTradedDayTime] = bucketData.bucket[_from][_lastTradedDayTime].add(_amount); + } + if ((_dailyLastTradedDayTime != _lastTradedDayTime) && _dailyLastTradedDayTime != 0 && now <= _endTime) { + // Increasing the total amount of the day by `_amount` + bucketData.bucket[_from][_dailyLastTradedDayTime] = bucketData.bucket[_from][_dailyLastTradedDayTime].add(_amount); + } + } + } + + function _checkInputParams( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodDays, + uint256 _endTime, + uint256 _restrictionType, + uint256 _earliestStartTime + ) + internal + pure + { + require(_restrictionType == 0 || _restrictionType == 1, "Invalid type"); + require(_startTime >= _earliestStartTime, "Invalid startTime"); + if (_restrictionType == uint256(RestrictionType.Fixed)) { + require(_allowedTokens > 0, "Invalid value"); + } else { + require( + _allowedTokens > 0 && _allowedTokens <= 100 * 10 ** 16, + "Invalid value" + ); + } + // Maximum limit for the rollingPeriod is 365 days + require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365, "Invalid rollingperiod"); + require( + BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays && _endTime > _startTime, + "Invalid times" + ); + } + + /** + * @notice Use to get the bucket details for a given address + * @param _user Address of the token holder for whom the bucket details has queried + * @return uint256 lastTradedDayTime + * @return uint256 sumOfLastPeriod + * @return uint256 days covered + * @return uint256 24h lastTradedDayTime + */ + function getIndividualBucketDetailsToUser(address _user) public view returns(uint256, uint256, uint256, uint256) { + return( + bucketData.userToBucket[_user].lastTradedDayTime, + bucketData.userToBucket[_user].sumOfLastPeriod, + bucketData.userToBucket[_user].daysCovered, + bucketData.userToBucket[_user].dailyLastTradedDayTime + ); + } + + /** + * @notice Use to get the bucket details for a given address + * @param _user Address of the token holder for whom the bucket details has queried + * @return uint256 lastTradedDayTime + * @return uint256 sumOfLastPeriod + * @return uint256 days covered + * @return uint256 24h lastTradedDayTime + */ + function getDefaultBucketDetailsToUser(address _user) public view returns(uint256, uint256, uint256, uint256) { + return( + bucketData.defaultUserToBucket[_user].lastTradedDayTime, + bucketData.defaultUserToBucket[_user].sumOfLastPeriod, + bucketData.defaultUserToBucket[_user].daysCovered, + bucketData.defaultUserToBucket[_user].dailyLastTradedDayTime + ); + } + + /** + * @notice Use to get the volume of token that being traded at a particular day (`_at` + 24 hours) for a given user + * @param _user Address of the token holder + * @param _at Timestamp + */ + function getTotalTradedByUser(address _user, uint256 _at) external view returns(uint256) { + return bucketData.bucket[_user][_at]; + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns(bytes4) { + return bytes4(0); + } + + /** + * @notice Use to return the list of exempted addresses + */ + function getExemptAddress() external view returns(address[] memory) { + return exemptions.exemptAddresses; + } + + function getIndividualRestriction(address _investor) external view returns(uint256, uint256, uint256, uint256, RestrictionType) { + return _volumeRestrictionSplay(individualRestrictions.individualRestriction[_investor]); + } + + function getIndividualDailyRestriction(address _investor) external view returns(uint256, uint256, uint256, uint256, RestrictionType) { + return _volumeRestrictionSplay(individualRestrictions.individualDailyRestriction[_investor]); + } + + function getDefaultRestriction() external view returns(uint256, uint256, uint256, uint256, RestrictionType) { + return _volumeRestrictionSplay(globalRestrictions.defaultRestriction); + } + + function getDefaultDailyRestriction() external view returns(uint256, uint256, uint256, uint256, RestrictionType) { + return _volumeRestrictionSplay(globalRestrictions.defaultDailyRestriction); + } + + function _volumeRestrictionSplay(VolumeRestriction memory _volumeRestriction) internal pure returns(uint256, uint256, uint256, uint256, RestrictionType) { + return ( + _volumeRestriction.allowedTokens, + _volumeRestriction.startTime, + _volumeRestriction.rollingPeriodInDays, + _volumeRestriction.endTime, + _volumeRestriction.typeOfRestriction + ); + } + + /** + * @notice Provide the restriction details of all the restricted addresses + * @return address List of the restricted addresses + * @return uint256 List of the tokens allowed to the restricted addresses corresponds to restricted address + * @return uint256 List of the start time of the restriction corresponds to restricted address + * @return uint256 List of the rolling period in days for a restriction corresponds to restricted address. + * @return uint256 List of the end time of the restriction corresponds to restricted address. + * @return uint8 List of the type of restriction to validate the value of the `allowedTokens` + * of the restriction corresponds to restricted address + */ + function getRestrictionData() external view returns( + address[] memory allAddresses, + uint256[] memory allowedTokens, + uint256[] memory startTime, + uint256[] memory rollingPeriodInDays, + uint256[] memory endTime, + uint8[] memory typeOfRestriction + ) { + return VolumeRestrictionLib.getRestrictionData(holderData, individualRestrictions); + } + + /** + * @notice return the amount of tokens for a given user as per the partition + */ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + return 0; + } + + /** + * @notice Returns the permissions flag that are associated with Percentage transfer Manager + */ + function getPermissions() public view returns(bytes32[] memory ) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } + +} diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol new file mode 100644 index 000000000..77ed2c7bd --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.5.0; + +import "../../proxy/VolumeRestrictionTMProxy.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying VolumeRestrictionTM module + */ +contract VolumeRestrictionTMFactory is ModuleFactory { + + address public logicContract; + + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` + * @param _polymathRegistry Address of the Polymath registry + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _logicContract, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + require(_logicContract != address(0), "Invalid address"); + version = "1.0.0"; + name = "VolumeRestrictionTM"; + title = "Volume Restriction Transfer Manager"; + description = "Manage transfers based on the volume of tokens that needs to be transact"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; + } + + + /** + * @notice Used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes calldata /* _data */) external returns(address) { + address polyToken = _takeFee(); + address volumeRestrictionTransferManager = address(new VolumeRestrictionTMProxy(msg.sender, address(polyToken), logicContract)); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(volumeRestrictionTransferManager, getName(), address(this), msg.sender, getSetupCost(), getSetupCostInPoly(), now); + return volumeRestrictionTransferManager; + } + + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string memory) { + /*solium-disable-next-line max-len*/ + return "Module used to restrict the volume of tokens traded by the token holders"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](5); + availableTags[0] = "Maximum Volume"; + availableTags[1] = "Transfer Restriction"; + availableTags[2] = "Daily Restriction"; + availableTags[3] = "Individual Restriction"; + availableTags[4] = "Default Restriction"; + return availableTags; + } + +} diff --git a/contracts/oracles/MakerDAOOracle.sol b/contracts/oracles/MakerDAOOracle.sol index 51773cd7d..f28c9e965 100644 --- a/contracts/oracles/MakerDAOOracle.sol +++ b/contracts/oracles/MakerDAOOracle.sol @@ -1,11 +1,10 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../interfaces/IOracle.sol"; import "../external/IMedianizer.sol"; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract MakerDAOOracle is IOracle, Ownable { - address public medianizer; address public currencyAddress; bytes32 public currencySymbol; @@ -14,9 +13,9 @@ contract MakerDAOOracle is IOracle, Ownable { uint256 public manualPrice; /*solium-disable-next-line security/no-block-members*/ - event ChangeMedianizer(address _newMedianizer, address _oldMedianizer, uint256 _now); - event SetManualPrice(uint256 _oldPrice, uint256 _newPrice, uint256 _time); - event SetManualOverride(bool _override, uint256 _time); + event ChangeMedianizer(address _newMedianizer, address _oldMedianizer); + event SetManualPrice(uint256 _oldPrice, uint256 _newPrice); + event SetManualOverride(bool _override); /** * @notice Creates a new Maker based oracle @@ -24,7 +23,7 @@ contract MakerDAOOracle is IOracle, Ownable { * @param _currencyAddress Address of currency (0x0 for ETH) * @param _currencySymbol Symbol of currency */ - constructor (address _medianizer, address _currencyAddress, bytes32 _currencySymbol) public { + constructor(address _medianizer, address _currencyAddress, bytes32 _currencySymbol) public { medianizer = _medianizer; currencyAddress = _currencyAddress; currencySymbol = _currencySymbol; @@ -37,7 +36,7 @@ contract MakerDAOOracle is IOracle, Ownable { function changeMedianier(address _medianizer) public onlyOwner { require(_medianizer != address(0), "0x not allowed"); /*solium-disable-next-line security/no-block-members*/ - emit ChangeMedianizer(_medianizer, medianizer, now); + emit ChangeMedianizer(_medianizer, medianizer); medianizer = _medianizer; } @@ -66,7 +65,7 @@ contract MakerDAOOracle is IOracle, Ownable { /** * @notice Returns price - should throw if not valid */ - function getPrice() external view returns(uint256) { + function getPrice() external returns(uint256) { if (manualOverride) { return manualPrice; } @@ -81,7 +80,7 @@ contract MakerDAOOracle is IOracle, Ownable { */ function setManualPrice(uint256 _price) public onlyOwner { /*solium-disable-next-line security/no-block-members*/ - emit SetManualPrice(manualPrice, _price, now); + emit SetManualPrice(manualPrice, _price); manualPrice = _price; } @@ -92,7 +91,7 @@ contract MakerDAOOracle is IOracle, Ownable { function setManualOverride(bool _override) public onlyOwner { manualOverride = _override; /*solium-disable-next-line security/no-block-members*/ - emit SetManualOverride(_override, now); + emit SetManualOverride(_override); } } diff --git a/contracts/oracles/PolyOracle.sol b/contracts/oracles/PolyOracle.sol index d46242929..a029a11ce 100644 --- a/contracts/oracles/PolyOracle.sol +++ b/contracts/oracles/PolyOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../external/oraclizeAPI.sol"; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; @@ -11,7 +11,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { /*solium-disable-next-line max-len*/ string public oracleURL = "[URL] json(https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?id=2496&convert=USD&CMC_PRO_API_KEY=${[decrypt] BCA0Bqxmn3jkSENepaHxQv09Z/vGdEO9apO+B9RplHyV3qOL/dw5Indlei3hoXrGk9G14My8MFpHJycB7UoVnl+4mlzEsjTlS2UBAYVrl0fAepfiSyM30/GMZAoJmDagY+0YyNZvpkgXn86Q/59Bi48PWEet}).data.\"2496\".quote.USD.price"; string public oracleQueryType = "nested"; - uint256 public sanityBounds = 20*10**16; + uint256 public sanityBounds = 20 * 10 ** 16; uint256 public gasLimit = 100000; uint256 public oraclizeTimeTolerance = 5 minutes; uint256 public staleTime = 6 hours; @@ -20,29 +20,29 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { uint256 public latestUpdate; uint256 public latestScheduledUpdate; - mapping (bytes32 => uint256) public requestIds; - mapping (bytes32 => bool) public ignoreRequestIds; + mapping(bytes32 => uint256) public requestIds; + mapping(bytes32 => bool) public ignoreRequestIds; - mapping (address => bool) public admin; + mapping(address => bool) public admin; bool public freezeOracle; event PriceUpdated(uint256 _price, uint256 _oldPrice, bytes32 _queryId, uint256 _time); event NewOraclizeQuery(uint256 _time, bytes32 _queryId, string _query); - event AdminSet(address _admin, bool _valid, uint256 _time); + event AdminSet(address _admin, bool _valid); event StalePriceUpdate(bytes32 _queryId, uint256 _time, string _result); - modifier isAdminOrOwner { - require(admin[msg.sender] || msg.sender == owner, "Address is not admin or owner"); + modifier isAdminOrOwner() { + require(admin[msg.sender] || msg.sender == owner(), "Address is not admin or owner"); _; } /** * @notice Constructor - accepts ETH to initialise a balance for subsequent Oraclize queries */ - constructor() payable public { + constructor() public payable { // Use 50 gwei for now - oraclize_setCustomGasPrice(50*10**9); + oraclize_setCustomGasPrice(50 * 10 ** 9); } /** @@ -50,7 +50,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @param _requestId requestId corresponding to Oraclize query * @param _result data returned by Oraclize URL query */ - function __callback(bytes32 _requestId, string _result) public { + function __callback(bytes32 _requestId, string memory _result) public { require(msg.sender == oraclize_cbAddress(), "Only Oraclize can access this method"); require(!freezeOracle, "Oracle is frozen"); require(!ignoreRequestIds[_requestId], "Ignoring requestId"); @@ -63,7 +63,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { /*solium-disable-next-line security/no-block-members*/ require(requestIds[_requestId] <= now + oraclizeTimeTolerance, "Result is early"); uint256 newPOLYUSD = parseInt(_result, 18); - uint256 bound = POLYUSD.mul(sanityBounds).div(10**18); + uint256 bound = POLYUSD.mul(sanityBounds).div(10 ** 18); if (latestUpdate != 0) { require(newPOLYUSD <= POLYUSD.add(bound), "Result is too large"); require(newPOLYUSD >= POLYUSD.sub(bound), "Result is too small"); @@ -77,7 +77,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @notice Allows owner to schedule future Oraclize calls * @param _times UNIX timestamps to schedule Oraclize calls as of. Empty list means trigger an immediate query. */ - function schedulePriceUpdatesFixed(uint256[] _times) public payable isAdminOrOwner { + function schedulePriceUpdatesFixed(uint256[] memory _times) public payable isAdminOrOwner { bytes32 requestId; uint256 maximumScheduledUpdated; if (_times.length == 0) { @@ -155,7 +155,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @notice Allows owner to set URL used in Oraclize queries * @param _oracleURL URL to use */ - function setOracleURL(string _oracleURL) public onlyOwner { + function setOracleURL(string memory _oracleURL) public onlyOwner { oracleURL = _oracleURL; } @@ -163,7 +163,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @notice Allows owner to set type used in Oraclize queries * @param _oracleQueryType to use */ - function setOracleQueryType(string _oracleQueryType) public onlyOwner { + function setOracleQueryType(string memory _oracleQueryType) public onlyOwner { oracleQueryType = _oracleQueryType; } @@ -215,7 +215,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @param _requestIds Oraclize queryIds (as logged out when Oraclize query is scheduled) * @param _ignore whether or not they should be ignored */ - function setIgnoreRequestIds(bytes32[] _requestIds, bool[] _ignore) public onlyOwner { + function setIgnoreRequestIds(bytes32[] memory _requestIds, bool[] memory _ignore) public onlyOwner { require(_requestIds.length == _ignore.length, "Incorrect parameter lengths"); for (uint256 i = 0; i < _requestIds.length; i++) { ignoreRequestIds[_requestIds[i]] = _ignore[i]; @@ -230,7 +230,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { function setAdmin(address _admin, bool _valid) public onlyOwner { admin[_admin] = _valid; /*solium-disable-next-line security/no-block-members*/ - emit AdminSet(_admin, _valid, now); + emit AdminSet(_admin, _valid); } /** @@ -265,7 +265,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { /** * @notice Returns price - should throw if not valid */ - function getPrice() external view returns(uint256) { + function getPrice() external returns(uint256) { /*solium-disable-next-line security/no-block-members*/ require(latestUpdate >= now - staleTime, "Invalid price"); return POLYUSD; diff --git a/contracts/oracles/StableOracle.sol b/contracts/oracles/StableOracle.sol new file mode 100644 index 000000000..3dabf26d2 --- /dev/null +++ b/contracts/oracles/StableOracle.sol @@ -0,0 +1,113 @@ +pragma solidity ^0.5.0; + +import "../interfaces/IOracle.sol"; +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +contract StableOracle is IOracle, Ownable { + using SafeMath for uint256; + + IOracle public oracle; + uint256 public lastPrice; + uint256 public evictPercentage; //% multiplid by 10**16 + + bool public manualOverride; + uint256 public manualPrice; + + /*solium-disable-next-line security/no-block-members*/ + event ChangeOracle(address _oldOracle, address _newOracle); + event ChangeEvictPercentage(uint256 _oldEvictPercentage, uint256 _newEvictPercentage); + event SetManualPrice(uint256 _oldPrice, uint256 _newPrice); + event SetManualOverride(bool _override); + + /** + * @notice Creates a new stable oracle based on existing oracle + * @param _oracle address of underlying oracle + */ + constructor(address _oracle, uint256 _evictPercentage) public { + require(_oracle != address(0), "Invalid oracle"); + oracle = IOracle(_oracle); + evictPercentage = _evictPercentage; + } + + /** + * @notice Updates medianizer address + * @param _oracle Address of underlying oracle + */ + function changeOracle(address _oracle) public onlyOwner { + require(_oracle != address(0), "Invalid oracle"); + /*solium-disable-next-line security/no-block-members*/ + emit ChangeOracle(address(oracle), _oracle); + oracle = IOracle(_oracle); + } + + /** + * @notice Updates eviction percentage + * @param _evictPercentage Percentage multiplied by 10**16 + */ + function changeEvictPercentage(uint256 _evictPercentage) public onlyOwner { + emit ChangeEvictPercentage(evictPercentage, _evictPercentage); + evictPercentage = _evictPercentage; + } + + /** + * @notice Returns address of oracle currency (0x0 for ETH) + */ + function getCurrencyAddress() external view returns(address) { + return oracle.getCurrencyAddress(); + } + + /** + * @notice Returns symbol of oracle currency (0x0 for ETH) + */ + function getCurrencySymbol() external view returns(bytes32) { + return oracle.getCurrencySymbol(); + } + + /** + * @notice Returns denomination of price + */ + function getCurrencyDenominated() external view returns(bytes32) { + return oracle.getCurrencyDenominated(); + } + + /** + * @notice Returns price - should throw if not valid + */ + function getPrice() external returns(uint256) { + if (manualOverride) { + return manualPrice; + } + uint256 currentPrice = oracle.getPrice(); + if ((lastPrice == 0) || (_change(currentPrice, lastPrice) >= evictPercentage)) { + lastPrice = currentPrice; + } + return lastPrice; + } + + function _change(uint256 _newPrice, uint256 _oldPrice) internal pure returns(uint256) { + uint256 diff = _newPrice > _oldPrice ? _newPrice.sub(_oldPrice) : _oldPrice.sub(_newPrice); + return diff.mul(10**18).div(_oldPrice); + } + + /** + * @notice Set a manual price. NA - this will only be used if manualOverride == true + * @param _price Price to set + */ + function setManualPrice(uint256 _price) public onlyOwner { + /*solium-disable-next-line security/no-block-members*/ + emit SetManualPrice(manualPrice, _price); + manualPrice = _price; + } + + /** + * @notice Determine whether manual price is used or not + * @param _override Whether to use the manual override price or not + */ + function setManualOverride(bool _override) public onlyOwner { + manualOverride = _override; + /*solium-disable-next-line security/no-block-members*/ + emit SetManualOverride(_override); + } + +} diff --git a/contracts/proxy/CappedSTOProxy.sol b/contracts/proxy/CappedSTOProxy.sol new file mode 100644 index 000000000..264769608 --- /dev/null +++ b/contracts/proxy/CappedSTOProxy.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "../storage/modules/STO/STOStorage.sol"; +import "../storage/modules/ModuleStorage.sol"; +import "../storage/modules/STO/CappedSTOStorage.sol"; + +/** + * @title CappedSTO module Proxy + */ +contract CappedSTOProxy is CappedSTOStorage, STOStorage, ModuleStorage, Pausable, ReentrancyGuard, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/CountTransferManagerProxy.sol b/contracts/proxy/CountTransferManagerProxy.sol new file mode 100644 index 000000000..5ec6727e4 --- /dev/null +++ b/contracts/proxy/CountTransferManagerProxy.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/TransferManager/CountTransferManagerStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title CountTransferManager module Proxy + */ +contract CountTransferManagerProxy is CountTransferManagerStorage, ModuleStorage, Pausable, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/DataStoreProxy.sol b/contracts/proxy/DataStoreProxy.sol new file mode 100644 index 000000000..b79f5579d --- /dev/null +++ b/contracts/proxy/DataStoreProxy.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../datastore/DataStoreStorage.sol"; + +/** + * @title DataStoreProxy Proxy + */ +contract DataStoreProxy is DataStoreStorage, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _implementation representing the address of the new implementation to be set + */ + constructor( + address _securityToken, + address _implementation + ) + public + { + require(_implementation != address(0) && _securityToken != address(0), + "Address should not be 0x" + ); + securityToken = ISecurityToken(_securityToken); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/DummySTOProxy.sol b/contracts/proxy/DummySTOProxy.sol new file mode 100644 index 000000000..3a9924b38 --- /dev/null +++ b/contracts/proxy/DummySTOProxy.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "../storage/modules/STO/STOStorage.sol"; +import "../storage/modules/ModuleStorage.sol"; +import "../storage/modules/STO/DummySTOStorage.sol"; + +/** + * @title DummySTO module Proxy + */ +contract DummySTOProxy is DummySTOStorage, STOStorage, ModuleStorage, Pausable, ReentrancyGuard, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/ERC20DividendCheckpointProxy.sol b/contracts/proxy/ERC20DividendCheckpointProxy.sol new file mode 100644 index 000000000..be16be477 --- /dev/null +++ b/contracts/proxy/ERC20DividendCheckpointProxy.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/Checkpoint/ERC20DividendCheckpointStorage.sol"; +import "../storage/modules/Checkpoint/DividendCheckpointStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract ERC20DividendCheckpointProxy is ERC20DividendCheckpointStorage, DividendCheckpointStorage, ModuleStorage, Pausable, OwnedProxy { + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require(_implementation != address(0), "Implementation address should not be 0x"); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/EtherDividendCheckpointProxy.sol b/contracts/proxy/EtherDividendCheckpointProxy.sol new file mode 100644 index 000000000..cfbab6961 --- /dev/null +++ b/contracts/proxy/EtherDividendCheckpointProxy.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/Checkpoint/DividendCheckpointStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract EtherDividendCheckpointProxy is DividendCheckpointStorage, ModuleStorage, Pausable, OwnedProxy { + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require(_implementation != address(0), "Implementation address should not be 0x"); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/GeneralPermissionManagerProxy.sol b/contracts/proxy/GeneralPermissionManagerProxy.sol new file mode 100644 index 000000000..51a39fbbc --- /dev/null +++ b/contracts/proxy/GeneralPermissionManagerProxy.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "../storage/modules/ModuleStorage.sol"; +import "../storage/modules/PermissionManager/GeneralPermissionManagerStorage.sol"; + +/** + * @title GeneralPermissionManager module Proxy + */ +contract GeneralPermissionManagerProxy is GeneralPermissionManagerStorage, ModuleStorage, Pausable, ReentrancyGuard, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/GeneralTransferManagerProxy.sol b/contracts/proxy/GeneralTransferManagerProxy.sol new file mode 100644 index 000000000..a53fdcb84 --- /dev/null +++ b/contracts/proxy/GeneralTransferManagerProxy.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/TransferManager/GeneralTransferManagerStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract GeneralTransferManagerProxy is GeneralTransferManagerStorage, ModuleStorage, Pausable, OwnedProxy { + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require(_implementation != address(0), "Implementation address should not be 0x"); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/ManualApprovalTransferManagerProxy.sol b/contracts/proxy/ManualApprovalTransferManagerProxy.sol new file mode 100644 index 000000000..a98eed41f --- /dev/null +++ b/contracts/proxy/ManualApprovalTransferManagerProxy.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/TransferManager/ManualApprovalTransferManagerStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + @title ManualApprovalTransferManager module Proxy + */ +contract ManualApprovalTransferManagerProxy is ManualApprovalTransferManagerStorage, ModuleStorage, Pausable, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/ModuleRegistryProxy.sol b/contracts/proxy/ModuleRegistryProxy.sol index aea5a70c5..168905a56 100644 --- a/contracts/proxy/ModuleRegistryProxy.sol +++ b/contracts/proxy/ModuleRegistryProxy.sol @@ -1,9 +1,8 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../storage/EternalStorage.sol"; import "./OwnedUpgradeabilityProxy.sol"; - /** * @title ModuleRegistryProxy * @dev This proxy holds the storage of the ModuleRegistry contract and delegates every call to the current implementation set. @@ -13,4 +12,4 @@ import "./OwnedUpgradeabilityProxy.sol"; /*solium-disable-next-line no-empty-blocks*/ contract ModuleRegistryProxy is EternalStorage, OwnedUpgradeabilityProxy { -} \ No newline at end of file +} diff --git a/contracts/proxy/OwnedProxy.sol b/contracts/proxy/OwnedProxy.sol new file mode 100644 index 000000000..efadc00cf --- /dev/null +++ b/contracts/proxy/OwnedProxy.sol @@ -0,0 +1,90 @@ +pragma solidity ^0.5.0; + +import "./Proxy.sol"; + +/** + * @title OwnedProxy + * @dev This contract combines an upgradeability proxy with basic authorization control functionalities + */ +contract OwnedProxy is Proxy { + // Owner of the contract + address private __owner; + + // Address of the current implementation + address internal __implementation; + + /** + * @dev Event to show ownership has been transferred + * @param _previousOwner representing the address of the previous owner + * @param _newOwner representing the address of the new owner + */ + event ProxyOwnershipTransferred(address _previousOwner, address _newOwner); + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier ifOwner() { + if (msg.sender == _owner()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev the constructor sets the original owner of the contract to the sender account. + */ + constructor() public { + _setOwner(msg.sender); + } + + /** + * @dev Tells the address of the owner + * @return the address of the owner + */ + function _owner() internal view returns(address) { + return __owner; + } + + /** + * @dev Sets the address of the owner + */ + function _setOwner(address _newOwner) internal { + require(_newOwner != address(0), "Address should not be 0x"); + __owner = _newOwner; + } + + /** + * @notice Internal function to provide the address of the implementation contract + */ + function _implementation() internal view returns(address) { + return __implementation; + } + + /** + * @dev Tells the address of the proxy owner + * @return the address of the proxy owner + */ + function proxyOwner() external ifOwner returns(address) { + return _owner(); + } + + /** + * @dev Tells the address of the current implementation + * @return address of the current implementation + */ + function implementation() external ifOwner returns(address) { + return _implementation(); + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function transferProxyOwnership(address _newOwner) external ifOwner { + require(_newOwner != address(0), "Address should not be 0x"); + emit ProxyOwnershipTransferred(_owner(), _newOwner); + _setOwner(_newOwner); + } + +} diff --git a/contracts/proxy/OwnedUpgradeabilityProxy.sol b/contracts/proxy/OwnedUpgradeabilityProxy.sol index 7ec520630..314dc23cb 100644 --- a/contracts/proxy/OwnedUpgradeabilityProxy.sol +++ b/contracts/proxy/OwnedUpgradeabilityProxy.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.5.0; import "./UpgradeabilityProxy.sol"; @@ -7,7 +7,6 @@ import "./UpgradeabilityProxy.sol"; * @dev This contract combines an upgradeability proxy with basic authorization control functionalities */ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { - // Owner of the contract address private __upgradeabilityOwner; @@ -40,7 +39,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @dev Tells the address of the owner * @return the address of the owner */ - function _upgradeabilityOwner() internal view returns (address) { + function _upgradeabilityOwner() internal view returns(address) { return __upgradeabilityOwner; } @@ -55,7 +54,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { /** * @notice Internal function to provide the address of the implementation contract */ - function _implementation() internal view returns (address) { + function _implementation() internal view returns(address) { return __implementation; } @@ -63,7 +62,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @dev Tells the address of the proxy owner * @return the address of the proxy owner */ - function proxyOwner() external ifOwner returns (address) { + function proxyOwner() external ifOwner returns(address) { return _upgradeabilityOwner(); } @@ -71,7 +70,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @dev Tells the version name of the current implementation * @return string representing the name of the current version */ - function version() external ifOwner returns (string) { + function version() external ifOwner returns(string memory) { return __version; } @@ -79,7 +78,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @dev Tells the address of the current implementation * @return address of the current implementation */ - function implementation() external ifOwner returns (address) { + function implementation() external ifOwner returns(address) { return _implementation(); } @@ -98,7 +97,7 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @param _newVersion representing the version name of the new implementation to be set. * @param _newImplementation representing the address of the new implementation to be set. */ - function upgradeTo(string _newVersion, address _newImplementation) external ifOwner { + function upgradeTo(string calldata _newVersion, address _newImplementation) external ifOwner { _upgradeTo(_newVersion, _newImplementation); } @@ -110,10 +109,12 @@ contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { * @param _data represents the msg.data to bet sent in the low level call. This parameter may include the function * signature of the implementation to be called with the needed payload */ - function upgradeToAndCall(string _newVersion, address _newImplementation, bytes _data) external payable ifOwner { + function upgradeToAndCall(string calldata _newVersion, address _newImplementation, bytes calldata _data) external payable ifOwner { _upgradeTo(_newVersion, _newImplementation); + bool success; /*solium-disable-next-line security/no-call-value*/ - require(address(this).call.value(msg.value)(_data), "Fail in executing the function of implementation contract"); + (success, ) = address(this).call.value(msg.value)(_data); + require(success, "Fail in executing the function of implementation contract"); } } diff --git a/contracts/proxy/PercentageTransferManagerProxy.sol b/contracts/proxy/PercentageTransferManagerProxy.sol new file mode 100644 index 000000000..bc5af795f --- /dev/null +++ b/contracts/proxy/PercentageTransferManagerProxy.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/TransferManager/PercentageTransferManagerStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title PercentageTransferManager module Proxy + */ +contract PercentageTransferManagerProxy is PercentageTransferManagerStorage, ModuleStorage, Pausable, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor (address _securityToken, address _polyAddress, address _implementation) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/PreSaleSTOProxy.sol b/contracts/proxy/PreSaleSTOProxy.sol new file mode 100644 index 000000000..e3fe6f573 --- /dev/null +++ b/contracts/proxy/PreSaleSTOProxy.sol @@ -0,0 +1,32 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "../storage/modules/STO/STOStorage.sol"; +import "../storage/modules/ModuleStorage.sol"; +import "../storage/modules/STO/PreSaleSTOStorage.sol"; + +/** + * @title PreSaleSTO module Proxy + */ +contract PreSaleSTOProxy is PreSaleSTOStorage, STOStorage, ModuleStorage, Pausable, ReentrancyGuard, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor (address _securityToken, address _polyAddress, address _implementation) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/Proxy.sol b/contracts/proxy/Proxy.sol index 6cfbce44f..ac9d471ca 100644 --- a/contracts/proxy/Proxy.sol +++ b/contracts/proxy/Proxy.sol @@ -1,16 +1,15 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; /** * @title Proxy * @dev Gives the possibility to delegate any call to a foreign implementation. */ contract Proxy { - /** * @dev Tells the address of the implementation where every call will be delegated. * @return address of the implementation to which it will be delegated */ - function _implementation() internal view returns (address); + function _implementation() internal view returns(address); /** * @dev Fallback function. @@ -31,14 +30,11 @@ contract Proxy { // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) - // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) - // Copy the returned data. returndatacopy(0, 0, returndatasize) - switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } @@ -46,7 +42,7 @@ contract Proxy { } } - function () public payable { + function() external payable { _fallback(); } -} \ No newline at end of file +} diff --git a/contracts/proxy/SecurityTokenRegistryProxy.sol b/contracts/proxy/SecurityTokenRegistryProxy.sol index 6acc6e38f..906797985 100644 --- a/contracts/proxy/SecurityTokenRegistryProxy.sol +++ b/contracts/proxy/SecurityTokenRegistryProxy.sol @@ -1,9 +1,8 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "../storage/EternalStorage.sol"; import "./OwnedUpgradeabilityProxy.sol"; - /** * @title SecurityTokenRegistryProxy * @dev This proxy holds the storage of the SecurityTokenRegistry contract and delegates every call to the current implementation set. @@ -13,4 +12,4 @@ import "./OwnedUpgradeabilityProxy.sol"; /*solium-disable-next-line no-empty-blocks*/ contract SecurityTokenRegistryProxy is EternalStorage, OwnedUpgradeabilityProxy { -} \ No newline at end of file +} diff --git a/contracts/proxy/USDTieredSTOProxy.sol b/contracts/proxy/USDTieredSTOProxy.sol new file mode 100644 index 000000000..109b1ad2e --- /dev/null +++ b/contracts/proxy/USDTieredSTOProxy.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/STO/USDTieredSTOStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "../storage/modules/STO/STOStorage.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title USDTiered STO module Proxy + */ +contract USDTieredSTOProxy is USDTieredSTOStorage, STOStorage, ModuleStorage, Pausable, ReentrancyGuard, OwnedProxy { + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor(address _securityToken, address _polyAddress, address _implementation) public ModuleStorage(_securityToken, _polyAddress) { + require(_implementation != address(0), "Implementation address should not be 0x"); + __implementation = _implementation; + } + +} diff --git a/contracts/proxy/UpgradeabilityProxy.sol b/contracts/proxy/UpgradeabilityProxy.sol index 0f7806b71..dc702dae9 100644 --- a/contracts/proxy/UpgradeabilityProxy.sol +++ b/contracts/proxy/UpgradeabilityProxy.sol @@ -1,14 +1,13 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./Proxy.sol"; -import "openzeppelin-solidity/contracts/AddressUtils.sol"; +import "openzeppelin-solidity/contracts/utils/Address.sol"; /** * @title UpgradeabilityProxy * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded */ contract UpgradeabilityProxy is Proxy { - // Version name of the current implementation string internal __version; @@ -27,12 +26,12 @@ contract UpgradeabilityProxy is Proxy { * @param _newVersion representing the version name of the new implementation to be set * @param _newImplementation representing the address of the new implementation to be set */ - function _upgradeTo(string _newVersion, address _newImplementation) internal { + function _upgradeTo(string memory _newVersion, address _newImplementation) internal { require( __implementation != _newImplementation && _newImplementation != address(0), "Old address is not allowed and implementation address should not be 0x" ); - require(AddressUtils.isContract(_newImplementation), "Cannot set a proxy implementation to a non-contract address"); + require(Address.isContract(_newImplementation), "Cannot set a proxy implementation to a non-contract address"); require(bytes(_newVersion).length > 0, "Version should not be empty string"); require(keccak256(abi.encodePacked(__version)) != keccak256(abi.encodePacked(_newVersion)), "New version equals to current"); __version = _newVersion; @@ -40,4 +39,4 @@ contract UpgradeabilityProxy is Proxy { emit Upgraded(_newVersion, _newImplementation); } -} \ No newline at end of file +} diff --git a/contracts/proxy/VestingEscrowWalletProxy.sol b/contracts/proxy/VestingEscrowWalletProxy.sol new file mode 100644 index 000000000..705adaf4a --- /dev/null +++ b/contracts/proxy/VestingEscrowWalletProxy.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/Wallet/VestingEscrowWalletStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + /** + * @title Escrow wallet module for vesting functionality + */ +contract VestingEscrowWalletProxy is VestingEscrowWalletStorage, ModuleStorage, Pausable, OwnedProxy { + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor (address _securityToken, address _polyAddress, address _implementation) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + } \ No newline at end of file diff --git a/contracts/proxy/VolumeRestrictionTMProxy.sol b/contracts/proxy/VolumeRestrictionTMProxy.sol new file mode 100644 index 000000000..4fb255dba --- /dev/null +++ b/contracts/proxy/VolumeRestrictionTMProxy.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.5.0; + +import "../storage/modules/TransferManager/VolumeRestrictionTMStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../storage/modules/ModuleStorage.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract VolumeRestrictionTMProxy is VolumeRestrictionTMStorage, ModuleStorage, Pausable, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor (address _securityToken, address _polyAddress, address _implementation) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/contracts/storage/EternalStorage.sol b/contracts/storage/EternalStorage.sol index 637b5f2f3..40175f31d 100644 --- a/contracts/storage/EternalStorage.sol +++ b/contracts/storage/EternalStorage.sol @@ -1,7 +1,6 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; contract EternalStorage { - /// @notice Internal mappings used to store all kinds on data into the contract mapping(bytes32 => uint256) internal uintStorage; mapping(bytes32 => string) internal stringStorage; @@ -53,43 +52,10 @@ contract EternalStorage { bytes32Storage[_key] = _value; } - function set(bytes32 _key, string _value) internal { + function set(bytes32 _key, string memory _value) internal { stringStorage[_key] = _value; } - //////////////////// - /// get functions - //////////////////// - /// @notice Get function use to get the value of the singleton state variables - /// Ex1- string public version = "0.0.1"; - /// string _version = getString(keccak256(abi.encodePacked("version")); - /// Ex2 - assert(temp1 == temp2); replace to - /// assert(getUint(keccak256(abi.encodePacked(temp1)) == getUint(keccak256(abi.encodePacked(temp2)); - /// Ex3 - mapping(string => SymbolDetails) registeredSymbols; where SymbolDetails is the structure having different type of values as - /// {uint256 date, string name, address owner} etc. - /// string _name = getString(keccak256(abi.encodePacked("registeredSymbols_name", "TOKEN")); - - function getBool(bytes32 _key) internal view returns (bool) { - return boolStorage[_key]; - } - - function getUint(bytes32 _key) internal view returns (uint256) { - return uintStorage[_key]; - } - - function getAddress(bytes32 _key) internal view returns (address) { - return addressStorage[_key]; - } - - function getString(bytes32 _key) internal view returns (string) { - return stringStorage[_key]; - } - - function getBytes32(bytes32 _key) internal view returns (bytes32) { - return bytes32Storage[_key]; - } - - //////////////////////////// // deleteArray functions //////////////////////////// @@ -99,7 +65,6 @@ contract EternalStorage { /// in this case we have the helper function deleteArrayBytes32() which will do it for us /// deleteArrayBytes32(keccak256(abi.encodePacked("tokensOwnedByOwner", 0x1), 3); -- it will delete the index 3 - //Deletes from mapping (bytes32 => array[]) at index _index function deleteArrayAddress(bytes32 _key, uint256 _index) internal { address[] storage array = addressArrayStorage[_key]; @@ -151,7 +116,7 @@ contract EternalStorage { bytes32ArrayStorage[_key].push(_value); } - function pushArray(bytes32 _key, string _value) internal { + function pushArray(bytes32 _key, string memory _value) internal { stringArrayStorage[_key].push(_value); } @@ -165,21 +130,21 @@ contract EternalStorage { /// @notice used to intialize the array /// Ex1- mapping (address => address[]) public reputation; /// reputation[0x1] = new address[](0); It can be replaced as - /// setArray(hash('reputation', 0x1), new address[](0)); - - function setArray(bytes32 _key, address[] _value) internal { + /// setArray(hash('reputation', 0x1), new address[](0)); + + function setArray(bytes32 _key, address[] memory _value) internal { addressArrayStorage[_key] = _value; } - function setArray(bytes32 _key, uint256[] _value) internal { + function setArray(bytes32 _key, uint256[] memory _value) internal { uintArrayStorage[_key] = _value; } - function setArray(bytes32 _key, bytes32[] _value) internal { + function setArray(bytes32 _key, bytes32[] memory _value) internal { bytes32ArrayStorage[_key] = _value; } - function setArray(bytes32 _key, string[] _value) internal { + function setArray(bytes32 _key, string[] memory _value) internal { stringArrayStorage[_key] = _value; } @@ -192,19 +157,15 @@ contract EternalStorage { /// Ex2- uint256 _len = tokensOwnedByOwner[0x1].length; replace with /// getArrayBytes32(keccak256(abi.encodePacked("tokensOwnedByOwner", 0x1)).length; - function getArrayAddress(bytes32 _key) internal view returns(address[]) { + function getArrayAddress(bytes32 _key) public view returns(address[] memory) { return addressArrayStorage[_key]; } - function getArrayBytes32(bytes32 _key) internal view returns(bytes32[]) { + function getArrayBytes32(bytes32 _key) public view returns(bytes32[] memory) { return bytes32ArrayStorage[_key]; } - function getArrayString(bytes32 _key) internal view returns(string[]) { - return stringArrayStorage[_key]; - } - - function getArrayUint(bytes32 _key) internal view returns(uint[]) { + function getArrayUint(bytes32 _key) public view returns(uint[] memory) { return uintArrayStorage[_key]; } @@ -213,8 +174,8 @@ contract EternalStorage { /////////////////////////////////// /// @notice set the value of particular index of the address array /// Ex1- mapping(bytes32 => address[]) moduleList; - /// general way is -- moduleList[moduleType][index] = temp; - /// It can be re-write as -- setArrayIndexValue(keccak256(abi.encodePacked('moduleList', moduleType)), index, temp); + /// general way is -- moduleList[moduleType][index] = temp; + /// It can be re-write as -- setArrayIndexValue(keccak256(abi.encodePacked('moduleList', moduleType)), index, temp); function setArrayIndexValue(bytes32 _key, uint256 _index, address _value) internal { addressArrayStorage[_key][_index] = _value; @@ -228,35 +189,41 @@ contract EternalStorage { bytes32ArrayStorage[_key][_index] = _value; } - function setArrayIndexValue(bytes32 _key, uint256 _index, string _value) internal { + function setArrayIndexValue(bytes32 _key, uint256 _index, string memory _value) internal { stringArrayStorage[_key][_index] = _value; } - ///////////////////////////// - /// Public getters functions - ///////////////////////////// + /// Public getters functions + /////////////////////// @notice Get function use to get the value of the singleton state variables + /// Ex1- string public version = "0.0.1"; + /// string _version = getString(keccak256(abi.encodePacked("version")); + /// Ex2 - assert(temp1 == temp2); replace to + /// assert(getUint(keccak256(abi.encodePacked(temp1)) == getUint(keccak256(abi.encodePacked(temp2)); + /// Ex3 - mapping(string => SymbolDetails) registeredSymbols; where SymbolDetails is the structure having different type of values as + /// {uint256 date, string name, address owner} etc. + /// string _name = getString(keccak256(abi.encodePacked("registeredSymbols_name", "TOKEN")); - function getUintValues(bytes32 _variable) public view returns(uint256) { + function getUintValue(bytes32 _variable) public view returns(uint256) { return uintStorage[_variable]; } - function getBoolValues(bytes32 _variable) public view returns(bool) { + function getBoolValue(bytes32 _variable) public view returns(bool) { return boolStorage[_variable]; } - function getStringValues(bytes32 _variable) public view returns(string) { + function getStringValue(bytes32 _variable) public view returns(string memory) { return stringStorage[_variable]; } - function getAddressValues(bytes32 _variable) public view returns(address) { + function getAddressValue(bytes32 _variable) public view returns(address) { return addressStorage[_variable]; } - function getBytes32Values(bytes32 _variable) public view returns(bytes32) { + function getBytes32Value(bytes32 _variable) public view returns(bytes32) { return bytes32Storage[_variable]; } - function getBytesValues(bytes32 _variable) public view returns(bytes) { + function getBytesValue(bytes32 _variable) public view returns(bytes memory) { return bytesStorage[_variable]; } diff --git a/contracts/storage/modules/Checkpoint/DividendCheckpointStorage.sol b/contracts/storage/modules/Checkpoint/DividendCheckpointStorage.sol new file mode 100644 index 000000000..27315b16e --- /dev/null +++ b/contracts/storage/modules/Checkpoint/DividendCheckpointStorage.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.5.0; + +/** + * @title Holds the storage variable for the DividendCheckpoint modules (i.e ERC20, Ether) + * @dev abstract contract + */ +contract DividendCheckpointStorage { + + // Address to which reclaimed dividends and withholding tax is sent + address payable public wallet; + uint256 public EXCLUDED_ADDRESS_LIMIT = 150; + bytes32 public constant DISTRIBUTE = "DISTRIBUTE"; + bytes32 public constant MANAGE = "MANAGE"; + bytes32 public constant CHECKPOINT = "CHECKPOINT"; + + struct Dividend { + uint256 checkpointId; + uint256 created; // Time at which the dividend was created + uint256 maturity; // Time after which dividend can be claimed - set to 0 to bypass + uint256 expiry; // Time until which dividend can be claimed - after this time any remaining amount can be withdrawn by issuer - + // set to very high value to bypass + uint256 amount; // Dividend amount in WEI + uint256 claimedAmount; // Amount of dividend claimed so far + uint256 totalSupply; // Total supply at the associated checkpoint (avoids recalculating this) + bool reclaimed; // True if expiry has passed and issuer has reclaimed remaining dividend + uint256 totalWithheld; + uint256 totalWithheldWithdrawn; + mapping (address => bool) claimed; // List of addresses which have claimed dividend + mapping (address => bool) dividendExcluded; // List of addresses which cannot claim dividends + mapping (address => uint256) withheld; // Amount of tax withheld from claim + bytes32 name; // Name/title - used for identification + } + + // List of all dividends + Dividend[] public dividends; + + // List of addresses which cannot claim dividends + address[] public excluded; + + // Mapping from address to withholding tax as a percentage * 10**16 + mapping (address => uint256) public withholdingTax; + + // Total amount of ETH withheld per investor + mapping(address => uint256) public investorWithheld; + +} diff --git a/contracts/storage/modules/Checkpoint/ERC20DividendCheckpointStorage.sol b/contracts/storage/modules/Checkpoint/ERC20DividendCheckpointStorage.sol new file mode 100644 index 000000000..1c6bca68c --- /dev/null +++ b/contracts/storage/modules/Checkpoint/ERC20DividendCheckpointStorage.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.5.0; + +/** + * @title It holds the storage variables related to ERC20DividendCheckpoint module + */ +contract ERC20DividendCheckpointStorage { + // Mapping to token address for each dividend + mapping(uint256 => address) public dividendTokens; + +} diff --git a/contracts/storage/modules/ModuleStorage.sol b/contracts/storage/modules/ModuleStorage.sol new file mode 100644 index 000000000..720c653fb --- /dev/null +++ b/contracts/storage/modules/ModuleStorage.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; + +/** + * @title Storage for Module contract + * @notice Contract is abstract + */ +contract ModuleStorage { + address public factory; + + address public securityToken; + + bytes32 public constant FEE_ADMIN = "FEE_ADMIN"; + + IERC20 public polyToken; + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor(address _securityToken, address _polyAddress) public { + securityToken = _securityToken; + factory = msg.sender; + polyToken = IERC20(_polyAddress); + } + +} diff --git a/contracts/storage/modules/PermissionManager/GeneralPermissionManagerStorage.sol b/contracts/storage/modules/PermissionManager/GeneralPermissionManagerStorage.sol new file mode 100644 index 000000000..cc50bc871 --- /dev/null +++ b/contracts/storage/modules/PermissionManager/GeneralPermissionManagerStorage.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the GeneralPermissionManager storage + */ +contract GeneralPermissionManagerStorage { + + // Mapping used to hold the permissions on the modules provided to delegate, module add => delegate add => permission bytes32 => bool + mapping (address => mapping (address => mapping (bytes32 => bool))) public perms; + // Mapping hold the delagate details + mapping (address => bytes32) public delegateDetails; + // Array to track all delegates + address[] public allDelegates; + + // Permission flag + bytes32 public constant CHANGE_PERMISSION = "CHANGE_PERMISSION"; + +} diff --git a/contracts/storage/modules/STO/CappedSTOStorage.sol b/contracts/storage/modules/STO/CappedSTOStorage.sol new file mode 100644 index 000000000..7ddeea403 --- /dev/null +++ b/contracts/storage/modules/STO/CappedSTOStorage.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the CappedSTO storage + */ +contract CappedSTOStorage { + + // Determine whether users can invest on behalf of a beneficiary + bool public allowBeneficialInvestments = false; + // How many token units a buyer gets (multiplied by 10^18) per wei / base unit of POLY + // If rate is 10^18, buyer will get 1 token unit for every wei / base unit of poly. + uint256 public rate; + //How many token base units this STO will be allowed to sell to investors + // 1 full token = 10^decimals_of_token base units + uint256 public cap; + + mapping (address => uint256) public investors; + +} \ No newline at end of file diff --git a/contracts/storage/modules/STO/DummySTOStorage.sol b/contracts/storage/modules/STO/DummySTOStorage.sol new file mode 100644 index 000000000..0979974ec --- /dev/null +++ b/contracts/storage/modules/STO/DummySTOStorage.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the DummySTO storage + */ +contract DummySTOStorage { + + bytes32 public constant ADMIN = "ADMIN"; + + uint256 public investorCount; + + uint256 public cap; + string public someString; + + mapping (address => uint256) public investors; + +} \ No newline at end of file diff --git a/contracts/storage/modules/STO/ISTOStorage.sol b/contracts/storage/modules/STO/ISTOStorage.sol new file mode 100644 index 000000000..4ff7b9294 --- /dev/null +++ b/contracts/storage/modules/STO/ISTOStorage.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.5.0; + +/** + * @title Storage layout for the ISTO contract + */ +contract ISTOStorage { + + mapping (uint8 => bool) public fundRaiseTypes; + mapping (uint8 => uint256) public fundsRaised; + + // Start time of the STO + uint256 public startTime; + // End time of the STO + uint256 public endTime; + // Time STO was paused + uint256 public pausedTime; + // Number of individual investors + uint256 public investorCount; + // Address where ETH & POLY funds are delivered + address public wallet; + // Final amount of tokens sold + uint256 public totalTokensSold; + +} \ No newline at end of file diff --git a/contracts/storage/modules/STO/PreSaleSTOStorage.sol b/contracts/storage/modules/STO/PreSaleSTOStorage.sol new file mode 100644 index 000000000..9c8ecc0a6 --- /dev/null +++ b/contracts/storage/modules/STO/PreSaleSTOStorage.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the PreSaleSTO storage + */ +contract PreSaleSTOStorage { + + bytes32 public constant PRE_SALE_ADMIN = "PRE_SALE_ADMIN"; + + mapping (address => uint256) public investors; + +} \ No newline at end of file diff --git a/contracts/storage/modules/STO/STOStorage.sol b/contracts/storage/modules/STO/STOStorage.sol new file mode 100644 index 000000000..87ae2c3e0 --- /dev/null +++ b/contracts/storage/modules/STO/STOStorage.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.0; + +/** + * @title Storage layout for the STO contract + */ + +contract STOStorage { + bytes32 internal constant INVESTORFLAGS = "INVESTORFLAGS"; + + mapping (uint8 => bool) public fundRaiseTypes; + mapping (uint8 => uint256) public fundsRaised; + + // Start time of the STO + uint256 public startTime; + // End time of the STO + uint256 public endTime; + // Time STO was paused + uint256 public pausedTime; + // Number of individual investors + uint256 public investorCount; + // Address where ETH & POLY funds are delivered + address payable public wallet; + // Final amount of tokens sold + uint256 public totalTokensSold; + +} diff --git a/contracts/storage/modules/STO/USDTieredSTOStorage.sol b/contracts/storage/modules/STO/USDTieredSTOStorage.sol new file mode 100644 index 000000000..4e37c6dd1 --- /dev/null +++ b/contracts/storage/modules/STO/USDTieredSTOStorage.sol @@ -0,0 +1,79 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; + +/** + * @title Contract used to store layout for the USDTieredSTO storage + */ +contract USDTieredSTOStorage { + + bytes32 internal constant INVESTORSKEY = 0xdf3a8dd24acdd05addfc6aeffef7574d2de3f844535ec91e8e0f3e45dba96731; //keccak256(abi.encodePacked("INVESTORS")) + + ///////////// + // Storage // + ///////////// + struct Tier { + // NB rates mentioned below are actually price and are used like price in the logic. + // How many token units a buyer gets per USD in this tier (multiplied by 10**18) + uint256 rate; + // How many token units a buyer gets per USD in this tier (multiplied by 10**18) when investing in POLY up to tokensDiscountPoly + uint256 rateDiscountPoly; + // How many tokens are available in this tier (relative to totalSupply) + uint256 tokenTotal; + // How many token units are available in this tier (relative to totalSupply) at the ratePerTierDiscountPoly rate + uint256 tokensDiscountPoly; + // How many tokens have been minted in this tier (relative to totalSupply) + uint256 mintedTotal; + // How many tokens have been minted in this tier (relative to totalSupply) for each fund raise type + mapping(uint8 => uint256) minted; + // How many tokens have been minted in this tier (relative to totalSupply) at discounted POLY rate + uint256 mintedDiscountPoly; + } + + mapping(address => uint256) public nonAccreditedLimitUSDOverride; + + mapping(bytes32 => mapping(bytes32 => string)) oracleKeys; + + // Determine whether users can invest on behalf of a beneficiary + bool public allowBeneficialInvestments; + + // Whether or not the STO has been finalized + bool public isFinalized; + + // Address of issuer reserve wallet for unsold tokens + address public reserveWallet; + + // List of stable coin addresses + address[] internal usdTokens; + + // Current tier + uint256 public currentTier; + + // Amount of USD funds raised + uint256 public fundsRaisedUSD; + + // Amount of stable coins raised + mapping (address => uint256) public stableCoinsRaised; + + // Amount in USD invested by each address + mapping(address => uint256) public investorInvestedUSD; + + // Amount in fund raise type invested by each investor + mapping(address => mapping(uint8 => uint256)) public investorInvested; + + // List of active stable coin addresses + mapping (address => bool) internal usdTokenEnabled; + + // Default limit in USD for non-accredited investors multiplied by 10**18 + uint256 public nonAccreditedLimitUSD; + + // Minimum investable amount in USD + uint256 public minimumInvestmentUSD; + + // Final amount of tokens returned to issuer + uint256 public finalAmountReturned; + + // Array of Tiers + Tier[] public tiers; + +} diff --git a/contracts/storage/modules/TransferManager/CountTransferManagerStorage.sol b/contracts/storage/modules/TransferManager/CountTransferManagerStorage.sol new file mode 100644 index 000000000..4a33663b1 --- /dev/null +++ b/contracts/storage/modules/TransferManager/CountTransferManagerStorage.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the CountTransferManager storage + */ +contract CountTransferManagerStorage { + + bytes32 public constant ADMIN = "ADMIN"; + + // The maximum number of concurrent token holders + uint256 public maxHolderCount; + +} diff --git a/contracts/storage/modules/TransferManager/GeneralTransferManagerStorage.sol b/contracts/storage/modules/TransferManager/GeneralTransferManagerStorage.sol new file mode 100644 index 000000000..f246b3675 --- /dev/null +++ b/contracts/storage/modules/TransferManager/GeneralTransferManagerStorage.sol @@ -0,0 +1,51 @@ +pragma solidity ^0.5.0; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract GeneralTransferManagerStorage { + + bytes32 public constant WHITELIST = "WHITELIST"; + bytes32 public constant INVESTORSKEY = 0xdf3a8dd24acdd05addfc6aeffef7574d2de3f844535ec91e8e0f3e45dba96731; //keccak256(abi.encodePacked("INVESTORS")) + bytes32 public constant INVESTORFLAGS = "INVESTORFLAGS"; + bytes32 public constant FLAGS = "FLAGS"; + uint256 internal constant ONE = uint256(1); + + //Address from which issuances come + address public issuanceAddress; + + //Address which can sign whitelist changes + address public signingAddress; + + + // //from and to timestamps that an investor can send / receive tokens respectively + // // Now Stored in DataStore + // struct TimeRestriction { + // uint64 fromTime; + // uint64 toTime; + // uint64 expiryTime; + // uint8 added; + // } + + // Allows all TimeRestrictions to be offset + struct Defaults { + uint64 fromTime; + uint64 toTime; + } + + // Offset to be applied to all timings (except KYC expiry) + Defaults public defaults; + + // Map of used nonces by customer + mapping(address => mapping(uint256 => bool)) public nonceMap; + + //If true, there are no transfer restrictions, for any addresses + bool public allowAllTransfers = false; + //If true, time lock is ignored for transfers (address must still be on whitelist) + bool public allowAllWhitelistTransfers = false; + //If true, time lock is ignored for issuances (address must still be on whitelist) + bool public allowAllWhitelistIssuances = true; + //If true, time lock is ignored for burn transactions + bool public allowAllBurnTransfers = false; + +} diff --git a/contracts/storage/modules/TransferManager/ManualApprovalTransferManagerStorage.sol b/contracts/storage/modules/TransferManager/ManualApprovalTransferManagerStorage.sol new file mode 100644 index 000000000..29671ff42 --- /dev/null +++ b/contracts/storage/modules/TransferManager/ManualApprovalTransferManagerStorage.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the ManualApprovalTransferManager storage + */ +contract ManualApprovalTransferManagerStorage { + + //Address from which issuances come + address public issuanceAddress = address(0); + + //Address which can sign whitelist changes + address public signingAddress = address(0); + + bytes32 public constant TRANSFER_APPROVAL = "TRANSFER_APPROVAL"; + + //Manual approval is an allowance (that has been approved) with an expiry time + struct ManualApproval { + address from; + address to; + uint256 allowance; + uint256 expiryTime; + bytes32 description; + } + + mapping (address => mapping (address => uint256)) public approvalIndex; + // An array to track all approvals + ManualApproval[] public approvals; + +} diff --git a/contracts/storage/modules/TransferManager/PercentageTransferManagerStorage.sol b/contracts/storage/modules/TransferManager/PercentageTransferManagerStorage.sol new file mode 100644 index 000000000..f2db59690 --- /dev/null +++ b/contracts/storage/modules/TransferManager/PercentageTransferManagerStorage.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the PercentageTransferManager storage + */ +contract PercentageTransferManagerStorage { + + // Permission key for modifying the whitelist + bytes32 public constant WHITELIST = "WHITELIST"; + bytes32 public constant ADMIN = "ADMIN"; + + // Maximum percentage that any holder can have, multiplied by 10**16 - e.g. 20% is 20 * 10**16 + uint256 public maxHolderPercentage; + + // Ignore transactions which are part of the primary issuance + bool public allowPrimaryIssuance = true; + + // Addresses on this list are always able to send / receive tokens + mapping (address => bool) public whitelist; + +} diff --git a/contracts/storage/modules/TransferManager/VolumeRestrictionTMStorage.sol b/contracts/storage/modules/TransferManager/VolumeRestrictionTMStorage.sol new file mode 100644 index 000000000..0c88cbd65 --- /dev/null +++ b/contracts/storage/modules/TransferManager/VolumeRestrictionTMStorage.sol @@ -0,0 +1,86 @@ +pragma solidity ^0.5.0; + +/** + * @title Storage layout for VolumeRestrictionTM + */ +contract VolumeRestrictionTMStorage { + + enum RestrictionType { Fixed, Percentage } + + enum TypeOfPeriod { MultipleDays, OneDay, Both } + + struct RestrictedHolder { + // 1 represent true & 0 for false + uint8 seen; + // Type of period will be enum index of TypeOfPeriod enum + uint8 typeOfPeriod; + // Index of the array where the holder address lives + uint128 index; + } + + struct RestrictedData { + mapping(address => RestrictedHolder) restrictedHolders; + address[] restrictedAddresses; + } + + // Restricted data (refernce from the VolumeRestrictionLib library ) + RestrictedData holderData; + + struct VolumeRestriction { + // If typeOfRestriction is `Percentage` then allowedTokens will be in + // the % (w.r.t to totalSupply) with a multiplier of 10**16 . else it + // will be fixed amount of tokens + uint256 allowedTokens; + uint256 startTime; + uint256 rollingPeriodInDays; + uint256 endTime; + RestrictionType typeOfRestriction; + } + + struct IndividualRestrictions { + // Restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) individualRestriction; + // Daily restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) individualDailyRestriction; + } + + // Individual and daily restrictions for investors + IndividualRestrictions individualRestrictions; + + struct GlobalRestrictions { + // Global restriction that applies to all token holders + VolumeRestriction defaultRestriction; + // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) + VolumeRestriction defaultDailyRestriction; + } + + // Individual and daily restrictions for investors + GlobalRestrictions globalRestrictions; + + struct BucketDetails { + uint256 lastTradedDayTime; + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) + uint256 dailyLastTradedDayTime; + } + + struct BucketData { + // Storing _from => day's timestamp => total amount transact in a day --individual + mapping(address => mapping(uint256 => uint256)) bucket; + // Storing the information that used to validate the transaction + mapping(address => BucketDetails) userToBucket; + // Storing the information related to default restriction + mapping(address => BucketDetails) defaultUserToBucket; + } + + BucketData bucketData; + + // Hold exempt index + struct Exemptions { + mapping(address => uint256) exemptIndex; + address[] exemptAddresses; + } + + Exemptions exemptions; + +} diff --git a/contracts/storage/modules/Wallet/VestingEscrowWalletStorage.sol b/contracts/storage/modules/Wallet/VestingEscrowWalletStorage.sol new file mode 100644 index 000000000..f3752ad7f --- /dev/null +++ b/contracts/storage/modules/Wallet/VestingEscrowWalletStorage.sol @@ -0,0 +1,54 @@ +pragma solidity ^0.5.0; + +/** + * @title Wallet for core vesting escrow functionality + */ +contract VestingEscrowWalletStorage { + + struct Schedule { + // Name of the template + bytes32 templateName; + // Tokens that were already claimed + uint256 claimedTokens; + // Start time of the schedule + uint256 startTime; + } + + struct Template { + // Total amount of tokens + uint256 numberOfTokens; + // Schedule duration (How long the schedule will last) + uint256 duration; + // Schedule frequency (It is a cliff time period) + uint256 frequency; + // Index of the template in an array template names + uint256 index; + } + + // Number of tokens that are hold by the `this` contract but are unassigned to any schedule + uint256 public unassignedTokens; + // Address of the Treasury wallet. All of the unassigned token will transfer to that address. + address public treasuryWallet; + // List of all beneficiaries who have the schedules running/completed/created + address[] public beneficiaries; + // Flag whether beneficiary has been already added or not + mapping(address => bool) internal beneficiaryAdded; + + // Holds schedules array corresponds to the affiliate/employee address + mapping(address => Schedule[]) public schedules; + // Holds template names array corresponds to the affiliate/employee address + mapping(address => bytes32[]) internal userToTemplates; + // Mapping use to store the indexes for different template names for a user. + // affiliate/employee address => template name => index + mapping(address => mapping(bytes32 => uint256)) internal userToTemplateIndex; + // Holds affiliate/employee addresses coressponds to the template name + mapping(bytes32 => address[]) internal templateToUsers; + // Mapping use to store the indexes for different users for a template. + // template name => affiliate/employee address => index + mapping(bytes32 => mapping(address => uint256)) internal templateToUserIndex; + // Store the template details corresponds to the template name + mapping(bytes32 => Template) templates; + + // List of all template names + bytes32[] public templateNames; +} \ No newline at end of file diff --git a/contracts/tokens/OZStorage.sol b/contracts/tokens/OZStorage.sol new file mode 100644 index 000000000..536b733c7 --- /dev/null +++ b/contracts/tokens/OZStorage.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.5.0; + +contract OZStorage { + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowed; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + address private _owner; + + /// @dev counter to allow mutex lock with only one SSTORE operation + uint256 private _guardCounter; + + function totalSupply() internal view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address _investor) internal view returns(uint256) { + return _balances[_investor]; + } + +} \ No newline at end of file diff --git a/contracts/tokens/STFactory.sol b/contracts/tokens/STFactory.sol index 85153e816..fc1029f37 100644 --- a/contracts/tokens/STFactory.sol +++ b/contracts/tokens/STFactory.sol @@ -1,17 +1,21 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; import "./SecurityToken.sol"; import "../interfaces/ISTFactory.sol"; +import "../datastore/DataStoreFactory.sol"; /** * @title Proxy for deploying SecurityToken instances */ contract STFactory is ISTFactory { - address public transferManagerFactory; + address public stDelegate; + DataStoreFactory public dataStoreFactory; - constructor (address _transferManagerFactory) public { + constructor(address _transferManagerFactory, address _dataStoreFactory, address _stDelegate) public { transferManagerFactory = _transferManagerFactory; + dataStoreFactory = DataStoreFactory(_dataStoreFactory); + stDelegate = _stDelegate; } /** @@ -19,24 +23,30 @@ contract STFactory is ISTFactory { * Future versions of the proxy can attach different modules or pass different parameters. */ function deployToken( - string _name, - string _symbol, + string calldata _name, + string calldata _symbol, uint8 _decimals, - string _tokenDetails, + string calldata _tokenDetails, address _issuer, bool _divisible, address _polymathRegistry - ) external returns (address) { - address newSecurityTokenAddress = new SecurityToken( + ) + external + returns(address) + { + SecurityToken newSecurityToken = new SecurityToken( _name, _symbol, _decimals, - _divisible ? 1 : uint256(10)**_decimals, + _divisible ? 1 : uint256(10) ** _decimals, _tokenDetails, - _polymathRegistry + _polymathRegistry, + stDelegate ); - SecurityToken(newSecurityTokenAddress).addModule(transferManagerFactory, "", 0, 0); - SecurityToken(newSecurityTokenAddress).transferOwnership(_issuer); - return newSecurityTokenAddress; + //NB When dataStore is generated, the security token address is automatically set via the constructor in DataStoreProxy. + newSecurityToken.changeDataStore(dataStoreFactory.generateDataStore(address(newSecurityToken))); + newSecurityToken.addModule(transferManagerFactory, "", 0, 0); + newSecurityToken.transferOwnership(_issuer); + return address(newSecurityToken); } } diff --git a/contracts/tokens/STGetter.sol b/contracts/tokens/STGetter.sol new file mode 100644 index 000000000..e1712396a --- /dev/null +++ b/contracts/tokens/STGetter.sol @@ -0,0 +1,210 @@ +pragma solidity ^0.5.0; + +import "./OZStorage.sol"; +import "./SecurityTokenStorage.sol"; +import "../libraries/TokenLib.sol"; +import "../interfaces/IDataStore.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../modules/PermissionManager/IPermissionManager.sol"; + +contract STGetter is OZStorage, SecurityTokenStorage { + + using SafeMath for uint256; + + /** + * @notice Used to return the details of a document with a known name (`bytes32`). + * @param _name Name of the document + * @return string The URI associated with the document. + * @return bytes32 The hash (of the contents) of the document. + * @return uint256 the timestamp at which the document was last modified. + */ + function getDocument(bytes32 _name) external view returns (string memory, bytes32, uint256) { + return ( + _documents[_name].uri, + _documents[_name].docHash, + _documents[_name].lastModified + ); + } + + /** + * @notice Used to retrieve a full list of documents attached to the smart contract. + * @return bytes32 List of all documents names present in the contract. + */ + function getAllDocuments() external view returns (bytes32[] memory) { + return _docNames; + } + + /** + * @notice Gets list of times that checkpoints were created + * @return List of checkpoint times + */ + function getCheckpointTimes() external view returns(uint256[] memory) { + return checkpointTimes; + } + + /** + * @notice Returns the count of address that were added as (potential) investors + * @return Investor count + */ + function getInvestorCount() external view returns(uint256) { + IDataStore dataStoreInstance = IDataStore(dataStore); + return dataStoreInstance.getAddressArrayLength(INVESTORSKEY); + } + + /** + * @notice returns an array of investors + * NB - this length may differ from investorCount as it contains all investors that ever held tokens + * @return list of addresses + */ + function getInvestors() public view returns(address[] memory investors) { + IDataStore dataStoreInstance = IDataStore(dataStore); + investors = dataStoreInstance.getAddressArray(INVESTORSKEY); + } + + /** + * @notice returns an array of investors with non zero balance at a given checkpoint + * @param _checkpointId Checkpoint id at which investor list is to be populated + * @return list of investors + */ + function getInvestorsAt(uint256 _checkpointId) external view returns(address[] memory) { + uint256 count; + uint256 i; + IDataStore dataStoreInstance = IDataStore(dataStore); + address[] memory investors = dataStoreInstance.getAddressArray(INVESTORSKEY); + for (i = 0; i < investors.length; i++) { + if (balanceOfAt(investors[i], _checkpointId) > 0) { + count++; + } + } + address[] memory holders = new address[](count); + count = 0; + for (i = 0; i < investors.length; i++) { + if (balanceOfAt(investors[i], _checkpointId) > 0) { + holders[count] = investors[i]; + count++; + } + } + return holders; + } + + /** + * @notice Returns the data associated to a module + * @param _module address of the module + * @return bytes32 name + * @return address module address + * @return address module factory address + * @return bool module archived + * @return uint8 array of module types + * @return bytes32 module label + */ + function getModule(address _module) external view returns(bytes32, address, address, bool, uint8[] memory, bytes32) { + return ( + modulesToData[_module].name, + modulesToData[_module].module, + modulesToData[_module].moduleFactory, + modulesToData[_module].isArchived, + modulesToData[_module].moduleTypes, + modulesToData[_module].label + ); + } + + /** + * @notice Returns a list of modules that match the provided name + * @param _name name of the module + * @return address[] list of modules with this name + */ + function getModulesByName(bytes32 _name) external view returns(address[] memory) { + return names[_name]; + } + + /** + * @notice Returns a list of modules that match the provided module type + * @param _type type of the module + * @return address[] list of modules with this type + */ + function getModulesByType(uint8 _type) external view returns(address[] memory) { + return modules[_type]; + } + + /** + * @notice Queries balances as of a defined checkpoint + * @param _investor Investor to query balance for + * @param _checkpointId Checkpoint ID to query as of + */ + function balanceOfAt(address _investor, uint256 _checkpointId) public view returns(uint256) { + require(_checkpointId <= currentCheckpointId); + return TokenLib.getValueAt(checkpointBalances[_investor], _checkpointId, balanceOf(_investor)); + } + + /** + * @notice Queries totalSupply as of a defined checkpoint + * @param _checkpointId Checkpoint ID to query + * @return uint256 + */ + function totalSupplyAt(uint256 _checkpointId) external view returns(uint256) { + require(_checkpointId <= currentCheckpointId); + return checkpointTotalSupply[_checkpointId]; + } + + /** + * @notice generates subset of investors + * NB - can be used in batches if investor list is large. start and end both are included in array. + * @param _start Position of investor to start iteration from + * @param _end Position of investor to stop iteration at + * @return list of investors + */ + function iterateInvestors(uint256 _start, uint256 _end) external view returns(address[] memory) { + IDataStore dataStoreInstance = IDataStore(dataStore); + return dataStoreInstance.getAddressArrayElements(INVESTORSKEY, _start, _end); + } + + /** + * @notice Validate permissions with PermissionManager if it exists, If no Permission return false + * @dev Note that IModule withPerm will allow ST owner all permissions anyway + * @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions) + * @param _delegate address of delegate + * @param _module address of PermissionManager module + * @param _perm the permissions + * @return success + */ + function checkPermission(address _delegate, address _module, bytes32 _perm) public view returns(bool) { + for (uint256 i = 0; i < modules[PERMISSION_KEY].length; i++) { + if (!modulesToData[modules[PERMISSION_KEY][i]].isArchived) { + if (IPermissionManager(modules[PERMISSION_KEY][i]).checkPermission(_delegate, _module, _perm)) { + return true; + } + } + } + return false; + } + + /** + * @notice Get the balance according to the provided partitions + * @param _owner Whom balance need to queried + * @param _partition Partition which differentiate the tokens. + * @return Amount of tokens as per the given partitions + */ + function balanceOfByPartition(address _owner, bytes32 _partition) external view returns(uint256) { + address[] memory tms = modules[TRANSFER_KEY]; + uint256 _amount = 0; + for (uint256 i = 0; i < tms.length; i++) { + _amount += ITransferManager(tms[i]).getTokensByPartition(_owner, _partition); + } + if (_amount == 0 && _partition == "UNLOCKED") { + return balanceOf(_owner); + } + return _amount; + } + + /** + * @notice Returns the version of the SecurityToken + */ + function getVersion() external view returns(uint8[] memory) { + uint8[] memory _version = new uint8[](3); + _version[0] = securityTokenVersion.major; + _version[1] = securityTokenVersion.minor; + _version[2] = securityTokenVersion.patch; + return _version; + } + +} \ No newline at end of file diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index e9782b2d1..d76d8ad32 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -1,142 +1,73 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "openzeppelin-solidity/contracts/math/Math.sol"; -import "../interfaces/IERC20.sol"; +import "../proxy/Proxy.sol"; +import "../PolymathRegistry.sol"; +import "../libraries/KindMath.sol"; import "../interfaces/IModule.sol"; +import "./SecurityTokenStorage.sol"; +import "../libraries/TokenLib.sol"; import "../interfaces/IModuleFactory.sol"; +import "../interfaces/token/IERC1594.sol"; +import "../interfaces/token/IERC1643.sol"; +import "../interfaces/token/IERC1644.sol"; import "../interfaces/IModuleRegistry.sol"; import "../interfaces/IFeatureRegistry.sol"; -import "../modules/TransferManager/ITransferManager.sol"; -import "../RegistryUpdater.sol"; -import "../libraries/Util.sol"; -import "openzeppelin-solidity/contracts/ReentrancyGuard.sol"; -import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; -import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol"; -import "../libraries/TokenLib.sol"; +import "../interfaces/ITransferManager.sol"; +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; +import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; /** -* @title Security Token contract -* @notice SecurityToken is an ERC20 token with added capabilities: -* @notice - Implements the ST-20 Interface -* @notice - Transfers are restricted -* @notice - Modules can be attached to it to control its behaviour -* @notice - ST should not be deployed directly, but rather the SecurityTokenRegistry should be used -* @notice - ST does not inherit from ISecurityToken due to: -* @notice - https://github.com/ethereum/solidity/issues/4847 -*/ -contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, RegistryUpdater { + * @title Security Token contract + * @notice SecurityToken is an ERC1400 token with added capabilities: + * @notice - Implements the ERC1400 Interface + * @notice - Transfers are restricted + * @notice - Modules can be attached to it to control its behaviour + * @notice - ST should not be deployed directly, but rather the SecurityTokenRegistry should be used + * @notice - ST does not inherit from ISecurityToken due to: + * @notice - https://github.com/ethereum/solidity/issues/4847 + */ +contract SecurityToken is ERC20, ERC20Detailed, Ownable, ReentrancyGuard, SecurityTokenStorage, IERC1594, IERC1643, IERC1644, Proxy { + using SafeMath for uint256; - TokenLib.InvestorDataStorage investorData; - - // Used to hold the semantic version data - struct SemanticVersion { - uint8 major; - uint8 minor; - uint8 patch; - } - - SemanticVersion securityTokenVersion; - - // off-chain data - string public tokenDetails; - - uint8 constant PERMISSION_KEY = 1; - uint8 constant TRANSFER_KEY = 2; - uint8 constant MINT_KEY = 3; - uint8 constant CHECKPOINT_KEY = 4; - uint8 constant BURN_KEY = 5; - - uint256 public granularity; - - // Value of current checkpoint - uint256 public currentCheckpointId; - - // Used to temporarily halt all transactions - bool public transfersFrozen; - - // Used to permanently halt all minting - bool public mintingFrozen; - - // Used to permanently halt controller actions - bool public controllerDisabled; - - // Address whitelisted by issuer as controller - address public controller; - - // Records added modules - module list should be order agnostic! - mapping (uint8 => address[]) modules; - - // Records information about the module - mapping (address => TokenLib.ModuleData) modulesToData; - - // Records added module names - module list should be order agnostic! - mapping (bytes32 => address[]) names; - - // Map each investor to a series of checkpoints - mapping (address => TokenLib.Checkpoint[]) checkpointBalances; - - // List of checkpoints that relate to total supply - TokenLib.Checkpoint[] checkpointTotalSupply; - - // Times at which each checkpoint was created - uint256[] checkpointTimes; - + // Emit when transfers are frozen or unfrozen + event FreezeTransfers(bool _status); + // Emit when is permanently frozen by the issuer + event FreezeIssuance(); + // Emit when the token details get updated + event UpdateTokenDetails(string _oldDetails, string _newDetails); + // Emit when the granularity get changed + event GranularityChanged(uint256 _oldGranularity, uint256 _newGranularity); // Emit at the time when module get added event ModuleAdded( uint8[] _types, - bytes32 _name, - address _moduleFactory, + bytes32 indexed _name, + address indexed _moduleFactory, address _module, uint256 _moduleCost, uint256 _budget, - uint256 _timestamp + bytes32 _label ); - - // Emit when the token details get updated - event UpdateTokenDetails(string _oldDetails, string _newDetails); - // Emit when the granularity get changed - event GranularityChanged(uint256 _oldGranularity, uint256 _newGranularity); // Emit when Module get archived from the securityToken - event ModuleArchived(uint8[] _types, address _module, uint256 _timestamp); + event ModuleArchived(uint8[] _types, address _module); // Emit when Module get unarchived from the securityToken - event ModuleUnarchived(uint8[] _types, address _module, uint256 _timestamp); + event ModuleUnarchived(uint8[] _types, address _module); // Emit when Module get removed from the securityToken - event ModuleRemoved(uint8[] _types, address _module, uint256 _timestamp); + event ModuleRemoved(uint8[] _types, address _module); // Emit when the budget allocated to a module is changed event ModuleBudgetChanged(uint8[] _moduleTypes, address _module, uint256 _oldBudget, uint256 _budget); - // Emit when transfers are frozen or unfrozen - event FreezeTransfers(bool _status, uint256 _timestamp); // Emit when new checkpoint created - event CheckpointCreated(uint256 indexed _checkpointId, uint256 _timestamp); - // Emit when is permanently frozen by the issuer - event FreezeMinting(uint256 _timestamp); - // Events to log minting and burning - event Minted(address indexed _to, uint256 _value); - event Burnt(address indexed _from, uint256 _value); - + event CheckpointCreated(uint256 indexed _checkpointId); // Events to log controller actions event SetController(address indexed _oldController, address indexed _newController); - event ForceTransfer( - address indexed _controller, - address indexed _from, - address indexed _to, - uint256 _value, - bool _verifyTransfer, - bytes _data - ); - event ForceBurn( - address indexed _controller, - address indexed _from, - uint256 _value, - bool _verifyTransfer, - bytes _data - ); - event DisableController(uint256 _timestamp); - - function _isModule(address _module, uint8 _type) internal view returns (bool) { - require(modulesToData[_module].module == _module, "Wrong address"); - require(!modulesToData[_module].isArchived, "Module archived"); + event DisableController(); + + function _isModule(address _module, uint8 _type) internal view returns(bool) { + if (modulesToData[_module].module != _module || modulesToData[_module].isArchived) + return false; for (uint256 i = 0; i < modulesToData[_module].moduleTypes.length; i++) { if (modulesToData[_module].moduleTypes[i] == _type) { return true; @@ -153,7 +84,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // Require msg.sender to be the specified module type or the owner of the token modifier onlyModuleOrOwner(uint8 _type) { - if (msg.sender == owner) { + if (msg.sender == owner()) { _; } else { require(_isModule(msg.sender, _type)); @@ -161,78 +92,86 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr } } - modifier checkGranularity(uint256 _value) { - require(_value % granularity == 0, "Invalid granularity"); + modifier isIssuanceAllowed() { + require(issuance, "Issuance frozen"); _; } - modifier isMintingAllowed() { - require(!mintingFrozen, "Minting frozen"); + modifier checkGranularity(uint256 _value) { + require(_value % granularity == 0, "Invalid granularity"); _; } - - modifier isEnabled(string _nameKey) { - require(IFeatureRegistry(featureRegistry).getFeatureStatus(_nameKey)); + + // Modifier to check whether the msg.sender is authorised or not + modifier onlyController() { + require(msg.sender == controller, "Not Authorised"); _; } - /** - * @notice Revert if called by an account which is not a controller - */ - modifier onlyController() { - require(msg.sender == controller, "Not controller"); - require(!controllerDisabled, "Controller disabled"); + modifier isEnabled(string memory _nameKey) { + require(IFeatureRegistry(featureRegistry).getFeatureStatus(_nameKey)); _; } - + /** - * @notice Constructor + * @notice constructor * @param _name Name of the SecurityToken * @param _symbol Symbol of the Token * @param _decimals Decimals for the securityToken * @param _granularity granular level of the token * @param _tokenDetails Details of the token that are stored off-chain * @param _polymathRegistry Contract address of the polymath registry + * @param _delegate Contract address of the delegate */ - constructor ( - string _name, - string _symbol, + constructor( + string memory _name, + string memory _symbol, uint8 _decimals, uint256 _granularity, - string _tokenDetails, - address _polymathRegistry - ) - public - DetailedERC20(_name, _symbol, _decimals) - RegistryUpdater(_polymathRegistry) + string memory _tokenDetails, + address _polymathRegistry, + address _delegate + ) + public + ERC20Detailed(_name, _symbol, _decimals) { + require(_polymathRegistry != address(0), "Invalid address"); + require(_delegate != address(0), "Invalid address"); + polymathRegistry = _polymathRegistry; //When it is created, the owner is the STR updateFromRegistry(); + delegate = _delegate; tokenDetails = _tokenDetails; granularity = _granularity; - securityTokenVersion = SemanticVersion(2,0,0); + securityTokenVersion = SemanticVersion(2, 0, 0); } /** - * @notice Attachs a module to the SecurityToken - * @dev E.G.: On deployment (through the STR) ST gets a TransferManager module attached to it - * @dev to control restrictions on transfers. - * @param _moduleFactory is the address of the module factory to be added - * @param _data is data packed into bytes used to further configure the module (See STO usage) - * @param _maxCost max amount of POLY willing to pay to the module. - * @param _budget max amount of ongoing POLY willing to assign to the module. - */ - function addModule( + * @notice Attachs a module to the SecurityToken + * @dev E.G.: On deployment (through the STR) ST gets a TransferManager module attached to it + * @dev to control restrictions on transfers. + * @param _moduleFactory is the address of the module factory to be added + * @param _data is data packed into bytes used to further configure the module (See STO usage) + * @param _maxCost max amount of POLY willing to pay to the module. + * @param _budget max amount of ongoing POLY willing to assign to the module. + * @param _label custom module label. + */ + function addModuleWithLabel( address _moduleFactory, - bytes _data, + bytes memory _data, uint256 _maxCost, - uint256 _budget - ) external onlyOwner nonReentrant { + uint256 _budget, + bytes32 _label + ) + public + onlyOwner + nonReentrant + { //Check that the module factory exists in the ModuleRegistry - will throw otherwise IModuleRegistry(moduleRegistry).useModule(_moduleFactory); IModuleFactory moduleFactory = IModuleFactory(_moduleFactory); uint8[] memory moduleTypes = moduleFactory.getTypes(); - uint256 moduleCost = moduleFactory.getSetupCost(); + uint256 moduleCost = moduleFactory.getSetupCostInPoly(); require(moduleCost <= _maxCost, "Invalid cost"); //Approve fee for module ERC20(polyToken).approve(_moduleFactory, moduleCost); @@ -249,13 +188,27 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr moduleIndexes[i] = modules[moduleTypes[i]].length; modules[moduleTypes[i]].push(module); } - modulesToData[module] = TokenLib.ModuleData( - moduleName, module, _moduleFactory, false, moduleTypes, moduleIndexes, names[moduleName].length + modulesToData[module] = ModuleData( + moduleName, + module, + _moduleFactory, + false, + moduleTypes, + moduleIndexes, + names[moduleName].length, + _label ); names[moduleName].push(module); //Emit log event /*solium-disable-next-line security/no-block-members*/ - emit ModuleAdded(moduleTypes, moduleName, _moduleFactory, module, moduleCost, _budget, now); + emit ModuleAdded(moduleTypes, moduleName, _moduleFactory, module, moduleCost, _budget, _label); + } + + /** + * @notice addModule function will call addModuleWithLabel() with an empty label for backward compatible + */ + function addModule(address _moduleFactory, bytes calldata _data, uint256 _maxCost, uint256 _budget) external { + addModuleWithLabel(_moduleFactory, _data, _maxCost, _budget, ""); } /** @@ -279,84 +232,10 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _module address of module to unarchive */ function removeModule(address _module) external onlyOwner { - require(modulesToData[_module].isArchived, "Not archived"); - require(modulesToData[_module].module != address(0), "Module missing"); - /*solium-disable-next-line security/no-block-members*/ - emit ModuleRemoved(modulesToData[_module].moduleTypes, _module, now); - // Remove from module type list - uint8[] memory moduleTypes = modulesToData[_module].moduleTypes; - for (uint256 i = 0; i < moduleTypes.length; i++) { - _removeModuleWithIndex(moduleTypes[i], modulesToData[_module].moduleIndexes[i]); - /* modulesToData[_module].moduleType[moduleTypes[i]] = false; */ - } - // Remove from module names list - uint256 index = modulesToData[_module].nameIndex; - bytes32 name = modulesToData[_module].name; - uint256 length = names[name].length; - names[name][index] = names[name][length - 1]; - names[name].length = length - 1; - if ((length - 1) != index) { - modulesToData[names[name][index]].nameIndex = index; - } - // Remove from modulesToData - delete modulesToData[_module]; - } - - /** - * @notice Internal - Removes a module attached to the SecurityToken by index - */ - function _removeModuleWithIndex(uint8 _type, uint256 _index) internal { - uint256 length = modules[_type].length; - modules[_type][_index] = modules[_type][length - 1]; - modules[_type].length = length - 1; - - if ((length - 1) != _index) { - //Need to find index of _type in moduleTypes of module we are moving - uint8[] memory newTypes = modulesToData[modules[_type][_index]].moduleTypes; - for (uint256 i = 0; i < newTypes.length; i++) { - if (newTypes[i] == _type) { - modulesToData[modules[_type][_index]].moduleIndexes[i] = _index; - } - } - } - } - - /** - * @notice Returns the data associated to a module - * @param _module address of the module - * @return bytes32 name - * @return address module address - * @return address module factory address - * @return bool module archived - * @return uint8 module type - */ - function getModule(address _module) external view returns (bytes32, address, address, bool, uint8[]) { - return (modulesToData[_module].name, - modulesToData[_module].module, - modulesToData[_module].moduleFactory, - modulesToData[_module].isArchived, - modulesToData[_module].moduleTypes); - } - - /** - * @notice Returns a list of modules that match the provided name - * @param _name name of the module - * @return address[] list of modules with this name - */ - function getModulesByName(bytes32 _name) external view returns (address[]) { - return names[_name]; + TokenLib.removeModule(_module, modules, modulesToData, names); } /** - * @notice Returns a list of modules that match the provided module type - * @param _type type of the module - * @return address[] list of modules with this type - */ - function getModulesByType(uint8 _type) external view returns (address[]) { - return modules[_type]; - } - - /** * @notice Allows the owner to withdraw unspent POLY stored by them on the ST or any ERC20 token. * @dev Owner can transfer POLY to the ST which will be used to pay for modules that require a POLY fee. * @param _tokenContract Address of the ERC20Basic compliance token @@ -365,35 +244,24 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr function withdrawERC20(address _tokenContract, uint256 _value) external onlyOwner { require(_tokenContract != address(0)); IERC20 token = IERC20(_tokenContract); - require(token.transfer(owner, _value)); + require(token.transfer(owner(), _value)); } /** - * @notice allows owner to increase/decrease POLY approval of one of the modules * @param _module module address * @param _change change in allowance * @param _increase true if budget has to be increased, false if decrease */ function changeModuleBudget(address _module, uint256 _change, bool _increase) external onlyOwner { - require(modulesToData[_module].module != address(0), "Module missing"); - uint256 currentAllowance = IERC20(polyToken).allowance(address(this), _module); - uint256 newAllowance; - if (_increase) { - require(IERC20(polyToken).increaseApproval(_module, _change), "IncreaseApproval fail"); - newAllowance = currentAllowance.add(_change); - } else { - require(IERC20(polyToken).decreaseApproval(_module, _change), "Insufficient allowance"); - newAllowance = currentAllowance.sub(_change); - } - emit ModuleBudgetChanged(modulesToData[_module].moduleTypes, _module, currentAllowance, newAllowance); + TokenLib.changeModuleBudget(_module, _change, _increase, polyToken, modulesToData); } /** * @notice updates the tokenDetails associated with the token * @param _newTokenDetails New token details */ - function updateTokenDetails(string _newTokenDetails) external onlyOwner { + function updateTokenDetails(string calldata _newTokenDetails) external onlyOwner { emit UpdateTokenDetails(tokenDetails, _newTokenDetails); tokenDetails = _newTokenDetails; } @@ -408,6 +276,15 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr granularity = _granularity; } + /** + * @notice Allows owner to change data store + * @param _dataStore Address of the token data store + */ + function changeDataStore(address _dataStore) external onlyOwner { + require(_dataStore != address(0), "Invalid address"); + dataStore = _dataStore; + } + /** * @notice Keeps track of the number of non-zero token holders * @param _from sender of transfer @@ -415,67 +292,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _value value of transfer */ function _adjustInvestorCount(address _from, address _to, uint256 _value) internal { - TokenLib.adjustInvestorCount(investorData, _from, _to, _value, balanceOf(_to), balanceOf(_from)); - } - - /** - * @notice returns an array of investors - * NB - this length may differ from investorCount as it contains all investors that ever held tokens - * @return list of addresses - */ - function getInvestors() external view returns(address[]) { - return investorData.investors; - } - - /** - * @notice returns an array of investors at a given checkpoint - * NB - this length may differ from investorCount as it contains all investors that ever held tokens - * @param _checkpointId Checkpoint id at which investor list is to be populated - * @return list of investors - */ - function getInvestorsAt(uint256 _checkpointId) external view returns(address[]) { - uint256 count = 0; - uint256 i; - for (i = 0; i < investorData.investors.length; i++) { - if (balanceOfAt(investorData.investors[i], _checkpointId) > 0) { - count++; - } - } - address[] memory investors = new address[](count); - count = 0; - for (i = 0; i < investorData.investors.length; i++) { - if (balanceOfAt(investorData.investors[i], _checkpointId) > 0) { - investors[count] = investorData.investors[i]; - count++; - } - } - return investors; - } - - /** - * @notice generates subset of investors - * NB - can be used in batches if investor list is large - * @param _start Position of investor to start iteration from - * @param _end Position of investor to stop iteration at - * @return list of investors - */ - function iterateInvestors(uint256 _start, uint256 _end) external view returns(address[]) { - require(_end <= investorData.investors.length, "Invalid end"); - address[] memory investors = new address[](_end.sub(_start)); - uint256 index = 0; - for (uint256 i = _start; i < _end; i++) { - investors[index] = investorData.investors[i]; - index++; - } - return investors; - } - - /** - * @notice Returns the investor count - * @return Investor count - */ - function getInvestorCount() external view returns(uint256) { - return investorData.investorCount; + holderCount = TokenLib.adjustInvestorCount(holderCount, _from, _to, _value, balanceOf(_to), balanceOf(_from), dataStore); } /** @@ -485,7 +302,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr require(!transfersFrozen, "Already frozen"); transfersFrozen = true; /*solium-disable-next-line security/no-block-members*/ - emit FreezeTransfers(true, now); + emit FreezeTransfers(true); } /** @@ -495,18 +312,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr require(transfersFrozen, "Not frozen"); transfersFrozen = false; /*solium-disable-next-line security/no-block-members*/ - emit FreezeTransfers(false, now); - } - - /** - * @notice Internal - adjusts totalSupply at checkpoint after minting or burning tokens - */ - function _adjustTotalSupplyCheckpoints() internal { - TokenLib.adjustCheckpoints(checkpointTotalSupply, totalSupply(), currentCheckpointId); + emit FreezeTransfers(false); } /** - * @notice Internal - adjusts token holder balance at checkpoint after a token transfer + * @notice Internal - adjusts token holder balance at checkpoint before a token transfer * @param _investor address of the token holder affected */ function _adjustBalanceCheckpoints(address _investor) internal { @@ -519,21 +329,25 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _value value of transfer * @return bool success */ - function transfer(address _to, uint256 _value) public returns (bool success) { - return transferWithData(_to, _value, ""); + function transfer(address _to, uint256 _value) public returns(bool success) { + transferWithData(_to, _value, ""); + return true; } - + /** - * @notice Overloaded version of the transfer function - * @param _to receiver of transfer - * @param _value value of transfer - * @param _data data to indicate validation - * @return bool success + * @notice Transfer restrictions can take many forms and typically involve on-chain rules or whitelists. + * However for many types of approved transfers, maintaining an on-chain list of approved transfers can be + * cumbersome and expensive. An alternative is the co-signing approach, where in addition to the token holder + * approving a token transfer, and authorised entity provides signed data which further validates the transfer. + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * for the token contract to interpret or record. This could be signed data authorising the transfer + * (e.g. a dynamic whitelist) but is flexible enough to accomadate other use-cases. */ - function transferWithData(address _to, uint256 _value, bytes _data) public returns (bool success) { + function transferWithData(address _to, uint256 _value, bytes memory _data) public { require(_updateTransfer(msg.sender, _to, _value, _data), "Transfer invalid"); require(super.transfer(_to, _value)); - return true; } /** @@ -544,21 +358,26 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @return bool success */ function transferFrom(address _from, address _to, uint256 _value) public returns(bool) { - return transferFromWithData(_from, _to, _value, ""); + transferFromWithData(_from, _to, _value, ""); + return true; } /** - * @notice Overloaded version of the transferFrom function - * @param _from sender of transfer - * @param _to receiver of transfer - * @param _value value of transfer - * @param _data data to indicate validation - * @return bool success + * @notice Transfer restrictions can take many forms and typically involve on-chain rules or whitelists. + * However for many types of approved transfers, maintaining an on-chain list of approved transfers can be + * cumbersome and expensive. An alternative is the co-signing approach, where in addition to the token holder + * approving a token transfer, and authorised entity provides signed data which further validates the transfer. + * @dev `msg.sender` MUST have a sufficient `allowance` set and this `allowance` must be debited by the `_value`. + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * for the token contract to interpret or record. This could be signed data authorising the transfer + * (e.g. a dynamic whitelist) but is flexible enough to accomadate other use-cases. */ - function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) public returns(bool) { + function transferFromWithData(address _from, address _to, uint256 _value, bytes memory _data) public { require(_updateTransfer(_from, _to, _value, _data), "Transfer invalid"); require(super.transferFrom(_from, _to, _value)); - return true; } /** @@ -569,7 +388,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _data data to indicate validation * @return bool success */ - function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal nonReentrant returns(bool) { + function _updateTransfer(address _from, address _to, uint256 _value, bytes memory _data) internal nonReentrant returns(bool) { // NB - the ordering in this function implies the following: // - investor counts are updated before transfer managers are called - i.e. transfer managers will see //investor counts including the current transfer. @@ -578,7 +397,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // - to avoid the situation where a transfer manager transfers tokens, and this function is called recursively, //the function is marked as nonReentrant. This means that no TM can transfer (or mint / burn) tokens. _adjustInvestorCount(_from, _to, _value); - bool verified = _verifyTransfer(_from, _to, _value, _data, true); + bool verified = _executeTransfer(_from, _to, _value, _data); _adjustBalanceCheckpoints(_from); _adjustBalanceCheckpoints(_to); return verified; @@ -587,24 +406,23 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr /** * @notice Validate transfer with TransferManager module if it exists * @dev TransferManager module has a key of 2 - * @dev _isTransfer boolean flag is the deciding factor for whether the - * state variables gets modified or not within the different modules. i.e isTransfer = true - * leads to change in the modules environment otherwise _verifyTransfer() works as a read-only * function (no change in the state). * @param _from sender of transfer * @param _to receiver of transfer * @param _value value of transfer * @param _data data to indicate validation - * @param _isTransfer whether transfer is being executed * @return bool */ - function _verifyTransfer( + function _executeTransfer( address _from, address _to, uint256 _value, - bytes _data, - bool _isTransfer - ) internal checkGranularity(_value) returns (bool) { + bytes memory _data + ) + internal + checkGranularity(_value) + returns(bool) + { if (!transfersFrozen) { bool isInvalid = false; bool isValid = false; @@ -615,7 +433,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr module = modules[TRANSFER_KEY][i]; if (!modulesToData[module].isArchived) { unarchived = true; - ITransferManager.Result valid = ITransferManager(module).verifyTransfer(_from, _to, _value, _data, _isTransfer); + ITransferManager.Result valid = ITransferManager(module).executeTransfer(_from, _to, _value, _data); if (valid == ITransferManager.Result.INVALID) { isInvalid = true; } else if (valid == ITransferManager.Result.VALID) { @@ -632,124 +450,106 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr } /** - * @notice Validates a transfer with a TransferManager module if it exists - * @dev TransferManager module has a key of 2 - * @param _from sender of transfer - * @param _to receiver of transfer - * @param _value value of transfer - * @param _data data to indicate validation - * @return bool + * @notice A security token issuer can specify that issuance has finished for the token + * (i.e. no new tokens can be minted or issued). + * @dev If a token returns FALSE for `isIssuable()` then it MUST always return FALSE in the future. + * If a token returns FALSE for `isIssuable()` then it MUST never allow additional tokens to be issued. + * @return bool `true` signifies the minting is allowed. While `false` denotes the end of minting */ - function verifyTransfer(address _from, address _to, uint256 _value, bytes _data) public returns (bool) { - return _verifyTransfer(_from, _to, _value, _data, false); + function isIssuable() external view returns (bool) { + return issuance; } /** - * @notice Permanently freeze minting of this security token. + * @notice Permanently freeze issuance of this security token. * @dev It MUST NOT be possible to increase `totalSuppy` after this function is called. */ - function freezeMinting() external isMintingAllowed() isEnabled("freezeMintingAllowed") onlyOwner { - mintingFrozen = true; + function freezeIssuance() external isIssuanceAllowed isEnabled("freezeIssuanceAllowed") onlyOwner { + issuance = false; /*solium-disable-next-line security/no-block-members*/ - emit FreezeMinting(now); + emit FreezeIssuance(); } /** - * @notice Mints new tokens and assigns them to the target _investor. - * @dev Can only be called by the issuer or STO attached to the token - * @param _investor Address where the minted tokens will be delivered - * @param _value Number of tokens be minted - * @return success + * @notice This function must be called to increase the total supply (Corresponds to mint function of ERC20). + * @dev It only be called by the token issuer or the operator defined by the issuer. ERC1594 doesn't have + * have the any logic related to operator but its superset ERC1400 have the operator logic and this function + * is allowed to call by the operator. + * @param _tokenHolder The account that will receive the created tokens (account should be whitelisted or KYCed). + * @param _value The amount of tokens need to be issued + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. */ - function mint(address _investor, uint256 _value) public returns (bool success) { - return mintWithData(_investor, _value, ""); + function issue( + address _tokenHolder, + uint256 _value, + bytes memory _data + ) + public + isIssuanceAllowed + onlyModuleOrOwner(MINT_KEY) + { + // Add a function to validate the `_data` parameter + require(_updateTransfer(address(0), _tokenHolder, _value, _data), "Transfer invalid"); + _mint(_tokenHolder, _value); + emit Issued(msg.sender, _tokenHolder, _value, _data); } /** - * @notice mints new tokens and assigns them to the target _investor. - * @dev Can only be called by the issuer or STO attached to the token - * @param _investor Address where the minted tokens will be delivered - * @param _value Number of tokens be minted - * @param _data data to indicate validation + * @notice issue new tokens and assigns them to the target _tokenHolder. + * @dev Can only be called by the issuer or STO attached to the token. + * @param _tokenHolders A list of addresses to whom the minted tokens will be dilivered + * @param _values A list of number of tokens get minted and transfer to corresponding address of the investor from _tokenHolders[] list * @return success */ - function mintWithData( - address _investor, - uint256 _value, - bytes _data - ) public onlyModuleOrOwner(MINT_KEY) isMintingAllowed() returns (bool success) { - require(_investor != address(0), "Investor is 0"); - require(_updateTransfer(address(0), _investor, _value, _data), "Transfer invalid"); - _adjustTotalSupplyCheckpoints(); - totalSupply_ = totalSupply_.add(_value); - balances[_investor] = balances[_investor].add(_value); - emit Minted(_investor, _value); - emit Transfer(address(0), _investor, _value); - return true; + function issueMulti(address[] calldata _tokenHolders, uint256[] calldata _values) external { + require(_tokenHolders.length == _values.length, "Incorrect inputs"); + for (uint256 i = 0; i < _tokenHolders.length; i++) { + issue(_tokenHolders[i], _values[i], ""); + } } /** - * @notice Mints new tokens and assigns them to the target _investor. - * @dev Can only be called by the issuer or STO attached to the token. - * @param _investors A list of addresses to whom the minted tokens will be dilivered - * @param _values A list of number of tokens get minted and transfer to corresponding address of the investor from _investor[] list - * @return success + * @notice This function redeem an amount of the token of a msg.sender. For doing so msg.sender may incentivize + * using different ways that could be implemented with in the `redeem` function definition. But those implementations + * are out of the scope of the ERC1594. + * @param _value The amount of tokens need to be redeemed + * @param _data The `bytes _data` it can be used in the token contract to authenticate the redemption. */ - function mintMulti(address[] _investors, uint256[] _values) external returns (bool success) { - require(_investors.length == _values.length, "Incorrect inputs"); - for (uint256 i = 0; i < _investors.length; i++) { - mint(_investors[i], _values[i]); - } - return true; + function redeem(uint256 _value, bytes calldata _data) external onlyModule(BURN_KEY) { + // Add a function to validate the `_data` parameter + require(_checkAndBurn(msg.sender, _value, _data), "Invalid redeem"); } /** - * @notice Validate permissions with PermissionManager if it exists, If no Permission return false - * @dev Note that IModule withPerm will allow ST owner all permissions anyway - * @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions) - * @param _delegate address of delegate - * @param _module address of PermissionManager module - * @param _perm the permissions - * @return success + * @notice Checks if an address is a module of certain type + * @param _module Address to check + * @param _type type to check against */ - function checkPermission(address _delegate, address _module, bytes32 _perm) public view returns(bool) { - for (uint256 i = 0; i < modules[PERMISSION_KEY].length; i++) { - if (!modulesToData[modules[PERMISSION_KEY][i]].isArchived) - return TokenLib.checkPermission(modules[PERMISSION_KEY], _delegate, _module, _perm); - } - return false; + function isModule(address _module, uint8 _type) public view returns(bool) { + return _isModule(_module, _type); } - function _burn(address _from, uint256 _value, bytes _data) internal returns(bool) { - require(_value <= balances[_from], "Value too high"); + function _checkAndBurn(address _from, uint256 _value, bytes memory _data) internal returns(bool) { bool verified = _updateTransfer(_from, address(0), _value, _data); - _adjustTotalSupplyCheckpoints(); - balances[_from] = balances[_from].sub(_value); - totalSupply_ = totalSupply_.sub(_value); - emit Burnt(_from, _value); - emit Transfer(_from, address(0), _value); + _burn(_from, _value); + emit Redeemed(address(0), msg.sender, _value, _data); return verified; } /** - * @notice Burn function used to burn the securityToken - * @param _value No. of tokens that get burned - * @param _data data to indicate validation + * @notice This function redeem an amount of the token of a msg.sender. For doing so msg.sender may incentivize + * using different ways that could be implemented with in the `redeem` function definition. But those implementations + * are out of the scope of the ERC1594. + * @dev It is analogy to `transferFrom` + * @param _tokenHolder The account whose tokens gets redeemed. + * @param _value The amount of tokens need to be redeemed + * @param _data The `bytes _data` it can be used in the token contract to authenticate the redemption. */ - function burnWithData(uint256 _value, bytes _data) public onlyModule(BURN_KEY) { - require(_burn(msg.sender, _value, _data), "Burn invalid"); - } - - /** - * @notice Burn function used to burn the securityToken on behalf of someone else - * @param _from Address for whom to burn tokens - * @param _value No. of tokens that get burned - * @param _data data to indicate validation - */ - function burnFromWithData(address _from, uint256 _value, bytes _data) public onlyModule(BURN_KEY) { - require(_value <= allowed[_from][msg.sender], "Value too high"); - allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); - require(_burn(_from, _value, _data), "Burn invalid"); + function redeemFrom(address _tokenHolder, uint256 _value, bytes calldata _data) external onlyModule(BURN_KEY) { + // Add a function to validate the `_data` parameter + require(_updateTransfer(_tokenHolder, address(0), _value, _data), "Invalid redeem"); + _burnFrom(_tokenHolder, _value); + emit Redeemed(msg.sender, _tokenHolder, _value, _data); } /** @@ -757,104 +557,198 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @return uint256 */ function createCheckpoint() external onlyModuleOrOwner(CHECKPOINT_KEY) returns(uint256) { - require(currentCheckpointId < 2**256 - 1); + require(currentCheckpointId < 2 ** 256 - 1); currentCheckpointId = currentCheckpointId + 1; /*solium-disable-next-line security/no-block-members*/ checkpointTimes.push(now); /*solium-disable-next-line security/no-block-members*/ - emit CheckpointCreated(currentCheckpointId, now); + checkpointTotalSupply[currentCheckpointId] = totalSupply(); + emit CheckpointCreated(currentCheckpointId); return currentCheckpointId; } /** - * @notice Gets list of times that checkpoints were created - * @return List of checkpoint times + * @notice Used by the issuer to set the controller addresses + * @param _controller address of the controller */ - function getCheckpointTimes() external view returns(uint256[]) { - return checkpointTimes; + function setController(address _controller) public onlyOwner { + require(_isControllable()); + // Below condition is to restrict the owner/issuer to become the controller(In an ideal world). + // But for non ideal case issuer could set another address which is not the owner of the token + // but issuer holds its private key. + require(_controller != msg.sender); + emit SetController(controller, _controller); + controller = _controller; } /** - * @notice Queries totalSupply as of a defined checkpoint - * @param _checkpointId Checkpoint ID to query - * @return uint256 + * @notice Used by the issuer to permanently disable controller functionality + * @dev enabled via feature switch "disableControllerAllowed" */ - function totalSupplyAt(uint256 _checkpointId) external view returns(uint256) { - require(_checkpointId <= currentCheckpointId); - return TokenLib.getValueAt(checkpointTotalSupply, _checkpointId, totalSupply()); + function disableController() external isEnabled("disableControllerAllowed") onlyOwner { + require(_isControllable()); + controllerDisabled = true; + delete controller; + emit DisableController(); } /** - * @notice Queries balances as of a defined checkpoint - * @param _investor Investor to query balance for - * @param _checkpointId Checkpoint ID to query as of + * @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the + * cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped + * with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * @return bool It signifies whether the transaction will be executed or not. + * @return byte Ethereum status code (ESC) + * @return bytes32 Application specific reason code */ - function balanceOfAt(address _investor, uint256 _checkpointId) public view returns(uint256) { - require(_checkpointId <= currentCheckpointId); - return TokenLib.getValueAt(checkpointBalances[_investor], _checkpointId, balanceOf(_investor)); + function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32) { + return _canTransfer(msg.sender, _to, _value, _data); } /** - * @notice Used by the issuer to set the controller addresses - * @param _controller address of the controller + * @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the + * cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped + * with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer. + * @return bool It signifies whether the transaction will be executed or not. + * @return byte Ethereum status code (ESC) + * @return bytes32 Application specific reason code */ - function setController(address _controller) public onlyOwner { - require(!controllerDisabled); - emit SetController(controller, _controller); - controller = _controller; + function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32) { + (bool success, byte reasonCode, bytes32 appCode) = _canTransfer(_from, _to, _value, _data); + if (success && _value > allowance(_from, msg.sender)) { + return (false, 0x53, bytes32(0)); + } else + return (success, reasonCode, appCode); + } + + function _canTransfer(address _from, address _to, uint256 _value, bytes memory _data) internal view returns (bool, byte, bytes32) { + bytes32 appCode; + bool success; + if (_value % granularity != 0) { + return (false, 0x50, "Invalid granularity"); + } + (success, appCode) = TokenLib.verifyTransfer(modules[TRANSFER_KEY], modulesToData, _from, _to, _value, _data, transfersFrozen); + if (!success) + return (false, 0x50, appCode); + + else if (balanceOf(_from) < _value) + return (false, 0x52, bytes32(0)); + + else if (_to == address(0)) + return (false, 0x57, bytes32(0)); + + else if (!KindMath.checkAdd(balanceOf(_to), _value)) + return (false, 0x50, bytes32(0)); + return (true, 0x51, bytes32(0)); } /** - * @notice Used by the issuer to permanently disable controller functionality - * @dev enabled via feature switch "disableControllerAllowed" + * @notice Used to attach a new document to the contract, or update the URI or hash of an existing attached document + * @dev Can only be executed by the owner of the contract. + * @param _name Name of the document. It should be unique always + * @param _uri Off-chain uri of the document from where it is accessible to investors/advisors to read. + * @param _documentHash hash (of the contents) of the document. */ - function disableController() external isEnabled("disableControllerAllowed") onlyOwner { - require(!controllerDisabled); - controllerDisabled = true; - delete controller; - /*solium-disable-next-line security/no-block-members*/ - emit DisableController(now); + function setDocument(bytes32 _name, string calldata _uri, bytes32 _documentHash) external onlyOwner { + require(_name != bytes32(0), "Zero value is not allowed"); + require(bytes(_uri).length > 0, "Should not be a empty uri"); + if (_documents[_name].lastModified == uint256(0)) { + _docNames.push(_name); + _docIndexes[_name] = _docNames.length; + } + _documents[_name] = Document(_documentHash, now, _uri); + emit DocumentUpdated(_name, _uri, _documentHash); } /** - * @notice Used by a controller to execute a forced transfer - * @param _from address from which to take tokens - * @param _to address where to send tokens - * @param _value amount of tokens to transfer - * @param _data data to indicate validation - * @param _log data attached to the transfer by controller to emit in event + * @notice Used to remove an existing document from the contract by giving the name of the document. + * @dev Can only be executed by the owner of the contract. + * @param _name Name of the document. It should be unique always + */ + function removeDocument(bytes32 _name) external onlyOwner { + require(_documents[_name].lastModified != uint256(0), "Document should be existed"); + uint256 index = _docIndexes[_name] - 1; + if (index != _docNames.length - 1) { + _docNames[index] = _docNames[_docNames.length - 1]; + _docIndexes[_docNames[index]] = index + 1; + } + _docNames.length--; + emit DocumentRemoved(_name, _documents[_name].uri, _documents[_name].docHash); + delete _documents[_name]; + } + + /** + * @notice Internal function to know whether the controller functionality + * allowed or not. + * @return bool `true` when controller address is non-zero otherwise return `false`. */ - function forceTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _log) public onlyController { - require(_to != address(0)); - require(_value <= balances[_from]); - bool verified = _updateTransfer(_from, _to, _value, _data); - balances[_from] = balances[_from].sub(_value); - balances[_to] = balances[_to].add(_value); - emit ForceTransfer(msg.sender, _from, _to, _value, verified, _log); - emit Transfer(_from, _to, _value); + function _isControllable() internal view returns (bool) { + return !controllerDisabled; } /** - * @notice Used by a controller to execute a forced burn - * @param _from address from which to take tokens - * @param _value amount of tokens to transfer - * @param _data data to indicate validation - * @param _log data attached to the transfer by controller to emit in event + * @notice In order to provide transparency over whether `controllerTransfer` / `controllerRedeem` are useable + * or not `isControllable` function will be used. + * @dev If `isControllable` returns `false` then it always return `false` and + * `controllerTransfer` / `controllerRedeem` will always revert. + * @return bool `true` when controller address is non-zero otherwise return `false`. + */ + function isControllable() external view returns (bool) { + return _isControllable(); + } + + /** + * @notice This function allows an authorised address to transfer tokens between any two token holders. + * The transfer must still respect the balances of the token holders (so the transfer must be for at most + * `balanceOf(_from)` tokens) and potentially also need to respect other transfer restrictions. + * @dev This function can only be executed by the `controller` address. + * @param _from Address The address which you want to send tokens from + * @param _to Address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + * @param _data data to validate the transfer. (It is not used in this reference implementation + * because use of `_data` parameter is implementation specific). + * @param _operatorData data attached to the transfer by controller to emit in event. (It is more like a reason string + * for calling this function (aka force transfer) which provides the transparency on-chain). */ - function forceBurn(address _from, uint256 _value, bytes _data, bytes _log) public onlyController { - bool verified = _burn(_from, _value, _data); - emit ForceBurn(msg.sender, _from, _value, verified, _log); + function controllerTransfer(address _from, address _to, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external onlyController { + require(_isControllable()); + _updateTransfer(_from, _to, _value, _data); + _transfer(_from, _to, _value); + emit ControllerTransfer(msg.sender, _from, _to, _value, _data, _operatorData); } /** - * @notice Returns the version of the SecurityToken + * @notice This function allows an authorised address to redeem tokens for any token holder. + * The redemption must still respect the balances of the token holder (so the redemption must be for at most + * `balanceOf(_tokenHolder)` tokens) and potentially also need to respect other transfer restrictions. + * @dev This function can only be executed by the `controller` address. + * @param _tokenHolder The account whose tokens will be redeemed. + * @param _value uint256 the amount of tokens need to be redeemed. + * @param _data data to validate the transfer. (It is not used in this reference implementation + * because use of `_data` parameter is implementation specific). + * @param _operatorData data attached to the transfer by controller to emit in event. (It is more like a reason string + * for calling this function (aka force transfer) which provides the transparency on-chain). */ - function getVersion() external view returns(uint8[]) { - uint8[] memory _version = new uint8[](3); - _version[0] = securityTokenVersion.major; - _version[1] = securityTokenVersion.minor; - _version[2] = securityTokenVersion.patch; - return _version; + function controllerRedeem(address _tokenHolder, uint256 _value, bytes calldata _data, bytes calldata _operatorData) external onlyController { + require(_isControllable()); + _checkAndBurn(_tokenHolder, _value, _data); + emit ControllerRedemption(msg.sender, _tokenHolder, _value, _data, _operatorData); } + function _implementation() internal view returns(address) { + return delegate; + } + + function updateFromRegistry() public onlyOwner { + moduleRegistry = PolymathRegistry(polymathRegistry).getAddress("ModuleRegistry"); + securityTokenRegistry = PolymathRegistry(polymathRegistry).getAddress("SecurityTokenRegistry"); + featureRegistry = PolymathRegistry(polymathRegistry).getAddress("FeatureRegistry"); + polyToken = PolymathRegistry(polymathRegistry).getAddress("PolyToken"); + } } diff --git a/contracts/tokens/SecurityTokenStorage.sol b/contracts/tokens/SecurityTokenStorage.sol new file mode 100644 index 000000000..293bb4727 --- /dev/null +++ b/contracts/tokens/SecurityTokenStorage.sol @@ -0,0 +1,112 @@ +pragma solidity ^0.5.0; + +contract SecurityTokenStorage { + + uint8 constant PERMISSION_KEY = 1; + uint8 constant TRANSFER_KEY = 2; + uint8 constant MINT_KEY = 3; + uint8 constant CHECKPOINT_KEY = 4; + uint8 constant BURN_KEY = 5; + uint8 constant DATA_KEY = 6; + bytes32 internal constant INVESTORSKEY = 0xdf3a8dd24acdd05addfc6aeffef7574d2de3f844535ec91e8e0f3e45dba96731; //keccak256(abi.encodePacked("INVESTORS")) + + ////////////////////////// + /// Document datastructure + ////////////////////////// + + struct Document { + bytes32 docHash; // Hash of the document + uint256 lastModified; // Timestamp at which document details was last modified + string uri; // URI of the document that exist off-chain + } + + // Used to hold the semantic version data + struct SemanticVersion { + uint8 major; + uint8 minor; + uint8 patch; + } + + // Struct for module data + struct ModuleData { + bytes32 name; + address module; + address moduleFactory; + bool isArchived; + uint8[] moduleTypes; + uint256[] moduleIndexes; + uint256 nameIndex; + bytes32 label; + } + + // Structures to maintain checkpoints of balances for governance / dividends + struct Checkpoint { + uint256 checkpointId; + uint256 value; + } + + // Address of the controller which is a delegated entity + // set by the issuer/owner of the token + address public controller; + + address public polymathRegistry; + address public moduleRegistry; + address public securityTokenRegistry; + address public featureRegistry; + address public polyToken; + address public delegate; + // Address of the data store used to store shared data + address public dataStore; + + uint256 public granularity; + + // Value of current checkpoint + uint256 public currentCheckpointId; + + // off-chain data + string public tokenDetails; + + // Used to permanently halt controller actions + bool public controllerDisabled = false; + + // Used to temporarily halt all transactions + bool public transfersFrozen; + + // Number of investors with non-zero balance + uint256 public holderCount; + + // Variable which tells whether issuance is ON or OFF forever + // Implementers need to implement one more function to reset the value of `issuance` variable + // to false. That function is not a part of the standard (EIP-1594) as it is depend on the various factors + // issuer, followed compliance rules etc. So issuers have the choice how they want to close the issuance. + bool internal issuance = true; + + // Array use to store all the document name present in the contracts + bytes32[] _docNames; + + // Times at which each checkpoint was created + uint256[] checkpointTimes; + + SemanticVersion securityTokenVersion; + + // Records added modules - module list should be order agnostic! + mapping(uint8 => address[]) modules; + + // Records information about the module + mapping(address => ModuleData) modulesToData; + + // Records added module names - module list should be order agnostic! + mapping(bytes32 => address[]) names; + + // Mapping of checkpoints that relate to total supply + mapping (uint256 => uint256) checkpointTotalSupply; + + // Map each investor to a series of checkpoints + mapping(address => Checkpoint[]) checkpointBalances; + + // mapping to store the documents details in the document + mapping(bytes32 => Document) internal _documents; + // mapping to store the document name indexes + mapping(bytes32 => uint256) internal _docIndexes; + +} \ No newline at end of file diff --git a/docs/ethereum_status_codes.md b/docs/ethereum_status_codes.md new file mode 100644 index 000000000..60a2ff0d9 --- /dev/null +++ b/docs/ethereum_status_codes.md @@ -0,0 +1,46 @@ + +# For ERC1400 + +| Code | Reason | +| ------ | ------------------------------------------------------------- | +| `0x50` | transfer failure | +| `0x51` | transfer success | +| `0x52` | insufficient balance | +| `0x53` | insufficient allowance | +| `0x54` | transfers halted (contract paused) | +| `0x55` | funds locked (lockup period) | +| `0x56` | invalid sender | +| `0x57` | invalid receiver | +| `0x58` | invalid operator (transfer agent) | +| `0x59` | | +| `0x5a` | | +| `0x5b` | | +| `0x5a` | | +| `0x5b` | | +| `0x5c` | | +| `0x5d` | | +| `0x5e` | | +| `0x5f` | token meta or info + +# For Application specific (Polymath) + +| Code | Reason | +| ------ | ------------------------------------------------------------- | +| `0xA0` | Not affected | +| `0xA1` | Success | +| `0xA2` | Max holders reach | +| `0xA3` | Manual Approval Expired | +| `0xA4` | funds limit reached | +| `0xA5` | Tx Volume limit reached | +| `0xA6` | Blacklisted tx | +| `0xA7` | funds locked (lockup period) | +| `0xA8` | Invalid granularity | +| `0xA9` | | +| `0xAa` | | +| `0xAb` | | +| `0xAa` | | +| `0xAb` | | +| `0xAc` | | +| `0xAd` | | +| `0xAe` | | +| `0xAf` | token meta or info | diff --git a/docs/images/Core Architecture 2.0.0 Diagram.png b/docs/images/Core Architecture 2.0.0 Diagram.png new file mode 100644 index 000000000..e227d159c Binary files /dev/null and b/docs/images/Core Architecture 2.0.0 Diagram.png differ diff --git a/docs/investor_flags.md b/docs/investor_flags.md new file mode 100644 index 000000000..9ccf74473 --- /dev/null +++ b/docs/investor_flags.md @@ -0,0 +1,23 @@ +# Flags List + + + + + + + + + + + + + + + + + + + + + +
Flag Name Description
0 isAccredited Defines if an Investor is Accredited or not. True for Accredited, false for not Accredited.
1 canNotBuyFromSto Defines if an Investor is restricted from participating in STOs
diff --git a/docs/permissions_list.md b/docs/permissions_list.md index b49c95a0e..9e7a213db 100644 --- a/docs/permissions_list.md +++ b/docs/permissions_list.md @@ -143,7 +143,7 @@ allocateTokensMulti() - TransferManager + TransferManager CountTransferManager changeHolderCount() withPerm(ADMIN) @@ -176,19 +176,13 @@ modifyWhitelistMulti() - ManualApprovalTransferManager + ManualApprovalTransferManager addManualApproval() - withPerm(TRANSFER_APPROVAL) - - - addManualBlocking() + withPerm(TRANSFER_APPROVAL) revokeManualApproval() - - revokeManualBlocking() - PercentageTransferManager modifyWhitelist() @@ -205,72 +199,142 @@ changeHolderPercentage() - LockupVolumeRestrictionTM - addLockup() - withPerm(ADMIN) + VolumeRestrictionTM + changeExemptWalletList() + withPerm(ADMIN) - addLockUpMulti() + addIndividualRestriction() - removeLockUp() + addIndividualRestrictionMulti() - modifyLockUp() + addGlobalRestriction() - SingleTradeVolumeRestrictionTM - setAllowPrimaryIssuance() - withPerm(ADMIN) + addDailyGlobalRestriction() + + + removeIndividualRestriction() + + + removeIndividualRestrictionMulti() + + + removeGlobalRestriction() + + + removeDailyGlobalRestriction() + + + modifyIndividualRestriction() + + + modifyIndividualRestrictionMulti() + + + modifyGlobalRestriction() + + + modifyDailyGlobalRestriction() + + + BlacklistTransferManager + addBlacklistType() + withPerm(ADMIN) + + + addBlacklistTypeMulti() - changeTransferLimitToPercentage() + modifyBlacklistType() - changeTransferLimitToTokens() + modifyBlacklistTypeMulti() - changeGlobalLimitInTokens() + deleteBlacklistType() - changeGlobalLimitInPercentage() + deleteBlacklistTypeMulti() - addExemptWallet() + addInvestorToBlacklist() - removeExemptWallet() + addInvestorToBlacklistMulti() - addExemptWalletMulti() + addMultiInvestorToBlacklistMulti() - removeExemptWalletMulti() + addInvestorToNewBlacklist() - setTransferLimitInTokens() + deleteInvestorFromAllBlacklist() - setTransferLimitInPercentage() + deleteInvestorFromAllBlacklistMulti() - removeTransferLimitInPercentage() + deleteInvestorFromBlacklist() - removeTransferLimitInTokens() + deleteMultiInvestorsFromBlacklistMulti() - setTransferLimitInTokensMulti() + Wallet + VestingEscrowWallet + changeTreasuryWallet() + onlyOwner - setTransferLimitInPercentageMulti() + depositTokens() + withPerm(ADMIN) - removeTransferLimitInTokensMulti() + sendToTreasury() - removeTransferLimitInPercentageMulti + pushAvailableTokens() + + addTemplate() + + + removeTemplate() + + + addSchedule() + + + addScheduleFromTemplate() + + + modifySchedule() + + + revokeSchedule() + + + revokeAllSchedules() + + + pushAvailableTokensMulti() + + + addScheduleMulti() + + + addScheduleFromTemplateMulti() + + + revokeSchedulesMulti() + + + modifyScheduleMulti() + diff --git a/migrations/1_deploy_token.js b/migrations/1_deploy_token.js index 47f0e97a9..a31ddc4a7 100644 --- a/migrations/1_deploy_token.js +++ b/migrations/1_deploy_token.js @@ -1,9 +1,8 @@ -const DevPolyToken = artifacts.require('./helpers/PolyTokenFaucet.sol') -const Web3 = require('web3') -web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) +const DevPolyToken = artifacts.require("./helpers/PolyTokenFaucet.sol"); +const Web3 = require("web3"); +web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); -module.exports = function (deployer, network, accounts) { - const PolymathAccount = accounts[0] - return deployer.deploy(DevPolyToken, {from: PolymathAccount}).then(() => {}) - -} \ No newline at end of file +module.exports = function(deployer, network, accounts) { + const PolymathAccount = accounts[0]; + return deployer.deploy(DevPolyToken, { from: PolymathAccount }).then(() => {}); +}; diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 14bdf9fed..a0f718c20 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -1,326 +1,574 @@ -const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') -const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol') -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol') -const PercentageTransferManagerFactory = artifacts.require('./PercentageTransferManagerFactory.sol') -const USDTieredSTOProxyFactory = artifacts.require('./USDTieredSTOProxyFactory.sol'); -const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol') -const EtherDividendCheckpointFactory = artifacts.require('./EtherDividendCheckpointFactory.sol') -const ERC20DividendCheckpointFactory = artifacts.require('./ERC20DividendCheckpointFactory.sol') -const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); -const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); -const ManualApprovalTransferManagerFactory = artifacts.require('./ManualApprovalTransferManagerFactory.sol') -const CappedSTOFactory = artifacts.require('./CappedSTOFactory.sol') -const USDTieredSTOFactory = artifacts.require('./USDTieredSTOFactory.sol') -const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol') -const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol') -const FeatureRegistry = artifacts.require('./FeatureRegistry.sol') -const STFactory = artifacts.require('./tokens/STFactory.sol') -const DevPolyToken = artifacts.require('./helpers/PolyTokenFaucet.sol') -const MockOracle = artifacts.require('./MockOracle.sol') -const TokenLib = artifacts.require('./TokenLib.sol'); -const SecurityToken = artifacts.require('./tokens/SecurityToken.sol') +const PolymathRegistry = artifacts.require("./PolymathRegistry.sol"); +const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); +const GeneralTransferManagerLogic = artifacts.require("./GeneralTransferManager.sol"); +const GeneralPermissionManagerLogic = artifacts.require("./GeneralPermissionManager.sol"); +const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); +const PercentageTransferManagerLogic = artifacts.require("./PercentageTransferManager.sol"); +const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); +const USDTieredSTOLogic = artifacts.require("./USDTieredSTO.sol"); +const CountTransferManagerFactory = artifacts.require("./CountTransferManagerFactory.sol"); +const CountTransferManagerLogic = artifacts.require("./CountTransferManager.sol"); +const EtherDividendCheckpointLogic = artifacts.require("./EtherDividendCheckpoint.sol"); +const ERC20DividendCheckpointLogic = artifacts.require("./ERC20DividendCheckpoint.sol"); +const EtherDividendCheckpointFactory = artifacts.require("./EtherDividendCheckpointFactory.sol"); +const ERC20DividendCheckpointFactory = artifacts.require("./ERC20DividendCheckpointFactory.sol"); +const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); +const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); +const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApprovalTransferManagerFactory.sol"); +const ManualApprovalTransferManagerLogic = artifacts.require("./ManualApprovalTransferManager.sol"); +const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); +const CappedSTOLogic = artifacts.require("./CappedSTO.sol"); +const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); +const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); +const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryProxy.sol"); +const FeatureRegistry = artifacts.require("./FeatureRegistry.sol"); +const STFactory = artifacts.require("./tokens/STFactory.sol"); +const DevPolyToken = artifacts.require("./helpers/PolyTokenFaucet.sol"); +const MockOracle = artifacts.require("./MockOracle.sol"); +const StableOracle = artifacts.require("./StableOracle.sol"); +const TokenLib = artifacts.require("./TokenLib.sol"); +const SecurityToken = artifacts.require("./tokens/SecurityToken.sol"); +const STRGetter = artifacts.require('./STRGetter.sol'); +const STGetter = artifacts.require('./STGetter.sol'); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); +const VolumeRestrictionTMFactory = artifacts.require('./VolumeRestrictionTMFactory.sol') +const VolumeRestrictionTMLogic = artifacts.require('./VolumeRestrictionTM.sol'); +const VolumeRestrictionLib = artifacts.require('./VolumeRestrictionLib.sol'); -let BigNumber = require('bignumber.js'); -const cappedSTOSetupCost = new BigNumber(20000).times(new BigNumber(10).pow(18)); // 20K POLY fee -const usdTieredSTOSetupCost = new BigNumber(100000).times(new BigNumber(10).pow(18)); // 100K POLY fee -const initRegFee = new BigNumber(250).times(new BigNumber(10).pow(18)); // 250 POLY fee for registering ticker or security token in registry +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const nullAddress = "0x0000000000000000000000000000000000000000"; +const cappedSTOSetupCost = new BN(20000).mul(new BN(10).pow(new BN(18))); // 20K POLY fee +const usdTieredSTOSetupCost = new BN(100000).mul(new BN(10).pow(new BN(18))); // 100K POLY fee +const initRegFee = new BN(250).mul(new BN(10).pow(new BN(18))); // 250 POLY fee for registering ticker or security token in registry let PolyToken; let UsdToken; let ETHOracle; let POLYOracle; +let StablePOLYOracle; -const Web3 = require('web3') +module.exports = function(deployer, network, accounts) { + // Ethereum account address hold by the Polymath (Act as the main account which have ownable permissions) + let PolymathAccount; + let moduleRegistry; + let polymathRegistry; + let web3; + if (network === "development") { + web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + PolymathAccount = accounts[0]; + PolyToken = DevPolyToken.address; // Development network polytoken address + deployer.deploy(DevPolyToken, { from: PolymathAccount }).then(() => { + DevPolyToken.deployed().then(mockedUSDToken => { + UsdToken = mockedUSDToken.address; + }); + }); + deployer + .deploy( + MockOracle, + PolyToken, + web3.utils.fromAscii("POLY"), + web3.utils.fromAscii("USD"), + new BN(5).mul(new BN(10).pow(new BN(17))), + { from: PolymathAccount } + ).then(() => { + return MockOracle.deployed(); + }).then(mockedOracle => { + POLYOracle = mockedOracle.address; + }).then(() => { + return deployer + .deploy( + StableOracle, + POLYOracle, + new BN(10).mul(new BN(10).pow(new BN(16))), + { from: PolymathAccount } + ); + }).then(() => { + return StableOracle.deployed(); + }).then(stableOracle => { + StablePOLYOracle = stableOracle.address; + }); + deployer + .deploy( + MockOracle, + nullAddress, + web3.utils.fromAscii("ETH"), + web3.utils.fromAscii("USD"), + new BN(500).mul(new BN(10).pow(new BN(18))), + { from: PolymathAccount } + ) + .then(() => { + MockOracle.deployed().then(mockedOracle => { + ETHOracle = mockedOracle.address; + }); + }); + } else if (network === "kovan") { + web3 = new Web3(new Web3.providers.HttpProvider("https://kovan.infura.io/g5xfoQ0jFSE9S5LwM1Ei")); + PolymathAccount = accounts[0]; + PolyToken = "0xb347b9f5b56b431b2cf4e1d90a5995f7519ca792"; // PolyToken Kovan Faucet Address + POLYOracle = "0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C"; // Poly Oracle Kovan Address + ETHOracle = "0xCE5551FC9d43E9D2CC255139169FC889352405C8"; // ETH Oracle Kovan Address + StablePOLYOracle = ""; // TODO + } else if (network === "mainnet") { + web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/g5xfoQ0jFSE9S5LwM1Ei")); + PolymathAccount = accounts[0]; + PolyToken = "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC"; // Mainnet PolyToken Address + POLYOracle = "0x52cb4616E191Ff664B0bff247469ce7b74579D1B"; // Poly Oracle Mainnet Address + ETHOracle = "0x60055e9a93aae267da5a052e95846fa9469c0e7a"; // ETH Oracle Mainnet Address + StablePOLYOracle = ""; // TODO + } + if (network === "coverage") { + web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + PolymathAccount = accounts[0]; + PolyToken = DevPolyToken.address; // Development network polytoken address + deployer + .deploy(MockOracle, PolyToken, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), new BN(0.5).mul(new BN(10).pow(new BN(18))), { from: PolymathAccount }) + .then(() => { + return MockOracle.deployed(); + }).then(mockedOracle => { + POLYOracle = mockedOracle.address; + }).then(() => { + return deployer + .deploy( + StableOracle, + POLYOracle, + new BN(10).mul(new BN(10).pow(new BN(16))), + { from: PolymathAccount } + ) + }).then(() => { + return StableOracle.deployed(); + }).then(stableOracle => { + StablePOLYOracle = stableOracle.address; + }); + deployer.deploy(MockOracle, nullAddress, web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), new BN(500).mul(new BN(10).pow(new BN(18))), { from: PolymathAccount }).then(() => { + MockOracle.deployed().then(mockedOracle => { + ETHOracle = mockedOracle.address; + }); + }); + } -module.exports = function (deployer, network, accounts) { - // Ethereum account address hold by the Polymath (Act as the main account which have ownable permissions) - let PolymathAccount; - let moduleRegistry; - let polymathRegistry; - let web3 - if (network === 'development') { - web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) - PolymathAccount = accounts[0] - PolyToken = DevPolyToken.address // Development network polytoken address - deployer.deploy(DevPolyToken, {from: PolymathAccount}).then(() => { - DevPolyToken.deployed().then((mockedUSDToken) => { - UsdToken = mockedUSDToken.address; - }); - }); - deployer.deploy(MockOracle, PolyToken, "POLY", "USD", new BigNumber(0.5).times(new BigNumber(10).pow(18)), {from: PolymathAccount}).then(() => { - MockOracle.deployed().then((mockedOracle) => { - POLYOracle = mockedOracle.address; - }); - }); - deployer.deploy(MockOracle, 0, "ETH", "USD", new BigNumber(500).times(new BigNumber(10).pow(18)), {from: PolymathAccount}).then(() => { - MockOracle.deployed().then((mockedOracle) => { - ETHOracle = mockedOracle.address; - }); - }); + const functionSignatureProxy = { + name: "initialize", + type: "function", + inputs: [ + { + type: "address", + name: "_polymathRegistry" + }, + { + type: "address", + name: "_STFactory" + }, + { + type: "uint256", + name: "_stLaunchFee" + }, + { + type: "uint256", + name: "_tickerRegFee" + }, + { + type: "address", + name: "_owner" + }, + { + type: 'address', + name: '_getterContract' + } + ] + }; - } else if (network === 'kovan') { - web3 = new Web3(new Web3.providers.HttpProvider('https://kovan.infura.io/g5xfoQ0jFSE9S5LwM1Ei')) - PolymathAccount = accounts[0] - PolyToken = '0xb347b9f5b56b431b2cf4e1d90a5995f7519ca792' // PolyToken Kovan Faucet Address - POLYOracle = '0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C' // Poly Oracle Kovan Address - ETHOracle = '0xCE5551FC9d43E9D2CC255139169FC889352405C8' // ETH Oracle Kovan Address - } else if (network === 'mainnet') { - web3 = new Web3(new Web3.providers.HttpProvider('https://mainnet.infura.io/g5xfoQ0jFSE9S5LwM1Ei')) - PolymathAccount = accounts[0] - PolyToken = '0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC' // Mainnet PolyToken Address - POLYOracle = '0x52cb4616E191Ff664B0bff247469ce7b74579D1B' // Poly Oracle Mainnet Address - ETHOracle = '0x60055e9a93aae267da5a052e95846fa9469c0e7a' // ETH Oracle Mainnet Address - } if (network === 'coverage') { - web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) - PolymathAccount = accounts[0] - PolyToken = DevPolyToken.address // Development network polytoken address - deployer.deploy(MockOracle, PolyToken, "POLY", "USD", new BigNumber(0.5).times(new BigNumber(10).pow(18)), {from: PolymathAccount}).then(() => { - MockOracle.deployed().then((mockedOracle) => { - POLYOracle = mockedOracle.address; - }); - }); - deployer.deploy(MockOracle, 0, "ETH", "USD", new BigNumber(500).times(new BigNumber(10).pow(18)), {from: PolymathAccount}).then(() => { - MockOracle.deployed().then((mockedOracle) => { - ETHOracle = mockedOracle.address; - }); - }); - } + const functionSignatureProxyMR = { + name: "initialize", + type: "function", + inputs: [ + { + type: "address", + name: "_polymathRegistry" + }, + { + type: "address", + name: "_owner" + } + ] + }; - const functionSignatureProxy = { - name: 'initialize', - type: 'function', - inputs: [{ - type:'address', - name: '_polymathRegistry' - },{ - type: 'address', - name: '_STFactory' - },{ - type: 'uint256', - name: '_stLaunchFee' - },{ - type: 'uint256', - name: '_tickerRegFee' - },{ - type: 'address', - name: '_polyToken' - },{ - type: 'address', - name: '_owner' - }] - }; + // POLYMATH NETWORK Configuration :: DO THIS ONLY ONCE + // A) Deploy the PolymathRegistry contract + return deployer + .deploy(PolymathRegistry, { from: PolymathAccount }) + .then(() => { + return PolymathRegistry.deployed(); + }) + .then(_polymathRegistry => { + polymathRegistry = _polymathRegistry; + return polymathRegistry.changeAddress("PolyToken", PolyToken, { from: PolymathAccount }); + }) + .then(() => { + // Deploy libraries + return deployer.deploy(TokenLib, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(VolumeRestrictionLib, { from: PolymathAccount }); + }) + .then(() => { + // Link libraries + deployer.link(VolumeRestrictionLib, VolumeRestrictionTMLogic); + deployer.link(TokenLib, SecurityToken); + deployer.link(TokenLib, STFactory); + deployer.link(TokenLib, STGetter); + // A) Deploy the ModuleRegistry Contract (It contains the list of verified ModuleFactory) + return deployer.deploy(ModuleRegistry, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(ModuleRegistryProxy, { from: PolymathAccount }); + }) + .then(() => { + return ModuleRegistryProxy.at(ModuleRegistryProxy.address); + }) + .then(moduleRegistryProxy => { + let bytesProxyMR = web3.eth.abi.encodeFunctionCall(functionSignatureProxyMR, [polymathRegistry.address, PolymathAccount]); + return moduleRegistryProxy.upgradeToAndCall("1.0.0", ModuleRegistry.address, bytesProxyMR, { from: PolymathAccount }); + }) + .then(() => { + return ModuleRegistry.at(ModuleRegistryProxy.address); + }) + .then(moduleRegistryInstance => { + moduleRegistry = moduleRegistryInstance; + // Add module registry to polymath registry + return polymathRegistry.changeAddress("ModuleRegistry", ModuleRegistryProxy.address, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the GeneralTransferManagerLogic Contract (Factory used to generate the GeneralTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the GeneralPermissionManagerLogic Contract (Factory used to generate the GeneralPermissionManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralPermissionManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the CountTransferManagerLogic Contract (Factory used to generate the CountTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(CountTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the ManualApprovalTransferManagerLogic Contract (Factory used to generate the ManualApprovalTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(ManualApprovalTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the PercentageTransferManagerLogic Contract (Factory used to generate the PercentageTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(PercentageTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the ERC20DividendCheckpointLogic Contract (Factory used to generate the ERC20DividendCheckpoint contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(ERC20DividendCheckpointLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the EtherDividendCheckpointLogic Contract (Factory used to generate the EtherDividendCheckpoint contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(EtherDividendCheckpointLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the USDTieredSTOLogic Contract (Factory used to generate the USDTieredSTO contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(USDTieredSTOLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the VolumeRestrictionTMLogic Contract (Factory used to generate the VolumeRestrictionTM contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(VolumeRestrictionTMLogic, "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the CappedSTOLogic Contract (Factory used to generate the CappedSTO contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(CappedSTOLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the DataStoreLogic Contract + return deployer.deploy(DataStoreLogic, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the DataStoreFactory Contract + return deployer.deploy(DataStoreFactory, DataStoreLogic.address, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the GeneralTransferManagerFactory Contract (Factory used to generate the GeneralTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralTransferManagerFactory, new BN(0), new BN(0), GeneralTransferManagerLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // C) Deploy the GeneralPermissionManagerFactory Contract (Factory used to generate the GeneralPermissionManager contract and + // this manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralPermissionManagerFactory, new BN(0), new BN(0), GeneralPermissionManagerLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the CountTransferManagerFactory Contract (Factory used to generate the CountTransferManager contract use + // to track the counts of the investors of the security token) + return deployer.deploy(CountTransferManagerFactory, new BN(0), new BN(0), CountTransferManagerLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the PercentageTransferManagerFactory Contract (Factory used to generate the PercentageTransferManager contract use + // to track the percentage of investment the investors could do for a particular security token) + return deployer.deploy(PercentageTransferManagerFactory, new BN(0), new BN(0), PercentageTransferManagerLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the EtherDividendCheckpointFactory Contract (Factory used to generate the EtherDividendCheckpoint contract use + // to provide the functionality of the dividend in terms of ETH) + return deployer.deploy(EtherDividendCheckpointFactory, new BN(0), new BN(0), EtherDividendCheckpointLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the ERC20DividendCheckpointFactory Contract (Factory used to generate the ERC20DividendCheckpoint contract use + // to provide the functionality of the dividend in terms of ERC20 token) + return deployer.deploy(ERC20DividendCheckpointFactory, new BN(0), new BN(0), ERC20DividendCheckpointLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the VolumeRestrictionTMFactory Contract (Factory used to generate the VolumeRestrictionTM contract use + // to provide the functionality of restricting the token volume) + return deployer.deploy(VolumeRestrictionTMFactory, new BN(0), new BN(0), VolumeRestrictionTMLogic.address, polymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Deploy the ManualApprovalTransferManagerFactory Contract (Factory used to generate the ManualApprovalTransferManager contract use + // to manual approve the transfer that will overcome the other transfer restrictions) + return deployer.deploy(ManualApprovalTransferManagerFactory, new BN(0), new BN(0), ManualApprovalTransferManagerLogic.address, polymathRegistry.address, { + from: PolymathAccount + }); + }) + .then(() => { + // Deploy the STGetter contract (Logic contract that have the getters of the securityToken) + return deployer.deploy(STGetter, { from: PolymathAccount }); + }) + .then(() => { + // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. + return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, DataStoreFactory.address, STGetter.address, { from: PolymathAccount }); + }) + .then(() => { + // K) Deploy the FeatureRegistry contract to control feature switches + return deployer.deploy(FeatureRegistry, PolymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // Assign the address into the FeatureRegistry key + return polymathRegistry.changeAddress("FeatureRegistry", FeatureRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // J) Deploy the SecurityTokenRegistry contract (Used to hold the deployed secuirtyToken details. It also act as the interface to deploy the SecurityToken) + return deployer.deploy(SecurityTokenRegistry, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(SecurityTokenRegistryProxy, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(STRGetter, {from: PolymathAccount}); + }) + .then(() => { + return SecurityTokenRegistryProxy.at(SecurityTokenRegistryProxy.address); + }) + .then((securityTokenRegistryProxy) => { + let bytesProxy = web3.eth.abi.encodeFunctionCall(functionSignatureProxy, [ + PolymathRegistry.address, + STFactory.address, + initRegFee, + initRegFee, + PolymathAccount, + STRGetter.address + ]); + return securityTokenRegistryProxy.upgradeToAndCall("1.0.0", SecurityTokenRegistry.address, bytesProxy, { + from: PolymathAccount + }); + }) + .then(() => { + // Assign the address into the SecurityTokenRegistry key + return polymathRegistry.changeAddress("SecurityTokenRegistry", SecurityTokenRegistryProxy.address, { from: PolymathAccount }); + }) + .then(() => { + // Update all addresses into the registry contract by calling the function updateFromregistry + return moduleRegistry.updateFromRegistry({ from: PolymathAccount }); + }) + .then(() => { + // D) Register the PercentageTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the PercentageTransferManager contract. + return moduleRegistry.registerModule(PercentageTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the CountTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the CountTransferManager contract. + return moduleRegistry.registerModule(CountTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the GeneralTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the GeneralTransferManager contract. + return moduleRegistry.registerModule(GeneralTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // E) Register the GeneralPermissionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the GeneralPermissionManager contract. + return moduleRegistry.registerModule(GeneralPermissionManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // E) Register the GeneralPermissionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the GeneralPermissionManager contract. + return moduleRegistry.registerModule(EtherDividendCheckpointFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the VolumeRestrictionTMFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the VolumeRestrictionTM contract. + return moduleRegistry.registerModule(VolumeRestrictionTMFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the ManualApprovalTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the ManualApprovalTransferManager contract. + return moduleRegistry.registerModule(ManualApprovalTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // E) Register the ERC20DividendCheckpointFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the ERC20DividendCheckpoint contract. + return moduleRegistry.registerModule(ERC20DividendCheckpointFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // F) Once the GeneralTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(GeneralTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the CountTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(CountTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the PercentageTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(PercentageTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the GeneralPermissionManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(GeneralPermissionManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the EtherDividendCheckpointFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(EtherDividendCheckpointFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the ERC20DividendCheckpointFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(ERC20DividendCheckpointFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the VolumeRestrictionTMFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(VolumeRestrictionTMFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the ManualApprovalTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(ManualApprovalTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // M) Deploy the CappedSTOFactory (Use to generate the CappedSTO contract which will used to collect the funds ). + return deployer.deploy(CappedSTOFactory, cappedSTOSetupCost, new BN(0), CappedSTOLogic.address, polymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // N) Register the CappedSTOFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the CappedSTOFactory contract. + return moduleRegistry.registerModule(CappedSTOFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the CappedSTOFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(CappedSTOFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // H) Deploy the USDTieredSTOFactory (Use to generate the USDTieredSTOFactory contract which will used to collect the funds ). + return deployer.deploy(USDTieredSTOFactory, usdTieredSTOSetupCost, new BN(0), USDTieredSTOLogic.address, polymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // I) Register the USDTieredSTOFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the USDTieredSTOFactory contract. + return moduleRegistry.registerModule(USDTieredSTOFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // J) Once the USDTieredSTOFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(USDTieredSTOFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + return polymathRegistry.changeAddress("PolyUsdOracle", POLYOracle, { from: PolymathAccount }); + }) + .then(() => { + return polymathRegistry.changeAddress("EthUsdOracle", ETHOracle, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(SecurityToken, "a", "a", 18, 1, "a", polymathRegistry.address, STGetter.address, { from: PolymathAccount }); + return polymathRegistry.changeAddress("StablePolyUsdOracle", StablePOLYOracle, { from: PolymathAccount }); + }) + .then(() => { + console.log("\n"); + console.log(` - const functionSignatureProxyMR = { - name: 'initialize', - type: 'function', - inputs: [{ - type:'address', - name: '_polymathRegistry' - },{ - type: 'address', - name: '_owner' - }] - }; - - // POLYMATH NETWORK Configuration :: DO THIS ONLY ONCE - // A) Deploy the PolymathRegistry contract - return deployer.deploy(PolymathRegistry, {from: PolymathAccount}).then(() => { - return PolymathRegistry.deployed(); - }).then((_polymathRegistry) => { - polymathRegistry = _polymathRegistry; - return polymathRegistry.changeAddress("PolyToken", PolyToken, {from: PolymathAccount}); - }).then(() => { - // Deploy libraries - return deployer.deploy(TokenLib, {from: PolymathAccount}); - }).then(() => { - // Link libraries - deployer.link(TokenLib, SecurityToken); - deployer.link(TokenLib, STFactory); - // A) Deploy the ModuleRegistry Contract (It contains the list of verified ModuleFactory) - return deployer.deploy(ModuleRegistry, {from: PolymathAccount}); - }).then(() => { - return deployer.deploy(ModuleRegistryProxy, {from: PolymathAccount}); - }).then(() => { - let bytesProxyMR = web3.eth.abi.encodeFunctionCall(functionSignatureProxyMR, [polymathRegistry.address, PolymathAccount]); - return ModuleRegistryProxy.at(ModuleRegistryProxy.address).upgradeToAndCall("1.0.0", ModuleRegistry.address, bytesProxyMR, {from: PolymathAccount}); - }).then(() => { - moduleRegistry = ModuleRegistry.at(ModuleRegistryProxy.address); - // Add module registry to polymath registry - return polymathRegistry.changeAddress("ModuleRegistry", ModuleRegistryProxy.address, {from: PolymathAccount}); - }).then(() => { - // B) Deploy the GeneralTransferManagerFactory Contract (Factory used to generate the GeneralTransferManager contract and this - // manager attach with the securityToken contract at the time of deployment) - return deployer.deploy(GeneralTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // C) Deploy the GeneralPermissionManagerFactory Contract (Factory used to generate the GeneralPermissionManager contract and - // this manager attach with the securityToken contract at the time of deployment) - return deployer.deploy(GeneralPermissionManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the CountTransferManagerFactory Contract (Factory used to generate the CountTransferManager contract use - // to track the counts of the investors of the security token) - return deployer.deploy(CountTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the PercentageTransferManagerFactory Contract (Factory used to generate the PercentageTransferManager contract use - // to track the percentage of investment the investors could do for a particular security token) - return deployer.deploy(PercentageTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the EtherDividendCheckpointFactory Contract (Factory used to generate the EtherDividendCheckpoint contract use - // to provide the functionality of the dividend in terms of ETH) - return deployer.deploy(EtherDividendCheckpointFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the ERC20DividendCheckpointFactory Contract (Factory used to generate the ERC20DividendCheckpoint contract use - // to provide the functionality of the dividend in terms of ERC20 token) - return deployer.deploy(ERC20DividendCheckpointFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the ManualApprovalTransferManagerFactory Contract (Factory used to generate the ManualApprovalTransferManager contract use - // to manual approve the transfer that will overcome the other transfer restrictions) - return deployer.deploy(ManualApprovalTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. - return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // K) Deploy the FeatureRegistry contract to control feature switches - return deployer.deploy(FeatureRegistry, PolymathRegistry.address, {from: PolymathAccount}); - }).then(() => { - // Assign the address into the FeatureRegistry key - return polymathRegistry.changeAddress("FeatureRegistry", FeatureRegistry.address, {from: PolymathAccount}); - }).then(() => { - // J) Deploy the SecurityTokenRegistry contract (Used to hold the deployed secuirtyToken details. It also act as the interface to deploy the SecurityToken) - return deployer.deploy(SecurityTokenRegistry, {from: PolymathAccount}) - }).then(()=> { - return deployer.deploy(SecurityTokenRegistryProxy, {from: PolymathAccount}); - }).then(() => { - let bytesProxy = web3.eth.abi.encodeFunctionCall(functionSignatureProxy, [PolymathRegistry.address, STFactory.address, initRegFee, initRegFee, PolyToken, PolymathAccount]); - return SecurityTokenRegistryProxy.at(SecurityTokenRegistryProxy.address).upgradeToAndCall("1.0.0", SecurityTokenRegistry.address, bytesProxy, {from: PolymathAccount}); - }).then(() => { - // Assign the address into the SecurityTokenRegistry key - return polymathRegistry.changeAddress("SecurityTokenRegistry", SecurityTokenRegistryProxy.address, {from: PolymathAccount}); - }).then(() => { - // Update all addresses into the registry contract by calling the function updateFromregistry - return moduleRegistry.updateFromRegistry({from: PolymathAccount}); - }).then(() => { - // D) Register the PercentageTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the PercentageTransferManager contract. - return moduleRegistry.registerModule(PercentageTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // D) Register the CountTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the CountTransferManager contract. - return moduleRegistry.registerModule(CountTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // D) Register the GeneralTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the GeneralTransferManager contract. - return moduleRegistry.registerModule(GeneralTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // E) Register the GeneralPermissionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the GeneralPermissionManager contract. - return moduleRegistry.registerModule(GeneralPermissionManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // E) Register the GeneralPermissionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the GeneralPermissionManager contract. - return moduleRegistry.registerModule(EtherDividendCheckpointFactory.address, {from: PolymathAccount}); - }).then(() => { - // D) Register the ManualApprovalTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the ManualApprovalTransferManager contract. - return moduleRegistry.registerModule(ManualApprovalTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // E) Register the ERC20DividendCheckpointFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the ERC20DividendCheckpoint contract. - return moduleRegistry.registerModule(ERC20DividendCheckpointFactory.address, {from: PolymathAccount}); - }).then(() => { - // F) Once the GeneralTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(GeneralTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the CountTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(CountTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the PercentageTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(PercentageTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the GeneralPermissionManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(GeneralPermissionManagerFactory.address, true, {from: PolymathAccount}) - }).then(() => { - // G) Once the EtherDividendCheckpointFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(EtherDividendCheckpointFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the ERC20DividendCheckpointFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(ERC20DividendCheckpointFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the ManualApprovalTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(ManualApprovalTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // M) Deploy the CappedSTOFactory (Use to generate the CappedSTO contract which will used to collect the funds ). - return deployer.deploy(CappedSTOFactory, PolyToken, cappedSTOSetupCost, 0, 0, {from: PolymathAccount}) - }).then(() => { - // N) Register the CappedSTOFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the CappedSTOFactory contract. - return moduleRegistry.registerModule(CappedSTOFactory.address, {from: PolymathAccount}) - }).then(()=>{ - // G) Once the CappedSTOFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(CappedSTOFactory.address, true, {from: PolymathAccount}) - }).then(() => { - // Deploy the proxy factory - return deployer.deploy(USDTieredSTOProxyFactory, {from: PolymathAccount}); - }).then(() => { - // H) Deploy the USDTieredSTOFactory (Use to generate the USDTieredSTOFactory contract which will used to collect the funds ). - return deployer.deploy(USDTieredSTOFactory, PolyToken, usdTieredSTOSetupCost, 0, 0, USDTieredSTOProxyFactory.address, {from: PolymathAccount}) - }).then(() => { - // I) Register the USDTieredSTOFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the USDTieredSTOFactory contract. - return moduleRegistry.registerModule(USDTieredSTOFactory.address, {from: PolymathAccount}) - }).then(()=>{ - // J) Once the USDTieredSTOFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(USDTieredSTOFactory.address, true, {from: PolymathAccount}) - }).then(() => { - return polymathRegistry.changeAddress("PolyUsdOracle", POLYOracle, {from: PolymathAccount}); - }).then(() => { - return polymathRegistry.changeAddress("EthUsdOracle", ETHOracle, {from: PolymathAccount}); - }).then(() => { - return deployer.deploy(SecurityToken, 'a', 'a', 18, 1, 'a', polymathRegistry.address, {from: PolymathAccount}); - }).then(() => { - console.log('\n'); - console.log(` ----------------------- Polymath Network Smart Contracts: ----------------------- PolymathRegistry: ${PolymathRegistry.address} SecurityTokenRegistry (Proxy): ${SecurityTokenRegistryProxy.address} ModuleRegistry (Proxy): ${ModuleRegistryProxy.address} FeatureRegistry: ${FeatureRegistry.address} + STRGetter: ${STRGetter.address} ETHOracle: ${ETHOracle} POLYOracle: ${POLYOracle} + POLYStableOracle: ${StablePOLYOracle} STFactory: ${STFactory.address} + GeneralTransferManagerLogic: ${GeneralTransferManagerLogic.address} GeneralTransferManagerFactory: ${GeneralTransferManagerFactory.address} + GeneralPermissionManagerLogic: ${GeneralPermissionManagerLogic.address} GeneralPermissionManagerFactory: ${GeneralPermissionManagerFactory.address} + CappedSTOLogic: ${CappedSTOLogic.address} CappedSTOFactory: ${CappedSTOFactory.address} + USDTieredSTOLogic: ${USDTieredSTOLogic.address} USDTieredSTOFactory: ${USDTieredSTOFactory.address} - USDTieredSTOProxyFactory: ${USDTieredSTOProxyFactory.address} + CountTransferManagerLogic: ${CountTransferManagerLogic.address} CountTransferManagerFactory: ${CountTransferManagerFactory.address} + PercentageTransferManagerLogic: ${PercentageTransferManagerLogic.address} PercentageTransferManagerFactory: ${PercentageTransferManagerFactory.address} + ManualApprovalTransferManagerLogic: ${ManualApprovalTransferManagerLogic.address} ManualApprovalTransferManagerFactory: ${ManualApprovalTransferManagerFactory.address} + EtherDividendCheckpointLogic: ${EtherDividendCheckpointLogic.address} + ERC20DividendCheckpointLogic: ${ERC20DividendCheckpointLogic.address} EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} + VolumeRestrictionTMFactory: ${VolumeRestrictionTMFactory.address} + VolumeRestrictionTMLogic: ${VolumeRestrictionTMLogic.address} --------------------------------------------------------------------------------- `); - console.log('\n'); - // -------- END OF POLYMATH NETWORK Configuration -------// - }); -} + console.log("\n"); + // -------- END OF POLYMATH NETWORK Configuration -------// + }); +}; diff --git a/package.json b/package.json index 744c3bc39..3cc06302e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "polymath-core", - "version": "1.4.0", + "version": "3.0.0", "description": "Polymath Network Core Smart Contracts", "main": "truffle.js", "directories": { @@ -8,6 +8,7 @@ }, "scripts": { "test": "scripts/test.sh 2> /dev/null", + "gas": "scripts/gasUsage.sh", "wintest": "scripts\\wintest.cmd", "wincov": "scripts\\wincov.cmd", "docs": "scripts/docs.sh", @@ -29,8 +30,9 @@ "flatten-mocks": "sol-merger './contracts/mocks/*.sol' ./flat/mocks", "flatten-oracles": "sol-merger './contracts/oracles/*.sol' ./flat/oracles", "flatten-proxies": "sol-merger './contracts/proxy/*.sol' ./flat/proxy", + "flatten-proxyFactories": "sol-merger './contracts/modules/STO/ProxyFactory/*.sol' ./flat/modules/STO/proxy", "flatten": "sol-merger './contracts/*.sol' ./flat", - "flatten-all": "npm run flatten-modules && npm run flatten-token && npm run flatten-mocks && npm run flatten-oracles && npm run flatten-proxies && npm run flatten", + "flatten-all": "npm run flatten-modules && npm run flatten-token && npm run flatten-mocks && npm run flatten-oracles && npm run flatten-proxies && npm run flatten && npm run flatten-proxyFactories", "ethereum-bridge": "node_modules/.bin/ethereum-bridge -H localhost:8545 -a 9 --dev", "st20generator": "node demo/ST20Generator", "pretty": "prettier --write --print-width 140 --tab-width 4 \"**/*.js\"" @@ -55,53 +57,48 @@ }, "homepage": "https://github.com/PolymathNetwork/polymath-core#readme", "dependencies": { + "truffle": "^5.0.4", + "truffle-hdwallet-provider": "^1.0.4", + "web3-provider-engine": "^14.1.0" + }, + "devDependencies": { "babel-polyfill": "6.26.0", "babel-preset-es2015": "6.24.1", "babel-preset-stage-2": "6.24.1", "babel-preset-stage-3": "6.24.1", "babel-register": "6.26.0", - "bignumber.js": "5.0.0", - "chalk": "^2.4.1", + "chalk": "^2.4.2", "coveralls": "^3.0.1", - "ethereumjs-testrpc": "^6.0.3", - "ethers": "^4.0.7", - "fs": "0.0.2", - "openzeppelin-solidity": "1.10.0", - "readline-sync": "^1.4.9", - "request": "^2.88.0", - "request-promise": "^4.2.2", - "shelljs": "^0.8.2", - "solc": "^0.4.24", - "truffle-contract": "^3.0.4", - "truffle-hdwallet-provider-privkey": "0.2.0", - "web3": "1.0.0-beta.34" - }, - "devDependencies": { - "@soldoc/soldoc": "^0.4.3", "eslint": "^5.8.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.10.0", "eslint-plugin-node": "^8.0.0", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", + "eth-gas-reporter": "^0.1.12", "ethereum-bridge": "^0.6.1", "ethereumjs-abi": "^0.6.5", - "fast-csv": "^2.4.1", - "ganache-cli": "^6.1.8", - "prettier": "^1.14.3", + "fs": "0.0.2", + "ganache-cli": "6.1.8", + "mocha-junit-reporter": "^1.18.0", + "openzeppelin-solidity": "2.1.2", + "prettier": "^1.15.3", + "prompt": "^1.0.0", + "request": "^2.88.0", + "request-promise": "^4.2.2", "sol-merger": "^0.1.2", "solidity-coverage": "^0.5.11", "solidity-docgen": "^0.1.0", "solium": "^1.1.6", - "truffle": "4.1.14", - "truffle-wallet-provider": "0.0.5" + "table": "^5.2.3", + "web3": "1.0.0-beta.35" }, "greenkeeper": { "ignore": [ "openzeppelin-solidity", "web3", - "bignumber.js", - "truffle-hdwallet-provider-privkey" + "truffle-hdwallet-provider", + "ganache-cli" ] } } diff --git a/scripts/calculateSize.js b/scripts/calculateSize.js index 46cf238db..3f3db87f9 100644 --- a/scripts/calculateSize.js +++ b/scripts/calculateSize.js @@ -1,30 +1,35 @@ -const fs = require('fs'); -const path = require('path'); -var size = new Array(); +const fs = require("fs"); +const path = require("path"); +const exec = require('child_process').execSync; +const chalk = require('chalk'); +const { table } = require("table"); - - -function readFiles() { - if (fs.existsSync('./build/contracts/')) { - let files = fs.readdirSync('./build/contracts/'); - return files; +async function readFiles() { + if (fs.existsSync("./build/contracts/")) { + return fs.readdirSync("./build/contracts/"); } else { - console.log("Directory doesn't exists"); + console.log('Compiling contracts. This may take a while, please wait.'); + exec('truffle compile'); + return fs.readdirSync("./build/contracts/"); } } async function printSize() { - let files = readFiles(); - files.forEach((item) => { - let content = JSON.parse(fs.readFileSync(`./build/contracts/${item}`).toString()).deployedBytecode; - let sizeInKB = ((content.toString()).length / 2) / 1024; - size.push(sizeInKB); - }); + let files = await readFiles(); console.log(`NOTE- Maximum size of contracts allowed to deloyed on the Ethereum mainnet is 24 KB(EIP170)`); console.log(`---- Size of the contracts ----`); - for(let i = 0; i < files.length; i++) { - console.log(`${path.basename(files[i], '.json')} - ${size[i]} KB`); - } + let dataTable = [['Contracts', 'Size in KB']]; + files.forEach(item => { + let content = JSON.parse(fs.readFileSync(`./build/contracts/${item}`).toString()).deployedBytecode; + let sizeInKB = content.toString().length / 2 / 1024; + if (sizeInKB > 24) + dataTable.push([chalk.red(path.basename(item, ".json")),chalk.red(sizeInKB)]); + else if (sizeInKB > 20) + dataTable.push([chalk.yellow(path.basename(item, ".json")),chalk.yellow(sizeInKB)]); + else + dataTable.push([chalk.green(path.basename(item, ".json")),chalk.green(sizeInKB)]); + }); + console.log(table(dataTable)); } -printSize(); \ No newline at end of file +printSize(); diff --git a/scripts/compareStorageLayout.js b/scripts/compareStorageLayout.js new file mode 100644 index 000000000..382efdedf --- /dev/null +++ b/scripts/compareStorageLayout.js @@ -0,0 +1,146 @@ +const fs = require("fs"); +const _ = require("underscore"); +const solc = require("solc"); +const prompt = require("prompt"); +const path = require("path"); +const util = require("util"); +const exec = util.promisify(require("child_process").exec); + +console.log(`Mandatory: Solc cli tool should be installed globally`); +prompt.start(); + +prompt.get(["LogicContract", "ProxyContract"], async (err, result) => { + let temp; + let logicFilePath; + let proxyFilePath; + + const fileList = walkSync("./contracts", []); + + let paths = findPath(result.LogicContract, result.ProxyContract, fileList); + + if (paths.length == 2) { + console.log("Contracts exists \n"); + + await flatContracts(paths); + let temp; + let logicFilePath = `./flat/${path.basename(paths[0])}`; + let proxyFilePath = `./flat/${path.basename(paths[1])}`; + + if (path.basename(paths[0]) === result.LogicContract) { + temp = logicFilePath; + logicFilePath = proxyFilePath; + proxyFilePath = temp; + } + + let logicAST = await getAST(logicFilePath); + let proxyAST = await getAST(proxyFilePath); + // Deleting the temp folder (no longer required) + await flushTemp(); + + console.log(compareStorageLayouts(parseContract(logicAST), parseContract(proxyAST))); + } else { + console.log("Contracts doesn't exists"); + } +}); + +function traverseAST(_input, _elements) { + if (_input.children) { + for (var i = 0; i < _input.children.length; i++) { + traverseAST(_input.children[i], _elements); + } + } + _elements.push(_input); +} + +function compareStorageLayouts(logicLayout, proxyLayout) { + function makeComp(x) { + return [x.constant, x.name, x.stateVariable, x.storageLocation, x.type, x.value, x.visibility].join(":"); + } + // if(newLayout.length < oldLayout.length) return false; + for (var i = 0; i < logicLayout.length; i++) { + const a = logicLayout[i].attributes; + const comp1 = makeComp(a); + console.log(comp1); + const b = proxyLayout[i].attributes; + const comp2 = makeComp(b); + console.log(comp2); + if (comp1 != comp2) { + return false; + } + } + return true; +} + +function parseContract(input) { + + const elements = []; + const AST = input; + traverseAST(AST, elements); + // console.log(elements); + + // filter out all Contract Definitions + const contractDefinitions = _.filter(elements, (e, i) => e.name == "ContractDefinition"); + + // filter out all linearizedBaseContracts + // pick the last one as the last contract always has the full inheritance + const linearizedBaseContracts = _.last(_.map(contractDefinitions, e => e.attributes.linearizedBaseContracts)); + + // get all stateVariables + const stateVariables = _.filter(elements, e => e.attributes && e.attributes.stateVariable); + + // group them by scope + const stateVariableMap = _.groupBy(stateVariables, e => e.attributes.scope); + + orderedStateVariables = _.reduceRight( + linearizedBaseContracts, + (a, b) => { + return a.concat(stateVariableMap[b] || []); + }, + [] + ); + return orderedStateVariables; +} + +var walkSync = function(dir, filelist) { + files = fs.readdirSync(dir); + filelist = filelist || []; + files.forEach(function(file) { + if (fs.statSync(path.join(dir, file)).isDirectory()) { + filelist = walkSync(path.join(dir, file), filelist); + } else { + filelist.push(path.join(dir, file)); + } + }); + return filelist; +}; + +var findPath = function(logicContractName, proxyContractName, fileList) { + let paths = new Array(); + for (let i = 0; i < fileList.length; i++) { + if ( + logicContractName === path.basename(fileList[i]) || + logicContractName === path.basename(fileList[i]).split(".")[0] || + (proxyContractName === path.basename(fileList[i]) || proxyContractName === path.basename(fileList[i]).split(".")[0]) + ) { + paths.push(fileList[i]); + } + } + return paths; +}; + +async function flatContracts(_paths, _logic) { + let promises = new Array(); + for (let i = 0; i < _paths.length; i++) { + promises.push(await exec(`./node_modules/.bin/sol-merger ${_paths[i]} ./flat`)); + } + await Promise.all(promises); +} + +async function getAST(_filePath) { + await exec(`solc -o temp --ast-json ${_filePath}`, {maxBuffer: 1024 * 1000}); + return JSON.parse(fs.readFileSync(`./temp/${path.basename(_filePath)}_json.ast`, "utf8").toString()); +} + +async function flushTemp() { + await exec(`rm -rf temp`); +} \ No newline at end of file diff --git a/scripts/coverage.sh b/scripts/coverage.sh index e42ed6a84..5d1e2a24e 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -2,4 +2,4 @@ rm -rf flat -TRAVIS_PULL_REQUEST=true scripts/test.sh \ No newline at end of file +COVERAGE=true scripts/test.sh \ No newline at end of file diff --git a/scripts/docs.sh b/scripts/docs.sh index db7aba417..af7f6e395 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -32,12 +32,21 @@ create_docs() { cd $WEBSITE_DIRECTORY fi + echo "Fetching solc binary" + curl -L -o solidity-ubuntu-trusty.zip https://github.com/ethereum/solidity/releases/download/v0.4.24/solidity-ubuntu-trusty.zip + unzip solidity-ubuntu-trusty.zip + CWD=$(pwd) + OLD_SOLC_PATH=$SOLC_PATH + export SOLC_PATH=$CWD/solc + echo "Generating the API documentation in branch $latestTag" # Command to generate the documentation using the solidity-docgen migrate=$(SOLC_ARGS="openzeppelin-solidity="$CORE_ROUTE"/node_modules/openzeppelin-solidity" \ solidity-docgen -x external/oraclizeAPI.sol,mocks/MockPolyOracle.sol,oracles/PolyOracle.sol $CORE_ROUTE $CORE_ROUTE/contracts $CORE_ROUTE/polymath-developer-portal/) + export SOLC_PATH=$OLD_SOLC_PATH + echo "Successfully docs are generated..." echo "Installing npm dependencies..." diff --git a/scripts/encoders/encode_CappedSTO.js b/scripts/encoders/encode_CappedSTO.js index 12527a1e1..dc9d054c2 100644 --- a/scripts/encoders/encode_CappedSTO.js +++ b/scripts/encoders/encode_CappedSTO.js @@ -1,10 +1,10 @@ -const Web3 = require('web3'); +const Web3 = require("web3"); -if (typeof web3 !== 'undefined') { - web3 = new Web3(web3.currentProvider); +if (typeof web3 !== "undefined") { + web3 = new Web3(web3.currentProvider); } else { - // set the provider you want from Web3.providers - web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + // set the provider you want from Web3.providers + web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); } let startTime = process.argv.slice(2)[0]; @@ -13,33 +13,42 @@ let cap = process.argv.slice(2)[2]; let rate = process.argv.slice(2)[3]; let wallet = process.argv.slice(2)[4]; - -let bytesSTO = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'uint256', - name: '_startTime' - },{ - type: 'uint256', - name: '_endTime' - },{ - type: 'uint256', - name: '_cap' - },{ - type: 'uint256', - name: '_rate' - },{ - type: 'uint8', - name: '_fundRaiseType' - },{ - type: 'address', - name: '_polyToken' - },{ - type: 'address', - name: '_fundsReceiver' - } +let bytesSTO = web3.eth.abi.encodeFunctionCall( + { + name: "configure", + type: "function", + inputs: [ + { + type: "uint256", + name: "_startTime" + }, + { + type: "uint256", + name: "_endTime" + }, + { + type: "uint256", + name: "_cap" + }, + { + type: "uint256", + name: "_rate" + }, + { + type: "uint8", + name: "_fundRaiseType" + }, + { + type: "address", + name: "_polyToken" + }, + { + type: "address", + name: "_fundsReceiver" + } ] - }, [startTime, endTime, web3.utils.toWei(cap, 'ether'), rate,0,0,wallet]); + }, + [startTime, endTime, web3.utils.toWei(cap, "ether"), rate, 0, 0, wallet] +); - console.log(bytesSTO); +console.log(bytesSTO); diff --git a/scripts/gasUsage.sh b/scripts/gasUsage.sh new file mode 100755 index 000000000..a7794d371 --- /dev/null +++ b/scripts/gasUsage.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +mv truffle-config.js truffle-config-bk.js +cp truffle-config-gas.js truffle-config.js + +scripts/test.sh + +mv truffle-config-bk.js truffle-config.js \ No newline at end of file diff --git a/scripts/patch.js b/scripts/patch.js index b1bbbcb3f..602e0ef0b 100644 --- a/scripts/patch.js +++ b/scripts/patch.js @@ -1,25 +1,26 @@ -const fs = require('fs'); -const request = require('request'); -const regex = /node ..\/n(.)*,/gmi; -const regex2 = /truffle test(.)*,/gmi; +const fs = require("fs"); +const request = require("request"); +const regex = /node ..\/n(.)*,/gim; +const regex2 = /truffle test(.)*,/gim; -request('https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js').pipe(fs.createWriteStream('node_modules\\solidity-coverage\\lib\\app.js')); +request("https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js").pipe( + fs.createWriteStream("node_modules\\solidity-coverage\\lib\\app.js") +); -fs.readFile('.solcover.js', 'utf8', function (err,data) { - if (err) { - return console.log(err); - } +fs.readFile(".solcover.js", "utf8", function(err, data) { + if (err) { + return console.log(err); + } - let testCommand = 'truffle test --network coverage'; - fs.readdirSync('./test').forEach(file => { - if(file != 'a_poly_oracle.js' && file != 's_v130_to_v140_upgrade.js') - testCommand = testCommand + ' test\\\\' + file; - }); - testCommand = testCommand + '\','; - let result = data.replace(regex2, testCommand); - result = result.replace(regex, testCommand); + let testCommand = "truffle test --network coverage"; + fs.readdirSync("./test").forEach(file => { + if (file != "a_poly_oracle.js") testCommand = testCommand + " test\\\\" + file; + }); + testCommand = testCommand + "',"; + let result = data.replace(regex2, testCommand); + result = result.replace(regex, testCommand); - fs.writeFile('.solcover.js', result, 'utf8', function (err) { - if (err) return console.log(err); - }); -}); \ No newline at end of file + fs.writeFile(".solcover.js", result, "utf8", function(err) { + if (err) return console.log(err); + }); +}); diff --git a/scripts/test.sh b/scripts/test.sh index 6dd3ce95a..538810761 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -19,11 +19,7 @@ cleanup() { fi } -if ! [ -z "${TRAVIS_PULL_REQUEST+x}" ] && [ "$TRAVIS_PULL_REQUEST" != false ]; then - testrpc_port=8545 -else - testrpc_port=8545 -fi +testrpc_port=8545 testrpc_running() { nc -z localhost "$testrpc_port" @@ -60,20 +56,19 @@ start_testrpc() { --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000" ) - if ! [ -z "${TRAVIS_PULL_REQUEST+x}" ] && [ "$TRAVIS_PULL_REQUEST" != false ]; then - node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port "$testrpc_port" "${accounts[@]}" > /dev/null & + if [ "$COVERAGE" = true ]; then + node_modules/.bin/testrpc-sc --gasLimit 0xfffffffff --port "$testrpc_port" "${accounts[@]}" > /dev/null & else node_modules/.bin/ganache-cli --gasLimit 8000000 "${accounts[@]}" > /dev/null & fi - testrpc_pid=$! } if testrpc_running; then echo "Using existing testrpc instance" - # Do not start ethereum bridge unless it is a cron job from travis - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then + # Do not start ethereum bridge unless it is a cron job + if [ "$CIRCLE_CI_CRON" = true ]; then bridge_running if bridge_running; then echo "Using existing ethereum-bridge instance" @@ -85,25 +80,34 @@ if testrpc_running; then else echo "Starting our own testrpc instance" start_testrpc - # Do not start ethereum bridge unless it is a cron job from travis - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then + # Do not start ethereum bridge unless it is a cron job + if [ "$CIRCLE_CI_CRON" = true ]; then echo "Starting our own ethereum-bridge instance" sleep 10 start_bridge fi fi -if ! [ -z "${TRAVIS_PULL_REQUEST+x}" ] && [ "$TRAVIS_PULL_REQUEST" != false ]; then +if [ "$COVERAGE" = true ]; then curl -o node_modules/solidity-coverage/lib/app.js https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js + curl -o node_modules/solidity-parser-sc/build/parser.js https://raw.githubusercontent.com/maxsam4/solidity-parser/solidity-0.5/build/parser.js node_modules/.bin/solidity-coverage - if [ "$CONTINUOUS_INTEGRATION" = true ]; then - cat coverage/lcov.info | node_modules/.bin/coveralls + if [ "$CIRCLECI" = true ]; then + cat coverage/lcov.info | node_modules/.bin/coveralls || echo 'Failed to report coverage to Coveralls' fi else - # Do not run a_poly_oracle,js tests unless it is a cron job from travis - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then - node_modules/.bin/truffle test `ls test/*.js` + if [ "$CIRCLECI" = true ]; then # using mocha junit reporter for parallelism in CircleCI + mkdir test-results + mkdir test-results/mocha + rm truffle-config.js + mv truffle-ci.js truffle-config.js + # only run poly oracle and upgrade tests if cron job by CI + if [ "$CIRCLE_CI_CRON" = true ]; then + node_modules/.bin/truffle test `ls test/*.js | circleci tests split --split-by=timings` + else + node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js | circleci tests split --split-by=timings` + fi else - node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` + node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js` fi -fi \ No newline at end of file +fi diff --git a/scripts/tokenInfo.js b/scripts/tokenInfo-v1.js similarity index 53% rename from scripts/tokenInfo.js rename to scripts/tokenInfo-v1.js index 2df8c57c4..ea14a7efe 100644 --- a/scripts/tokenInfo.js +++ b/scripts/tokenInfo-v1.js @@ -1,19 +1,18 @@ const Web3 = require("web3"); -const fs = require("fs"); -const async = require("async"); -const path = require("path"); const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/")); +var request = require('request-promise') -const securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityTokenRegistry.json').toString()).abi; -const securityTokenABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityToken.json').toString()).abi; -const generalTransferManagerABI = JSON.parse(require('fs').readFileSync('../build/contracts/GeneralTransferManager.json').toString()).abi; -const securityTokenRegistryAddress = "0xEf58491224958d978fACF55D2120c55A24516B98"; -const securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); +const securityTokenABI = JSON.parse(require('fs').readFileSync('../CLI/data/SecurityToken1-4-0.json').toString()).abi; +const generalTransferManagerABI = JSON.parse(require('fs').readFileSync('../CLI/data/GeneralTransferManager1-4-0.json').toString()).abi; async function getTokens() { - let strEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:securityTokenRegistry.address, topics: ["0x2510d802a0818e71139a7680a6388bcffcd3fa686e02a0f7319488c5bdb38fcb"]}); - for (let i = 0; i < strEvents.length; i++) { - let tokenAddress = '0x' + strEvents[i].topics[1].slice(26,66) + const securityTokenRegistryAddress = "0xEf58491224958d978fACF55D2120c55A24516B98"; + const securityTokenRegistryABI = await getABIfromEtherscan(securityTokenRegistryAddress); + const securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); + + let logs = await getLogsFromEtherscan(securityTokenRegistry.options.address, web3.utils.hexToNumber('0x5C5C18'), 'latest', 'LogNewSecurityToken(string,address,address)'); + for (let i = 0; i < logs.length; i++) { + let tokenAddress = '0x' + logs[i].topics[1].slice(26, 66) await getInfo(tokenAddress); } } @@ -32,7 +31,7 @@ async function getInfo(tokenAddress) { console.log("Finished Issuer Minting: " + await token.methods.finishedIssuerMinting().call()); console.log("Finished STO Minting: " + await token.methods.finishedSTOMinting().call()); let gtmRes = await token.methods.modules(2, 0).call(); - let gtmEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:gtmRes.moduleAddress}); + let gtmEvents = await getLogsFromEtherscan(gtmRes.moduleAddress, web3.utils.hexToNumber('0x5C5C18'), 'latest', 'LogModifyWhitelist(address,uint256,address,uint256,uint256,uint256,bool)'); console.log("Count of GeneralTransferManager Events: " + gtmEvents.length); console.log("Modules Attached (TransferManager):"); await getModules(2, token); @@ -65,4 +64,41 @@ async function getModules(type, token) { } } -getTokens(); +async function getLogsFromEtherscan(_address, _fromBlock, _toBlock, _eventSignature) { + let urlDomain = 'api'; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: 'logs', + action: 'getLogs', + fromBlock: _fromBlock, + toBlock: _toBlock, + address: _address, + topic0: web3.utils.sha3(_eventSignature), + apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' + }, + method: 'GET', + json: true + }; + let data = await request(options); + return data.result; +} + +async function getABIfromEtherscan(_address) { + let urlDomain = 'api'; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: 'contract', + action: 'getabi', + address: _address, + apikey: 'THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559' + }, + method: 'GET', + json: true + }; + let data = await request(options); + return JSON.parse(data.result); +} + +getTokens(); \ No newline at end of file diff --git a/scripts/tokenInfo-v2.js b/scripts/tokenInfo-v2.js new file mode 100644 index 000000000..749bb299d --- /dev/null +++ b/scripts/tokenInfo-v2.js @@ -0,0 +1,126 @@ +const Web3 = require("web3"); +const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/")); +var request = require("request-promise"); + +const securityTokenABI = JSON.parse( + require("fs") + .readFileSync("../build/contracts/SecurityToken.json") + .toString() +).abi; +const generalTransferManagerABI = JSON.parse( + require("fs") + .readFileSync("../build/contracts/GeneralTransferManager.json") + .toString() +).abi; + +async function getTokens() { + const securityTokenRegistryAddress = "0x240f9f86b1465bf1b8eb29bc88cbf65573dfdd97"; + const securityTokenRegistryABI = await getABIfromEtherscan(securityTokenRegistryAddress); + const securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); + + let logs = await getLogsFromEtherscan( + securityTokenRegistry.options.address, + 0, + "latest", + "NewSecurityToken(string,string,address,address,uint256,address,bool,uint256)" + ); + console.log(logs.length); + for (let i = 0; i < logs.length; i++) { + let tokenAddress = "0x" + logs[i].topics[1].slice(26, 66); + await getInfo(tokenAddress); + } +} + +async function getInfo(tokenAddress) { + let token = new web3.eth.Contract(securityTokenABI, tokenAddress); + console.log("Token - " + tokenAddress); + console.log("----------------------"); + //console.log("Owner: " + await token.methods.owner().call()); + console.log("Name: " + (await token.methods.name().call())); + console.log("Details: " + (await token.methods.tokenDetails().call())); + console.log("Symbol: " + (await token.methods.symbol().call())); + console.log("Granularity: " + (await token.methods.granularity().call())); + console.log("Total Supply: " + (await token.methods.totalSupply().call())); + console.log("Transfers Frozen: " + (await token.methods.transfersFrozen().call())); + console.log("Minting Frozen: " + (await token.methods.mintingFrozen().call())); + let controllerDisabled = await token.methods.controllerDisabled().call(); + if (controllerDisabled) { + console.log("Controller disabled: YES"); + } else { + console.log("Controller: " + (await token.methods.controller().call())); + } + console.log("Investors: " + (await token.methods.getInvestorCount().call())); + console.log("Latest Checkpoint: " + (await token.methods.currentCheckpointId().call())); + let gtmEventsCount = 0; + let gtmModules = await token.methods.getModulesByName(web3.utils.toHex("GeneralTransferManager")).call(); + for (const m of gtmModules) { + let gtmEvents = await getLogsFromEtherscan( + m, + 9299699, + "latest", + "ModifyWhitelist(address,uint256,address,uint256,uint256,uint256,bool)" + ); + gtmEventsCount += gtmEvents.length; + } + console.log("Count of GeneralTransferManager Events: " + gtmEventsCount); + console.log("Modules Attached (TransferManager):"); + await getModules(2, token); + console.log("Modules Attached (PermissionManager):"); + await getModules(1, token); + console.log("Modules Attached (STO):"); + await getModules(3, token); + console.log("Modules Attached (Checkpoint):"); + await getModules(4, token); + console.log("Modules Attached (Burn):"); + await getModules(5, token); + console.log(); + console.log(); +} + +async function getModules(type, token) { + let modules = await token.methods.getModulesByType(type).call(); + for (const m of modules) { + let moduleData = await token.methods.getModule(m).call(); + console.log(" Name: " + web3.utils.toAscii(moduleData[0])); + console.log(" Address: " + m); + } +} + +async function getLogsFromEtherscan(_address, _fromBlock, _toBlock, _eventSignature) { + let urlDomain = "api"; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: "logs", + action: "getLogs", + fromBlock: _fromBlock, + toBlock: _toBlock, + address: _address, + topic0: web3.utils.sha3(_eventSignature), + apikey: "THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559" + }, + method: "GET", + json: true + }; + let data = await request(options); + return data.result; +} + +async function getABIfromEtherscan(_address) { + let urlDomain = "api"; + const options = { + url: `https://${urlDomain}.etherscan.io/api`, + qs: { + module: "contract", + action: "getabi", + address: _address, + apikey: "THM9IUVC2DJJ6J5MTICDE6H1HGQK14X559" + }, + method: "GET", + json: true + }; + let data = await request(options); + return JSON.parse(data.result); +} + +getTokens(); diff --git a/scripts/wintest.cmd b/scripts/wintest.cmd index e5a2a6a61..26976da18 100644 --- a/scripts/wintest.cmd +++ b/scripts/wintest.cmd @@ -18,7 +18,5 @@ for %%i in (test\*.js) do call :PushTest %%i :PushTest if NOT "%1" == "test\a_poly_oracle.js" ( - if NOT "%1" == "test\s_v130_to_v140_upgrade.js" ( - set var=%var% %1 - ) + set var=%var% %1 ) diff --git a/test/a_poly_oracle.js b/test/a_poly_oracle.js index 6363fe151..025b6ec72 100644 --- a/test/a_poly_oracle.js +++ b/test/a_poly_oracle.js @@ -5,10 +5,10 @@ import { increaseTime } from "./helpers/time"; import { catchRevert } from "./helpers/exceptions"; const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("PolyOracle", accounts => { +contract("PolyOracle", async (accounts) => { let I_PolyOracle; let owner; const URL = @@ -52,41 +52,43 @@ contract("PolyOracle", accounts => { describe("Scheduling test cases", async () => { it("Should schedule the timing of the call - fails - non owner", async () => { let timeScheduling = [ - latestTime() + duration.minutes(1), - latestTime() + duration.minutes(2), - latestTime() + duration.minutes(3) + await latestTime() + duration.minutes(1), + await latestTime() + duration.minutes(2), + await latestTime() + duration.minutes(3) ]; - await catchRevert(I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: accounts[1], value: web3.utils.toWei("2") })); + await catchRevert(I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: accounts[1], value: new BN(web3.utils.toWei("2")) })); }); it("Should schedule the timing of the call - fails - no value", async () => { let timeScheduling = [ - latestTime() + duration.minutes(1), - latestTime() + duration.minutes(2), - latestTime() + duration.minutes(3) + await latestTime() + duration.minutes(1), + await latestTime() + duration.minutes(2), + await latestTime() + duration.minutes(3) ]; await catchRevert(I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: owner })); }); it("Should schedule the timing of the call - single call", async () => { - let blockNo = latestBlock(); - let tx = await I_PolyOracle.schedulePriceUpdatesFixed([], { from: owner, value: web3.utils.toWei("1") }); - assert.isAtMost(tx.logs[0].args._time.toNumber(), latestTime()); + let blockNo = await latestBlock(); + let tx = await I_PolyOracle.schedulePriceUpdatesFixed([], { from: owner, value: new BN(web3.utils.toWei("1")) }); + assert.isAtMost(tx.logs[0].args._time.toNumber(), await latestTime()); // await increaseTime(50); - const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 1); + const logNewPriceWatcher = (await I_PolyOracle.getPastEvents('PriceUpdated', {filter: {from: blockNo}}))[0]; // const log = await logNewPriceWatcher; assert.equal(logNewPriceWatcher.event, "PriceUpdated", "PriceUpdated not emitted."); assert.isNotNull(logNewPriceWatcher.args._price, "Price returned was null."); assert.equal(logNewPriceWatcher.args._oldPrice.toNumber(), 0); console.log( - "Success! Current price is: " + logNewPriceWatcher.args._price.dividedBy(new BigNumber(10).pow(18)).toNumber() + " USD/POLY" + "Success! Current price is: " + + logNewPriceWatcher.args._price.div(new BN(10).pow(new BN(18))).toNumber() + + " USD/POLY" ); }); it("Should schedule the timing of the call - multiple calls", async () => { - let blockNo = latestBlock(); - let timeScheduling = [latestTime() + duration.seconds(10), latestTime() + duration.seconds(20)]; - let tx = await I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: owner, value: web3.utils.toWei("1.5") }); + let blockNo = await latestBlock(); + let timeScheduling = [await latestTime() + duration.seconds(10), await latestTime() + duration.seconds(20)]; + let tx = await I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: owner, value: new BN(web3.utils.toWei("1.5")) }); let event_data = tx.logs; @@ -97,34 +99,34 @@ contract("PolyOracle", accounts => { } // Wait for the callback to be invoked by oraclize and the event to be emitted - const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = (await I_PolyOracle.getPastEvents('PriceUpdated', {filter: {from: blockNo}}))[1]; const log = await logNewPriceWatcher; assert.equal(log.event, "PriceUpdated", "PriceUpdated not emitted."); assert.isNotNull(log.args._price, "Price returned was null."); - console.log("Success! Current price is: " + log.args._price.dividedBy(new BigNumber(10).pow(18)).toNumber() + " USD/POLY"); + console.log("Success! Current price is: " + log.args._price.div(new BN(10).pow(new BN(18))).toNumber() + " USD/POLY"); }); it("Should schedule to call using iters - fails", async () => { - await catchRevert(I_PolyOracle.schedulePriceUpdatesRolling(latestTime() + 10, 30, 2, { from: accounts[6] })); + await catchRevert(I_PolyOracle.schedulePriceUpdatesRolling(await latestTime() + 10, 30, 2, { from: accounts[6] })); }); it("Should schedule to call using iters", async () => { - let blockNo = latestBlock(); + let blockNo = await latestBlock(); console.log(`Latest Block number of the local chain:${blockNo}`); - let tx = await I_PolyOracle.schedulePriceUpdatesRolling(latestTime() + 10, 10, 2, { from: owner }); + let tx = await I_PolyOracle.schedulePriceUpdatesRolling(await latestTime() + 10, 10, 2, { from: owner }); let event_data = tx.logs; for (var i = 0; i < event_data.length; i++) { let time = event_data[i].args._time; requestIds.push(event_data[i].args._queryId); console.log(` checking the time for the ${i} index and the scheduling time is ${time}`); - assert.isAtMost(time.toNumber(), latestTime() + (i + 1) * 30); + assert.isAtMost(time.toNumber(), await latestTime() + (i + 1) * 30); } // Wait for the callback to be invoked by oraclize and the event to be emitted - const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = (await I_PolyOracle.getPastEvents('PriceUpdated', {filter: {from: blockNo}}))[1]; const log = await logNewPriceWatcher; assert.equal(log.event, "PriceUpdated", "PriceUpdated not emitted."); assert.isNotNull(log.args._price, "Price returned was null."); - console.log("Success! Current price is: " + log.args._price.dividedBy(new BigNumber(10).pow(18)).toNumber() + " USD/POLY"); + console.log("Success! Current price is: " + log.args._price.div(new BN(10).pow(new BN(18))).toNumber() + " USD/POLY"); latestPrice = log.args._price; }); }); @@ -154,26 +156,26 @@ contract("PolyOracle", accounts => { }); it("Should change the sanity bounds manually - fails - bad owner", async () => { - await catchRevert(I_PolyOracle.setSanityBounds(new BigNumber(25).times(new BigNumber(10).pow(16)), { from: accounts[6] })); + await catchRevert(I_PolyOracle.setSanityBounds(new BN(25).mul(new BN(10).pow(16)), { from: accounts[6] })); }); it("Should change the sanity bounds manually", async () => { console.log(JSON.stringify(await I_PolyOracle.sanityBounds.call())); - await I_PolyOracle.setSanityBounds(new BigNumber(25).times(new BigNumber(10).pow(16)), { from: owner }); + await I_PolyOracle.setSanityBounds(new BN(25).mul(new BN(10).pow(16)), { from: owner }); let sanityBounds = await I_PolyOracle.sanityBounds.call(); console.log(JSON.stringify(await I_PolyOracle.sanityBounds.call())); - assert.equal(sanityBounds.toNumber(), new BigNumber(25).times(new BigNumber(10).pow(16)).toNumber()); + assert.equal(sanityBounds.toNumber(), new BN(25).mul(new BN(10).pow(16)).toNumber()); }); it("Should change the gas price manually - fails - bad owner", async () => { - await catchRevert(I_PolyOracle.setGasPrice(new BigNumber(60).times(new BigNumber(10).pow(9)), { from: accounts[6] })); + await catchRevert(I_PolyOracle.setGasPrice(new BN(60).mul(new BN(10).pow(9)), { from: accounts[6] })); }); it("Should change the gas price manually", async () => { - await I_PolyOracle.setGasPrice(new BigNumber(60).times(new BigNumber(10).pow(9)), { from: owner }); - let blockNo = latestBlock(); - let timeScheduling = [latestTime() + duration.seconds(10), latestTime() + duration.seconds(20)]; - let tx = await I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: owner, value: web3.utils.toWei("2") }); + await I_PolyOracle.setGasPrice(new BN(60).mul(new BN(10).pow(9)), { from: owner }); + let blockNo = await latestBlock(); + let timeScheduling = [await latestTime() + duration.seconds(10), await latestTime() + duration.seconds(20)]; + let tx = await I_PolyOracle.schedulePriceUpdatesFixed(timeScheduling, { from: owner, value: new BN(web3.utils.toWei("2")) }); let event_data = tx.logs; @@ -182,13 +184,14 @@ contract("PolyOracle", accounts => { console.log(` checking the time for the ${i} index and the scheduling time is ${time}`); assert.isAtMost(time.toNumber(), timeScheduling[i]); } - - const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = (await I_PolyOracle.getPastEvents('PriceUpdated', {filter: {from: blockNo}}))[1]; assert.equal(logNewPriceWatcher.event, "PriceUpdated", "PriceUpdated not emitted."); assert.isNotNull(logNewPriceWatcher.args._price, "Price returned was null."); console.log( - "Success! Current price is: " + logNewPriceWatcher.args._price.dividedBy(new BigNumber(10).pow(18)).toNumber() + " USD/POLY" + "Success! Current price is: " + + logNewPriceWatcher.args._price.div(new BN(10).pow(new BN(18))).toNumber() + + " USD/POLY" ); // assert.isTrue(false); }); @@ -243,14 +246,16 @@ contract("PolyOracle", accounts => { }); it("Should schedule the timing of the call - after changes", async () => { - let blockNo = latestBlock(); - let tx = await I_PolyOracle.schedulePriceUpdatesFixed([], { from: owner, value: web3.utils.toWei("1") }); - assert.isAtMost(tx.logs[0].args._time.toNumber(), latestTime()); - const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 1); + let blockNo = await latestBlock(); + let tx = await I_PolyOracle.schedulePriceUpdatesFixed([], { from: owner, value: new BN(web3.utils.toWei("1")) }); + assert.isAtMost(tx.logs[0].args._time.toNumber(), await latestTime()); + const logNewPriceWatcher = (await I_PolyOracle.getPastEvents('PriceUpdated', {filter: {from: blockNo}}))[0]; assert.equal(logNewPriceWatcher.event, "PriceUpdated", "PriceUpdated not emitted."); assert.isNotNull(logNewPriceWatcher.args._price, "Price returned was null."); console.log( - "Success! Current price is: " + logNewPriceWatcher.args._price.dividedBy(new BigNumber(10).pow(18)).toNumber() + " USD/POLY" + "Success! Current price is: " + + logNewPriceWatcher.args._price.div(new BN(10).pow(new BN(18))).toNumber() + + " USD/POLY" ); // assert.isTrue(false); }); diff --git a/test/b_capped_sto.js b/test/b_capped_sto.js index f9238ffd9..1766fd8be 100644 --- a/test/b_capped_sto.js +++ b/test/b_capped_sto.js @@ -1,8 +1,8 @@ import latestTime from "./helpers/latestTime"; -import { duration, ensureException, promisifyLogWatch, latestBlock } from "./helpers/utils"; +import { duration, ensureException, latestBlock } from "./helpers/utils"; import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeModuleCall } from "./helpers/encodeCall"; -import { setUpPolymathNetwork, deployGPMAndVerifyed } from "./helpers/createInstances"; +import { setUpPolymathNetwork, deployGPMAndVerifyed, deployCappedSTOAndVerifyed } from "./helpers/createInstances"; import { catchRevert } from "./helpers/exceptions"; const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); @@ -10,15 +10,17 @@ const CappedSTO = artifacts.require("./CappedSTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; +let toBN = Web3.utils.toBN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port let ETH = 0; let POLY = 1; let DAI = 2; -contract("CappedSTO", accounts => { +contract("CappedSTO", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -58,6 +60,10 @@ contract("CappedSTO", accounts => { let I_PolymathRegistry; let I_STRProxied; let I_MRProxied; + let I_STRGetter; + let I_STGetter; + let stGetter_eth; + let stGetter_poly; let pauseTime; // SecurityToken Details for funds raise Type ETH @@ -78,15 +84,15 @@ contract("CappedSTO", accounts => { const budget = 0; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // Capped STO details let startTime_ETH1; let endTime_ETH1; let startTime_ETH2; let endTime_ETH2; - const cap = web3.utils.toWei("10000"); - const rate = 1000; + const cap = new BN(web3.utils.toWei("10000")); + const rate = new BN(web3.utils.toWei("1000")); const E_fundRaiseType = 0; const address_zero = "0x0000000000000000000000000000000000000000"; @@ -95,15 +101,18 @@ contract("CappedSTO", accounts => { let startTime_POLY2; let endTime_POLY2; let blockNo; - const P_cap = web3.utils.toWei("50000"); + const P_cap = new BN(web3.utils.toWei("50000")); const P_fundRaiseType = 1; - const P_rate = 5; - const cappedSTOSetupCost = web3.utils.toWei("20000", "ether"); - const maxCost = cappedSTOSetupCost; + const P_rate = new BN(web3.utils.toWei("5")); + const cappedSTOSetupCost = new BN(web3.utils.toWei("20000", "ether")); + const cappedSTOSetupCostPOLY = new BN(web3.utils.toWei("80000", "ether")); + const maxCost = cappedSTOSetupCostPOLY; const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; + let currentTime; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_investor1 = accounts[4]; @@ -113,7 +122,6 @@ contract("CappedSTO", accounts => { token_owner = account_issuer; let instances = await setUpPolymathNetwork(account_polymath, token_owner); - [ I_PolymathRegistry, I_PolyToken, @@ -125,27 +133,16 @@ contract("CappedSTO", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 5: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, new BN(0)); // STEP 6: Deploy the CappedSTOFactory - I_CappedSTOFactory = await CappedSTOFactory.new(I_PolyToken.address, cappedSTOSetupCost, 0, 0, { from: token_owner }); - - assert.notEqual( - I_CappedSTOFactory.address.valueOf(), - address_zero, - "CappedSTOFactory contract was not deployed" - ); - - // STEP 7: Register the Modules with the ModuleRegistry contract - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); + [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, cappedSTOSetupCost); // Printing all the contract addresses console.log(` @@ -176,68 +173,65 @@ contract("CappedSTO", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - - I_SecurityToken_ETH = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken_ETH.ModuleAdded({ from: _blockNo }), 1); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); + I_SecurityToken_ETH = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter_eth = await STGetter.at(I_SecurityToken_ETH.address); + const log = (await I_SecurityToken_ETH.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); assert.equal(web3.utils.hexToString(log.args._name), "GeneralTransferManager"); }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken_ETH.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter_eth.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should mint the tokens before attaching the STO", async () => { - await catchRevert( - I_SecurityToken_ETH.mint(address_zero, web3.utils.toWei("1"), { from: token_owner }) - ); + await catchRevert(I_SecurityToken_ETH.issue(address_zero, new BN(new BN(web3.utils.toWei("1"))), "0x0", { from: token_owner })); }); it("Should fail to launch the STO due to security token doesn't have the sufficient POLY", async () => { - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); - let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, 0, [E_fundRaiseType], account_fundsReceiver]); + let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, new BN(0), [E_fundRaiseType], account_fundsReceiver]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to launch the STO due to rate is 0", async () => { - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); - await I_PolyToken.transfer(I_SecurityToken_ETH.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.transfer(I_SecurityToken_ETH.address, cappedSTOSetupCostPOLY, { from: token_owner }); - let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, 0, [E_fundRaiseType], account_fundsReceiver]); + let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, new BN(0), [E_fundRaiseType], account_fundsReceiver]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to launch the STO due funds reciever account 0x", async () => { - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, [E_fundRaiseType], address_zero]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to launch the STO due to raise type of 0 length", async () => { - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, [], account_fundsReceiver]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to launch the STO due to startTime > endTime", async () => { @@ -250,26 +244,26 @@ contract("CappedSTO", accounts => { account_fundsReceiver ]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to launch the STO due to cap is of 0 securityToken", async () => { - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); - let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, 0, rate, [E_fundRaiseType], account_fundsReceiver]); + let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, new BN(0), rate, [E_fundRaiseType], account_fundsReceiver]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); - it("Should fail to launch the STO due to different value incompare to getInitFunction", async() => { - let startTime = latestTime() + duration.days(1); + it("Should fail to launch the STO due to different value incompare to getInitFunction", async () => { + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); - let bytesSTO = encodeModuleCall(['uint256', 'uint256', 'uint256'], [startTime, endTime, 0, ]); - await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + let bytesSTO = encodeModuleCall(["uint256", "uint256", "uint256"], [startTime, endTime, 0]); + await catchRevert(I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should successfully attach the STO module to the security token", async () => { - startTime_ETH1 = latestTime() + duration.days(1); + startTime_ETH1 = await latestTime() + duration.days(1); endTime_ETH1 = startTime_ETH1 + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [ startTime_ETH1, @@ -279,26 +273,28 @@ contract("CappedSTO", accounts => { [E_fundRaiseType], account_fundsReceiver ]); - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - I_CappedSTO_Array_ETH.push(CappedSTO.at(tx.logs[3].args._module)); + I_CappedSTO_Array_ETH.push(await CappedSTO.at(tx.logs[3].args._module)); }); - it("Should call the configure function -- fail because of the bad owner", async()=> { + it("Should call the configure function -- fail because of the bad owner", async () => { await catchRevert( - I_CappedSTO_Array_ETH[0].configure(startTime_ETH1, endTime_ETH1, cap, rate, [E_fundRaiseType], account_fundsReceiver, {from: account_polymath }) + I_CappedSTO_Array_ETH[0].configure(startTime_ETH1, endTime_ETH1, cap, rate, [E_fundRaiseType], account_fundsReceiver, { + from: account_polymath + }) ); - }) + }); }); describe("verify the data of STO", async () => { it("Should verify the configuration of the STO", async () => { - assert.equal(await I_CappedSTO_Array_ETH[0].startTime.call(), startTime_ETH1, "STO Configuration doesn't set as expected"); - assert.equal(await I_CappedSTO_Array_ETH[0].endTime.call(), endTime_ETH1, "STO Configuration doesn't set as expected"); - assert.equal((await I_CappedSTO_Array_ETH[0].cap.call()).toNumber(), cap, "STO Configuration doesn't set as expected"); - assert.equal(await I_CappedSTO_Array_ETH[0].rate.call(), rate, "STO Configuration doesn't set as expected"); + assert.equal(await I_CappedSTO_Array_ETH[0].startTime(), startTime_ETH1, "1STO Configuration doesn't set as expected"); + assert.equal(await I_CappedSTO_Array_ETH[0].endTime(), endTime_ETH1, "2STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_ETH[0].cap()).toString(), cap.toString(), "3STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_ETH[0].rate()).toString(), rate.toString(), "4STO Configuration doesn't set as expected"); assert.equal( await I_CappedSTO_Array_ETH[0].fundRaiseTypes.call(E_fundRaiseType), true, @@ -309,100 +305,66 @@ contract("CappedSTO", accounts => { describe("Buy tokens", async () => { it("Should buy the tokens -- failed due to startTime is greater than Current time", async () => { - await catchRevert( - web3.eth.sendTransaction({ - from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - value: web3.utils.toWei("1", "ether") - }) - ); - }); - - it("Should buy the tokens -- failed due to invested amount is zero", async () => { - await catchRevert( - web3.eth.sendTransaction({ - from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - value: web3.utils.toWei("0", "ether") - }) - ); + await catchRevert(I_CappedSTO_Array_ETH[0].buyTokens(account_investor1, { from: account_investor1, value: new BN(web3.utils.toWei("1", "ether")) })); + await increaseTime(duration.days(1)); }); it("Should buy the tokens -- Failed due to investor is not in the whitelist", async () => { - await catchRevert( - web3.eth.sendTransaction({ - from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - value: web3.utils.toWei("1", "ether") - }) - ); - }); - - it("Should buy the tokens -- Failed due to wrong granularity", async () => { - await catchRevert( - web3.eth.sendTransaction({ - from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - value: web3.utils.toWei("0.1111", "ether") - }) - ); - }); + await catchRevert(I_CappedSTO_Array_ETH[0].buyTokens(account_investor1, { from: account_investor1, value: new BN(web3.utils.toWei("1", "ether")) })); - it("Should Buy the tokens", async () => { - blockNo = latestBlock(); - fromTime = latestTime(); - toTime = latestTime() + duration.days(15); + blockNo = await latestBlock(); + fromTime = await latestTime(); + toTime = await latestTime() + duration.days(15); expiryTime = toTime + duration.days(100); P_fromTime = fromTime + duration.days(1); P_toTime = P_fromTime + duration.days(50); P_expiryTime = toTime + duration.days(100); - balanceOfReceiver = await web3.eth.getBalance(account_fundsReceiver); // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor1, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, fromTime, toTime, expiryTime, { from: account_issuer }); assert.equal(tx.logs[0].args._investor, account_investor1, "Failed in adding the investor in whitelist"); + }); - // Jump time - await increaseTime(duration.days(1)); - // Fallback transaction - await web3.eth.sendTransaction({ + it("Should buy the tokens -- failed due to invested amount is zero", async () => { + await catchRevert(I_CappedSTO_Array_ETH[0].buyTokens(account_investor1, { from: account_investor1, value: new BN(web3.utils.toWei("0", "ether")) })); + }); + + it("Should Buy the tokens", async () => { + + balanceOfReceiver = new BN(await web3.eth.getBalance(account_fundsReceiver)); + + await I_CappedSTO_Array_ETH[0].buyTokens(account_investor1, { from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - gas: 2100000, value: web3.utils.toWei("1", "ether") }); - assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1); + assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).div(new BN(10).pow(new BN(18))).toNumber(), 1); assert.equal(await I_CappedSTO_Array_ETH[0].investorCount.call(), 1); - assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); - assert.equal((await I_CappedSTO_Array_ETH[0].getTokensSold.call()).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); + assert.equal((await I_CappedSTO_Array_ETH[0].getTokensSold.call()).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); it("Verification of the event Token Purchase", async () => { - const log = await promisifyLogWatch(I_CappedSTO_Array_ETH[0].TokenPurchase({ from: blockNo }), 1); - + const log = (await I_CappedSTO_Array_ETH[0].getPastEvents('TokenPurchase', {filter: {from: blockNo}}))[0]; assert.equal(log.args.purchaser, account_investor1, "Wrong address of the investor"); - assert.equal(log.args.amount.dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000, "Wrong No. token get dilivered"); + assert.equal(log.args.amount.div(new BN(10).pow(new BN(18))).toNumber(), 1000, "Wrong No. token get dilivered"); }); - it("Should fail to buy the tokens -- Because fundRaiseType is ETH not POLY", async ()=> { - await I_PolyToken.getTokens(web3.utils.toWei("500"), account_investor1); - await I_PolyToken.approve(I_CappedSTO_Array_ETH[0].address, web3.utils.toWei("500"), {from: account_investor1}); - await catchRevert( - I_CappedSTO_Array_ETH[0].buyTokensWithPoly(web3.utils.toWei("500"), {from: account_investor1}) - ); - }) + it("Should fail to buy the tokens -- Because fundRaiseType is ETH not POLY", async () => { + await I_PolyToken.getTokens(new BN(new BN(web3.utils.toWei("500"))), account_investor1); + await I_PolyToken.approve(I_CappedSTO_Array_ETH[0].address, new BN(new BN(web3.utils.toWei("500"))), { from: account_investor1 }); + await catchRevert(I_CappedSTO_Array_ETH[0].buyTokensWithPoly(new BN(new BN(web3.utils.toWei("500"))), { from: account_investor1 })); + }); it("Should pause the STO -- Failed due to wrong msg.sender", async () => { await catchRevert(I_CappedSTO_Array_ETH[0].pause({ from: account_investor1 })); }); it("Should pause the STO", async () => { - pauseTime = latestTime(); + pauseTime = await latestTime(); let tx = await I_CappedSTO_Array_ETH[0].pause({ from: account_issuer }); assert.isTrue(await I_CappedSTO_Array_ETH[0].paused.call()); }); @@ -413,7 +375,7 @@ contract("CappedSTO", accounts => { from: account_investor1, to: I_CappedSTO_Array_ETH[0].address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }) ); }); @@ -427,71 +389,78 @@ contract("CappedSTO", accounts => { assert.isFalse(await I_CappedSTO_Array_ETH[0].paused.call()); }); - it("Should buy the tokens -- Failed due to wrong granularity", async () => { - await catchRevert( - web3.eth.sendTransaction({ - from: account_investor1, - to: I_CappedSTO_Array_ETH[0].address, - value: web3.utils.toWei("0.1111", "ether") - }) - ); - }); - - it("Should restrict to buy tokens after hiting the cap in second tx first tx pass", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + it("Should buy the granular unit tokens and refund pending amount", async () => { + await I_SecurityToken_ETH.changeGranularity(new BN(10).pow(new BN(21)), { from: token_owner }); + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, fromTime, toTime + duration.days(20), expiryTime, - true, { from: account_issuer } ); - assert.equal(tx.logs[0].args._investor, account_investor2, "Failed in adding the investor in whitelist"); + let initBalance = new BN(await web3.eth.getBalance(account_investor2)); + tx = await I_CappedSTO_Array_ETH[0].buyTokens(account_investor2, { + from: account_investor2, + value: new BN(web3.utils.toWei("1.5", "ether")), + gasPrice: 1 + }); + let finalBalance = new BN(await web3.eth.getBalance(account_investor2)); + assert.equal( + finalBalance + .add(new BN(tx.receipt.gasUsed)) + .add(new BN(web3.utils.toWei("1", "ether"))) + .toString(), + initBalance.toString() + ); + await I_SecurityToken_ETH.changeGranularity(1, { from: token_owner }); + assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).div(new BN(10).pow(new BN(18))).toNumber(), 2); + + assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); + }); + it("Should restrict to buy tokens after hiting the cap in second tx first tx pass", async () => { // Fallback transaction await web3.eth.sendTransaction({ from: account_investor2, to: I_CappedSTO_Array_ETH[0].address, gas: 2100000, - value: web3.utils.toWei("9", "ether") + value: new BN(web3.utils.toWei("8", "ether")) }); - assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 10); + assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).div(new BN(10).pow(new BN(18))).toNumber(), 10); assert.equal(await I_CappedSTO_Array_ETH[0].investorCount.call(), 2); - assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 9000); - await catchRevert(I_CappedSTO_Array_ETH[0].buyTokens(account_investor2, { value: web3.utils.toWei("81") })); + assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 9000); + await catchRevert(I_CappedSTO_Array_ETH[0].buyTokens(account_investor2, { value: new BN(web3.utils.toWei("81")) })); }); it("Should fundRaised value equal to the raised value in the funds receiver wallet", async () => { const newBalance = await web3.eth.getBalance(account_fundsReceiver); //console.log("WWWW",newBalance,await I_CappedSTO.fundsRaised.call(),balanceOfReceiver); - let op = new BigNumber(newBalance) - .minus(balanceOfReceiver) - .toNumber(); + let op = new BN(newBalance).sub(balanceOfReceiver); assert.equal( - (await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).toNumber(), - op, + (await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).toString(), + op.toString(), "Somewhere raised money get stolen or sent to wrong wallet" ); }); it("Should get the raised amount of ether", async () => { - assert.equal(await I_CappedSTO_Array_ETH[0].getRaised.call(ETH), web3.utils.toWei("10", "ether")); + assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(ETH)).toString(), new BN(web3.utils.toWei("10", "ether")).toString()); }); it("Should get the raised amount of poly", async () => { - assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(POLY)).toNumber(), web3.utils.toWei("0", "ether")); + assert.equal((await I_CappedSTO_Array_ETH[0].getRaised.call(POLY)).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); }); describe("Reclaim poly sent to STO by mistake", async () => { it("Should fail to reclaim POLY because token contract address is 0 address", async () => { - let value = web3.utils.toWei("100", "ether"); + let value = new BN(web3.utils.toWei("100", "ether")); await I_PolyToken.getTokens(value, account_investor1); await I_PolyToken.transfer(I_CappedSTO_Array_ETH[0].address, value, { from: account_investor1 }); @@ -499,30 +468,30 @@ contract("CappedSTO", accounts => { }); it("Should successfully reclaim POLY", async () => { - let initInvestorBalance = await I_PolyToken.balanceOf(account_investor1); - let initOwnerBalance = await I_PolyToken.balanceOf(token_owner); - let initContractBalance = await I_PolyToken.balanceOf(I_CappedSTO_Array_ETH[0].address); - let value = web3.utils.toWei("100", "ether"); + let initInvestorBalance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let initOwnerBalance = new BN(await I_PolyToken.balanceOf(token_owner)); + let initContractBalance = new BN(await I_PolyToken.balanceOf(I_CappedSTO_Array_ETH[0].address)); + let value = new BN(web3.utils.toWei("100", "ether")); await I_PolyToken.getTokens(value, account_investor1); await I_PolyToken.transfer(I_CappedSTO_Array_ETH[0].address, value, { from: account_investor1 }); await I_CappedSTO_Array_ETH[0].reclaimERC20(I_PolyToken.address, { from: token_owner }); assert.equal( - (await I_PolyToken.balanceOf(account_investor1)).toNumber(), - initInvestorBalance.toNumber(), + (await I_PolyToken.balanceOf(account_investor1)).toString(), + initInvestorBalance.toString(), "tokens are not transferred out from investor account" ); assert.equal( - (await I_PolyToken.balanceOf(token_owner)).toNumber(), + (await I_PolyToken.balanceOf(token_owner)).toString(), initOwnerBalance .add(value) .add(initContractBalance) - .toNumber(), + .toString(), "tokens are not added to the owner account" ); assert.equal( - (await I_PolyToken.balanceOf(I_CappedSTO_Array_ETH[0].address)).toNumber(), - 0, + (await I_PolyToken.balanceOf(I_CappedSTO_Array_ETH[0].address)).toString(), + new BN(0).toString(), "tokens are not trandfered out from STO contract" ); }); @@ -530,11 +499,11 @@ contract("CappedSTO", accounts => { describe("Attach second ETH STO module", async () => { it("Should successfully attach the second STO module to the security token", async () => { - startTime_ETH2 = latestTime() + duration.days(1); + startTime_ETH2 = await latestTime() + duration.days(1); endTime_ETH2 = startTime_ETH2 + duration.days(30); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); - await I_PolyToken.transfer(I_SecurityToken_ETH.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken_ETH.address, cappedSTOSetupCostPOLY, { from: token_owner }); let bytesSTO = encodeModuleCall(STOParameters, [ startTime_ETH2, endTime_ETH2, @@ -543,18 +512,18 @@ contract("CappedSTO", accounts => { [E_fundRaiseType], account_fundsReceiver ]); - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - I_CappedSTO_Array_ETH.push(CappedSTO.at(tx.logs[3].args._module)); + I_CappedSTO_Array_ETH.push(await CappedSTO.at(tx.logs[3].args._module)); }); it("Should verify the configuration of the STO", async () => { assert.equal(await I_CappedSTO_Array_ETH[1].startTime.call(), startTime_ETH2, "STO Configuration doesn't set as expected"); assert.equal(await I_CappedSTO_Array_ETH[1].endTime.call(), endTime_ETH2, "STO Configuration doesn't set as expected"); - assert.equal((await I_CappedSTO_Array_ETH[1].cap.call()).toNumber(), cap, "STO Configuration doesn't set as expected"); - assert.equal(await I_CappedSTO_Array_ETH[1].rate.call(), rate, "STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_ETH[1].cap.call()).toString(), cap.toString(), "STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_ETH[1].rate.call()).toString(), rate.toString(), "STO Configuration doesn't set as expected"); assert.equal( await I_CappedSTO_Array_ETH[1].fundRaiseTypes.call(E_fundRaiseType), true, @@ -563,9 +532,9 @@ contract("CappedSTO", accounts => { }); it("Should successfully whitelist investor 3", async () => { - balanceOfReceiver = await web3.eth.getBalance(account_fundsReceiver); + balanceOfReceiver = new BN(await web3.eth.getBalance(account_fundsReceiver)); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor3, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor3, fromTime, toTime, expiryTime, { from: account_issuer, gas: 500000 }); @@ -580,7 +549,7 @@ contract("CappedSTO", accounts => { // Buying on behalf of another user should fail await catchRevert( - I_CappedSTO_Array_ETH[1].buyTokens(account_investor3, { from: account_issuer, value: web3.utils.toWei("1", "ether") }) + I_CappedSTO_Array_ETH[1].buyTokens(account_investor3, { from: account_issuer, value: new BN(web3.utils.toWei("1", "ether")) }) ); }); @@ -591,37 +560,36 @@ contract("CappedSTO", accounts => { }); it("Should allow non-matching beneficiary -- failed because it is already active", async () => { - await catchRevert( - I_CappedSTO_Array_ETH[1].changeAllowBeneficialInvestments(true, { from: account_issuer }) - ); + await catchRevert(I_CappedSTO_Array_ETH[1].changeAllowBeneficialInvestments(true, { from: account_issuer })); }); it("Should invest in second STO", async () => { - await I_CappedSTO_Array_ETH[1].buyTokens(account_investor3, { from: account_issuer, value: web3.utils.toWei("1", "ether") }); + await I_CappedSTO_Array_ETH[1].buyTokens(account_investor3, { from: account_issuer, value: new BN(web3.utils.toWei("1", "ether")) }); - assert.equal((await I_CappedSTO_Array_ETH[1].getRaised.call(ETH)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1); + assert.equal((await I_CappedSTO_Array_ETH[1].getRaised.call(ETH)).div(new BN(10).pow(new BN(18))).toNumber(), 1); assert.equal(await I_CappedSTO_Array_ETH[1].investorCount.call(), 1); - assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor3)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken_ETH.balanceOf(account_investor3)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); }); describe("Test cases for reaching limit number of STO modules", async () => { it("Should successfully attach 10 STO modules", async () => { const MAX_MODULES = 10; - let startTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(1); let endTime = startTime + duration.days(30); - - await I_PolyToken.getTokens(cappedSTOSetupCost * 19, token_owner); - await I_PolyToken.transfer(I_SecurityToken_ETH.address, cappedSTOSetupCost * 19, { from: token_owner }); + for (var i = 0; i < MAX_MODULES; i++) { + await I_PolyToken.getTokens(new BN(cappedSTOSetupCostPOLY), token_owner); + }; + await I_PolyToken.transfer(I_SecurityToken_ETH.address, new BN(cappedSTOSetupCostPOLY.mul(new BN(MAX_MODULES))), { from: token_owner }); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, [E_fundRaiseType], account_fundsReceiver]); for (var STOIndex = 2; STOIndex < MAX_MODULES; STOIndex++) { - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, `Wrong module type added at index ${STOIndex}`); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "CappedSTO", `Wrong STO module added at index ${STOIndex}`); - I_CappedSTO_Array_ETH.push(CappedSTO.at(tx.logs[3].args._module)); + I_CappedSTO_Array_ETH.push(await CappedSTO.at(tx.logs[3].args._module)); } }); @@ -631,10 +599,10 @@ contract("CappedSTO", accounts => { for (var STOIndex = 2; STOIndex < MAX_MODULES; STOIndex++) { await I_CappedSTO_Array_ETH[STOIndex].buyTokens(account_investor3, { from: account_investor3, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); assert.equal( - (await I_CappedSTO_Array_ETH[STOIndex].getRaised.call(ETH)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_CappedSTO_Array_ETH[STOIndex].getRaised.call(ETH)).div(new BN(10).pow(new BN(18))).toNumber(), 1 ); assert.equal(await I_CappedSTO_Array_ETH[STOIndex].investorCount.call(), 1); @@ -653,15 +621,15 @@ contract("CappedSTO", accounts => { it("POLY: Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(P_name, P_symbol, P_tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, P_symbol, "SecurityToken doesn't get deployed"); - - I_SecurityToken_POLY = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, P_symbol, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken_POLY.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken_POLY = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter_poly = await STGetter.at(I_SecurityToken_POLY.address); + const log = (await I_SecurityToken_POLY.getPastEvents('ModuleAdded', {filter: {from: blockNo}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); @@ -669,16 +637,16 @@ contract("CappedSTO", accounts => { }); it("POLY: Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken_POLY.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter_poly.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("POLY: Should successfully attach the STO module to the security token", async () => { - startTime_POLY1 = latestTime() + duration.days(2); + startTime_POLY1 = await latestTime() + duration.days(2); endTime_POLY1 = startTime_POLY1 + duration.days(30); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); - await I_PolyToken.transfer(I_SecurityToken_POLY.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken_POLY.address, cappedSTOSetupCostPOLY, { from: token_owner }); let bytesSTO = encodeModuleCall(STOParameters, [ startTime_POLY1, @@ -689,11 +657,11 @@ contract("CappedSTO", accounts => { account_fundsReceiver ]); - const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - I_CappedSTO_Array_POLY.push(CappedSTO.at(tx.logs[3].args._module)); + I_CappedSTO_Array_POLY.push(await CappedSTO.at(tx.logs[3].args._module)); }); }); @@ -702,90 +670,86 @@ contract("CappedSTO", accounts => { assert.equal( (await I_CappedSTO_Array_POLY[0].startTime.call()).toNumber(), startTime_POLY1, - "STO Configuration doesn't set as expected" + "1STO Configuration doesn't set as expected" ); assert.equal( (await I_CappedSTO_Array_POLY[0].endTime.call()).toNumber(), endTime_POLY1, - "STO Configuration doesn't set as expected" + "2STO Configuration doesn't set as expected" ); assert.equal( - (await I_CappedSTO_Array_POLY[0].cap.call()).dividedBy(new BigNumber(10).pow(18)).toNumber(), - BigNumber(P_cap).dividedBy(new BigNumber(10).pow(18)), - "STO Configuration doesn't set as expected" + (await I_CappedSTO_Array_POLY[0].cap.call()).div(new BN(10).pow(new BN(18))).toString(), + new BN(P_cap).div(new BN(10).pow(new BN(18))), + "3STO Configuration doesn't set as expected" ); - assert.equal(await I_CappedSTO_Array_POLY[0].rate.call(), P_rate, "STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_POLY[0].rate.call()).toString(), new BN(P_rate).toString(), "STO Configuration doesn't set as expected"); assert.equal( await I_CappedSTO_Array_POLY[0].fundRaiseTypes.call(P_fundRaiseType), true, - "STO Configuration doesn't set as expected" + "4STO Configuration doesn't set as expected" ); }); }); describe("Buy tokens", async () => { it("Should Buy the tokens", async () => { - await I_PolyToken.getTokens(10000 * Math.pow(10, 18), account_investor1); - blockNo = latestBlock(); + await I_PolyToken.getTokens(new BN(10).pow(new BN(22)), account_investor1); + blockNo = await latestBlock(); assert.equal( - (await I_PolyToken.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_PolyToken.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 10500, "Tokens are not transfered properly" ); - - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor1, P_fromTime, P_toTime, P_expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, P_fromTime, P_toTime, P_expiryTime, { from: account_issuer, gas: 500000 }); - assert.equal(tx.logs[0].args._investor, account_investor1, "Failed in adding the investor in whitelist"); - // Jump time await increaseTime(duration.days(17)); - - await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, 1000 * Math.pow(10, 18), { from: account_investor1 }); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, new BN(10).pow(new BN(21)), { from: account_investor1 }); // buyTokensWithPoly transaction - await I_CappedSTO_Array_POLY[0].buyTokensWithPoly(1000 * Math.pow(10, 18), { + await I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(10).pow(new BN(21)), { from: account_investor1 }); - - assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); assert.equal(await I_CappedSTO_Array_POLY[0].investorCount.call(), 1); assert.equal( - (await I_SecurityToken_POLY.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_SecurityToken_POLY.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 5000 ); + }); it("Verification of the event Token Purchase", async () => { - const log = await promisifyLogWatch(I_CappedSTO_Array_POLY[0].TokenPurchase({ from: blockNo }), 1); + const log = (await I_CappedSTO_Array_POLY[0].getPastEvents('TokenPurchase', {filter: {from: blockNo}}))[0]; assert.equal(log.args.purchaser, account_investor1, "Wrong address of the investor"); - assert.equal(log.args.amount.dividedBy(new BigNumber(10).pow(18)).toNumber(), 5000, "Wrong No. token get dilivered"); + assert.equal(log.args.amount.div(new BN(10).pow(new BN(18))).toNumber(), 5000, "Wrong No. token get dilivered"); }); - it("Should failed to buy tokens -- because fundraisetype is POLY not ETH", async() => { + it("Should failed to buy tokens -- because fundraisetype is POLY not ETH", async () => { await catchRevert( // Fallback transaction web3.eth.sendTransaction({ from: account_investor1, to: I_CappedSTO_Array_POLY[0].address, gas: 2100000, - value: web3.utils.toWei("2", "ether") + value: new BN(web3.utils.toWei("2", "ether")) }) ); }); - it("Should fail in buying tokens because buying is paused", async() => { + it("Should fail in buying tokens because buying is paused", async () => { await I_CappedSTO_Array_POLY[0].pause({ from: account_issuer }); - await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, 1000 * Math.pow(10, 18), { from: account_investor1 }); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, new BN(10).pow(new BN(21)), { from: account_investor1 }); // buyTokensWithPoly transaction await catchRevert( - I_CappedSTO_Array_POLY[0].buyTokensWithPoly(1000 * Math.pow(10, 18), { + I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(10).pow(new BN(21)), { from: account_investor1, gas: 6000000 }) @@ -793,56 +757,68 @@ contract("CappedSTO", accounts => { await I_CappedSTO_Array_POLY[0].unpause({ from: account_issuer }); }); - - it("Should restrict to buy tokens after hiting the cap in second tx first tx pass", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + it("Should buy the granular unit tokens and charge only required POLY", async () => { + await I_SecurityToken_POLY.changeGranularity(new BN(10).pow(new BN(22)), { from: token_owner }); + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, P_fromTime, P_toTime + duration.days(20), P_expiryTime, - true, { from: account_issuer, gas: 500000 } ); - + console.log((await I_SecurityToken_POLY.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber()); assert.equal(tx.logs[0].args._investor, account_investor2, "Failed in adding the investor in whitelist"); + await I_PolyToken.getTokens(new BN(10).pow(new BN(22)), account_investor2); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, new BN(9000).mul(new BN(10).pow(new BN(18))), { from: account_investor2 }); + const initRaised = (await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).div(new BN(10).pow(new BN(18))).toNumber(); + tx = await I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(3000).mul(new BN(10).pow(new BN(18))), { from: account_investor2 }); + await I_SecurityToken_POLY.changeGranularity(1, { from: token_owner }); + assert.equal( + (await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).div(new BN(10).pow(new BN(18))).toNumber(), + initRaised + 2000 + ); //2000 this call, 1000 earlier + assert.equal((await I_PolyToken.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 8000); + assert.equal( + (await I_SecurityToken_POLY.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), + 10000 + ); + }); - await I_PolyToken.getTokens(10000 * Math.pow(10, 18), account_investor2); - - await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, 9000 * Math.pow(10, 18), { from: account_investor2 }); - + it("Should restrict to buy tokens after hiting the cap in second tx first tx pass", async () => { // buyTokensWithPoly transaction - await I_CappedSTO_Array_POLY[0].buyTokensWithPoly(9000 * Math.pow(10, 18), { from: account_investor2 }); + await I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(7000).mul(new BN(10).pow(new BN(18))), { from: account_investor2 }); - assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 10000); + assert.equal( + (await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).div(new BN(10).pow(new BN(18))).toNumber(), + 10000 + ); assert.equal(await I_CappedSTO_Array_POLY[0].investorCount.call(), 2); assert.equal( - (await I_SecurityToken_POLY.balanceOf(account_investor2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_SecurityToken_POLY.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 45000 ); - await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, 1000 * Math.pow(10, 18), { from: account_investor1 }); - await catchRevert( - I_CappedSTO_Array_POLY[0].buyTokensWithPoly(1000 * Math.pow(10, 18), { from: account_investor1 }) - ); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, new BN(1000).mul(new BN(10).pow(new BN(18))), { from: account_investor1 }); + await catchRevert(I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(1000).mul(new BN(10).pow(new BN(18))), { from: account_investor1 })); }); it("Should failed at the time of buying the tokens -- Because STO get expired", async () => { await increaseTime(duration.days(31)); // increased beyond the end time of the STO - await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, 1000 * Math.pow(10, 18), { from: account_investor1 }); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[0].address, new BN(1000).mul(new BN(10).pow(new BN(18))), { from: account_investor1 }); await catchRevert( - I_CappedSTO_Array_POLY[0].buyTokensWithPoly(1000 * Math.pow(10, 18), { from: account_investor1, gas: 6000000 }) + I_CappedSTO_Array_POLY[0].buyTokensWithPoly(new BN(1000).mul(new BN(10).pow(new BN(18))), { from: account_investor1, gas: 6000000 }) ); }); it("Should fundRaised value equal to the raised value in the funds receiver wallet", async () => { const balanceRaised = await I_PolyToken.balanceOf.call(account_fundsReceiver); assert.equal( - (await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).toNumber(), - balanceRaised, + (await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).toString(), + balanceRaised.toString(), "Somewhere raised money get stolen or sent to wrong wallet" ); }); @@ -850,12 +826,13 @@ contract("CappedSTO", accounts => { describe("Test cases for the CappedSTOFactory", async () => { it("should get the exact details of the factory", async () => { - assert.equal((await I_CappedSTOFactory.getSetupCost.call()).toNumber(), cappedSTOSetupCost); + assert.equal((await I_CappedSTOFactory.getSetupCost.call()).toString(), cappedSTOSetupCost.toString()); + assert.equal((await I_CappedSTOFactory.getSetupCostInPoly.call()).toString(), cappedSTOSetupCostPOLY.toString()); assert.equal((await I_CappedSTOFactory.getTypes.call())[0], 3); assert.equal(web3.utils.hexToString(await I_CappedSTOFactory.getName.call()), "CappedSTO", "Wrong Module added"); assert.equal( await I_CappedSTOFactory.description.call(), - "Use to collects the funds and once the cap is reached then investment will be no longer entertained", + "This smart contract creates a maximum number of tokens (i.e. hard cap) which the total aggregate of tokens acquired by all investors cannot exceed. Security tokens are sent to the investor upon reception of the funds (ETH or POLY), and any security tokens left upon termination of the offering will not be minted.", "Wrong Module added" ); assert.equal(await I_CappedSTOFactory.title.call(), "Capped STO", "Wrong Module added"); @@ -866,7 +843,7 @@ contract("CappedSTO", accounts => { ); let tags = await I_CappedSTOFactory.getTags.call(); assert.equal(web3.utils.hexToString(tags[0]), "Capped"); - assert.equal(await I_CappedSTOFactory.version.call(), "1.0.0"); + assert.equal(await I_CappedSTOFactory.version.call(), "2.1.0"); }); it("Should fail to change the title -- bad owner", async () => { @@ -874,11 +851,11 @@ contract("CappedSTO", accounts => { }); it("Should fail to change the title -- zero length", async () => { - await catchRevert(I_CappedSTOFactory.changeTitle("", { from: token_owner })); + await catchRevert(I_CappedSTOFactory.changeTitle("", { from: account_polymath })); }); it("Should successfully change the title", async () => { - await I_CappedSTOFactory.changeTitle("STO Capped", { from: token_owner }); + await I_CappedSTOFactory.changeTitle("STO Capped", { from: account_polymath }); assert.equal(await I_CappedSTOFactory.title.call(), "STO Capped", "Title doesn't get changed"); }); @@ -887,11 +864,11 @@ contract("CappedSTO", accounts => { }); it("Should fail to change the description -- zero length", async () => { - await catchRevert(I_CappedSTOFactory.changeDescription("", { from: token_owner })); + await catchRevert(I_CappedSTOFactory.changeDescription("", { from: account_polymath })); }); it("Should successfully change the description", async () => { - await I_CappedSTOFactory.changeDescription("It is only a STO", { from: token_owner }); + await I_CappedSTOFactory.changeDescription("It is only a STO", { from: account_polymath }); assert.equal(await I_CappedSTOFactory.description.call(), "It is only a STO", "Description doesn't get changed"); }); @@ -900,16 +877,16 @@ contract("CappedSTO", accounts => { }); it("Should fail to change the name -- zero length", async () => { - await catchRevert(I_CappedSTOFactory.changeName(web3.utils.stringToHex(""), { from: token_owner })); + await catchRevert(I_CappedSTOFactory.changeName(web3.utils.stringToHex(""), { from: account_polymath })); }); it("Should successfully change the name", async () => { - await I_CappedSTOFactory.changeName(web3.utils.stringToHex("STOCapped"), { from: token_owner }); + await I_CappedSTOFactory.changeName(web3.utils.stringToHex("STOCapped"), { from: account_polymath }); assert.equal(web3.utils.hexToString(await I_CappedSTOFactory.getName.call()), "STOCapped", "Name doesn't get changed"); }); it("Should successfully change the name", async () => { - await I_CappedSTOFactory.changeName(web3.utils.stringToHex("CappedSTO"), { from: token_owner }); + await I_CappedSTOFactory.changeName(web3.utils.stringToHex("CappedSTO"), { from: account_polymath }); assert.equal(web3.utils.hexToString(await I_CappedSTOFactory.getName.call()), "CappedSTO", "Name doesn't get changed"); }); }); @@ -920,11 +897,11 @@ contract("CappedSTO", accounts => { }); it("Should get the raised amount of ether", async () => { - assert.equal(await I_CappedSTO_Array_POLY[0].getRaised.call(ETH), web3.utils.toWei("0", "ether")); + assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(ETH)).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Should get the raised amount of poly", async () => { - assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).toNumber(), web3.utils.toWei("10000", "ether")); + assert.equal((await I_CappedSTO_Array_POLY[0].getRaised.call(POLY)).toString(), new BN(web3.utils.toWei("10000", "ether")).toString()); }); it("Should get the investors", async () => { @@ -945,11 +922,11 @@ contract("CappedSTO", accounts => { describe("Attach second POLY STO module", async () => { it("Should successfully attach a second STO to the security token", async () => { - startTime_POLY2 = latestTime() + duration.days(1); + startTime_POLY2 = await latestTime() + duration.days(1); endTime_POLY2 = startTime_POLY2 + duration.days(30); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); - await I_PolyToken.transfer(I_SecurityToken_POLY.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken_POLY.address, cappedSTOSetupCostPOLY, { from: token_owner }); let bytesSTO = encodeModuleCall(STOParameters, [ startTime_POLY2, @@ -960,44 +937,44 @@ contract("CappedSTO", accounts => { account_fundsReceiver ]); - const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - I_CappedSTO_Array_POLY.push(CappedSTO.at(tx.logs[3].args._module)); + I_CappedSTO_Array_POLY.push(await CappedSTO.at(tx.logs[3].args._module)); }); it("Should verify the configuration of the STO", async () => { assert.equal( - (await I_CappedSTO_Array_POLY[1].startTime.call()).toNumber(), - startTime_POLY2, - "STO Configuration doesn't set as expected" + (await I_CappedSTO_Array_POLY[1].startTime.call()).toString(), + startTime_POLY2.toString(), + "1STO Configuration doesn't set as expected" ); assert.equal( - (await I_CappedSTO_Array_POLY[1].endTime.call()).toNumber(), - endTime_POLY2, - "STO Configuration doesn't set as expected" + (await I_CappedSTO_Array_POLY[1].endTime.call()).toString(), + endTime_POLY2.toString(), + "2STO Configuration doesn't set as expected" ); assert.equal( - (await I_CappedSTO_Array_POLY[1].cap.call()).dividedBy(new BigNumber(10).pow(18)).toNumber(), - BigNumber(P_cap).dividedBy(new BigNumber(10).pow(18)), - "STO Configuration doesn't set as expected" + (await I_CappedSTO_Array_POLY[1].cap.call()).div(new BN(10).pow(new BN(18))).toString(), + new BN(P_cap).div(new BN(10).pow(new BN(18))).toString(), + "3STO Configuration doesn't set as expected" ); - assert.equal(await I_CappedSTO_Array_POLY[1].rate.call(), P_rate, "STO Configuration doesn't set as expected"); + assert.equal((await I_CappedSTO_Array_POLY[1].rate.call()).toString(), new BN(P_rate).toString(), "STO Configuration doesn't set as expected"); assert.equal( await I_CappedSTO_Array_POLY[1].fundRaiseTypes.call(P_fundRaiseType), true, - "STO Configuration doesn't set as expected" + "4STO Configuration doesn't set as expected" ); }); it("Should successfully invest in second STO", async () => { - const polyToInvest = 1000; - const stToReceive = polyToInvest * P_rate; + const polyToInvest = new BN(1000); + const stToReceive = new BN(polyToInvest.mul(new BN(P_rate).div(new BN(10).pow(new BN(18))))); - await I_PolyToken.getTokens(polyToInvest * Math.pow(10, 18), account_investor3); + await I_PolyToken.getTokens(polyToInvest.mul(new BN(10).pow(new BN(18))), account_investor3); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor3, P_fromTime, P_toTime, P_expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor3, P_fromTime, P_toTime, P_expiryTime, { from: account_issuer, gas: 500000 }); @@ -1005,25 +982,25 @@ contract("CappedSTO", accounts => { // Jump time to beyond STO start await increaseTime(duration.days(2)); - await I_PolyToken.approve(I_CappedSTO_Array_POLY[1].address, polyToInvest * Math.pow(10, 18), { from: account_investor3 }); + await I_PolyToken.approve(I_CappedSTO_Array_POLY[1].address, polyToInvest.mul(new BN(10).pow(new BN(18))), { from: account_investor3 }); // buyTokensWithPoly transaction - await I_CappedSTO_Array_POLY[1].buyTokensWithPoly(polyToInvest * Math.pow(10, 18), { + await I_CappedSTO_Array_POLY[1].buyTokensWithPoly(polyToInvest.mul(new BN(10).pow(new BN(18))), { from: account_investor3, gas: 6000000 }); assert.equal( - (await I_CappedSTO_Array_POLY[1].getRaised.call(POLY)).dividedBy(new BigNumber(10).pow(18)).toNumber(), - polyToInvest + (await I_CappedSTO_Array_POLY[1].getRaised.call(POLY)).div(new BN(10).pow(new BN(18))).toString(), + polyToInvest.toString() ); assert.equal(await I_CappedSTO_Array_POLY[1].investorCount.call(), 1); assert.equal( - (await I_SecurityToken_POLY.balanceOf(account_investor3)).dividedBy(new BigNumber(10).pow(18)).toNumber(), - stToReceive + (await I_SecurityToken_POLY.balanceOf(account_investor3)).div(new BN(10).pow(new BN(18))).toString(), + stToReceive.toString() ); - }); + }); }); }); diff --git a/test/c_checkpoints.js b/test/c_checkpoints.js index 830acf600..96066453e 100644 --- a/test/c_checkpoints.js +++ b/test/c_checkpoints.js @@ -2,15 +2,17 @@ import latestTime from "./helpers/latestTime"; import { duration, ensureException, promisifyLogWatch, latestBlock } from "./helpers/utils"; import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; import { setUpPolymathNetwork } from "./helpers/createInstances"; +import { catchRevert } from "./helpers/exceptions"; const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("Checkpoints", accounts => { +contract("Checkpoints", async function(accounts) { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -19,14 +21,15 @@ contract("Checkpoints", accounts => { let account_investor2; let account_investor3; let account_investor4; - - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); + let account_controller; let message = "Transaction Should Fail!"; + // investor Details + let fromTime; + let toTime; + let expiryTime; + // Contract Instance Declaration let I_GeneralPermissionManagerFactory; let I_SecurityTokenRegistryProxy; @@ -44,6 +47,9 @@ contract("Checkpoints", accounts => { let I_SecurityToken; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -58,15 +64,18 @@ contract("Checkpoints", accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); before(async () => { + fromTime = await latestTime(); + toTime = await latestTime(); + expiryTime = toTime + duration.days(15); // Accounts setup account_polymath = accounts[0]; account_issuer = accounts[1]; token_owner = account_issuer; - + account_controller = accounts[3]; account_investor1 = accounts[6]; account_investor2 = accounts[7]; account_investor3 = accounts[8]; @@ -86,7 +95,9 @@ contract("Checkpoints", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // Printing all the contract addresses @@ -115,47 +126,50 @@ contract("Checkpoints", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); }); - it("Should set controller to token owner", async () => { - await I_SecurityToken.setController(token_owner, { from: token_owner }); + it("Should set controller to token owner --failed not allowed", async () => { + await catchRevert( + I_SecurityToken.setController(token_owner, { from: token_owner }) + ); }); + it("Should set the tcontroller", async() => { + await I_SecurityToken.setController(account_controller, {from: token_owner}); + }) + it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); }); describe("Buy tokens using on-chain whitelist", async () => { it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( + let ltime = new BN(await latestTime()); + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - false, + ltime, + ltime, + ltime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 } ); - assert.equal( tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), @@ -163,20 +177,19 @@ contract("Checkpoints", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("10", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("10", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("10", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("10", "ether")).toString()); }); it("Should Buy some more tokens", async () => { // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( + let ltime = new BN(await latestTime()); + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - false, + ltime, + ltime, + ltime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -190,18 +203,18 @@ contract("Checkpoints", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("10", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("10", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("10", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("10", "ether")).toString()); }); it("Add a new token holder", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + let ltime = new BN(await latestTime()); + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - false, + ltime, + ltime, + ltime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -216,9 +229,9 @@ contract("Checkpoints", accounts => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("10", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("10", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei("10", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("10", "ether")).toString()); }); it("Fuzz test balance checkpoints", async () => { @@ -226,10 +239,10 @@ contract("Checkpoints", accounts => { let cps = []; let ts = []; for (let j = 0; j < 10; j++) { - let balance1 = new BigNumber(await I_SecurityToken.balanceOf(account_investor1)); - let balance2 = new BigNumber(await I_SecurityToken.balanceOf(account_investor2)); - let balance3 = new BigNumber(await I_SecurityToken.balanceOf(account_investor3)); - let totalSupply = new BigNumber(await I_SecurityToken.totalSupply()); + let balance1 = new BN(await I_SecurityToken.balanceOf(account_investor1)); + let balance2 = new BN(await I_SecurityToken.balanceOf(account_investor2)); + let balance3 = new BN(await I_SecurityToken.balanceOf(account_investor3)); + let totalSupply = new BN(await I_SecurityToken.totalSupply()); cps.push([balance1, balance2, balance3]); ts.push(totalSupply); console.log( @@ -241,7 +254,7 @@ contract("Checkpoints", accounts => { JSON.stringify(totalSupply) ); await I_SecurityToken.createCheckpoint({ from: token_owner }); - let checkpointTimes = await I_SecurityToken.getCheckpointTimes(); + let checkpointTimes = await stGetter.getCheckpointTimes(); assert.equal(checkpointTimes.length, j + 1); console.log("Checkpoint Times: " + checkpointTimes); let txs = Math.floor(Math.random() * 3); @@ -264,21 +277,19 @@ contract("Checkpoints", accounts => { } else { receiver = account_investor3; } - let m = Math.random(); - let amount = new BigNumber(await I_SecurityToken.balanceOf(sender)) - .mul(Math.random().toFixed(10)) - .toFixed(0); - if (m > 0.8) { + let m = Math.floor(Math.random() * 10) + 1; + let amount; + if (m > 8) { console.log("Sending full balance"); - amount = new BigNumber(await I_SecurityToken.balanceOf(sender)); + amount = new BN(await I_SecurityToken.balanceOf(sender)); + } else { + amount = new BN(await I_SecurityToken.balanceOf(sender)).mul(new BN(m)).div(new BN(10)); } console.log("Sender: " + sender + " Receiver: " + receiver + " Amount: " + JSON.stringify(amount)); await I_SecurityToken.transfer(receiver, amount, { from: sender }); } if (Math.random() > 0.5) { - let n = new BigNumber(Math.random().toFixed(10)) - .mul(10 ** 17) - .toFixed(0); + let n = new BN(Math.random().toFixed(10)).mul(new BN(10).pow(new BN(17))); let p = Math.random() * 3; let r = Math.random() * 3; let minter; @@ -290,10 +301,10 @@ contract("Checkpoints", accounts => { minter = account_investor3; } console.log("Minting: " + n.toString() + " to: " + minter); - await I_SecurityToken.mint(minter, n, { from: token_owner }); + await I_SecurityToken.issue(minter, n, "0x0", { from: token_owner }); } if (Math.random() > 0.5) { - let n = new BigNumber(Math.random().toFixed(10)).mul(10 ** 17); + let n = new BN(Math.random().toFixed(10)).mul(new BN(10).pow(new BN(17))); let p = Math.random() * 3; let r = Math.random() * 3; let burner; @@ -304,20 +315,19 @@ contract("Checkpoints", accounts => { } else { burner = account_investor3; } - let burnerBalance = new BigNumber(await I_SecurityToken.balanceOf(burner)); - if (n.gt(burnerBalance.div(2))) { - n = burnerBalance.div(2); + let burnerBalance = new BN(await I_SecurityToken.balanceOf(burner)); + if (n.gt(burnerBalance.div(new BN(2)))) { + n = burnerBalance.div(new BN(2)); } - n = n.toFixed(0); console.log("Burning: " + n.toString() + " from: " + burner); - await I_SecurityToken.forceBurn(burner, n, "", "", { from: token_owner }); + await I_SecurityToken.controllerRedeem(burner, n, "0x0", "0x0", { from: account_controller }); } console.log("Checking Interim..."); for (let k = 0; k < cps.length; k++) { - let balance1 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor1, k + 1)); - let balance2 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor2, k + 1)); - let balance3 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor3, k + 1)); - let totalSupply = new BigNumber(await I_SecurityToken.totalSupplyAt(k + 1)); + let balance1 = new BN(await stGetter.balanceOfAt(account_investor1, k + 1)); + let balance2 = new BN(await stGetter.balanceOfAt(account_investor2, k + 1)); + let balance3 = new BN(await stGetter.balanceOfAt(account_investor3, k + 1)); + let totalSupply = new BN(await stGetter.totalSupplyAt(k + 1)); let balances = [balance1, balance2, balance3]; console.log("Checking TotalSupply: " + totalSupply + " is " + ts[k] + " at checkpoint: " + (k + 1)); assert.isTrue(totalSupply.eq(ts[k])); @@ -330,10 +340,10 @@ contract("Checkpoints", accounts => { } console.log("Checking..."); for (let k = 0; k < cps.length; k++) { - let balance1 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor1, k + 1)); - let balance2 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor2, k + 1)); - let balance3 = new BigNumber(await I_SecurityToken.balanceOfAt(account_investor3, k + 1)); - let totalSupply = new BigNumber(await I_SecurityToken.totalSupplyAt(k + 1)); + let balance1 = new BN(await stGetter.balanceOfAt(account_investor1, k + 1)); + let balance2 = new BN(await stGetter.balanceOfAt(account_investor2, k + 1)); + let balance3 = new BN(await stGetter.balanceOfAt(account_investor3, k + 1)); + let totalSupply = new BN(await stGetter.totalSupplyAt(k + 1)); let balances = [balance1, balance2, balance3]; console.log("Checking TotalSupply: " + totalSupply + " is " + ts[k] + " at checkpoint: " + (k + 1)); assert.isTrue(totalSupply.eq(ts[k])); diff --git a/test/d_count_transfer_manager.js b/test/d_count_transfer_manager.js index 1a1e7717a..7302fd99a 100644 --- a/test/d_count_transfer_manager.js +++ b/test/d_count_transfer_manager.js @@ -9,12 +9,13 @@ const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const CountTransferManagerFactory = artifacts.require("./CountTransferManagerFactory.sol"); const CountTransferManager = artifacts.require("./CountTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("CountTransferManager", accounts => { +contract("CountTransferManager", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -24,11 +25,6 @@ contract("CountTransferManager", accounts => { let account_investor3; let account_investor4; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - let message = "Transaction Should Fail!"; // Contract Instance Declaration @@ -39,7 +35,9 @@ contract("CountTransferManager", accounts => { let I_CountTransferManagerFactory; let I_GeneralPermissionManager; let I_CountTransferManager; + let I_CountTransferManager2; let I_GeneralTransferManager; + let I_GeneralTransferManager2; let I_ExchangeTransferManager; let I_ModuleRegistry; let I_ModuleRegistryProxy; @@ -49,12 +47,18 @@ contract("CountTransferManager", accounts => { let I_SecurityTokenRegistry; let I_STFactory; let I_SecurityToken; + let I_SecurityToken2; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; + let stGetter2; // SecurityToken Details const name = "Team"; const symbol = "sap"; + const symbol2 = "sapp"; const tokenDetails = "This is equity type of issuance"; const decimals = 18; const contact = "team@polymath.network"; @@ -65,13 +69,16 @@ contract("CountTransferManager", accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // CountTransferManager details const holderCount = 2; // Maximum number of token holders let bytesSTO = encodeModuleCall(["uint256"], [holderCount]); + let currentTime; + before(async () => { + currentTime = new BN(await latestTime()); // Accounts setup account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -81,6 +88,7 @@ contract("CountTransferManager", accounts => { account_investor1 = accounts[7]; account_investor2 = accounts[8]; account_investor3 = accounts[9]; + account_investor4 = accounts[6]; // Step 1: Deploy the genral PM ecosystem let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -96,13 +104,15 @@ contract("CountTransferManager", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 2: Deploy the CountTransferManager - [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 3: Deploy Paid the CountTransferManager - [P_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); + [P_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500", "ether"))); // Printing all the contract addresses console.log(` @@ -131,14 +141,14 @@ contract("CountTransferManager", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -146,14 +156,14 @@ contract("CountTransferManager", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the CountTransferManager factory with the security token", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); await catchRevert( - I_SecurityToken.addModule(P_CountTransferManagerFactory.address, bytesSTO, web3.utils.toWei("500", "ether"), 0, { + I_SecurityToken.addModule(P_CountTransferManagerFactory.address, bytesSTO, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }) ); @@ -161,12 +171,12 @@ contract("CountTransferManager", accounts => { it("Should successfully attach the CountTransferManager factory with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); const tx = await I_SecurityToken.addModule( P_CountTransferManagerFactory.address, bytesSTO, - web3.utils.toWei("500", "ether"), - 0, + new BN(web3.utils.toWei("2000", "ether")), + new BN(0), { from: token_owner } ); assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "CountTransferManagerFactory doesn't get deployed"); @@ -175,19 +185,19 @@ contract("CountTransferManager", accounts => { "CountTransferManager", "CountTransferManagerFactory module was not added" ); - P_CountTransferManager = CountTransferManager.at(tx.logs[3].args._module); + P_CountTransferManager = await CountTransferManager.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the CountTransferManager with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_CountTransferManagerFactory.address, bytesSTO, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_CountTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "CountTransferManager", "CountTransferManager module was not added" ); - I_CountTransferManager = CountTransferManager.at(tx.logs[2].args._module); + I_CountTransferManager = await CountTransferManager.at(tx.logs[2].args._module); }); }); @@ -195,12 +205,11 @@ contract("CountTransferManager", accounts => { it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 500000 @@ -217,20 +226,19 @@ contract("CountTransferManager", accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should Buy some more tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 500000 @@ -244,20 +252,19 @@ contract("CountTransferManager", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("2", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); - it("Should able to buy some more tokens (more than 2 hoders) -- because CountTransferManager is paused", async() => { - await I_CountTransferManager.pause({from: account_issuer }); + it("Should able to buy some more tokens (more than 2 hoders) -- because CountTransferManager is paused", async () => { + await I_CountTransferManager.pause({ from: account_issuer }); let snapId = await takeSnapshot(); - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 500000 @@ -270,19 +277,18 @@ contract("CountTransferManager", accounts => { "Failed in adding the investor in whitelist" ); - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("3", "ether"), { from: token_owner }) + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("3", "ether")), "0x0", { from: token_owner }); await revertToSnapshot(snapId); - }) + }); it("Should fail to buy some more tokens (more than 2 holders)", async () => { - await I_CountTransferManager.unpause({from: account_issuer }); + await I_CountTransferManager.unpause({ from: account_issuer }); // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 500000 @@ -295,23 +301,23 @@ contract("CountTransferManager", accounts => { "Failed in adding the investor in whitelist" ); - await catchRevert(I_SecurityToken.mint(account_investor3, web3.utils.toWei("3", "ether"), { from: token_owner })); + await catchRevert(I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("3", "ether")), "0x0", { from: token_owner })); }); it("Should still be able to add to original token holders", async () => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("2", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("4", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("4", "ether")).toString()); }); it("Should still be able to transfer between existing token holders before count change", async () => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("2", "ether"), { from: account_investor2 }); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("2", "ether")), { from: account_investor2 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Should fail in modifying the holder count", async () => { @@ -329,18 +335,18 @@ contract("CountTransferManager", accounts => { it("Should still be able to transfer between existing token holders after count change", async () => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2", "ether"), { from: account_investor1 }); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2", "ether")), { from: account_investor1 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("4", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("4", "ether")).toString()); }); it("Should not be able to transfer to a new token holder", async () => { // await I_CountTransferManager.unpause({from: token_owner}); - await catchRevert(I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2", "ether"), { from: account_investor2 })); + await catchRevert(I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("2", "ether")), { from: account_investor2 })); }); it("Should be able to consolidate balances", async () => { - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); }); it("Should get the permission list", async () => { @@ -348,6 +354,114 @@ contract("CountTransferManager", accounts => { assert.equal(perm.length, 1); }); + describe("Test cases for adding and removing acc holder at the same time", async () => { + it("deploy a new token & auto attach modules", async () => { + //register ticker and deploy token + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol2, contact, { from: token_owner }); + + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx2 = await I_STRProxied.generateSecurityToken(name, symbol2, tokenDetails, false, { from: token_owner }); + + I_SecurityToken2 = await SecurityToken.at(tx2.logs[2].args._securityTokenAddress); + stGetter2 = await STGetter.at(I_SecurityToken2.address); + let moduleData = (await stGetter2.getModulesByType(2))[0]; + I_GeneralTransferManager2 = await GeneralTransferManager.at(moduleData); + }); + + it("add 3 holders to the token", async () => { + await I_GeneralTransferManager2.modifyKYCData( + account_investor1, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 500000 + } + ); + + await I_GeneralTransferManager2.modifyKYCData( + account_investor2, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 500000 + } + ); + + await I_GeneralTransferManager2.modifyKYCData( + account_investor3, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 500000 + } + ); + + await I_GeneralTransferManager2.modifyKYCData( + account_investor4, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 500000 + } + ); + + // Jump time + await increaseTime(5000); + + // Add 3 holders to the token + await I_SecurityToken2.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + await I_SecurityToken2.issue(account_investor2, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + await I_SecurityToken2.issue(account_investor3, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + assert.equal((await I_SecurityToken2.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter2.getModulesByType(2))[0]; + I_GeneralTransferManager2 = await GeneralTransferManager.at(moduleData); + }); + + it("Should successfully attach the CountTransferManager factory with the security token", async () => { + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); + await catchRevert( + I_SecurityToken2.addModule(P_CountTransferManagerFactory.address, bytesSTO, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { + from: token_owner + }) + ); + }); + + it("Should successfully attach the CountTransferManager with the security token and set max holder to 2", async () => { + const tx = await I_SecurityToken2.addModule(I_CountTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "CountTransferManager", + "CountTransferManager module was not added" + ); + I_CountTransferManager2 = await CountTransferManager.at(tx.logs[2].args._module); + await I_CountTransferManager2.changeHolderCount(2, { from: token_owner }); + console.log("current max holder number is " + (await I_CountTransferManager2.maxHolderCount({ from: token_owner }))); + }); + + it("Should allow add a new token holder while transfer all the tokens at one go", async () => { + let amount = await I_SecurityToken2.balanceOf(account_investor2); + let investorCount = await stGetter2.holderCount({ from: account_investor2 }); + console.log("current investor count is " + investorCount); + await I_SecurityToken2.transfer(account_investor4, amount, { from: account_investor2 }); + assert((await I_SecurityToken2.balanceOf(account_investor4)).toString(), amount.toString(), { from: account_investor2 }); + assert(await stGetter2.holderCount({ from: account_investor2 }), investorCount); + }); + }); + describe("Test cases for the factory", async () => { it("should get the exact details of the factory", async () => { assert.equal(await I_CountTransferManagerFactory.getSetupCost.call(), 0); @@ -376,78 +490,57 @@ contract("CountTransferManager", accounts => { }); }); - describe("Test cases for the ModuleFactory", async() => { - it("Should successfully change the SetupCost -- fail beacuse of bad owner", async() => { + describe("Test cases for the ModuleFactory", async () => { + it("Should successfully change the SetupCost -- fail beacuse of bad owner", async () => { await catchRevert( - I_CountTransferManagerFactory.changeFactorySetupFee(web3.utils.toWei("500"), {from: account_investor3}) + I_CountTransferManagerFactory.changeSetupCost(new BN(web3.utils.toWei("500")), { from: account_investor3 }) ); }); - it("Should successfully change the setupCost", async() => { - await I_CountTransferManagerFactory.changeFactorySetupFee(web3.utils.toWei("800"), { from: account_polymath }); - assert.equal(await I_CountTransferManagerFactory.getSetupCost.call(), web3.utils.toWei("800")); - }) - - it("Should successfully change the usage fee -- fail beacuse of bad owner", async() => { - await catchRevert( - I_CountTransferManagerFactory.changeFactoryUsageFee(web3.utils.toWei("500"), {from: account_investor3}) - ); + it("Should successfully change the setupCost", async () => { + await I_CountTransferManagerFactory.changeSetupCost(new BN(web3.utils.toWei("800")), { from: account_polymath }); + assert.equal((await I_CountTransferManagerFactory.getSetupCost.call()).toString(), new BN(web3.utils.toWei("800")).toString()); }); - it("Should successfully change the usage fee", async() => { - await I_CountTransferManagerFactory.changeFactoryUsageFee(web3.utils.toWei("800"), { from: account_polymath }); - assert.equal(await I_CountTransferManagerFactory.usageCost.call(), web3.utils.toWei("800")); - }); - - it("Should successfully change the subscription fee -- fail beacuse of bad owner", async() => { + it("Should successfully change the usage fee -- fail beacuse of bad owner", async () => { await catchRevert( - I_CountTransferManagerFactory.changeFactorySubscriptionFee(web3.utils.toWei("500"), {from: account_investor3}) + I_CountTransferManagerFactory.changeUsageCost(new BN(web3.utils.toWei("500")), { from: account_investor3 }) ); }); - it("Should successfully change the subscription fee", async() => { - await I_CountTransferManagerFactory.changeFactorySubscriptionFee(web3.utils.toWei("800"), { from: account_polymath }); - assert.equal(await I_CountTransferManagerFactory.monthlySubscriptionCost.call(), web3.utils.toWei("800")); + it("Should successfully change the usage fee", async () => { + await I_CountTransferManagerFactory.changeUsageCost(new BN(web3.utils.toWei("800")), { from: account_polymath }); + assert.equal((await I_CountTransferManagerFactory.usageCost.call()).toString(), new BN(web3.utils.toWei("800")).toString()); }); - it("Should successfully change the version of the factory -- failed because of bad owner", async() => { - await catchRevert( - I_CountTransferManagerFactory.changeVersion("5.0.0", {from: account_investor3}) - ); + it("Should successfully change the version of the factory -- failed because of bad owner", async () => { + await catchRevert(I_CountTransferManagerFactory.changeVersion("5.0.0", { from: account_investor3 })); }); - it("Should successfully change the version of the fatory -- failed because of the 0 string", async() => { - await catchRevert( - I_CountTransferManagerFactory.changeVersion("", {from: account_polymath}) - ); + it("Should successfully change the version of the fatory -- failed because of the 0 string", async () => { + await catchRevert(I_CountTransferManagerFactory.changeVersion("", { from: account_polymath })); }); - it("Should successfully change the version of the fatory", async() => { - await I_CountTransferManagerFactory.changeVersion("5.0.0", {from: account_polymath}); + it("Should successfully change the version of the fatory", async () => { + await I_CountTransferManagerFactory.changeVersion("5.0.0", { from: account_polymath }); assert.equal(await I_CountTransferManagerFactory.version.call(), "5.0.0"); }); - }) + }); - describe("Test case for the changeSTVersionBounds", async() => { - it("Should successfully change the version bounds -- failed because of the non permitted bound type", async() => { - await catchRevert( - I_CountTransferManagerFactory.changeSTVersionBounds("middleType", [1,2,3], {from: account_polymath}) - ); - }) + describe("Test case for the changeSTVersionBounds", async () => { + it("Should successfully change the version bounds -- failed because of the non permitted bound type", async () => { + await catchRevert(I_CountTransferManagerFactory.changeSTVersionBounds("middleType", [1, 2, 3], { from: account_polymath })); + }); - it("Should successfully change the version bound --failed because the new version length < 3", async()=> { - await catchRevert( - I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1,2], {from: account_polymath}) - ); - }) + it("Should successfully change the version bound --failed because the new version length < 3", async () => { + await catchRevert(I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1, 2], { from: account_polymath })); + }); - it("Should successfully change the version bound", async()=> { - await I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1,2,1], {from: account_polymath}); - await I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1,4,9], {from: account_polymath}); - await catchRevert( - I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1,0,0], {from: account_polymath}) - ); - }) - }) + it("Should successfully change the version bound", async () => { + await I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1, 2, 1], { from: account_polymath }); + await I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1, 4, 9], { from: account_polymath }); + await catchRevert(I_CountTransferManagerFactory.changeSTVersionBounds("lowerBound", [1, new BN(0), 0], { from: account_polymath })); + }); + }); }); }); diff --git a/test/e_erc20_dividends.js b/test/e_erc20_dividends.js index 26adb85b0..656060a0a 100644 --- a/test/e_erc20_dividends.js +++ b/test/e_erc20_dividends.js @@ -3,21 +3,24 @@ import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; import { catchRevert } from "./helpers/exceptions"; import { setUpPolymathNetwork, deployERC20DividendAndVerifyed, deployGPMAndVerifyed } from "./helpers/createInstances"; +import { encodeModuleCall } from "./helpers/encodeCall"; const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const ERC20DividendCheckpoint = artifacts.require("./ERC20DividendCheckpoint"); const GeneralPermissionManager = artifacts.require("GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("ERC20DividendCheckpoint", accounts => { +contract("ERC20DividendCheckpoint", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; let token_owner; + let wallet; let account_investor1; let account_investor2; let account_investor3; @@ -25,11 +28,6 @@ contract("ERC20DividendCheckpoint", accounts => { let account_manager; let account_temp; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - let message = "Transaction Should Fail!"; let dividendName = "0x546573744469766964656e640000000000000000000000000000000000000000"; @@ -54,6 +52,9 @@ contract("ERC20DividendCheckpoint", accounts => { let I_SecurityToken; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -69,15 +70,20 @@ contract("ERC20DividendCheckpoint", accounts => { const checkpointKey = 4; //Manager details - const managerDetails = "Hello, I am a legit manager"; + const managerDetails = web3.utils.fromAscii("Hello"); // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); + + const one_address = "0x0000000000000000000000000000000000000001"; + const address_zero = "0x0000000000000000000000000000000000000000"; - const one_address = '0x0000000000000000000000000000000000000001'; + let currentTime; + + const DividendParameters = ["address"]; before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -89,7 +95,8 @@ contract("ERC20DividendCheckpoint", accounts => { account_investor4 = accounts[9]; account_temp = accounts[2]; account_manager = accounts[5]; - + wallet = accounts[3]; + // Step 1: Deploy the genral PM ecosystem let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -104,11 +111,17 @@ contract("ERC20DividendCheckpoint", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - [P_ERC20DividendCheckpointFactory] = await deployERC20DividendAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); - [I_ERC20DividendCheckpointFactory] = await deployERC20DividendAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [P_ERC20DividendCheckpointFactory] = await deployERC20DividendAndVerifyed( + account_polymath, + I_MRProxied, + new BN(web3.utils.toWei("500", "ether")) + ); + [I_ERC20DividendCheckpointFactory] = await deployERC20DividendAndVerifyed(account_polymath, I_MRProxied, 0); // Printing all the contract addresses console.log(` @@ -137,28 +150,29 @@ contract("ERC20DividendCheckpoint", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the ERC20DividendCheckpoint with the security token - fail insufficient payment", async () => { + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); await catchRevert( - I_SecurityToken.addModule(P_ERC20DividendCheckpointFactory.address, "", web3.utils.toWei("500", "ether"), 0, { + I_SecurityToken.addModule(P_ERC20DividendCheckpointFactory.address, bytesDividend, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }) ); @@ -166,9 +180,10 @@ contract("ERC20DividendCheckpoint", accounts => { it("Should successfully attach the ERC20DividendCheckpoint with the security token with budget", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); - const tx = await I_SecurityToken.addModule(P_ERC20DividendCheckpointFactory.address, "", web3.utils.toWei("500", "ether"), 0, { + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); + const tx = await I_SecurityToken.addModule(P_ERC20DividendCheckpointFactory.address, bytesDividend, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0].toNumber(), checkpointKey, "ERC20DividendCheckpoint doesn't get deployed"); @@ -177,19 +192,20 @@ contract("ERC20DividendCheckpoint", accounts => { "ERC20DividendCheckpoint", "ERC20DividendCheckpoint module was not added" ); - P_ERC20DividendCheckpoint = ERC20DividendCheckpoint.at(tx.logs[3].args._module); + P_ERC20DividendCheckpoint = await ERC20DividendCheckpoint.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the ERC20DividendCheckpoint with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_ERC20DividendCheckpointFactory.address, "", 0, 0, { from: token_owner }); + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); + const tx = await I_SecurityToken.addModule(I_ERC20DividendCheckpointFactory.address, bytesDividend, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), checkpointKey, "ERC20DividendCheckpoint doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "ERC20DividendCheckpoint", "ERC20DividendCheckpoint module was not added" ); - I_ERC20DividendCheckpoint = ERC20DividendCheckpoint.at(tx.logs[2].args._module); + I_ERC20DividendCheckpoint = await ERC20DividendCheckpoint.at(tx.logs[2].args._module); }); }); @@ -197,12 +213,11 @@ contract("ERC20DividendCheckpoint", accounts => { it("Buy some tokens for account_investor1 (1 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 500000 @@ -219,20 +234,19 @@ contract("ERC20DividendCheckpoint", accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Buy some tokens for account_investor2 (2 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 500000 @@ -246,21 +260,21 @@ contract("ERC20DividendCheckpoint", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("2", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Should fail in creating the dividend - incorrect allowance", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("1.5", "ether"), token_owner); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1.5", "ether")), token_owner); await catchRevert( I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner } ) @@ -268,15 +282,15 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Should fail in creating the dividend - maturity > expiry", async () => { - let maturity = latestTime(); - let expiry = latestTime() - duration.days(10); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("1.5", "ether"), { from: token_owner }); + let maturity = await latestTime(); + let expiry = await latestTime() - duration.days(10); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("1.5", "ether")), { from: token_owner }); await catchRevert( I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner } ) @@ -284,14 +298,14 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Should fail in creating the dividend - now > expiry", async () => { - let maturity = latestTime() - duration.days(2); - let expiry = latestTime() - duration.days(1); + let maturity = await latestTime() - duration.days(2); + let expiry = await latestTime() - duration.days(1); await catchRevert( I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner } ) @@ -299,80 +313,85 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Should fail in creating the dividend - bad token", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); await catchRevert( - I_ERC20DividendCheckpoint.createDividend(maturity, expiry, 0, web3.utils.toWei("1.5", "ether"), dividendName, { + I_ERC20DividendCheckpoint.createDividend(maturity, expiry, address_zero, new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner }) ); }); it("Should fail in creating the dividend - amount is 0", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); await catchRevert( - I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, 0, dividendName, { from: token_owner }) + I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, new BN(0), dividendName, { from: token_owner }) ); }); it("Create new dividend of POLY tokens", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let tx = await I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 1, "Dividend should be created at checkpoint 1"); assert.equal(tx.logs[0].args._name.toString(), dividendName, "Dividend name incorrect in event"); + let data = await I_ERC20DividendCheckpoint.getDividendsData(); + assert.equal(data[1][0].toNumber(), maturity, "maturity match"); + assert.equal(data[2][0].toNumber(), expiry, "expiry match"); + assert.equal(data[3][0].toString(), new BN(web3.utils.toWei("1.5", "ether")).toString(), "amount match"); + assert.equal(data[4][0].toNumber(), 0, "claimed match"); + assert.equal(data[5][0], dividendName, "dividendName match"); }); it("Investor 1 transfers his token balance to investor 2", async () => { - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); assert.equal(await I_SecurityToken.balanceOf(account_investor1), 0); - assert.equal(await I_SecurityToken.balanceOf(account_investor2), web3.utils.toWei("3", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("3", "ether")).toString()); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint - fails maturity in the future", async () => { - await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(0, 0, 10, { from: token_owner })); + await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: token_owner })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint - fails not owner", async () => { // Increase time by 2 day await increaseTime(duration.days(2)); - await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(0, 0, 10, { from: account_temp })); + await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: account_temp })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint - fails wrong index", async () => { - await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(2, 0, 10, { from: token_owner })); + await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint", async () => { - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - await I_ERC20DividendCheckpoint.pushDividendPayment(0, 0, 10, { from: token_owner, gas: 5000000 }); - let investor1BalanceAfter = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - assert.equal(investor1BalanceAfter.sub(investor1Balance).toNumber(), web3.utils.toWei("0.5", "ether")); - assert.equal(investor2BalanceAfter.sub(investor2Balance).toNumber(), web3.utils.toWei("1", "ether")); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + await I_ERC20DividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: token_owner, gas: 5000000 }); + let investor1BalanceAfter = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter = new BN(await I_PolyToken.balanceOf(account_investor2)); + assert.equal(investor1BalanceAfter.sub(investor1Balance).toString(), new BN(web3.utils.toWei("0.5", "ether")).toString()); + assert.equal(investor2BalanceAfter.sub(investor2Balance).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); //Check fully claimed - assert.equal((await I_ERC20DividendCheckpoint.dividends(0))[5].toNumber(), web3.utils.toWei("1.5", "ether")); + assert.equal((await I_ERC20DividendCheckpoint.dividends(0))[5].toString(), new BN(web3.utils.toWei("1.5", "ether")).toString()); }); it("Buy some tokens for account_temp (1 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_temp, - latestTime(), - latestTime(), - latestTime() + duration.days(20), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(20))), { from: account_issuer, gas: 500000 @@ -382,59 +401,59 @@ contract("ERC20DividendCheckpoint", accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_temp.toLowerCase(), "Failed in adding the investor in whitelist"); // Mint some tokens - await I_SecurityToken.mint(account_temp, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_temp, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_temp)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_temp)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should not allow to create dividend without name", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("1.5", "ether"), token_owner); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("1.5", "ether"), { from: token_owner }); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1.5", "ether")), token_owner); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("1.5", "ether")), { from: token_owner }); await catchRevert( - I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei("1.5", "ether"), "", { + I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, new BN(web3.utils.toWei("1.5", "ether")), "0x0", { from: token_owner }) ); }); it("Create new dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("1.5", "ether"), token_owner); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1.5", "ether")), token_owner); // transfer approved in above test let tx = await I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: token_owner } ); + console.log("Gas used w/ no exclusions: " + tx.receipt.gasUsed); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 2, "Dividend should be created at checkpoint 1"); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint - fails past expiry", async () => { await increaseTime(duration.days(12)); - await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(1, 0, 10, { from: token_owner })); + await catchRevert(I_ERC20DividendCheckpoint.pushDividendPayment(1, new BN(0), 10, { from: token_owner })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoin - fails already reclaimed", async () => { let tx = await I_ERC20DividendCheckpoint.reclaimDividend(1, { from: token_owner, gas: 500000 }); - assert.equal(tx.logs[0].args._claimedAmount.toNumber(), web3.utils.toWei("1.5", "ether")); + assert.equal(tx.logs[0].args._claimedAmount.toString(), new BN(web3.utils.toWei("1.5", "ether")).toString()); await catchRevert(I_ERC20DividendCheckpoint.reclaimDividend(1, { from: token_owner, gas: 500000 })); }); it("Buy some tokens for account_investor3 (7 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(100000))), { from: account_issuer, gas: 500000 @@ -448,17 +467,19 @@ contract("ERC20DividendCheckpoint", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("7", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("7", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei("7", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Should allow to exclude same number of address as EXCLUDED_ADDRESS_LIMIT", async () => { let limit = await I_ERC20DividendCheckpoint.EXCLUDED_ADDRESS_LIMIT(); - limit = limit.toNumber(); + limit = limit.toNumber() + 42; let addresses = []; addresses.push(account_temp); - while (--limit) addresses.push(limit); + let tempAdd = '0x0000000000000000000000000000000000000000'; + while (--limit > 42) addresses.push(web3.utils.toChecksumAddress(tempAdd.substring(0, 42 - limit.toString().length) + limit)); + //'0x00000000000000000000000000000000000000' + limit)); await I_ERC20DividendCheckpoint.setDefaultExcluded(addresses, { from: token_owner }); let excluded = await I_ERC20DividendCheckpoint.getDefaultExcluded(); assert.equal(excluded[0], account_temp); @@ -474,7 +495,7 @@ contract("ERC20DividendCheckpoint", accounts => { it("Should not allow to exclude 0x0 address", async () => { let addresses = []; addresses.push(account_investor3); - addresses.push(0); + addresses.push(address_zero); await catchRevert(I_ERC20DividendCheckpoint.setDefaultExcluded(addresses, { from: token_owner })); }); @@ -486,27 +507,33 @@ contract("ERC20DividendCheckpoint", accounts => { it("Should not allow to exclude more address than EXCLUDED_ADDRESS_LIMIT", async () => { let limit = await I_ERC20DividendCheckpoint.EXCLUDED_ADDRESS_LIMIT(); - limit = limit.toNumber(); + limit = limit.toNumber() + 42; let addresses = []; addresses.push(account_temp); - while (limit--) addresses.push(limit); + let tempAdd = '0x0000000000000000000000000000000000000000'; + while (limit-- > 42) addresses.push(web3.utils.toChecksumAddress(tempAdd.substring(0, 42 - limit.toString().length) + limit)); + + // while (limit-- > 42) addresses.push(web3.utils.toChecksumAddress('0x00000000000000000000000000000000000000' + limit)); + console.log(addresses.length); await catchRevert(I_ERC20DividendCheckpoint.setDefaultExcluded(addresses, { from: token_owner })); }); it("Create another new dividend", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("11", "ether"), token_owner); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("11", "ether"), { from: token_owner }); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("11", "ether")), token_owner); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("11", "ether")), { from: token_owner }); let tx = await I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("10", "ether"), + new BN(web3.utils.toWei("10", "ether")), dividendName, { from: token_owner } ); + console.log("Gas used w/ max exclusions - default: " + tx.receipt.gasUsed); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 3, "Dividend should be created at checkpoint 2"); + assert.equal((await I_ERC20DividendCheckpoint.isExcluded.call(account_temp, tx.logs[0].args._dividendIndex)), true, "account_temp is excluded"); }); it("should investor 3 claims dividend - fail bad index", async () => { @@ -515,16 +542,26 @@ contract("ERC20DividendCheckpoint", accounts => { it("should investor 3 claims dividend", async () => { console.log((await I_ERC20DividendCheckpoint.dividends(2))[5].toNumber()); - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); await I_ERC20DividendCheckpoint.pullDividendPayment(2, { from: account_investor3, gasPrice: 0 }); - let investor1BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); + let investor1BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); assert.equal(investor2BalanceAfter1.sub(investor2Balance).toNumber(), 0); - assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), web3.utils.toWei("7", "ether")); + assert.equal(investor3BalanceAfter1.sub(investor3Balance).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + let info = await I_ERC20DividendCheckpoint.getDividendProgress.call(2); + console.log(info); + assert.equal(info[0][1], account_temp, "account_temp"); + assert.equal(info[1][1], false, "account_temp is not claimed"); + assert.equal(info[2][1], true, "account_temp is excluded"); + assert.equal(info[3][1], 0, "account_temp is not withheld"); + assert.equal(info[0][2], account_investor3, "account_investor3"); + assert.equal(info[1][2], true, "account_investor3 is claimed"); + assert.equal(info[2][2], false, "account_investor3 is claimed"); + assert.equal(info[3][2], 0, "account_investor3 is not withheld"); }); it("should investor 3 claims dividend - fails already claimed", async () => { @@ -532,22 +569,21 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("should issuer pushes remain", async () => { - console.log((await I_ERC20DividendCheckpoint.dividends(2))[5].toNumber()); - let investor1BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let investorTempBalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_temp)); - await I_ERC20DividendCheckpoint.pushDividendPayment(2, 0, 10, { from: token_owner }); - let investor1BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let investorTempBalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_temp)); + let investor1BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); + let investorTempBalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_temp)); + await I_ERC20DividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner }); + let investor1BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor3)); + let investorTempBalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_temp)); assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toNumber(), 0); - assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toNumber(), web3.utils.toWei("3", "ether")); + assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toString(), new BN(web3.utils.toWei("3", "ether")).toString()); assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toNumber(), 0); assert.equal(investorTempBalanceAfter2.sub(investorTempBalanceAfter1).toNumber(), 0); //Check fully claimed - assert.equal((await I_ERC20DividendCheckpoint.dividends(2))[5].toNumber(), web3.utils.toWei("10", "ether")); + assert.equal((await I_ERC20DividendCheckpoint.dividends(2))[5].toString(), new BN(web3.utils.toWei("10", "ether")).toString()); }); it("Delete global exclusion list", async () => { @@ -555,26 +591,26 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Investor 2 transfers 1 ETH of his token balance to investor 1", async () => { - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_investor2 }); - assert.equal(await I_SecurityToken.balanceOf(account_investor1), web3.utils.toWei("1", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor2), web3.utils.toWei("2", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor3), web3.utils.toWei("7", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_temp), web3.utils.toWei("1", "ether")); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_investor2 }); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_temp)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Create another new dividend with explicit checkpoint - fails bad allowance", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(2); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(2); let tx = await I_SecurityToken.createCheckpoint({ from: token_owner }); console.log(JSON.stringify(tx.logs[0].args)); console.log((await I_SecurityToken.currentCheckpointId()).toNumber()); - await I_PolyToken.getTokens(web3.utils.toWei("20", "ether"), token_owner); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("20", "ether")), token_owner); await catchRevert( I_ERC20DividendCheckpoint.createDividendWithCheckpoint( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("20", "ether"), + new BN(web3.utils.toWei("20", "ether")), 4, dividendName, { from: token_owner } @@ -585,15 +621,15 @@ contract("ERC20DividendCheckpoint", accounts => { it("Create another new dividend with explicit - fails maturity > expiry", async () => { console.log((await I_SecurityToken.currentCheckpointId()).toNumber()); - let maturity = latestTime(); - let expiry = latestTime() - duration.days(10); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("20", "ether"), { from: token_owner }); + let maturity = await latestTime(); + let expiry = await latestTime() - duration.days(10); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("20", "ether")), { from: token_owner }); await catchRevert( I_ERC20DividendCheckpoint.createDividendWithCheckpoint( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("20", "ether"), + new BN(web3.utils.toWei("20", "ether")), 4, dividendName, { from: token_owner } @@ -604,14 +640,14 @@ contract("ERC20DividendCheckpoint", accounts => { it("Create another new dividend with explicit - fails now > expiry", async () => { console.log((await I_SecurityToken.currentCheckpointId()).toNumber()); - let maturity = latestTime() - duration.days(5); - let expiry = latestTime() - duration.days(2); + let maturity = await latestTime() - duration.days(5); + let expiry = await latestTime() - duration.days(2); await catchRevert( I_ERC20DividendCheckpoint.createDividendWithCheckpoint( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("20", "ether"), + new BN(web3.utils.toWei("20", "ether")), 4, dividendName, { from: token_owner } @@ -620,14 +656,14 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Create another new dividend with explicit - fails bad checkpoint", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(2); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(2); await catchRevert( I_ERC20DividendCheckpoint.createDividendWithCheckpoint( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("20", "ether"), + new BN(web3.utils.toWei("20", "ether")), 5, dividendName, { from: token_owner } @@ -636,46 +672,43 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Set withholding tax of 20% on account_temp and 10% on investor2", async () => { - await I_ERC20DividendCheckpoint.setWithholding( - [account_temp, account_investor2], - [BigNumber(20 * 10 ** 16), BigNumber(10 * 10 ** 16)], - { from: token_owner } - ); + await I_ERC20DividendCheckpoint.setWithholding([account_temp, account_investor2], [new BN(20).mul(new BN(10).pow(new BN(16))), new BN(10).mul(new BN(10).pow(new BN(16)))], { + from: token_owner + }); }); it("Should not allow mismatching input lengths", async () => { await catchRevert( - I_ERC20DividendCheckpoint.setWithholding([account_temp], [BigNumber(20 * 10 ** 16), BigNumber(10 * 10 ** 16)], { + I_ERC20DividendCheckpoint.setWithholding([account_temp], [new BN(20).mul(new BN(10).pow(new BN(16))), new BN(10).mul(new BN(10).pow(new BN(16)))], { from: token_owner }) ); }); it("Should not allow withholding greater than limit", async () => { - await catchRevert(I_ERC20DividendCheckpoint.setWithholding([account_temp], [BigNumber(20 * 10 ** 26)], { from: token_owner })); - await catchRevert( - I_ERC20DividendCheckpoint.setWithholdingFixed([account_temp], BigNumber(20 * 10 ** 26), { from: token_owner }), - "" - ); + await catchRevert(I_ERC20DividendCheckpoint.setWithholding([account_temp], [new BN(20).mul(new BN(10).pow(new BN(26)))], { from: token_owner })); + await catchRevert(I_ERC20DividendCheckpoint.setWithholdingFixed([account_temp], new BN(20).mul(new BN(10).pow(new BN(26))), { from: token_owner }), ""); }); it("Should not create dividend with more exclusions than limit", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("11", "ether"), token_owner); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("11", "ether"), { from: token_owner }); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("11", "ether")), token_owner); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("11", "ether")), { from: token_owner }); let limit = await I_ERC20DividendCheckpoint.EXCLUDED_ADDRESS_LIMIT(); - limit = limit.toNumber(); + limit = limit.toNumber() + 42; let addresses = []; addresses.push(account_temp); addresses.push(token_owner); - while (--limit) addresses.push(limit); + let tempAdd = '0x0000000000000000000000000000000000000000'; + while (--limit > 42) addresses.push(web3.utils.toChecksumAddress(tempAdd.substring(0, 42 - limit.toString().length) + limit)); + // while (--limit > 42) addresses.push(web3.utils.toChecksumAddress('0x00000000000000000000000000000000000000' + limit)); await catchRevert( I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("10", "ether"), + new BN(web3.utils.toWei("10", "ether")), 4, addresses, dividendName, @@ -685,55 +718,59 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Create another new dividend with explicit checkpoint and exclusion", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("11", "ether"), token_owner); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("11", "ether")), token_owner); //token transfer approved in above test let tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("10", "ether"), + new BN(web3.utils.toWei("10", "ether")), 4, [account_investor1], dividendName, { from: token_owner } ); - assert.equal(tx.logs[0].args._checkpointId.toNumber(), 4, "Dividend should be created at checkpoint 3"); + assert.equal(tx.logs[0].args._checkpointId.toNumber(), 4, "Dividend should be created at checkpoint 4"); }); it("Should not create new dividend with duplicate exclusion", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("11", "ether"), token_owner); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("11", "ether")), token_owner); //token transfer approved in above test - await catchRevert(I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("10", "ether"), - 4, - [account_investor1, account_investor1], - dividendName, - { from: token_owner } - )); + await catchRevert( + I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("10", "ether")), + 4, + [account_investor1, account_investor1], + dividendName, + { from: token_owner } + ) + ); }); it("Should not create new dividend with 0x0 address in exclusion", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); - await I_PolyToken.getTokens(web3.utils.toWei("11", "ether"), token_owner); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("11", "ether")), token_owner); //token transfer approved in above test - await catchRevert(I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("10", "ether"), - 4, - [0], - dividendName, - { from: token_owner } - )); + await catchRevert( + I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("10", "ether")), + 4, + [address_zero], + dividendName, + { from: token_owner } + ) + ); }); it("Should not allow excluded to pull Dividend Payment", async () => { @@ -767,48 +804,48 @@ contract("ERC20DividendCheckpoint", accounts => { let dividendAmount2 = await I_ERC20DividendCheckpoint.calculateDividend.call(3, account_investor2); let dividendAmount3 = await I_ERC20DividendCheckpoint.calculateDividend.call(3, account_investor3); let dividendAmount_temp = await I_ERC20DividendCheckpoint.calculateDividend.call(3, account_temp); - assert.equal(dividendAmount1[0].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount2[0].toNumber(), web3.utils.toWei("2", "ether")); - assert.equal(dividendAmount3[0].toNumber(), web3.utils.toWei("7", "ether")); - assert.equal(dividendAmount_temp[0].toNumber(), web3.utils.toWei("1", "ether")); - assert.equal(dividendAmount1[1].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount2[1].toNumber(), web3.utils.toWei("0.2", "ether")); - assert.equal(dividendAmount3[1].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount_temp[1].toNumber(), web3.utils.toWei("0.2", "ether")); + assert.equal(dividendAmount1[0].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount2[0].toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + assert.equal(dividendAmount3[0].toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + assert.equal(dividendAmount_temp[0].toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal(dividendAmount1[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString()); + assert.equal(dividendAmount3[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount_temp[1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString()); }); it("Investor 2 claims dividend", async () => { - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let tempBalance = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); + let tempBalance = new BN(await web3.eth.getBalance(account_temp)); await I_ERC20DividendCheckpoint.pullDividendPayment(3, { from: account_investor2, gasPrice: 0 }); - let investor1BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let tempBalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); + let tempBalanceAfter1 = new BN(await web3.eth.getBalance(account_temp)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); - assert.equal(investor2BalanceAfter1.sub(investor2Balance).toNumber(), web3.utils.toWei("1.8", "ether")); + assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), new BN(web3.utils.toWei("1.8", "ether")).toString()); assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), 0); assert.equal(tempBalanceAfter1.sub(tempBalance).toNumber(), 0); }); it("Should issuer pushes temp investor - investor1 excluded", async () => { - let investor1BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let tempBalanceAfter1 = new BigNumber(await I_PolyToken.balanceOf(account_temp)); + let investor1BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); + let tempBalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_temp)); await I_ERC20DividendCheckpoint.pushDividendPaymentToAddresses(3, [account_temp, account_investor1], { from: token_owner }); - let investor1BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3BalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); - let tempBalanceAfter2 = new BigNumber(await I_PolyToken.balanceOf(account_temp)); + let investor1BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor3)); + let tempBalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_temp)); assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toNumber(), 0); assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toNumber(), 0); assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toNumber(), 0); - assert.equal(tempBalanceAfter2.sub(tempBalanceAfter1).toNumber(), web3.utils.toWei("0.8", "ether")); + assert.equal(tempBalanceAfter2.sub(tempBalanceAfter1).toString(), new BN(web3.utils.toWei("0.8", "ether")).toString()); //Check fully claimed - assert.equal((await I_ERC20DividendCheckpoint.dividends(3))[5].toNumber(), web3.utils.toWei("3", "ether")); + assert.equal((await I_ERC20DividendCheckpoint.dividends(3))[5].toString(), new BN(web3.utils.toWei("3", "ether")).toString()); }); it("should calculate dividend after the push dividend payment", async () => { @@ -823,10 +860,80 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Issuer reclaims withholding tax", async () => { - let issuerBalance = new BigNumber(await I_PolyToken.balanceOf(token_owner)); - await I_ERC20DividendCheckpoint.withdrawWithholding(3, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await I_PolyToken.balanceOf(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0.4", "ether")); + let info = await I_ERC20DividendCheckpoint.getDividendProgress.call(3); + + console.log("Address:"); + console.log(info[0][0]); + console.log(info[0][1]); + console.log(info[0][2]); + console.log(info[0][3]); + + console.log("Claimed:"); + console.log(info[1][0]); + console.log(info[1][1]); + console.log(info[1][2]); + console.log(info[1][3]); + + console.log("Excluded:"); + console.log(info[2][0]); + console.log(info[2][1]); + console.log(info[2][2]); + console.log(info[2][3]); + + console.log("Withheld:"); + console.log(info[3][0].toString()); + console.log(info[3][1].toString()); + console.log(info[3][2].toString()); + console.log(info[3][3].toString()); + + console.log("Claimed:"); + console.log(info[4][0].toString()); + console.log(info[4][1].toString()); + console.log(info[4][2].toString()); + console.log(info[4][3].toString()); + + console.log("Balance:"); + console.log(info[5][0].toString()); + console.log(info[5][1].toString()); + console.log(info[5][2].toString()); + console.log(info[5][3].toString()); + + assert.equal(info[0][0], account_investor1, "account match"); + assert.equal(info[0][1], account_investor2, "account match"); + assert.equal(info[0][2], account_temp, "account match"); + assert.equal(info[0][3], account_investor3, "account match"); + + assert.equal(info[3][0].toString(), 0, "withheld match"); + assert.equal(info[3][1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString(), "withheld match"); + assert.equal(info[3][2].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString(), "withheld match"); + assert.equal(info[3][3].toString(), 0, "withheld match"); + + assert.equal(info[4][0].toString(), 0, "excluded"); + assert.equal(info[4][1].toString(), new BN(web3.utils.toWei("1.8", "ether")).toString(), "claim match"); + assert.equal(info[4][2].toString(), new BN(web3.utils.toWei("0.8", "ether")).toString(), "claim match"); + assert.equal(info[4][3].toString(), new BN(web3.utils.toWei("7", "ether")).toString(), "claim match"); + + assert.equal(info[5][0].toString(), (await stGetter.balanceOfAt(account_investor1, new BN(4))).toString(), "balance match"); + assert.equal(info[5][1].toString(), (await stGetter.balanceOfAt(account_investor2, new BN(4))).toString(), "balance match"); + assert.equal(info[5][2].toString(), (await stGetter.balanceOfAt(account_temp, new BN(4))).toString(), "balance match"); + assert.equal(info[5][3].toString(), (await stGetter.balanceOfAt(account_investor3, new BN(4))).toString(), "balance match"); + + + let issuerBalance = new BN(await I_PolyToken.balanceOf(wallet)); + await I_ERC20DividendCheckpoint.withdrawWithholding(new BN(3), { from: token_owner, gasPrice: 0 }); + let issuerBalanceAfter = new BN(await I_PolyToken.balanceOf(wallet)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0.4", "ether")).toString()); + + }); + + it("Issuer changes wallet address", async () => { + await catchRevert(I_ERC20DividendCheckpoint.changeWallet(token_owner, { from: wallet })); + await I_ERC20DividendCheckpoint.changeWallet(token_owner, {from: token_owner}); + let newWallet = await I_ERC20DividendCheckpoint.wallet.call(); + assert.equal(newWallet, token_owner, "Wallets match"); + await I_ERC20DividendCheckpoint.changeWallet(wallet, {from: token_owner}); + newWallet = await I_ERC20DividendCheckpoint.wallet.call(); + assert.equal(newWallet, wallet, "Wallets match"); }); it("Issuer unable to reclaim dividend (expiry not passed)", async () => { @@ -843,10 +950,10 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("Issuer is able to reclaim dividend after expiry", async () => { - let tokenOwnerBalance = new BigNumber(await I_PolyToken.balanceOf(token_owner)); + let tokenOwnerBalance = new BN(await I_PolyToken.balanceOf(wallet)); await I_ERC20DividendCheckpoint.reclaimDividend(3, { from: token_owner, gasPrice: 0 }); - let tokenOwnerAfter = new BigNumber(await I_PolyToken.balanceOf(token_owner)); - assert.equal(tokenOwnerAfter.sub(tokenOwnerBalance).toNumber(), web3.utils.toWei("7", "ether")); + let tokenOwnerAfter = new BN(await I_PolyToken.balanceOf(wallet)); + assert.equal(tokenOwnerAfter.sub(tokenOwnerBalance).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Issuer is unable to reclaim already reclaimed dividend", async () => { @@ -867,19 +974,14 @@ contract("ERC20DividendCheckpoint", accounts => { assert.equal(index.length, 0); }); - it("Get the init data", async () => { - let tx = await I_ERC20DividendCheckpoint.getInitFunction.call(); - assert.equal(web3.utils.toAscii(tx).replace(/\u0000/g, ""), 0); - }); - it("Should get the listed permissions", async () => { let tx = await I_ERC20DividendCheckpoint.getPermissions.call(); assert.equal(tx.length, 2); }); it("should registr a delegate", async () => { - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", 0, 0, { from: token_owner }); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), @@ -887,78 +989,73 @@ contract("ERC20DividendCheckpoint", accounts => { "GeneralPermissionManagerFactory module was not added" ); I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); - tx = await I_GeneralPermissionManager.addDelegate(account_manager, managerDetails, { from: token_owner}); + tx = await I_GeneralPermissionManager.addDelegate(account_manager, managerDetails, { from: token_owner }); assert.equal(tx.logs[0].args._delegate, account_manager); }); it("should not allow manager without permission to set default excluded", async () => { - await catchRevert(I_ERC20DividendCheckpoint.setDefaultExcluded( - [0], - { from: account_manager } - )); + await catchRevert(I_ERC20DividendCheckpoint.setDefaultExcluded([address_zero], { from: account_manager })); }); it("should not allow manager without permission to set withholding", async () => { - await catchRevert(I_ERC20DividendCheckpoint.setWithholding( - [0], - [0], - { from: account_manager } - )); + await catchRevert(I_ERC20DividendCheckpoint.setWithholding([address_zero], [new BN(0)], { from: account_manager })); }); it("should not allow manager without permission to set withholding fixed", async () => { - await catchRevert(I_ERC20DividendCheckpoint.setWithholdingFixed( - [0], - 0, - { from: account_manager } - )); + await catchRevert(I_ERC20DividendCheckpoint.setWithholdingFixed([address_zero], new BN(0), { from: account_manager })); }); it("should not allow manager without permission to create dividend", async () => { - await I_PolyToken.transfer(account_manager, web3.utils.toWei("100", "ether"), { from: token_owner }); - await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei("100", "ether"), { from: account_manager }); - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + await I_PolyToken.transfer(account_manager, new BN(web3.utils.toWei("100", "ether")), { from: token_owner }); + await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, new BN(web3.utils.toWei("100", "ether")), { from: account_manager }); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); - await catchRevert(I_ERC20DividendCheckpoint.createDividend( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), - dividendName, - { from: account_manager } - )); + await catchRevert( + I_ERC20DividendCheckpoint.createDividend( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("1.5", "ether")), + dividendName, + { from: account_manager } + ) + ); }); it("should not allow manager without permission to create dividend with checkpoint", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); - await catchRevert(I_ERC20DividendCheckpoint.createDividendWithCheckpoint( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), - checkpointID.toNumber(), - dividendName, - { from: account_manager } - )); + await catchRevert( + I_ERC20DividendCheckpoint.createDividendWithCheckpoint( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("1.5", "ether")), + checkpointID.toNumber(), + dividendName, + { from: account_manager } + ) + ); }); it("should not allow manager without permission to create dividend with exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; - await catchRevert(I_ERC20DividendCheckpoint.createDividendWithExclusions( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), - exclusions, - dividendName, - { from: account_manager } - )); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; + await catchRevert( + I_ERC20DividendCheckpoint.createDividendWithExclusions( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("1.5", "ether")), + exclusions, + dividendName, + { from: account_manager } + ) + ); }); it("should not allow manager without permission to create checkpoint", async () => { @@ -966,76 +1063,59 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("should not allow manager without permission to create dividend with checkpoint and exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); - await I_SecurityToken.createCheckpoint({ from: token_owner }); - await catchRevert(I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), - checkpointID.toNumber(), - exclusions, - dividendName, - { from: account_manager } - )); + await I_SecurityToken.createCheckpoint({ from: token_owner }); + await catchRevert( + I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( + maturity, + expiry, + I_PolyToken.address, + new BN(web3.utils.toWei("1.5", "ether")), + checkpointID.toNumber(), + exclusions, + dividendName, + { from: account_manager } + ) + ); }); it("should give permission to manager", async () => { - await I_GeneralPermissionManager.changePermission( - account_manager, - I_ERC20DividendCheckpoint.address, - "CHECKPOINT", - true, - { from: token_owner } - ); - let tx = await I_GeneralPermissionManager.changePermission( - account_manager, - I_ERC20DividendCheckpoint.address, - "MANAGE", - true, - { from: token_owner } - ); + await I_GeneralPermissionManager.changePermission(account_manager, I_ERC20DividendCheckpoint.address, web3.utils.fromAscii("CHECKPOINT"), true, { + from: token_owner + }); + let tx = await I_GeneralPermissionManager.changePermission(account_manager, I_ERC20DividendCheckpoint.address, web3.utils.fromAscii("MANAGE"), true, { + from: token_owner + }); assert.equal(tx.logs[0].args._delegate, account_manager); }); it("should allow manager with permission to set default excluded", async () => { - let tx = await I_ERC20DividendCheckpoint.setDefaultExcluded( - [1], - { from: account_manager } - ); + let tx = await I_ERC20DividendCheckpoint.setDefaultExcluded([one_address], { from: account_manager }); assert.equal(tx.logs[0].args._excluded[0], one_address); }); it("should allow manager with permission to set withholding", async () => { - let tx = await I_ERC20DividendCheckpoint.setWithholding( - [0], - [0], - { from: account_manager } - ); + let tx = await I_ERC20DividendCheckpoint.setWithholding([one_address], [new BN(0)], { from: account_manager }); assert.equal(tx.logs[0].args._withholding[0], 0); }); it("should allow manager withpermission to set withholding fixed", async () => { - let tx = await I_ERC20DividendCheckpoint.setWithholdingFixed( - [0], - 0, - { from: account_manager } - ); + let tx = await I_ERC20DividendCheckpoint.setWithholdingFixed([one_address], new BN(0), { from: account_manager }); assert.equal(tx.logs[0].args._withholding, 0); }); it("should allow manager with permission to create dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let tx = await I_ERC20DividendCheckpoint.createDividend( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), dividendName, { from: account_manager } ); @@ -1043,49 +1123,67 @@ contract("ERC20DividendCheckpoint", accounts => { }); it("should allow manager with permission to create dividend with checkpoint", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); let tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), checkpointID.toNumber(), dividendName, { from: account_manager } ); - assert.equal(tx.logs[0].args._checkpointId.toNumber(), 8); + let info = await I_ERC20DividendCheckpoint.getCheckpointData.call(checkpointID); + + assert.equal(info[0][0], account_investor1, "account match"); + assert.equal(info[0][1], account_investor2, "account match"); + assert.equal(info[0][2], account_temp, "account match"); + assert.equal(info[0][3], account_investor3, "account match"); + assert.equal(info[1][0].toString(), (await stGetter.balanceOfAt.call(account_investor1, checkpointID)).toString(), "balance match"); + assert.equal(info[1][1].toString(), (await stGetter.balanceOfAt.call(account_investor2, checkpointID)).toString(), "balance match"); + assert.equal(info[1][2].toString(), (await stGetter.balanceOfAt.call(account_temp, checkpointID)).toString(), "balance match"); + assert.equal(info[1][3].toString(), (await stGetter.balanceOfAt.call(account_investor3, checkpointID)).toString(), "balance match"); + assert.equal(info[2][0].toNumber(), 0, "withholding match"); + assert.equal(info[2][1].toString(), new BN((10 * 10 ** 16).toString()).toString(), "withholding match"); + assert.equal(info[2][2].toString(), new BN((20 * 10 ** 16).toString()).toString(), "withholding match"); + assert.equal(info[2][3].toNumber(), 0, "withholding match"); + assert.equal(tx.logs[0].args._checkpointId.toNumber(), checkpointID); }); it("should allow manager with permission to create dividend with exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; + let maturity = (await latestTime()) + duration.days(1); + let expiry = (await latestTime()) + duration.days(10); + let exclusions = [account_temp]; let tx = await I_ERC20DividendCheckpoint.createDividendWithExclusions( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), exclusions, dividendName, { from: account_manager } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 9); + console.log("Gas used w/ max exclusions - non-default: " + tx.receipt.gasUsed); + let info = await I_ERC20DividendCheckpoint.getDividendProgress.call(tx.logs[0].args._dividendIndex); + assert.equal(info[0][2], account_temp, "account_temp is excluded"); + assert.equal(info[2][2], true, "account_temp is excluded"); }); it("should allow manager with permission to create dividend with checkpoint and exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); let tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions( maturity, expiry, I_PolyToken.address, - web3.utils.toWei("1.5", "ether"), + new BN(web3.utils.toWei("1.5", "ether")), checkpointID.toNumber(), exclusions, dividendName, @@ -1105,7 +1203,7 @@ contract("ERC20DividendCheckpoint", accounts => { it("should get the exact details of the factory", async () => { assert.equal((await I_ERC20DividendCheckpointFactory.getSetupCost.call()).toNumber(), 0); assert.equal((await I_ERC20DividendCheckpointFactory.getTypes.call())[0], 4); - assert.equal(await I_ERC20DividendCheckpointFactory.version.call(), "1.0.0"); + assert.equal(await I_ERC20DividendCheckpointFactory.version.call(), "2.1.0"); assert.equal( web3.utils.toAscii(await I_ERC20DividendCheckpointFactory.getName.call()).replace(/\u0000/g, ""), "ERC20DividendCheckpoint", diff --git a/test/f_ether_dividends.js b/test/f_ether_dividends.js index 261a7fd4b..3c379ac43 100644 --- a/test/f_ether_dividends.js +++ b/test/f_ether_dividends.js @@ -4,21 +4,24 @@ import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeProxyCall } from "./helpers/encodeCall"; import { catchRevert } from "./helpers/exceptions"; import { setUpPolymathNetwork, deployEtherDividendAndVerifyed, deployGPMAndVerifyed } from "./helpers/createInstances"; +import { encodeModuleCall } from "./helpers/encodeCall"; const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const EtherDividendCheckpoint = artifacts.require("./EtherDividendCheckpoint"); const GeneralPermissionManager = artifacts.require("GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("EtherDividendCheckpoint", accounts => { +contract("EtherDividendCheckpoint", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; let token_owner; + let wallet; let account_investor1; let account_investor2; let account_investor3; @@ -26,13 +29,10 @@ contract("EtherDividendCheckpoint", accounts => { let account_manager; let account_temp; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - let message = "Transaction Should Fail!"; let dividendName = "0x546573744469766964656e640000000000000000000000000000000000000000"; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // Contract Instance Declaration let I_SecurityTokenRegistryProxy; @@ -54,6 +54,9 @@ contract("EtherDividendCheckpoint", accounts => { let I_PolyToken; let I_MRProxied; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -61,19 +64,22 @@ contract("EtherDividendCheckpoint", accounts => { const tokenDetails = "This is equity type of issuance"; const decimals = 18; const contact = "team@polymath.network"; - const managerDetails = "Hello, I am a legit manager"; + const managerDetails = web3.utils.fromAscii("Hello"); let snapId; // Module key const delegateManagerKey = 1; const transferManagerKey = 2; const stoKey = 3; const checkpointKey = 4; + const DividendParameters = ["address"]; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -85,6 +91,7 @@ contract("EtherDividendCheckpoint", accounts => { account_investor4 = accounts[9]; account_manager = accounts[5]; account_temp = accounts[2]; + wallet = accounts[3]; // Step 1: Deploy the genral PM ecosystem let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -100,11 +107,13 @@ contract("EtherDividendCheckpoint", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - [P_EtherDividendCheckpointFactory] = await deployEtherDividendAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); - [I_EtherDividendCheckpointFactory] = await deployEtherDividendAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [P_EtherDividendCheckpointFactory] = await deployEtherDividendAndVerifyed(account_polymath, I_MRProxied, web3.utils.toWei("500", "ether")); + [I_EtherDividendCheckpointFactory] = await deployEtherDividendAndVerifyed(account_polymath, I_MRProxied, 0); // Printing all the contract addresses console.log(` @@ -134,15 +143,15 @@ contract("EtherDividendCheckpoint", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -150,14 +159,15 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the ERC20DividendCheckpoint with the security token", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); await catchRevert( - I_SecurityToken.addModule(P_EtherDividendCheckpointFactory.address, "", web3.utils.toWei("500", "ether"), 0, { + I_SecurityToken.addModule(P_EtherDividendCheckpointFactory.address, bytesDividend, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }) ); @@ -165,8 +175,9 @@ contract("EtherDividendCheckpoint", accounts => { it("Should successfully attach the EtherDividendCheckpoint with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); - const tx = await I_SecurityToken.addModule(P_EtherDividendCheckpointFactory.address, "", web3.utils.toWei("500", "ether"), 0, { + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); + const tx = await I_SecurityToken.addModule(P_EtherDividendCheckpointFactory.address, bytesDividend, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0].toNumber(), checkpointKey, "EtherDividendCheckpoint doesn't get deployed"); @@ -175,19 +186,20 @@ contract("EtherDividendCheckpoint", accounts => { "EtherDividendCheckpoint", "EtherDividendCheckpoint module was not added" ); - P_EtherDividendCheckpoint = EtherDividendCheckpoint.at(tx.logs[3].args._module); + P_EtherDividendCheckpoint = await EtherDividendCheckpoint.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the EtherDividendCheckpoint with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_EtherDividendCheckpointFactory.address, "", 0, 0, { from: token_owner }); + let bytesDividend = encodeModuleCall(DividendParameters, [wallet]); + const tx = await I_SecurityToken.addModule(I_EtherDividendCheckpointFactory.address, bytesDividend, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), checkpointKey, "EtherDividendCheckpoint doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "EtherDividendCheckpoint", "EtherDividendCheckpoint module was not added" ); - I_EtherDividendCheckpoint = EtherDividendCheckpoint.at(tx.logs[2].args._module); + I_EtherDividendCheckpoint = await EtherDividendCheckpoint.at(tx.logs[2].args._module); }); }); @@ -195,12 +207,11 @@ contract("EtherDividendCheckpoint", accounts => { it("Buy some tokens for account_investor1 (1 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(300000))), { from: account_issuer, gas: 500000 @@ -217,20 +228,19 @@ contract("EtherDividendCheckpoint", accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Buy some tokens for account_investor2 (2 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(3000000))), { from: account_issuer, gas: 500000 @@ -244,95 +254,95 @@ contract("EtherDividendCheckpoint", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("2", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Should fail in creating the dividend", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); await catchRevert(I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner })); }); it("Should fail in creating the dividend", async () => { - let maturity = latestTime(); - let expiry = latestTime() - duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() - duration.days(10); await catchRevert( I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner, - value: web3.utils.toWei("1.5", "ether") + value: new BN(web3.utils.toWei("1.5", "ether")) }) ); }); it("Should fail in creating the dividend", async () => { - let maturity = latestTime() - duration.days(2); - let expiry = latestTime() - duration.days(1); + let maturity = await latestTime() - duration.days(2); + let expiry = await latestTime() - duration.days(1); await catchRevert( I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner, - value: web3.utils.toWei("1.5", "ether") + value: new BN(web3.utils.toWei("1.5", "ether")) }) ); }); it("Set withholding tax of 20% on investor 2", async () => { - await I_EtherDividendCheckpoint.setWithholdingFixed([account_investor2], BigNumber(20 * 10 ** 16), { from: token_owner }); + await I_EtherDividendCheckpoint.setWithholdingFixed([account_investor2], new BN(web3.utils.toWei("0.2", "ether")), { from: token_owner }); }); it("Should fail in creating the dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); await catchRevert( - I_EtherDividendCheckpoint.createDividend(maturity, expiry, "", { + I_EtherDividendCheckpoint.createDividend(maturity, expiry, "0x0", { from: token_owner, - value: web3.utils.toWei("1.5", "ether") + value: new BN(web3.utils.toWei("1.5", "ether")) }) ); }); it("Create new dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner, - value: web3.utils.toWei("1.5", "ether") + value: new BN(web3.utils.toWei("1.5", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 1, "Dividend should be created at checkpoint 1"); assert.equal(tx.logs[0].args._name.toString(), dividendName, "Dividend name incorrect in event"); }); it("Investor 1 transfers his token balance to investor 2", async () => { - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); assert.equal(await I_SecurityToken.balanceOf(account_investor1), 0); - assert.equal(await I_SecurityToken.balanceOf(account_investor2), web3.utils.toWei("3", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("3", "ether")).toString()); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint", async () => { - await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, 0, 10, { from: token_owner })); + await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: token_owner })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint", async () => { // Increase time by 2 day await increaseTime(duration.days(2)); - await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, 0, 10, { from: account_temp })); + await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: account_temp })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint", async () => { - await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(2, 0, 10, { from: token_owner })); + await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner })); }); it("Issuer pushes dividends iterating over account holders - dividends proportional to checkpoint", async () => { - let investor1Balance = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2Balance = new BigNumber(await web3.eth.getBalance(account_investor2)); - await I_EtherDividendCheckpoint.pushDividendPayment(0, 0, 10, { from: token_owner }); - let investor1BalanceAfter = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter = new BigNumber(await web3.eth.getBalance(account_investor2)); - assert.equal(investor1BalanceAfter.sub(investor1Balance).toNumber(), web3.utils.toWei("0.5", "ether")); - assert.equal(investor2BalanceAfter.sub(investor2Balance).toNumber(), web3.utils.toWei("0.8", "ether")); + let investor1Balance = new BN(await web3.eth.getBalance(account_investor1)); + let investor2Balance = new BN(await web3.eth.getBalance(account_investor2)); + await I_EtherDividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: token_owner }); + let investor1BalanceAfter = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter = new BN(await web3.eth.getBalance(account_investor2)); + assert.equal(investor1BalanceAfter.sub(investor1Balance).toString(), new BN(web3.utils.toWei("0.5", "ether")).toString()); + assert.equal(investor2BalanceAfter.sub(investor2Balance).toString(), new BN(web3.utils.toWei("0.8", "ether")).toString()); //Check fully claimed - assert.equal((await I_EtherDividendCheckpoint.dividends(0))[5].toNumber(), web3.utils.toWei("1.5", "ether")); + assert.equal((await I_EtherDividendCheckpoint.dividends(0))[5].toString(), new BN(web3.utils.toWei("1.5", "ether")).toString()); }); it("Should not allow reclaiming withholding tax with incorrect index", async () => { @@ -343,28 +353,27 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Issuer reclaims withholding tax", async () => { - let issuerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let issuerBalance = new BN(await web3.eth.getBalance(wallet)); await I_EtherDividendCheckpoint.withdrawWithholding(0, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0.2", "ether")); + let issuerBalanceAfter = new BN(await web3.eth.getBalance(wallet)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0.2", "ether")).toString()); }); it("No more withholding tax to withdraw", async () => { - let issuerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let issuerBalance = new BN(await web3.eth.getBalance(token_owner)); await I_EtherDividendCheckpoint.withdrawWithholding(0, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0", "ether")); + let issuerBalanceAfter = new BN(await web3.eth.getBalance(token_owner)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Buy some tokens for account_temp (1 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_temp, - latestTime(), - latestTime(), - latestTime() + duration.days(20), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(200000))), { from: account_issuer, gas: 500000 @@ -374,48 +383,47 @@ contract("EtherDividendCheckpoint", accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_temp.toLowerCase(), "Failed in adding the investor in whitelist"); // Mint some tokens - await I_SecurityToken.mint(account_temp, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_temp, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_temp)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_temp)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Create new dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner, - value: web3.utils.toWei("1.5", "ether") + value: new BN(web3.utils.toWei("1.5", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 2, "Dividend should be created at checkpoint 2"); }); it("Issuer pushes dividends fails due to passed expiry", async () => { await increaseTime(duration.days(12)); - await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, 0, 10, { from: token_owner })); + await catchRevert(I_EtherDividendCheckpoint.pushDividendPayment(0, new BN(0), 10, { from: token_owner })); }); it("Issuer reclaims dividend", async () => { let tx = await I_EtherDividendCheckpoint.reclaimDividend(1, { from: token_owner, gas: 500000 }); - assert.equal(tx.logs[0].args._claimedAmount.toNumber(), web3.utils.toWei("1.5", "ether")); + assert.equal(tx.logs[0].args._claimedAmount.toString(), new BN(web3.utils.toWei("1.5", "ether")).toString()); await catchRevert(I_EtherDividendCheckpoint.reclaimDividend(1, { from: token_owner, gas: 500000 })); }); it("Still no more withholding tax to withdraw", async () => { - let issuerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let issuerBalance = new BN(await web3.eth.getBalance(token_owner)); await I_EtherDividendCheckpoint.withdrawWithholding(0, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0", "ether")); + let issuerBalanceAfter = new BN(await web3.eth.getBalance(token_owner)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Buy some tokens for account_investor3 (7 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10000))), { from: account_issuer, gas: 500000 @@ -429,46 +437,46 @@ contract("EtherDividendCheckpoint", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("7", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("7", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei("7", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Create another new dividend", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { from: token_owner, - value: web3.utils.toWei("11", "ether") + value: new BN(web3.utils.toWei("11", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 3, "Dividend should be created at checkpoint 3"); }); it("should investor 3 claims dividend - fails bad index", async () => { - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); await catchRevert(I_EtherDividendCheckpoint.pullDividendPayment(5, { from: account_investor3, gasPrice: 0 })); }); it("Should investor 3 claims dividend", async () => { - let investor1Balance = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2Balance = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3Balance = new BigNumber(await web3.eth.getBalance(account_investor3)); + let investor1Balance = new BN(await web3.eth.getBalance(account_investor1)); + let investor2Balance = new BN(await web3.eth.getBalance(account_investor2)); + let investor3Balance = new BN(await web3.eth.getBalance(account_investor3)); await I_EtherDividendCheckpoint.pullDividendPayment(2, { from: account_investor3, gasPrice: 0 }); - let investor1BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor3)); + let investor1BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); assert.equal(investor2BalanceAfter1.sub(investor2Balance).toNumber(), 0); - assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), web3.utils.toWei("7", "ether")); + assert.equal(investor3BalanceAfter1.sub(investor3Balance).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Still no more withholding tax to withdraw", async () => { - let issuerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let issuerBalance = new BN(await web3.eth.getBalance(token_owner)); await I_EtherDividendCheckpoint.withdrawWithholding(0, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0", "ether")); + let issuerBalanceAfter = new BN(await web3.eth.getBalance(token_owner)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("should investor 3 claims dividend", async () => { @@ -476,37 +484,37 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Issuer pushes remainder", async () => { - let investor1BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor3)); - await I_EtherDividendCheckpoint.pushDividendPayment(2, 0, 10, { from: token_owner }); - let investor1BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor3)); + let investor1BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); + await I_EtherDividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner }); + let investor1BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor3)); assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toNumber(), 0); - assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toNumber(), web3.utils.toWei("2.4", "ether")); + assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toString(), new BN(web3.utils.toWei("2.4", "ether")).toString()); assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toNumber(), 0); //Check fully claimed - assert.equal((await I_EtherDividendCheckpoint.dividends(2))[5].toNumber(), web3.utils.toWei("11", "ether")); + assert.equal((await I_EtherDividendCheckpoint.dividends(2))[5].toString(), new BN(web3.utils.toWei("11", "ether")).toString()); }); it("Issuer withdraws new withholding tax", async () => { - let issuerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let issuerBalance = new BN(await web3.eth.getBalance(wallet)); await I_EtherDividendCheckpoint.withdrawWithholding(2, { from: token_owner, gasPrice: 0 }); - let issuerBalanceAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toNumber(), web3.utils.toWei("0.6", "ether")); + let issuerBalanceAfter = new BN(await web3.eth.getBalance(wallet)); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0.6", "ether")).toString()); }); it("Investor 2 transfers 1 ETH of his token balance to investor 1", async () => { - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_investor2 }); - assert.equal(await I_SecurityToken.balanceOf(account_investor1), web3.utils.toWei("1", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor2), web3.utils.toWei("2", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor3), web3.utils.toWei("7", "ether")); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_investor2 }); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Create another new dividend with no value - fails", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(2); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(2); let tx = await I_SecurityToken.createCheckpoint({ from: token_owner }); await catchRevert( I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, { from: token_owner, value: 0 }) @@ -514,60 +522,62 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Create another new dividend with explicit", async () => { - let maturity = latestTime(); - let expiry = latestTime() - duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() - duration.days(10); await catchRevert( I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, { from: token_owner, - value: web3.utils.toWei("11", "ether") + value: new BN(web3.utils.toWei("11", "ether")) }) ); }); it("Create another new dividend with bad expirty - fails", async () => { - let maturity = latestTime() - duration.days(5); - let expiry = latestTime() - duration.days(2); + let maturity = await latestTime() - duration.days(5); + let expiry = await latestTime() - duration.days(2); await catchRevert( I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, { from: token_owner, - value: web3.utils.toWei("11", "ether") + value: new BN(web3.utils.toWei("11", "ether")) }) ); }); it("Create another new dividend with bad checkpoint in the future - fails", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(2); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(2); await catchRevert( I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 5, dividendName, { from: token_owner, - value: web3.utils.toWei("11", "ether") + value: new BN(web3.utils.toWei("11", "ether")) }) ); }); it("Should not create dividend with more exclusions than limit", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); await I_SecurityToken.createCheckpoint({ from: token_owner }); let limit = await I_EtherDividendCheckpoint.EXCLUDED_ADDRESS_LIMIT(); - limit = limit.toNumber(); + limit = limit.toNumber() + 42; let addresses = []; addresses.push(account_temp); addresses.push(token_owner); - while (--limit) addresses.push(limit); + let tempAdd = '0x0000000000000000000000000000000000000000'; + while (--limit > 42) addresses.push(web3.utils.toChecksumAddress(tempAdd.substring(0, 42 - limit.toString().length) + limit)); + // while (--limit > 42) addresses.push(web3.utils.toChecksumAddress('0x00000000000000000000000000000000000000' + limit)); await catchRevert( I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, 4, addresses, dividendName, { from: token_owner, - value: web3.utils.toWei("10", "ether") + value: new BN(web3.utils.toWei("10", "ether")) }), "tx -> failed because too many address excluded" ); }); it("Create another new dividend with explicit checkpoint and excluding account_investor1", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); //checkpoint created in above test let tx = await I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( maturity, @@ -575,43 +585,43 @@ contract("EtherDividendCheckpoint", accounts => { 4, [account_investor1], dividendName, - { from: token_owner, value: web3.utils.toWei("10", "ether") } + { from: token_owner, value: new BN(web3.utils.toWei("10", "ether")) } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 4, "Dividend should be created at checkpoint 4"); }); it("Should not create new dividend with duplicate exclusion", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); //checkpoint created in above test - await catchRevert(I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - 4, - [account_investor1, account_investor1], - dividendName, - { from: token_owner, value: web3.utils.toWei("10", "ether") } - )); + await catchRevert( + I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( + maturity, + expiry, + 4, + [account_investor1, account_investor1], + dividendName, + { from: token_owner, value: new BN(web3.utils.toWei("10", "ether")) } + ) + ); }); it("Should not create new dividend with 0x0 address in exclusion", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); //checkpoint created in above test - await catchRevert(I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - 4, - [0], - dividendName, - { from: token_owner, value: web3.utils.toWei("10", "ether") } - )); + await catchRevert( + I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, 4, [address_zero], dividendName, { + from: token_owner, + value: new BN(web3.utils.toWei("10", "ether")) + }) + ); }); it("Non-owner pushes investor 1 - fails", async () => { - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); await catchRevert( I_EtherDividendCheckpoint.pushDividendPaymentToAddresses(3, [account_investor2, account_investor1], { from: account_investor2, @@ -621,9 +631,9 @@ contract("EtherDividendCheckpoint", accounts => { }); it("issuer pushes investor 1 with bad dividend index - fails", async () => { - let investor1Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor1)); - let investor2Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor2)); - let investor3Balance = new BigNumber(await I_PolyToken.balanceOf(account_investor3)); + let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); + let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); + let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); await catchRevert( I_EtherDividendCheckpoint.pushDividendPaymentToAddresses(6, [account_investor2, account_investor1], { from: token_owner, @@ -638,48 +648,48 @@ contract("EtherDividendCheckpoint", accounts => { let dividendAmount3 = await I_EtherDividendCheckpoint.calculateDividend.call(3, account_investor3); let dividendAmount_temp = await I_EtherDividendCheckpoint.calculateDividend.call(3, account_temp); //1 has 1/11th, 2 has 2/11th, 3 has 7/11th, temp has 1/11th, but 1 is excluded - assert.equal(dividendAmount1[0].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount1[1].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount2[0].toNumber(), web3.utils.toWei("2", "ether")); - assert.equal(dividendAmount2[1].toNumber(), web3.utils.toWei("0.4", "ether")); - assert.equal(dividendAmount3[0].toNumber(), web3.utils.toWei("7", "ether")); - assert.equal(dividendAmount3[1].toNumber(), web3.utils.toWei("0", "ether")); - assert.equal(dividendAmount_temp[0].toNumber(), web3.utils.toWei("1", "ether")); - assert.equal(dividendAmount_temp[1].toNumber(), web3.utils.toWei("0", "ether")); + assert.equal(dividendAmount1[0].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount1[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount2[0].toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("0.4", "ether")).toString()); + assert.equal(dividendAmount3[0].toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + assert.equal(dividendAmount3[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); + assert.equal(dividendAmount_temp[0].toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal(dividendAmount_temp[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Investor 2 claims dividend", async () => { - let investor1Balance = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2Balance = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3Balance = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalance = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1Balance = new BN(await web3.eth.getBalance(account_investor1)); + let investor2Balance = new BN(await web3.eth.getBalance(account_investor2)); + let investor3Balance = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalance = new BN(await web3.eth.getBalance(account_temp)); await I_EtherDividendCheckpoint.pullDividendPayment(3, { from: account_investor2, gasPrice: 0 }); - let investor1BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalanceAfter1 = new BN(await web3.eth.getBalance(account_temp)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); - assert.equal(investor2BalanceAfter1.sub(investor2Balance).toNumber(), web3.utils.toWei("1.6", "ether")); + assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), new BN(web3.utils.toWei("1.6", "ether")).toString()); assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), 0); assert.equal(tempBalanceAfter1.sub(tempBalance).toNumber(), 0); }); it("Should issuer pushes investor 1 and temp investor", async () => { - let investor1BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalanceAfter1 = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalanceAfter1 = new BN(await web3.eth.getBalance(account_temp)); await I_EtherDividendCheckpoint.pushDividendPaymentToAddresses(3, [account_investor1, account_temp], { from: token_owner }); - let investor1BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalanceAfter2 = new BigNumber(await web3.eth.getBalance(account_temp)); + let investor1BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalanceAfter2 = new BN(await web3.eth.getBalance(account_temp)); assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toNumber(), 0); assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toNumber(), 0); assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toNumber(), 0); - assert.equal(tempBalanceAfter2.sub(tempBalanceAfter1).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal(tempBalanceAfter2.sub(tempBalanceAfter1).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); //Check fully claimed - assert.equal((await I_EtherDividendCheckpoint.dividends(3))[5].toNumber(), web3.utils.toWei("3", "ether")); + assert.equal((await I_EtherDividendCheckpoint.dividends(3))[5].toString(), new BN(web3.utils.toWei("3", "ether")).toString()); }); it("should calculate dividend after the push dividend payment", async () => { @@ -699,10 +709,10 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Issuer is able to reclaim dividend after expiry", async () => { - let tokenOwnerBalance = new BigNumber(await web3.eth.getBalance(token_owner)); + let tokenOwnerBalance = new BN(await web3.eth.getBalance(wallet)); await I_EtherDividendCheckpoint.reclaimDividend(3, { from: token_owner, gasPrice: 0 }); - let tokenOwnerAfter = new BigNumber(await web3.eth.getBalance(token_owner)); - assert.equal(tokenOwnerAfter.sub(tokenOwnerBalance).toNumber(), web3.utils.toWei("7", "ether")); + let tokenOwnerAfter = new BN(await web3.eth.getBalance(wallet)); + assert.equal(tokenOwnerAfter.sub(tokenOwnerBalance).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); }); it("Issuer is able to reclaim dividend after expiry", async () => { @@ -714,12 +724,11 @@ contract("EtherDividendCheckpoint", accounts => { }); it("Assign token balance to an address that can't receive funds", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( I_PolyToken.address, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(1000000))), { from: account_issuer, gas: 500000 @@ -728,47 +737,47 @@ contract("EtherDividendCheckpoint", accounts => { // Jump time await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(I_PolyToken.address, web3.utils.toWei("1", "ether"), { from: token_owner }); - assert.equal(await I_SecurityToken.balanceOf(account_investor1), web3.utils.toWei("1", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor2), web3.utils.toWei("2", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_investor3), web3.utils.toWei("7", "ether")); - assert.equal(await I_SecurityToken.balanceOf(account_temp), web3.utils.toWei("1", "ether")); - assert.equal(await I_SecurityToken.balanceOf(I_PolyToken.address), web3.utils.toWei("1", "ether")); + await I_SecurityToken.issue(I_PolyToken.address, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_temp)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(I_PolyToken.address)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Create another new dividend", async () => { - let maturity = latestTime(); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime(); + let expiry = await latestTime() + duration.days(10); let tx = await I_EtherDividendCheckpoint.createDividendWithExclusions(maturity, expiry, [], dividendName, { from: token_owner, - value: web3.utils.toWei("12", "ether") + value: new BN(web3.utils.toWei("12", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 6, "Dividend should be created at checkpoint 6"); }); it("Should issuer pushes all dividends", async () => { - let investor1BalanceBefore = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceBefore = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceBefore = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalanceBefore = new BigNumber(await web3.eth.getBalance(account_temp)); - let tokenBalanceBefore = new BigNumber(await web3.eth.getBalance(I_PolyToken.address)); - - await I_EtherDividendCheckpoint.pushDividendPayment(4, 0, 10, { from: token_owner }); - - let investor1BalanceAfter = new BigNumber(await web3.eth.getBalance(account_investor1)); - let investor2BalanceAfter = new BigNumber(await web3.eth.getBalance(account_investor2)); - let investor3BalanceAfter = new BigNumber(await web3.eth.getBalance(account_investor3)); - let tempBalanceAfter = new BigNumber(await web3.eth.getBalance(account_temp)); - let tokenBalanceAfter = new BigNumber(await web3.eth.getBalance(I_PolyToken.address)); - - assert.equal(investor1BalanceAfter.sub(investor1BalanceBefore).toNumber(), web3.utils.toWei("1", "ether")); - assert.equal(investor2BalanceAfter.sub(investor2BalanceBefore).toNumber(), web3.utils.toWei("1.6", "ether")); - assert.equal(investor3BalanceAfter.sub(investor3BalanceBefore).toNumber(), web3.utils.toWei("7", "ether")); - assert.equal(tempBalanceAfter.sub(tempBalanceBefore).toNumber(), web3.utils.toWei("1", "ether")); - assert.equal(tokenBalanceAfter.sub(tokenBalanceBefore).toNumber(), web3.utils.toWei("0", "ether")); + let investor1BalanceBefore = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceBefore = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceBefore = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalanceBefore = new BN(await web3.eth.getBalance(account_temp)); + let tokenBalanceBefore = new BN(await web3.eth.getBalance(I_PolyToken.address)); + + await I_EtherDividendCheckpoint.pushDividendPayment(4, new BN(0), 10, { from: token_owner }); + + let investor1BalanceAfter = new BN(await web3.eth.getBalance(account_investor1)); + let investor2BalanceAfter = new BN(await web3.eth.getBalance(account_investor2)); + let investor3BalanceAfter = new BN(await web3.eth.getBalance(account_investor3)); + let tempBalanceAfter = new BN(await web3.eth.getBalance(account_temp)); + let tokenBalanceAfter = new BN(await web3.eth.getBalance(I_PolyToken.address)); + + assert.equal(investor1BalanceAfter.sub(investor1BalanceBefore).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal(investor2BalanceAfter.sub(investor2BalanceBefore).toString(), new BN(web3.utils.toWei("1.6", "ether")).toString()); + assert.equal(investor3BalanceAfter.sub(investor3BalanceBefore).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); + assert.equal(tempBalanceAfter.sub(tempBalanceBefore).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal(tokenBalanceAfter.sub(tokenBalanceBefore).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); //Check partially claimed - assert.equal((await I_EtherDividendCheckpoint.dividends(4))[5].toNumber(), web3.utils.toWei("11", "ether")); + assert.equal((await I_EtherDividendCheckpoint.dividends(4))[5].toString(), new BN(web3.utils.toWei("11", "ether")).toString()); }); it("Should give the right dividend index", async () => { @@ -781,19 +790,14 @@ contract("EtherDividendCheckpoint", accounts => { assert.equal(index.length, 0); }); - it("Get the init data", async () => { - let tx = await I_EtherDividendCheckpoint.getInitFunction.call(); - assert.equal(web3.utils.toAscii(tx).replace(/\u0000/g, ""), 0); - }); - it("Should get the listed permissions", async () => { let tx = await I_EtherDividendCheckpoint.getPermissions.call(); assert.equal(tx.length, 2); }); it("should registr a delegate", async () => { - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", 0, 0, { from: token_owner }); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), @@ -801,65 +805,65 @@ contract("EtherDividendCheckpoint", accounts => { "GeneralPermissionManagerFactory module was not added" ); I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); - tx = await I_GeneralPermissionManager.addDelegate(account_manager, managerDetails, { from: token_owner}); + tx = await I_GeneralPermissionManager.addDelegate(account_manager, managerDetails, { from: token_owner }); assert.equal(tx.logs[0].args._delegate, account_manager); }); it("should not allow manager without permission to create dividend", async () => { - await I_PolyToken.transfer(account_manager, web3.utils.toWei("2", "ether"), { from: token_owner }); - await I_PolyToken.approve(I_EtherDividendCheckpoint.address, web3.utils.toWei("1.5", "ether"), { from: account_manager }); - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + await I_PolyToken.transfer(account_manager, new BN(web3.utils.toWei("2", "ether")), { from: token_owner }); + await I_PolyToken.approve(I_EtherDividendCheckpoint.address, new BN(web3.utils.toWei("1.5", "ether")), { from: account_manager }); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); - await catchRevert(I_EtherDividendCheckpoint.createDividend( - maturity, - expiry, - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - )); + await catchRevert( + I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }) + ); }); it("should not allow manager without permission to create dividend with checkpoint", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); - await catchRevert(I_EtherDividendCheckpoint.createDividendWithCheckpoint( - maturity, - expiry, - checkpointID.toNumber(), - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - )); + await catchRevert( + I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, checkpointID.toNumber(), dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }) + ); }); it("should not allow manager without permission to create dividend with exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; - await catchRevert(I_EtherDividendCheckpoint.createDividendWithExclusions( - maturity, - expiry, - exclusions, - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - )); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; + await catchRevert( + I_EtherDividendCheckpoint.createDividendWithExclusions(maturity, expiry, exclusions, dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }) + ); }); it("should not allow manager without permission to create dividend with checkpoint and exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); - await I_SecurityToken.createCheckpoint({ from: token_owner }); - await catchRevert(I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( - maturity, - expiry, - checkpointID.toNumber(), - exclusions, - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - )); + await I_SecurityToken.createCheckpoint({ from: token_owner }); + await catchRevert( + I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( + maturity, + expiry, + checkpointID.toNumber(), + exclusions, + dividendName, + { from: account_manager, value: new BN(web3.utils.toWei("12", "ether")) } + ) + ); }); it("should not allow manager without permission to create checkpoint", async () => { @@ -867,69 +871,53 @@ contract("EtherDividendCheckpoint", accounts => { }); it("should give permission to manager", async () => { - await I_GeneralPermissionManager.changePermission( - account_manager, - I_EtherDividendCheckpoint.address, - "CHECKPOINT", - true, - { from: token_owner } - ); - let tx = await I_GeneralPermissionManager.changePermission( - account_manager, - I_EtherDividendCheckpoint.address, - "MANAGE", - true, - { from: token_owner } - ); + await I_GeneralPermissionManager.changePermission(account_manager, I_EtherDividendCheckpoint.address, web3.utils.fromAscii("CHECKPOINT"), true, { + from: token_owner + }); + let tx = await I_GeneralPermissionManager.changePermission(account_manager, I_EtherDividendCheckpoint.address, web3.utils.fromAscii("MANAGE"), true, { + from: token_owner + }); assert.equal(tx.logs[0].args._delegate, account_manager); }); it("should allow manager with permission to create dividend", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); - let tx = await I_EtherDividendCheckpoint.createDividend( - maturity, - expiry, - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - ); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 9); }); it("should allow manager with permission to create dividend with checkpoint", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); - let tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint( - maturity, - expiry, - checkpointID.toNumber(), - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - ); + let tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, checkpointID.toNumber(), dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 10); }); it("should allow manager with permission to create dividend with exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; - let tx = await I_EtherDividendCheckpoint.createDividendWithExclusions( - maturity, - expiry, - exclusions, - dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } - ); + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; + let tx = await I_EtherDividendCheckpoint.createDividendWithExclusions(maturity, expiry, exclusions, dividendName, { + from: account_manager, + value: new BN(web3.utils.toWei("12", "ether")) + }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 11); }); it("should allow manager with permission to create dividend with checkpoint and exclusion", async () => { - let maturity = latestTime() + duration.days(1); - let expiry = latestTime() + duration.days(10); - let exclusions = [1]; + let maturity = await latestTime() + duration.days(1); + let expiry = await latestTime() + duration.days(10); + let exclusions = [one_address]; let checkpointID = await I_SecurityToken.createCheckpoint.call({ from: token_owner }); await I_SecurityToken.createCheckpoint({ from: token_owner }); let tx = await I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions( @@ -938,7 +926,7 @@ contract("EtherDividendCheckpoint", accounts => { checkpointID.toNumber(), exclusions, dividendName, - { from: account_manager, value: web3.utils.toWei("12", "ether") } + { from: account_manager, value: new BN(web3.utils.toWei("12", "ether")) } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 12); }); @@ -954,7 +942,7 @@ contract("EtherDividendCheckpoint", accounts => { it("should get the exact details of the factory", async () => { assert.equal((await I_EtherDividendCheckpointFactory.getSetupCost.call()).toNumber(), 0); assert.equal((await I_EtherDividendCheckpointFactory.getTypes.call())[0], 4); - assert.equal(await I_EtherDividendCheckpointFactory.version.call(), "1.0.0"); + assert.equal(await I_EtherDividendCheckpointFactory.version.call(), "2.1.0"); assert.equal( web3.utils.toAscii(await I_EtherDividendCheckpointFactory.getName.call()).replace(/\u0000/g, ""), "EtherDividendCheckpoint", diff --git a/test/g_general_permission_manager.js b/test/g_general_permission_manager.js index d554891ee..6e836fb33 100644 --- a/test/g_general_permission_manager.js +++ b/test/g_general_permission_manager.js @@ -1,21 +1,20 @@ -import latestTime from './helpers/latestTime'; -import {signData} from './helpers/signData'; -import { pk } from './helpers/testprivateKey'; -import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; -import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import latestTime from "./helpers/latestTime"; +import { pk } from "./helpers/testprivateKey"; +import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; +import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { catchRevert } from "./helpers/exceptions"; import { setUpPolymathNetwork, deployGPMAndVerifyed } from "./helpers/createInstances"; -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter"); -const Web3 = require('web3'); -const BigNumber = require('bignumber.js'); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port - -contract('GeneralPermissionManager', accounts => { +const Web3 = require("web3"); +const BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port +contract("GeneralPermissionManager", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -28,10 +27,7 @@ contract('GeneralPermissionManager', accounts => { let account_delegate; let account_delegate2; let account_delegate3; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); + const delegateDetails = web3.utils.fromAscii("I am delegate"); let message = "Transaction Should Fail!"; @@ -54,6 +50,9 @@ contract('GeneralPermissionManager', accounts => { let I_STRProxied; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -61,7 +60,7 @@ contract('GeneralPermissionManager', accounts => { const tokenDetails = "This is equity type of issuance"; const decimals = 18; const contact = "team@polymath.network"; - const delegateDetails = "Hello I am legit delegate"; + const managerDetails = web3.utils.fromAscii("Hello"); // Module key const delegateManagerKey = 1; @@ -69,10 +68,14 @@ contract('GeneralPermissionManager', accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; - before(async() => { - // Accounts setup + before(async () => { + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -99,13 +102,15 @@ contract('GeneralPermissionManager', accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 5: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 6: Deploy the GeneralDelegateManagerFactory - [P_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + [P_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); // Printing all the contract addresses console.log(` @@ -134,15 +139,15 @@ contract('GeneralPermissionManager', accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -150,26 +155,28 @@ contract('GeneralPermissionManager', accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the General permission manager factory with the security token -- failed because Token is not paid", async () => { let errorThrown = false; - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); await catchRevert( - I_SecurityToken.addModule(P_GeneralPermissionManagerFactory.address, "0x", web3.utils.toWei("500", "ether"), 0, { from: token_owner }) + I_SecurityToken.addModule(P_GeneralPermissionManagerFactory.address, "0x", new BN(web3.utils.toWei("2000", "ether")), new BN(0), { + from: token_owner + }) ); }); it("Should successfully attach the General permission manager factory with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); const tx = await I_SecurityToken.addModule( P_GeneralPermissionManagerFactory.address, "0x", - web3.utils.toWei("500", "ether"), - 0, + new BN(web3.utils.toWei("2000", "ether")), + new BN(0), { from: token_owner } ); assert.equal(tx.logs[3].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); @@ -178,21 +185,20 @@ contract('GeneralPermissionManager', accounts => { "GeneralPermissionManager", "GeneralPermissionManagerFactory module was not added" ); - P_GeneralPermissionManager = GeneralPermissionManager.at(tx.logs[3].args._module); + P_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the General permission manager factory with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "GeneralPermissionManager", "GeneralPermissionManagerFactory module was not added" ); - I_GeneralPermissionManager = GeneralPermissionManager.at(tx.logs[2].args._module); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); }); - }); describe("General Permission Manager test cases", async () => { @@ -201,51 +207,43 @@ contract('GeneralPermissionManager', accounts => { assert.equal(web3.utils.toAscii(tx).replace(/\u0000/g, ""), 0); }); - it("Should fail in adding the delegate -- msg.sender doesn't have permission", async() => { + it("Should fail in adding the delegate -- msg.sender doesn't have permission", async () => { let errorThrown = false; - await catchRevert( - I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: account_investor1}) - ); + await catchRevert(I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: account_investor1 })); }); - it("Should fail in adding the delegate -- no delegate details provided", async() => { - await catchRevert( - I_GeneralPermissionManager.addDelegate(account_delegate, '', { from: token_owner }) - ); + it("Should fail in adding the delegate -- no delegate details provided", async () => { + await catchRevert(I_GeneralPermissionManager.addDelegate(account_delegate, "0x0", { from: token_owner })); }); - it("Should fail in adding the delegate -- no delegate address provided", async() => { - await catchRevert( - I_GeneralPermissionManager.addDelegate('', delegateDetails, { from: token_owner }) - ); + it("Should fail in adding the delegate -- no delegate address provided", async () => { + await catchRevert(I_GeneralPermissionManager.addDelegate(address_zero, delegateDetails, { from: token_owner })); }); - it("Should fail to remove the delegate -- failed because delegate does not exisit", async() => { - await catchRevert( - I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: token_owner}) - ); + it("Should fail to remove the delegate -- failed because delegate does not exisit", async () => { + await catchRevert(I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: token_owner })); }); - it("Should successfully add the delegate", async() => { - let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner}); + it("Should successfully add the delegate", async () => { + let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner }); assert.equal(tx.logs[0].args._delegate, account_delegate); }); - it("Should successfully add the delegate -- failed because trying to add the already present delegate", async() => { + it("Should successfully add the delegate -- failed because trying to add the already present delegate", async () => { + await catchRevert(I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner })); + }); + + it("Should fail to provide the permission -- because msg.sender doesn't have permission", async () => { await catchRevert( - I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner}) + I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"), true, { + from: account_investor1 + }) ); - }) - - it("Should fail to provide the permission -- because msg.sender doesn't have permission", async() => { - await catchRevert( - I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, "WHITELIST", true, {from: account_investor1}) - ); }); it("Should check the permission", async () => { assert.isFalse( - await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "WHITELIST") + await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST")) ); }); @@ -253,7 +251,7 @@ contract('GeneralPermissionManager', accounts => { let tx = await I_GeneralPermissionManager.changePermission( account_delegate, I_GeneralTransferManager.address, - "WHITELIST", + web3.utils.fromAscii("WHITELIST"), true, { from: token_owner } ); @@ -262,47 +260,42 @@ contract('GeneralPermissionManager', accounts => { it("Should check the permission", async () => { assert.isTrue( - await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "WHITELIST") + await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST")) ); }); it("Security token should deny all permission if all permission managers are disabled", async () => { await I_SecurityToken.archiveModule(I_GeneralPermissionManager.address, { from: token_owner }); - assert.isFalse( - await I_SecurityToken.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "WHITELIST") - ); + assert.isFalse(await stGetter.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"))); await I_SecurityToken.unarchiveModule(I_GeneralPermissionManager.address, { from: token_owner }); - assert.isTrue( - await I_SecurityToken.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "WHITELIST") - ); + assert.isTrue(await stGetter.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"))); }); - it("Should fail to remove the delegate -- failed because unauthorized msg.sender", async() => { - await catchRevert( - I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: account_delegate}) - ); + it("Should fail to remove the delegate -- failed because unauthorized msg.sender", async () => { + await catchRevert(I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: account_delegate })); }); - it("Should remove the delegate", async() => { - await I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: token_owner}) + it("Should remove the delegate", async () => { + await I_GeneralPermissionManager.deleteDelegate(account_delegate, { from: token_owner }); }); it("Should check the permission", async () => { assert.isFalse( - await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "WHITELIST") + await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST")) ); }); - it("Should successfully add the delegate", async() => { - let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner}); + it("Should successfully add the delegate", async () => { + let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner }); assert.equal(tx.logs[0].args._delegate, account_delegate); }); - it("Should check the delegate details", async() => { - assert.equal(web3.utils.toAscii(await I_GeneralPermissionManager.delegateDetails.call(account_delegate)) - .replace(/\u0000/g, ''), - delegateDetails, - "Wrong delegate address get checked"); + it("Should check the delegate details", async () => { + assert.equal( + web3.utils.toAscii(await I_GeneralPermissionManager.delegateDetails.call(account_delegate)).replace(/\u0000/g, ""), + web3.utils.toAscii(delegateDetails), + "Wrong delegate address get checked" + ); }); it("Should get the permission of the general permission manager contract", async () => { @@ -310,84 +303,116 @@ contract('GeneralPermissionManager', accounts => { assert.equal(web3.utils.toAscii(tx[0]).replace(/\u0000/g, ""), "CHANGE_PERMISSION", "Wrong permissions"); }); - it("Should return all delegates", async() => { - await I_GeneralPermissionManager.addDelegate(account_delegate2, delegateDetails, { from: token_owner}); + it("Should return all delegates", async () => { + await I_GeneralPermissionManager.addDelegate(account_delegate2, delegateDetails, { from: token_owner }); let tx = await I_GeneralPermissionManager.getAllDelegates.call(); assert.equal(tx.length, 2); - assert.equal(tx[0], account_delegate); + assert.equal(tx[0], account_delegate); assert.equal(tx[1], account_delegate2); }); - it("Should check is delegate for 0x address - failed 0x address is not allowed", async() => { - await catchRevert( - I_GeneralPermissionManager.checkDelegate.call("0x0000000000000000000000000000000000000000000000000") - ); + it("Should check is delegate for 0x address - failed 0x address is not allowed", async () => { + await catchRevert(I_GeneralPermissionManager.checkDelegate.call(address_zero)); }); - it("Should return false when check is delegate - because user is not a delegate", async() => { + it("Should return false when check is delegate - because user is not a delegate", async () => { assert.equal(await I_GeneralPermissionManager.checkDelegate.call(account_investor1), false); }); - it("Should return true when check is delegate - because user is a delegate", async() => { + it("Should return true when check is delegate - because user is a delegate", async () => { assert.equal(await I_GeneralPermissionManager.checkDelegate.call(account_delegate), true); }); - - it("Should successfully provide the permissions in batch -- failed because of array length is 0", async() => { - await I_GeneralPermissionManager.addDelegate(account_delegate3, delegateDetails, { from: token_owner}); + it("Should successfully provide the permissions in batch -- failed because of array length is 0", async () => { + await I_GeneralPermissionManager.addDelegate(account_delegate3, delegateDetails, { from: token_owner }); await catchRevert( - I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [], ["WHITELIST","CHANGE_PERMISSION"], [true, true], {from: token_owner}) + I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [], [web3.utils.fromAscii("WHITELIST"), web3.utils.fromAscii("CHANGE_PERMISSION")], [true, true], { + from: token_owner + }) ); }); - it("Should successfully provide the permissions in batch -- failed because of perm array length is 0", async() => { + it("Should successfully provide the permissions in batch -- failed because of perm array length is 0", async () => { await catchRevert( - I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], [], [true, true], {from: token_owner}) + I_GeneralPermissionManager.changePermissionMulti( + account_delegate3, + [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], + [], + [true, true], + { from: token_owner } + ) ); }); - it("Should successfully provide the permissions in batch -- failed because mismatch in arrays length", async() => { + it("Should successfully provide the permissions in batch -- failed because mismatch in arrays length", async () => { await catchRevert( - I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [I_GeneralTransferManager.address], ["WHITELIST","CHANGE_PERMISSION"], [true, true], {from: token_owner}) + I_GeneralPermissionManager.changePermissionMulti( + account_delegate3, + [I_GeneralTransferManager.address], + [web3.utils.fromAscii("WHITELIST"), web3.utils.fromAscii("CHANGE_PERMISSION")], + [true, true], + { from: token_owner } + ) ); }); - it("Should successfully provide the permissions in batch -- failed because mismatch in arrays length", async() => { + it("Should successfully provide the permissions in batch -- failed because mismatch in arrays length", async () => { await catchRevert( - I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], ["WHITELIST","CHANGE_PERMISSION"], [true], {from: token_owner}) + I_GeneralPermissionManager.changePermissionMulti( + account_delegate3, + [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], + [web3.utils.fromAscii("WHITELIST"), web3.utils.fromAscii("CHANGE_PERMISSION")], + [true], + { from: token_owner } + ) ); }); - it("Should successfully provide the permissions in batch", async() => { - let tx = await I_GeneralPermissionManager.changePermissionMulti(account_delegate3, [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], ["WHITELIST","CHANGE_PERMISSION"], [true, true], {from: token_owner}); + it("Should successfully provide the permissions in batch", async () => { + let tx = await I_GeneralPermissionManager.changePermissionMulti( + account_delegate3, + [I_GeneralTransferManager.address, I_GeneralPermissionManager.address], + [web3.utils.fromAscii("WHITELIST"), web3.utils.fromAscii("CHANGE_PERMISSION")], + [true, true], + { from: token_owner } + ); assert.equal(tx.logs[0].args._delegate, account_delegate3); - assert.isTrue(await I_GeneralPermissionManager.checkPermission.call(account_delegate3, I_GeneralTransferManager.address, "WHITELIST")); - assert.isTrue(await I_GeneralPermissionManager.checkPermission.call(account_delegate3, I_GeneralPermissionManager.address, "CHANGE_PERMISSION")); + assert.isTrue( + await I_GeneralPermissionManager.checkPermission.call(account_delegate3, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST")) + ); + assert.isTrue( + await I_GeneralPermissionManager.checkPermission.call( + account_delegate3, + I_GeneralPermissionManager.address, + web3.utils.fromAscii("CHANGE_PERMISSION") + ) + ); }); - it("Should provide all delegates with specified permission", async() => { - await I_GeneralPermissionManager.changePermission(account_delegate2, I_GeneralTransferManager.address, "WHITELIST", true, {from: token_owner}); - let tx = await I_GeneralPermissionManager.getAllDelegatesWithPerm.call(I_GeneralTransferManager.address, "WHITELIST"); + it("Should provide all delegates with specified permission", async () => { + await I_GeneralPermissionManager.changePermission(account_delegate2, I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"), true, { + from: token_owner + }); + let tx = await I_GeneralPermissionManager.getAllDelegatesWithPerm.call(I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST")); assert.equal(tx.length, 3); assert.equal(tx[0], account_delegate); assert.equal(tx[1], account_delegate2); }); - it("Should get all delegates for the permission manager", async() => { - let tx = await I_GeneralPermissionManager.getAllDelegatesWithPerm.call(I_GeneralPermissionManager.address, "CHANGE_PERMISSION"); + it("Should get all delegates for the permission manager", async () => { + let tx = await I_GeneralPermissionManager.getAllDelegatesWithPerm.call(I_GeneralPermissionManager.address, web3.utils.fromAscii("CHANGE_PERMISSION")); assert.equal(tx.length, 1); assert.equal(tx[0], account_delegate3); - }) + }); - it("Should return all modules and all permission", async() => { - let tx = await I_GeneralPermissionManager.getAllModulesAndPermsFromTypes.call(account_delegate3, [2,1]); + it("Should return all modules and all permission", async () => { + let tx = await I_GeneralPermissionManager.getAllModulesAndPermsFromTypes.call(account_delegate3, [2, 1]); assert.equal(tx[0][0], I_GeneralTransferManager.address); assert.equal(tx[1][0], "0x57484954454c4953540000000000000000000000000000000000000000000000"); assert.equal(tx[0][1], I_GeneralPermissionManager.address); assert.equal(tx[1][1], "0x4348414e47455f5045524d495353494f4e000000000000000000000000000000"); }); - }); describe("General Permission Manager Factory test cases", async () => { @@ -418,9 +443,9 @@ contract('GeneralPermissionManager', accounts => { assert.equal(tags.length, 0); }); - it("Should ge the version of the factory", async() => { + it("Should ge the version of the factory", async () => { let version = await I_GeneralPermissionManagerFactory.version.call(); assert.equal(version, "1.0.0"); - }) + }); }); }); diff --git a/test/h_general_transfer_manager.js b/test/h_general_transfer_manager.js index 3e060c288..a88b35370 100644 --- a/test/h_general_transfer_manager.js +++ b/test/h_general_transfer_manager.js @@ -1,23 +1,23 @@ import latestTime from "./helpers/latestTime"; import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; -import { signData } from "./helpers/signData"; +import { getSignGTMData } from "./helpers/signData"; import { pk } from "./helpers/testprivateKey"; import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; import { catchRevert } from "./helpers/exceptions"; import { setUpPolymathNetwork, deployGPMAndVerifyed, deployDummySTOAndVerifyed, deployGTMAndVerifyed } from "./helpers/createInstances"; - const DummySTO = artifacts.require("./DummySTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("GeneralTransferManager", accounts => { +contract("GeneralTransferManager", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -32,9 +32,9 @@ contract("GeneralTransferManager", accounts => { let account_affiliates2; // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); + let fromTime; + let toTime; + let expiryTime; let message = "Transaction Should Fail!"; @@ -58,6 +58,9 @@ contract("GeneralTransferManager", accounts => { let I_PolyToken; let I_PolymathRegistry; let P_GeneralTransferManagerFactory; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -72,17 +75,28 @@ contract("GeneralTransferManager", accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // Dummy STO details - const startTime = latestTime() + duration.seconds(5000); // Start time will be 5000 seconds more than the latest time - const endTime = startTime + duration.days(80); // Add 80 days more - const cap = web3.utils.toWei("10", "ether"); + let startTime; + let endTime; + const cap = new BN(web3.utils.toWei("10", "ether")); const someString = "A string which is not used"; const STOParameters = ["uint256", "uint256", "uint256", "string"]; + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + let signer; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); + fromTime = await latestTime(); + toTime = await latestTime(); + expiryTime = toTime + duration.days(15); + startTime = await latestTime() + duration.seconds(5000); // Start time will be 5000 seconds more than the latest time + endTime = startTime + duration.days(80); // Add 80 days more + account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -92,11 +106,18 @@ contract("GeneralTransferManager", accounts => { account_investor1 = accounts[8]; account_investor2 = accounts[9]; account_delegate = accounts[7]; + account_investor3 = accounts[5]; account_investor4 = accounts[6]; account_affiliates1 = accounts[3]; account_affiliates2 = accounts[4]; + let oneeth = new BN(web3.utils.toWei("1", "ether")); + signer = web3.eth.accounts.create(); + await web3.eth.personal.importRawKey(signer.privateKey, ""); + await web3.eth.personal.unlockAccount(signer.address, "", 6000); + await web3.eth.sendTransaction({ from: token_owner, to: signer.address, value: oneeth }); + // Step 1: Deploy the genral PM ecosystem let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -111,13 +132,15 @@ contract("GeneralTransferManager", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [P_GeneralTransferManagerFactory] = await deployGTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); - [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [P_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + [P_GeneralTransferManagerFactory] = await deployGTMAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); + [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, 0); + [P_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); // Printing all the contract addresses console.log(` @@ -148,15 +171,14 @@ contract("GeneralTransferManager", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -164,134 +186,200 @@ contract("GeneralTransferManager", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); - it("Should attach the paid GTM -- failed because of no tokens", async() => { + it("Should attach the paid GTM -- failed because of no tokens", async () => { await catchRevert( - I_SecurityToken.addModule(P_GeneralTransferManagerFactory.address, "", web3.utils.toWei("500"), 0, {from: account_issuer}) + I_SecurityToken.addModule(P_GeneralTransferManagerFactory.address, "0x0", new BN(web3.utils.toWei("2000")), new BN(0), { from: account_issuer }) ); - }) + }); - it("Should attach the paid GTM", async() => { + it("Should attach the paid GTM", async () => { let snap_id = await takeSnapshot(); - await I_PolyToken.getTokens(web3.utils.toWei("500"), I_SecurityToken.address); - await I_SecurityToken.addModule(P_GeneralTransferManagerFactory.address, "", web3.utils.toWei("500"), 0, {from: account_issuer}); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), I_SecurityToken.address); + await I_SecurityToken.addModule(P_GeneralTransferManagerFactory.address, "0x0", new BN(web3.utils.toWei("2000")), new BN(0), { + from: account_issuer + }); await revertToSnapshot(snap_id); - }) + }); + + it("Should add investor flags", async () => { + let snap_id = await takeSnapshot(); + await I_GeneralTransferManager.modifyInvestorFlagMulti([account_investor1, account_investor1, account_investor2], [0, 1, 1], [true, true, true], { from: account_issuer }); + let investors = await I_GeneralTransferManager.getInvestors(0, 1); + assert.equal(investors[0], account_investor1); + assert.equal(investors[1], account_investor2); + let investorCount = await stGetter.getInvestorCount(); + assert.equal(investorCount.toNumber(), 2); + let allInvestorFlags = await I_GeneralTransferManager.getAllInvestorFlags(); + assert.deepEqual(investors, allInvestorFlags[0]); + assert.equal(allInvestorFlags[1][0].toNumber(), 3)//0x000....00011 + assert.equal(allInvestorFlags[1][1].toNumber(), 2)//0x000....00010 + let investorFlags = await I_GeneralTransferManager.getInvestorFlags(allInvestorFlags[0][0]); + assert.equal(investorFlags, 3)//0x000....00011 + await revertToSnapshot(snap_id); + }); it("Should whitelist the affiliates before the STO attached", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelistMulti( + console.log(`Estimate gas of one Whitelist: + ${await I_GeneralTransferManager.modifyKYCData.estimateGas( + account_affiliates1, + currentTime + currentTime.add(new BN(duration.days(30))), + currentTime + currentTime.add(new BN(duration.days(90))), + currentTime + currentTime.add(new BN(duration.days(965))), + { + from: account_issuer + } + )}` + ); + let fromTime1 = currentTime + currentTime.add(new BN(duration.days(30))); + let fromTime2 = currentTime.add(new BN(duration.days(30))); + let toTime1 = currentTime + currentTime.add(new BN(duration.days(90))); + let toTime2 = currentTime.add(new BN(duration.days(90))); + let expiryTime1 = currentTime + currentTime.add(new BN(duration.days(965))); + let expiryTime2 = currentTime.add(new BN(duration.days(365))); + + let tx = await I_GeneralTransferManager.modifyKYCDataMulti( [account_affiliates1, account_affiliates2], - [latestTime() + duration.days(30), latestTime() + duration.days(30)], - [latestTime() + duration.days(90), latestTime() + duration.days(90)], - [latestTime() + duration.years(1), latestTime() + duration.years(1)], - [false, false], + [fromTime1, fromTime2], + [toTime1, toTime2], + [expiryTime1, expiryTime2], { from: account_issuer, gas: 6000000 } ); + await I_GeneralTransferManager.modifyInvestorFlagMulti([account_affiliates1, account_affiliates2], [1, 1], [true, true], { from: account_issuer }); assert.equal(tx.logs[0].args._investor, account_affiliates1); assert.equal(tx.logs[1].args._investor, account_affiliates2); + assert.deepEqual(await I_GeneralTransferManager.getAllInvestors.call(), [account_affiliates1, account_affiliates2]); + console.log(await I_GeneralTransferManager.getAllKYCData.call()); + let data = await I_GeneralTransferManager.getKYCData.call([account_affiliates1, account_affiliates2]); + assert.equal(data[0][0].toString(), fromTime1); + assert.equal(data[0][1].toString(), fromTime2); + assert.equal(data[1][0].toString(), toTime1); + assert.equal(data[1][1].toString(), toTime2); + assert.equal(data[2][0].toString(), expiryTime1); + assert.equal(data[2][1].toString(), expiryTime2); + assert.equal(await I_GeneralTransferManager.getInvestorFlag(account_affiliates1, 1), true); + assert.equal(await I_GeneralTransferManager.getInvestorFlag(account_affiliates2, 1), true); + }); + + it("Should whitelist lots of addresses and check gas", async () => { + let mockInvestors = []; + for (let i = 0; i < 50; i++) { + mockInvestors.push("0x1000000000000000000000000000000000000000".substring(0, 42 - i.toString().length) + i.toString()); + } + + let times = range1(50); + let bools = rangeB(50); + let tx = await I_GeneralTransferManager.modifyKYCDataMulti(mockInvestors, times, times, times, { + from: account_issuer + }); + console.log("Multi Whitelist x 50: " + tx.receipt.gasUsed); + assert.deepEqual( + await I_GeneralTransferManager.getAllInvestors.call(), + [account_affiliates1, account_affiliates2].concat(mockInvestors) + ); }); it("Should mint the tokens to the affiliates", async () => { - await I_SecurityToken.mintMulti([account_affiliates1, account_affiliates2], [100 * Math.pow(10, 18), 100 * Math.pow(10, 18)], { + console.log(` + Estimate gas cost for minting the tokens: ${await I_SecurityToken.issueMulti.estimateGas([account_affiliates1, account_affiliates2], [new BN(100).mul(new BN(10).pow(new BN(18))), new BN(10).pow(new BN(20))], { + from: account_issuer + })} + `) + await I_SecurityToken.issueMulti([account_affiliates1, account_affiliates2], [new BN(100).mul(new BN(10).pow(new BN(18))), new BN(10).pow(new BN(20))], { from: account_issuer, gas: 6000000 }); - assert.equal((await I_SecurityToken.balanceOf.call(account_affiliates1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 100); - assert.equal((await I_SecurityToken.balanceOf.call(account_affiliates2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 100); + assert.equal((await I_SecurityToken.balanceOf.call(account_affiliates1)).div(new BN(10).pow(new BN(18))).toNumber(), 100); + assert.equal((await I_SecurityToken.balanceOf.call(account_affiliates2)).div(new BN(10).pow(new BN(18))).toNumber(), 100); }); - it("Should successfully attach the STO factory with the security token -- failed because of no tokens", async () => { let bytesSTO = encodeModuleCall(STOParameters, [ - latestTime() + duration.seconds(1000), - latestTime() + duration.days(40), + await latestTime() + duration.seconds(1000), + await latestTime() + duration.days(40), cap, someString ]); await catchRevert( - I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, web3.utils.toWei("500"), 0, { from: token_owner }) + I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, new BN(web3.utils.toWei("2000")), new BN(0), { from: token_owner }) ); }); it("Should successfully attach the STO factory with the security token", async () => { let snap_id = await takeSnapshot(); let bytesSTO = encodeModuleCall(STOParameters, [ - latestTime() + duration.seconds(1000), - latestTime() + duration.days(40), + await latestTime() + duration.seconds(1000), + await latestTime() + duration.days(40), cap, someString ]); - await I_PolyToken.getTokens(web3.utils.toWei("500"), I_SecurityToken.address); - const tx = await I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, web3.utils.toWei("500"), 0, { from: token_owner }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), I_SecurityToken.address); + const tx = await I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, new BN(web3.utils.toWei("2000")), new BN(0), { + from: token_owner + }); assert.equal(tx.logs[3].args._types[0].toNumber(), stoKey, "DummySTO doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[3].args._name).replace(/\u0000/g, ""), "DummySTO", "DummySTOFactory module was not added" ); - I_DummySTO = DummySTO.at(tx.logs[3].args._module); + I_DummySTO = await DummySTO.at(tx.logs[3].args._module); await revertToSnapshot(snap_id); }); it("Should successfully attach the STO factory with the security token - invalid data", async () => { - let bytesSTO = encodeModuleCall(['uint256', 'string'], [ - latestTime() + duration.seconds(1000), - someString - ]); - await catchRevert( - I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, 0, 0, { from: token_owner }) - ); + let bytesSTO = encodeModuleCall(["uint256", "string"], [await latestTime() + duration.seconds(1000), someString]); + await catchRevert(I_SecurityToken.addModule(P_DummySTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner })); }); it("Should successfully attach the STO factory with the security token", async () => { let bytesSTO = encodeModuleCall(STOParameters, [ - latestTime() + duration.seconds(1000), - latestTime() + duration.days(40), + await latestTime() + duration.seconds(1000), + await latestTime() + duration.days(40), cap, someString ]); - const tx = await I_SecurityToken.addModule(I_DummySTOFactory.address, bytesSTO, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_DummySTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), stoKey, "DummySTO doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "DummySTO", "DummySTOFactory module was not added" ); - I_DummySTO = DummySTO.at(tx.logs[2].args._module); + I_DummySTO = await DummySTO.at(tx.logs[2].args._module); }); it("Should successfully attach the permission manager factory with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, 0, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "GeneralPermissionManager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "GeneralPermissionManager", "GeneralPermissionManager module was not added" ); - I_GeneralPermissionManager = GeneralPermissionManager.at(tx.logs[2].args._module); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); }); }); describe("Buy tokens using on-chain whitelist", async () => { it("Should buy the tokens -- Failed due to investor is not in the whitelist", async () => { - await catchRevert(I_DummySTO.generateTokens(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner })); + await catchRevert(I_DummySTO.generateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); }); it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -308,81 +396,156 @@ contract("GeneralTransferManager", accounts => { await increaseTime(5000); // Mint some tokens - await I_DummySTO.generateTokens(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + console.log( + `Gas usage of minting of tokens: ${await I_DummySTO.generateTokens.estimateGas(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })}` + ) + await I_DummySTO.generateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should fail in buying the token from the STO", async () => { - await catchRevert(I_DummySTO.generateTokens(account_affiliates1, web3.utils.toWei("1", "ether"), { from: token_owner })); + await catchRevert(I_DummySTO.generateTokens(account_affiliates1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); }); - it("Should fail in buying the tokens from the STO -- because amount is 0", async() => { - await catchRevert( - I_DummySTO.generateTokens(account_investor1, 0, { from: token_owner }) - ); + it("Should fail in buying the tokens from the STO -- because amount is 0", async () => { + await catchRevert(I_DummySTO.generateTokens(account_investor1, new BN(0), { from: token_owner })); }); - it("Should fail in buying the tokens from the STO -- because STO is paused", async() => { - await I_DummySTO.pause({from: account_issuer }); - await catchRevert(I_DummySTO.generateTokens(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner })); + it("Should fail in buying the tokens from the STO -- because STO is paused", async () => { + await I_DummySTO.pause({ from: account_issuer }); + await catchRevert(I_DummySTO.generateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); // Reverting the changes releated to pause - await I_DummySTO.unpause({from: account_issuer }); + await I_DummySTO.unpause({ from: account_issuer }); }); - it("Should buy more tokens from the STO to investor1", async() => { - await I_DummySTO.generateTokens(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("2", "ether")); + it("Should buy more tokens from the STO to investor1", async () => { + await I_DummySTO.generateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner }); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Should fail in investing the money in STO -- expiry limit reached", async () => { await increaseTime(duration.days(10)); - await catchRevert(I_DummySTO.generateTokens(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner })); + await catchRevert(I_DummySTO.generateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); + }); + }); + + describe("Buy tokens using on-chain whitelist and defaults", async () => { + // let snap_id; + + it("Should Buy the tokens", async () => { + // Add the Investor in to the whitelist + // snap_id = await takeSnapshot(); + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, new BN(0), new BN(0), currentTime.add(new BN(duration.days(20))), { + from: account_issuer, + gas: 6000000 + }); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor1.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + tx = await I_GeneralTransferManager.modifyKYCData( + account_investor2, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(20))), + { + from: account_issuer, + gas: 6000000 + } + ); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor2.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + // Jump time + await increaseTime(5000); + + // Can transfer tokens + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Add a from default and check transfers are disabled then enabled in the future", async () => { + let tx = await I_GeneralTransferManager.changeDefaults(currentTime.add(new BN(duration.days(12))), new BN(0), { from: token_owner }); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_investor2 }); + await catchRevert(I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 })); + await increaseTime(duration.days(5)); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); + }); + + it("Add a to default and check transfers are disabled then enabled in the future", async () => { + let tx = await I_GeneralTransferManager.changeDefaults(0, currentTime.add(new BN(duration.days(16))), { from: token_owner }); + await catchRevert(I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_investor2 })); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); + await increaseTime(duration.days(2)); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("2", "ether")), { from: account_investor2 }); + // revert changes + await I_GeneralTransferManager.modifyKYCData(account_investor2, new BN(0), new BN(0), new BN(0), { + from: account_issuer, + gas: 6000000 + }); + await I_GeneralTransferManager.changeDefaults(0, new BN(0), { from: token_owner }); }); }); describe("Buy tokens using off-chain whitelist", async () => { it("Should buy the tokens -- Failed due to investor is not in the whitelist", async () => { - await catchRevert(I_DummySTO.generateTokens(account_investor2, web3.utils.toWei("1", "ether"), { from: token_owner })); + await catchRevert(I_DummySTO.generateTokens(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); + }); + + it("Should provide the permission and change the signing address", async () => { + let log = await I_GeneralPermissionManager.addDelegate(account_delegate, web3.utils.fromAscii("My details"), { from: token_owner }); + assert.equal(log.logs[0].args._delegate, account_delegate); + + await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("FLAGS"), true, { + from: token_owner + }); + + assert.isTrue( + await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("FLAGS")) + ); + console.log(JSON.stringify(signer)); + let tx = await I_GeneralTransferManager.changeSigningAddress(signer.address, { from: account_delegate }); + assert.equal(tx.logs[0].args._signingAddress, signer.address); }); it("Should buy the tokens -- Failed due to incorrect signature input", async () => { // Add the Investor in to the whitelist //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk - let validFrom = latestTime(); - let validTo = latestTime() + duration.days(5); + let validFrom = await latestTime(); + let validTo = await latestTime() + duration.days(5); let nonce = 5; - const sig = signData( + const sig = getSignGTMData( account_investor2, account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, - token_owner_pk + signer.privateKey ); - const r = `0x${sig.r.toString("hex")}`; - const s = `0x${sig.s.toString("hex")}`; - const v = sig.v; - await catchRevert( - I_GeneralTransferManager.modifyWhitelistSigned( + I_GeneralTransferManager.modifyKYCDataSigned( account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, - v, - r, - s, + sig, { from: account_investor2, gas: 6000000 @@ -394,39 +557,31 @@ contract("GeneralTransferManager", accounts => { it("Should buy the tokens -- Failed due to incorrect signature timing", async () => { // Add the Investor in to the whitelist //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk - let validFrom = latestTime() - 100; - let validTo = latestTime() - 1; + let validFrom = await latestTime() - 100; + let validTo = await latestTime() - 1; let nonce = 5; - const sig = signData( + const sig = getSignGTMData( I_GeneralTransferManager.address, account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, - token_owner_pk + signer.privateKey ); - const r = `0x${sig.r.toString("hex")}`; - const s = `0x${sig.s.toString("hex")}`; - const v = sig.v; - await catchRevert( - I_GeneralTransferManager.modifyWhitelistSigned( + I_GeneralTransferManager.modifyKYCDataSigned( account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, - v, - r, - s, + sig, { from: account_investor2, gas: 6000000 @@ -438,39 +593,31 @@ contract("GeneralTransferManager", accounts => { it("Should buy the tokens -- Failed due to incorrect signature signer", async () => { // Add the Investor in to the whitelist //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk - let validFrom = latestTime(); - let validTo = latestTime() + 60 * 60; + let validFrom = await latestTime(); + let validTo = await latestTime() + 60 * 60; let nonce = 5; - const sig = signData( + const sig = getSignGTMData( account_investor2, account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, "2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200" ); - const r = `0x${sig.r.toString("hex")}`; - const s = `0x${sig.s.toString("hex")}`; - const v = sig.v; - await catchRevert( - I_GeneralTransferManager.modifyWhitelistSigned( + I_GeneralTransferManager.modifyKYCDataSigned( account_investor2, fromTime, toTime, expiryTime, - true, validFrom, validTo, nonce, - v, - r, - s, + sig, { from: account_investor2, gas: 6000000 @@ -479,41 +626,33 @@ contract("GeneralTransferManager", accounts => { ); }); - it("Should Buy the tokens", async () => { + it("Should Buy the tokens with signers signature", async () => { // Add the Investor in to the whitelist //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk - let validFrom = latestTime(); - let validTo = latestTime() + duration.days(5); + let validFrom = await latestTime(); + let validTo = await latestTime() + duration.days(5); let nonce = 5; - const sig = signData( + const sig = getSignGTMData( I_GeneralTransferManager.address, account_investor2, - latestTime(), - latestTime() + duration.days(80), + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), expiryTime + duration.days(200), - true, validFrom, validTo, nonce, - token_owner_pk + signer.privateKey ); - const r = `0x${sig.r.toString("hex")}`; - const s = `0x${sig.s.toString("hex")}`; - const v = sig.v; - - let tx = await I_GeneralTransferManager.modifyWhitelistSigned( + let tx = await I_GeneralTransferManager.modifyKYCDataSigned( account_investor2, - latestTime(), - latestTime() + duration.days(80), + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), expiryTime + duration.days(200), - true, validFrom, validTo, nonce, - v, - r, - s, + sig, { from: account_investor2, gas: 6000000 @@ -530,53 +669,81 @@ contract("GeneralTransferManager", accounts => { await increaseTime(10000); // Mint some tokens - await I_DummySTO.generateTokens(account_investor2, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_DummySTO.generateTokens(account_investor2, new BN(web3.utils.toWei("1", "ether")), { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); - it("Should fail if the txn is generated with same nonce", async() => { + it("Should fail if the txn is generated with same nonce", async () => { // Add the Investor in to the whitelist //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk - let validFrom = latestTime(); - let validTo = latestTime() + duration.days(5); + let validFrom = await latestTime(); + let validTo = await latestTime() + duration.days(5); let nonce = 5; - const sig = signData( + const sig = getSignGTMData( I_GeneralTransferManager.address, account_investor2, - latestTime(), - latestTime() + duration.days(80), + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), expiryTime + duration.days(200), - true, validFrom, validTo, nonce, - token_owner_pk + signer.privateKey ); - const r = `0x${sig.r.toString("hex")}`; - const s = `0x${sig.s.toString("hex")}`; - const v = sig.v; + await catchRevert( + I_GeneralTransferManager.modifyKYCDataSigned( + account_investor2, + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), + expiryTime + duration.days(200), + validFrom, + validTo, + nonce, + sig, + { + from: account_investor2, + gas: 6000000 + } + ) + ); + }); - await catchRevert(I_GeneralTransferManager.modifyWhitelistSigned( + it("Should sign with token owner key", async () => { + // Add the Investor in to the whitelist + //tmAddress, investorAddress, fromTime, toTime, validFrom, validTo, pk + let validFrom = await latestTime(); + let validTo = await latestTime() + duration.days(5); + let nonce = 6; + const sig = getSignGTMData( + I_GeneralTransferManager.address, account_investor2, - latestTime(), - latestTime() + duration.days(80), + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), expiryTime + duration.days(200), - true, validFrom, validTo, nonce, - v, - r, - s, + "0x" + token_owner_pk + ); + + await I_GeneralTransferManager.modifyKYCDataSigned( + account_investor2, + currentTime.toNumber(), + currentTime.add(new BN(duration.days(100))).toNumber(), + expiryTime + duration.days(200), + validFrom, + validTo, + nonce, + sig, { from: account_investor2, gas: 6000000 } ) - ); - }) + + }); it("Should fail in changing the signing address", async () => { await catchRevert(I_GeneralTransferManager.changeSigningAddress(account_polymath, { from: account_investor4 })); @@ -588,46 +755,30 @@ contract("GeneralTransferManager", accounts => { assert.equal(web3.utils.toAscii(perm[1]).replace(/\u0000/g, ""), "FLAGS"); }); - it("Should provide the permission and change the signing address", async() => { - let log = await I_GeneralPermissionManager.addDelegate(account_delegate, "My details", {from: token_owner}); - assert.equal(log.logs[0].args._delegate, account_delegate); - - await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, "FLAGS", true, { - from: token_owner - }); - - assert.isTrue( - await I_GeneralPermissionManager.checkPermission.call(account_delegate, I_GeneralTransferManager.address, "FLAGS") - ); - - let tx = await I_GeneralTransferManager.changeSigningAddress(account_polymath, { from: account_delegate }); - assert.equal(tx.logs[0].args._signingAddress, account_polymath); - }); - it("Should fail to pull fees as no budget set", async () => { - await catchRevert(I_GeneralTransferManager.takeFee(web3.utils.toWei("1", "ether"), { from: account_polymath })); + await catchRevert(I_GeneralTransferManager.takeFee(new BN(web3.utils.toWei("1", "ether")), { from: account_polymath })); }); it("Should set a budget for the GeneralTransferManager", async () => { - await I_SecurityToken.changeModuleBudget(I_GeneralTransferManager.address, 10 * Math.pow(10, 18), true, { from: token_owner }); + await I_SecurityToken.changeModuleBudget(I_GeneralTransferManager.address, new BN(10).pow(new BN(19)), true, { from: token_owner }); - await catchRevert(I_GeneralTransferManager.takeFee(web3.utils.toWei("1", "ether"), { from: token_owner })); - await I_PolyToken.getTokens(10 * Math.pow(10, 18), token_owner); - await I_PolyToken.transfer(I_SecurityToken.address, 10 * Math.pow(10, 18), { from: token_owner }); + await catchRevert(I_GeneralTransferManager.takeFee(new BN(web3.utils.toWei("1", "ether")), { from: token_owner })); + await I_PolyToken.getTokens(new BN(10).pow(new BN(19)), token_owner); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(10).pow(new BN(19)), { from: token_owner }); }); it("Factory owner should pull fees - fails as not permissioned by issuer", async () => { - await catchRevert(I_GeneralTransferManager.takeFee(web3.utils.toWei("1", "ether"), { from: account_delegate })); + await catchRevert(I_GeneralTransferManager.takeFee(new BN(web3.utils.toWei("1", "ether")), { from: account_delegate })); }); it("Factory owner should pull fees", async () => { - await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, "FEE_ADMIN", true, { + await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, web3.utils.fromAscii("FEE_ADMIN"), true, { from: token_owner }); let balanceBefore = await I_PolyToken.balanceOf(account_polymath); - await I_GeneralTransferManager.takeFee(web3.utils.toWei("1", "ether"), { from: account_delegate }); + await I_GeneralTransferManager.takeFee(new BN(web3.utils.toWei("1", "ether")), { from: account_delegate }); let balanceAfter = await I_PolyToken.balanceOf(account_polymath); - assert.equal(balanceBefore.add(web3.utils.toWei("1", "ether")).toNumber(), balanceAfter.toNumber(), "Fee is transferred"); + assert.equal(balanceBefore.add(new BN(web3.utils.toWei("1", "ether"))).toString(), balanceAfter.toString(), "Fee is transferred"); }); it("Should change the white list transfer variable", async () => { @@ -638,7 +789,7 @@ contract("GeneralTransferManager", accounts => { it("should failed in trasfering the tokens", async () => { let tx = await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(true, { from: token_owner }); await I_GeneralTransferManager.pause({ from: token_owner }); - await catchRevert(I_SecurityToken.transfer(account_investor1, web3.utils.toWei("2", "ether"), { from: account_investor2 })); + await catchRevert(I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("2", "ether")), { from: account_investor2 })); }); it("Should change the Issuance address", async () => { @@ -660,17 +811,16 @@ contract("GeneralTransferManager", accounts => { describe("WhiteList that addresses", async () => { it("Should fail in adding the investors in whitelist", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(20); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); let expiryTime = toTime + duration.days(10); await catchRevert( - I_GeneralTransferManager.modifyWhitelistMulti( + I_GeneralTransferManager.modifyKYCDataMulti( [account_investor3, account_investor4], [fromTime, fromTime], [toTime, toTime], [expiryTime, expiryTime], - [true, true], { from: account_delegate, gas: 6000000 @@ -680,17 +830,16 @@ contract("GeneralTransferManager", accounts => { }); it("Should fail in adding the investors in whitelist -- array length mismatch", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(20); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); let expiryTime = toTime + duration.days(10); await catchRevert( - I_GeneralTransferManager.modifyWhitelistMulti( + I_GeneralTransferManager.modifyKYCDataMulti( [account_investor3, account_investor4], [fromTime], [toTime, toTime], [expiryTime, expiryTime], - [true, true], { from: account_delegate, gas: 6000000 @@ -700,17 +849,16 @@ contract("GeneralTransferManager", accounts => { }); it("Should fail in adding the investors in whitelist -- array length mismatch", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(20); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); let expiryTime = toTime + duration.days(10); await catchRevert( - I_GeneralTransferManager.modifyWhitelistMulti( + I_GeneralTransferManager.modifyKYCDataMulti( [account_investor3, account_investor4], [fromTime, fromTime], [toTime], [expiryTime, expiryTime], - [true, true], { from: account_delegate, gas: 6000000 @@ -720,17 +868,16 @@ contract("GeneralTransferManager", accounts => { }); it("Should fail in adding the investors in whitelist -- array length mismatch", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(20); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); let expiryTime = toTime + duration.days(10); await catchRevert( - I_GeneralTransferManager.modifyWhitelistMulti( + I_GeneralTransferManager.modifyKYCDataMulti( [account_investor3, account_investor4], [fromTime, fromTime], [toTime, toTime], [expiryTime], - [true, true], { from: account_delegate, gas: 6000000 @@ -740,16 +887,15 @@ contract("GeneralTransferManager", accounts => { }); it("Should successfully add the investors in whitelist", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(20); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); let expiryTime = toTime + duration.days(10); - let tx = await I_GeneralTransferManager.modifyWhitelistMulti( + let tx = await I_GeneralTransferManager.modifyKYCDataMulti( [account_investor3, account_investor4], [fromTime, fromTime], [toTime, toTime], [expiryTime, expiryTime], - [true, true], { from: token_owner, gas: 6000000 @@ -779,7 +925,7 @@ contract("GeneralTransferManager", accounts => { "Allows an issuer to maintain a time based whitelist of authorised token holders.Addresses are added via modifyWhitelist and take a fromTime (the time from which they can send tokens) and a toTime (the time from which they can receive tokens). There are additional flags, allowAllWhitelistIssuances, allowAllWhitelistTransfers & allowAllTransfers which allow you to set corresponding contract level behaviour. Init function takes no parameters.", "Wrong Module added" ); - assert.equal(await I_GeneralPermissionManagerFactory.version.call(), "1.0.0"); + assert.equal(await I_GeneralTransferManagerFactory.version.call(), "2.1.0"); }); it("Should get the tags of the factory", async () => { @@ -807,7 +953,7 @@ contract("GeneralTransferManager", accounts => { assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ""), "Dummy"); }); - it("Should get the version of factory", async() => { + it("Should get the version of factory", async () => { let version = await I_DummySTOFactory.version.call(); assert.equal(version, "1.0.0"); }); @@ -815,11 +961,11 @@ contract("GeneralTransferManager", accounts => { describe("Test cases for the get functions of the dummy sto", async () => { it("Should get the raised amount of ether", async () => { - assert.equal(await I_DummySTO.getRaised.call(0), web3.utils.toWei("0", "ether")); + assert.equal((await I_DummySTO.getRaised.call(0)).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Should get the raised amount of poly", async () => { - assert.equal((await I_DummySTO.getRaised.call(1)).toNumber(), web3.utils.toWei("0", "ether")); + assert.equal((await I_DummySTO.getRaised.call(1)).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); it("Should get the investors", async () => { @@ -831,8 +977,18 @@ contract("GeneralTransferManager", accounts => { assert.equal(web3.utils.toAscii(tx[0]).replace(/\u0000/g, ""), "ADMIN"); }); - it("Should get the amount of tokens sold", async() => { + it("Should get the amount of tokens sold", async () => { assert.equal(await I_DummySTO.getTokensSold.call(), 0); - }) + }); }); }); + +function range1(i) { + return i ? range1(i - 1).concat(i) : []; +} +function rangeB(i) { + return i ? rangeB(i - 1).concat(0) : []; +} + +function range1(i) {return i?range1(i-1).concat(i):[]} +function rangeB(i) {return i?rangeB(i-1).concat(0):[]} diff --git a/test/helpers/contracts/PolyToken.sol b/test/helpers/contracts/PolyToken.sol index e14d5d663..b9863b0c2 100644 --- a/test/helpers/contracts/PolyToken.sol +++ b/test/helpers/contracts/PolyToken.sol @@ -1,11 +1,9 @@ -pragma solidity ^0.4.24; +pragma solidity ^0.5.0; -import "openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; - -contract PolyToken is MintableToken { - - constructor () public { +contract PolyToken is ERC20Mintable { + constructor() public { } diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 2021d350e..76733d8fe 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -1,37 +1,57 @@ import { encodeProxyCall, encodeModuleCall } from "./encodeCall"; const PolymathRegistry = artifacts.require("./PolymathRegistry.sol"); +const MockOracle = artifacts.require("./MockOracle.sol"); +const StableOracle = artifacts.require("./StableOracle.sol"); const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); -const SecurityToken = artifacts.require("./SecurityToken.sol"); const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); +const CappedSTO = artifacts.require("./CappedSTO.sol"); const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryProxy.sol"); const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); const SecurityTokenRegistryMock = artifacts.require("./SecurityTokenRegistryMock.sol"); +const ERC20DividendCheckpoint = artifacts.require("./ERC20DividendCheckpoint.sol"); +const EtherDividendCheckpoint = artifacts.require("./EtherDividendCheckpoint.sol"); const ERC20DividendCheckpointFactory = artifacts.require("./ERC20DividendCheckpointFactory.sol"); const EtherDividendCheckpointFactory = artifacts.require("./EtherDividendCheckpointFactory.sol"); +const ManualApprovalTransferManager = artifacts.require("./ManualApprovalTransferManager.sol"); const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApprovalTransferManagerFactory.sol"); -const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionTMFactory.sol'); const TrackedRedemptionFactory = artifacts.require("./TrackedRedemptionFactory.sol"); const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); +const PercentageTransferManager = artifacts.require("./PercentageTransferManager.sol"); +const BlacklistTransferManagerFactory = artifacts.require("./BlacklistTransferManagerFactory.sol"); const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); -const USDTieredSTOProxyFactory = artifacts.require("./USDTieredSTOProxyFactory"); -const ManualApprovalTransferManager = artifacts.require("./ManualApprovalTransferManager"); +const USDTieredSTO = artifacts.require("./USDTieredSTO"); const FeatureRegistry = artifacts.require("./FeatureRegistry.sol"); const STFactory = artifacts.require("./STFactory.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager.sol"); const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager.sol"); const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); +const CountTransferManager = artifacts.require("./CountTransferManager.sol"); const CountTransferManagerFactory = artifacts.require("./CountTransferManagerFactory.sol"); -const VolumeRestrictionTransferManagerFactory = artifacts.require("./LockupVolumeRestrictionTMFactory"); +const LockUpTransferManagerFactory = artifacts.require("./LockUpTransferManagerFactory"); const PreSaleSTOFactory = artifacts.require("./PreSaleSTOFactory.sol"); +const PreSaleSTO = artifacts.require("./PreSaleSTO.sol"); const PolyToken = artifacts.require("./PolyToken.sol"); const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); const DummySTOFactory = artifacts.require("./DummySTOFactory.sol"); +const DummySTO = artifacts.require("./DummySTO.sol"); const MockBurnFactory = artifacts.require("./MockBurnFactory.sol"); +const STRGetter = artifacts.require("./STRGetter.sol"); const MockWrongTypeFactory = artifacts.require("./MockWrongTypeFactory.sol"); +const STGetter = artifacts.require("./STGetter.sol"); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); +const SignedTransferManagerFactory = artifacts.require("./SignedTransferManagerFactory"); +const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFactory.sol"); +const VolumeRestrictionTM = artifacts.require("./VolumeRestrictionTM.sol"); +const VestingEscrowWalletFactory = artifacts.require("./VestingEscrowWalletFactory.sol"); +const VestingEscrowWallet = artifacts.require("./VestingEscrowWallet.sol"); const Web3 = require("web3"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port // Contract Instance Declaration @@ -41,33 +61,54 @@ let I_TrackedRedemptionFactory; let I_ScheduledCheckpointFactory; let I_MockBurnFactory; let I_MockWrongTypeBurnFactory; -let I_SingleTradeVolumeRestrictionManagerFactory; +let I_ManualApprovalTransferManagerLogic; let I_ManualApprovalTransferManagerFactory; let I_VolumeRestrictionTransferManagerFactory; +let I_PercentageTransferManagerLogic; let I_PercentageTransferManagerFactory; +let I_EtherDividendCheckpointLogic; let I_EtherDividendCheckpointFactory; +let I_CountTransferManagerLogic; let I_CountTransferManagerFactory; +let I_ERC20DividendCheckpointLogic; let I_ERC20DividendCheckpointFactory; +let I_GeneralPermissionManagerLogic; +let I_VolumeRestrictionTMFactory; let I_GeneralPermissionManagerFactory; +let I_GeneralTransferManagerLogic; let I_GeneralTransferManagerFactory; +let I_VestingEscrowWalletFactory; let I_GeneralTransferManager; +let I_VolumeRestrictionTMLogic; let I_ModuleRegistryProxy; +let I_PreSaleSTOLogic; let I_PreSaleSTOFactory; let I_ModuleRegistry; let I_FeatureRegistry; let I_SecurityTokenRegistry; +let I_CappedSTOLogic; let I_CappedSTOFactory; let I_SecurityToken; +let I_DummySTOLogic; let I_DummySTOFactory; let I_PolyToken; let I_STFactory; +let I_USDTieredSTOLogic; let I_PolymathRegistry; let I_SecurityTokenRegistryProxy; +let I_BlacklistTransferManagerFactory; +let I_VestingEscrowWalletLogic; let I_STRProxied; let I_MRProxied; +let I_STRGetter; +let I_STGetter; +let I_SignedTransferManagerFactory; +let I_USDOracle; +let I_POLYOracle; +let I_StablePOLYOracle; // Initial fee for ticker registry and security token registry -const initRegFee = web3.utils.toWei("250"); +const initRegFee = new BN(web3.utils.toWei("250")); const STRProxyParameters = ["address", "address", "uint256", "uint256", "address", "address"]; const MRProxyParameters = ["address", "address"]; @@ -82,7 +123,9 @@ export async function setUpPolymathNetwork(account_polymath, token_owner) { let b = await deployFeatureRegistry(account_polymath); // STEP 3: Deploy the ModuleRegistry let c = await deployModuleRegistry(account_polymath); - // STEP 4: Deploy the GeneralTransferManagerFactory + // STEP 4a: Deploy the GeneralTransferManagerFactory + let logic = await deployGTMLogic(account_polymath); + // STEP 4b: Deploy the GeneralTransferManagerFactory let d = await deployGTM(account_polymath); // Step 6: Deploy the STversionProxy contract let e = await deploySTFactory(account_polymath); @@ -92,18 +135,52 @@ export async function setUpPolymathNetwork(account_polymath, token_owner) { await setInPolymathRegistry(account_polymath); // STEP 9: Register the Modules with the ModuleRegistry contract await registerGTM(account_polymath); - let tempArray = new Array(a, b, c, d, e, f); - return mergeReturn(tempArray); + // STEP 10: Add dummy oracles + await addOracles(account_polymath); + + let tempArray = new Array( + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STRGetter, + I_STGetter, + I_USDOracle, + I_POLYOracle, + I_StablePOLYOracle + ); + return Promise.all(tempArray); } +export async function addOracles(account_polymath) { + let USDETH = new BN(500).mul(new BN(10).pow(new BN(18))); // 500 USD/ETH + let USDPOLY = new BN(25).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY + let StableChange = new BN(10).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY + I_USDOracle = await MockOracle.new("0x0000000000000000000000000000000000000000", web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), USDETH, { from: account_polymath }); // 500 dollars per POLY + I_POLYOracle = await MockOracle.new(I_PolyToken.address, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), USDPOLY, { from: account_polymath }); // 25 cents per POLY + I_StablePOLYOracle = await StableOracle.new(I_POLYOracle.address, StableChange, { from: account_polymath }); + await I_PolymathRegistry.changeAddress("EthUsdOracle", I_USDOracle.address, { from: account_polymath }); + await I_PolymathRegistry.changeAddress("PolyUsdOracle", I_POLYOracle.address, { from: account_polymath }); + await I_PolymathRegistry.changeAddress("StablePolyUsdOracle", I_StablePOLYOracle.address, { from: account_polymath }); +} -async function deployPolyRegistryAndPolyToken(account_polymath, token_owner) { +export async function deployPolyRegistryAndPolyToken(account_polymath, token_owner) { // Step 0: Deploy the PolymathRegistry I_PolymathRegistry = await PolymathRegistry.new({ from: account_polymath }); // Step 1: Deploy the token Faucet and Mint tokens for token_owner - I_PolyToken = await PolyTokenFaucet.new(); - await I_PolyToken.getTokens(10000 * Math.pow(10, 18), token_owner); + I_PolyToken = await PolyTokenFaucet.new({ from: account_polymath }); + + await I_PolyToken.getTokens(new BN(10000).mul(new BN(10).pow(new BN(18))), token_owner); + + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { from: account_polymath }); return new Array(I_PolymathRegistry, I_PolyToken); } @@ -127,8 +204,26 @@ async function deployModuleRegistry(account_polymath) { return new Array(I_ModuleRegistry, I_ModuleRegistryProxy, I_MRProxied); } +async function deployGTMLogic(account_polymath) { + I_GeneralTransferManagerLogic = await GeneralTransferManager.new( + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + { from: account_polymath } + ); + + assert.notEqual( + I_GeneralTransferManagerLogic.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerLogic contract was not deployed" + ); + + return new Array(I_GeneralTransferManagerLogic); +} + async function deployGTM(account_polymath) { - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { from: account_polymath }); + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(new BN(0), new BN(0), I_GeneralTransferManagerLogic.address, I_PolymathRegistry.address, { + from: account_polymath + }); assert.notEqual( I_GeneralTransferManagerFactory.address.valueOf(), @@ -140,11 +235,14 @@ async function deployGTM(account_polymath) { } async function deploySTFactory(account_polymath) { - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + I_STGetter = await STGetter.new({from: account_polymath}); + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, I_STGetter.address, { from: account_polymath }); assert.notEqual(I_STFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", "STFactory contract was not deployed"); - return new Array(I_STFactory); + return new Array(I_STFactory, I_STGetter); } async function deploySTR(account_polymath) { @@ -158,18 +256,20 @@ async function deploySTR(account_polymath) { // Step 9 (a): Deploy the proxy I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ from: account_polymath }); + I_STRGetter = await STRGetter.new({from: account_polymath}); + let bytesProxy = encodeProxyCall(STRProxyParameters, [ I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }); - I_STRProxied = SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - - return new Array(I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, I_STRProxied); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + I_STRGetter = await STRGetter.at(I_SecurityTokenRegistryProxy.address); + return new Array(I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, I_STRProxied, I_STRGetter); } async function setInPolymathRegistry(account_polymath) { @@ -192,22 +292,41 @@ async function registerAndVerifyByMR(factoryAdrress, owner, mr) { /// Deploy the TransferManagers -export async function deployGTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployGTMAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(setupCost, new BN(0), I_GeneralTransferManagerLogic.address, I_PolymathRegistry.address, { + from: accountPolymath + }); assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), + I_GeneralTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", - "GeneralPermissionManagerFactory contract was not deployed" + "GeneralTransferManagerFactory contract was not deployed" ); // (B) : Register the GeneralDelegateManagerFactory await registerAndVerifyByMR(I_GeneralTransferManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_GeneralTransferManagerFactory); + return Promise.all(new Array(I_GeneralTransferManagerFactory)); } -export async function deployCountTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_CountTransferManagerFactory = await CountTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployVRTMAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_VolumeRestrictionTMLogic = await VolumeRestrictionTM.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + + I_VolumeRestrictionTMFactory = await VolumeRestrictionTMFactory.new(setupCost, new BN(0), I_VolumeRestrictionTMLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); + + assert.notEqual( + I_VolumeRestrictionTMFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "VolumeRestrictionTMFactory contract was not deployed" + ); + + // (B) : Register the GeneralDelegateManagerFactory + await registerAndVerifyByMR(I_VolumeRestrictionTMFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_VolumeRestrictionTMFactory); +} + +export async function deployCountTMAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_CountTransferManagerLogic = await CountTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_CountTransferManagerFactory = await CountTransferManagerFactory.new(setupCost, new BN(0), I_CountTransferManagerLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_CountTransferManagerFactory.address.valueOf(), @@ -216,11 +335,12 @@ export async function deployCountTMAndVerifyed(accountPolymath, MRProxyInstance, ); await registerAndVerifyByMR(I_CountTransferManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_CountTransferManagerFactory); + return Promise.all(new Array(I_CountTransferManagerFactory)); } -export async function deployManualApprovalTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_ManualApprovalTransferManagerFactory = await ManualApprovalTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployManualApprovalTMAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_ManualApprovalTransferManagerLogic = await ManualApprovalTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_ManualApprovalTransferManagerFactory = await ManualApprovalTransferManagerFactory.new(setupCost, new BN(0), ManualApprovalTransferManager.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_ManualApprovalTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", @@ -228,11 +348,12 @@ export async function deployManualApprovalTMAndVerifyed(accountPolymath, MRProxy ); await registerAndVerifyByMR(I_ManualApprovalTransferManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_ManualApprovalTransferManagerFactory); + return Promise.all(new Array(I_ManualApprovalTransferManagerFactory)); } -export async function deployPercentageTMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_PercentageTransferManagerFactory = await PercentageTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployPercentageTMAndVerified(accountPolymath, MRProxyInstance, setupCost) { + I_PercentageTransferManagerLogic = await PercentageTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_PercentageTransferManagerFactory = await PercentageTransferManagerFactory.new(setupCost, new BN(0), I_PercentageTransferManagerLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_PercentageTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", @@ -240,35 +361,38 @@ export async function deployPercentageTMAndVerified(accountPolymath, MRProxyInst ); await registerAndVerifyByMR(I_PercentageTransferManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_PercentageTransferManagerFactory); + return Promise.all(new Array(I_PercentageTransferManagerFactory)); } -export async function deployLockupVolumeRTMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_VolumeRestrictionTransferManagerFactory = await VolumeRestrictionTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployBlacklistTMAndVerified(accountPolymath, MRProxyInstance, setupCost) { + + I_BlacklistTransferManagerFactory = await BlacklistTransferManagerFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( - I_VolumeRestrictionTransferManagerFactory.address.valueOf(), + I_BlacklistTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", - "VolumeRestrictionTransferManagerFactory contract was not deployed" + "BlacklistTransferManagerFactory contract was not deployed" ); - await registerAndVerifyByMR(I_VolumeRestrictionTransferManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_VolumeRestrictionTransferManagerFactory); + await registerAndVerifyByMR(I_BlacklistTransferManagerFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_BlacklistTransferManagerFactory); } -export async function deploySingleTradeVolumeRMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployLockupVolumeRTMAndVerified(accountPolymath, MRProxyInstance, setupCost) { + I_VolumeRestrictionTransferManagerFactory = await LockUpTransferManagerFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { + from: accountPolymath + }); assert.notEqual( - I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + I_VolumeRestrictionTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + "LockUpTransferManagerFactory contract was not deployed" ); - await registerAndVerifyByMR(I_SingleTradeVolumeRestrictionManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_SingleTradeVolumeRestrictionManagerFactory); + await registerAndVerifyByMR(I_VolumeRestrictionTransferManagerFactory.address, accountPolymath, MRProxyInstance); + return Promise.all(new Array(I_VolumeRestrictionTransferManagerFactory)); } -export async function deployScheduleCheckpointAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployScheduleCheckpointAndVerified(accountPolymath, MRProxyInstance, setupCost) { + I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_ScheduledCheckpointFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", @@ -276,13 +400,14 @@ export async function deployScheduleCheckpointAndVerified(accountPolymath, MRPro ); await registerAndVerifyByMR(I_ScheduledCheckpointFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_ScheduledCheckpointFactory); + return Promise.all(new Array(I_ScheduledCheckpointFactory)); } /// Deploy the Permission Manager -export async function deployGPMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployGPMAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_GeneralPermissionManagerLogic = await GeneralPermissionManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(setupCost, new BN(0), I_GeneralPermissionManagerLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_GeneralPermissionManagerFactory.address.valueOf(), @@ -292,14 +417,14 @@ export async function deployGPMAndVerifyed(accountPolymath, MRProxyInstance, pol // (B) : Register the GeneralDelegateManagerFactory await registerAndVerifyByMR(I_GeneralPermissionManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_GeneralPermissionManagerFactory); + return Promise.all(new Array(I_GeneralPermissionManagerFactory)); } - /// Deploy the STO Modules -export async function deployDummySTOAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_DummySTOFactory = await DummySTOFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployDummySTOAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_DummySTOLogic = await DummySTO.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_DummySTOFactory = await DummySTOFactory.new(setupCost, new BN(0), I_DummySTOLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_DummySTOFactory.address.valueOf(), @@ -307,11 +432,12 @@ export async function deployDummySTOAndVerifyed(accountPolymath, MRProxyInstance "DummySTOFactory contract was not deployed" ); await registerAndVerifyByMR(I_DummySTOFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_DummySTOFactory); + return Promise.all(new Array(I_DummySTOFactory)); } -export async function deployCappedSTOAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_CappedSTOFactory = await CappedSTOFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployCappedSTOAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_CappedSTOLogic = await CappedSTO.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_CappedSTOFactory = await CappedSTOFactory.new(setupCost, new BN(0), I_CappedSTOLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_CappedSTOFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", @@ -319,12 +445,12 @@ export async function deployCappedSTOAndVerifyed(accountPolymath, MRProxyInstanc ); await registerAndVerifyByMR(I_CappedSTOFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_CappedSTOFactory); - + return Promise.all(new Array(I_CappedSTOFactory)); } -export async function deployPresaleSTOAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_PreSaleSTOFactory = await PreSaleSTOFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployPresaleSTOAndVerified(accountPolymath, MRProxyInstance, setupCost) { + I_PreSaleSTOLogic = await PreSaleSTO.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_PreSaleSTOFactory = await PreSaleSTOFactory.new(setupCost, new BN(0), I_PreSaleSTOLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_PreSaleSTOFactory.address.valueOf(), @@ -333,13 +459,17 @@ export async function deployPresaleSTOAndVerified(accountPolymath, MRProxyInstan ); await registerAndVerifyByMR(I_PreSaleSTOFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_PreSaleSTOFactory); + return Promise.all(new Array(I_PreSaleSTOFactory)); } -export async function deployUSDTieredSTOAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_USDTieredSTOProxyFactory = await USDTieredSTOProxyFactory.new({from: accountPolymath}); +export async function deployUSDTieredSTOAndVerified(accountPolymath, MRProxyInstance, setupCost) { + I_USDTieredSTOLogic = await USDTieredSTO.new( + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + { from: accountPolymath } + ); - I_USDTieredSTOFactory = await USDTieredSTOFactory.new(polyToken, setupCost, 0, 0, I_USDTieredSTOProxyFactory.address, { from: accountPolymath }); + I_USDTieredSTOFactory = await USDTieredSTOFactory.new(setupCost, new BN(0), I_USDTieredSTOLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_USDTieredSTOFactory.address.valueOf(), @@ -348,14 +478,20 @@ export async function deployUSDTieredSTOAndVerified(accountPolymath, MRProxyInst ); await registerAndVerifyByMR(I_USDTieredSTOFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_USDTieredSTOFactory); + return Promise.all(new Array(I_USDTieredSTOFactory)); } - /// Deploy the Dividend Modules -export async function deployERC20DividendAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_ERC20DividendCheckpointFactory = await ERC20DividendCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployERC20DividendAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_ERC20DividendCheckpointLogic = await ERC20DividendCheckpoint.new( + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + { from: accountPolymath } + ); + I_ERC20DividendCheckpointFactory = await ERC20DividendCheckpointFactory.new(setupCost, new BN(0), I_ERC20DividendCheckpointLogic.address, I_PolymathRegistry.address, { + from: accountPolymath + }); assert.notEqual( I_ERC20DividendCheckpointFactory.address.valueOf(), @@ -363,11 +499,18 @@ export async function deployERC20DividendAndVerifyed(accountPolymath, MRProxyIns "ERC20DividendCheckpointFactory contract was not deployed" ); await registerAndVerifyByMR(I_ERC20DividendCheckpointFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_ERC20DividendCheckpointFactory); + return Promise.all(new Array(I_ERC20DividendCheckpointFactory)); } -export async function deployEtherDividendAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_EtherDividendCheckpointFactory = await EtherDividendCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployEtherDividendAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_EtherDividendCheckpointLogic = await EtherDividendCheckpoint.new( + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + { from: accountPolymath } + ); + I_EtherDividendCheckpointFactory = await EtherDividendCheckpointFactory.new(setupCost, new BN(0), I_EtherDividendCheckpointLogic.address, I_PolymathRegistry.address, { + from: accountPolymath + }); assert.notEqual( I_EtherDividendCheckpointFactory.address.valueOf(), @@ -376,14 +519,13 @@ export async function deployEtherDividendAndVerifyed(accountPolymath, MRProxyIns ); await registerAndVerifyByMR(I_EtherDividendCheckpointFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_EtherDividendCheckpointFactory); + return Promise.all(new Array(I_EtherDividendCheckpointFactory)); } - /// Deploy the Burn Module -export async function deployRedemptionAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_TrackedRedemptionFactory = await TrackedRedemptionFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployRedemptionAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_TrackedRedemptionFactory = await TrackedRedemptionFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_TrackedRedemptionFactory.address.valueOf(), @@ -392,12 +534,25 @@ export async function deployRedemptionAndVerifyed(accountPolymath, MRProxyInstan ); await registerAndVerifyByMR(I_TrackedRedemptionFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_TrackedRedemptionFactory); + return Promise.all(new Array(I_TrackedRedemptionFactory)); } +export async function deployVestingEscrowWalletAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_VestingEscrowWalletLogic = await VestingEscrowWallet.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_VestingEscrowWalletFactory = await VestingEscrowWalletFactory.new(setupCost, new BN(0), I_VestingEscrowWalletLogic.address, I_PolymathRegistry.address, { from: accountPolymath }); + + assert.notEqual( + I_VestingEscrowWalletFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "VestingEscrowWalletFactory contract was not deployed" + ); + + await registerAndVerifyByMR(I_VestingEscrowWalletFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_VestingEscrowWalletFactory); +} -export async function deployMockRedemptionAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_MockBurnFactory = await MockBurnFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployMockRedemptionAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_MockBurnFactory = await MockBurnFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_MockBurnFactory.address.valueOf(), @@ -406,11 +561,11 @@ export async function deployMockRedemptionAndVerifyed(accountPolymath, MRProxyIn ); await registerAndVerifyByMR(I_MockBurnFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_MockBurnFactory); + return Promise.all(new Array(I_MockBurnFactory)); } -export async function deployMockWrongTypeRedemptionAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_MockWrongTypeBurnFactory = await MockWrongTypeFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); +export async function deployMockWrongTypeRedemptionAndVerifyed(accountPolymath, MRProxyInstance, setupCost) { + I_MockWrongTypeBurnFactory = await MockWrongTypeFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); assert.notEqual( I_MockWrongTypeBurnFactory.address.valueOf(), @@ -419,18 +574,17 @@ export async function deployMockWrongTypeRedemptionAndVerifyed(accountPolymath, ); await registerAndVerifyByMR(I_MockWrongTypeBurnFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_MockWrongTypeBurnFactory); + return Promise.all(new Array(I_MockWrongTypeBurnFactory)); } +export async function deploySignedTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { + I_SignedTransferManagerFactory = await SignedTransferManagerFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); + assert.notEqual( + I_SignedTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SignedTransferManagerFactory contract was not deployed" + ); - -/// Helper function -function mergeReturn(returnData) { - let returnArray = new Array(); - for (let i = 0; i < returnData.length; i++) { - for (let j = 0; j < returnData[i].length; j++) { - returnArray.push(returnData[i][j]); - } - } - return returnArray; + await registerAndVerifyByMR(I_SignedTransferManagerFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_SignedTransferManagerFactory); } diff --git a/test/helpers/exceptions.js b/test/helpers/exceptions.js index 22c05be07..ea0327af8 100644 --- a/test/helpers/exceptions.js +++ b/test/helpers/exceptions.js @@ -25,6 +25,9 @@ module.exports = { catchRevert: async function(promise) { await tryCatch(promise, "revert"); }, + catchPermission: async function(promise) { + await tryCatch(promise, "revert Permission check failed"); + }, catchOutOfGas: async function(promise) { await tryCatch(promise, "out of gas"); }, diff --git a/test/helpers/latestTime.js b/test/helpers/latestTime.js index 38c744969..58671e254 100644 --- a/test/helpers/latestTime.js +++ b/test/helpers/latestTime.js @@ -1,4 +1,9 @@ // Returns the time of the last mined block in seconds -export default function latestTime() { - return web3.eth.getBlock("latest").timestamp; +export default async function latestTime() { + let block = await latestBlock(); + return block.timestamp; +} + +async function latestBlock() { + return web3.eth.getBlock("latest"); } diff --git a/test/helpers/signData.js b/test/helpers/signData.js index ea7476cc3..649f8a2c5 100644 --- a/test/helpers/signData.js +++ b/test/helpers/signData.js @@ -1,21 +1,20 @@ -const ethers = require("ethers"); -const utils = ethers.utils; -const ethUtil = require("ethereumjs-util"); +const Web3 = require("web3"); +let BN = Web3.utils.BN; -//this, _investor, _fromTime, _toTime, _validTo -function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, nonce, pk) { - let packedData = utils - .solidityKeccak256( - ["address", "address", "uint256", "uint256", "uint256", "bool", "uint256", "uint256", "uint256"], - [tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, nonce] - ) - .slice(2); - packedData = new Buffer(packedData, "hex"); - packedData = Buffer.concat([new Buffer(`\x19Ethereum Signed Message:\n${packedData.length.toString()}`), packedData]); - packedData = web3.sha3(`0x${packedData.toString("hex")}`, { encoding: "hex" }); - return ethUtil.ecsign(new Buffer(packedData.slice(2), "hex"), new Buffer(pk, "hex")); +function getSignSTMData(tmAddress, nonce, expiry, fromAddress, toAddress, amount, pk) { + let hash = web3.utils.soliditySha3({t: 'address', v: tmAddress}, {t: 'uint256', v: new BN(nonce)}, {t: 'uint256', v: new BN(expiry)}, {t: 'address', v: fromAddress}, {t: 'address', v: toAddress}, {t: 'uint256', v: new BN(amount)}); + let signature = (web3.eth.accounts.sign(hash, pk)).signature; + let data = web3.eth.abi.encodeParameters(['address', 'uint256', 'uint256', 'bytes'], [tmAddress, new BN(nonce).toString(), new BN(expiry).toString(), signature]); + return data; +} + +function getSignGTMData(tmAddress, investorAddress, fromTime, toTime, expiryTime, validFrom, validTo, nonce, pk) { + let hash = web3.utils.soliditySha3({t: 'address', v: tmAddress}, {t: 'address', v: investorAddress}, {t: 'uint256', v: new BN(fromTime)}, {t: 'uint256', v: new BN(toTime)}, {t: 'uint256', v: new BN(expiryTime)}, {t: 'uint256', v: new BN(validFrom)}, {t: 'uint256', v: new BN(validTo)}, {t: 'uint256', v: new BN(nonce)}); + let signature = (web3.eth.accounts.sign(hash, pk)); + return signature.signature; } module.exports = { - signData + getSignSTMData, + getSignGTMData }; diff --git a/test/helpers/time.js b/test/helpers/time.js index 3e3362d81..2d665219c 100644 --- a/test/helpers/time.js +++ b/test/helpers/time.js @@ -2,38 +2,44 @@ // aren’t included within the original RPC specification. // See https://github.com/ethereumjs/testrpc#implemented-methods -function increaseTime(duration) { - const id = Date.now(); - - return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync( - { - jsonrpc: "2.0", - method: "evm_increaseTime", - params: [duration], - id: id - }, - err1 => { - if (err1) return reject(err1); +async function advanceBlock() { + return new Promise((resolve, reject) => { + web3.currentProvider.send({ + jsonrpc: '2.0', + method: 'evm_mine', + }, + (err, result) => { + if (err) { + return reject(err); + } + resolve(result.result); + } + ); + }); +} - web3.currentProvider.sendAsync( - { - jsonrpc: "2.0", - method: "evm_mine", - id: id + 1 - }, - (err2, res) => { - return err2 ? reject(err2) : resolve(res); - } - ); - } - ); - }); +// Increases ganache time by the passed duration in seconds +async function increaseTime(duration) { + await new Promise((resolve, reject) => { + web3.currentProvider.send({ + jsonrpc: '2.0', + method: 'evm_increaseTime', + params: [duration], + }, + (err, result) => { + if (err) { + return reject(err); + } + resolve(result.result); + } + ); + }); + await advanceBlock(); } export default function takeSnapshot() { return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync( + web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_snapshot", @@ -51,9 +57,9 @@ export default function takeSnapshot() { }); } -function revertToSnapshot(snapShotId) { +async function revertToSnapshot(snapShotId) { return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync( + web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_revert", diff --git a/test/helpers/utils.js b/test/helpers/utils.js index a2c23c218..b89d8b9ee 100644 --- a/test/helpers/utils.js +++ b/test/helpers/utils.js @@ -49,25 +49,7 @@ export const duration = { } }; -/** - * Helper to wait for log emission. - * @param {Object} _event The event to wait for. - */ -export function promisifyLogWatch(_event, _times) { - return new Promise((resolve, reject) => { - let i = 0; - _event.watch((error, log) => { - if (error !== null) reject(error); - i = i + 1; - console.log("Received event: " + i + " out of: " + _times); - if (i == _times) { - _event.stopWatching(); - resolve(log); - } - }); - }); -} - -export function latestBlock() { - return web3.eth.getBlock("latest").number; +export async function latestBlock() { + let block = await web3.eth.getBlock("latest"); + return block.number; } diff --git a/test/i_Issuance.js b/test/i_Issuance.js index ac1fcdd37..fda71fc93 100644 --- a/test/i_Issuance.js +++ b/test/i_Issuance.js @@ -9,12 +9,13 @@ const CappedSTO = artifacts.require("./CappedSTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("Issuance", accounts => { +contract("Issuance", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -26,12 +27,12 @@ contract("Issuance", accounts => { let blockNo; let balanceOfReceiver; let message = "Transaction Should Fail!"; - const TM_Perm = "WHITELIST"; - const delegateDetails = "I am delegate"; + const TM_Perm = web3.utils.fromAscii("WHITELIST"); + const delegateDetails = web3.utils.fromAscii("I am delegate"); // investor Details - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); - let expiryTime = toTime + duration.days(100); + let fromTime; + let toTime; + let expiryTime; // Contract Instance Declaration let I_GeneralPermissionManagerFactory; @@ -51,6 +52,9 @@ contract("Issuance", accounts => { let I_CappedSTO; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details (Launched ST on the behalf of the issuer) const name = "Demo Token"; @@ -64,23 +68,28 @@ contract("Issuance", accounts => { const stoKey = 3; const budget = 0; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // Capped STO details //let startTime; // Start time will be 5000 seconds more than the latest time //let endTime; // Add 30 days more - const cap = web3.utils.toWei("10000"); - const rate = 1000; + const cap = new BN(web3.utils.toWei("10000")); + const rate = new BN(web3.utils.toWei("1000")); const fundRaiseType = [0]; - const cappedSTOSetupCost = web3.utils.toWei("20000", "ether"); - const maxCost = cappedSTOSetupCost; + const cappedSTOSetupCost = new BN(web3.utils.toWei("20000", "ether")); + const cappedSTOSetupCostPOLY = new BN(web3.utils.toWei("80000", "ether")); + const maxCost = cappedSTOSetupCostPOLY; const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; const STRProxyParameters = ["address", "address", "uint256", "uint256", "address", "address"]; const MRProxyParameters = ["address", "address"]; before(async () => { + fromTime = await latestTime(); + toTime = await latestTime(); + expiryTime = toTime + duration.days(15); // Accounts setup account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -104,13 +113,15 @@ contract("Issuance", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 2: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 3: Deploy the CappedSTOFactory - [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, 0); // Printing all the contract addresses console.log(` @@ -132,11 +143,9 @@ contract("Issuance", accounts => { }); describe("Launch SecurityToken & STO on the behalf of the issuer", async () => { - describe("Create securityToken for the issuer by the polymath", async () => { - it("POLYMATH: Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.getTokens(10000 * Math.pow(10, 18), account_polymath); + await I_PolyToken.getTokens(new BN(10000).mul(new BN(10).pow(new BN(18))), account_polymath); await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_polymath }); let tx = await I_STRProxied.registerTicker(account_polymath, symbol, name, { from: account_polymath }); assert.equal(tx.logs[0].args._owner, account_polymath); @@ -145,15 +154,15 @@ contract("Issuance", accounts => { it("POLYMATH: Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_polymath }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: account_polymath }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); @@ -161,38 +170,28 @@ contract("Issuance", accounts => { }); it("POLYMATH: Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("POLYMATH: Should successfully attach the STO factory with the security token", async () => { // STEP 4: Deploy the CappedSTOFactory - I_CappedSTOFactory = await CappedSTOFactory.new(I_PolyToken.address, cappedSTOSetupCost, 0, 0, { from: account_polymath }); - - assert.notEqual( - I_CappedSTOFactory.address.valueOf(), - address_zero, - "CappedSTOFactory contract was not deployed" - ); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); + [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, cappedSTOSetupCost); let bytesSTO = encodeModuleCall(STOParameters, [ - latestTime() + duration.seconds(5000), - latestTime() + duration.days(30), + await latestTime() + duration.seconds(5000), + await latestTime() + duration.days(30), cap, rate, fundRaiseType, account_fundsReceiver ]); - await I_PolyToken.getTokens(cappedSTOSetupCost, account_polymath); - await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCost, { from: account_polymath }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, account_polymath); + await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCostPOLY, { from: account_polymath }); - const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: account_polymath }); + const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: account_polymath }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal( @@ -200,22 +199,21 @@ contract("Issuance", accounts => { "CappedSTO", "CappedSTOFactory module was not added" ); - I_CappedSTO = CappedSTO.at(tx.logs[3].args._module); + I_CappedSTO = await CappedSTO.at(tx.logs[3].args._module); }); }); describe("Transfer Manager operations by the polymath_account", async () => { it("Should modify the whitelist", async () => { - fromTime = latestTime(); - toTime = latestTime() + duration.days(15); + fromTime = await latestTime(); + toTime = await latestTime() + duration.days(15); expiryTime = toTime + duration.days(100); - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, fromTime + duration.days(70), toTime + duration.days(90), expiryTime + duration.days(50), - true, { from: account_polymath } @@ -223,17 +221,21 @@ contract("Issuance", accounts => { assert.equal(tx.logs[0].args._investor, account_investor1, "Failed in adding the investor in whitelist"); }); - it("Should add the delegate with permission", async() => { - //First attach a permission manager to the token - await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "", 0, 0, {from: account_polymath}); - let moduleData = (await I_SecurityToken.getModulesByType(permissionManagerKey))[0]; - I_GeneralPermissionManager = GeneralPermissionManager.at(moduleData); - // Add permission to the deletgate (A regesteration process) - await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: account_polymath}); - // Providing the permission to the delegate - await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, TM_Perm, true, { from: account_polymath }); - - assert.isTrue(await I_GeneralPermissionManager.checkPermission(account_delegate, I_GeneralTransferManager.address, TM_Perm)); + it("Should add the delegate with permission", async () => { + //First attach a permission manager to the token + await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: account_polymath }); + let moduleData = (await stGetter.getModulesByType(permissionManagerKey))[0]; + I_GeneralPermissionManager = await GeneralPermissionManager.at(moduleData); + // Add permission to the deletgate (A regesteration process) + await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: account_polymath }); + // Providing the permission to the delegate + await I_GeneralPermissionManager.changePermission(account_delegate, I_GeneralTransferManager.address, TM_Perm, true, { + from: account_polymath + }); + + assert.isTrue( + await I_GeneralPermissionManager.checkPermission(account_delegate, I_GeneralTransferManager.address, TM_Perm) + ); }); it("POLYMATH: Should change the ownership of the SecurityToken", async () => { @@ -246,7 +248,7 @@ contract("Issuance", accounts => { describe("Operations on the STO", async () => { it("Should Buy the tokens", async () => { balanceOfReceiver = await web3.eth.getBalance(account_fundsReceiver); - blockNo = latestBlock(); + blockNo = await latestBlock(); // Jump time await increaseTime(5000); // Fallback transaction @@ -254,24 +256,24 @@ contract("Issuance", accounts => { from: account_investor1, to: I_CappedSTO.address, gas: 6100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); - assert.equal((await I_CappedSTO.getRaised.call(0)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1); + assert.equal((await I_CappedSTO.getRaised.call(0)).div(new BN(10).pow(new BN(18))).toNumber(), 1); assert.equal(await I_CappedSTO.investorCount.call(), 1); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); it("Verification of the event Token Purchase", async () => { - const log = await promisifyLogWatch(I_CappedSTO.TokenPurchase({ from: blockNo }), 1); + const log = (await I_CappedSTO.getPastEvents('TokenPurchase', {filter: {from: blockNo}}))[0]; assert.equal(log.args.purchaser, account_investor1, "Wrong address of the investor"); - assert.equal(log.args.amount.dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000, "Wrong No. token get dilivered"); + assert.equal(log.args.amount.div(new BN(10).pow(new BN(18))).toNumber(), 1000, "Wrong No. token get dilivered"); }); it("should add the investor into the whitelist by the delegate", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor2, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor2, fromTime, toTime, expiryTime, { from: account_delegate, gas: 7000000 }); @@ -283,14 +285,14 @@ contract("Issuance", accounts => { from: account_investor2, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); - assert.equal((await I_CappedSTO.getRaised.call(0)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 2); + assert.equal((await I_CappedSTO.getRaised.call(0)).div(new BN(10).pow(new BN(18))).toNumber(), 2); assert.equal(await I_CappedSTO.investorCount.call(), 2); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); }); }); diff --git a/test/j_manual_approval_transfer_manager.js b/test/j_manual_approval_transfer_manager.js index 709991b92..c7509043c 100644 --- a/test/j_manual_approval_transfer_manager.js +++ b/test/j_manual_approval_transfer_manager.js @@ -10,9 +10,10 @@ const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const ManualApprovalTransferManager = artifacts.require("./ManualApprovalTransferManager"); const CountTransferManager = artifacts.require("./CountTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port contract("ManualApprovalTransferManager", accounts => { @@ -55,6 +56,9 @@ contract("ManualApprovalTransferManager", accounts => { let I_SecurityToken; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -68,12 +72,16 @@ contract("ManualApprovalTransferManager", accounts => { const transferManagerKey = 2; const stoKey = 3; + let expiryTimeMA; + let approvalTime; + // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = web3.utils.toWei("1000"); const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; - + let currentTime; before(async () => { + currentTime = new BN(await latestTime()); // Accounts setup account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -100,17 +108,19 @@ contract("ManualApprovalTransferManager", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 2: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 3: Deploy the ManualApprovalTransferManagerFactory - [I_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 4: Deploy the Paid ManualApprovalTransferManagerFactory - [P_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); + [P_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, web3.utils.toWei("500", "ether")); // STEP 5: Deploy the CountTransferManagerFactory - [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, 0); // Printing all the contract addresses console.log(` @@ -141,24 +151,24 @@ contract("ManualApprovalTransferManager", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(log.args._types[0].toString(), 2); assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); }); @@ -166,12 +176,11 @@ contract("ManualApprovalTransferManager", accounts => { it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 6000000 @@ -186,22 +195,21 @@ contract("ManualApprovalTransferManager", accounts => { // Jump time await increaseTime(5000); - + currentTime = new BN(await latestTime()); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("4", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("30", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("4", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), web3.utils.toWei("30", "ether")); }); it("Should Buy some more tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 6000000 @@ -215,15 +223,15 @@ contract("ManualApprovalTransferManager", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("10", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), web3.utils.toWei("10", "ether")); }); it("Should successfully attach the ManualApprovalTransferManager with the security token", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await I_PolyToken.getTokens(web3.utils.toWei("2000", "ether"), token_owner); await catchRevert( - I_SecurityToken.addModule(P_ManualApprovalTransferManagerFactory.address, "0x", web3.utils.toWei("500", "ether"), 0, { + I_SecurityToken.addModule(P_ManualApprovalTransferManagerFactory.address, "0x0", web3.utils.toWei("2000", "ether"), 0, { from: token_owner }) ); @@ -231,43 +239,42 @@ contract("ManualApprovalTransferManager", accounts => { it("Should successfully attach the General permission manager factory with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("2000", "ether"), { from: token_owner }); const tx = await I_SecurityToken.addModule( P_ManualApprovalTransferManagerFactory.address, - "0x", - web3.utils.toWei("500", "ether"), + "0x0", + web3.utils.toWei("2000", "ether"), 0, { from: token_owner } ); - assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "Manual Approval Transfer Manager doesn't get deployed"); + assert.equal(tx.logs[3].args._types[0].toString(), transferManagerKey, "Manual Approval Transfer Manager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[3].args._name).replace(/\u0000/g, ""), "ManualApprovalTransferManager", "ManualApprovalTransferManagerFactory module was not added" ); - P_ManualApprovalTransferManagerFactory = ManualApprovalTransferManager.at(tx.logs[3].args._module); + P_ManualApprovalTransferManagerFactory = await ManualApprovalTransferManager.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the ManualApprovalTransferManager with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_ManualApprovalTransferManagerFactory.address, "", 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "ManualApprovalTransferManager doesn't get deployed"); + const tx = await I_SecurityToken.addModule(I_ManualApprovalTransferManagerFactory.address, "0x0", 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toString(), transferManagerKey, "ManualApprovalTransferManager doesn't get deployed"); assert.equal( web3.utils.toUtf8(tx.logs[2].args._name), "ManualApprovalTransferManager", "ManualApprovalTransferManager module was not added" ); - I_ManualApprovalTransferManager = ManualApprovalTransferManager.at(tx.logs[2].args._module); + I_ManualApprovalTransferManager = await ManualApprovalTransferManager.at(tx.logs[2].args._module); }); - //function verifyTransfer(address _from, address _to, uint256 _amount, bool _isTransfer) public returns(Result) { - it("Cannot call verifyTransfer on the TM directly if _isTransfer == true", async () => { + + it("Cannot call executeTransfer on the TM directly", async () => { await catchRevert( - I_ManualApprovalTransferManager.verifyTransfer( + I_ManualApprovalTransferManager.executeTransfer( account_investor4, account_investor4, web3.utils.toWei("2", "ether"), - "", - true, + "0x0", { from: token_owner } ) ); @@ -278,19 +285,17 @@ contract("ManualApprovalTransferManager", accounts => { account_investor4, account_investor4, web3.utils.toWei("2", "ether"), - "", - false, + "0x0", { from: token_owner } ); }); it("Add a new token holder", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -306,9 +311,9 @@ contract("ManualApprovalTransferManager", accounts => { await I_ManualApprovalTransferManager.pause({from: token_owner}); // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("10", "ether")),"0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), web3.utils.toWei("10", "ether")); // Unpause at the transferManager level await I_ManualApprovalTransferManager.unpause({from: token_owner}); }); @@ -318,28 +323,17 @@ contract("ManualApprovalTransferManager", accounts => { // Mint some tokens await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_investor2 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("5", "ether")); - }); - - it("Should fail to add a manual approval because invalid _from address", async () => { - await catchRevert( - I_ManualApprovalTransferManager.addManualApproval( - "", - account_investor4, - web3.utils.toWei("2", "ether"), - latestTime() + duration.days(1), - { from: token_owner } - ) - ); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), web3.utils.toWei("31", "ether")); }); it("Should fail to add a manual approval because invalid _to address", async () => { await catchRevert( I_ManualApprovalTransferManager.addManualApproval( account_investor1, - "", + "0x0000000000000000000000000000000000000000", web3.utils.toWei("2", "ether"), - latestTime() + duration.days(1), + currentTime.add(new BN(duration.days(1))), + web3.utils.fromAscii("DESCRIPTION"), { from: token_owner } ) ); @@ -352,153 +346,438 @@ contract("ManualApprovalTransferManager", accounts => { account_investor4, web3.utils.toWei("2", "ether"), 99999, + web3.utils.fromAscii("DESCRIPTION"), { from: token_owner } ) ); }); - it("Add a manual approval for a 4th investor", async () => { + it("Add a manual approval for a 4th investor & return correct length", async () => { + approvalTime = currentTime.add(new BN(duration.days(1))); await I_ManualApprovalTransferManager.addManualApproval( account_investor1, account_investor4, - web3.utils.toWei("2", "ether"), - latestTime() + duration.days(1), - { from: token_owner } + web3.utils.toWei("3", "ether"), + approvalTime, + web3.utils.fromAscii("DESCRIPTION"), + { + from: token_owner + } ); + assert.equal((await I_ManualApprovalTransferManager.getTotalApprovalsLength.call()).toString(), 1); }); - it("Should fail to add a manual approval because allowance is laready exists", async () => { + it("Should return all approvals correctly", async () => { + + console.log("current approval length is " + (await I_ManualApprovalTransferManager.getTotalApprovalsLength.call()).toString()); + + let tx = await I_ManualApprovalTransferManager.getAllApprovals({from: token_owner }); + assert.equal(tx[0][0], account_investor1); + console.log("1"); + assert.equal(tx[1][0], account_investor4); + console.log("2"); + assert.equal(tx[2][0], web3.utils.toWei("3")); + console.log("3"); + assert.equal(tx[3][0].toString(), approvalTime); + console.log("4"); + assert.equal(web3.utils.toUtf8(tx[4][0]), "DESCRIPTION"); + }) + + it("Should try to add the same manual approval for the same `_from` & `_to` address", async() => { await catchRevert( I_ManualApprovalTransferManager.addManualApproval( account_investor1, account_investor4, - web3.utils.toWei("2", "ether"), - latestTime() + duration.days(5), - { from: token_owner } + web3.utils.toWei("5", "ether"), + currentTime.add(new BN(duration.days(1))), + web3.utils.fromAscii("DESCRIPTION"), + { + from: token_owner + } ) ); + }) + + it("Check verifyTransfer without actually transferring", async () => { + let verified = await I_SecurityToken.canTransfer.call( + account_investor4, + web3.utils.toWei("2", "ether"), + "0x0", + { + from: account_investor1 + } + ); + console.log(JSON.stringify(verified[0])); + assert.equal(verified[0], true); + + verified = await I_SecurityToken.canTransfer.call(account_investor4, web3.utils.toWei("4", "ether"), "0x0", { + from: account_investor1 + }); + assert.equal(verified[0], false); + + verified = await I_SecurityToken.canTransfer.call(account_investor4, web3.utils.toWei("1", "ether"), "0x0", { + from: account_investor1 + }); + assert.equal(verified[0], true); }); - it("Should fail to revoke manual approval because invalid _from address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualApproval("", account_investor4, { from: token_owner })); + it("Should fail to sell the tokens more than the allowance", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor4, web3.utils.toWei("4"), {from: account_investor1}) + ); + }) + + it("Approval fails with wrong from to address", async () => { + await catchRevert(I_SecurityToken.transfer(account_investor5, web3.utils.toWei("1", "ether"), { from: account_investor1 })); }); - it("Should fail to revoke manual approval because invalid _to address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, "", { from: token_owner })); + it("Should sell the tokens to investor 4 (GTM will give INVALID as investor 4 not in the whitelist)", async() => { + let oldBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1"), {from: account_investor1}); + let newBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + assert.equal((newBal4.sub(oldBal4)).div(new BN(10).pow(new BN(18))).toString(), 1); }); - it("Should revoke manual approval", async () => { - let tx = await I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, { - from: token_owner - }); - assert.equal(tx.logs[0].args._from, account_investor1); - assert.equal(tx.logs[0].args._to, account_investor4); - assert.equal(tx.logs[0].args._addedBy, token_owner); + it("Should sell more tokens to investor 4 with in the same day(GTM will give INVALID as investor 4 not in the whitelist)", async() => { + let oldBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1"), {from: account_investor1}); + let newBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + assert.equal((newBal4.sub(oldBal4)).div(new BN(10).pow(new BN(18))).toString(), 1); + + + let tx = await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor1); + let tx2 = await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor4); + + assert.equal(tx2[0].length, 1); + assert.equal(tx[0].length, 1); + }); + + it("Should fail to transact after the approval get expired", async() => { + await increaseTime(duration.days(1)); + currentTime = new BN(await latestTime()); + await catchRevert( + I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1"), {from: account_investor1}) + ); + }); + + it("Should fail to modify the manual approval when the approval get expired", async() => { + await catchRevert( + I_ManualApprovalTransferManager.modifyManualApproval( + account_investor1, + account_investor4, + currentTime.add(new BN(duration.days(2))), + web3.utils.toWei("5"), + web3.utils.fromAscii("New Description"), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should attach the manual approval for the investor4 again", async() => { + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor4))[0].length, 0); + currentTime = new BN(await latestTime()); await I_ManualApprovalTransferManager.addManualApproval( account_investor1, account_investor4, web3.utils.toWei("2", "ether"), - latestTime() + duration.days(1), - { from: token_owner } + currentTime.add(new BN(duration.days(1))), + web3.utils.fromAscii("DESCRIPTION"), + { + from: token_owner + } ); + assert.equal((await I_ManualApprovalTransferManager.getTotalApprovalsLength.call()).toString(), 1); + let data = await I_ManualApprovalTransferManager.approvals.call(0); + assert.equal(data[0], account_investor1); + assert.equal(data[1], account_investor4); + assert.equal(data[2], web3.utils.toWei("2")); + assert.equal(web3.utils.toUtf8(data[4]), "DESCRIPTION"); + }); + + it("Should modify the manual approval expiry time for 4th investor", async () => { + currentTime = new BN(await latestTime()); + expiryTimeMA = currentTime.add(new BN(duration.days(3))); + let tx = await I_ManualApprovalTransferManager.modifyManualApproval( + account_investor1, + account_investor4, + expiryTimeMA, + web3.utils.toWei("5"), + web3.utils.fromAscii("New Description"), + 45, + { + from: token_owner + } + ); + + let data = await I_ManualApprovalTransferManager.approvals.call(0); + assert.equal(data[0], account_investor1); + assert.equal(data[1], account_investor4); + assert.equal(data[2], web3.utils.toWei("2")); + assert.equal(data[3].toString(), expiryTimeMA); + assert.equal(web3.utils.toUtf8(data[4]), "New Description"); + assert.equal(tx.logs[0].args._from, account_investor1); + assert.equal(tx.logs[0].args._to, account_investor4); + assert.equal((tx.logs[0].args._expiryTime).toString(), expiryTimeMA); + assert.equal((tx.logs[0].args._allowance).toString(), web3.utils.toWei("2")); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._description), "New Description"); }); - it("Use 50% of manual approval for transfer", async () => { - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1", "ether"), { from: account_investor1 }); - - assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei("1", "ether")); + it("Should transact after two days", async() => { + await increaseTime(2); + currentTime = new BN(await latestTime()); + let oldBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1"), {from: account_investor1}); + let newBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + assert.equal((newBal4.sub(oldBal4)).div(new BN(10).pow(new BN(18))).toString(), 1); }); - it("Check verifyTransfer without actually transferring", async () => { - let verified = await I_SecurityToken.verifyTransfer.call( + it("Should modify the allowance of the manual approval (increase)", async() => { + currentTime = new BN(await latestTime()); + await I_ManualApprovalTransferManager.modifyManualApproval( account_investor1, account_investor4, - web3.utils.toWei("1", "ether"), - "" + expiryTimeMA, + web3.utils.toWei("4"), + web3.utils.fromAscii("New Description"), + 1, + { + from: token_owner + } ); - console.log(JSON.stringify(verified)); - assert.equal(verified, true); - verified = await I_SecurityToken.verifyTransfer.call(account_investor1, account_investor4, web3.utils.toWei("2", "ether"), ""); - assert.equal(verified, false); - - verified = await I_SecurityToken.verifyTransfer.call(account_investor1, account_investor4, web3.utils.toWei("1", "ether"), ""); - assert.equal(verified, true); + let data = await I_ManualApprovalTransferManager.approvals.call(0); + assert.equal(data[0], account_investor1); + assert.equal(data[1], account_investor4); + assert.equal(data[2].toString(), web3.utils.toWei("5")); + assert.equal(data[3].toString(), expiryTimeMA); + assert.equal(web3.utils.toUtf8(data[4]), "New Description"); }); - it("Use remaining 50% of manual approval for transfer", async () => { - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1", "ether"), { from: account_investor1 }); - - assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei("2", "ether")); + it("Should transact according to new allowance", async() => { + currentTime = new BN(await latestTime()); + let oldBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("3"), {from: account_investor1}); + let newBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + assert.equal((newBal4.sub(oldBal4)).div(new BN(10).pow(new BN(18))).toString(), 3); }); - it("Check further transfers fail", async () => { - await catchRevert(I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1", "ether"), { from: account_investor1 })); + it("Should decrease the allowance", async() => { + currentTime = new BN(await latestTime()); + await I_ManualApprovalTransferManager.modifyManualApproval( + account_investor1, + account_investor4, + expiryTimeMA, + web3.utils.toWei("1"), + web3.utils.fromAscii("New Description"), + 0, + { + from: token_owner + } + ); - //Check that other transfers are still valid - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); + let data = await I_ManualApprovalTransferManager.approvals.call(0); + assert.equal(data[0], account_investor1); + assert.equal(data[1], account_investor4); + assert.equal(data[2].toString(), web3.utils.toWei("1")); + assert.equal(data[3].toString(), expiryTimeMA); + assert.equal(web3.utils.toUtf8(data[4]), "New Description"); }); - it("Should fail to add a manual block because invalid _from address", async () => { + it("Should fail to transfer the tokens because allowance get changed", async() => { await catchRevert( - I_ManualApprovalTransferManager.addManualBlocking("", account_investor2, latestTime() + duration.days(1), { - from: token_owner - }) + I_SecurityToken.transfer(account_investor4, web3.utils.toWei("2"), {from: account_investor1}) ); }); - it("Should fail to add a manual block because invalid _to address", async () => { + it("Should successfully transfer the tokens within the allowance limit", async() => { + currentTime = new BN(await latestTime()); + let oldBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei("1"), {from: account_investor1}); + let newBal4 = await I_SecurityToken.balanceOf.call(account_investor4); + assert.equal((newBal4.sub(oldBal4)).div(new BN(10).pow(new BN(18))).toString(), 1); + }); + + it("Should fail to modify because allowance is zero", async() => { await catchRevert( - I_ManualApprovalTransferManager.addManualBlocking(account_investor1, "", latestTime() + duration.days(1), { - from: token_owner - }) + I_ManualApprovalTransferManager.modifyManualApproval( + account_investor1, + account_investor4, + expiryTimeMA, + web3.utils.toWei("5"), + web3.utils.fromAscii("New Description"), + 0, + { + from: token_owner + } + ) ); }); - it("Should fail to add a manual block because invalid expiry time", async () => { + it("Should fail to revoke the manual Approval -- bad owner", async() => { await catchRevert( - I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, 99999, { from: token_owner }) + I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, {from: account_investor5}) ); - }); + }) - it("Add a manual block for a 2nd investor", async () => { - await I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(1), { - from: token_owner - }); + it("Should revoke the manual Approval b/w investor4 and 1", async() => { + await I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, {from: token_owner}); + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor1))[0].length, 0); + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor4))[0].length, 0); }); - it("Should fail to add a manual block because blocking already exist", async () => { + it("Should fail to revoke the same manual approval again", async() => { await catchRevert( - I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(5), { from: token_owner }) + I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, {from: token_owner}) ); }); - it("Check manual block causes failure", async () => { - await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 })); + it("Should fail to add multiple manual approvals -- failed because of bad owner", async () => { + await catchRevert ( + I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")], + [currentTime.add(new BN(duration.days(1))),currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1"), web3.utils.fromAscii("DESCRIPTION_2")], + { + from: account_investor5 + } + ) + ) + }); + + it("Should fail to add multiple manual approvals -- failed because of length mismatch", async () => { + await catchRevert ( + I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")], + [currentTime.add(new BN(duration.days(1))),currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1"), web3.utils.fromAscii("DESCRIPTION_2")], + { + from: token_owner + } + ) + ) + }); + + it("Should fail to add multiple manual approvals -- failed because of length mismatch", async () => { + await catchRevert ( + I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")], + [currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1"), web3.utils.fromAscii("DESCRIPTION_2")], + { + from: token_owner + } + ) + ) + }); + + it("Should fail to add multiple manual approvals -- failed because of length mismatch", async () => { + await catchRevert ( + I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether")], + [currentTime.add(new BN(duration.days(1))),currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1"), web3.utils.fromAscii("DESCRIPTION_2")], + { + from: token_owner + } + ) + ) + }); + + it("Should fail to add multiple manual approvals -- failed because of length mismatch", async () => { + await catchRevert ( + I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")], + [currentTime.add(new BN(duration.days(1))),currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1")], + { + from: token_owner + } + ) + ) }); - it("Should fail to revoke manual block because invalid _from address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualBlocking("0x0", account_investor2, { from: token_owner })); - }); + it("Add multiple manual approvals", async () => { + let time = currentTime.add(new BN(duration.days(1))); + await I_ManualApprovalTransferManager.addManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + [web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")], + [time,currentTime.add(new BN(duration.days(1)))], + [web3.utils.fromAscii("DESCRIPTION_1"), web3.utils.fromAscii("DESCRIPTION_2")], + { + from: token_owner + } + ); - it("Should fail to revoke manual block because invalid _to address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualBlocking(account_investor1, "0x0", { from: token_owner })); + assert.equal(await I_ManualApprovalTransferManager.getTotalApprovalsLength.call(), 2); + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor3))[0].length , 2); + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor3))[0][1], account_investor3); + assert.equal((await I_ManualApprovalTransferManager.getActiveApprovalsToUser.call(account_investor3))[1][0], account_investor3); + let approvalDetail = await I_ManualApprovalTransferManager.getApprovalDetails.call(account_investor2, account_investor3); + assert.equal(approvalDetail[0].toString(), time); + assert.equal(approvalDetail[1].toString(), web3.utils.toWei("2", "ether")); + assert.equal(web3.utils.toUtf8(approvalDetail[2]), "DESCRIPTION_1"); }); - it("Revoke manual block and check transfer works", async () => { - await I_ManualApprovalTransferManager.revokeManualBlocking(account_investor1, account_investor2, { from: token_owner }); - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); - }); + it("Should fail to revoke the multiple manual approvals -- because of bad owner", async() => { + await catchRevert( + I_ManualApprovalTransferManager.revokeManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + { + from: account_investor5 + } + ) + ); + }) - it("Check manual block ignored after expiry", async () => { - await I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(1), { - from: token_owner - }); + it("Should fail to revoke the multiple manual approvals -- because of input length mismatch", async() => { + await catchRevert( + I_ManualApprovalTransferManager.revokeManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3], + { + from: token_owner + } + ) + ); + }) - await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 })); - await increaseTime(1 + 24 * 60 * 60); - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); + it("Revoke multiple manual approvals", async () => { + await I_ManualApprovalTransferManager.revokeManualApprovalMulti( + [account_investor2,account_investor3], + [account_investor3,account_investor4], + { + from: token_owner + } + ); + assert.equal(await I_ManualApprovalTransferManager.getTotalApprovalsLength.call(), 0); + }); + + it("Add a manual approval for a 5th investor from issuance", async () => { + await I_ManualApprovalTransferManager.addManualApproval( + "0x0000000000000000000000000000000000000000", + account_investor5, + web3.utils.toWei("2", "ether"), + currentTime.add(new BN(duration.days(1))), + web3.utils.fromAscii("DESCRIPTION"), + { + from: token_owner + } + ); }); it("Should successfully attach the CountTransferManager with the security token (count of 1)", async () => { @@ -517,10 +796,10 @@ contract("ManualApprovalTransferManager", accounts => { ); const tx = await I_SecurityToken.addModule(I_CountTransferManagerFactory.address, bytesCountTM, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed"); + assert.equal(tx.logs[2].args._types[0].toString(), transferManagerKey, "CountTransferManager doesn't get deployed"); let name = web3.utils.toUtf8(tx.logs[2].args._name); assert.equal(name, "CountTransferManager", "CountTransferManager module was not added"); - I_CountTransferManager = CountTransferManager.at(tx.logs[2].args._module); + I_CountTransferManager = await CountTransferManager.at(tx.logs[2].args._module); }); it("Should get the permission list", async () => { @@ -541,16 +820,16 @@ contract("ManualApprovalTransferManager", accounts => { let name = web3.utils.toUtf8(await I_ManualApprovalTransferManagerFactory.getName.call()); assert.equal(name, "ManualApprovalTransferManager", "Wrong Module added"); let desc = await I_ManualApprovalTransferManagerFactory.description.call(); - assert.equal(desc, "Manage transfers using single approvals / blocking", "Wrong Module added"); + assert.equal(desc, "Manage transfers using single approvals", "Wrong Module added"); let title = await I_ManualApprovalTransferManagerFactory.title.call(); assert.equal(title, "Manual Approval Transfer Manager", "Wrong Module added"); let inst = await I_ManualApprovalTransferManagerFactory.getInstructions.call(); assert.equal( inst, - "Allows an issuer to set manual approvals or blocks for specific pairs of addresses and amounts. Init function takes no parameters.", + "Allows an issuer to set manual approvals for specific pairs of addresses and amounts. Init function takes no parameters.", "Wrong Module added" ); - assert.equal(await I_ManualApprovalTransferManagerFactory.version.call(), "1.0.0"); + assert.equal(await I_ManualApprovalTransferManagerFactory.version.call(), "2.1.0"); }); it("Should get the tags of the factory", async () => { diff --git a/test/k_module_registry.js b/test/k_module_registry.js index 477ce62d5..c13a46929 100644 --- a/test/k_module_registry.js +++ b/test/k_module_registry.js @@ -3,24 +3,27 @@ import { duration, ensureException, latestBlock } from "./helpers/utils"; import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; import { catchRevert } from "./helpers/exceptions"; -import { setUpPolymathNetwork } from "./helpers/createInstances"; +import {deployCappedSTOAndVerifyed, setUpPolymathNetwork} from "./helpers/createInstances"; const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); +const CappedSTO = artifacts.require("./CappedSTO.sol"); const DummySTOFactory = artifacts.require("./DummySTOFactory.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager.sol"); const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); const MockFactory = artifacts.require("./MockFactory.sol"); const TestSTOFactory = artifacts.require("./TestSTOFactory.sol"); const ReclaimTokens = artifacts.require("./ReclaimTokens.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("ModuleRegistry", accounts => { +contract("ModuleRegistry", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -32,14 +35,12 @@ contract("ModuleRegistry", accounts => { let account_temp; let balanceOfReceiver; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); let ID_snap; let message = "Transaction Should fail!"; // Contract Instance Declaration let I_GeneralPermissionManagerFactory; + let I_GeneralPermissionManagerLogic; let I_GeneralTransferManagerFactory; let I_SecurityTokenRegistryProxy; let I_GeneralPermissionManager; @@ -56,13 +57,16 @@ contract("ModuleRegistry", accounts => { let I_SecurityToken; let I_ReclaimERC20; let I_STRProxied; - let I_CappedSTO; + let I_CappedSTOLogic; let I_PolyToken; let I_MockFactory; let I_TestSTOFactory; let I_DummySTOFactory; let I_PolymathRegistry; let I_SecurityToken2; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details (Launched ST on the behalf of the issuer) const name = "Demo Token"; @@ -76,9 +80,10 @@ contract("ModuleRegistry", accounts => { const stoKey = 3; const budget = 0; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // delagate details const delegateDetails = "I am delegate .."; @@ -87,14 +92,16 @@ contract("ModuleRegistry", accounts => { // Capped STO details let startTime; let endTime; - const cap = web3.utils.toWei("10000"); + const cap = new BN(web3.utils.toWei("10000")); const rate = 1000; const fundRaiseType = [0]; const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; const MRProxyParameters = ["address", "address"]; + let currentTime; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_investor1 = accounts[9]; @@ -118,7 +125,9 @@ contract("ModuleRegistry", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from: account_polymath}); @@ -141,10 +150,7 @@ contract("ModuleRegistry", accounts => { describe("Test the initialize the function", async () => { it("Should successfully update the implementation address -- fail because polymathRegistry address is 0x", async () => { - let bytesProxy = encodeProxyCall(MRProxyParameters, [ - address_zero, - account_polymath - ]); + let bytesProxy = encodeProxyCall(MRProxyParameters, [address_zero, account_polymath]); catchRevert( I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesProxy, { from: account_polymath @@ -154,10 +160,7 @@ contract("ModuleRegistry", accounts => { }); it("Should successfully update the implementation address -- fail because owner address is 0x", async () => { - let bytesProxy = encodeProxyCall(MRProxyParameters, [ - I_PolymathRegistry.address, - address_zero - ]); + let bytesProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, address_zero]); catchRevert( I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesProxy, { from: account_polymath @@ -167,10 +170,7 @@ contract("ModuleRegistry", accounts => { }); it("Should successfully update the implementation address -- fail because all params are 0x", async () => { - let bytesProxy = encodeProxyCall(MRProxyParameters, [ - address_zero, - address_zero - ]); + let bytesProxy = encodeProxyCall(MRProxyParameters, [address_zero, address_zero]); catchRevert( I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesProxy, { from: account_polymath @@ -179,15 +179,12 @@ contract("ModuleRegistry", accounts => { ); }); - it("Should successfully update the implementation address", async() => { - let bytesProxy = encodeProxyCall(MRProxyParameters, [ - I_PolymathRegistry.address, - account_polymath - ]); + it("Should successfully update the implementation address", async () => { + let bytesProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesProxy, { from: account_polymath }); I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, { from: account_polymath }); - }) + }); }); describe("Test cases for the ModuleRegistry", async () => { @@ -199,33 +196,33 @@ contract("ModuleRegistry", accounts => { it("Should successfully update the registry contract addresses", async () => { await I_MRProxied.updateFromRegistry({ from: account_polymath }); assert.equal( - await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("securityTokenRegistry")), + await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("securityTokenRegistry")), I_SecurityTokenRegistryProxy.address ); assert.equal( - await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("featureRegistry")), + await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("featureRegistry")), I_FeatureRegistry.address ); - assert.equal(await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("polyToken")), I_PolyToken.address); + assert.equal(await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("polyToken")), I_PolyToken.address); }); }); describe("Test the state variables", async () => { it("Should be the right owner", async () => { - let _owner = await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("owner")); + let _owner = await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("owner")); assert.equal(_owner, account_polymath, "Owner should be the correct"); }); it("Should be the expected value of the paused and intialised variable", async () => { - let _paused = await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")); + let _paused = await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")); assert.isFalse(_paused, "Should be the false"); - let _intialised = await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("initialised")); + let _intialised = await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("initialised")); assert.isTrue(_intialised, "Values should be the true"); }); it("Should be the expected value of the polymath registry", async () => { - let _polymathRegistry = await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("polymathRegistry")); + let _polymathRegistry = await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("polymathRegistry")); assert.equal( _polymathRegistry, I_PolymathRegistry.address, @@ -260,24 +257,21 @@ contract("ModuleRegistry", accounts => { }); it("Should fail in registering the module-- type = 0", async () => { - I_MockFactory = await MockFactory.new(I_PolyToken.address, 0, 0, 0, { from: account_polymath }); + I_MockFactory = await MockFactory.new(new BN(0), new BN(0), address_zero, I_PolymathRegistry.address, { from: account_polymath }); catchRevert(I_MRProxied.registerModule(I_MockFactory.address, { from: account_polymath })); }); it("Should fail to register the new module because msg.sender is not the owner of the module", async() => { - I_CappedSTOFactory3 = await CappedSTOFactory.new(I_PolyToken.address, 0, 0, 0, { from: account_temp }); - catchRevert( - I_MRProxied.registerModule(I_CappedSTOFactory3.address, { from: token_owner }) - ); + I_CappedSTOLogic = await CappedSTO.new(address_zero, address_zero, { from: account_polymath }); + I_CappedSTOFactory3 = await CappedSTOFactory.new(new BN(0), new BN(0), I_CappedSTOLogic.address, I_PolymathRegistry.address, { from: account_temp }); + catchRevert(I_MRProxied.registerModule(I_CappedSTOFactory3.address, { from: token_owner })); }); - it("Should successfully register the module -- fail because no module type uniqueness", async() => { - await I_MockFactory.changeTypes({from: account_polymath }); - catchRevert( - I_MRProxied.registerModule(I_MockFactory.address, { from: account_polymath }) - ); - }) + it("Should successfully register the module -- fail because no module type uniqueness", async () => { + await I_MockFactory.changeTypes({ from: account_polymath }); + catchRevert(I_MRProxied.registerModule(I_MockFactory.address, { from: account_polymath })); + }); }); describe("Test case for verifyModule", async () => { @@ -292,7 +286,7 @@ contract("ModuleRegistry", accounts => { }); it("Should successfully verify the module -- false", async () => { - I_CappedSTOFactory1 = await CappedSTOFactory.new(I_PolyToken.address, 0, 0, 0, { from: account_polymath }); + I_CappedSTOFactory1 = await CappedSTOFactory.new(new BN(0), new BN(0), I_CappedSTOLogic.address, I_PolymathRegistry.address, { from: account_polymath }); await I_MRProxied.registerModule(I_CappedSTOFactory1.address, { from: account_polymath }); let tx = await I_MRProxied.verifyModule(I_CappedSTOFactory1.address, false, { from: account_polymath }); assert.equal(tx.logs[0].args._moduleFactory, I_CappedSTOFactory1.address, "Failed in verifying the module"); @@ -306,30 +300,26 @@ contract("ModuleRegistry", accounts => { describe("Test cases for the useModule function of the module registry", async () => { it("Deploy the securityToken", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500"), account_issuer); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: account_issuer }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), account_issuer); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: account_issuer }); await I_STRProxied.registerTicker(account_issuer, symbol, name, { from: account_issuer }); let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, true, { from: account_issuer }); - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase()); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase()); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); }); it("Should fail in adding module. Because module is un-verified", async () => { - startTime = latestTime() + duration.seconds(5000); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - await catchRevert(I_SecurityToken.addModule(I_CappedSTOFactory1.address, bytesSTO, 0, 0, { from: token_owner })); + await catchRevert(I_SecurityToken.addModule(I_CappedSTOFactory1.address, bytesSTO, new BN(0), new BN(0), { from: token_owner })); }); it("Should fail to register module because custom modules not allowed", async () => { - I_CappedSTOFactory2 = await CappedSTOFactory.new(I_PolyToken.address, 0, 0, 0, { from: token_owner }); + I_CappedSTOFactory2 = await CappedSTOFactory.new(new BN(0), new BN(0), I_CappedSTOLogic.address, I_PolymathRegistry.address, { from: token_owner }); - assert.notEqual( - I_CappedSTOFactory2.address.valueOf(), - address_zero, - "CappedSTOFactory contract was not deployed" - ); + assert.notEqual(I_CappedSTOFactory2.address.valueOf(), address_zero, "CappedSTOFactory contract was not deployed"); await catchRevert(I_MRProxied.registerModule(I_CappedSTOFactory2.address, { from: token_owner })); }); @@ -349,11 +339,11 @@ contract("ModuleRegistry", accounts => { }); it("Should successfully add module because custom modules switched on", async () => { - startTime = latestTime() + duration.seconds(5000); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); let tx = await I_MRProxied.registerModule(I_CappedSTOFactory2.address, { from: token_owner }); - tx = await I_SecurityToken.addModule(I_CappedSTOFactory2.address, bytesSTO, 0, 0, { from: token_owner }); + tx = await I_SecurityToken.addModule(I_CappedSTOFactory2.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal( @@ -365,28 +355,27 @@ contract("ModuleRegistry", accounts => { assert.equal(_reputation.length, 1); }); - it("Should successfully add module when custom modules switched on -- fail because factory owner is different", async() => { - await I_MRProxied.registerModule(I_CappedSTOFactory3.address, { from: account_temp }) - startTime = latestTime() + duration.seconds(5000); + it("Should successfully add module when custom modules switched on -- fail because factory owner is different", async () => { + await I_MRProxied.registerModule(I_CappedSTOFactory3.address, { from: account_temp }); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - catchRevert( - I_SecurityToken.addModule(I_CappedSTOFactory3.address, bytesSTO, 0, 0, { from: token_owner }) - ); - }) + catchRevert(I_SecurityToken.addModule(I_CappedSTOFactory3.address, bytesSTO, new BN(0), new BN(0), { from: token_owner })); + }); it("Should successfully add verified module", async () => { - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + I_GeneralPermissionManagerLogic = await GeneralPermissionManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: account_polymath }); + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(new BN(0), new BN(0), I_GeneralPermissionManagerLogic.address, I_PolymathRegistry.address, { from: account_polymath }); await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "", 0, 0, { from: token_owner }); + let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0], permissionManagerKey, "module doesn't get deployed"); }); it("Should failed in adding the TestSTOFactory module because not compatible with the current protocol version --lower", async () => { - I_TestSTOFactory = await TestSTOFactory.new(I_PolyToken.address, 0, 0, 0, { from: account_polymath }); + I_TestSTOFactory = await TestSTOFactory.new(new BN(0), new BN(0), address_zero, I_PolymathRegistry.address, { from: account_polymath }); await I_MRProxied.registerModule(I_TestSTOFactory.address, { from: account_polymath }); await I_MRProxied.verifyModule(I_TestSTOFactory.address, true, { from: account_polymath }); // Taking the snapshot the revert the changes from here @@ -397,35 +386,35 @@ contract("ModuleRegistry", accounts => { assert.equal(_lstVersion[1], 1); let bytesData = encodeModuleCall( ["uint256", "uint256", "uint256", "string"], - [latestTime(), latestTime() + duration.days(1), cap, "Test STO"] + [await latestTime(), currentTime.add(new BN(duration.days(1))), cap, "Test STO"] ); - await catchRevert(I_SecurityToken.addModule(I_TestSTOFactory.address, bytesData, 0, 0, { from: token_owner })); + await catchRevert(I_SecurityToken.addModule(I_TestSTOFactory.address, bytesData, new BN(0), new BN(0), { from: token_owner })); await revertToSnapshot(id); }); it("Should failed in adding the TestSTOFactory module because not compatible with the current protocol version --upper", async () => { - await I_TestSTOFactory.changeSTVersionBounds("upperBound", [0, 0, 1], { from: account_polymath }); + await I_TestSTOFactory.changeSTVersionBounds("upperBound", [0, new BN(0), 1], { from: account_polymath }); let _ustVersion = await I_TestSTOFactory.getUpperSTVersionBounds.call(); assert.equal(_ustVersion[0], 0); assert.equal(_ustVersion[2], 1); - await I_STRProxied.setProtocolVersion(I_STFactory.address, 2, 0, 1); + await I_STRProxied.setProtocolVersion(I_STFactory.address, 2, new BN(0), 1); // Generate the new securityToken let newSymbol = "toro"; - await I_PolyToken.getTokens(web3.utils.toWei("500"), account_issuer); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: account_issuer }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), account_issuer); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: account_issuer }); await I_STRProxied.registerTicker(account_issuer, newSymbol, name, { from: account_issuer }); let tx = await I_STRProxied.generateSecurityToken(name, newSymbol, tokenDetails, true, { from: account_issuer }); - assert.equal(tx.logs[1].args._ticker, newSymbol.toUpperCase()); - I_SecurityToken2 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, newSymbol.toUpperCase()); + I_SecurityToken2 = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); let bytesData = encodeModuleCall( ["uint256", "uint256", "uint256", "string"], - [latestTime(), latestTime() + duration.days(1), cap, "Test STO"] + [await latestTime(), currentTime.add(new BN(duration.days(1))), cap, "Test STO"] ); - await catchRevert(I_SecurityToken2.addModule(I_TestSTOFactory.address, bytesData, 0, 0, { from: token_owner })); + await catchRevert(I_SecurityToken2.addModule(I_TestSTOFactory.address, bytesData, new BN(0), new BN(0), { from: token_owner })); }); }); @@ -479,7 +468,7 @@ contract("ModuleRegistry", accounts => { let sto1 = (await I_MRProxied.getModulesByType.call(3))[0]; let sto2 = (await I_MRProxied.getModulesByType.call(3))[1]; - let sto3 = (await I_MRProxied.getModulesByType.call(3))[2]; + let sto3 = (await I_MRProxied.getModulesByType.call(3))[2]; let sto4 = (await I_MRProxied.getModulesByType.call(3))[3]; assert.equal(sto1, I_CappedSTOFactory1.address); @@ -496,10 +485,10 @@ contract("ModuleRegistry", accounts => { // re-ordering assert.equal(sto3_end, sto3); // delete related data - assert.equal(await I_MRProxied.getUintValues.call(web3.utils.soliditySha3("registry", sto4)), 0); + assert.equal(await I_MRProxied.getUintValue.call(web3.utils.soliditySha3("registry", sto4)), 0); assert.equal(await I_MRProxied.getReputationByFactory.call(sto4), 0); assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 3); - assert.equal(await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("verified", sto4)), false); + assert.equal(await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("verified", sto4)), false); await revertToSnapshot(snap); }); @@ -522,10 +511,10 @@ contract("ModuleRegistry", accounts => { // re-ordering assert.equal(sto1_end, sto1); // delete related data - assert.equal(await I_MRProxied.getUintValues.call(web3.utils.soliditySha3("registry", sto2)), 0); + assert.equal(await I_MRProxied.getUintValue.call(web3.utils.soliditySha3("registry", sto2)), 0); assert.equal(await I_MRProxied.getReputationByFactory.call(sto2), 0); assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 3); - assert.equal(await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("verified", sto2)), false); + assert.equal(await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("verified", sto2)), false); }); it("Should fail if module already removed", async () => { @@ -535,28 +524,23 @@ contract("ModuleRegistry", accounts => { describe("Test cases for IRegistry functionality", async () => { describe("Test cases for reclaiming funds", async () => { - - it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async() => { - await I_PolyToken.transfer(I_MRProxied.address, web3.utils.toWei("1"), { from: token_owner }); - catchRevert( - I_MRProxied.reclaimERC20("0x000000000000000000000000000000000000000", { from: account_polymath }) - ); + it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async () => { + await I_PolyToken.transfer(I_MRProxied.address, new BN(web3.utils.toWei("1")), { from: token_owner }); + catchRevert(I_MRProxied.reclaimERC20(address_zero, { from: account_polymath })); }); - - it("Should successfully reclaim POLY tokens -- not authorised", async() => { - catchRevert( - I_MRProxied.reclaimERC20(I_PolyToken.address, { from: account_temp }) - ); + + it("Should successfully reclaim POLY tokens -- not authorised", async () => { + catchRevert(I_MRProxied.reclaimERC20(I_PolyToken.address, { from: account_temp })); }); it("Should successfully reclaim POLY tokens", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1"), I_MRProxied.address); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1")), I_MRProxied.address); let bal1 = await I_PolyToken.balanceOf.call(account_polymath); await I_MRProxied.reclaimERC20(I_PolyToken.address); let bal2 = await I_PolyToken.balanceOf.call(account_polymath); assert.isAtLeast( - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber(), - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber() + bal2.div(new BN(10).pow(new BN(18))).toNumber(), + bal2.div(new BN(10).pow(new BN(18))).toNumber() ); }); }); @@ -568,7 +552,7 @@ contract("ModuleRegistry", accounts => { it("Should successfully pause the contract", async () => { await I_MRProxied.pause({ from: account_polymath }); - let status = await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")); + let status = await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")); assert.isOk(status); }); @@ -578,52 +562,66 @@ contract("ModuleRegistry", accounts => { it("Should successfully unpause the contract", async () => { await I_MRProxied.unpause({ from: account_polymath }); - let status = await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")); + let status = await I_MRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")); assert.isNotOk(status); }); }); - describe("Test cases for the ReclaimTokens contract", async() => { - - it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async() => { + describe("Test cases for the ReclaimTokens contract", async () => { + it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async () => { I_ReclaimERC20 = await ReclaimTokens.at(I_FeatureRegistry.address); - await I_PolyToken.transfer(I_ReclaimERC20.address, web3.utils.toWei("1"), { from: token_owner }); - catchRevert( - I_ReclaimERC20.reclaimERC20("0x000000000000000000000000000000000000000", { from: account_polymath }) - ); + await I_PolyToken.transfer(I_ReclaimERC20.address, new BN(web3.utils.toWei("1")), { from: token_owner }); + catchRevert(I_ReclaimERC20.reclaimERC20(address_zero, { from: account_polymath })); }); - - it("Should successfully reclaim POLY tokens -- not authorised", async() => { - catchRevert( - I_ReclaimERC20.reclaimERC20(I_PolyToken.address, { from: account_temp }) - ); + + it("Should successfully reclaim POLY tokens -- not authorised", async () => { + catchRevert(I_ReclaimERC20.reclaimERC20(I_PolyToken.address, { from: account_temp })); }); it("Should successfully reclaim POLY tokens", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1"), I_ReclaimERC20.address); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1")), I_ReclaimERC20.address); let bal1 = await I_PolyToken.balanceOf.call(account_polymath); await I_ReclaimERC20.reclaimERC20(I_PolyToken.address); let bal2 = await I_PolyToken.balanceOf.call(account_polymath); assert.isAtLeast( - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber(), - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber() + bal2.div(new BN(10).pow(new BN(18))).toNumber(), + bal2.div(new BN(10).pow(new BN(18))).toNumber() ); }); - }) - - describe("Test case for the PolymathRegistry", async() => { + }); - it("Should successfully get the address -- fail because key is not exist", async() => { - catchRevert( - I_PolymathRegistry.getAddress("PolyOracle") - ); + describe("Test case for the PolymathRegistry", async () => { + it("Should successfully get the address -- fail because key is not exist", async () => { + catchRevert(I_PolymathRegistry.getAddress("PolyOracle")); }); - it("Should successfully get the address", async() => { + it("Should successfully get the address", async () => { let _moduleR = await I_PolymathRegistry.getAddress("ModuleRegistry"); assert.equal(_moduleR, I_ModuleRegistryProxy.address); - }) - }) + }); + }); + + describe("Test cases for the transferOwnership", async () => { + it("Should fail to transfer the ownership -- not authorised", async () => { + catchRevert(I_MRProxied.transferOwnership(account_temp, { from: account_issuer })); + }); + + it("Should fail to transfer the ownership -- 0x address is not allowed", async () => { + catchRevert(I_MRProxied.transferOwnership(address_zero, { from: account_polymath })); + }); + + it("Should successfully transfer the ownership of the STR", async () => { + let tx = await I_MRProxied.transferOwnership(account_temp, { from: account_polymath }); + assert.equal(tx.logs[0].args.previousOwner, account_polymath); + assert.equal(tx.logs[0].args.newOwner, account_temp); + }); + + it("New owner has authorisation", async () => { + let tx = await I_MRProxied.transferOwnership(account_polymath, { from: account_temp }); + assert.equal(tx.logs[0].args.previousOwner, account_temp); + assert.equal(tx.logs[0].args.newOwner, account_polymath); + }); + }); }); }); }); diff --git a/test/l_percentage_transfer_manager.js b/test/l_percentage_transfer_manager.js index b15ee41a0..04222882a 100644 --- a/test/l_percentage_transfer_manager.js +++ b/test/l_percentage_transfer_manager.js @@ -8,12 +8,13 @@ const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const PercentageTransferManager = artifacts.require("./PercentageTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); const SecurityToken = artifacts.require("./SecurityToken.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("PercentageTransferManager", accounts => { +contract("PercentageTransferManager", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -24,11 +25,6 @@ contract("PercentageTransferManager", accounts => { let account_investor4; let account_delegate; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - let message = "Transaction Should Fail!"; // Contract Instance Declaration @@ -50,7 +46,10 @@ contract("PercentageTransferManager", accounts => { let I_STFactory; let I_SecurityToken; let I_PolyToken; - var I_PolymathRegistry; + let I_STRGetter; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -58,7 +57,8 @@ contract("PercentageTransferManager", accounts => { const tokenDetails = "This is equity type of issuance"; const decimals = 18; const contact = "team@polymath.network"; - const delegateDetails = "Hello I am legit delegate"; + const managerDetails = web3.utils.fromAscii("Hello"); + const delegateDetails = web3.utils.fromAscii("I am delegate"); // Module key const delegateManagerKey = 1; @@ -66,26 +66,35 @@ contract("PercentageTransferManager", accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // PercentageTransferManager details - const holderPercentage = 70 * 10**16; // Maximum number of token holders - - let bytesSTO = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'uint256', - name: '_maxHolderPercentage' - },{ - type: 'bool', - name: '_allowPrimaryIssuance' - } - ] - }, [holderPercentage, false]); - - before(async() => { - // Accounts setup + const holderPercentage = 70 * 10 ** 16; // Maximum number of token holders + + let bytesSTO = web3.eth.abi.encodeFunctionCall( + { + name: "configure", + type: "function", + inputs: [ + { + type: "uint256", + name: "_maxHolderPercentage" + }, + { + type: "bool", + name: "_allowPrimaryIssuance" + } + ] + }, + [holderPercentage, false] + ); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + + before(async () => { + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -94,6 +103,7 @@ contract("PercentageTransferManager", accounts => { account_investor1 = accounts[7]; account_investor2 = accounts[8]; account_investor3 = accounts[9]; + account_investor4 = accounts[5] account_delegate = accounts[6]; let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -109,18 +119,23 @@ contract("PercentageTransferManager", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 2: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 3(a): Deploy the PercentageTransferManager - [I_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified(account_polymath, I_MRProxied, 0); // STEP 4(b): Deploy the PercentageTransferManager - [P_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); - + [P_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified( + account_polymath, + I_MRProxied, + new BN(web3.utils.toWei("500", "ether")) + ); // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -150,15 +165,15 @@ contract("PercentageTransferManager", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -166,33 +181,31 @@ contract("PercentageTransferManager", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the General permission manager factory with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "GeneralPermissionManager", "GeneralPermissionManagerFactory module was not added" ); - I_GeneralPermissionManager = GeneralPermissionManager.at(tx.logs[2].args._module); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); }); - }); describe("Buy tokens using on-chain whitelist", async () => { it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -209,20 +222,19 @@ contract("PercentageTransferManager", accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should Buy some more tokens", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -236,15 +248,15 @@ contract("PercentageTransferManager", accounts => { ); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should successfully attach the PercentageTransferManager factory with the security token - failed payment", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); await catchRevert( - I_SecurityToken.addModule(P_PercentageTransferManagerFactory.address, bytesSTO, web3.utils.toWei("500", "ether"), 0, { + I_SecurityToken.addModule(P_PercentageTransferManagerFactory.address, bytesSTO, new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }) ); @@ -252,12 +264,12 @@ contract("PercentageTransferManager", accounts => { it("Should successfully attach the PercentageTransferManager factory with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); const tx = await I_SecurityToken.addModule( P_PercentageTransferManagerFactory.address, bytesSTO, - web3.utils.toWei("500", "ether"), - 0, + new BN(web3.utils.toWei("2000", "ether")), + new BN(0), { from: token_owner } ); assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "PercentageTransferManagerFactory doesn't get deployed"); @@ -266,28 +278,27 @@ contract("PercentageTransferManager", accounts => { "PercentageTransferManager", "PercentageTransferManagerFactory module was not added" ); - P_PercentageTransferManager = PercentageTransferManager.at(tx.logs[3].args._module); + P_PercentageTransferManager = await PercentageTransferManager.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the PercentageTransferManager with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_PercentageTransferManagerFactory.address, bytesSTO, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_PercentageTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "PercentageTransferManager", "PercentageTransferManager module was not added" ); - I_PercentageTransferManager = PercentageTransferManager.at(tx.logs[2].args._module); + I_PercentageTransferManager = await PercentageTransferManager.at(tx.logs[2].args._module); }); it("Add a new token holder", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), { from: account_issuer, gas: 6000000 @@ -302,9 +313,9 @@ contract("PercentageTransferManager", accounts => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Should pause the tranfers at transferManager level", async () => { @@ -314,9 +325,9 @@ contract("PercentageTransferManager", accounts => { it("Should still be able to transfer between existing token holders up to limit", async () => { // Add the Investor in to the whitelist // Mint some tokens - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_investor2 }); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_investor2 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Should unpause the tranfers at transferManager level", async () => { @@ -324,48 +335,42 @@ contract("PercentageTransferManager", accounts => { }); it("Should not be able to transfer between existing token holders over limit", async () => { - await catchRevert(I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2", "ether"), { from: account_investor1 })); + await catchRevert(I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("2", "ether")), { from: account_investor1 })); }); - it("Should not be able to mint token amount over limit", async() => { - await catchRevert(I_SecurityToken.mint(account_investor3, web3.utils.toWei('100', 'ether'), { from: token_owner })) + it("Should not be able to issue token amount over limit", async () => { + await catchRevert(I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("100", "ether")), "0x0", { from: token_owner })); }); - it("Allow unlimited primary issuance and remint", async() => { + it("Allow unlimited primary issuance and remint", async () => { let snapId = await takeSnapshot(); await I_PercentageTransferManager.setAllowPrimaryIssuance(true, { from: token_owner }); - await I_SecurityToken.mint(account_investor3, web3.utils.toWei('100', 'ether'), { from: token_owner }); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("100", "ether")), "0x0", { from: token_owner }); // trying to call it again with the same value. should fail - await catchRevert( - I_PercentageTransferManager.setAllowPrimaryIssuance(true, { from: token_owner }) - ) + await catchRevert(I_PercentageTransferManager.setAllowPrimaryIssuance(true, { from: token_owner })); await revertToSnapshot(snapId); }); - it("Should not be able to transfer between existing token holders over limit", async() => { - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei('2', 'ether'), { from: account_investor1 }) - ) + it("Should not be able to transfer between existing token holders over limit", async () => { + await catchRevert(I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("2", "ether")), { from: account_investor1 })); }); it("Should not be able to modify holder percentage to 100 - Unauthorized msg.sender", async () => { - await catchRevert( - I_PercentageTransferManager.changeHolderPercentage(100 * 10 ** 16, { from: account_delegate }) - ) + await catchRevert(I_PercentageTransferManager.changeHolderPercentage(new BN(10).pow(new BN(18)), { from: account_delegate })); }); - it("Should successfully add the delegate", async() => { - let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner}); + it("Should successfully add the delegate", async () => { + let tx = await I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: token_owner }); assert.equal(tx.logs[0].args._delegate, account_delegate); }); - it("Should provide the permission", async() => { + it("Should provide the permission", async () => { let tx = await I_GeneralPermissionManager.changePermission( account_delegate, I_PercentageTransferManager.address, - "ADMIN", + web3.utils.fromAscii("ADMIN"), true, - {from: token_owner} + { from: token_owner } ); assert.equal(tx.logs[0].args._delegate, account_delegate); }); @@ -373,32 +378,38 @@ contract("PercentageTransferManager", accounts => { it("Modify holder percentage to 100", async () => { // Add the Investor in to the whitelist // Mint some tokens - await I_PercentageTransferManager.changeHolderPercentage(100 * 10 ** 16, { from: account_delegate }); + await I_PercentageTransferManager.changeHolderPercentage(new BN(10).pow(new BN(18)), { from: account_delegate }); - assert.equal((await I_PercentageTransferManager.maxHolderPercentage()).toNumber(), 100 * 10 ** 16); + assert.equal((await I_PercentageTransferManager.maxHolderPercentage()).toString(), (new BN(10).pow(new BN(18))).toString()); }); it("Should be able to transfer between existing token holders up to limit", async () => { await I_PercentageTransferManager.modifyWhitelist(account_investor3, false, { from: token_owner }); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2", "ether"), { from: account_investor1 }); + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("2", "ether")), { from: account_investor1 }); }); - it("Should whitelist in batch --failed because of mismatch in array lengths", async() => { + it("Should whitelist in batch --failed because of mismatch in array lengths", async () => { + let addressArray = [account_investor3, account_investor4]; await catchRevert( - I_PercentageTransferManager.modifyWhitelistMulti([account_investor3, account_investor4], [false], { from: token_owner }) + I_PercentageTransferManager.modifyWhitelistMulti(addressArray, [false], { from: token_owner }) ); - }) + }); - it("Should whitelist in batch", async() => { + it("Should whitelist in batch", async () => { let snapId = await takeSnapshot(); - await I_PercentageTransferManager.modifyWhitelistMulti([account_investor3, account_investor4], [false, true], { from: token_owner }); + let addressArray = []; + addressArray.push(account_investor3); + addressArray.push(account_investor4); + await I_PercentageTransferManager.modifyWhitelistMulti(addressArray, [false, true], { + from: token_owner + }); await revertToSnapshot(snapId); - }) + }); it("Should be able to whitelist address and then transfer regardless of holders", async () => { - await I_PercentageTransferManager.changeHolderPercentage(30 * 10 ** 16, { from: token_owner }); + await I_PercentageTransferManager.changeHolderPercentage(new BN(30).mul(new BN(10).pow(new BN(16))), { from: token_owner }); await I_PercentageTransferManager.modifyWhitelist(account_investor1, true, { from: token_owner }); - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("2", "ether"), { from: account_investor3 }); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("2", "ether")), { from: account_investor3 }); }); it("Should get the permission", async () => { diff --git a/test/m_presale_sto.js b/test/m_presale_sto.js index a89932dc4..300850352 100644 --- a/test/m_presale_sto.js +++ b/test/m_presale_sto.js @@ -3,18 +3,19 @@ import { duration, ensureException, promisifyLogWatch, latestBlock } from "./hel import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; import { catchRevert } from "./helpers/exceptions"; -import { setUpPolymathNetwork, deployPresaleSTOAndVerified } from "./helpers/createInstances" +import { setUpPolymathNetwork, deployPresaleSTOAndVerified } from "./helpers/createInstances"; const PreSaleSTOFactory = artifacts.require("./PreSaleSTOFactory.sol"); const PreSaleSTO = artifacts.require("./PreSaleSTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("PreSaleSTO", accounts => { +contract("PreSaleSTO", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -49,6 +50,9 @@ contract("PreSaleSTO", accounts => { let I_PreSaleSTO; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details for funds raise Type ETH const name = "Team"; @@ -68,13 +72,16 @@ contract("PreSaleSTO", accounts => { const budget = 0; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); let endTime; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; const STOParameters = ["uint256"]; + let currentTime; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_investor1 = accounts[4]; @@ -96,13 +103,15 @@ contract("PreSaleSTO", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 4: Deploy the PreSaleSTOFactory - [I_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, 0); // STEP 5: Deploy the paid PresaleSTOFactory - [P_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [P_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, 0); // Printing all the contract addresses console.log(` @@ -132,15 +141,15 @@ contract("PreSaleSTO", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); @@ -148,22 +157,24 @@ contract("PreSaleSTO", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should fail to launch the STO due to endTime is 0", async () => { let bytesSTO = encodeModuleCall(STOParameters, [0]); - await catchRevert(I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner })); + await catchRevert(I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner })); }); it("Should successfully attach the Paid STO factory with the security token", async () => { let snap_id = await takeSnapshot(); - endTime = latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time + endTime = await latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time let bytesSTO = encodeModuleCall(STOParameters, [endTime]); - await I_PolyToken.getTokens(web3.utils.toWei("500"), I_SecurityToken.address); - const tx = await I_SecurityToken.addModule(P_PreSaleSTOFactory.address, bytesSTO, web3.utils.toWei("500"), 0, { from: token_owner }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("500")), I_SecurityToken.address); + const tx = await I_SecurityToken.addModule(P_PreSaleSTOFactory.address, bytesSTO, new BN(web3.utils.toWei("500")), new BN(0), { + from: token_owner + }); assert.equal(tx.logs[2].args._types[0], stoKey, "PreSaleSTO doesn't get deployed"); assert.equal( @@ -171,23 +182,21 @@ contract("PreSaleSTO", accounts => { "PreSaleSTO", "PreSaleSTOFactory module was not added" ); - I_PreSaleSTO = PreSaleSTO.at(tx.logs[2].args._module); + I_PreSaleSTO = await PreSaleSTO.at(tx.logs[2].args._module); await revertToSnapshot(snap_id); }); it("Should successfully attach the STO factory with the security token -- fail because signature is different", async () => { - endTime = latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time + endTime = await latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time let bytesSTO = encodeModuleCall(["string"], ["hey"]); - await catchRevert( - I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner }) - ); + await catchRevert(I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner })); }); it("Should successfully attach the STO factory with the security token", async () => { - endTime = latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time + endTime = await latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time let bytesSTO = encodeModuleCall(STOParameters, [endTime]); - const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0], stoKey, "PreSaleSTO doesn't get deployed"); assert.equal( @@ -195,14 +204,14 @@ contract("PreSaleSTO", accounts => { "PreSaleSTO", "PreSaleSTOFactory module was not added" ); - I_PreSaleSTO = PreSaleSTO.at(tx.logs[2].args._module); + I_PreSaleSTO = await PreSaleSTO.at(tx.logs[2].args._module); }); it("Should successfully attach the STO factory with the security token", async () => { - endTime = latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time + endTime = await latestTime() + duration.days(30); // Start time will be 5000 seconds more than the latest time let bytesSTO = encodeModuleCall(STOParameters, [endTime]); - const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0], stoKey, "PreSaleSTO doesn't get deployed"); assert.equal( @@ -210,7 +219,7 @@ contract("PreSaleSTO", accounts => { "PreSaleSTO", "PreSaleSTOFactory module was not added" ); - I_PreSaleSTO = PreSaleSTO.at(tx.logs[2].args._module); + I_PreSaleSTO = await PreSaleSTO.at(tx.logs[2].args._module); }); }); @@ -227,16 +236,16 @@ contract("PreSaleSTO", accounts => { describe("Buy tokens", async () => { it("Should allocate the tokens -- failed due to investor not on whitelist", async () => { - await catchRevert(I_PreSaleSTO.allocateTokens(account_investor1, 1000, web3.utils.toWei("1", "ether"), 0)); + await catchRevert(I_PreSaleSTO.allocateTokens(account_investor1, 1000, new BN(web3.utils.toWei("1", "ether")), 0)); }); it("Should Buy the tokens", async () => { - fromTime = latestTime(); + fromTime = await latestTime(); toTime = fromTime + duration.days(100); expiryTime = toTime + duration.days(100); // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor1, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, fromTime, toTime, expiryTime, { from: account_issuer, gas: 6000000 }); @@ -245,39 +254,39 @@ contract("PreSaleSTO", accounts => { // Jump time await increaseTime(duration.days(1)); - await I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether"), 0, { + await I_PreSaleSTO.allocateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether")), new BN(0), { from: account_issuer }); - assert.equal((await I_PreSaleSTO.getRaised.call(0)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1); + assert.equal((await I_PreSaleSTO.getRaised.call(0)).div(new BN(10).pow(new BN(18))).toNumber(), 1); console.log(await I_PreSaleSTO.getNumberInvestors.call()); assert.equal((await I_PreSaleSTO.getNumberInvestors.call()).toNumber(), 1); // assert.isTrue(false); }); - it("Should allocate the tokens --failed because of amount is 0", async() => { + it("Should allocate the tokens --failed because of amount is 0", async () => { await catchRevert( - I_PreSaleSTO.allocateTokens(account_investor1, 0, web3.utils.toWei("1", "ether"), 0, { + I_PreSaleSTO.allocateTokens(account_investor1, new BN(0), new BN(web3.utils.toWei("1", "ether")), new BN(0), { from: account_issuer }) ); - }) + }); it("Should allocate the tokens -- failed due to msg.sender is not pre sale admin", async () => { await catchRevert( - I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether"), 0, { + I_PreSaleSTO.allocateTokens(account_investor1, new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether")), new BN(0), { from: account_fundsReceiver }) ); }); it("Should allocate tokens to multiple investors", async () => { - fromTime = latestTime(); + fromTime = await latestTime(); toTime = fromTime + duration.days(100); expiryTime = toTime + duration.days(100); // Add the Investor in to the whitelist - let tx1 = await I_GeneralTransferManager.modifyWhitelist(account_investor2, fromTime, toTime, expiryTime, true, { + let tx1 = await I_GeneralTransferManager.modifyKYCData(account_investor2, fromTime, toTime, expiryTime, { from: account_issuer, gas: 6000000 }); @@ -285,7 +294,7 @@ contract("PreSaleSTO", accounts => { assert.equal(tx1.logs[0].args._investor, account_investor2, "Failed in adding the investor in whitelist"); // Add the Investor in to the whitelist - let tx2 = await I_GeneralTransferManager.modifyWhitelist(account_investor3, fromTime, toTime, expiryTime, true, { + let tx2 = await I_GeneralTransferManager.modifyKYCData(account_investor3, fromTime, toTime, expiryTime, { from: account_issuer, gas: 6000000 }); @@ -294,82 +303,84 @@ contract("PreSaleSTO", accounts => { await I_PreSaleSTO.allocateTokensMulti( [account_investor2, account_investor3], - [web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether")], + [new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether"))], [0, 0], - [web3.utils.toWei("1000", "ether"), web3.utils.toWei("1000", "ether")], + [new BN(web3.utils.toWei("1000", "ether")), new BN(web3.utils.toWei("1000", "ether"))], { from: account_issuer } ); - assert.equal((await I_PreSaleSTO.getRaised.call(1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 2000); + assert.equal((await I_PreSaleSTO.getRaised.call(1)).div(new BN(10).pow(new BN(18))).toNumber(), 2000); assert.equal((await I_PreSaleSTO.getNumberInvestors.call()).toNumber(), 3); }); - it("Should successfully mint multiple tokens -- failed because array mismatch", async() => { + it("Should successfully mint multiple tokens -- failed because array mismatch", async () => { await catchRevert( I_PreSaleSTO.allocateTokensMulti( [account_investor2], - [web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether")], + [new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether"))], [0, 0], - [web3.utils.toWei("1000", "ether"), web3.utils.toWei("1000", "ether")], + [new BN(web3.utils.toWei("1000", "ether")), new BN(web3.utils.toWei("1000", "ether"))], { from: account_issuer } ) ); - }) + }); - it("Should successfully mint multiple tokens -- failed because array mismatch", async() => { + it("Should successfully mint multiple tokens -- failed because array mismatch", async () => { await catchRevert( I_PreSaleSTO.allocateTokensMulti( [account_investor2, account_investor3], - [web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether")], + [new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether"))], [0], - [web3.utils.toWei("1000", "ether"), web3.utils.toWei("1000", "ether")], + [new BN(web3.utils.toWei("1000", "ether")), new BN(web3.utils.toWei("1000", "ether"))], { from: account_issuer } ) ); }); - it("Should successfully mint multiple tokens -- failed because array mismatch", async() => { + it("Should successfully mint multiple tokens -- failed because array mismatch", async () => { await catchRevert( I_PreSaleSTO.allocateTokensMulti( [account_investor2, account_investor3], - [web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether")], - [0,0], - [web3.utils.toWei("1000", "ether")], + [new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether"))], + [0, 0], + [new BN(web3.utils.toWei("1000", "ether"))], { from: account_issuer } ) ); }); - it("Should successfully mint multiple tokens -- failed because array mismatch", async() => { + it("Should successfully mint multiple tokens -- failed because array mismatch", async () => { await catchRevert( I_PreSaleSTO.allocateTokensMulti( [account_investor2, account_investor3], - [web3.utils.toWei("1", "ether"), web3.utils.toWei("1", "ether")], + [new BN(web3.utils.toWei("1", "ether")), new BN(web3.utils.toWei("1", "ether"))], [0], - [web3.utils.toWei("1000", "ether"), web3.utils.toWei("1000", "ether")], + [new BN(web3.utils.toWei("1000", "ether")), new BN(web3.utils.toWei("1000", "ether"))], { from: account_issuer } ) ); }); - it("Should buy some more tokens to previous investor", async() => { - await I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei("1000", "ether"), web3.utils.toWei("1", "ether"), 0, { from: account_issuer }); + it("Should buy some more tokens to previous investor", async () => { + await I_PreSaleSTO.allocateTokens(account_investor1, new BN(web3.utils.toWei("1000", "ether")), new BN(web3.utils.toWei("1", "ether")), new BN(0), { + from: account_issuer + }); // No change in the investor count assert.equal((await I_PreSaleSTO.getNumberInvestors.call()).toNumber(), 3); - }) + }); it("Should failed at the time of buying the tokens -- Because STO has ended", async () => { await increaseTime(duration.days(100)); // increased beyond the end time of the STO await catchRevert( - I_PreSaleSTO.allocateTokens(account_investor1, 1000, web3.utils.toWei("1", "ether"), 0, { from: account_issuer }) + I_PreSaleSTO.allocateTokens(account_investor1, 1000, new BN(web3.utils.toWei("1", "ether")), new BN(0), { from: account_issuer }) ); }); }); describe("Reclaim poly sent to STO by mistake", async () => { it("Should fail to reclaim POLY because token contract address is 0 address", async () => { - let value = web3.utils.toWei("100", "ether"); + let value = new BN(web3.utils.toWei("100", "ether")); await I_PolyToken.getTokens(value, account_investor1); await I_PolyToken.transfer(I_PreSaleSTO.address, value, { from: account_investor1 }); @@ -377,7 +388,7 @@ contract("PreSaleSTO", accounts => { }); it("Should successfully reclaim POLY", async () => { - let value = web3.utils.toWei("100", "ether"); + let value = new BN(web3.utils.toWei("100", "ether")); await I_PolyToken.getTokens(value, account_investor1); let initInvestorBalance = await I_PolyToken.balanceOf(account_investor1); let initOwnerBalance = await I_PolyToken.balanceOf(token_owner); @@ -386,29 +397,29 @@ contract("PreSaleSTO", accounts => { await I_PolyToken.transfer(I_PreSaleSTO.address, value, { from: account_investor1 }); await I_PreSaleSTO.reclaimERC20(I_PolyToken.address, { from: token_owner }); assert.equal( - (await I_PolyToken.balanceOf(account_investor1)).toNumber(), - initInvestorBalance.sub(value).toNumber(), + (await I_PolyToken.balanceOf(account_investor1)).toString(), + initInvestorBalance.sub(value).toString(), "tokens are not transferred out from investor account" ); assert.equal( - (await I_PolyToken.balanceOf(token_owner)).toNumber(), + (await I_PolyToken.balanceOf(token_owner)).toString(), initOwnerBalance .add(value) .add(initContractBalance) - .toNumber(), + .toString(), "tokens are not added to the owner account" ); assert.equal( (await I_PolyToken.balanceOf(I_PreSaleSTO.address)).toNumber(), - 0, + new BN(0).toNumber(), "tokens are not trandfered out from STO contract" ); }); - it("Should get the the tokens sold", async() => { + it("Should get the the tokens sold", async () => { let _tokensSold = await I_PreSaleSTO.getTokensSold.call(); console.log(_tokensSold); - }) + }); }); describe("Test cases for the PresaleSTOFactory", async () => { diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index ef6b80c51..51680c96d 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -11,13 +11,17 @@ const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryPro const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); const SecurityTokenRegistryMock = artifacts.require("./SecurityTokenRegistryMock.sol"); const STFactory = artifacts.require("./STFactory.sol"); +const STRGetter = artifacts.require('./STRGetter.sol'); +const STGetter = artifacts.require("./STGetter.sol"); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("SecurityTokenRegistry", accounts => { +contract("SecurityTokenRegistry", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -30,9 +34,6 @@ contract("SecurityTokenRegistry", accounts => { let dummy_token; let balanceOfReceiver; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(100); let ID_snap; const message = "Transaction Should Fail!!"; @@ -59,6 +60,13 @@ contract("SecurityTokenRegistry", accounts => { let I_SecurityTokenRegistryProxy; let I_STRProxied; let I_MRProxied; + let I_STRGetter; + let I_Getter; + let I_STGetter; + let stGetter; + let I_USDOracle; + let I_POLYOracle; + let I_StablePOLYOracle; // SecurityToken Details (Launched ST on the behalf of the issuer) const name = "Demo Token"; @@ -71,6 +79,7 @@ contract("SecurityTokenRegistry", accounts => { const symbol2 = "DET2"; const tokenDetails2 = "This is equity type of issuance"; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // Module key const permissionManagerKey = 1; @@ -79,18 +88,20 @@ contract("SecurityTokenRegistry", accounts => { const budget = 0; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); - const newRegFee = web3.utils.toWei("300"); + const initRegFee = new BN(web3.utils.toWei("250")); + const initRegFeePOLY = new BN(web3.utils.toWei("1000")); const STRProxyParameters = ["address", "address", "uint256", "uint256", "address", "address"]; const STOParameters = ["uint256", "uint256", "uint256", "string"]; // Capped STO details - const cap = web3.utils.toWei("10000"); + const cap = new BN(web3.utils.toWei("10000")); const someString = "Hello string"; + let currentTime; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_investor1 = accounts[9]; @@ -114,15 +125,21 @@ contract("SecurityTokenRegistry", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter, + I_USDOracle, + I_POLYOracle, + I_StablePOLYOracle ] = instances; // STEP 8: Deploy the CappedSTOFactory - [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, 0); // Step 9: Deploy the SecurityTokenRegistry - + console.log(I_SecurityTokenRegistry.address); I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ from: account_polymath }); + console.log(I_SecurityTokenRegistry.address); assert.notEqual( I_SecurityTokenRegistry.address.valueOf(), @@ -132,6 +149,8 @@ contract("SecurityTokenRegistry", accounts => { // Step 9 (a): Deploy the proxy I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ from: account_polymath }); + // Step 10 : Deploy the getter contract + I_STRGetter = await STRGetter.new({ from: account_polymath }); //Step 11: update the registries addresses from the PolymathRegistry contract await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, { from: account_polymath }); await I_MRProxied.updateFromRegistry({ from: account_polymath }); @@ -160,10 +179,10 @@ contract("SecurityTokenRegistry", accounts => { I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -177,10 +196,10 @@ contract("SecurityTokenRegistry", accounts => { address_zero, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -192,12 +211,12 @@ contract("SecurityTokenRegistry", accounts => { let bytesProxy = encodeProxyCall(STRProxyParameters, [ I_PolymathRegistry.address, I_STFactory.address, - 0, + new BN(0), initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -210,11 +229,11 @@ contract("SecurityTokenRegistry", accounts => { I_PolymathRegistry.address, I_STFactory.address, initRegFee, - 0, - I_PolyToken.address, - account_polymath + new BN(0), + account_polymath, + I_STRGetter.address ]); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -222,33 +241,16 @@ contract("SecurityTokenRegistry", accounts => { ); }); - it("Should successfully update the implementation address -- fail because PolyToken address is 0x", async () => { - let bytesProxy = encodeProxyCall(STRProxyParameters, [ - I_PolymathRegistry.address, - I_STFactory.address, - initRegFee, - initRegFee, - address_zero, - account_polymath - ]); - catchRevert( - I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { - from: account_polymath - }), - "tx-> revert because PolyToken address is 0x" - ); - }); - it("Should successfully update the implementation address -- fail because owner address is 0x", async () => { let bytesProxy = encodeProxyCall(STRProxyParameters, [ I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - address_zero + address_zero, + I_STRGetter.address ]); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -257,15 +259,8 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should successfully update the implementation address -- fail because all params get 0", async () => { - let bytesProxy = encodeProxyCall(STRProxyParameters, [ - address_zero, - address_zero, - 0, - 0, - address_zero, - address_zero - ]); - catchRevert( + let bytesProxy = encodeProxyCall(STRProxyParameters, [address_zero, address_zero, new BN(0), new BN(0), address_zero, address_zero]); + await catchRevert( I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }), @@ -279,77 +274,81 @@ contract("SecurityTokenRegistry", accounts => { I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }); - I_STRProxied = SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + I_Getter = await STRGetter.at(I_SecurityTokenRegistryProxy.address); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); }); }); describe(" Test cases of the registerTicker", async () => { it("verify the intial parameters", async () => { - let intialised = await I_STRProxied.getBoolValues.call(web3.utils.soliditySha3("initialised")); + let intialised = await I_STRProxied.getBoolValue.call(web3.utils.soliditySha3("initialised")); assert.isTrue(intialised, "Should be true"); - let expiry = await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("expiryLimit")); + let expiry = await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("expiryLimit")); assert.equal(expiry.toNumber(), 5184000, "Expiry limit should be equal to 60 days"); - let polytoken = await I_STRProxied.getAddressValues.call(web3.utils.soliditySha3("polyToken")); + let polytoken = await I_STRProxied.getAddressValue.call(web3.utils.soliditySha3("polyToken")); assert.equal(polytoken, I_PolyToken.address, "Should be the polytoken address"); - let stlaunchFee = await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("stLaunchFee")); - assert.equal(stlaunchFee.toNumber(), initRegFee, "Should be provided reg fee"); + let stlaunchFee = await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("stLaunchFee")); + assert.equal(stlaunchFee.toString(), initRegFee.toString(), "Should be provided reg fee"); - let tickerRegFee = await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("tickerRegFee")); - assert.equal(tickerRegFee.toNumber(), tickerRegFee, "Should be provided reg fee"); + let tickerRegFee = await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("tickerRegFee")); + assert.equal(tickerRegFee.toString(), tickerRegFee.toString(), "Should be provided reg fee"); - let polymathRegistry = await I_STRProxied.getAddressValues.call(web3.utils.soliditySha3("polymathRegistry")); + let polymathRegistry = await I_STRProxied.getAddressValue.call(web3.utils.soliditySha3("polymathRegistry")); assert.equal(polymathRegistry, I_PolymathRegistry.address, "Should be the address of the polymath registry"); - let owner = await I_STRProxied.getAddressValues.call(web3.utils.soliditySha3("owner")); + let getterContract = await I_STRProxied.getAddressValue.call(web3.utils.soliditySha3("STRGetter")); + assert.equal(getterContract, I_STRGetter.address, "Should be the address of the getter contract"); + + let owner = await I_STRProxied.getAddressValue.call(web3.utils.soliditySha3("owner")); assert.equal(owner, account_polymath, "Should be the address of the registry owner"); }); it("Can't call the intialize function again", async () => { - catchRevert( + await catchRevert( I_STRProxied.initialize( I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ), "tx revert -> Can't call the intialize function again" ); }); it("Should fail to register ticker if tickerRegFee not approved", async () => { - catchRevert( + await catchRevert( I_STRProxied.registerTicker(account_temp, symbol, name, { from: account_temp }), "tx revert -> POLY allowance not provided for registration fee" ); }); it("Should fail to register ticker if owner is 0x", async () => { - await I_PolyToken.getTokens(initRegFee, account_temp); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_temp }); + await I_PolyToken.getTokens(initRegFeePOLY, account_temp); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: account_temp }); - catchRevert( + await catchRevert( I_STRProxied.registerTicker(address_zero, symbol, name, { from: account_temp }), "tx revert -> owner should not be 0x" ); }); it("Should fail to register ticker due to the symbol length is 0", async () => { - catchRevert(I_STRProxied.registerTicker(account_temp, "", name, { from: account_temp }), "tx revert -> Symbol Length is 0"); + await catchRevert(I_STRProxied.registerTicker(account_temp, "", name, { from: account_temp }), "tx revert -> Symbol Length is 0"); }); it("Should fail to register ticker due to the symbol length is greater than 10", async () => { - catchRevert( + await catchRevert( I_STRProxied.registerTicker(account_temp, "POLYMATHNET", name, { from: account_temp }), "tx revert -> Symbol Length is greater than 10" ); @@ -359,23 +358,83 @@ contract("SecurityTokenRegistry", accounts => { let tx = await I_STRProxied.registerTicker(account_temp, symbol, name, { from: account_temp }); assert.equal(tx.logs[0].args._owner, account_temp, `Owner should be the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, symbol, `Symbol should be ${symbol}`); + let data = await I_Getter.getTickerDetails.call(symbol); + assert.equal(data[0], account_temp); + assert.equal(data[3], name); + // trying to access the function data directly from the STRGetter then it should give all values zero + data = await I_STRGetter.getTickerDetails.call(symbol); + assert.equal(data[0], address_zero); + assert.equal(data[3], ""); + }); + + it("Should change ticker price based on oracle", async () => { + let snap_Id = await takeSnapshot(); + let origPriceUSD = new BN(web3.utils.toWei("250")); + let origPricePOLY = new BN(web3.utils.toWei("1000")); + let currentRate = await I_POLYOracle.getPrice.call(); + console.log("Current Rate: " + currentRate); + let feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + let feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), origPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), origPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(27).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // No change as difference is less than 10% + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), origPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), origPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(20).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + let newPricePOLY = new BN(web3.utils.toWei("1250")); + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), newPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), newPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(21).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // No change as difference is less than 10% + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), newPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), newPricePOLY.toString()); + await I_StablePOLYOracle.changeEvictPercentage(new BN(10).pow(new BN(16))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // Change as eviction percentage updated + // newPricePOLY = new BN(web3.utils.toWei("1250")); + //1190.476190476190476190 = 250/0.21 + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), "1190476190476190476190"); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), "1190476190476190476190"); + await revertToSnapshot(snap_Id); }); - it("Should register the ticker when the tickerRegFee is 0", async() => { + it("Should register the ticker when the tickerRegFee is 0", async () => { let snap_Id = await takeSnapshot(); await I_STRProxied.changeTickerRegistrationFee(0, { from: account_polymath }); let tx = await I_STRProxied.registerTicker(account_temp, "ZERO", name, { from: account_temp }); assert.equal(tx.logs[0].args._owner, account_temp, `Owner should be the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "ZERO", `Symbol should be ZERO`); await revertToSnapshot(snap_Id); - }) + }); it("Should fail to register same symbol again", async () => { // Give POLY to token issuer - await I_PolyToken.getTokens(initRegFee, token_owner); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.getTokens(initRegFeePOLY, token_owner); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); // Call registration function - catchRevert( + await catchRevert( I_STRProxied.registerTicker(token_owner, symbol, name, { from: token_owner }), "tx revert -> Symbol is already alloted to someone else" ); @@ -383,7 +442,7 @@ contract("SecurityTokenRegistry", accounts => { it("Should successfully register pre registerd ticker if expiry is reached", async () => { await increaseTime(5184000 + 100); // 60(5184000) days of expiry + 100 sec for buffer - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, symbol, name, { from: token_owner }); assert.equal(tx.logs[0].args._owner, token_owner, `Owner should be the ${token_owner}`); assert.equal(tx.logs[0].args._ticker, symbol, `Symbol should be ${symbol}`); @@ -391,47 +450,47 @@ contract("SecurityTokenRegistry", accounts => { it("Should fail to register ticker if registration is paused", async () => { await I_STRProxied.pause({ from: account_polymath }); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); - catchRevert( + await catchRevert( I_STRProxied.registerTicker(token_owner, "AAA", name, { from: token_owner }), "tx revert -> Registration is paused" ); }); it("Should fail to pause if already paused", async () => { - catchRevert(I_STRProxied.pause({ from: account_polymath }), "tx revert -> Registration is already paused"); + await catchRevert(I_STRProxied.pause({ from: account_polymath }), "tx revert -> Registration is already paused"); }); it("Should successfully register ticker if registration is unpaused", async () => { await I_STRProxied.unpause({ from: account_polymath }); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, "AAA", name, { from: token_owner }); assert.equal(tx.logs[0].args._owner, token_owner, `Owner should be the ${token_owner}`); assert.equal(tx.logs[0].args._ticker, "AAA", `Symbol should be AAA`); }); it("Should fail to unpause if already unpaused", async () => { - catchRevert(I_STRProxied.unpause({ from: account_polymath }), "tx revert -> Registration is already unpaused"); + await catchRevert(I_STRProxied.unpause({ from: account_polymath }), "tx revert -> Registration is already unpaused"); }); }); describe("Test cases for the expiry limit", async () => { it("Should fail to set the expiry limit because msg.sender is not owner", async () => { - catchRevert(I_STRProxied.changeExpiryLimit(duration.days(10), { from: account_temp }), "tx revert -> msg.sender is not owner"); + await catchRevert(I_STRProxied.changeExpiryLimit(duration.days(10), { from: account_temp }), "tx revert -> msg.sender is not owner"); }); it("Should successfully set the expiry limit", async () => { await I_STRProxied.changeExpiryLimit(duration.days(10), { from: account_polymath }); assert.equal( - (await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("expiryLimit"))).toNumber(), + (await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("expiryLimit"))).toNumber(), duration.days(10), "Failed to change the expiry limit" ); }); it("Should fail to set the expiry limit because new expiry limit is lesser than one day", async () => { - catchRevert( + await catchRevert( I_STRProxied.changeExpiryLimit(duration.seconds(5000), { from: account_polymath }), "tx revert -> New expiry limit is lesser than one day" ); @@ -440,14 +499,14 @@ contract("SecurityTokenRegistry", accounts => { describe("Test cases for the getTickerDetails", async () => { it("Should get the details of the symbol", async () => { - let tx = await I_STRProxied.getTickerDetails.call(symbol); + let tx = await I_Getter.getTickerDetails.call(symbol); assert.equal(tx[0], token_owner, "Should equal to the rightful owner of the ticker"); assert.equal(tx[3], name, `Name of the token should equal to ${name}`); assert.equal(tx[4], false, "Status if the symbol should be undeployed -- false"); }); it("Should get the details of unregistered token", async () => { - let tx = await I_STRProxied.getTickerDetails.call("TORO"); + let tx = await I_Getter.getTickerDetails.call("TORO"); assert.equal(tx[0], address_zero, "Should be 0x as ticker is not exists in the registry"); assert.equal(tx[3], "", "Should be an empty string"); assert.equal(tx[4], false, "Status if the symbol should be undeployed -- false"); @@ -456,19 +515,19 @@ contract("SecurityTokenRegistry", accounts => { describe("Generate SecurityToken", async () => { it("Should get the ticker details successfully and prove the data is not storing in to the logic contract", async () => { - let data = await I_STRProxied.getTickerDetails(symbol, { from: token_owner }); + let data = await I_Getter.getTickerDetails(symbol, { from: token_owner }); assert.equal(data[0], token_owner, "Token owner should be equal"); assert.equal(data[3], name, "Name of the token should match with the registered symbol infor"); assert.equal(data[4], false, "Token is not launched yet so it should return False"); - data = await I_SecurityTokenRegistry.getTickerDetails(symbol, { from: token_owner }); + data = await I_STRGetter.getTickerDetails(symbol, { from: token_owner }); console.log("This is the data from the original securityTokenRegistry contract"); assert.equal(data[0], address_zero, "Token owner should be 0x"); }); it("Should fail to generate new security token if fee not provided", async () => { - await I_PolyToken.approve(I_STRProxied.address, 0, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, new BN(0), { from: token_owner }); - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }), "tx revert -> POLY allowance not provided for registration fee" ); @@ -476,9 +535,9 @@ contract("SecurityTokenRegistry", accounts => { it("Should fail to generate token if registration is paused", async () => { await I_STRProxied.pause({ from: account_polymath }); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }), "tx revert -> Registration is paused" ); @@ -487,36 +546,36 @@ contract("SecurityTokenRegistry", accounts => { it("Should fail to generate the securityToken -- Because ticker length is 0", async () => { await I_STRProxied.unpause({ from: account_polymath }); - catchRevert( - I_STRProxied.generateSecurityToken(name, "", tokenDetails, false, { from: token_owner }), + await catchRevert( + I_STRProxied.generateSecurityToken(name, "0x0", tokenDetails, false, { from: token_owner }), "tx revert -> Zero ticker length is not allowed" ); }); it("Should fail to generate the securityToken -- Because name length is 0", async () => { - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken("", symbol, tokenDetails, false, { from: token_owner }), "tx revert -> 0 name length is not allowed" ); }); it("Should fail to generate the securityToken -- Because msg.sender is not the rightful owner of the ticker", async () => { - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: account_temp }), "tx revert -> Because msg.sender is not the rightful owner of the ticker" ); }); it("Should generate the new security token with the same symbol as registered above", async () => { - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTrasnferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey, `Should be equal to the ${transferManagerKey}`); @@ -524,30 +583,54 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should fail to generate the SecurityToken when token is already deployed with the same symbol", async () => { - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }), "tx revert -> Because ticker is already in use" ); }); - it("Should fail to generate the SecurityToken because ticker gets expired", async() => { + it("Should fail to generate the SecurityToken because ticker gets expired", async () => { let snap_Id = await takeSnapshot(); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, "CCC", name, { from: token_owner }); await increaseTime(duration.days(65)); - catchRevert( + await catchRevert( I_STRProxied.generateSecurityToken(name, "CCC", tokenDetails, false, { from: token_owner }), "tx revert -> Because ticker is expired" ); await revertToSnapshot(snap_Id); }); - it("Should generate the SecurityToken when launch fee is 0", async() => { + it("Should generate the SecurityToken when launch fee is 0", async () => { let snap_Id = await takeSnapshot(); await I_STRProxied.changeSecurityLaunchFee(0, { from: account_polymath }); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, "CCC", name, { from: token_owner }); await I_STRProxied.generateSecurityToken(name, "CCC", tokenDetails, false, { from: token_owner }), + await revertToSnapshot(snap_Id); + }); + + it("Should get all created security tokens", async() => { + let snap_Id = await takeSnapshot(); + await I_PolyToken.getTokens(web3.utils.toWei("2000"), account_temp); + await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("2000"), { from: account_temp }); + await I_STRProxied.registerTicker(account_temp, "TMP", name, { from: account_temp }); + let tx = await I_STRProxied.generateSecurityToken(name, "TMP", tokenDetails, false, { from: account_temp }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, "TMP", "SecurityToken doesn't get deployed"); + + let securityTokenTmp = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + + let tokens = await I_Getter.getTokensByOwner.call(token_owner); + assert.equal(tokens.length, 1, "tokens array length error"); + assert.equal(tokens[0], I_SecurityToken.address, "ST address incorrect"); + + let allTokens = await I_Getter.getTokens.call(); + assert.equal(allTokens.length, 2); + assert.equal(allTokens[0], securityTokenTmp.address); + assert.equal(allTokens[1], I_SecurityToken.address); + await revertToSnapshot(snap_Id); }); }); @@ -555,42 +638,41 @@ contract("SecurityTokenRegistry", accounts => { describe("Generate SecurityToken v2", async () => { it("Should deploy the st version 2", async () => { // Step 7: Deploy the STFactory contract + I_STGetter = await STGetter.new(); + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); - I_STFactory002 = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + I_STFactory002 = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, I_STGetter.address, { from: account_polymath }); assert.notEqual( I_STFactory002.address.valueOf(), address_zero, "STFactory002 contract was not deployed" ); - await I_STRProxied.setProtocolVersion(I_STFactory002.address, 2, 2, 0, { from: account_polymath }); - let _protocol = await I_STRProxied.getProtocolVersion.call(); + await I_STRProxied.setProtocolVersion(I_STFactory002.address, new BN(2), new BN(2), new BN(0), { from: account_polymath }); + let _protocol = await I_Getter.getProtocolVersion.call(); assert.equal(_protocol[0], 2); assert.equal(_protocol[1], 2); assert.equal(_protocol[2], 0); }); it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, symbol2, name2, { from: token_owner }); assert.equal(tx.logs[0].args._owner, token_owner, `Token owner should be ${token_owner}`); assert.equal(tx.logs[0].args._ticker, symbol2, `Symbol should be ${symbol2}`); }); it("Should generate the new security token with version 2", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); + let tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed"); - - I_SecurityToken002 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - let tokens = await I_STRProxied.getTokensByOwner.call(token_owner); - assert.equal(tokens[0], I_SecurityToken.address); - assert.equal(tokens[1], I_SecurityToken002.address); + assert.equal(tx.logs[2].args._ticker, symbol2, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken002.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken002 = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + const log = (await I_SecurityToken002.getPastEvents('ModuleAdded'))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); @@ -600,17 +682,13 @@ contract("SecurityTokenRegistry", accounts => { describe("Deploy the new SecurityTokenRegistry", async () => { it("Should deploy the new SecurityTokenRegistry contract logic", async () => { I_SecurityTokenRegistryV2 = await SecurityTokenRegistryMock.new({ from: account_polymath }); - assert.notEqual( - I_SecurityTokenRegistryV2.address.valueOf(), - address_zero, - "SecurityTokenRegistry contract was not deployed" - ); + assert.notEqual(I_SecurityTokenRegistryV2.address.valueOf(), address_zero, "SecurityTokenRegistry contract was not deployed"); }); it("Should fail to upgrade the logic contract of the STRProxy -- bad owner", async () => { await I_STRProxied.pause({ from: account_polymath }); - catchRevert( + await catchRevert( I_SecurityTokenRegistryProxy.upgradeTo("1.1.0", I_SecurityTokenRegistryV2.address, { from: account_temp }), "tx revert -> bad owner" ); @@ -619,11 +697,11 @@ contract("SecurityTokenRegistry", accounts => { it("Should upgrade the logic contract into the STRProxy", async () => { await I_SecurityTokenRegistryProxy.upgradeTo("1.1.0", I_SecurityTokenRegistryV2.address, { from: account_polymath }); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - assert.isTrue(await I_STRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")), "Paused value should be false"); + assert.isTrue(await I_STRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")), "Paused value should be false"); }); it("Should check the old data persist or not", async () => { - let data = await I_STRProxied.getTickerDetails.call(symbol); + let data = await I_Getter.getTickerDetails.call(symbol); assert.equal(data[0], token_owner, "Should be equal to the token owner address"); assert.equal(data[3], name, "Should be equal to the name of the token that is provided earlier"); assert.isTrue(data[4], "Token status should be deployed == true"); @@ -631,32 +709,32 @@ contract("SecurityTokenRegistry", accounts => { it("Should unpause the logic contract", async () => { await I_STRProxied.unpause({ from: account_polymath }); - assert.isFalse(await I_STRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")), "Paused value should be false"); + assert.isFalse(await I_STRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")), "Paused value should be false"); }); }); describe("Generate custom tokens", async () => { it("Should fail if msg.sender is not polymath", async () => { - catchRevert( - I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, dummy_token, "I am custom ST", latestTime(), { + await catchRevert( + I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_delegate }), "tx revert -> msg.sender is not polymath account" ); }); - it("Should fail to genrate the custom security token -- ticker length is greater than 10 chars", async() => { - catchRevert( - I_STRProxied.modifySecurityToken("LOGAN", "LOGLOGLOGLOG", account_temp, dummy_token, "I am custom ST", latestTime(), { + it("Should fail to genrate the custom security token -- ticker length is greater than 10 chars", async () => { + await catchRevert( + I_STRProxied.modifySecurityToken("LOGAN", "LOGLOGLOGLOG", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_polymath }), - "tx revert -> msg.sender is not polymath account" + "tx revert -> ticker length is greater than 10 chars" ); - }) + }); it("Should fail to generate the custom security token -- name should not be 0 length ", async () => { - catchRevert( - I_STRProxied.modifySecurityToken("", "LOG", account_temp, dummy_token, "I am custom ST", latestTime(), { + await catchRevert( + I_STRProxied.modifySecurityToken("", "LOG", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_polymath }), "tx revert -> name should not be 0 length" @@ -664,8 +742,8 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should fail if ST address is 0 address", async () => { - catchRevert( - I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, 0, "I am custom ST", latestTime(), { + await catchRevert( + I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, address_zero, "I am custom ST", currentTime, { from: account_polymath }), "tx revert -> Security token address is 0" @@ -673,8 +751,8 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should fail if symbol length is 0", async () => { - catchRevert( - I_STRProxied.modifySecurityToken("", "", account_temp, dummy_token, "I am custom ST", latestTime(), { + await catchRevert( + I_STRProxied.modifySecurityToken("", "0x0", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_polymath }), "tx revert -> zero length of the symbol is not allowed" @@ -682,26 +760,26 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should fail to generate the custom ST -- deployedAt param is 0", async () => { - catchRevert( - I_STRProxied.modifySecurityToken(name2, symbol2, token_owner, dummy_token, "I am custom ST", 0, { from: account_polymath }), + await catchRevert( + I_STRProxied.modifySecurityToken(name2, symbol2, token_owner, dummy_token, "I am custom ST", new BN(0), { from: account_polymath }), "tx revert -> because deployedAt param is 0" ); }); it("Should successfully generate custom token", async () => { // Register the new ticker -- Fulfiling the TickerStatus.ON condition - await I_PolyToken.getTokens(web3.utils.toWei("1000"), account_temp); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_temp }); - let tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1000")), account_temp); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: account_temp }); + let tickersListArray = await I_Getter.getTickersByOwner.call(account_temp); console.log(tickersListArray); await I_STRProxied.registerTicker(account_temp, "LOG", "LOGAN", { from: account_temp }); - tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); + tickersListArray = await I_Getter.getTickersByOwner.call(account_temp); console.log(tickersListArray); // Generating the ST - let tx = await I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, dummy_token, "I am custom ST", latestTime(), { + let tx = await I_STRProxied.modifySecurityToken("LOGAN", "LOG", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_polymath }); - tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); + tickersListArray = await I_Getter.getTickersByOwner.call(account_temp); console.log(tickersListArray); assert.equal(tx.logs[1].args._ticker, "LOG", "Symbol should match with the registered symbol"); assert.equal( @@ -709,7 +787,7 @@ contract("SecurityTokenRegistry", accounts => { dummy_token, `Address of the SecurityToken should be matched with the input value of addCustomSecurityToken` ); - let symbolDetails = await I_STRProxied.getTickerDetails("LOG"); + let symbolDetails = await I_Getter.getTickerDetails("LOG"); assert.equal(symbolDetails[0], account_temp, `Owner of the symbol should be ${account_temp}`); assert.equal(symbolDetails[3], "LOGAN", `Name of the symbol should be LOGAN`); }); @@ -717,10 +795,10 @@ contract("SecurityTokenRegistry", accounts => { it("Should successfully generate the custom token", async () => { // Fulfilling the TickerStatus.NN condition // - // await catchRevert(I_STRProxied.modifySecurityToken("LOGAN2", "LOG2", account_temp, dummy_token, "I am custom ST", latestTime(), {from: account_polymath})); - // await I_STRProxied.modifyTicker(account_temp, "LOG2", "LOGAN2", latestTime(), latestTime() + duration.days(10), false, {from: account_polymath}); + // await catchRevert(I_STRProxied.modifySecurityToken("LOGAN2", "LOG2", account_temp, dummy_token, "I am custom ST", await latestTime(), {from: account_polymath})); + // await I_STRProxied.modifyTicker(account_temp, "LOG2", "LOGAN2", await latestTime(), currentTime.add(new BN(duration.days(10))), false, {from: account_polymath}); // await increaseTime(duration.days(1)); - let tx = await I_STRProxied.modifySecurityToken("LOGAN2", "LOG2", account_temp, dummy_token, "I am custom ST", latestTime(), { + let tx = await I_STRProxied.modifySecurityToken("LOGAN2", "LOG2", account_temp, dummy_token, "I am custom ST", currentTime, { from: account_polymath }); assert.equal(tx.logs[1].args._ticker, "LOG2", "Symbol should match with the registered symbol"); @@ -731,22 +809,31 @@ contract("SecurityTokenRegistry", accounts => { ); assert.equal(tx.logs[0].args._owner, account_temp, `Token owner should be ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "LOG2", `Symbol should be LOG2`); - let symbolDetails = await I_STRProxied.getTickerDetails("LOG2"); + let symbolDetails = await I_Getter.getTickerDetails("LOG2"); assert.equal(symbolDetails[0], account_temp, `Owner of the symbol should be ${account_temp}`); assert.equal(symbolDetails[3], "LOGAN2", `Name of the symbol should be LOGAN`); }); - it("Should successfully modify the ticker", async() => { + it("Should successfully modify the ticker", async () => { let snap_Id = await takeSnapshot(); - let tx = await I_STRProxied.modifyTicker(account_temp, "LOG2", "LOGAN2", latestTime(), latestTime() + duration.days(60), false, {from: account_polymath}); + let tx = await I_STRProxied.modifyTicker( + account_temp, + "LOG2", + "LOGAN2", + currentTime, + currentTime.add(new BN(duration.days(60))), + false, + { from: account_polymath } + ); await revertToSnapshot(snap_Id); - }) + }); }); describe("Test case for modifyTicker", async () => { it("Should add the custom ticker --failed because of bad owner", async () => { - catchRevert( - I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", latestTime(), latestTime() + duration.days(10), false, { + currentTime = new BN(await latestTime()); + await catchRevert( + I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", currentTime, currentTime.add(new BN(duration.days(10))), false, { from: account_temp }), "tx revert -> failed beacause of bad owner0" @@ -754,8 +841,8 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should add the custom ticker --fail ticker length should not be 0", async () => { - catchRevert( - I_STRProxied.modifyTicker(token_owner, "", "Ether", latestTime(), latestTime() + duration.days(10), false, { + await catchRevert( + I_STRProxied.modifyTicker(token_owner, "", "Ether", currentTime, currentTime.add(new BN(duration.days(10))), false, { from: account_polymath }), "tx revert -> failed beacause ticker length should not be 0" @@ -763,8 +850,8 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should add the custom ticker --failed because time should not be 0", async () => { - catchRevert( - I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", 0, latestTime() + duration.days(10), false, { + await catchRevert( + I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", new BN(0), currentTime.add(new BN(duration.days(10))), false, { from: account_polymath }), "tx revert -> failed because time should not be 0" @@ -772,8 +859,9 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should add the custom ticker --failed because registeration date is greater than the expiryDate", async () => { - catchRevert( - I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", latestTime(), latestTime() - duration.minutes(10), false, { + let ctime = currentTime; + await catchRevert( + I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", ctime, ctime.sub(new BN(duration.minutes(10))), false, { from: account_polymath }), "tx revert -> failed because registeration date is greater than the expiryDate" @@ -781,13 +869,14 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should add the custom ticker --failed because owner should not be 0x", async () => { - catchRevert( + let ctime = currentTime; + await catchRevert( I_STRProxied.modifyTicker( - "0x000000000000000000000000000000000000000000", + address_zero, "ETH", "Ether", - latestTime(), - latestTime() + duration.minutes(10), + ctime, + ctime.add(new BN(duration.minutes(10))), false, { from: account_polymath } ), @@ -796,12 +885,13 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should add the new custom ticker", async () => { + let ctime = currentTime; let tx = await I_STRProxied.modifyTicker( account_temp, "ETH", "Ether", - latestTime(), - latestTime() + duration.minutes(10), + ctime, + ctime.add(new BN(duration.minutes(10))), false, { from: account_polymath } ); @@ -810,12 +900,13 @@ contract("SecurityTokenRegistry", accounts => { }); it("Should change the details of the existing ticker", async () => { + let ctime = currentTime; let tx = await I_STRProxied.modifyTicker( token_owner, "ETH", "Ether", - latestTime(), - latestTime() + duration.minutes(10), + ctime, + ctime.add(new BN(duration.minutes(10))), false, { from: account_polymath } ); @@ -825,7 +916,7 @@ contract("SecurityTokenRegistry", accounts => { describe("Test cases for the transferTickerOwnership()", async () => { it("Should able to transfer the ticker ownership -- failed because token is not deployed having the same ticker", async () => { - catchRevert( + await catchRevert( I_STRProxied.transferTickerOwnership(account_issuer, "ETH", { from: account_temp }), "tx revert -> failed because token is not deployed having the same ticker" ); @@ -833,14 +924,14 @@ contract("SecurityTokenRegistry", accounts => { it("Should able to transfer the ticker ownership -- failed because new owner is 0x", async () => { await I_SecurityToken002.transferOwnership(account_temp, { from: token_owner }); - catchRevert( - I_STRProxied.transferTickerOwnership("0x00000000000000000000000000000000000000000", symbol2, { from: token_owner }), + await catchRevert( + I_STRProxied.transferTickerOwnership(address_zero, symbol2, { from: token_owner }), "tx revert -> failed because new owner is 0x" ); }); it("Should able to transfer the ticker ownership -- failed because ticker is of zero length", async () => { - catchRevert( + await catchRevert( I_STRProxied.transferTickerOwnership(account_temp, "", { from: token_owner }), "tx revert -> failed because ticker is of zero length" ); @@ -849,7 +940,7 @@ contract("SecurityTokenRegistry", accounts => { it("Should able to transfer the ticker ownership", async () => { let tx = await I_STRProxied.transferTickerOwnership(account_temp, symbol2, { from: token_owner, gas: 5000000 }); assert.equal(tx.logs[0].args._newOwner, account_temp); - let symbolDetails = await I_STRProxied.getTickerDetails.call(symbol2); + let symbolDetails = await I_Getter.getTickerDetails.call(symbol2); assert.equal(symbolDetails[0], account_temp, `Owner of the symbol should be ${account_temp}`); assert.equal(symbolDetails[3], name2, `Name of the symbol should be ${name2}`); }); @@ -857,37 +948,37 @@ contract("SecurityTokenRegistry", accounts => { describe("Test case for the changeSecurityLaunchFee()", async () => { it("Should able to change the STLaunchFee-- failed because of bad owner", async () => { - catchRevert( - I_STRProxied.changeSecurityLaunchFee(web3.utils.toWei("500"), { from: account_temp }), + await catchRevert( + I_STRProxied.changeSecurityLaunchFee(new BN(web3.utils.toWei("500")), { from: account_temp }), "tx revert -> failed because of bad owner" ); }); it("Should able to change the STLaunchFee-- failed because of putting the same fee", async () => { - catchRevert( + await catchRevert( I_STRProxied.changeSecurityLaunchFee(initRegFee, { from: account_polymath }), "tx revert -> failed because of putting the same fee" ); }); it("Should able to change the STLaunchFee", async () => { - let tx = await I_STRProxied.changeSecurityLaunchFee(web3.utils.toWei("500"), { from: account_polymath }); - assert.equal(tx.logs[0].args._newFee, web3.utils.toWei("500")); - let stLaunchFee = await I_STRProxied.getUintValues(web3.utils.soliditySha3("stLaunchFee")); - assert.equal(stLaunchFee, web3.utils.toWei("500")); + let tx = await I_STRProxied.changeSecurityLaunchFee(new BN(web3.utils.toWei("500")), { from: account_polymath }); + assert.equal(tx.logs[0].args._newFee.toString(), new BN(web3.utils.toWei("500")).toString()); + let stLaunchFee = await I_STRProxied.getUintValue(web3.utils.soliditySha3("stLaunchFee")); + assert.equal(stLaunchFee.toString(), new BN(web3.utils.toWei("500")).toString()); }); }); describe("Test cases for the changeExpiryLimit()", async () => { it("Should able to change the ExpiryLimit-- failed because of bad owner", async () => { - catchRevert( + await catchRevert( I_STRProxied.changeExpiryLimit(duration.days(15), { from: account_temp }), "tx revert -> failed because of bad owner" ); }); it("Should able to change the ExpiryLimit-- failed because expirylimit is less than 1 day", async () => { - catchRevert( + await catchRevert( I_STRProxied.changeExpiryLimit(duration.minutes(50), { from: account_polymath }), "tx revert -> failed because expirylimit is less than 1 day" ); @@ -896,63 +987,63 @@ contract("SecurityTokenRegistry", accounts => { it("Should able to change the ExpiryLimit", async () => { let tx = await I_STRProxied.changeExpiryLimit(duration.days(20), { from: account_polymath }); assert.equal(tx.logs[0].args._newExpiry, duration.days(20)); - let expiry = await I_STRProxied.getUintValues(web3.utils.soliditySha3("expiryLimit")); + let expiry = await I_STRProxied.getUintValue(web3.utils.soliditySha3("expiryLimit")); assert.equal(expiry, duration.days(20)); }); }); describe("Test cases for the changeTickerRegistrationFee()", async () => { it("Should able to change the TickerRegFee-- failed because of bad owner", async () => { - catchRevert( - I_STRProxied.changeTickerRegistrationFee(web3.utils.toWei("500"), { from: account_temp }), + await catchRevert( + I_STRProxied.changeTickerRegistrationFee(new BN(web3.utils.toWei("500")), { from: account_temp }), "tx revert -> failed because of bad owner" ); }); it("Should able to change the TickerRegFee-- failed because of putting the same fee", async () => { - catchRevert( + await catchRevert( I_STRProxied.changeTickerRegistrationFee(initRegFee, { from: account_polymath }), "tx revert -> failed because of putting the same fee" ); }); it("Should able to change the TickerRegFee", async () => { - let tx = await I_STRProxied.changeTickerRegistrationFee(web3.utils.toWei("400"), { from: account_polymath }); - assert.equal(tx.logs[0].args._newFee, web3.utils.toWei("400")); - let tickerRegFee = await I_STRProxied.getUintValues(web3.utils.soliditySha3("tickerRegFee")); - assert.equal(tickerRegFee, web3.utils.toWei("400")); + let tx = await I_STRProxied.changeTickerRegistrationFee(new BN(web3.utils.toWei("400")), { from: account_polymath }); + assert.equal(tx.logs[0].args._newFee.toString(), new BN(web3.utils.toWei("400")).toString()); + let tickerRegFee = await I_STRProxied.getUintValue(web3.utils.soliditySha3("tickerRegFee")); + assert.equal(tickerRegFee.toString(), new BN(web3.utils.toWei("400")).toString()); }); it("Should fail to register the ticker with the old fee", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - catchRevert( + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); + await catchRevert( I_STRProxied.registerTicker(token_owner, "POLY", "Polymath", { from: token_owner }), "tx revert -> failed because of ticker registeration fee gets change" ); }); it("Should register the ticker with the new fee", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1000"), token_owner); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: token_owner }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1600")), token_owner); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, "POLY", "Polymath", { from: token_owner }); assert.equal(tx.logs[0].args._owner, token_owner, `Token owner should be ${token_owner}`); assert.equal(tx.logs[0].args._ticker, "POLY", `Symbol should be POLY`); }); it("Should fail to launch the securityToken with the old launch fee", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - catchRevert( + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); + await catchRevert( I_STRProxied.generateSecurityToken("Polymath", "POLY", tokenDetails, false, { from: token_owner }), "tx revert -> failed because of old launch fee" ); }); it("Should launch the the securityToken", async () => { - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("2000")), { from: token_owner }); let tx = await I_STRProxied.generateSecurityToken("Polymath", "POLY", tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, "POLY", "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, "POLY", "SecurityToken doesn't get deployed"); }); }); @@ -966,7 +1057,7 @@ contract("SecurityTokenRegistry", accounts => { it("Should change the polytoken address -- failed because of 0x address", async () => { catchRevert( - I_STRProxied.updatePolyTokenAddress("0x0000000000000000000000000000000000000000000", { from: account_polymath }), + I_STRProxied.updatePolyTokenAddress(address_zero, { from: account_polymath }), "tx revert -> failed because 0x address" ); }); @@ -974,27 +1065,28 @@ contract("SecurityTokenRegistry", accounts => { it("Should successfully change the polytoken address", async () => { let _id = await takeSnapshot(); await I_STRProxied.updatePolyTokenAddress(dummy_token, { from: account_polymath }); - assert.equal(await I_STRProxied.getAddressValues.call(web3.utils.soliditySha3("polyToken")), dummy_token); + assert.equal(await I_STRProxied.getAddressValue.call(web3.utils.soliditySha3("polyToken")), dummy_token); await revertToSnapshot(_id); }); }); describe("Test cases for getters", async () => { it("Should get the security token address", async () => { - let address = await I_STRProxied.getSecurityTokenAddress.call(symbol); + let address = await I_Getter.getSecurityTokenAddress.call(symbol); assert.equal(address, I_SecurityToken.address); }); it("Should get the security token data", async () => { - let data = await I_STRProxied.getSecurityTokenData.call(I_SecurityToken.address); + let data = await I_Getter.getSecurityTokenData.call(I_SecurityToken.address); assert.equal(data[0], symbol); assert.equal(data[1], token_owner); }); it("Should get the tickers by owner", async () => { - let tickersList = await I_STRProxied.getTickersByOwner.call(token_owner); + let tickersList = await I_Getter.getTickersByOwner.call(token_owner); + console.log(tickersList); assert.equal(tickersList.length, 4); - let tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); + let tickersListArray = await I_Getter.getTickersByOwner.call(account_temp); console.log(tickersListArray); assert.equal(tickersListArray.length, 3); }); @@ -1002,14 +1094,14 @@ contract("SecurityTokenRegistry", accounts => { describe("Test case for the Removing the ticker", async () => { it("Should remove the ticker from the polymath ecosystem -- bad owner", async () => { - catchRevert( + await catchRevert( I_STRProxied.removeTicker(symbol2, { from: account_investor1 }), "tx revert -> failed because msg.sender should be account_polymath" ); }); it("Should remove the ticker from the polymath ecosystem -- fail because ticker doesn't exist in the ecosystem", async () => { - catchRevert( + await catchRevert( I_STRProxied.removeTicker("HOLA", { from: account_polymath }), "tx revert -> failed because ticker doesn't exist in the polymath ecosystem" ); @@ -1023,52 +1115,53 @@ contract("SecurityTokenRegistry", accounts => { describe(" Test cases of the registerTicker", async () => { it("Should register the ticker 1", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1000"), account_temp); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("1000"), { from: account_temp }); - let tx = await I_STRProxied.registerTicker(account_temp, "TOK1", "", { from: account_temp }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1600")), account_temp); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("1600")), { from: account_temp }); + let tx = await I_STRProxied.registerTicker(account_temp, "TOK1", "0x0", { from: account_temp }); assert.equal(tx.logs[0].args._owner, account_temp, `Owner should be the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "TOK1", `Symbol should be TOK1`); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); it("Should register the ticker 2", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1000"), account_temp); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("1000"), { from: account_temp }); - let tx = await I_STRProxied.registerTicker(account_temp, "TOK2", "", { from: account_temp }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1600")), account_temp); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("1600")), { from: account_temp }); + let tx = await I_STRProxied.registerTicker(account_temp, "TOK2", "0x0", { from: account_temp }); assert.equal(tx.logs[0].args._owner, account_temp, `Owner should be the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "TOK2", `Symbol should be TOK2`); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); it("Should register the ticker 3", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1000"), account_temp); - await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("1000"), { from: account_temp }); - let tx = await I_STRProxied.registerTicker(account_temp, "TOK3", "", { from: account_temp }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("1600")), account_temp); + await I_PolyToken.approve(I_STRProxied.address, new BN(web3.utils.toWei("1600")), { from: account_temp }); + let tx = await I_STRProxied.registerTicker(account_temp, "TOK3", "0x0", { from: account_temp }); assert.equal(tx.logs[0].args._owner, account_temp, `Owner should be the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "TOK3", `Symbol should be TOK3`); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); it("Should successfully remove the ticker 2", async () => { let tx = await I_STRProxied.removeTicker("TOK2", { from: account_polymath }); assert.equal(tx.logs[0].args._ticker, "TOK2", "Ticker doesn't get deleted successfully"); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); it("Should modify ticker 1", async () => { + currentTime = new BN(await latestTime()); let tx = await I_STRProxied.modifyTicker( account_temp, "TOK1", "TOKEN 1", - latestTime(), - latestTime() + duration.minutes(10), + currentTime, + currentTime.add(new BN(duration.minutes(10))), false, { from: account_polymath } ); assert.equal(tx.logs[0].args._owner, account_temp, `Should be equal to the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "TOK1", "Should be equal to TOK1"); assert.equal(tx.logs[0].args._name, "TOKEN 1", "Should be equal to TOKEN 1"); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); it("Should modify ticker 3", async () => { @@ -1076,31 +1169,26 @@ contract("SecurityTokenRegistry", accounts => { account_temp, "TOK3", "TOKEN 3", - latestTime(), - latestTime() + duration.minutes(10), + currentTime, + currentTime.add(new BN(duration.minutes(10))), false, { from: account_polymath } ); assert.equal(tx.logs[0].args._owner, account_temp, `Should be equal to the ${account_temp}`); assert.equal(tx.logs[0].args._ticker, "TOK3", "Should be equal to TOK3"); assert.equal(tx.logs[0].args._name, "TOKEN 3", "Should be equal to TOKEN 3"); - console.log((await I_STRProxied.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); + console.log((await I_Getter.getTickersByOwner.call(account_temp)).map(x => web3.utils.toUtf8(x))); }); }); describe("Test cases for IRegistry functionality", async () => { describe("Test cases for reclaiming funds", async () => { - - it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async() => { - I_PolyToken.transfer(I_STRProxied.address, web3.utils.toWei("1"), { from: token_owner }); - catchRevert( - I_STRProxied.reclaimERC20("0x000000000000000000000000000000000000000", { from: account_polymath }) - ); + it("Should successfully reclaim POLY tokens -- fail because token address will be 0x", async () => { + I_PolyToken.transfer(I_STRProxied.address, new BN(web3.utils.toWei("1")), { from: token_owner }); + await catchRevert(I_STRProxied.reclaimERC20(address_zero, { from: account_polymath })); }); - it("Should successfully reclaim POLY tokens -- not authorised", async() => { - catchRevert( - I_STRProxied.reclaimERC20(I_PolyToken.address, { from: account_temp }) - ); + it("Should successfully reclaim POLY tokens -- not authorised", async () => { + await catchRevert(I_STRProxied.reclaimERC20(I_PolyToken.address, { from: account_temp })); }); it("Should successfully reclaim POLY tokens", async () => { @@ -1108,84 +1196,71 @@ contract("SecurityTokenRegistry", accounts => { await I_STRProxied.reclaimERC20(I_PolyToken.address, { from: account_polymath }); let bal2 = await I_PolyToken.balanceOf.call(account_polymath); assert.isAtLeast( - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber(), - bal2.dividedBy(new BigNumber(10).pow(18)).toNumber() + bal2.div(new BN(10).pow(new BN(18))).toNumber(), + bal2.div(new BN(10).pow(new BN(18))).toNumber() ); }); }); describe("Test cases for pausing the contract", async () => { it("Should fail to pause if msg.sender is not owner", async () => { - catchRevert(I_STRProxied.pause({ from: account_temp }), "tx revert -> msg.sender should be account_polymath"); + await catchRevert(I_STRProxied.pause({ from: account_temp }), "tx revert -> msg.sender should be account_polymath"); }); it("Should successfully pause the contract", async () => { await I_STRProxied.pause({ from: account_polymath }); - let status = await I_STRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")); + let status = await I_STRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")); assert.isOk(status); }); it("Should fail to unpause if msg.sender is not owner", async () => { - catchRevert(I_STRProxied.unpause({ from: account_temp }), "tx revert -> msg.sender should be account_polymath"); + await catchRevert(I_STRProxied.unpause({ from: account_temp }), "tx revert -> msg.sender should be account_polymath"); }); it("Should successfully unpause the contract", async () => { await I_STRProxied.unpause({ from: account_polymath }); - let status = await I_STRProxied.getBoolValues.call(web3.utils.soliditySha3("paused")); + let status = await I_STRProxied.getBoolValue.call(web3.utils.soliditySha3("paused")); assert.isNotOk(status); }); }); - describe("Test cases for the setProtocolVersion", async() => { - - it("Should successfully change the protocolVersion -- failed because of bad owner", async() => { - catchRevert( - I_STRProxied.setProtocolVersion(accounts[8], 5, 6, 7, { from: account_temp }) - ); + describe("Test cases for the setProtocolVersion", async () => { + it("Should successfully change the protocolVersion -- failed because of bad owner", async () => { + await catchRevert(I_STRProxied.setProtocolVersion(accounts[8], 5, 6, 7, { from: account_temp })); }); - - it("Should successfully change the protocolVersion -- failed because factory address is 0x", async() => { - catchRevert( - I_STRProxied.setProtocolVersion("0x000000000000000000000000000000000000000", 5, 6, 7, { from: account_polymath }) + + it("Should successfully change the protocolVersion -- failed because factory address is 0x", async () => { + await catchRevert( + I_STRProxied.setProtocolVersion(address_zero, 5, 6, 7, { from: account_polymath }) ); }); - - it("Should successfully change the protocolVersion -- not a valid vesrion", async() => { - catchRevert( - I_STRProxied.setProtocolVersion(accounts[8], 0, 0, 0, { from: account_polymath }) - ); + + it("Should successfully change the protocolVersion -- not a valid vesrion", async () => { + await catchRevert(I_STRProxied.setProtocolVersion(accounts[8], new BN(0), new BN(0), new BN(0), { from: account_polymath })); }); - it("Should successfully change the protocolVersion -- fail in second attempt because of invalid version", async() => { + it("Should successfully change the protocolVersion -- fail in second attempt because of invalid version", async () => { let snap_Id = await takeSnapshot(); - await I_STRProxied.setProtocolVersion(accounts[8], 2, 3, 1, {from: account_polymath }); - await catchRevert( - I_STRProxied.setProtocolVersion(accounts[8], 1, 3, 1, {from: account_polymath }) - ); + await I_STRProxied.setProtocolVersion(accounts[8], 2, 3, 1, { from: account_polymath }); + await catchRevert(I_STRProxied.setProtocolVersion(accounts[8], 1, 3, 1, { from: account_polymath })); await revertToSnapshot(snap_Id); }); - }); - describe("Test cases for the transferOwnership", async() => { - - it("Should fail to transfer the ownership -- not authorised", async() => { - catchRevert( - I_STRProxied.transferOwnership(account_temp, { from: account_issuer}) - ); + describe("Test cases for the transferOwnership", async () => { + it("Should fail to transfer the ownership -- not authorised", async () => { + await catchRevert(I_STRProxied.transferOwnership(account_temp, { from: account_issuer })); }); - it("Should fail to transfer the ownership -- 0x address is not allowed", async() => { - catchRevert( - I_STRProxied.transferOwnership("0x000000000000000000000000000000000000000", { from: account_polymath}) - ); + it("Should fail to transfer the ownership -- 0x address is not allowed", async () => { + await catchRevert(I_STRProxied.transferOwnership(address_zero, { from: account_polymath })); }); - it("Should successfully transfer the ownership of the STR", async() => { + it("Should successfully transfer the ownership of the STR", async () => { let tx = await I_STRProxied.transferOwnership(account_temp, { from: account_polymath }); assert.equal(tx.logs[0].args.previousOwner, account_polymath); assert.equal(tx.logs[0].args.newOwner, account_temp); }); - }) + }); }); }); diff --git a/test/o_security_token.js b/test/o_security_token.js index b43db486d..24f29d4e6 100644 --- a/test/o_security_token.js +++ b/test/o_security_token.js @@ -9,7 +9,7 @@ import { deployCappedSTOAndVerifyed, deployMockRedemptionAndVerifyed, deployMockWrongTypeRedemptionAndVerifyed - } from "./helpers/createInstances"; +} from "./helpers/createInstances"; const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); const CappedSTO = artifacts.require("./CappedSTO.sol"); @@ -17,12 +17,13 @@ const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); const MockRedemptionManager = artifacts.require("./MockRedemptionManager.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("SecurityToken", accounts => { +contract("SecurityToken", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_investor1; @@ -36,7 +37,8 @@ contract("SecurityToken", accounts => { let account_delegate; let account_temp; let account_controller; - let address_zero = "0x0000000000000000000000000000000000000000"; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; let balanceOfReceiver; // investor Details @@ -46,6 +48,11 @@ contract("SecurityToken", accounts => { let ID_snap; const message = "Transaction Should Fail!!"; + const uri = "https://www.gogl.bts.fly"; + const docHash = web3.utils.utf8ToHex("hello"); + + const empty_hash = "0x0000000000000000000000000000000000000000000000000000000000000000"; + // Contract Instance Declaration let I_GeneralPermissionManagerFactory; @@ -67,6 +74,9 @@ contract("SecurityToken", accounts => { let I_PolymathRegistry; let I_MockRedemptionManagerFactory; let I_MockRedemptionManager; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details (Launched ST on the behalf of the issuer) const name = "Demo Token"; @@ -82,25 +92,32 @@ contract("SecurityToken", accounts => { const budget = 0; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); // delagate details - const delegateDetails = "I am delegate .."; - const TM_Perm = "FLAGS"; - const TM_Perm_Whitelist = "WHITELIST"; + const delegateDetails = web3.utils.fromAscii("I am delegate .."); + const TM_Perm = web3.utils.fromAscii("FLAGS"); + const TM_Perm_Whitelist = web3.utils.fromAscii("WHITELIST"); // Capped STO details let startTime; let endTime; - const cap = web3.utils.toWei("10000"); - const rate = 1000; + const cap = new BN(web3.utils.toWei("10000")); + const rate = new BN(web3.utils.toWei("1000")); const fundRaiseType = [0]; - const cappedSTOSetupCost = web3.utils.toWei("20000", "ether"); - const maxCost = cappedSTOSetupCost; + const cappedSTOSetupCost = new BN(web3.utils.toWei("20000", "ether")); + const cappedSTOSetupCostPOLY = new BN(web3.utils.toWei("80000", "ether")); + const maxCost = cappedSTOSetupCostPOLY; const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; + let currentTime; + + async function readStorage(contractAddress, slot) { + return await web3.eth.getStorageAt(contractAddress, slot); + } + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_affiliate1 = accounts[2]; @@ -129,13 +146,15 @@ contract("SecurityToken", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; // STEP 2: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); // STEP 3: Deploy the CappedSTOFactory - [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, cappedSTOSetupCost); + [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, cappedSTOSetupCost); // Printing all the contract addresses console.log(` @@ -166,198 +185,241 @@ contract("SecurityToken", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not - console.log(log.args); assert.equal(log.args._types[0].toNumber(), transferManagerKey); assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); assert.notEqual(I_GeneralTransferManager.address.valueOf(), address_zero, "GeneralTransferManager contract was not deployed"); }); - it("Should mint the tokens before attaching the STO -- fail only be called by the owner", async () => { - let fromTime = latestTime(); - let toTime = fromTime + duration.days(100); - let expiryTime = toTime + duration.days(100); + it("Should issue the tokens before attaching the STO -- fail only be called by the owner", async () => { + currentTime = new BN(await latestTime()); + let toTime = new BN(currentTime.add(new BN(duration.days(100)))); + let expiryTime = new BN(toTime.add(new BN(duration.days(100)))); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_affiliate1, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_affiliate1, currentTime, toTime, expiryTime, { from: token_owner, gas: 6000000 }); assert.equal(tx.logs[0].args._investor, account_affiliate1, "Failed in adding the investor in whitelist"); - await catchRevert(I_SecurityToken.mint(account_investor1, 100 * Math.pow(10, 18), { from: account_delegate })); + await catchRevert(I_SecurityToken.issue(account_investor1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: account_delegate })); }); - it("Should mint the tokens before attaching the STO", async () => { - await I_SecurityToken.mint(account_affiliate1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 }); + it("Should issue the tokens before attaching the STO", async () => { + await I_SecurityToken.issue(account_affiliate1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner }); let balance = await I_SecurityToken.balanceOf(account_affiliate1); - assert.equal(balance.dividedBy(new BigNumber(10).pow(18)).toNumber(), 100); + assert.equal(balance.div(new BN(10).pow(new BN(18))).toNumber(), 100); }); - it("Should mint the multi tokens before attaching the STO -- fail only be called by the owner", async () => { - let fromTime = latestTime(); - let toTime = fromTime + duration.days(100); - let expiryTime = toTime + duration.days(100); + it("Should issue the multi tokens before attaching the STO -- fail only be called by the owner", async () => { + currentTime = new BN(await latestTime()); + let toTime = new BN(currentTime.add(new BN(duration.days(100)))); + let expiryTime = new BN(toTime.add(new BN(duration.days(100)))); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_affiliate2, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_affiliate2, currentTime, toTime, expiryTime, { from: token_owner, gas: 6000000 }); assert.equal(tx.logs[0].args._investor, account_affiliate2, "Failed in adding the investor in whitelist"); await catchRevert( - I_SecurityToken.mintMulti([account_affiliate1, account_affiliate2], [100 * Math.pow(10, 18), 110 * Math.pow(10, 18)], { + I_SecurityToken.issueMulti([account_affiliate1, account_affiliate2], [new BN(100).mul(new BN(10).pow(new BN(18))), new BN(110).mul(new BN(10).pow(new BN(18)))], { from: account_delegate, gas: 500000 }) ); }); - it("Should mintMulti", async () => { + it("Should check the balance of the locked tokens", async() => { + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_affiliate1)).toString())}`); + console.log(`\t Locked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.utf8ToHex(`LOCKED`))).toString())}`); + console.log(`\t Unlocked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.utf8ToHex(`UNLOCKED`))).toString())}`); + assert.equal( + web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.utf8ToHex(`LOCKED`))).toString()), + 0 + ); + assert.equal( + web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.utf8ToHex(`UNLOCKED`))).toString()), + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_affiliate1)).toString()) + ); + console.log(`\t Wrong partition: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.toHex(`OCKED`))).toString())}`); + assert.equal( + web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_affiliate1, web3.utils.toHex(`OCKED`))).toString()), + 0 + ); + }); + + + it("Should issueMulti", async () => { await catchRevert( - I_SecurityToken.mintMulti([account_affiliate1, account_affiliate2], [100 * Math.pow(10, 18)], { + I_SecurityToken.issueMulti([account_affiliate1, account_affiliate2], [new BN(100).mul(new BN(10).pow(new BN(18)))], { from: token_owner, gas: 500000 }) ); }); - it("Should mint the tokens for multiple afiliated investors before attaching the STO", async () => { - await I_SecurityToken.mintMulti([account_affiliate1, account_affiliate2], [100 * Math.pow(10, 18), 110 * Math.pow(10, 18)], { - from: token_owner, - gas: 500000 + it("Should issue the tokens for multiple afiliated investors before attaching the STO", async () => { + await I_SecurityToken.issueMulti([account_affiliate1, account_affiliate2], [new BN(100).mul(new BN(10).pow(new BN(18))), new BN(110).mul(new BN(10).pow(new BN(18)))], { + from: token_owner }); let balance1 = await I_SecurityToken.balanceOf(account_affiliate1); - assert.equal(balance1.dividedBy(new BigNumber(10).pow(18)).toNumber(), 200); + assert.equal(balance1.div(new BN(10).pow(new BN(18))).toNumber(), 200); let balance2 = await I_SecurityToken.balanceOf(account_affiliate2); - assert.equal(balance2.dividedBy(new BigNumber(10).pow(18)).toNumber(), 110); + assert.equal(balance2.div(new BN(10).pow(new BN(18))).toNumber(), 110); + }); + it("Should ST be issuable", async() => { + assert.isTrue(await I_SecurityToken.isIssuable.call()); + }) + it("Should finish the minting -- fail because feature is not activated", async () => { - await catchRevert(I_SecurityToken.freezeMinting({ from: token_owner })); + await catchRevert(I_SecurityToken.freezeIssuance({ from: token_owner })); }); it("Should finish the minting -- fail to activate the feature because msg.sender is not polymath", async () => { - await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeMintingAllowed", true, { from: token_owner })); + await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeIssuanceAllowed", true, { from: token_owner })); }); it("Should finish the minting -- successfully activate the feature", async () => { - await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeMintingAllowed", false, { from: account_polymath })); - - assert.equal(false, await I_FeatureRegistry.getFeatureStatus("freezeMintingAllowed", { from: account_temp })); - await I_FeatureRegistry.setFeatureStatus("freezeMintingAllowed", true, { from: account_polymath }); - assert.equal(true, await I_FeatureRegistry.getFeatureStatus("freezeMintingAllowed", { from: account_temp })); + await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeIssuanceAllowed", false, { from: account_polymath })); + assert.equal(false, await I_FeatureRegistry.getFeatureStatus("freezeIssuanceAllowed", { from: account_temp })); + await I_FeatureRegistry.setFeatureStatus("freezeIssuanceAllowed", true, { from: account_polymath }); + assert.equal(true, await I_FeatureRegistry.getFeatureStatus("freezeIssuanceAllowed", { from: account_temp })); - await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeMintingAllowed", true, { from: account_polymath })); + await catchRevert(I_FeatureRegistry.setFeatureStatus("freezeIssuanceAllowed", true, { from: account_polymath })); }); it("Should finish the minting -- fail because msg.sender is not the owner", async () => { - await catchRevert(I_SecurityToken.freezeMinting({ from: account_temp })); + await catchRevert(I_SecurityToken.freezeIssuance({ from: account_temp })); }); it("Should finish minting & restrict the further minting", async () => { let id = await takeSnapshot(); - await I_SecurityToken.freezeMinting({ from: token_owner }); - - await catchRevert(I_SecurityToken.mint(account_affiliate1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 })); + await I_SecurityToken.freezeIssuance({ from: token_owner }); + assert.isFalse(await I_SecurityToken.isIssuable.call()); + await catchRevert(I_SecurityToken.issue(account_affiliate1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner, gas: 500000 })); await revertToSnapshot(id); }); it("Should fail to attach the STO factory because not enough poly in contract", async () => { - startTime = latestTime() + duration.seconds(5000); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - - await catchRevert(I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner })); + await catchRevert(I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner })); }); it("Should fail to attach the STO factory because max cost too small", async () => { - startTime = latestTime() + duration.seconds(5000); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); - await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCostPOLY, { from: token_owner }); await catchRevert( - I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, web3.utils.toWei("1000", "ether"), 0, { from: token_owner }) + I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, new BN(web3.utils.toWei("1000", "ether")), new BN(0), { from: token_owner }) ); }); + it("Should successfully add module with label", async () => { + let snapId = await takeSnapshot(); + startTime = await latestTime() + duration.seconds(5000); + endTime = startTime + duration.days(30); + let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); + + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCostPOLY, { from: token_owner }); + console.log("0"); + const tx = await I_SecurityToken.addModuleWithLabel(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), web3.utils.fromAscii("stofactory"), { + from: token_owner + }); + assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); + assert.equal(web3.utils.toUtf8(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); + console.log("module label is .. " + web3.utils.toAscii(tx.logs[3].args._label)); + assert(web3.utils.toAscii(tx.logs[3].args._label), "stofactory", "label doesnt match"); + I_CappedSTO = await CappedSTO.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + it("Should successfully attach the STO factory with the security token", async () => { - startTime = latestTime() + duration.seconds(5000); + startTime = await latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); - await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCost, { from: token_owner }); + await I_PolyToken.getTokens(cappedSTOSetupCostPOLY, token_owner); + await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCostPOLY, { from: token_owner }); - const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, new BN(0), { from: token_owner }); assert.equal(tx.logs[3].args._types[0], stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.toUtf8(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - I_CappedSTO = CappedSTO.at(tx.logs[3].args._module); + I_CappedSTO = await CappedSTO.at(tx.logs[3].args._module); }); - it("Should successfully mint tokens while STO attached", async () => { - await I_SecurityToken.mint(account_affiliate1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 }); + it("Should successfully issue tokens while STO attached", async () => { + await I_SecurityToken.issue(account_affiliate1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner }); + let balance = await I_SecurityToken.balanceOf(account_affiliate1); - assert.equal(balance.dividedBy(new BigNumber(10).pow(18)).toNumber(), 300); + assert.equal(balance.div(new BN(10).pow(new BN(18))).toNumber(), 300); }); - it("Should fail to mint tokens while STO attached after freezeMinting called", async () => { + it("Should fail to issue tokens while STO attached after freezeMinting called", async () => { let id = await takeSnapshot(); - await I_SecurityToken.freezeMinting({ from: token_owner }); + await I_SecurityToken.freezeIssuance({ from: token_owner }); - await catchRevert(I_SecurityToken.mint(account_affiliate1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 })); + await catchRevert(I_SecurityToken.issue(account_affiliate1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner })); await revertToSnapshot(id); }); }); describe("Module related functions", async () => { - it("Should get the modules of the securityToken by index", async () => { - let moduleData = await I_SecurityToken.getModule.call(I_CappedSTO.address); + it(" Should get the modules of the securityToken by name", async () => { + let moduleData = await stGetter.getModule.call(I_CappedSTO.address); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), "CappedSTO"); assert.equal(moduleData[1], I_CappedSTO.address); assert.equal(moduleData[2], I_CappedSTOFactory.address); assert.equal(moduleData[3], false); assert.equal(moduleData[4][0], 3); + assert.equal(moduleData[5], 0x0000000000000000000000000000000000000000); }); it("Should get the modules of the securityToken by index (not added into the security token yet)", async () => { - let moduleData = await I_SecurityToken.getModule.call(token_owner); + let moduleData = await stGetter.getModule.call(token_owner); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), ""); assert.equal(moduleData[1], address_zero); }); it("Should get the modules of the securityToken by name", async () => { - let moduleList = await I_SecurityToken.getModulesByName.call("CappedSTO"); + let moduleList = await stGetter.getModulesByName.call(web3.utils.fromAscii("CappedSTO")); assert.isTrue(moduleList.length == 1, "Only one STO"); - let moduleData = await I_SecurityToken.getModule.call(moduleList[0]); + let moduleData = await stGetter.getModule.call(moduleList[0]); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), "CappedSTO"); assert.equal(moduleData[1], I_CappedSTO.address); }); it("Should get the modules of the securityToken by name (not added into the security token yet)", async () => { - let moduleData = await I_SecurityToken.getModulesByName.call("GeneralPermissionManager"); - assert.isTrue(moduleData.length == 0, "No Permission Manager"); + let moduleData = await stGetter.getModulesByName.call(web3.utils.fromAscii("GeneralPermissionManager")); + assert.isTrue(moduleData.length == new BN(0), "No Permission Manager"); }); it("Should get the modules of the securityToken by name (not added into the security token yet)", async () => { - let moduleData = await I_SecurityToken.getModulesByName.call("CountTransferManager"); - assert.isTrue(moduleData.length == 0, "No Permission Manager"); + let moduleData = await stGetter.getModulesByName.call(web3.utils.fromAscii("CountTransferManager")); + assert.isTrue(moduleData.length == new BN(0), "No Permission Manager"); }); it("Should fail in updating the token details", async () => { @@ -370,7 +432,7 @@ contract("SecurityToken", accounts => { }); it("Should successfully remove the general transfer manager module from the securityToken -- fails msg.sender should be Owner", async () => { - await catchRevert(I_SecurityToken.removeModule(I_GeneralTransferManager.address, { from: token_owner })); + await catchRevert(I_SecurityToken.removeModule(I_GeneralTransferManager.address, { from: account_delegate })); }); it("Should fail to remove the module - module not archived", async () => { @@ -378,7 +440,7 @@ contract("SecurityToken", accounts => { }); it("Should fail to remove the module - incorrect address", async () => { - await catchRevert(I_SecurityToken.removeModule(0, { from: token_owner })); + await catchRevert(I_SecurityToken.removeModule(address_zero, { from: token_owner })); }); it("Should successfully remove the general transfer manager module from the securityToken", async () => { @@ -387,48 +449,53 @@ contract("SecurityToken", accounts => { let tx = await I_SecurityToken.removeModule(I_GeneralTransferManager.address, { from: token_owner }); assert.equal(tx.logs[0].args._types[0], transferManagerKey); assert.equal(tx.logs[0].args._module, I_GeneralTransferManager.address); - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("500"), {from: token_owner}); - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("200"), {from: account_investor1 }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 200); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("500")), "0x0", { from: token_owner }); + let _canTransfer = await I_SecurityToken.canTransfer.call(account_investor2, new BN(web3.utils.toWei("200")), "0x0", {from: account_investor1}); + + assert.isTrue(_canTransfer[0]); + assert.equal(_canTransfer[1], 0x51); + assert.equal(_canTransfer[2], empty_hash); + + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("200")), { from: account_investor1 }); + + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 200); await revertToSnapshot(key); }); - it("Should successfully remove the module from the middle of the names mapping", async() => { + it("Should successfully remove the module from the middle of the names mapping", async () => { let snap_Id = await takeSnapshot(); let D_GPM, D_GPM_1, D_GPM_2; let FactoryInstances; let GPMAddress = new Array(); - [D_GPM] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [D_GPM_1] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [D_GPM_2] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + [D_GPM] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + [D_GPM_1] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + [D_GPM_2] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); FactoryInstances = [D_GPM, D_GPM_1, D_GPM_2]; // Adding module in the ST for (let i = 0; i < FactoryInstances.length; i++) { - let tx = await I_SecurityToken.addModule(FactoryInstances[i].address, "", 0, 0, {from: token_owner }); - assert.equal(tx.logs[2].args._types[0], permissionManagerKey, "fail in adding the GPM") + let tx = await I_SecurityToken.addModule(FactoryInstances[i].address, "0x0", new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0], permissionManagerKey, "fail in adding the GPM"); GPMAddress.push(tx.logs[2].args._module); } // Archive the one of the module - await I_SecurityToken.archiveModule(GPMAddress[0], {from: token_owner}); + await I_SecurityToken.archiveModule(GPMAddress[0], { from: token_owner }); // Remove the module - let tx = await I_SecurityToken.removeModule(GPMAddress[0], {from: token_owner}); + let tx = await I_SecurityToken.removeModule(GPMAddress[0], { from: token_owner }); assert.equal(tx.logs[0].args._types[0], permissionManagerKey); assert.equal(tx.logs[0].args._module, GPMAddress[0]); await revertToSnapshot(snap_Id); }); - it("Should successfully archive the module first and fail during achiving the module again", async() => { + it("Should successfully archive the module first and fail during achiving the module again", async () => { let key = await takeSnapshot(); await I_SecurityToken.archiveModule(I_GeneralTransferManager.address, { from: token_owner }); - await catchRevert( - I_SecurityToken.archiveModule(I_GeneralTransferManager.address, { from: token_owner }) - ); + await catchRevert(I_SecurityToken.archiveModule(I_GeneralTransferManager.address, { from: token_owner })); await revertToSnapshot(key); }); it("Should verify the revertion of snapshot works properly", async () => { - let moduleData = await I_SecurityToken.getModule.call(I_GeneralTransferManager.address); + let moduleData = await stGetter.getModule.call(I_GeneralTransferManager.address); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), "GeneralTransferManager"); assert.equal(moduleData[1], I_GeneralTransferManager.address); }); @@ -437,18 +504,18 @@ contract("SecurityToken", accounts => { let tx = await I_SecurityToken.archiveModule(I_GeneralTransferManager.address, { from: token_owner }); assert.equal(tx.logs[0].args._types[0], transferManagerKey); assert.equal(tx.logs[0].args._module, I_GeneralTransferManager.address); - let moduleData = await I_SecurityToken.getModule.call(I_GeneralTransferManager.address); + let moduleData = await stGetter.getModule.call(I_GeneralTransferManager.address); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), "GeneralTransferManager"); assert.equal(moduleData[1], I_GeneralTransferManager.address); assert.equal(moduleData[2], I_GeneralTransferManagerFactory.address); assert.equal(moduleData[3], true); }); - it("Should successfully mint tokens while GTM archived", async () => { + it("Should successfully issue tokens while GTM archived", async () => { let key = await takeSnapshot(); - await I_SecurityToken.mint(1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 }); - let balance = await I_SecurityToken.balanceOf(1); - assert.equal(balance.dividedBy(new BigNumber(10).pow(18)).toNumber(), 100); + await I_SecurityToken.issue(one_address, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner, gas: 500000 }); + let balance = await I_SecurityToken.balanceOf(one_address); + assert.equal(balance.div(new BN(10).pow(new BN(18))).toNumber(), 100); await revertToSnapshot(key); }); @@ -456,7 +523,7 @@ contract("SecurityToken", accounts => { let tx = await I_SecurityToken.unarchiveModule(I_GeneralTransferManager.address, { from: token_owner }); assert.equal(tx.logs[0].args._types[0], transferManagerKey); assert.equal(tx.logs[0].args._module, I_GeneralTransferManager.address); - let moduleData = await I_SecurityToken.getModule.call(I_GeneralTransferManager.address); + let moduleData = await stGetter.getModule.call(I_GeneralTransferManager.address); assert.equal(web3.utils.toAscii(moduleData[0]).replace(/\u0000/g, ""), "GeneralTransferManager"); assert.equal(moduleData[1], I_GeneralTransferManager.address); assert.equal(moduleData[2], I_GeneralTransferManagerFactory.address); @@ -464,48 +531,42 @@ contract("SecurityToken", accounts => { }); it("Should successfully unarchive the general transfer manager module from the securityToken -- fail because module is already unarchived", async () => { - await catchRevert( - I_SecurityToken.unarchiveModule(I_GeneralTransferManager.address, { from: token_owner }) - ); + await catchRevert(I_SecurityToken.unarchiveModule(I_GeneralTransferManager.address, { from: token_owner })); }); - it("Should successfully archive the module -- fail because module is not existed", async() => { - await catchRevert( - I_SecurityToken.archiveModule(I_GeneralPermissionManagerFactory.address, { from: token_owner }) - ); - }) + it("Should successfully archive the module -- fail because module is not existed", async () => { + await catchRevert(I_SecurityToken.archiveModule(I_GeneralPermissionManagerFactory.address, { from: token_owner })); + }); - it("Should fail to mint tokens while GTM unarchived", async () => { - await catchRevert(I_SecurityToken.mint(1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 })); + it("Should fail to issue tokens while GTM unarchived", async () => { + await catchRevert(I_SecurityToken.issue(one_address, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner, gas: 500000 })); }); it("Should change the budget of the module - fail incorrect address", async () => { - await catchRevert(I_SecurityToken.changeModuleBudget(0, 100 * Math.pow(10, 18), true, { from: token_owner })); + await catchRevert(I_SecurityToken.changeModuleBudget(address_zero, new BN(100).mul(new BN(10).pow(new BN(18))), true, { from: token_owner })); }); it("Should change the budget of the module", async () => { let budget = await I_PolyToken.allowance.call(I_SecurityToken.address, I_CappedSTO.address); - let increaseAmount = 100 * Math.pow(10, 18); + let increaseAmount = new BN(100).mul(new BN(10).pow(new BN(18))); let tx = await I_SecurityToken.changeModuleBudget(I_CappedSTO.address, increaseAmount, true, { from: token_owner }); assert.equal(tx.logs[1].args._moduleTypes[0], stoKey); assert.equal(tx.logs[1].args._module, I_CappedSTO.address); - assert.equal(tx.logs[1].args._budget.toNumber(), budget.plus(increaseAmount).toNumber()); + assert.equal(tx.logs[1].args._budget.toString(), budget.add(increaseAmount).toString()); }); - it("Should change the budget of the module (decrease it)", async() => { + it("Should change the budget of the module (decrease it)", async () => { let budget = await I_PolyToken.allowance.call(I_SecurityToken.address, I_CappedSTO.address); - let decreaseAmount = 100 * Math.pow(10, 18); + let decreaseAmount = new BN(100).mul(new BN(10).pow(new BN(18))); let tx = await I_SecurityToken.changeModuleBudget(I_CappedSTO.address, decreaseAmount, false, { from: token_owner }); assert.equal(tx.logs[1].args._moduleTypes[0], stoKey); assert.equal(tx.logs[1].args._module, I_CappedSTO.address); - assert.equal(tx.logs[1].args._budget.toNumber(), budget.minus(decreaseAmount).toNumber()); + assert.equal(tx.logs[1].args._budget.toString(), budget.sub(decreaseAmount).toString()); }); - it("Should fail to get the total supply -- because checkpoint id is greater than present", async() => { - await catchRevert( - I_SecurityToken.totalSupplyAt.call(50) - ); - }) + it("Should fail to get the total supply -- because checkpoint id is greater than present", async () => { + await catchRevert(stGetter.totalSupplyAt.call(50)); + }); }); describe("General Transfer manager Related test cases", async () => { @@ -513,11 +574,11 @@ contract("SecurityToken", accounts => { balanceOfReceiver = await web3.eth.getBalance(account_fundsReceiver); // Add the Investor in to the whitelist - fromTime = latestTime(); + fromTime = await latestTime(); toTime = fromTime + duration.days(100); expiryTime = toTime + duration.days(100); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor1, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, fromTime, toTime, expiryTime, { from: token_owner, gas: 6000000 }); @@ -530,25 +591,30 @@ contract("SecurityToken", accounts => { from: account_investor1, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); console.log("AFTER"); - assert.equal((await I_CappedSTO.getRaised.call(0)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1); + assert.equal((await I_CappedSTO.getRaised.call(0)).div(new BN(10).pow(new BN(18))).toNumber(), 1); assert.equal(await I_CappedSTO.investorCount.call(), 1); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); it("Should Fail in transferring the token from one whitelist investor 1 to non whitelist investor 2", async () => { - await catchRevert(I_SecurityToken.transfer(account_investor2, 10 * Math.pow(10, 18), { from: account_investor1 })); + let _canTransfer = await I_SecurityToken.canTransfer.call(account_investor2, new BN(10).mul(new BN(10).pow(new BN(18))), "0x0", {from: account_investor1}); + + assert.isFalse(_canTransfer[0]); + assert.equal(_canTransfer[1], 0x50); + + await catchRevert(I_SecurityToken.transfer(account_investor2, new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1 })); }); it("Should fail to provide the permission to the delegate to change the transfer bools -- Bad owner", async () => { // Add permission to the deletgate (A regesteration process) - await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "", 0, 0, { from: token_owner }); - let moduleData = (await I_SecurityToken.getModulesByType(permissionManagerKey))[0]; - I_GeneralPermissionManager = GeneralPermissionManager.at(moduleData); + await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); + let moduleData = (await stGetter.getModulesByType(permissionManagerKey))[0]; + I_GeneralPermissionManager = await GeneralPermissionManager.at(moduleData); await catchRevert(I_GeneralPermissionManager.addDelegate(account_delegate, delegateDetails, { from: account_temp })); }); @@ -565,37 +631,51 @@ contract("SecurityToken", accounts => { it("Should activate the bool allowAllTransfer", async () => { ID_snap = await takeSnapshot(); let tx = await I_GeneralTransferManager.changeAllowAllTransfers(true, { from: account_delegate }); - assert.isTrue(tx.logs[0].args._allowAllTransfers, "AllowTransfer variable is not successfully updated"); }); it("Should fail to send tokens with the wrong granularity", async () => { - await catchRevert(I_SecurityToken.transfer(accounts[7], Math.pow(10, 17), { from: account_investor1 })); + await catchRevert(I_SecurityToken.transfer(accounts[7], new BN(10).pow(new BN(17)), { from: account_investor1 })); }); - it("Should adjust granularity", async () => { + it("Should not allow 0 granularity", async () => { await catchRevert(I_SecurityToken.changeGranularity(0, { from: token_owner })); }); it("Should adjust granularity", async () => { - await I_SecurityToken.changeGranularity(Math.pow(10, 17), { from: token_owner }); - await I_SecurityToken.transfer(accounts[7], Math.pow(10, 17), { from: account_investor1, gas: 2500000 }); - await I_SecurityToken.transfer(account_investor1, Math.pow(10, 17), { from: accounts[7], gas: 2500000 }); + await I_SecurityToken.changeGranularity(new BN(10).pow(new BN(17)), { from: token_owner }); + await I_SecurityToken.transfer(accounts[7], new BN(10).pow(new BN(17)), { from: account_investor1, gas: 2500000 }); + await I_SecurityToken.transfer(account_investor1, new BN(10).pow(new BN(17)), { from: accounts[7], gas: 2500000 }); + }); + + it("Should not allow unauthorized address to change data store", async () => { + await catchRevert(I_SecurityToken.changeDataStore(one_address, { from: account_polymath })); + }); + + it("Should not allow 0x0 address as data store", async () => { + await catchRevert(I_SecurityToken.changeDataStore(address_zero, { from: token_owner })); + }); + + it("Should change data store", async () => { + let ds = await I_SecurityToken.dataStore(); + await I_SecurityToken.changeDataStore(one_address, { from: token_owner }); + assert.equal(one_address, await I_SecurityToken.dataStore()); + await I_SecurityToken.changeDataStore(ds, { from: token_owner }); }); it("Should transfer from whitelist investor to non-whitelist investor in first tx and in 2nd tx non-whitelist to non-whitelist transfer", async () => { - await I_SecurityToken.transfer(accounts[7], 10 * Math.pow(10, 18), { from: account_investor1, gas: 2500000 }); + await I_SecurityToken.transfer(accounts[7], new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1, gas: 2500000 }); assert.equal( - (await I_SecurityToken.balanceOf(accounts[7])).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_SecurityToken.balanceOf(accounts[7])).div(new BN(10).pow(new BN(18))).toNumber(), 10, "Transfer doesn't take place properly" ); - await I_SecurityToken.transfer(account_temp, 5 * Math.pow(10, 18), { from: accounts[7], gas: 2500000 }); + await I_SecurityToken.transfer(account_temp, new BN(5).mul(new BN(10).pow(new BN(18))), { from: accounts[7], gas: 2500000 }); assert.equal( - (await I_SecurityToken.balanceOf(account_temp)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_SecurityToken.balanceOf(account_temp)).div(new BN(10).pow(new BN(18))).toNumber(), 5, "Transfer doesn't take place properly" ); @@ -614,60 +694,60 @@ contract("SecurityToken", accounts => { }); it("Should transfer from whitelist investor1 to whitelist investor 2", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor2, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor2, fromTime, toTime, expiryTime, { from: token_owner, gas: 500000 }); assert.equal(tx.logs[0].args._investor, account_investor2, "Failed in adding the investor in whitelist"); - await I_SecurityToken.transfer(account_investor2, 10 * Math.pow(10, 18), { from: account_investor1, gas: 2500000 }); + await I_SecurityToken.transfer(account_investor2, new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1, gas: 2500000 }); assert.equal( - (await I_SecurityToken.balanceOf(account_investor2)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_SecurityToken.balanceOf(account_investor2)).div(new BN(10).pow(new BN(18))).toNumber(), 10, "Transfer doesn't take place properly" ); }); it("Should transfer from whitelist investor1 to whitelist investor 2 -- value = 0", async () => { - let tx = await I_SecurityToken.transfer(account_investor2, 0, { from: account_investor1, gas: 2500000 }); + let tx = await I_SecurityToken.transfer(account_investor2, new BN(0), { from: account_investor1, gas: 2500000 }); assert.equal(tx.logs[0].args.value.toNumber(), 0); }); it("Should transferFrom from one investor to other", async () => { - await I_SecurityToken.approve(account_investor1, 2 * Math.pow(10, 18), { from: account_investor2 }); - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor3, fromTime, toTime, expiryTime, true, { + await I_SecurityToken.approve(account_investor1, new BN(2).mul(new BN(10).pow(new BN(18))), { from: account_investor2 }); + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor3, fromTime, toTime, expiryTime, { from: token_owner, gas: 500000 }); assert.equal(tx.logs[0].args._investor, account_investor3, "Failed in adding the investor in whitelist"); - let log = await I_SecurityToken.transferFrom(account_investor2, account_investor3, 2 * Math.pow(10, 18), { + let log = await I_SecurityToken.transferFrom(account_investor2, account_investor3, new BN(2).mul(new BN(10).pow(new BN(18))), { from: account_investor1 }); - assert.equal(log.logs[0].args.value.toNumber(), 2 * Math.pow(10, 18)); + assert.equal(log.logs[0].args.value.toString(), new BN(2).mul(new BN(10).pow(new BN(18))).toString()); }); it("Should Fail in trasferring from whitelist investor1 to non-whitelist investor", async () => { - await catchRevert(I_SecurityToken.transfer(account_temp, 10 * Math.pow(10, 18), { from: account_investor1, gas: 2500000 })); + await catchRevert(I_SecurityToken.transfer(account_temp, new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1, gas: 2500000 })); await revertToSnapshot(ID_snap); }); - it("Should successfully mint tokens while STO attached", async () => { - await I_SecurityToken.mint(account_affiliate1, 100 * Math.pow(10, 18), { from: token_owner, gas: 500000 }); + it("Should successfully issue tokens while STO attached", async () => { + await I_SecurityToken.issue(account_affiliate1, new BN(100).mul(new BN(10).pow(new BN(18))), "0x0", { from: token_owner }); let balance = await I_SecurityToken.balanceOf(account_affiliate1); - assert.equal(balance.dividedBy(new BigNumber(10).pow(18)).toNumber(), 400); + assert.equal(balance.div(new BN(10).pow(new BN(18))).toNumber(), 400); }); - it("Should mint the tokens for multiple afiliated investors while STO attached", async () => { - await I_SecurityToken.mintMulti([account_affiliate1, account_affiliate2], [100 * Math.pow(10, 18), 110 * Math.pow(10, 18)], { - from: token_owner, - gas: 500000 + it("Should issue the tokens for multiple afiliated investors while STO attached", async () => { + await I_SecurityToken.issueMulti([account_affiliate1, account_affiliate2], [new BN(100).mul(new BN(10).pow(new BN(18))), new BN(110).mul(new BN(10).pow(new BN(18)))], { + from: token_owner }); let balance1 = await I_SecurityToken.balanceOf(account_affiliate1); - assert.equal(balance1.dividedBy(new BigNumber(10).pow(18)).toNumber(), 500); + assert.equal(balance1.div(new BN(10).pow(new BN(18))).toNumber(), 500); let balance2 = await I_SecurityToken.balanceOf(account_affiliate2); - assert.equal(balance2.dividedBy(new BigNumber(10).pow(18)).toNumber(), 220); + assert.equal(balance2.div(new BN(10).pow(new BN(18))).toNumber(), 220); + }); it("Should provide more permissions to the delegate", async () => { @@ -682,7 +762,7 @@ contract("SecurityToken", accounts => { }); it("Should add the investor in the whitelist by the delegate", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist(account_temp, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_temp, fromTime, toTime, expiryTime, { from: account_delegate, gas: 6000000 }); @@ -696,33 +776,33 @@ contract("SecurityToken", accounts => { from: account_temp, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); - assert.equal((await I_CappedSTO.getRaised.call(0)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 2); + assert.equal((await I_CappedSTO.getRaised.call(0)).div(new BN(10).pow(new BN(18))).toNumber(), 2); assert.equal(await I_CappedSTO.investorCount.call(), 2); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), 1000); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).div(new BN(10).pow(new BN(18))).toNumber(), 1000); }); - it("STO should fail to mint tokens after minting is frozen", async () => { + it("STO should fail to issue tokens after minting is frozen", async () => { let id = await takeSnapshot(); - await I_SecurityToken.freezeMinting({ from: token_owner }); + await I_SecurityToken.freezeIssuance({ from: token_owner }); await catchRevert( web3.eth.sendTransaction({ from: account_temp, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }) ); await revertToSnapshot(id); }); it("Should remove investor from the whitelist by the delegate", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist(account_temp, 0, 0, 0, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_temp, new BN(0), new BN(0), new BN(0), { from: account_delegate, gas: 6000000 }); @@ -736,7 +816,7 @@ contract("SecurityToken", accounts => { from: account_temp, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }) ); }); @@ -751,7 +831,7 @@ contract("SecurityToken", accounts => { }); it("Should fail in buying to tokens", async () => { - let tx = await I_GeneralTransferManager.modifyWhitelist(account_temp, fromTime, toTime, expiryTime, true, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_temp, fromTime, toTime, expiryTime, { from: account_delegate, gas: 6000000 }); @@ -763,7 +843,7 @@ contract("SecurityToken", accounts => { from: account_temp, to: I_CappedSTO.address, gas: 2100000, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }) ); }); @@ -772,7 +852,7 @@ contract("SecurityToken", accounts => { await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(true, { from: token_owner }); console.log(await I_SecurityToken.balanceOf(account_investor1)); - await catchRevert(I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_temp })); + await catchRevert(I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_temp })); }); it("Should unfreeze all the transfers", async () => { @@ -785,13 +865,13 @@ contract("SecurityToken", accounts => { }); it("Should able to transfers the tokens from one user to another", async () => { - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("1", "ether"), { from: account_temp }); + await I_SecurityToken.transfer(account_investor1, new BN(web3.utils.toWei("1", "ether")), { from: account_temp }); }); it("Should check that the list of investors is correct", async () => { // Hardcode list of expected accounts based on transfers above - let investors = await I_SecurityToken.getInvestors(); + let investors = await stGetter.getInvestors.call(); let expectedAccounts = [account_affiliate1, account_affiliate2, account_investor1, account_temp]; for (let i = 0; i < expectedAccounts.length; i++) { assert.equal(investors[i], expectedAccounts[i]); @@ -828,38 +908,45 @@ contract("SecurityToken", accounts => { assert.equal(account_controller, controller, "Status not set correctly"); }); + it("Should ST be the controllable", async() => { + assert.isTrue(await I_SecurityToken.isControllable.call()); + }); + it("Should force burn the tokens - value too high", async () => { await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, { from: token_owner }); - let currentInvestorCount = await I_SecurityToken.getInvestorCount.call(); let currentBalance = await I_SecurityToken.balanceOf(account_temp); await catchRevert( - I_SecurityToken.forceBurn(account_temp, currentBalance + web3.utils.toWei("500", "ether"), "", "", { + I_SecurityToken.controllerRedeem(account_temp, currentBalance + new BN(web3.utils.toWei("500", "ether")), "0x0", "0x0", { from: account_controller }) ); }); it("Should force burn the tokens - wrong caller", async () => { await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, { from: token_owner }); - let currentInvestorCount = await I_SecurityToken.getInvestorCount.call(); let currentBalance = await I_SecurityToken.balanceOf(account_temp); - await catchRevert(I_SecurityToken.forceBurn(account_temp, currentBalance, "", "", { from: token_owner })); + let investors = await stGetter.getInvestors.call(); + for (let i = 0; i < investors.length; i++) { + console.log(investors[i]); + console.log(web3.utils.fromWei((await I_SecurityToken.balanceOf(investors[i])).toString())); + } + await catchRevert(I_SecurityToken.controllerRedeem(account_temp, currentBalance, "0x0", "0x0", { from: token_owner })); }); it("Should burn the tokens", async () => { - let currentInvestorCount = await I_SecurityToken.getInvestorCount.call(); + let currentInvestorCount = await I_SecurityToken.holderCount.call(); let currentBalance = await I_SecurityToken.balanceOf(account_temp); - // console.log(currentInvestorCount.toString(), currentBalance.toString()); - let tx = await I_SecurityToken.forceBurn(account_temp, currentBalance, "", "", { from: account_controller }); - // console.log(tx.logs[0].args._value.toNumber(), currentBalance.toNumber()); - assert.equal(tx.logs[0].args._value.toNumber(), currentBalance.toNumber()); - let newInvestorCount = await I_SecurityToken.getInvestorCount.call(); + let investors = await stGetter.getInvestors.call(); + let tx = await I_SecurityToken.controllerRedeem(account_temp, currentBalance, "0x0", "0x0", { from: account_controller }); + // console.log(tx.logs[1].args._value.toNumber(), currentBalance.toNumber()); + assert.equal(tx.logs[1].args._value.toString(), currentBalance.toString()); + let newInvestorCount = await I_SecurityToken.holderCount.call(); // console.log(newInvestorCount.toString()); assert.equal(newInvestorCount.toNumber() + 1, currentInvestorCount.toNumber(), "Investor count drops by one"); }); it("Should use getInvestorsAt to determine balances now", async () => { await I_SecurityToken.createCheckpoint({ from: token_owner }); - let investors = await I_SecurityToken.getInvestorsAt.call(1); + let investors = await stGetter.getInvestorsAt.call(1); console.log("Filtered investors:" + investors); let expectedAccounts = [account_affiliate1, account_affiliate2, account_investor1]; for (let i = 0; i < expectedAccounts.length; i++) { @@ -871,233 +958,232 @@ contract("SecurityToken", accounts => { it("Should prune investor length test #2", async () => { let balance = await I_SecurityToken.balanceOf(account_affiliate2); let balance2 = await I_SecurityToken.balanceOf(account_investor1); - await I_SecurityToken.transfer(account_affiliate1, balance, { from: account_affiliate2}); - await I_SecurityToken.transfer(account_affiliate1, balance2, { from: account_investor1}); + await I_SecurityToken.transfer(account_affiliate1, balance, { from: account_affiliate2 }); + await I_SecurityToken.transfer(account_affiliate1, balance2, { from: account_investor1 }); await I_SecurityToken.createCheckpoint({ from: token_owner }); - let investors = await I_SecurityToken.getInvestors.call(); + let investors = await stGetter.getInvestors.call(); console.log("All investors:" + investors); let expectedAccounts = [account_affiliate1, account_affiliate2, account_investor1, account_temp]; for (let i = 0; i < expectedAccounts.length; i++) { assert.equal(investors[i], expectedAccounts[i]); } assert.equal(investors.length, 4); - investors = await I_SecurityToken.getInvestorsAt.call(2); + investors = await stGetter.getInvestorsAt.call(2); console.log("Filtered investors:" + investors); expectedAccounts = [account_affiliate1]; for (let i = 0; i < expectedAccounts.length; i++) { assert.equal(investors[i], expectedAccounts[i]); } assert.equal(investors.length, 1); - await I_SecurityToken.transfer(account_affiliate2, balance, { from: account_affiliate1}); - await I_SecurityToken.transfer(account_investor1, balance2, { from: account_affiliate1}); + await I_SecurityToken.transfer(account_affiliate2, balance, { from: account_affiliate1 }); + await I_SecurityToken.transfer(account_investor1, balance2, { from: account_affiliate1 }); }); it("Should get filtered investors", async () => { - let investors = await I_SecurityToken.getInvestors.call(); + let investors = await stGetter.getInvestors.call(); console.log("All Investors: " + investors); - let filteredInvestors = await I_SecurityToken.iterateInvestors.call(0, 1); - console.log("Filtered Investors (0, 1): " + filteredInvestors); + let filteredInvestors = await stGetter.iterateInvestors.call(0, 0); + console.log("Filtered Investors (0, 0): " + filteredInvestors); assert.equal(filteredInvestors[0], investors[0]); assert.equal(filteredInvestors.length, 1); - filteredInvestors = await I_SecurityToken.iterateInvestors.call(2, 4); - console.log("Filtered Investors (2, 4): " + filteredInvestors); + filteredInvestors = await stGetter.iterateInvestors.call(2, 3); + console.log("Filtered Investors (2, 3): " + filteredInvestors); assert.equal(filteredInvestors[0], investors[2]); assert.equal(filteredInvestors[1], investors[3]); assert.equal(filteredInvestors.length, 2); - filteredInvestors = await I_SecurityToken.iterateInvestors.call(0, 4); - console.log("Filtered Investors (0, 4): " + filteredInvestors); + filteredInvestors = await stGetter.iterateInvestors.call(0, 3); + console.log("Filtered Investors (0, 3): " + filteredInvestors); assert.equal(filteredInvestors[0], investors[0]); assert.equal(filteredInvestors[1], investors[1]); assert.equal(filteredInvestors[2], investors[2]); assert.equal(filteredInvestors[3], investors[3]); assert.equal(filteredInvestors.length, 4); - await catchRevert( - I_SecurityToken.iterateInvestors(0, 5) - ); + await catchRevert(stGetter.iterateInvestors(0, 5)); }); it("Should check the balance of investor at checkpoint", async () => { - await catchRevert(I_SecurityToken.balanceOfAt(account_investor1, 5)); + await catchRevert(stGetter.balanceOfAt(account_investor1, 5)); }); it("Should check the balance of investor at checkpoint", async () => { - let balance = await I_SecurityToken.balanceOfAt(account_investor1, 0); + let balance = await stGetter.balanceOfAt(account_investor1, 0); assert.equal(balance.toNumber(), 0); }); }); - describe("Test cases for the Mock TrackedRedeemption", async() => { - - it("Should add the tracked redeemption module successfully", async() => { - [I_MockRedemptionManagerFactory] = await deployMockRedemptionAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - let tx = await I_SecurityToken.addModule(I_MockRedemptionManagerFactory.address, "", 0, 0, {from: token_owner }); + describe("Test cases for the Mock TrackedRedeemption", async () => { + it("Should add the tracked redeemption module successfully", async () => { + [I_MockRedemptionManagerFactory] = await deployMockRedemptionAndVerifyed(account_polymath, I_MRProxied, 0); + let tx = await I_SecurityToken.addModule(I_MockRedemptionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0], burnKey, "fail in adding the burn manager"); - I_MockRedemptionManager = MockRedemptionManager.at(tx.logs[2].args._module); + I_MockRedemptionManager = await MockRedemptionManager.at(tx.logs[2].args._module); // adding the burn module into the GTM - tx = await I_GeneralTransferManager.modifyWhitelist( + currentTime = new BN(await latestTime()); + tx = await I_GeneralTransferManager.modifyKYCData( I_MockRedemptionManager.address, - latestTime(), - latestTime() + duration.seconds(2), - latestTime() + duration.days(50), - true, + currentTime, + currentTime.add(new BN(duration.seconds(2))), + currentTime.add(new BN(duration.days(50))), { - from: account_delegate, - gas: 6000000 + from: account_delegate, + gas: 6000000 } ); assert.equal(tx.logs[0].args._investor, I_MockRedemptionManager.address, "Failed in adding the investor in whitelist"); }); - it("Should successfully burn tokens", async() => { - await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(false, {from: token_owner}); + it("Should successfully burn tokens", async () => { + await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(false, { from: token_owner }); // Minting some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1000"), {from: token_owner}); + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1000")), "0x0", { from: token_owner }); // Provide approval to trnafer the tokens to Module - await I_SecurityToken.approve(I_MockRedemptionManager.address, web3.utils.toWei("500"), {from: account_investor1}); + await I_SecurityToken.approve(I_MockRedemptionManager.address, new BN(web3.utils.toWei("500")), { from: account_investor1 }); // Allow all whitelist transfer - await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(true, {from: token_owner}); + await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(true, { from: token_owner }); // Transfer the tokens to module (Burn) - await I_MockRedemptionManager.transferToRedeem(web3.utils.toWei("500"), { from: account_investor1}); + await I_MockRedemptionManager.transferToRedeem(new BN(web3.utils.toWei("500")), { from: account_investor1 }); // Redeem tokens - let tx = await I_MockRedemptionManager.redeemTokenByOwner(web3.utils.toWei("250"), {from: account_investor1}); + let tx = await I_MockRedemptionManager.redeemTokenByOwner(new BN(web3.utils.toWei("250")), { from: account_investor1 }); assert.equal(tx.logs[0].args._investor, account_investor1, "Burn tokens of wrong owner"); - assert.equal((tx.logs[0].args._value).dividedBy(new BigNumber(10).pow(18)).toNumber(), 250); + assert.equal(tx.logs[0].args._value.div(new BN(10).pow(new BN(18))).toNumber(), 250); }); - it("Should fail to burn the tokens because module get archived", async() => { - await I_SecurityToken.archiveModule(I_MockRedemptionManager.address, {from: token_owner}); - await catchRevert( - I_MockRedemptionManager.redeemTokenByOwner(web3.utils.toWei("250"), {from: account_investor1}) - ); - }) + it("Should fail to burn the tokens because module get archived", async () => { + await I_SecurityToken.archiveModule(I_MockRedemptionManager.address, { from: token_owner }); + console.log(await stGetter.getModule.call(I_MockRedemptionManager.address)); + await catchRevert(I_MockRedemptionManager.redeemTokenByOwner(new BN(web3.utils.toWei("250")), { from: account_investor1 })); + }); - it("Should successfully fail in calling the burn functions", async() => { - [I_MockRedemptionManagerFactory] = await deployMockWrongTypeRedemptionAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - let tx = await I_SecurityToken.addModule(I_MockRedemptionManagerFactory.address, "", 0, 0, {from: token_owner }); - I_MockRedemptionManager = MockRedemptionManager.at(tx.logs[2].args._module); + it("Should successfully fail in calling the burn functions", async () => { + [I_MockRedemptionManagerFactory] = await deployMockWrongTypeRedemptionAndVerifyed(account_polymath, I_MRProxied, 0); + let tx = await I_SecurityToken.addModule(I_MockRedemptionManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); + I_MockRedemptionManager = await MockRedemptionManager.at(tx.logs[2].args._module); - // adding the burn module into the GTM - tx = await I_GeneralTransferManager.modifyWhitelist( + // adding the burn module into the GTM + currentTime = new BN(await latestTime()); + tx = await I_GeneralTransferManager.modifyKYCData( I_MockRedemptionManager.address, - latestTime(), - latestTime() + duration.seconds(2), - latestTime() + duration.days(50), - true, + currentTime, + currentTime.add(new BN(duration.seconds(2))), + currentTime.add(new BN(duration.days(50))), { - from: account_delegate, - gas: 6000000 + from: account_delegate, + gas: 6000000 } ); assert.equal(tx.logs[0].args._investor, I_MockRedemptionManager.address, "Failed in adding the investor in whitelist"); // Provide approval to trnafer the tokens to Module - await I_SecurityToken.approve(I_MockRedemptionManager.address, web3.utils.toWei("500"), {from: account_investor1}); + await I_SecurityToken.approve(I_MockRedemptionManager.address, new BN(web3.utils.toWei("500")), { from: account_investor1 }); // Transfer the tokens to module (Burn) - await I_MockRedemptionManager.transferToRedeem(web3.utils.toWei("500"), { from: account_investor1}); + await I_MockRedemptionManager.transferToRedeem(new BN(web3.utils.toWei("500")), { from: account_investor1 }); await catchRevert( // Redeem tokens - I_MockRedemptionManager.redeemTokenByOwner(web3.utils.toWei("250"), {from: account_investor1}) + I_MockRedemptionManager.redeemTokenByOwner(new BN(web3.utils.toWei("250")), { from: account_investor1 }) ); }); - - }) + }); describe("Withdraw Poly", async () => { - it("Should successfully withdraw the poly -- failed because of zero address of token", async() => { - await catchRevert(I_SecurityToken.withdrawERC20("0x00000000000000000000000000000000000000000", web3.utils.toWei("20000", "ether"), { from: account_temp })); - }) + it("Should successfully withdraw the poly -- failed because of zero address of token", async () => { + await catchRevert( + I_SecurityToken.withdrawERC20(address_zero, new BN(web3.utils.toWei("20000", "ether")), { + from: account_temp + }) + ); + }); it("Should successfully withdraw the poly", async () => { - await catchRevert(I_SecurityToken.withdrawERC20(I_PolyToken.address, web3.utils.toWei("20000", "ether"), { from: account_temp })); + await catchRevert( + I_SecurityToken.withdrawERC20(I_PolyToken.address, new BN(web3.utils.toWei("20000", "ether")), { from: account_temp }) + ); }); it("Should successfully withdraw the poly", async () => { let balanceBefore = await I_PolyToken.balanceOf(token_owner); - await I_SecurityToken.withdrawERC20(I_PolyToken.address, web3.utils.toWei("20000", "ether"), { from: token_owner }); + let stBalance = await I_PolyToken.balanceOf(I_SecurityToken.address); + await I_SecurityToken.withdrawERC20(I_PolyToken.address, new BN(stBalance), { from: token_owner }); let balanceAfter = await I_PolyToken.balanceOf(token_owner); assert.equal( - BigNumber(balanceAfter) - .sub(new BigNumber(balanceBefore)) - .toNumber(), - web3.utils.toWei("20000", "ether") + BN(balanceAfter) + .sub(new BN(balanceBefore)) + .toString(), + stBalance.toString() ); }); it("Should successfully withdraw the poly", async () => { - await catchRevert(I_SecurityToken.withdrawERC20(I_PolyToken.address, web3.utils.toWei("10", "ether"), { from: token_owner })); + await catchRevert(I_SecurityToken.withdrawERC20(I_PolyToken.address, new BN(web3.utils.toWei("10", "ether")), { from: token_owner })); }); }); describe("Force Transfer", async () => { - it("Should fail to forceTransfer because not approved controller", async () => { + it("Should fail to controllerTransfer because not approved controller", async () => { await catchRevert( - I_SecurityToken.forceTransfer(account_investor1, account_investor2, web3.utils.toWei("10", "ether"), "", "reason", { + I_SecurityToken.controllerTransfer(account_investor1, account_investor2, new BN(web3.utils.toWei("10", "ether")), "0x0", web3.utils.fromAscii("reason"), { from: account_investor1 }) ); }); - it("Should fail to forceTransfer because insufficient balance", async () => { + it("Should fail to controllerTransfer because insufficient balance", async () => { await catchRevert( - I_SecurityToken.forceTransfer(account_investor2, account_investor1, web3.utils.toWei("10", "ether"), "", "reason", { + I_SecurityToken.controllerTransfer(account_investor2, account_investor1, new BN(web3.utils.toWei("10", "ether")), "0x0", web3.utils.fromAscii("reason"), { from: account_controller }) ); }); - it("Should fail to forceTransfer because recipient is zero address", async () => { + it("Should fail to controllerTransfer because recipient is zero address", async () => { await catchRevert( - I_SecurityToken.forceTransfer(account_investor1, address_zero, web3.utils.toWei("10", "ether"), "", "reason", { + I_SecurityToken.controllerTransfer(account_investor1, address_zero, new BN(web3.utils.toWei("10", "ether")), "0x0", web3.utils.fromAscii("reason"), { from: account_controller }) ); }); - it("Should successfully forceTransfer", async () => { + it("Should successfully controllerTransfer", async () => { let sender = account_investor1; let receiver = account_investor2; - let start_investorCount = await I_SecurityToken.getInvestorCount.call(); + let start_investorCount = await I_SecurityToken.holderCount.call(); let start_balInv1 = await I_SecurityToken.balanceOf.call(account_investor1); let start_balInv2 = await I_SecurityToken.balanceOf.call(account_investor2); - let tx = await I_SecurityToken.forceTransfer( + let tx = await I_SecurityToken.controllerTransfer( account_investor1, account_investor2, - web3.utils.toWei("10", "ether"), - "", - "reason", + new BN(web3.utils.toWei("10", "ether")), + "0x0", + web3.utils.fromAscii("reason"), { from: account_controller } ); - let end_investorCount = await I_SecurityToken.getInvestorCount.call(); + let end_investorCount = await I_SecurityToken.holderCount.call(); let end_balInv1 = await I_SecurityToken.balanceOf.call(account_investor1); let end_balInv2 = await I_SecurityToken.balanceOf.call(account_investor2); - assert.equal(start_investorCount.add(1).toNumber(), end_investorCount.toNumber(), "Investor count not changed"); + assert.equal(start_investorCount.add(new BN(1)).toNumber(), end_investorCount.toNumber(), "Investor count not changed"); assert.equal( - start_balInv1.sub(web3.utils.toWei("10", "ether")).toNumber(), - end_balInv1.toNumber(), + start_balInv1.sub(new BN(web3.utils.toWei("10", "ether"))).toString(), + end_balInv1.toString(), "Investor balance not changed" ); assert.equal( - start_balInv2.add(web3.utils.toWei("10", "ether")).toNumber(), - end_balInv2.toNumber(), + start_balInv2.add(new BN(web3.utils.toWei("10", "ether"))).toString(), + end_balInv2.toString(), "Investor balance not changed" ); - console.log(tx.logs[0].args); - console.log(tx.logs[1].args); - assert.equal(account_controller, tx.logs[0].args._controller, "Event not emitted as expected"); - assert.equal(account_investor1, tx.logs[0].args._from, "Event not emitted as expected"); - assert.equal(account_investor2, tx.logs[0].args._to, "Event not emitted as expected"); - assert.equal(web3.utils.toWei("10", "ether"), tx.logs[0].args._value, "Event not emitted as expected"); - console.log(tx.logs[0].args._verifyTransfer); - assert.equal(false, tx.logs[0].args._verifyTransfer, "Event not emitted as expected"); - assert.equal("reason", web3.utils.hexToUtf8(tx.logs[0].args._data), "Event not emitted as expected"); - - assert.equal(account_investor1, tx.logs[1].args.from, "Event not emitted as expected"); - assert.equal(account_investor2, tx.logs[1].args.to, "Event not emitted as expected"); - assert.equal(web3.utils.toWei("10", "ether"), tx.logs[1].args.value, "Event not emitted as expected"); + let eventForceTransfer = tx.logs[1]; + let eventTransfer = tx.logs[0]; + assert.equal(account_controller, eventForceTransfer.args._controller, "Event not emitted as expected"); + assert.equal(account_investor1, eventForceTransfer.args._from, "Event not emitted as expected"); + assert.equal(account_investor2, eventForceTransfer.args._to, "Event not emitted as expected"); + assert.equal(new BN(web3.utils.toWei("10", "ether")).toString(), eventForceTransfer.args._value.toString(), "Event not emitted as expected"); + assert.equal("reason", web3.utils.hexToUtf8(eventForceTransfer.args._operatorData), "Event not emitted as expected"); + assert.equal(account_investor1, eventTransfer.args.from, "Event not emitted as expected"); + assert.equal(account_investor2, eventTransfer.args.to, "Event not emitted as expected"); + assert.equal(new BN(web3.utils.toWei("10", "ether")).toString(), eventTransfer.args.value.toString(), "Event not emitted as expected"); }); it("Should fail to freeze controller functionality because not owner", async () => { @@ -1122,6 +1208,10 @@ contract("SecurityToken", accounts => { assert.equal(true, await I_SecurityToken.controllerDisabled.call(), "State not changed"); }); + it("Should ST be not controllable", async() => { + assert.isFalse(await I_SecurityToken.isControllable.call()); + }); + it("Should fail to freeze controller functionality because already frozen", async () => { await catchRevert(I_SecurityToken.disableController({ from: token_owner })); }); @@ -1130,13 +1220,361 @@ contract("SecurityToken", accounts => { await catchRevert(I_SecurityToken.setController(account_controller, { from: token_owner })); }); - it("Should fail to forceTransfer because controller functionality frozen", async () => { + it("Should fail to controllerTransfer because controller functionality frozen", async () => { await catchRevert( - I_SecurityToken.forceTransfer(account_investor1, account_investor2, web3.utils.toWei("10", "ether"), "", "reason", { + I_SecurityToken.controllerTransfer(account_investor1, account_investor2, new BN(web3.utils.toWei("10", "ether")), "0x0", web3.utils.fromAscii("reason"), { from: account_controller }) ); }); + + }); + + describe("Test cases for the storage", async() => { + + it("Test the storage values of the ERC20 vairables", async() => { + let investors = await stGetter.getInvestors.call(); + + console.log("Verifying the balances of the Addresses"); + let index; + let key; + let newKey = new Array(); + + function encodeUint(data) { + return web3.eth.abi.encodeParameter('uint256', data); + } + + for (let i = 0; i < investors.length; i++) { + index = encodeUint(0); + key = web3.eth.abi.encodeParameter('address', investors[i]) + var tempKey = key + index.substring(2); + newKey.push(encodeUint(web3.utils.sha3(tempKey, {"encoding": "hex"}))); + } + + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[0])).toString()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[0]))).toString()) + ) + console.log(` + Balances from the contract: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[0])).toString())} + Balances from the storage: ${web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[0]))).toString())} + `) + + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[1])).toString()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[1]))).toString()) + ); + console.log(` + Balances from the contract: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[1])).toString())} + Balances from the storage: ${web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[1]))).toString())} + `) + + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[2])).toString()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[2]))).toString()) + ); + console.log(` + Balances from the contract: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[2])).toString())} + Balances from the storage: ${web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[2]))).toString())} + `) + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[3])).toString()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[3]))).toString()) + ) + console.log(` + Balances from the contract: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(investors[3])).toString())} + Balances from the storage: ${web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, newKey[3]))).toString())} + `) + assert.equal( + web3.utils.fromWei((await I_SecurityToken.totalSupply.call()).toString()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, 2))).toString()) + ); + console.log(` + TotalSupply from contract: ${web3.utils.fromWei((await I_SecurityToken.totalSupply.call()).toString())} + TotalSupply from the storage: ${web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, 2))).toString())} + `); + assert.equal( + await I_SecurityToken.name.call(), + (web3.utils.toAscii(await readStorage(I_SecurityToken.address, 3)).replace(/\u0000/g, "")).replace(/\u0014/g, "") + ) + console.log(` + Name of the ST: ${await I_SecurityToken.name.call()} + Name of the ST from the storage: ${web3.utils.toUtf8(await readStorage(I_SecurityToken.address, 3))} + `); + assert.equal( + await I_SecurityToken.symbol.call(), + (web3.utils.toUtf8(await readStorage(I_SecurityToken.address, 4)).replace(/\u0000/g, "")).replace(/\u0006/g, "") + ); + console.log(` + Symbol of the ST: ${await I_SecurityToken.symbol.call()} + Symbol of the ST from the storage: ${web3.utils.toUtf8(await readStorage(I_SecurityToken.address, 4))} + `); + + console.log(` + Address of the owner: ${await I_SecurityToken.owner.call()} + Address of the owner from the storage: ${(await readStorage(I_SecurityToken.address, 5)).substring(0, 42)} + `) + assert.equal( + await I_SecurityToken.owner.call(), + web3.utils.toChecksumAddress((await readStorage(I_SecurityToken.address, 5)).substring(0, 42)) + ); + + }); + + it("Verify the storage of the STStorage", async() => { + // for (let j = 7; j < 35; j++) { + // console.log(await readStorage(I_SecurityToken.address, j)); + // } + + console.log(` + Controller address from the contract: ${await stGetter.controller.call()} + Controller address from the storage: ${await readStorage(I_SecurityToken.address, 7)} + `) + // Different versions of web3 behave differently :/ + assert.oneOf( + await readStorage(I_SecurityToken.address, 7), + [ + (await stGetter.controller.call()).substring(0, 4), + (await stGetter.controller.call()).substring(0, 3), + await stGetter.controller.call() + ] + ); + + console.log(` + PolymathRegistry address from the contract: ${await stGetter.polymathRegistry.call()} + PolymathRegistry address from the storage: ${await readStorage(I_SecurityToken.address, 8)} + `) + + assert.equal( + await stGetter.polymathRegistry.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 8)) + ); + console.log(` + ModuleRegistry address from the contract: ${await stGetter.moduleRegistry.call()} + ModuleRegistry address from the storage: ${await readStorage(I_SecurityToken.address, 9)} + `) + + assert.equal( + await stGetter.moduleRegistry.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 9)) + ); + + console.log(` + SecurityTokenRegistry address from the contract: ${await stGetter.securityTokenRegistry.call()} + SecurityTokenRegistry address from the storage: ${await readStorage(I_SecurityToken.address, 10)} + `) + + assert.equal( + await stGetter.securityTokenRegistry.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 10)) + ); + + console.log(` + FeatureRegistry address from the contract: ${await stGetter.featureRegistry.call()} + FeatureRegistry address from the storage: ${await readStorage(I_SecurityToken.address, 11)} + `) + + assert.equal( + await stGetter.featureRegistry.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 11)) + ); + + console.log(` + PolyToken address from the contract: ${await stGetter.polyToken.call()} + PolyToken address from the storage: ${await readStorage(I_SecurityToken.address, 12)} + `) + + assert.equal( + await stGetter.polyToken.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 12)) + ); + + console.log(` + Delegate address from the contract: ${await stGetter.delegate.call()} + Delegate address from the storage: ${await readStorage(I_SecurityToken.address, 13)} + `) + + assert.equal( + await stGetter.delegate.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 13)) + ); + + console.log(` + Datastore address from the contract: ${await stGetter.dataStore.call()} + Datastore address from the storage: ${await readStorage(I_SecurityToken.address, 14)} + `) + + assert.equal( + await stGetter.dataStore.call(), + web3.utils.toChecksumAddress(await readStorage(I_SecurityToken.address, 14)) + ); + + console.log(` + Granularity value from the contract: ${await stGetter.granularity.call()} + Granularity value from the storage: ${(web3.utils.toBN(await readStorage(I_SecurityToken.address, 15))).toString()} + `) + + assert.equal( + web3.utils.fromWei(await stGetter.granularity.call()), + web3.utils.fromWei((web3.utils.toBN(await readStorage(I_SecurityToken.address, 15))).toString()) + ); + + console.log(` + Current checkpoint ID from the contract: ${await stGetter.currentCheckpointId.call()} + Current checkpoint ID from the storage: ${(web3.utils.toBN(await readStorage(I_SecurityToken.address, 16))).toString()} + `) + assert.equal( + await stGetter.currentCheckpointId.call(), + (web3.utils.toBN(await readStorage(I_SecurityToken.address, 16))).toString() + ); + + console.log(` + TokenDetails from the contract: ${await stGetter.tokenDetails.call()} + TokenDetails from the storage: ${(web3.utils.toUtf8((await readStorage(I_SecurityToken.address, 17)).substring(0, 60)))} + `) + assert.equal( + await stGetter.tokenDetails.call(), + (web3.utils.toUtf8((await readStorage(I_SecurityToken.address, 17)).substring(0, 60))).replace(/\u0000/g, "") + ); + + }); + + }); + + describe(`Test cases for the ERC1643 contract\n`, async () => { + + describe(`Test cases for the setDocument() function of the ERC1643\n`, async() => { + + it("\tShould failed in executing the setDocument() function because msg.sender is not authorised\n", async() => { + await catchRevert( + I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc1"), "https://www.gogl.bts.fly", "0x0", {from: account_temp}) + ); + }); + + it("\tShould failed to set a document details as name is empty\n", async() => { + await catchRevert( + I_SecurityToken.setDocument(web3.utils.utf8ToHex(""), "https://www.gogl.bts.fly", "0x0", {from: token_owner}) + ); + }); + + it("\tShould failed to set a document details as URI is empty\n", async() => { + await catchRevert( + I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc1"), "", "0x0", {from: token_owner}) + ); + }); + + it("\tShould sucessfully add the document details in the `_documents` mapping and change the length of the `_docsNames`\n", async() => { + let tx = await I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc1"), uri, docHash, {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc1"); + assert.equal(tx.logs[0].args._uri, uri); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._documentHash), web3.utils.toUtf8(docHash)); + assert.equal((await stGetter.getAllDocuments.call()).length, 1); + }); + + it("\tShould successfully add the new document and allow the empty docHash to be added in the `Document` structure\n", async() => { + let tx = await I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc2"), uri, "0x0", {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc2"); + assert.equal(tx.logs[0].args._uri, uri); + assert.equal(tx.logs[0].args._documentHash, empty_hash); + assert.equal((await stGetter.getAllDocuments.call()).length, 2); + }); + + it("\tShould successfully update the existing document and length of `_docsNames` should remain unaffected\n", async() => { + let tx = await I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc2"), "https://www.bts.l", "0x0", {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc2"); + assert.equal(tx.logs[0].args._uri, "https://www.bts.l"); + assert.equal(tx.logs[0].args._documentHash, empty_hash); + assert.equal((await stGetter.getAllDocuments.call()).length, 2); + }); + + describe("Test cases for the getters functions\n", async()=> { + + it("\tShould get the details of existed document\n", async() => { + let doc1Details = await stGetter.getDocument.call(web3.utils.utf8ToHex("doc1")); + assert.equal(doc1Details[0], uri); + assert.equal(web3.utils.toUtf8(doc1Details[1]), web3.utils.toUtf8(docHash)); + assert.closeTo(doc1Details[2].toNumber(), await latestTime(), 2); + + let doc2Details = await stGetter.getDocument.call(web3.utils.utf8ToHex("doc2")); + assert.equal(doc2Details[0], "https://www.bts.l"); + assert.equal(doc2Details[1], empty_hash); + assert.closeTo(doc2Details[2].toNumber(), await latestTime(), 2); + }); + + it("\tShould get the details of the non-existed document it means every value should be zero\n", async() => { + let doc3Details = await stGetter.getDocument.call(web3.utils.utf8ToHex("doc3")); + assert.equal(doc3Details[0], ""); + assert.equal(web3.utils.toUtf8(doc3Details[1]), ""); + assert.equal(doc3Details[2], 0); + }); + + it("\tShould get all the documents present in the contract\n", async() => { + let allDocs = await stGetter.getAllDocuments.call() + assert.equal(allDocs.length, 2); + assert.equal(web3.utils.toUtf8(allDocs[0]), "doc1"); + assert.equal(web3.utils.toUtf8(allDocs[1]), "doc2"); + }); + }) + }); + + describe("Test cases for the removeDocument()\n", async() => { + + it("\tShould failed to remove document because msg.sender is not authorised\n", async() => { + await catchRevert( + I_SecurityToken.removeDocument(web3.utils.utf8ToHex("doc2"), {from: account_temp}) + ); + }); + + it("\tShould failed to remove the document that is not existed in the contract\n", async() => { + await catchRevert( + I_SecurityToken.removeDocument(web3.utils.utf8ToHex("doc3"), {from: token_owner}) + ); + }); + + it("\tShould succssfully remove the document from the contract which is present in the last index of the `_docsName` and check the params of the `DocumentRemoved` event\n", async() => { + // first add the new document + await I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc3"), "https://www.bts.l", "0x0", {from: token_owner}); + // as this will be last in the array so remove this + let tx = await I_SecurityToken.removeDocument(web3.utils.utf8ToHex("doc3"), {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc3"); + assert.equal(tx.logs[0].args._uri, "https://www.bts.l"); + assert.equal(tx.logs[0].args._documentHash, empty_hash); + assert.equal((await stGetter.getAllDocuments.call()).length, 2); + + // remove the document that is not last in the `docsName` array + tx = await I_SecurityToken.removeDocument(web3.utils.utf8ToHex("doc1"), {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc1"); + assert.equal(tx.logs[0].args._uri, uri); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._documentHash), web3.utils.toUtf8(docHash)); + assert.equal((await stGetter.getAllDocuments.call()).length, 1); + }); + + it("\t Should delete the doc to validate the #17 issue problem", async() => { + let tx = await I_SecurityToken.removeDocument(web3.utils.utf8ToHex("doc2"), {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._name), "doc2"); + assert.equal(tx.logs[0].args._uri, "https://www.bts.l"); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._documentHash), ''); + assert.equal((await stGetter.getAllDocuments.call()).length, 0); + }); + + describe("Test cases for the getters functions\n", async()=> { + + it("\tShould get the details of the non-existed (earlier was present but get removed ) document it means every value should be zero\n", async() => { + let doc1Details = await stGetter.getDocument.call(web3.utils.utf8ToHex("doc1")); + assert.equal(doc1Details[0], ""); + assert.equal(web3.utils.toUtf8(doc1Details[1]), ""); + assert.equal(doc1Details[2], 0); + }); + + it("\tShould get all the documents present in the contract which should be 1\n", async() => { + // add one doc before the getter call + await I_SecurityToken.setDocument(web3.utils.utf8ToHex("doc4"), "https://www.bts.l", docHash, {from: token_owner}) + let allDocs = await stGetter.getAllDocuments.call() + assert.equal(allDocs.length, 1); + assert.equal(web3.utils.toUtf8(allDocs[0]), "doc4"); + }); + }); + }) }); }); diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index b9c825b2c..0aea11023 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -10,12 +10,15 @@ const MockOracle = artifacts.require("./MockOracle.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("USDTieredSTO", accounts => { +contract("USDTieredSTO", async (accounts) => { + let e18; + let e16; // Accounts Variable declaration let POLYMATH; let ISSUER; @@ -59,6 +62,9 @@ contract("USDTieredSTO", accounts => { let I_DaiToken; let I_PolymathRegistry; let P_USDTieredSTOFactory; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details for funds raise Type ETH const NAME = "Team"; @@ -71,14 +77,15 @@ contract("USDTieredSTO", accounts => { const STOKEY = 3; let snapId; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // Initial fee for ticker registry and security token registry - const REGFEE = web3.utils.toWei("250"); + let REGFEE; const STOSetupCost = 0; // MockOracle USD prices - const USDETH = new BigNumber(500).mul(10 ** 18); // 500 USD/ETH - const USDPOLY = new BigNumber(25).mul(10 ** 16); // 0.25 USD/POLY + let USDETH; // 500 USD/ETH + let USDPOLY; // 0.25 USD/POLY // STO Configuration Arrays let _startTime = []; @@ -157,43 +164,51 @@ contract("USDTieredSTO", accounts => { name: "_reserveWallet" }, { - type: "address", - name: "_usdToken" + type: "address[]", + name: "_usdTokens" } ] }; async function convert(_stoID, _tier, _discount, _currencyFrom, _currencyTo, _amount) { let USDTOKEN; - if (_discount) USDTOKEN = await I_USDTieredSTO_Array[_stoID].ratePerTierDiscountPoly.call(_tier); - else USDTOKEN = await I_USDTieredSTO_Array[_stoID].ratePerTier.call(_tier); + _amount = new BN(_amount); + if (_discount) USDTOKEN = (await I_USDTieredSTO_Array[_stoID].tiers.call(_tier))[1]; + else USDTOKEN = (await I_USDTieredSTO_Array[_stoID].tiers.call(_tier))[0]; + USDTOKEN = new BN(USDTOKEN); if (_currencyFrom == "TOKEN") { - let tokenToUSD = _amount - .div(10 ** 18) - .mul(USDTOKEN.div(10 ** 18)) - .mul(10 ** 18); // TOKEN * USD/TOKEN = USD + let tokenToUSD = new BN(_amount) + .mul(USDTOKEN) + .div(e18); if (_currencyTo == "USD") return tokenToUSD; if (_currencyTo == "ETH") { - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(ETH, tokenToUSD); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(ETH, tokenToUSD); } else if (_currencyTo == "POLY") { - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(POLY, tokenToUSD); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(POLY, tokenToUSD); } } if (_currencyFrom == "USD") { - if (_currencyTo == "TOKEN") return _amount.div(USDTOKEN).mul(10 ** 18); // USD / USD/TOKEN = TOKEN + if (_currencyTo == "TOKEN") return _amount.div(USDTOKEN).mul(e18); // USD / USD/TOKEN = TOKEN if (_currencyTo == "ETH" || _currencyTo == "POLY") - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(_currencyTo == "ETH" ? ETH : POLY, _amount); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(_currencyTo == "ETH" ? ETH : POLY, _amount); } if (_currencyFrom == "ETH" || _currencyFrom == "POLY") { - let ethToUSD = await I_USDTieredSTO_Array[_stoID].convertToUSD(_currencyTo == "ETH" ? ETH : POLY, _amount); + let ethToUSD = await I_USDTieredSTO_Array[_stoID].convertToUSD.call(_currencyTo == "ETH" ? ETH : POLY, _amount); if (_currencyTo == "USD") return ethToUSD; - if (_currencyTo == "TOKEN") return ethToUSD.div(USDTOKEN).mul(10 ** 18); // USD / USD/TOKEN = TOKEN + if (_currencyTo == "TOKEN") return ethToUSD.div(USDTOKEN).mul(e18); // USD / USD/TOKEN = TOKEN } return 0; } + let currentTime; + before(async () => { - // Accounts setup + e18 = new BN(10).pow(new BN(18)); + e16 = new BN(10).pow(new BN(16)); + currentTime = new BN(await latestTime()); + REGFEE = new BN(web3.utils.toWei("1000")); + USDETH = new BN(500).mul(new BN(10).pow(new BN(18))); // 500 USD/ETH + USDPOLY = new BN(25).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY POLYMATH = accounts[0]; ISSUER = accounts[1]; WALLET = accounts[2]; @@ -220,19 +235,22 @@ contract("USDTieredSTO", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter, + I_STGetter ] = instances; - + I_DaiToken = await PolyTokenFaucet.new({from: POLYMATH}); // STEP 4: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(POLYMATH, I_MRProxied, I_PolyToken.address, 0); + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(POLYMATH, I_MRProxied, 0); // STEP 5: Deploy the USDTieredSTOFactory - [I_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, I_PolyToken.address, STOSetupCost); - [P_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + [I_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, STOSetupCost); + [P_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, new BN(web3.utils.toWei("500"))); // Step 12: Deploy & Register Mock Oracles - I_USDOracle = await MockOracle.new(0, "ETH", "USD", USDETH, { from: POLYMATH }); // 500 dollars per POLY - I_POLYOracle = await MockOracle.new(I_PolyToken.address, "POLY", "USD", USDPOLY, { from: POLYMATH }); // 25 cents per POLY + I_USDOracle = await MockOracle.new(address_zero, web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), USDETH, { from: POLYMATH }); // 500 dollars per POLY + I_POLYOracle = await MockOracle.new(I_PolyToken.address, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), USDPOLY, { from: POLYMATH }); // 25 cents per POLY await I_PolymathRegistry.changeAddress("EthUsdOracle", I_USDOracle.address, { from: POLYMATH }); await I_PolymathRegistry.changeAddress("PolyUsdOracle", I_POLYOracle.address, { from: POLYMATH }); @@ -267,22 +285,22 @@ contract("USDTieredSTO", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.getTokens(REGFEE, ISSUER); await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(NAME, SYMBOL, TOKENDETAILS, true, { from: ISSUER }); - assert.equal(tx.logs[1].args._ticker, SYMBOL, "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + let tx = await I_STRProxied.generateSecurityToken(NAME, SYMBOL, TOKENDETAILS, true, { from: ISSUER }); + assert.equal(tx.logs[2].args._ticker, SYMBOL, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._types[0].toNumber(), TMKEY); + assert.equal(log.args._types[0].toString(), TMKEY); assert.equal(web3.utils.hexToString(log.args._name), "GeneralTransferManager"); }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(TMKEY))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(TMKEY))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); }); @@ -290,18 +308,18 @@ contract("USDTieredSTO", accounts => { it("Should successfully attach the first STO module to the security token", async () => { let stoId = 0; // No discount - _startTime.push(latestTime() + duration.days(2)); - _endTime.push(_startTime[stoId] + duration.days(100)); - _ratePerTier.push([BigNumber(10 * 10 ** 16), BigNumber(15 * 10 ** 16)]); // [ 0.10 USD/Token, 0.15 USD/Token ] - _ratePerTierDiscountPoly.push([BigNumber(10 * 10 ** 16), BigNumber(15 * 10 ** 16)]); // [ 0.10 USD/Token, 0.15 USD/Token ] - _tokensPerTierTotal.push([BigNumber(100000000).mul(new BigNumber(10 ** 18)), BigNumber(200000000).mul(new BigNumber(10 ** 18))]); // [ 100m Token, 200m Token ] - _tokensPerTierDiscountPoly.push([BigNumber(0), BigNumber(0)]); // [ 0, 0 ] - _nonAccreditedLimitUSD.push(new BigNumber(10000).mul(new BigNumber(10 ** 18))); // 10k USD - _minimumInvestmentUSD.push(new BigNumber(5 * 10 ** 18)); // 5 USD + _startTime.push(new BN(currentTime).add(new BN(duration.days(2)))); + _endTime.push(_startTime[stoId].add(new BN(duration.days(100)))); + _ratePerTier.push([new BN(10).mul(e16), new BN(15).mul(e16)]); // [ 0.10 USD/Token, 0.15 USD/Token ] + _ratePerTierDiscountPoly.push([new BN(10).mul(e16), new BN(15).mul(e16)]); // [ 0.10 USD/Token, 0.15 USD/Token ] + _tokensPerTierTotal.push([new BN(100000000).mul(new BN(e18)), new BN(200000000).mul(new BN(e18))]); // [ 100m Token, 200m Token ] + _tokensPerTierDiscountPoly.push([new BN(0), new BN(0)]); // [ new BN(0), 0 ] + _nonAccreditedLimitUSD.push(new BN(10000).mul(new BN(e18))); // 10k USD + _minimumInvestmentUSD.push(new BN(5).mul(e18)); // 5 USD _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -319,44 +337,44 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER, gasPrice: GAS_PRICE }); + let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER, gasPrice: GAS_PRICE }); console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); - assert.equal(await I_USDTieredSTO_Array[stoId].startTime.call(), _startTime[stoId], "Incorrect _startTime in config"); - assert.equal(await I_USDTieredSTO_Array[stoId].endTime.call(), _endTime[stoId], "Incorrect _endTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].startTime.call()).toString(), _startTime[stoId].toString(), "Incorrect _startTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].endTime.call()).toString(), _endTime[stoId].toString(), "Incorrect _endTime in config"); for (var i = 0; i < _ratePerTier[stoId].length; i++) { assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTier.call(i)).toNumber(), - _ratePerTier[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[0].toString(), + _ratePerTier[stoId][i].toString(), "Incorrect _ratePerTier in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTierDiscountPoly.call(i)).toNumber(), - _ratePerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[1].toString(), + _ratePerTierDiscountPoly[stoId][i].toString(), "Incorrect _ratePerTierDiscountPoly in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(i)).toNumber(), - _tokensPerTierTotal[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[2].toString(), + _tokensPerTierTotal[stoId][i].toString(), "Incorrect _tokensPerTierTotal in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierDiscountPoly.call(i)).toNumber(), - _tokensPerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[3].toString(), + _tokensPerTierDiscountPoly[stoId][i].toString(), "Incorrect _tokensPerTierDiscountPoly in config" ); } assert.equal( - (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toNumber(), - _nonAccreditedLimitUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toString(), + _nonAccreditedLimitUSD[stoId].toString(), "Incorrect _nonAccreditedLimitUSD in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toNumber(), - _minimumInvestmentUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toString(), + _minimumInvestmentUSD[stoId].toString(), "Incorrect _minimumInvestmentUSD in config" ); assert.equal(await I_USDTieredSTO_Array[stoId].wallet.call(), _wallet[stoId], "Incorrect _wallet in config"); @@ -365,16 +383,16 @@ contract("USDTieredSTO", accounts => { _reserveWallet[stoId], "Incorrect _reserveWallet in config" ); - assert.equal(await I_USDTieredSTO_Array[stoId].usdToken.call(), _usdToken[stoId], "Incorrect _usdToken in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].getUsdTokens())[0], _usdToken[stoId][0], "Incorrect _usdToken in config"); assert.equal( await I_USDTieredSTO_Array[stoId].getNumberOfTiers(), _tokensPerTierTotal[stoId].length, "Incorrect number of tiers" ); - assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, 0, "Incorrect number of permissions"); + assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, new BN(0), "Incorrect number of permissions"); }); - it("Should attach the paid STO factory -- failed because of no tokens", async() => { + it("Should attach the paid STO factory -- failed because of no tokens", async () => { let stoId = 0; // No discount let config = [ _startTime[stoId], @@ -393,11 +411,14 @@ contract("USDTieredSTO", accounts => { let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); await catchRevert( - I_SecurityToken.addModule(P_USDTieredSTOFactory.address, bytesSTO, web3.utils.toWei("500"), 0, { from: ISSUER, gasPrice: GAS_PRICE }) + I_SecurityToken.addModule(P_USDTieredSTOFactory.address, bytesSTO, new BN(web3.utils.toWei("2000")), new BN(0), { + from: ISSUER, + gasPrice: GAS_PRICE + }) ); }); - it("Should attach the paid STO factory", async() => { + it("Should attach the paid STO factory", async () => { let snapId = await takeSnapshot(); let stoId = 0; // No discount let config = [ @@ -416,8 +437,11 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await I_PolyToken.getTokens(web3.utils.toWei("500"), I_SecurityToken.address); - let tx = await I_SecurityToken.addModule(P_USDTieredSTOFactory.address, bytesSTO, web3.utils.toWei("500"), 0, { from: ISSUER, gasPrice: GAS_PRICE }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), I_SecurityToken.address); + let tx = await I_SecurityToken.addModule(P_USDTieredSTOFactory.address, bytesSTO, new BN(web3.utils.toWei("2000")), new BN(0), { + from: ISSUER, + gasPrice: GAS_PRICE + }); await revertToSnapshot(snapId); }); @@ -429,64 +453,62 @@ contract("USDTieredSTO", accounts => { }); it("Should allow non-matching beneficiary -- failed because it is already active", async () => { - await catchRevert( - I_USDTieredSTO_Array[0].changeAllowBeneficialInvestments(true, { from: ISSUER }) - ); + await catchRevert(I_USDTieredSTO_Array[0].changeAllowBeneficialInvestments(true, { from: ISSUER })); await revertToSnapshot(snapId); }); - it("Should successfully call the modifyTimes before starting the STO -- fail because of bad owner", async() => { + it("Should successfully call the modifyTimes before starting the STO -- fail because of bad owner", async () => { await catchRevert( - I_USDTieredSTO_Array[0].modifyTimes(latestTime() + duration.days(15), latestTime() + duration.days(55), { from: POLYMATH }) + I_USDTieredSTO_Array[0].modifyTimes(new BN(currentTime).add(new BN(duration.days(15))), new BN(currentTime).add(new BN(duration.days(55))), { from: POLYMATH }) ); - }) + }); - it("Should successfully call the modifyTimes before starting the STO", async() => { + it("Should successfully call the modifyTimes before starting the STO", async () => { let snapId = await takeSnapshot(); - let _startTime = latestTime() + duration.days(15); - let _endTime = latestTime() + duration.days(55) + let _startTime = new BN(currentTime).add(new BN(duration.days(15))); + let _endTime = new BN(currentTime).add(new BN(duration.days(55))); await I_USDTieredSTO_Array[0].modifyTimes(_startTime, _endTime, { from: ISSUER }); - assert.equal(await I_USDTieredSTO_Array[0].startTime.call(), _startTime, "Incorrect _startTime in config"); - assert.equal(await I_USDTieredSTO_Array[0].endTime.call(), _endTime, "Incorrect _endTime in config"); + assert.equal((await I_USDTieredSTO_Array[0].startTime.call()).toString(), _startTime.toString(), "Incorrect _startTime in config"); + assert.equal((await I_USDTieredSTO_Array[0].endTime.call()).toString(), _endTime.toString(), "Incorrect _endTime in config"); await revertToSnapshot(snapId); }); it("Should successfully attach the second STO module to the security token", async () => { let stoId = 1; // No discount - _startTime.push(latestTime() + duration.days(2)); - _endTime.push(_startTime[stoId] + duration.days(100)); + _startTime.push(new BN(currentTime).add(new BN(duration.days(2)))); + _endTime.push(new BN(_startTime[stoId]).add(new BN(currentTime).add(new BN(duration.days(100))))); _ratePerTier.push([ - BigNumber(10 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16) + new BN(10).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16) ]); _ratePerTierDiscountPoly.push([ - BigNumber(10 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16), - BigNumber(15 * 10 ** 16) + new BN(10).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16), + new BN(15).mul(e16) ]); _tokensPerTierTotal.push([ - BigNumber(5 * 10 ** 18), - BigNumber(10 * 10 ** 18), - BigNumber(10 * 10 ** 18), - BigNumber(10 * 10 ** 18), - BigNumber(10 * 10 ** 18), - BigNumber(50 * 10 ** 18) + new BN(5).mul(e18), + new BN(10).mul(e18), + new BN(10).mul(e18), + new BN(10).mul(e18), + new BN(10).mul(e18), + new BN(50).mul(e18) ]); - _tokensPerTierDiscountPoly.push([BigNumber(0), BigNumber(0), BigNumber(0), BigNumber(0), BigNumber(0), BigNumber(0)]); - _nonAccreditedLimitUSD.push(new BigNumber(10000).mul(new BigNumber(10 ** 18))); - _minimumInvestmentUSD.push(new BigNumber(0)); + _tokensPerTierDiscountPoly.push([new BN(0), new BN(0), new BN(0), new BN(0), new BN(0), new BN(0)]); + _nonAccreditedLimitUSD.push(new BN(10000).mul(new BN(e18))); + _minimumInvestmentUSD.push(new BN(0)); _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -504,44 +526,44 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER, gasPrice: GAS_PRICE }); + let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER, gasPrice: GAS_PRICE }); console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); - assert.equal(await I_USDTieredSTO_Array[stoId].startTime.call(), _startTime[stoId], "Incorrect _startTime in config"); - assert.equal(await I_USDTieredSTO_Array[stoId].endTime.call(), _endTime[stoId], "Incorrect _endTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].startTime.call()).toString(), _startTime[stoId].toString(), "Incorrect _startTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].endTime.call()).toString(), _endTime[stoId].toString(), "Incorrect _endTime in config"); for (var i = 0; i < _ratePerTier[stoId].length; i++) { assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTier.call(i)).toNumber(), - _ratePerTier[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[0].toString(), + _ratePerTier[stoId][i].toString(), "Incorrect _ratePerTier in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTierDiscountPoly.call(i)).toNumber(), - _ratePerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[1].toString(), + _ratePerTierDiscountPoly[stoId][i].toString(), "Incorrect _ratePerTierDiscountPoly in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(i)).toNumber(), - _tokensPerTierTotal[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[2].toString(), + _tokensPerTierTotal[stoId][i].toString(), "Incorrect _tokensPerTierTotal in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierDiscountPoly.call(i)).toNumber(), - _tokensPerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[3].toString(), + _tokensPerTierDiscountPoly[stoId][i].toString(), "Incorrect _tokensPerTierDiscountPoly in config" ); } assert.equal( - (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toNumber(), - _nonAccreditedLimitUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toString(), + _nonAccreditedLimitUSD[stoId].toString(), "Incorrect _nonAccreditedLimitUSD in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toNumber(), - _minimumInvestmentUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toString(), + _minimumInvestmentUSD[stoId].toString(), "Incorrect _minimumInvestmentUSD in config" ); assert.equal(await I_USDTieredSTO_Array[stoId].wallet.call(), _wallet[stoId], "Incorrect _wallet in config"); @@ -550,31 +572,29 @@ contract("USDTieredSTO", accounts => { _reserveWallet[stoId], "Incorrect _reserveWallet in config" ); - assert.equal(await I_USDTieredSTO_Array[stoId].usdToken.call(), _usdToken[stoId], "Incorrect _usdToken in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].getUsdTokens())[0], _usdToken[stoId][0], "Incorrect _usdToken in config"); assert.equal( await I_USDTieredSTO_Array[stoId].getNumberOfTiers(), _tokensPerTierTotal[stoId].length, "Incorrect number of tiers" ); - assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, 0, "Incorrect number of permissions"); + assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, new BN(0), "Incorrect number of permissions"); }); it("Should successfully attach the third STO module to the security token", async () => { let stoId = 2; // Poly discount - - _startTime.push(latestTime() + duration.days(2)); - _endTime.push(_startTime[stoId] + duration.days(100)); - _ratePerTier.push([BigNumber(1 * 10 ** 18), BigNumber(1.5 * 10 ** 18)]); // [ 1 USD/Token, 1.5 USD/Token ] - _ratePerTierDiscountPoly.push([BigNumber(0.5 * 10 ** 18), BigNumber(1 * 10 ** 18)]); // [ 0.5 USD/Token, 1.5 USD/Token ] - _tokensPerTierTotal.push([BigNumber(100 * 10 ** 18), BigNumber(50 * 10 ** 18)]); // [ 100 Token, 50 Token ] - _tokensPerTierDiscountPoly.push([BigNumber(100 * 10 ** 18), BigNumber(25 * 10 ** 18)]); // [ 100 Token, 25 Token ] - _nonAccreditedLimitUSD.push(new BigNumber(25 * 10 ** 18)); // [ 25 USD ] - _minimumInvestmentUSD.push(new BigNumber(5)); + _startTime.push(new BN(currentTime).add(new BN(duration.days(2)))); + _endTime.push(new BN(_startTime[stoId]).add(new BN(currentTime).add(new BN(duration.days(100))))); + _ratePerTier.push([new BN(1).mul(e18), new BN(150).mul(e16)]); // [ 1 USD/Token, 1.5 USD/Token ] + _ratePerTierDiscountPoly.push([new BN(50).mul(e16), new BN(1).mul(e18)]); // [ 0.5 USD/Token, 1.5 USD/Token ] + _tokensPerTierTotal.push([new BN(100).mul(e18), new BN(50).mul(e18)]); // [ 100 Token, 50 Token ] + _tokensPerTierDiscountPoly.push([new BN(100).mul(e18), new BN(25).mul(e18)]); // [ 100 Token, 25 Token ] + _nonAccreditedLimitUSD.push(new BN(25).mul(e18)); // [ 25 USD ] + _minimumInvestmentUSD.push(new BN(5)); _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); - + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], _endTime[stoId], @@ -589,30 +609,68 @@ contract("USDTieredSTO", accounts => { _reserveWallet[stoId], _usdToken[stoId] ]; - let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER, gasPrice: GAS_PRICE }); + let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER, gasPrice: GAS_PRICE }); console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); }); it("Should successfully attach the fourth STO module to the security token", async () => { let stoId = 3; - _startTime.push(latestTime() + duration.days(0.1)); - _endTime.push(_startTime[stoId] + duration.days(0.1)); - _ratePerTier.push([BigNumber(10 * 10 ** 16), BigNumber(15 * 10 ** 16)]); - _ratePerTierDiscountPoly.push([BigNumber(10 * 10 ** 16), BigNumber(12 * 10 ** 16)]); - _tokensPerTierTotal.push([BigNumber(100 * 10 ** 18), BigNumber(200 * 10 ** 18)]); - _tokensPerTierDiscountPoly.push([BigNumber(0), BigNumber(50 * 10 ** 18)]); - _nonAccreditedLimitUSD.push(new BigNumber(10000).mul(new BigNumber(10 ** 18))); - _minimumInvestmentUSD.push(new BigNumber(0)); + _startTime.push(new BN(currentTime).add(new BN(duration.days(0.1)))); + _endTime.push(new BN(_startTime[stoId]).add(new BN(currentTime).add(new BN(duration.days(0.1))))); + _ratePerTier.push([new BN(10).mul(e16), new BN(15).mul(e16)]); + _ratePerTierDiscountPoly.push([new BN(10).mul(e16), new BN(12).mul(e16)]); + _tokensPerTierTotal.push([new BN(100).mul(e18), new BN(200).mul( e18)]); + _tokensPerTierDiscountPoly.push([new BN(0), new BN(50).mul( e18)]); + _nonAccreditedLimitUSD.push(new BN(10000).mul(new BN(e18))); + _minimumInvestmentUSD.push(new BN(0)); + _fundRaiseTypes.push([0, 1, 2]); + _wallet.push(WALLET); + _reserveWallet.push(RESERVEWALLET); + _usdToken.push([I_DaiToken.address]); + + let config = [ + _startTime[stoId], + _endTime[stoId], + _ratePerTier[stoId], + _ratePerTierDiscountPoly[stoId], + _tokensPerTierTotal[stoId], + _tokensPerTierDiscountPoly[stoId], + _nonAccreditedLimitUSD[stoId], + _minimumInvestmentUSD[stoId], + _fundRaiseTypes[stoId], + _wallet[stoId], + _reserveWallet[stoId], + _usdToken[stoId] + ]; + + let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); + let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER, gasPrice: GAS_PRICE }); + console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); + assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); + assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); + }); + + it("Should successfully attach the fifth STO module to the security token", async () => { + let stoId = 4; // Non-divisible tokens + + _startTime.push(new BN(currentTime).add(new BN(duration.days(2)))); + _endTime.push(new BN(_startTime[stoId]).add(new BN(currentTime).add(new BN(duration.days(100))))); + _ratePerTier.push([new BN(1).mul(e18), new BN(150).mul(e16)]); // [ 1 USD/Token, 1.5 USD/Token ] + _ratePerTierDiscountPoly.push([new BN(50).mul(e16), new BN(1).mul(e18)]); // [ 0.5 USD/Token, 1.5 USD/Token ] + _tokensPerTierTotal.push([new BN(100).mul(e18), new BN(50).mul( e18)]); // [ 100 Token, 50 Token ] + _tokensPerTierDiscountPoly.push([new BN(100).mul(e18), new BN(25).mul(e18)]); // [ 100 Token, 25 Token ] + _nonAccreditedLimitUSD.push(new BN(25).mul(e18)); // [ 25 USD ] + _minimumInvestmentUSD.push(new BN(5)); _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -634,7 +692,10 @@ contract("USDTieredSTO", accounts => { console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); + // console.log(I_USDTieredSTO_Array[I_USDTieredSTO_Array.length - 1]); + let tokens = await I_USDTieredSTO_Array[I_USDTieredSTO_Array.length - 1].getUsdTokens.call(); + assert.equal(tokens[0], I_DaiToken.address, "USD Tokens should match"); }); it("Should fail because rates and tier array of different length", async () => { @@ -705,14 +766,14 @@ contract("USDTieredSTO", accounts => { for (var i = 0; i < config.length; i++) { let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config[i]); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); } }); it("Should fail because rate of token should be greater than 0", async () => { let stoId = 0; - let ratePerTier = [BigNumber(10 * 10 ** 16), BigNumber(0)]; + let ratePerTier = [new BN(10).mul(e16), new BN(0)]; let config = [ _startTime[stoId], _endTime[stoId], @@ -729,7 +790,7 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); }); it("Should fail because Zero address is not permitted for wallet", async () => { @@ -752,7 +813,7 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); }); it("Should fail because Zero address is not permitted for reserveWallet", async () => { @@ -770,18 +831,19 @@ contract("USDTieredSTO", accounts => { _minimumInvestmentUSD[stoId], _fundRaiseTypes[stoId], _wallet[stoId], - reserveWallet + reserveWallet, + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); }); it("Should fail because end time before start time", async () => { let stoId = 0; - let startTime = latestTime() + duration.days(35); - let endTime = latestTime() + duration.days(1); + let startTime = await latestTime() + duration.days(35); + let endTime = await latestTime() + duration.days(1); let config = [ startTime, endTime, @@ -798,13 +860,13 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); }); it("Should fail because start time is in the past", async () => { let stoId = 0; - let startTime = latestTime() - duration.days(35); + let startTime = await latestTime() - duration.days(35); let endTime = startTime + duration.days(50); let config = [ startTime, @@ -822,7 +884,7 @@ contract("USDTieredSTO", accounts => { ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER })); + await catchRevert(I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER })); }); }); @@ -845,57 +907,57 @@ contract("USDTieredSTO", accounts => { it("Should successfully change config before startTime - limits and tiers, times, addresses", async () => { let stoId = 3; - await I_USDTieredSTO_Array[stoId].modifyLimits(new BigNumber(1 * 10 ** 18), BigNumber(15 * 10 ** 18), { from: ISSUER }); + await I_USDTieredSTO_Array[stoId].modifyLimits(new BN(1).mul(e18), new BN(15).mul(e18), { from: ISSUER }); assert.equal( - (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toNumber(), - BigNumber(15 * 10 ** 18).toNumber(), + (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toString(), + new BN(15).mul(e18).toString(), "STO Configuration doesn't set as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toNumber(), - BigNumber(1 * 10 ** 18).toNumber(), + (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toString(), + new BN(1).mul(e18).toString(), "STO Configuration doesn't set as expected" ); await I_USDTieredSTO_Array[stoId].modifyTiers( - [BigNumber(15 * 10 ** 18)], - [BigNumber(13 * 10 ** 18)], - [BigNumber(15 * 10 ** 20)], - [BigNumber(15 * 10 ** 20)], + [new BN(15).mul(e18)], + [new BN(13).mul(e18)], + [new BN(1500).mul(e18)], + [new BN(1500).mul(e18)], { from: ISSUER } ); assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTier.call(0)).toNumber(), - BigNumber(15 * 10 ** 18).toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(0))[0].toString(), + new BN(15).mul(e18).toString(), "STO Configuration doesn't set as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTierDiscountPoly.call(0)).toNumber(), - BigNumber(13 * 10 ** 18).toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(0))[1].toString(), + new BN(13).mul(e18).toString(), "STO Configuration doesn't set as expected" ); assert.equal( - await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(0), - BigNumber(15 * 10 ** 20).toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(0))[2].toString(), + new BN(1500).mul(e18).toString(), "STO Configuration doesn't set as expected" ); assert.equal( - await I_USDTieredSTO_Array[stoId].tokensPerTierDiscountPoly.call(0), - BigNumber(15 * 10 ** 20).toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(0))[3].toString(), + new BN(1500).mul(e18).toString(), "STO Configuration doesn't set as expected" ); - let tempTime1 = latestTime() + duration.days(0.1); - let tempTime2 = latestTime() + duration.days(0.2); - - await I_USDTieredSTO_Array[stoId].modifyTimes(tempTime1, tempTime2, { from: ISSUER }); - assert.equal(await I_USDTieredSTO_Array[stoId].startTime.call(), tempTime1, "STO Configuration doesn't set as expected"); - assert.equal(await I_USDTieredSTO_Array[stoId].endTime.call(), tempTime2, "STO Configuration doesn't set as expected"); + let tempTime1 = new BN(currentTime).add(new BN(duration.days(0.1))); + let tempTime2 = new BN(currentTime).add(new BN(duration.days(0.2))); + await I_USDTieredSTO_Array[stoId].modifyTimes(new BN(tempTime1), new BN(tempTime2), { from: ISSUER }); + assert.equal((await I_USDTieredSTO_Array[stoId].startTime.call()).toString(), tempTime1.toString(), "STO Configuration doesn't set as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].endTime.call()).toString(), tempTime2.toString(), "STO Configuration doesn't set as expected"); + console.log("HERE"); await I_USDTieredSTO_Array[stoId].modifyAddresses( "0x0000000000000000000000000400000000000000", "0x0000000000000000000003000000000000000000", - address_zero, + [accounts[3]], { from: ISSUER } ); assert.equal( @@ -908,11 +970,7 @@ contract("USDTieredSTO", accounts => { "0x0000000000000000000003000000000000000000", "STO Configuration doesn't set as expected" ); - assert.equal( - await I_USDTieredSTO_Array[stoId].usdToken.call(), - address_zero, - "STO Configuration doesn't set as expected" - ); + assert.equal((await I_USDTieredSTO_Array[stoId].getUsdTokens())[0], accounts[3], "STO Configuration doesn't set as expected"); }); it("Should fail to change config after endTime", async () => { @@ -923,34 +981,23 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].modifyFunding([0, 1], { from: ISSUER })); - await catchRevert( - I_USDTieredSTO_Array[stoId].modifyLimits(new BigNumber(15 * 10 ** 18), BigNumber(1 * 10 ** 18), { from: ISSUER }) - ); + await catchRevert(I_USDTieredSTO_Array[stoId].modifyLimits(new BN(15).mul(e18), new BN(1).mul(e18), { from: ISSUER })); await catchRevert( I_USDTieredSTO_Array[stoId].modifyTiers( - [BigNumber(15 * 10 ** 18)], - [BigNumber(13 * 10 ** 18)], - [BigNumber(15 * 10 ** 20)], - [BigNumber(15 * 10 ** 20)], + [new BN(15).mul(e18)], + [new BN(13).mul(e18)], + [new BN(1500).mul(e18)], + [new BN(1500).mul(e18)], { from: ISSUER } ) ); - let tempTime1 = latestTime(); - let tempTime2 = latestTime() + duration.days(3); + let tempTime1 = await latestTime() + duration.days(1); + let tempTime2 = await latestTime() + duration.days(3); await catchRevert(I_USDTieredSTO_Array[stoId].modifyTimes(tempTime1, tempTime2, { from: ISSUER })); - await catchRevert( - I_USDTieredSTO_Array[stoId].modifyAddresses( - "0x0000000000000000000000000400000000000000", - "0x0000000000000000000003000000000000000000", - I_DaiToken.address, - { from: ISSUER } - ) - ); - await revertToSnapshot(snapId); }); }); @@ -963,51 +1010,42 @@ contract("USDTieredSTO", accounts => { assert.equal(await I_USDTieredSTO_Array[stoId].isOpen(), false, "STO is not showing correct status"); // Whitelist - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let whitelisted = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); //set as Accredited + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); // // Advance time to after STO start // await increaseTime(duration.days(3)); - // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - // Prep for investments - let investment_ETH = web3.utils.toWei("1", "ether"); // Invest 1 ETH - let investment_POLY = web3.utils.toWei("10000", "ether"); // Invest 10000 POLY + let investment_ETH = new BN(web3.utils.toWei("1", "ether")); // Invest 1 ETH + let investment_POLY = new BN(web3.utils.toWei("10000", "ether")); // Invest 10000 POLY await I_PolyToken.getTokens(investment_POLY, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: NONACCREDITED1 }); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); - let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + let investment_DAI = new BN(web3.utils.toWei("500", "ether")); // Invest 10000 POLY await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: ACCREDITED1 }); - // NONACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { from: NONACCREDITED1, value: investment_ETH })); - // NONACCREDITED POLY await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); - // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); - + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); - // ACCREDITED POLY await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); - // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); - + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1016,28 +1054,28 @@ contract("USDTieredSTO", accounts => { let snapId = await takeSnapshot(); // // Whitelist - // let fromTime = latestTime(); - // let toTime = latestTime() + duration.days(15); + // let fromTime = await latestTime(); + // let toTime = await latestTime() + duration.days(15); // let expiryTime = toTime + duration.days(100); // let whitelisted = true; // - // await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted,{ from: ISSUER }); - // await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted,{ from: ISSUER }); + // await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted,{ from: ISSUER }); + // await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted,{ from: ISSUER }); // Advance time to after STO start await increaseTime(duration.days(3)); // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); // Prep for investments - let investment_ETH = web3.utils.toWei("1", "ether"); // Invest 1 ETH - let investment_POLY = web3.utils.toWei("10000", "ether"); // Invest 10000 POLY + let investment_ETH = new BN(web3.utils.toWei("1", "ether")); // Invest 1 ETH + let investment_POLY = new BN(web3.utils.toWei("10000", "ether")); // Invest 10000 POLY await I_PolyToken.getTokens(investment_POLY, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: NONACCREDITED1 }); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); - let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + let investment_DAI = new BN(web3.utils.toWei("500", "ether")); // Invest 10000 POLY await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); @@ -1050,7 +1088,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1059,7 +1097,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1070,21 +1108,19 @@ contract("USDTieredSTO", accounts => { let snapId = await takeSnapshot(); // Whitelist - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let whitelisted = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); // Advance time to after STO start await increaseTime(duration.days(3)); - // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - - let investment_USD = new BigNumber(2).mul(10 ** 18); + let investment_USD = new BN(2).mul(e18); let investment_ETH = await convert(stoId, tierId, false, "USD", "ETH", investment_USD); let investment_POLY = await convert(stoId, tierId, false, "USD", "POLY", investment_USD); let investment_DAI = investment_USD; @@ -1106,7 +1142,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1115,7 +1151,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1125,33 +1161,31 @@ contract("USDTieredSTO", accounts => { let snapId = await takeSnapshot(); // Whitelist - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let whitelisted = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); // Advance time to after STO start await increaseTime(duration.days(3)); - // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - // Pause the STO await I_USDTieredSTO_Array[stoId].pause({ from: ISSUER }); assert.equal(await I_USDTieredSTO_Array[stoId].paused.call(), true, "STO did not pause successfully"); // Prep for investments - let investment_ETH = web3.utils.toWei("1", "ether"); // Invest 1 ETH - let investment_POLY = web3.utils.toWei("10000", "ether"); // Invest 10000 POLY + let investment_ETH = new BN(web3.utils.toWei("1", "ether")); // Invest 1 ETH + let investment_POLY = new BN(web3.utils.toWei("10000", "ether")); // Invest 10000 POLY await I_PolyToken.getTokens(investment_POLY, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: NONACCREDITED1 }); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); - let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + let investment_DAI = new BN(web3.utils.toWei("500", "ether")); // Invest 10000 POLY await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); @@ -1164,7 +1198,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1173,7 +1207,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); // Unpause the STO await I_USDTieredSTO_Array[stoId].unpause({ from: ISSUER }); @@ -1181,11 +1215,56 @@ contract("USDTieredSTO", accounts => { await I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { from: NONACCREDITED1, value: investment_ETH }); await I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 }); - await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 }); + await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 }); await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH }); await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 }); - await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 }); + await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 }); + + await revertToSnapshot(snapId); + }); + + it("should allow changing stable coin address in middle of STO", async () => { + let stoId = 0; + let snapId = await takeSnapshot(); + + // Whitelist + let fromTime = new BN(await latestTime()); + let toTime = fromTime.add(new BN(duration.days(15))); + let expiryTime = toTime.add(new BN(duration.days(100))); + let whitelisted = true; + + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + + // Advance time to after STO start + await increaseTime(duration.days(3)); + + // Prep for investments + let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); + await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); + await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); + await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: ACCREDITED1 }); + + // Make sure buying works before changing + await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 }); + + // Change Stable coin address + await I_USDTieredSTO_Array[stoId].modifyAddresses(WALLET, RESERVEWALLET, [I_PolyToken.address], { from: ISSUER }); + + // NONACCREDITED DAI + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); + + // ACCREDITED DAI + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); + + // Revert stable coin address + await I_USDTieredSTO_Array[stoId].modifyAddresses(WALLET, RESERVEWALLET, [I_DaiToken.address], { from: ISSUER }); + + // Make sure buying works again + await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 }); await revertToSnapshot(snapId); }); @@ -1195,30 +1274,28 @@ contract("USDTieredSTO", accounts => { let snapId = await takeSnapshot(); // Whitelist - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let whitelisted = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); // Advance time to after STO end await increaseTime(duration.days(3)); assert.equal(await I_USDTieredSTO_Array[stoId].isOpen(), false, "STO is not showing correct status"); - // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - // Prep for investments - let investment_ETH = web3.utils.toWei("1", "ether"); // Invest 1 ETH - let investment_POLY = web3.utils.toWei("10000", "ether"); // Invest 10000 POLY + let investment_ETH = new BN(web3.utils.toWei("1", "ether")); // Invest 1 ETH + let investment_POLY = new BN(web3.utils.toWei("10000", "ether")); // Invest 10000 POLY await I_PolyToken.getTokens(investment_POLY, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: NONACCREDITED1 }); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); - let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + let investment_DAI = new BN(web3.utils.toWei("500", "ether")); // Invest 10000 POLY await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); @@ -1231,7 +1308,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1240,7 +1317,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1250,21 +1327,19 @@ contract("USDTieredSTO", accounts => { let snapId = await takeSnapshot(); // Whitelist - let fromTime = latestTime(); - let toTime = latestTime(); + let fromTime = await latestTime(); + let toTime = await latestTime(); let expiryTime = toTime + duration.days(100); let whitelisted = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(RESERVEWALLET, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(RESERVEWALLET, fromTime, toTime, expiryTime, { from: ISSUER }); // Advance time to after STO start await increaseTime(duration.days(3)); - // Set as accredited - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - // Finalize STO await I_USDTieredSTO_Array[stoId].finalize({ from: ISSUER }); assert.equal(await I_USDTieredSTO_Array[stoId].isFinalized.call(), true, "STO has not been finalized"); @@ -1274,13 +1349,13 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].finalize({ from: ISSUER })); // Prep for investments - let investment_ETH = web3.utils.toWei("1", "ether"); // Invest 1 ETH - let investment_POLY = web3.utils.toWei("10000", "ether"); // Invest 10000 POLY + let investment_ETH = new BN(web3.utils.toWei("1", "ether")); // Invest 1 ETH + let investment_POLY = new BN(web3.utils.toWei("10000", "ether")); // Invest 10000 POLY await I_PolyToken.getTokens(investment_POLY, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: NONACCREDITED1 }); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); - let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + let investment_DAI = new BN(web3.utils.toWei("500", "ether")); // Invest 10000 POLY await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); @@ -1293,7 +1368,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1302,7 +1377,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1318,48 +1393,32 @@ contract("USDTieredSTO", accounts => { it("should whitelist ACCREDITED1 and NONACCREDITED1", async () => { let stoId = 0; - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let whitelisted = true; - const tx1 = await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { + const tx1 = await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); assert.equal(tx1.logs[0].args._investor, NONACCREDITED1, "Failed in adding the investor in whitelist"); - const tx2 = await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { + const tx2 = await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); assert.equal(tx2.logs[0].args._investor, ACCREDITED1, "Failed in adding the investor in whitelist"); }); - it("should successfully modify accredited addresses for first STO", async () => { + it("should successfully modify accredited addresses for the STOs", async () => { let stoId = 0; - - let status1 = await I_USDTieredSTO_Array[stoId].accredited.call(NONACCREDITED1); - assert.equal(status1, false, "Initial accreditation is set to true"); - - await I_USDTieredSTO_Array[stoId].changeAccredited([NONACCREDITED1], [true], { from: ISSUER }); - let status2 = await I_USDTieredSTO_Array[stoId].accredited.call(NONACCREDITED1); - assert.equal(status2, true, "Failed to set single address"); - - await I_USDTieredSTO_Array[stoId].changeAccredited([NONACCREDITED1, ACCREDITED1], [false, true], { from: ISSUER }); - let status3 = await I_USDTieredSTO_Array[stoId].accredited.call(NONACCREDITED1); - assert.equal(status3, false, "Failed to set multiple addresses"); - let status4 = await I_USDTieredSTO_Array[stoId].accredited.call(ACCREDITED1); - assert.equal(status4, true, "Failed to set multiple addresses"); - - await catchRevert(I_USDTieredSTO_Array[stoId].changeAccredited([NONACCREDITED1, ACCREDITED1], [true], { from: ISSUER })); - }); - - it("should successfully modify accredited addresses for second STO", async () => { - let stoId = 1; - - await I_USDTieredSTO_Array[stoId].changeAccredited([NONACCREDITED1, ACCREDITED1], [false, true], { from: ISSUER }); - let status1 = await I_USDTieredSTO_Array[stoId].accredited.call(NONACCREDITED1); - let status2 = await I_USDTieredSTO_Array[stoId].accredited.call(ACCREDITED1); - assert.equal(status1, false, "Failed to set multiple address"); - assert.equal(status2, true, "Failed to set multiple address"); + let totalStatus = await I_USDTieredSTO_Array[stoId].getAccreditedData.call(); + console.log(totalStatus); + assert.equal(totalStatus[0][0], NONACCREDITED1, "Account match"); + assert.equal(totalStatus[0][1], ACCREDITED1, "Account match"); + assert.equal(totalStatus[1][0], false, "Account match"); + assert.equal(totalStatus[1][1], true, "Account match"); + assert.equal(totalStatus[2][0].toNumber(), 0, "override match"); + assert.equal(totalStatus[2][1].toNumber(), 0, "override match"); }); }); @@ -1368,23 +1427,23 @@ contract("USDTieredSTO", accounts => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await web3.eth.sendTransaction({ @@ -1392,101 +1451,101 @@ contract("USDTieredSTO", accounts => { to: I_USDTieredSTO_Array[stoId].address, value: investment_ETH, gasPrice: GAS_PRICE, - gas: 1000000 + gas: 7000000 }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.gasUsed); - console.log(" Gas fallback purchase: ".grey + tx1.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.gasUsed)); + console.log(" Gas fallback purchase: ".grey + new BN(tx1.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedUSD.toNumber(), init_RaisedUSD.add(investment_USD).toNumber(), "Raised USD not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_RaisedDAI.toNumber(), init_RaisedDAI.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedUSD.toString(), init_RaisedUSD.add(investment_USD).toString(), "Raised USD not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); + assert.equal(final_RaisedDAI.toString(), init_RaisedDAI.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); // Additional checks on getters - assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toNumber(), 1, "Investor count not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toString(), 1, "Investor count not changed as expected"); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSold()).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSold()).toString(), + investment_Token.toString(), "getTokensSold not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toString(), + investment_Token.toString(), "getTokensMinted not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toString(), + investment_Token.toString(), "getTokensSoldForETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toNumber(), - 0, + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toString(), + new BN(0), "getTokensSoldForPOLY not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toNumber(), - investment_USD.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toString(), + investment_USD.toString(), "investorInvestedUSD not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, ETH)).toNumber(), - investment_ETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, ETH)).toString(), + investment_ETH.toString(), "investorInvestedETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, POLY)).toNumber(), - 0, + (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, POLY)).toString(), + new BN(0), "investorInvestedPOLY not changed as expected" ); }); @@ -1495,22 +1554,22 @@ contract("USDTieredSTO", accounts => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { @@ -1518,68 +1577,68 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_RaisedDAI.toNumber(), init_RaisedDAI.toNumber(), "Raised DAI not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); + assert.equal(final_RaisedDAI.toString(), init_RaisedDAI.toString(), "Raised DAI not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithPOLY at tier 0 for NONACCREDITED1", async () => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -1589,15 +1648,15 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -1605,60 +1664,60 @@ contract("USDTieredSTO", accounts => { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_RaisedDAI.toNumber(), init_RaisedDAI.toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_RaisedDAI.toString(), init_RaisedDAI.toString(), "Raised POLY not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); }); @@ -1667,7 +1726,7 @@ contract("USDTieredSTO", accounts => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -1678,107 +1737,110 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_InvestorDAIBal = await I_DaiToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let init_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); // Buy With DAI - let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithUSD: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithUSD: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_InvestorDAIBal = await I_DaiToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let final_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_InvestorDAIBal.toNumber(), - init_InvestorDAIBal.sub(investment_DAI).toNumber(), + final_InvestorDAIBal.toString(), + init_InvestorDAIBal.sub(investment_DAI).toString(), "Investor DAI Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_RaisedDAI.toNumber(), init_RaisedDAI.add(investment_DAI).toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); + assert.equal(final_RaisedDAI.toString(), init_RaisedDAI.add(investment_DAI).toString(), "Raised POLY not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); assert.equal( - final_WalletDAIBal.toNumber(), - init_WalletDAIBal.add(investment_DAI).toNumber(), + final_WalletDAIBal.toString(), + init_WalletDAIBal.add(investment_DAI).toString(), "Wallet DAI Balance not changed as expected" ); + assert.equal( + (await I_USDTieredSTO_Array[stoId].stableCoinsRaised.call(I_DaiToken.address)).toString(), + investment_DAI.toString(), + "DAI Raised not changed as expected" + ); }); it("should successfully buy using fallback at tier 0 for ACCREDITED1", async () => { let stoId = 0; let tierId = 0; - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await web3.eth.sendTransaction({ @@ -1786,82 +1848,82 @@ contract("USDTieredSTO", accounts => { to: I_USDTieredSTO_Array[stoId].address, value: investment_ETH, gasPrice: GAS_PRICE, - gas: 1000000 + gas: 7000000 }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.gasUsed); - console.log(" Gas fallback purchase: ".grey + tx1.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.gasUsed)); + console.log(" Gas fallback purchase: ".grey + new BN(tx1.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithETH at tier 0 for ACCREDITED1", async () => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { @@ -1869,66 +1931,66 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithPOLY at tier 0 for ACCREDITED1", async () => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -1947,14 +2009,14 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -1962,96 +2024,96 @@ contract("USDTieredSTO", accounts => { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); // Additional checks on getters - assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toNumber(), 2, "Investor count not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toString(), 2, "Investor count not changed as expected"); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSold()).toNumber(), - init_getTokensSold.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSold()).toString(), + init_getTokensSold.add(investment_Token).toString(), "getTokensSold not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toNumber(), - init_getTokensMinted.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toString(), + init_getTokensMinted.add(investment_Token).toString(), "getTokensMinted not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toNumber(), - init_getTokensSoldForETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toString(), + init_getTokensSoldForETH.toString(), "getTokensSoldForETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toNumber(), - init_getTokensSoldForPOLY.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toString(), + init_getTokensSoldForPOLY.add(investment_Token).toString(), "getTokensSoldForPOLY not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(ACCREDITED1)).toNumber(), - init_investorInvestedUSD.add(investment_USD).toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(ACCREDITED1)).toString(), + init_investorInvestedUSD.add(investment_USD).toString(), "investorInvestedUSD not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, ETH)).toNumber(), - init_investorInvestedETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, ETH)).toString(), + init_investorInvestedETH.toString(), "investorInvestedETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, POLY)).toNumber(), - init_investorInvestedPOLY.add(investment_POLY).toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, POLY)).toString(), + init_investorInvestedPOLY.add(investment_POLY).toString(), "investorInvestedPOLY not changed as expected" ); }); @@ -2059,18 +2121,27 @@ contract("USDTieredSTO", accounts => { it("should successfully modify NONACCREDITED cap for NONACCREDITED1", async () => { let stoId = 0; let tierId = 0; - console.log("Current investment: " + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toNumber()); - await I_USDTieredSTO_Array[stoId].changeNonAccreditedLimit([NONACCREDITED1], [_nonAccreditedLimitUSD[stoId].div(2)], { + console.log("Current investment: " + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toString()); + await I_USDTieredSTO_Array[stoId].changeNonAccreditedLimit([NONACCREDITED1], [_nonAccreditedLimitUSD[stoId].div(new BN(2))], { from: ISSUER }); - console.log("Current limit: " + (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSDOverride(NONACCREDITED1)).toNumber()); + let investorLimit = await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSDOverride.call(NONACCREDITED1); + console.log("Current limit: " + investorLimit.toString()); + let totalStatus = await I_USDTieredSTO_Array[stoId].getAccreditedData.call(); + + assert.equal(totalStatus[0][0], NONACCREDITED1, "Account match"); + assert.equal(totalStatus[0][1], ACCREDITED1, "Account match"); + assert.equal(totalStatus[1][0], false, "Account match"); + assert.equal(totalStatus[1][1], true, "Account match"); + assert.equal(totalStatus[2][0].toString(), _nonAccreditedLimitUSD[stoId].div(new BN(2)), "override match"); + assert.equal(totalStatus[2][1].toString(), 0, "override match"); }); it("should successfully buy a partial amount and refund balance when reaching NONACCREDITED cap", async () => { let stoId = 0; let tierId = 0; - let investment_USD = await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSDOverride(NONACCREDITED1); //_nonAccreditedLimitUSD[stoId]; + let investment_USD = await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSDOverride.call(NONACCREDITED1);//await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSDOverride(NONACCREDITED1); //_nonAccreditedLimitUSD[stoId]; let investment_Token = await convert(stoId, tierId, false, "USD", "TOKEN", investment_USD); let investment_ETH = await convert(stoId, tierId, false, "USD", "ETH", investment_USD); let investment_POLY = await convert(stoId, tierId, false, "USD", "POLY", investment_USD); @@ -2080,20 +2151,20 @@ contract("USDTieredSTO", accounts => { let refund_ETH = await convert(stoId, tierId, false, "USD", "ETH", refund_USD); let refund_POLY = await convert(stoId, tierId, false, "USD", "POLY", refund_USD); - console.log("Expected refund in tokens: " + refund_Token.toNumber()); + console.log("Expected refund in tokens: " + refund_Token.toString()); let snap = await takeSnapshot(); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy with ETH @@ -2102,79 +2173,79 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), + final_TokenSupply.toString(), init_TokenSupply .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), + final_InvestorTokenBal.toString(), init_InvestorTokenBal .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) .add(refund_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), + final_STOTokenSold.toString(), init_STOTokenSold .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); assert.equal( - final_RaisedETH.toNumber(), + final_RaisedETH.toString(), init_RaisedETH .add(investment_ETH) .sub(refund_ETH) - .toNumber(), + .toString(), "Raised ETH not changed as expected" ); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), + final_WalletETHBal.toString(), init_WalletETHBal .add(investment_ETH) .sub(refund_ETH) - .toNumber(), + .toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); await revertToSnapshot(snap); @@ -2183,14 +2254,14 @@ contract("USDTieredSTO", accounts => { init_TokenSupply = await I_SecurityToken.totalSupply(); init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -2198,76 +2269,76 @@ contract("USDTieredSTO", accounts => { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); final_TokenSupply = await I_SecurityToken.totalSupply(); final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), + final_TokenSupply.toString(), init_TokenSupply .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), + final_InvestorTokenBal.toString(), init_InvestorTokenBal .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), init_InvestorPOLYBal .sub(investment_POLY) .add(refund_POLY) - .toNumber(), + .toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), + final_STOTokenSold.toString(), init_STOTokenSold .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), + final_RaisedPOLY.toString(), init_RaisedPOLY .add(investment_POLY) .sub(refund_POLY) - .toNumber(), + .toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), + final_WalletPOLYBal.toString(), init_WalletPOLYBal .add(investment_POLY) .sub(refund_POLY) - .toNumber(), + .toString(), "Wallet POLY Balance not changed as expected" ); }); @@ -2276,7 +2347,7 @@ contract("USDTieredSTO", accounts => { let stoId = 0; let tierId = 0; - let investment_Token = new BigNumber(50).mul(10 ** 18); + let investment_Token = new BN(50).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -2300,16 +2371,16 @@ contract("USDTieredSTO", accounts => { let tierId; // set new exchange rates - let high_USDETH = new BigNumber(1000).mul(10 ** 18); // 1000 USD per ETH - let high_USDPOLY = new BigNumber(50).mul(10 ** 16); // 0.5 USD per POLY - let low_USDETH = new BigNumber(250).mul(10 ** 18); // 250 USD per ETH - let low_USDPOLY = new BigNumber(20).mul(10 ** 16); // 0.2 USD per POLY + let high_USDETH = new BN(1000).mul(e18); // 1000 USD per ETH + let high_USDPOLY = new BN(50).mul(e16); // 0.5 USD per POLY + let low_USDETH = new BN(250).mul(e18); // 250 USD per ETH + let low_USDPOLY = new BN(20).mul(e16); // 0.2 USD per POLY - let investment_USD = new BigNumber(web3.utils.toWei("50")); // USD - let investment_ETH_high = investment_USD.div(high_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY - let investment_ETH_low = investment_USD.div(low_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY + let investment_USD = new BN(web3.utils.toWei("50")); // USD + let investment_ETH_high = investment_USD.div(high_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(e18); // USD / USD/POLY = POLY + let investment_ETH_low = investment_USD.div(low_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(e18); // USD / USD/POLY = POLY await I_PolyToken.getTokens(investment_POLY_low, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY_low, { from: NONACCREDITED1 }); @@ -2365,12 +2436,12 @@ contract("USDTieredSTO", accounts => { let endTier = 1; assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); + let delta_Token = new BN(5).mul(e18); let ethTier0 = await convert(stoId, startTier, false, "TOKEN", "ETH", delta_Token); let ethTier1 = await convert(stoId, endTier, false, "TOKEN", "ETH", delta_Token); @@ -2380,14 +2451,14 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { @@ -2395,62 +2466,62 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should successfully buy across tiers for NONACCREDITED POLY", async () => { @@ -2459,12 +2530,12 @@ contract("USDTieredSTO", accounts => { let endTier = 2; assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); // Token + let delta_Token = new BN(5).mul(e18); // Token let polyTier0 = await convert(stoId, startTier, false, "TOKEN", "POLY", delta_Token); let polyTier1 = await convert(stoId, endTier, false, "TOKEN", "POLY", delta_Token); @@ -2477,77 +2548,77 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx2 = await I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should successfully buy across tiers for ACCREDITED ETH", async () => { @@ -2555,15 +2626,13 @@ contract("USDTieredSTO", accounts => { let startTier = 2; let endTier = 3; - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); // Token + let delta_Token = new BN(5).mul(e18); // Token let ethTier0 = await convert(stoId, startTier, false, "TOKEN", "ETH", delta_Token); let ethTier1 = await convert(stoId, endTier, false, "TOKEN", "ETH", delta_Token); @@ -2573,14 +2642,14 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { @@ -2588,62 +2657,62 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should successfully buy across tiers for ACCREDITED DAI", async () => { @@ -2652,12 +2721,12 @@ contract("USDTieredSTO", accounts => { let endTier = 4; assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); // Token + let delta_Token = new BN(5).mul(e18); // Token let daiTier0 = await convert(stoId, startTier, false, "TOKEN", "USD", delta_Token); let daiTier1 = await convert(stoId, endTier, false, "TOKEN", "USD", delta_Token); @@ -2670,83 +2739,83 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_InvestorDAIBal = await I_DaiToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let init_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); - let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithUSD: ".grey + tx2.receipt.gasUsed.toString().grey); + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1, gasPrice: GAS_PRICE }); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithUSD: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_InvestorDAIBal = await I_DaiToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(DAI); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let final_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_InvestorDAIBal.toNumber(), - init_InvestorDAIBal.sub(investment_DAI).toNumber(), + final_InvestorDAIBal.toString(), + init_InvestorDAIBal.sub(investment_DAI).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); - assert.equal(final_RaisedDAI.toNumber(), init_RaisedDAI.add(investment_DAI).toNumber(), "Raised DAI not changed as expected"); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); + assert.equal(final_RaisedDAI.toString(), init_RaisedDAI.add(investment_DAI).toString(), "Raised DAI not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); assert.equal( - final_WalletDAIBal.toNumber(), - init_WalletDAIBal.add(investment_DAI).toNumber(), + final_WalletDAIBal.toString(), + init_WalletDAIBal.add(investment_DAI).toString(), "Wallet POLY Balance not changed as expected" ); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should successfully buy across tiers for ACCREDITED POLY", async () => { @@ -2755,12 +2824,12 @@ contract("USDTieredSTO", accounts => { let endTier = 5; assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); // Token + let delta_Token = new BN(5).mul(e18); // Token let polyTier0 = await convert(stoId, startTier, false, "TOKEN", "POLY", delta_Token); let polyTier1 = await convert(stoId, endTier, false, "TOKEN", "POLY", delta_Token); @@ -2773,87 +2842,87 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx2 = await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should buy out the rest of the sto", async () => { let stoId = 1; let tierId = 5; - let minted = await I_USDTieredSTO_Array[stoId].mintedPerTierTotal.call(tierId); - console.log(minted.toNumber() + ":" + _tokensPerTierTotal[stoId][tierId]); + let minted = (await I_USDTieredSTO_Array[stoId].tiers.call(tierId))[4]; + console.log(minted.toString() + ":" + _tokensPerTierTotal[stoId][tierId]); let investment_Token = _tokensPerTierTotal[stoId][tierId].sub(minted); - console.log(investment_Token.toNumber()); + console.log(investment_Token.toString()); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); @@ -2872,28 +2941,28 @@ contract("USDTieredSTO", accounts => { let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - // assert.equal((await I_USDTieredSTO_Array[1].getTokensMinted()).toNumber(), _tokensPerTierTotal[1].reduce((a, b) => a + b, 0).toNumber(), "STO Token Sold not changed as expected"); + // assert.equal((await I_USDTieredSTO_Array[1].getTokensMinted()).toString(), _tokensPerTierTotal[1].reduce((a, b) => a + b, 0).toString(), "STO Token Sold not changed as expected"); }); it("should fail and revert when all tiers sold out", async () => { let stoId = 1; let tierId = 4; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -2922,7 +2991,7 @@ contract("USDTieredSTO", accounts => { // Buy with DAI NONACCREDITED await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1, gasPrice: GAS_PRICE }) ); // Buy with ETH ACCREDITED @@ -2940,7 +3009,7 @@ contract("USDTieredSTO", accounts => { // Buy with DAI ACCREDITED await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1, gasPrice: GAS_PRICE }) ); }); @@ -2949,16 +3018,16 @@ contract("USDTieredSTO", accounts => { let tierId = 4; // set new exchange rates - let high_USDETH = new BigNumber(1000).mul(10 ** 18); // 1000 USD per ETH - let high_USDPOLY = new BigNumber(50).mul(10 ** 16); // 0.5 USD per POLY - let low_USDETH = new BigNumber(250).mul(10 ** 18); // 250 USD per ETH - let low_USDPOLY = new BigNumber(20).mul(10 ** 16); // 0.2 USD per POLY + let high_USDETH = new BN(1000).mul(e18); // 1000 USD per ETH + let high_USDPOLY = new BN(50).mul(e16); // 0.5 USD per POLY + let low_USDETH = new BN(250).mul(e18); // 250 USD per ETH + let low_USDPOLY = new BN(20).mul(e16); // 0.2 USD per POLY - let investment_USD = new BigNumber(web3.utils.toWei("50")); // USD - let investment_ETH_high = investment_USD.div(high_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY - let investment_ETH_low = investment_USD.div(low_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY + let investment_USD = new BN(web3.utils.toWei("50")); // USD + let investment_ETH_high = investment_USD.div(high_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(e18); // USD / USD/POLY = POLY + let investment_ETH_low = investment_USD.div(low_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(e18); // USD / USD/POLY = POLY await I_PolyToken.getTokens(investment_POLY_low, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY_low, { from: NONACCREDITED1 }); @@ -3040,22 +3109,22 @@ contract("USDTieredSTO", accounts => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await web3.eth.sendTransaction({ @@ -3063,99 +3132,99 @@ contract("USDTieredSTO", accounts => { to: I_USDTieredSTO_Array[stoId].address, value: investment_ETH, gasPrice: GAS_PRICE, - gas: 1000000 + gas: 7000000 }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.gasUsed); - console.log(" Gas fallback purchase: ".grey + tx1.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.gasUsed)); + console.log(" Gas fallback purchase: ".grey + new BN(tx1.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedUSD.toNumber(), init_RaisedUSD.add(investment_USD).toNumber(), "Raised USD not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedUSD.toString(), init_RaisedUSD.add(investment_USD).toString(), "Raised USD not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); // Additional checks on getters - assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toNumber(), 1, "Investor count not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toString(), 1, "Investor count not changed as expected"); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSold()).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSold()).toString(), + investment_Token.toString(), "getTokensSold not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toString(), + investment_Token.toString(), "getTokensMinted not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toNumber(), - investment_Token.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toString(), + investment_Token.toString(), "getTokensSoldForETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toNumber(), - 0, + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toString(), + new BN(0), "getTokensSoldForPOLY not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toNumber(), - investment_USD.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(NONACCREDITED1)).toString(), + investment_USD.toString(), "investorInvestedUSD not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, ETH)).toNumber(), - investment_ETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, ETH)).toString(), + investment_ETH.toString(), "investorInvestedETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, POLY)).toNumber(), - 0, + (await I_USDTieredSTO_Array[stoId].investorInvested.call(NONACCREDITED1, POLY)).toString(), + new BN(0), "investorInvestedPOLY not changed as expected" ); }); @@ -3164,21 +3233,21 @@ contract("USDTieredSTO", accounts => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { @@ -3186,66 +3255,66 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithPOLY at tier 0 for NONACCREDITED1", async () => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, true, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, true, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", investment_Token); @@ -3255,14 +3324,14 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -3270,58 +3339,58 @@ contract("USDTieredSTO", accounts => { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); }); @@ -3330,23 +3399,21 @@ contract("USDTieredSTO", accounts => { let stoId = 2; let tierId = 0; - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); - - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await web3.eth.sendTransaction({ @@ -3354,82 +3421,82 @@ contract("USDTieredSTO", accounts => { to: I_USDTieredSTO_Array[stoId].address, value: investment_ETH, gasPrice: GAS_PRICE, - gas: 1000000 + gas: 7000000 }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.gasUsed); - console.log(" Gas fallback purchase: ".grey + tx1.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.gasUsed)); + console.log(" Gas fallback purchase: ".grey + new BN(tx1.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithETH at tier 0 for ACCREDITED1", async () => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx1 = await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { @@ -3437,66 +3504,66 @@ contract("USDTieredSTO", accounts => { value: investment_ETH, gasPrice: GAS_PRICE }); - let gasCost1 = new BigNumber(GAS_PRICE).mul(tx1.receipt.gasUsed); - console.log(" Gas buyWithETH: ".grey + tx1.receipt.gasUsed.toString().grey); + let gasCost1 = new BN(GAS_PRICE).mul(new BN(tx1.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx1.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), + final_InvestorETHBal.toString(), init_InvestorETHBal .sub(gasCost1) .sub(investment_ETH) - .toNumber(), + .toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.add(investment_ETH).toNumber(), "Raised ETH not changed as expected"); - assert.equal(final_RaisedPOLY.toNumber(), init_RaisedPOLY.toNumber(), "Raised POLY not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.add(investment_ETH).toString(), "Raised ETH not changed as expected"); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY.toString(), "Raised POLY not changed as expected"); assert.equal( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), + final_WalletETHBal.toString(), + init_WalletETHBal.add(investment_ETH).toString(), "Wallet ETH Balance not changed as expected" ); - assert.equal(final_WalletPOLYBal.toNumber(), init_WalletPOLYBal.toNumber(), "Wallet POLY Balance not changed as expected"); + assert.equal(final_WalletPOLYBal.toString(), init_WalletPOLYBal.toString(), "Wallet POLY Balance not changed as expected"); }); it("should successfully buy using buyWithPOLY at tier 0 for ACCREDITED1", async () => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, true, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, true, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", investment_Token); @@ -3515,14 +3582,14 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -3530,96 +3597,96 @@ contract("USDTieredSTO", accounts => { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); // Additional checks on getters - assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toNumber(), 2, "Investor count not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].investorCount.call()).toString(), 2, "Investor count not changed as expected"); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSold()).toNumber(), - init_getTokensSold.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSold()).toString(), + init_getTokensSold.add(investment_Token).toString(), "getTokensSold not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toNumber(), - init_getTokensMinted.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensMinted()).toString(), + init_getTokensMinted.add(investment_Token).toString(), "getTokensMinted not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toNumber(), - init_getTokensSoldForETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(ETH)).toString(), + init_getTokensSoldForETH.toString(), "getTokensSoldForETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toNumber(), - init_getTokensSoldForPOLY.add(investment_Token).toNumber(), + (await I_USDTieredSTO_Array[stoId].getTokensSoldFor(POLY)).toString(), + init_getTokensSoldForPOLY.add(investment_Token).toString(), "getTokensSoldForPOLY not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(ACCREDITED1)).toNumber(), - init_investorInvestedUSD.add(investment_USD).toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(ACCREDITED1)).toString(), + init_investorInvestedUSD.add(investment_USD).toString(), "investorInvestedUSD not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, ETH)).toNumber(), - init_investorInvestedETH.toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, ETH)).toString(), + init_investorInvestedETH.toString(), "investorInvestedETH not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, POLY)).toNumber(), - init_investorInvestedPOLY.add(investment_POLY).toNumber(), + (await I_USDTieredSTO_Array[stoId].investorInvested.call(ACCREDITED1, POLY)).toString(), + init_investorInvestedPOLY.add(investment_POLY).toString(), "investorInvestedPOLY not changed as expected" ); }); @@ -3643,14 +3710,14 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -3658,85 +3725,276 @@ contract("USDTieredSTO", accounts => { from: NONACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(NONACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(NONACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(NONACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(NONACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), + final_TokenSupply.toString(), init_TokenSupply .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), + final_InvestorTokenBal.toString(), init_InvestorTokenBal .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), + final_InvestorPOLYBal.toString(), init_InvestorPOLYBal .sub(investment_POLY) .add(refund_POLY) - .toNumber(), + .toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), + final_STOTokenSold.toString(), init_STOTokenSold .add(investment_Token) .sub(refund_Token) - .toNumber(), + .toString(), + "STO Token Sold not changed as expected" + ); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); + assert.equal( + final_RaisedPOLY.toString(), + init_RaisedPOLY + .add(investment_POLY) + .sub(refund_POLY) + .toString(), + "Raised POLY not changed as expected" + ); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); + assert.equal( + final_WalletPOLYBal.toString(), + init_WalletPOLYBal + .add(investment_POLY) + .sub(refund_POLY) + .toString(), + "Wallet POLY Balance not changed as expected" + ); + }); + + it("should successfully buy a granular amount and refund balance when buying indivisible token with POLY", async () => { + await I_SecurityToken.changeGranularity(e18, { from: ISSUER }); + let stoId = 4; + let tierId = 0; + let investment_Tokens = new BN(1050).mul(e16); + let investment_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", investment_Tokens); + + let refund_Tokens = new BN(50).mul(e16); + let refund_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", refund_Tokens); + + await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); + await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); + + let init_TokenSupply = await I_SecurityToken.totalSupply(); + let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); + let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); + let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); + let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); + let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); + + let tokensToMint = (await I_USDTieredSTO_Array[stoId].buyWithPOLY.call(ACCREDITED1, investment_POLY, {from: ACCREDITED1}))[2]; + + // Buy With POLY + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { + from: ACCREDITED1, + gasPrice: GAS_PRICE + }); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); + + let final_TokenSupply = await I_SecurityToken.totalSupply(); + let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); + let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); + let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); + let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); + let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); + + assert.equal( + final_TokenSupply.toString(), + init_TokenSupply + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), + "Token Supply not changed as expected" + ); + assert.equal(tokensToMint.toString(), investment_Tokens.sub(refund_Tokens).toString(), "View function returned incorrect data"); + assert.equal( + final_InvestorTokenBal.toString(), + init_InvestorTokenBal + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), + "Investor Token Balance not changed as expected" + ); + assert.equal( + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), + "Investor ETH Balance not changed as expected" + ); + assert.equal( + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal + .sub(investment_POLY) + .add(refund_POLY) + .toString(), + "Investor POLY Balance not changed as expected" + ); + assert.equal( + final_STOTokenSold.toString(), + init_STOTokenSold + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), + final_RaisedPOLY.toString(), init_RaisedPOLY .add(investment_POLY) .sub(refund_POLY) - .toNumber(), + .toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), + final_WalletPOLYBal.toString(), init_WalletPOLYBal .add(investment_POLY) .sub(refund_POLY) - .toNumber(), + .toString(), "Wallet POLY Balance not changed as expected" ); + await I_SecurityToken.changeGranularity(1, { from: ISSUER }); + }); + + it("should successfully buy a granular amount and refund balance when buying indivisible token with ETH", async () => { + await I_SecurityToken.changeGranularity(e18, { from: ISSUER }); + let stoId = 4; + let tierId = 0; + let investment_Tokens = new BN(1050).mul(e16); + let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Tokens); + let refund_Tokens = new BN(50).mul(e16); + let refund_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", refund_Tokens); + + let init_TokenSupply = await I_SecurityToken.totalSupply(); + let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); + let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); + let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); + let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); + + // Buy With ETH + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { + from: ACCREDITED1, + gasPrice: GAS_PRICE, + value: investment_ETH + }); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithETH: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); + + let final_TokenSupply = await I_SecurityToken.totalSupply(); + let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); + let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); + let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); + let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); + + assert.equal( + final_TokenSupply.toString(), + init_TokenSupply + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), + "Token Supply not changed as expected" + ); + assert.equal( + final_InvestorTokenBal.toString(), + init_InvestorTokenBal + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), + "Investor Token Balance not changed as expected" + ); + assert.equal( + final_InvestorETHBal.toString(), + init_InvestorETHBal + .sub(investment_ETH) + .sub(gasCost2) + .add(refund_ETH) + .toString(), + "Investor ETH Balance not changed as expected" + ); + assert.equal( + final_STOTokenSold.toString(), + init_STOTokenSold + .add(investment_Tokens) + .sub(refund_Tokens) + .toString(), + "STO Token Sold not changed as expected" + ); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal( + final_RaisedETH.toString(), + init_RaisedETH + .add(investment_ETH) + .sub(refund_ETH) + .toString(), + "Raised ETH not changed as expected" + ); + assert.equal(final_RaisedPOLY.toString(), init_RaisedPOLY, "Raised POLY not changed as expected"); + await I_SecurityToken.changeGranularity(1, { from: ISSUER }); }); it("should fail and revert when NONACCREDITED cap reached", async () => { let stoId = 2; let tierId = 0; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, true, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, true, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", investment_Token); @@ -3757,23 +4015,50 @@ contract("USDTieredSTO", accounts => { ); }); + it("should fail when rate set my contract is too low", async () => { + let stoId = 4; + let tierId = 0; + let investment_Tokens = new BN(e18); + let investment_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", investment_Tokens); + let investment_ETH = await convert(stoId, tierId, true, "TOKEN", "ETH", investment_Tokens); + const minTokens = new BN(1000).mul(e18); + + await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); + await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); + + // Buy With POLY + await catchRevert( + I_USDTieredSTO_Array[stoId].buyWithPOLYRateLimited(ACCREDITED1, investment_POLY, minTokens, { + from: ACCREDITED1, + gasPrice: GAS_PRICE + }) + ); + await catchRevert( + I_USDTieredSTO_Array[stoId].buyWithETHRateLimited(ACCREDITED1, minTokens, { + from: ACCREDITED1, + gasPrice: GAS_PRICE, + value: investment_ETH + }) + ); + }); + it("should fail and revert despite oracle price change when NONACCREDITED cap reached", async () => { let stoId = 2; let tierId = 0; // set new exchange rates - let high_USDETH = new BigNumber(1000).mul(10 ** 18); // 1000 USD per ETH - let high_USDPOLY = new BigNumber(50).mul(10 ** 16); // 0.5 USD per POLY - let low_USDETH = new BigNumber(250).mul(10 ** 18); // 250 USD per ETH - let low_USDPOLY = new BigNumber(20).mul(10 ** 16); // 0.2 USD per POLY + let high_USDETH = new BN(1000).mul(e18); // 1000 USD per ETH + let high_USDPOLY = new BN(50).mul(e16); // 0.5 USD per POLY + let low_USDETH = new BN(250).mul(e18); // 250 USD per ETH + let low_USDPOLY = new BN(20).mul(e16); // 0.2 USD per POLY - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, true, "TOKEN", "USD", investment_Token); - let investment_ETH_high = investment_USD.div(high_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY - let investment_ETH_low = investment_USD.div(low_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY + let investment_ETH_high = investment_USD.div(high_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(e18); // USD / USD/POLY = POLY + let investment_ETH_low = investment_USD.div(low_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(e18); // USD / USD/POLY = POLY await I_PolyToken.getTokens(investment_POLY_low, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY_low, { from: NONACCREDITED1 }); @@ -3829,19 +4114,19 @@ contract("USDTieredSTO", accounts => { let endTier = 1; assert.equal( - (await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), + (await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), startTier, "currentTier not changed as expected" ); - let delta_Token = new BigNumber(5).mul(10 ** 18); // Token + let delta_Token = new BN(5).mul(e18); // Token let polyTier0 = await convert(stoId, startTier, true, "TOKEN", "POLY", delta_Token); let polyTier1 = await convert(stoId, endTier, true, "TOKEN", "POLY", delta_Token); let investment_Token = delta_Token.add(delta_Token); // 10 Token let investment_POLY = polyTier0.add(polyTier1); // 0.0025 ETH - let tokensRemaining = (await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(startTier)).sub( - await I_USDTieredSTO_Array[stoId].mintedPerTierTotal.call(startTier) + let tokensRemaining = (await I_USDTieredSTO_Array[stoId].tiers.call(startTier))[2].sub( + (await I_USDTieredSTO_Array[stoId].tiers.call(startTier))[4] ); let prep_Token = tokensRemaining.sub(delta_Token); let prep_POLY = await convert(stoId, startTier, true, "TOKEN", "POLY", prep_Token); @@ -3851,9 +4136,9 @@ contract("USDTieredSTO", accounts => { let tx = await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, prep_POLY, { from: ACCREDITED1, gasPrice: GAS_PRICE }); console.log(" Gas buyWithPOLY: ".grey + tx.receipt.gasUsed.toString().grey); - let Tier0Token = await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(startTier); - let Tier0Minted = await I_USDTieredSTO_Array[stoId].mintedPerTierTotal.call(startTier); - assert.equal(Tier0Minted.toNumber(), Tier0Token.sub(delta_Token).toNumber()); + let Tier0Token = (await I_USDTieredSTO_Array[stoId].tiers.call(startTier))[2]; + let Tier0Minted = (await I_USDTieredSTO_Array[stoId].tiers.call(startTier))[4]; + assert.equal(Tier0Minted.toString(), Tier0Token.sub(delta_Token).toString()); await I_PolyToken.getTokens(investment_POLY, ACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY, { from: ACCREDITED1 }); @@ -3861,87 +4146,87 @@ contract("USDTieredSTO", accounts => { // Process investment let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let tx2 = await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); // Additional Checks - assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toNumber(), endTier, "currentTier not changed as expected"); + assert.equal((await I_USDTieredSTO_Array[stoId].currentTier.call()).toString(), endTier, "currentTier not changed as expected"); }); it("should successfully buy across the discount cap", async () => { let stoId = 2; let tierId = 1; - let discount_Token = new BigNumber(20).mul(10 ** 18); + let discount_Token = new BN(20).mul(e18); let discount_POLY = await convert(stoId, tierId, true, "TOKEN", "POLY", discount_Token); - let regular_Token = new BigNumber(10).mul(10 ** 18); + let regular_Token = new BN(10).mul(e18); let regular_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", regular_Token); let investment_Token = discount_Token.add(regular_Token); @@ -3952,14 +4237,14 @@ contract("USDTieredSTO", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); // Buy With POLY @@ -3967,58 +4252,58 @@ contract("USDTieredSTO", accounts => { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(ACCREDITED1)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(ACCREDITED1)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(ETH); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(POLY); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost2).toNumber(), + final_InvestorETHBal.toString(), + init_InvestorETHBal.sub(gasCost2).toString(), "Investor ETH Balance not changed as expected" ); assert.equal( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), + final_InvestorPOLYBal.toString(), + init_InvestorPOLYBal.sub(investment_POLY).toString(), "Investor POLY Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); - assert.equal(final_STOETHBal.toNumber(), init_STOETHBal.toNumber(), "STO ETH Balance not changed as expected"); - assert.equal(final_STOPOLYBal.toNumber(), init_STOPOLYBal.toNumber(), "STO POLY Balance not changed as expected"); - assert.equal(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), "Raised ETH not changed as expected"); + assert.equal(final_STOETHBal.toString(), init_STOETHBal.toString(), "STO ETH Balance not changed as expected"); + assert.equal(final_STOPOLYBal.toString(), init_STOPOLYBal.toString(), "STO POLY Balance not changed as expected"); + assert.equal(final_RaisedETH.toString(), init_RaisedETH.toString(), "Raised ETH not changed as expected"); assert.equal( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), + final_RaisedPOLY.toString(), + init_RaisedPOLY.add(investment_POLY).toString(), "Raised POLY not changed as expected" ); - assert.equal(final_WalletETHBal.toNumber(), init_WalletETHBal.toNumber(), "Wallet ETH Balance not changed as expected"); + assert.equal(final_WalletETHBal.toString(), init_WalletETHBal.toString(), "Wallet ETH Balance not changed as expected"); assert.equal( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), + final_WalletPOLYBal.toString(), + init_WalletPOLYBal.add(investment_POLY).toString(), "Wallet POLY Balance not changed as expected" ); }); @@ -4027,7 +4312,7 @@ contract("USDTieredSTO", accounts => { let stoId = 2; let tierId = 1; - let minted = await I_USDTieredSTO_Array[stoId].mintedPerTierTotal.call(tierId); + let minted = (await I_USDTieredSTO_Array[stoId].tiers.call(tierId))[4]; let investment_Token = _tokensPerTierTotal[stoId][tierId].sub(minted); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -4043,26 +4328,26 @@ contract("USDTieredSTO", accounts => { from: ACCREDITED1, gasPrice: GAS_PRICE }); - let gasCost2 = new BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); - console.log(" Gas buyWithPOLY: ".grey + tx2.receipt.gasUsed.toString().grey); + let gasCost2 = new BN(GAS_PRICE).mul(new BN(tx2.receipt.gasUsed)); + console.log(" Gas buyWithPOLY: ".grey + new BN(tx2.receipt.gasUsed).toString().grey); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(ACCREDITED1); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); assert.equal( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), + final_TokenSupply.toString(), + init_TokenSupply.add(investment_Token).toString(), "Token Supply not changed as expected" ); assert.equal( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), + final_InvestorTokenBal.toString(), + init_InvestorTokenBal.add(investment_Token).toString(), "Investor Token Balance not changed as expected" ); assert.equal( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), + final_STOTokenSold.toString(), + init_STOTokenSold.add(investment_Token).toString(), "STO Token Sold not changed as expected" ); }); @@ -4071,7 +4356,7 @@ contract("USDTieredSTO", accounts => { let stoId = 2; let tierId = 1; - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, false, "TOKEN", "USD", investment_Token); let investment_ETH = await convert(stoId, tierId, false, "TOKEN", "ETH", investment_Token); let investment_POLY = await convert(stoId, tierId, false, "TOKEN", "POLY", investment_Token); @@ -4111,18 +4396,18 @@ contract("USDTieredSTO", accounts => { let tierId = 1; // set new exchange rates - let high_USDETH = new BigNumber(1000).mul(10 ** 18); // 1000 USD per ETH - let high_USDPOLY = new BigNumber(50).mul(10 ** 16); // 0.5 USD per POLY - let low_USDETH = new BigNumber(250).mul(10 ** 18); // 250 USD per ETH - let low_USDPOLY = new BigNumber(20).mul(10 ** 16); // 0.2 USD per POLY + let high_USDETH = new BN(1000).mul(e18); // 1000 USD per ETH + let high_USDPOLY = new BN(50).mul(e16); // 0.5 USD per POLY + let low_USDETH = new BN(250).mul(e18); // 250 USD per ETH + let low_USDPOLY = new BN(20).mul(e16); // 0.2 USD per POLY - let investment_Token = new BigNumber(5).mul(10 ** 18); + let investment_Token = new BN(5).mul(e18); let investment_USD = await convert(stoId, tierId, true, "TOKEN", "USD", investment_Token); - let investment_ETH_high = investment_USD.div(high_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY - let investment_ETH_low = investment_USD.div(low_USDETH).mul(10 ** 18); // USD / USD/ETH = ETH - let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(10 ** 18); // USD / USD/POLY = POLY + let investment_ETH_high = investment_USD.div(high_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_high = investment_USD.div(high_USDPOLY).mul(e18); // USD / USD/POLY = POLY + let investment_ETH_low = investment_USD.div(low_USDETH).mul(e18); // USD / USD/ETH = ETH + let investment_POLY_low = investment_USD.div(low_USDPOLY).mul(e18); // USD / USD/POLY = POLY await I_PolyToken.getTokens(investment_POLY_low, NONACCREDITED1); await I_PolyToken.approve(I_USDTieredSTO_Array[stoId].address, investment_POLY_low, { from: NONACCREDITED1 }); @@ -4203,39 +4488,57 @@ contract("USDTieredSTO", accounts => { describe("Generic", async () => { it("should get the right number of investors", async () => { assert.equal( - (await I_USDTieredSTO_Array[0].investorCount.call()).toNumber(), - (await I_USDTieredSTO_Array[0].investorCount()).toNumber(), + (await I_USDTieredSTO_Array[0].investorCount.call()).toString(), + (await I_USDTieredSTO_Array[0].investorCount()).toString(), "Investor count not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[1].investorCount.call()).toNumber(), - (await I_USDTieredSTO_Array[1].investorCount()).toNumber(), + (await I_USDTieredSTO_Array[1].investorCount.call()).toString(), + (await I_USDTieredSTO_Array[1].investorCount()).toString(), "Investor count not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[2].investorCount.call()).toNumber(), - (await I_USDTieredSTO_Array[2].investorCount()).toNumber(), + (await I_USDTieredSTO_Array[2].investorCount.call()).toString(), + (await I_USDTieredSTO_Array[2].investorCount()).toString(), "Investor count not changed as expected" ); }); it("should get the right amounts invested", async () => { assert.equal( - (await I_USDTieredSTO_Array[0].fundsRaised.call(ETH)).toNumber(), - (await I_USDTieredSTO_Array[0].getRaised(0)).toNumber(), + (await I_USDTieredSTO_Array[0].fundsRaised.call(ETH)).toString(), + (await I_USDTieredSTO_Array[0].getRaised(0)).toString(), "getRaisedEther not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[0].fundsRaised.call(POLY)).toNumber(), - (await I_USDTieredSTO_Array[0].getRaised(1)).toNumber(), + (await I_USDTieredSTO_Array[0].fundsRaised.call(POLY)).toString(), + (await I_USDTieredSTO_Array[0].getRaised(1)).toString(), "getRaisedPOLY not changed as expected" ); assert.equal( - (await I_USDTieredSTO_Array[0].fundsRaisedUSD.call()).toNumber(), - (await I_USDTieredSTO_Array[0].fundsRaisedUSD()).toNumber(), + (await I_USDTieredSTO_Array[0].fundsRaisedUSD.call()).toString(), + (await I_USDTieredSTO_Array[0].fundsRaisedUSD()).toString(), "fundsRaisedUSD not changed as expected" ); }); + + it("should return minted tokens in a tier", async () => { + let totalMinted = (await I_USDTieredSTO_Array[0].getTokensSoldByTier.call(0)).toString(); + let individualMinted = await I_USDTieredSTO_Array[0].getTokensMintedByTier.call(0); + assert.equal( + totalMinted, + individualMinted[0] + .add(individualMinted[1]) + .add(individualMinted[2]) + .toString() + ); + }); + + it("should return correct tokens sold in token details", async () => { + let tokensSold = (await I_USDTieredSTO_Array[0].getTokensSold.call()).toString(); + let tokenDetails = await I_USDTieredSTO_Array[0].getSTODetails.call(); + assert.equal(tokensSold, tokenDetails[7].toString()); + }); }); describe("convertToUSD", async () => { @@ -4247,27 +4550,27 @@ contract("USDTieredSTO", accounts => { it("should get the right conversion for ETH to USD", async () => { // 20 ETH to 10000 USD - let ethInWei = new BigNumber(web3.utils.toWei("20", "ether")); - let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD(ETH, ethInWei); + let ethInWei = new BN(web3.utils.toWei("20", "ether")); + let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD.call(ETH, ethInWei); assert.equal( - usdInWei.div(10 ** 18).toNumber(), + usdInWei.div(e18).toString(), ethInWei - .div(10 ** 18) - .mul(USDETH.div(10 ** 18)) - .toNumber() + .div(e18) + .mul(USDETH.div(e18)) + .toString() ); }); it("should get the right conversion for POLY to USD", async () => { // 40000 POLY to 10000 USD - let polyInWei = new BigNumber(web3.utils.toWei("40000", "ether")); - let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD(POLY, polyInWei); + let polyInWei = new BN(web3.utils.toWei("40000", "ether")); + let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD.call(POLY, polyInWei); assert.equal( - usdInWei.div(10 ** 18).toNumber(), + usdInWei.toString(), polyInWei - .div(10 ** 18) - .mul(USDPOLY.div(10 ** 18)) - .toNumber() + .mul(USDPOLY) + .div(e18) + .toString() ); }); }); @@ -4275,27 +4578,24 @@ contract("USDTieredSTO", accounts => { describe("convertFromUSD", async () => { it("should get the right conversion for USD to ETH", async () => { // 10000 USD to 20 ETH - let usdInWei = new BigNumber(web3.utils.toWei("10000", "ether")); - let ethInWei = await I_USDTieredSTO_Array[0].convertFromUSD(ETH, usdInWei); + let usdInWei = new BN(web3.utils.toWei("10000", "ether")); + let ethInWei = await I_USDTieredSTO_Array[0].convertFromUSD.call(ETH, usdInWei); assert.equal( - ethInWei.div(10 ** 18).toNumber(), + ethInWei.div(e18).toString(), usdInWei - .div(10 ** 18) - .div(USDETH.div(10 ** 18)) - .toNumber() + .div(e18) + .div(USDETH.div(e18)) + .toString() ); }); it("should get the right conversion for USD to POLY", async () => { // 10000 USD to 40000 POLY - let usdInWei = new BigNumber(web3.utils.toWei("10000", "ether")); - let polyInWei = await I_USDTieredSTO_Array[0].convertFromUSD(POLY, usdInWei); + let usdInWei = new BN(web3.utils.toWei("10000", "ether")); + let polyInWei = await I_USDTieredSTO_Array[0].convertFromUSD.call(POLY, usdInWei); assert.equal( - polyInWei.div(10 ** 18).toNumber(), - usdInWei - .div(10 ** 18) - .div(USDPOLY.div(10 ** 18)) - .toNumber() + polyInWei.toString(), + usdInWei.mul(e18).div(USDPOLY).toString() ); }); }); @@ -4303,15 +4603,17 @@ contract("USDTieredSTO", accounts => { describe("Test cases for the USDTieredSTOFactory", async () => { it("should get the exact details of the factory", async () => { - assert.equal((await I_USDTieredSTOFactory.getSetupCost.call()).toNumber(), STOSetupCost); + assert.equal((await I_USDTieredSTOFactory.getSetupCost.call()).toString(), STOSetupCost); assert.equal((await I_USDTieredSTOFactory.getTypes.call())[0], 3); assert.equal(web3.utils.hexToString(await I_USDTieredSTOFactory.getName.call()), "USDTieredSTO", "Wrong Module added"); - assert.equal(await I_USDTieredSTOFactory.description.call(), - "It allows both accredited and non-accredited investors to contribute into the STO. Non-accredited investors will be capped at a maximum investment limit (as a default or specific to their jurisdiction). Tokens will be sold according to tiers sequentially & each tier has its own price and volume of tokens to sell. Upon receipt of funds (ETH, POLY or DAI), security tokens will automatically transfer to investor’s wallet address", - "Wrong Module added"); + assert.equal( + await I_USDTieredSTOFactory.description.call(), + "It allows both accredited and non-accredited investors to contribute into the STO. Non-accredited investors will be capped at a maximum investment limit (as a default or specific to their jurisdiction). Tokens will be sold according to tiers sequentially & each tier has its own price and volume of tokens to sell. Upon receipt of funds (ETH, POLY or DAI), security tokens will automatically transfer to investor’s wallet address", + "Wrong Module added" + ); assert.equal(await I_USDTieredSTOFactory.title.call(), "USD Tiered STO", "Wrong Module added"); assert.equal(await I_USDTieredSTOFactory.getInstructions.call(), "Initialises a USD tiered STO.", "Wrong Module added"); - assert.equal(await I_USDTieredSTOFactory.version.call(), "1.0.0"); + assert.equal(await I_USDTieredSTOFactory.version.call(), "2.1.0"); let tags = await I_USDTieredSTOFactory.getTags.call(); assert.equal(web3.utils.hexToString(tags[0]), "USD"); assert.equal(web3.utils.hexToString(tags[1]), "Tiered"); diff --git a/test/q_usd_tiered_sto_sim.js b/test/q_usd_tiered_sto_sim.js index 0548a6db6..1bed3ccb7 100644 --- a/test/q_usd_tiered_sto_sim.js +++ b/test/q_usd_tiered_sto_sim.js @@ -12,14 +12,15 @@ const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -const TOLERANCE = 2; // Allow balances to be off by 2 WEI for rounding purposes +//const TOLERANCE = 2; // Allow balances to be off by 2 WEI for rounding purposes -contract("USDTieredSTO Sim", accounts => { +contract("USDTieredSTO Sim", async (accounts) => { // Accounts Variable declaration let POLYMATH; let ISSUER; @@ -57,6 +58,9 @@ contract("USDTieredSTO Sim", accounts => { let I_PolyToken; let I_DaiToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details for funds raise Type ETH const NAME = "Team"; @@ -69,12 +73,12 @@ contract("USDTieredSTO Sim", accounts => { const STOKEY = 3; // Initial fee for ticker registry and security token registry - const REGFEE = web3.utils.toWei("250"); + const REGFEE = new BN(web3.utils.toWei("1000")); const STOSetupCost = 0; // MockOracle USD prices - const USDETH = new BigNumber(500).mul(10 ** 18); // 500 USD/ETH - const USDPOLY = new BigNumber(25).mul(10 ** 16); // 0.25 USD/POLY + const USDETH = new BN(500).mul(new BN(10).pow(new BN(18))); // 500 USD/ETH + const USDPOLY = new BN(25).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY // STO Configuration Arrays let _startTime = []; @@ -90,6 +94,9 @@ contract("USDTieredSTO Sim", accounts => { let _reserveWallet = []; let _usdToken = []; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + /* function configure( uint256 _startTime, uint256 _endTime, @@ -153,18 +160,30 @@ contract("USDTieredSTO Sim", accounts => { name: "_reserveWallet" }, { - type: "address", - name: "_usdToken" + type: "address[]", + name: "_usdTokens" } ] }; function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; + let random = Math.floor(Math.random() * 10 ** 10); + return new BN(random).mul(new BN(max).add(new BN(1)).sub(new BN(min))).div(new BN(10).pow(new BN(10))); + } + + function minBN(a, b) { + if (a.lt(b)) + return a; + else + return b; } + let currentTime; + let e18 = new BN(10).pow(new BN(18)); + let e16 = new BN(10).pow(new BN(16)); + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); POLYMATH = accounts[0]; ISSUER = accounts[1]; WALLET = accounts[2]; @@ -193,17 +212,19 @@ contract("USDTieredSTO Sim", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - - I_DaiToken = await PolyTokenFaucet.new({from: POLYMATH}); - // STEP 5: Deploy the USDTieredSTOFactory - [I_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, I_PolyToken.address, STOSetupCost); + I_DaiToken = await PolyTokenFaucet.new({ from: POLYMATH }); + + // STEP 5: Deploy the USDTieredSTOFactory + [I_USDTieredSTOFactory] = await deployUSDTieredSTOAndVerified(POLYMATH, I_MRProxied, STOSetupCost); // Step 12: Deploy & Register Mock Oracles - I_USDOracle = await MockOracle.new(0, "ETH", "USD", USDETH, { from: POLYMATH }); // 500 dollars per POLY - I_POLYOracle = await MockOracle.new(I_PolyToken.address, "POLY", "USD", USDPOLY, { from: POLYMATH }); // 25 cents per POLY + I_USDOracle = await MockOracle.new(address_zero, web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), USDETH, { from: POLYMATH }); // 500 dollars per POLY + I_POLYOracle = await MockOracle.new(I_PolyToken.address, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), USDPOLY, { from: POLYMATH }); // 25 cents per POLY await I_PolymathRegistry.changeAddress("EthUsdOracle", I_USDOracle.address, { from: POLYMATH }); await I_PolymathRegistry.changeAddress("PolyUsdOracle", I_POLYOracle.address, { from: POLYMATH }); @@ -239,13 +260,13 @@ contract("USDTieredSTO Sim", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.getTokens(REGFEE, ISSUER); await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(NAME, SYMBOL, TOKENDETAILS, true, { from: ISSUER }); - assert.equal(tx.logs[1].args._ticker, SYMBOL, "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + let tx = await I_STRProxied.generateSecurityToken(NAME, SYMBOL, TOKENDETAILS, true, { from: ISSUER }); + assert.equal(tx.logs[2].args._ticker, SYMBOL, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), TMKEY); @@ -253,21 +274,21 @@ contract("USDTieredSTO Sim", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(TMKEY))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(TMKEY))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the first STO module to the security token", async () => { let stoId = 0; - _startTime.push(latestTime() + duration.days(2)); - _endTime.push(_startTime[stoId] + duration.days(100)); - _ratePerTier.push([BigNumber(0.05 * 10 ** 18), BigNumber(0.13 * 10 ** 18), BigNumber(0.17 * 10 ** 18)]); // [ 0.05 USD/Token, 0.10 USD/Token, 0.15 USD/Token ] - _ratePerTierDiscountPoly.push([BigNumber(0.05 * 10 ** 18), BigNumber(0.08 * 10 ** 18), BigNumber(0.13 * 10 ** 18)]); // [ 0.05 USD/Token, 0.08 USD/Token, 0.13 USD/Token ] - _tokensPerTierTotal.push([BigNumber(200 * 10 ** 18), BigNumber(500 * 10 ** 18), BigNumber(300 * 10 ** 18)]); // [ 1000 Token, 2000 Token, 1500 Token ] - _tokensPerTierDiscountPoly.push([BigNumber(0), BigNumber(50 * 10 ** 18), BigNumber(300 * 10 ** 18)]); // [ 0 Token, 1000 Token, 1500 Token ] - _nonAccreditedLimitUSD.push(new BigNumber(10 * 10 ** 18)); // 20 USD - _minimumInvestmentUSD.push(new BigNumber(0)); // 1 wei USD + _startTime.push(new BN(currentTime).add(new BN(duration.days(2)))); + _endTime.push(new BN(_startTime[stoId]).add(new BN(currentTime).add(new BN(duration.days(100))))); + _ratePerTier.push([new BN(50).mul(e16), new BN(130).mul(e16), new BN(170).mul(e16)]); // [ 0.05 USD/Token, 0.10 USD/Token, 0.15 USD/Token ] + _ratePerTierDiscountPoly.push([new BN(50).mul(e16), new BN(80).mul(e16), new BN(130).mul(e16)]); // [ 0.05 USD/Token, 0.08 USD/Token, 0.13 USD/Token ] + _tokensPerTierTotal.push([new BN(200).mul(e18), new BN(500).mul(e18), new BN(300).mul(e18)]); // [ 1000 Token, 2000 Token, 1500 Token ] + _tokensPerTierDiscountPoly.push([new BN(0), new BN(50).mul(e18), new BN(300).mul(e18)]); // [ 0 Token, 1000 Token, 1500 Token ] + _nonAccreditedLimitUSD.push(new BN(10).mul(e18)); // 20 USD + _minimumInvestmentUSD.push(new BN(0)); // 1 wei USD _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); @@ -285,48 +306,48 @@ contract("USDTieredSTO Sim", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER, gasPrice: GAS_PRICE }); + let tx = await I_SecurityToken.addModule(I_USDTieredSTOFactory.address, bytesSTO, new BN(0), new BN(0), { from: ISSUER, gasPrice: GAS_PRICE }); console.log(" Gas addModule: ".grey + tx.receipt.gasUsed.toString().grey); assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + I_USDTieredSTO_Array.push(await USDTieredSTO.at(tx.logs[2].args._module)); - assert.equal(await I_USDTieredSTO_Array[stoId].startTime.call(), _startTime[stoId], "Incorrect _startTime in config"); - assert.equal(await I_USDTieredSTO_Array[stoId].endTime.call(), _endTime[stoId], "Incorrect _endTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].startTime.call()).toString(), _startTime[stoId].toString(), "Incorrect _startTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].endTime.call()).toString(), _endTime[stoId].toString(), "Incorrect _endTime in config"); for (var i = 0; i < _ratePerTier[stoId].length; i++) { assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTier.call(i)).toNumber(), - _ratePerTier[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[0].toString(), + _ratePerTier[stoId][i].toString(), "Incorrect _ratePerTier in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].ratePerTierDiscountPoly.call(i)).toNumber(), - _ratePerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[1].toString(), + _ratePerTierDiscountPoly[stoId][i].toString(), "Incorrect _ratePerTierDiscountPoly in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(i)).toNumber(), - _tokensPerTierTotal[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[2].toString(), + _tokensPerTierTotal[stoId][i].toString(), "Incorrect _tokensPerTierTotal in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].tokensPerTierDiscountPoly.call(i)).toNumber(), - _tokensPerTierDiscountPoly[stoId][i].toNumber(), + (await I_USDTieredSTO_Array[stoId].tiers.call(i))[3].toString(), + _tokensPerTierDiscountPoly[stoId][i].toString(), "Incorrect _tokensPerTierDiscountPoly in config" ); } assert.equal( - (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toNumber(), - _nonAccreditedLimitUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].nonAccreditedLimitUSD.call()).toString(), + _nonAccreditedLimitUSD[stoId].toString(), "Incorrect _nonAccreditedLimitUSD in config" ); assert.equal( - (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toNumber(), - _minimumInvestmentUSD[stoId].toNumber(), + (await I_USDTieredSTO_Array[stoId].minimumInvestmentUSD.call()).toString(), + _minimumInvestmentUSD[stoId].toString(), "Incorrect _minimumInvestmentUSD in config" ); assert.equal(await I_USDTieredSTO_Array[stoId].wallet.call(), _wallet[stoId], "Incorrect _wallet in config"); @@ -340,7 +361,7 @@ contract("USDTieredSTO Sim", accounts => { _tokensPerTierTotal[stoId].length, "Incorrect number of tiers" ); - assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, 0, "Incorrect number of permissions"); + assert.equal((await I_USDTieredSTO_Array[stoId].getPermissions()).length, new BN(0), "Incorrect number of permissions"); }); it("Should successfully prepare the STO", async () => { @@ -350,22 +371,25 @@ contract("USDTieredSTO Sim", accounts => { await increaseTime(duration.days(3)); // Whitelist - let fromTime = latestTime() + duration.days(15); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime() + duration.days(15); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let canBuyFromSTO = true; - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, canBuyFromSTO, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(ACCREDITED2, fromTime, toTime, expiryTime, canBuyFromSTO, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, canBuyFromSTO, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED2, fromTime, toTime, expiryTime, canBuyFromSTO, { from: ISSUER }); - await I_GeneralTransferManager.modifyWhitelist(NOTAPPROVED, fromTime, toTime, expiryTime, false, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(ACCREDITED2, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED2, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED1, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NONACCREDITED2, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyKYCData(NOTAPPROVED, fromTime, toTime, expiryTime, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(NOTAPPROVED, 1, true, { from: ISSUER }); await increaseTime(duration.days(3)); // Accreditation - await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1, ACCREDITED2], [true, true], { from: ISSUER }); - await I_USDTieredSTO_Array[stoId].changeAccredited([NONACCREDITED1, NONACCREDITED2], [false, false], { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED1, 0, true, { from: ISSUER }); + await I_GeneralTransferManager.modifyInvestorFlag(ACCREDITED2, 0, true, { from: ISSUER }); }); }); @@ -384,14 +408,15 @@ contract("USDTieredSTO Sim", accounts => { ---------------------------------------------------------- `); - let totalTokens = new BigNumber(0); + let totalTokens = new BN(0); for (var i = 0; i < _tokensPerTierTotal[stoId].length; i++) { totalTokens = totalTokens.add(_tokensPerTierTotal[stoId][i]); } - console.log("totalTokens: " + totalTokens.div(10 ** 18).toNumber()); - let tokensSold = new BigNumber(0); + let tokensSold = new BN(0); while (true) { - switch (getRandomInt(0, 5)) { + let rn = getRandomInt(0, 5); + let rno = rn.toNumber(); + switch (rno) { case 0: // ACCREDITED1 await invest(ACCREDITED1, true); break; @@ -423,8 +448,8 @@ contract("USDTieredSTO Sim", accounts => { } console.log("Next round"); tokensSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - console.log("Tokens Sold: " + tokensSold.toString()); - if (tokensSold.gte(totalTokens.sub(1 * 10 ** 18))) { + console.log("Tokens Sold: " + tokensSold.div(e18).toString()); + if (tokensSold.gte(totalTokens.sub(new BN(1)))) { console.log(`${tokensSold} tokens sold, simulation completed successfully!`.green); break; } @@ -437,33 +462,26 @@ contract("USDTieredSTO Sim", accounts => { let USD_to_date = await I_USDTieredSTO_Array[stoId].investorInvestedUSD.call(_investor); USD_remaining = _nonAccreditedLimitUSD[stoId].sub(USD_to_date); } else { - USD_remaining = totalTokens.mul(2); + USD_remaining = totalTokens.mul(new BN(2)); } let log_remaining = USD_remaining; let isPoly = Math.random() >= 0.33; let isDai = Math.random() >= 0.33; - let Token_counter = new BigNumber(getRandomInt(1 * 10 ** 10, 50 * 10 ** 10)).mul(10 ** 8); - let investment_USD = new BigNumber(0); - let investment_ETH = new BigNumber(0); - let investment_POLY = new BigNumber(0); - let investment_DAI = new BigNumber(0); - let investment_Token = new BigNumber(0); + let Token_counter = new BN(getRandomInt(new BN(1).mul(new BN(10).pow(new BN(10))), new BN(5).mul(new BN(10).pow(new BN(11))))).mul(new BN(10).pow(new BN(8))); + let investment_USD = new BN(0); + let investment_ETH = new BN(0); + let investment_POLY = new BN(0); + let investment_DAI = new BN(0); + let investment_Token = new BN(0); let Tokens_total = []; let Tokens_discount = []; for (var i = 0; i < _ratePerTier[stoId].length; i++) { - Tokens_total.push( - (await I_USDTieredSTO_Array[stoId].tokensPerTierTotal.call(i)).sub( - await I_USDTieredSTO_Array[stoId].mintedPerTierTotal.call(i) - ) - ); - Tokens_discount.push( - (await I_USDTieredSTO_Array[stoId].tokensPerTierDiscountPoly.call(i)).sub( - await I_USDTieredSTO_Array[stoId].mintedPerTierDiscountPoly.call(i) - ) - ); + let tierData = await I_USDTieredSTO_Array[stoId].tiers.call(i); + Tokens_total.push(new BN(tierData[2]).sub(tierData[4])); + Tokens_discount.push(new BN(tierData[3]).sub(tierData[5])); } let tier = 0; @@ -476,25 +494,25 @@ contract("USDTieredSTO Sim", accounts => { let USD_overflow; let Token_overflow; - while (Token_counter.gt(0)) { + while (Token_counter.gt(new BN(0))) { if (tier == _ratePerTier[stoId].length) { break; } - if (Tokens_total[tier].gt(0)) { + if (Tokens_total[tier].gt(new BN(0))) { if (isPoly) { // 1. POLY and discount (consume up to cap then move to regular) - if (Tokens_discount[tier].gt(0)) { - Token_Tier = new BigNumber.min([Tokens_total[tier], Tokens_discount[tier], Token_counter]); - USD_Tier = Token_Tier.mul(_ratePerTierDiscountPoly[stoId][tier].div(10 ** 18)); + if (Tokens_discount[tier].gt(new BN(0))) { + Token_Tier = minBN(minBN(Tokens_total[tier], Tokens_discount[tier]), Token_counter); + USD_Tier = Token_Tier.mul(_ratePerTierDiscountPoly[stoId][tier]).div(e18); if (USD_Tier.gte(USD_remaining)) { USD_overflow = USD_Tier.sub(USD_remaining); - Token_overflow = USD_overflow.mul(10 ** 18).div(_ratePerTierDiscountPoly[stoId][tier]); + Token_overflow = USD_overflow.mul(e18).div(_ratePerTierDiscountPoly[stoId][tier]); USD_Tier = USD_Tier.sub(USD_overflow); Token_Tier = Token_Tier.sub(Token_overflow); - Token_counter = new BigNumber(0); + Token_counter = new BN(0); } - POLY_Tier = new BigNumber(USD_Tier.mul(10 ** 18).toFixed(0)); - POLY_Tier = POLY_Tier.div(USDPOLY).toFixed(0); + POLY_Tier = new BN(USD_Tier.mul(e18)); + POLY_Tier = POLY_Tier.div(USDPOLY); USD_remaining = USD_remaining.sub(USD_Tier); Tokens_total[tier] = Tokens_total[tier].sub(Token_Tier); Tokens_discount[tier] = Tokens_discount[tier].sub(Token_Tier); @@ -504,18 +522,18 @@ contract("USDTieredSTO Sim", accounts => { investment_POLY = investment_POLY.add(POLY_Tier); } // 2. POLY and regular (consume up to cap then skip to next tier) - if (Tokens_total[tier].gt(0) && Token_counter.gt(0)) { - Token_Tier = new BigNumber.min([Tokens_total[tier], Token_counter]); - USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier].div(10 ** 18)); + if (Tokens_total[tier].gt(new BN(0)) && Token_counter.gt(new BN(0))) { + Token_Tier = minBN(Tokens_total[tier], Token_counter); + USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier]).div(e18); if (USD_Tier.gte(USD_remaining)) { USD_overflow = USD_Tier.sub(USD_remaining); - Token_overflow = USD_overflow.mul(10 ** 18).div(_ratePerTier[stoId][tier]); + Token_overflow = USD_overflow.mul(e18).div(_ratePerTier[stoId][tier]); USD_Tier = USD_Tier.sub(USD_overflow); Token_Tier = Token_Tier.sub(Token_overflow); - Token_counter = new BigNumber(0); + Token_counter = new BN(0); } - POLY_Tier = new BigNumber(USD_Tier.mul(10 ** 18).toFixed(0)); - POLY_Tier = POLY_Tier.div(USDPOLY).toFixed(0); + POLY_Tier = new BN(USD_Tier.mul(e18)); + POLY_Tier = POLY_Tier.div(USDPOLY); USD_remaining = USD_remaining.sub(USD_Tier); Tokens_total[tier] = Tokens_total[tier].sub(Token_Tier); Token_counter = Token_counter.sub(Token_Tier); @@ -525,16 +543,16 @@ contract("USDTieredSTO Sim", accounts => { } } else if (isDai) { // 3. DAI (consume up to cap then skip to next tier) - Token_Tier = new BigNumber.min([Tokens_total[tier], Token_counter]); - USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier].div(10 ** 18)); + Token_Tier = minBN(Tokens_total[tier], Token_counter); + USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier]).div(e18); if (USD_Tier.gte(USD_remaining)) { USD_overflow = USD_Tier.sub(USD_remaining); - Token_overflow = USD_overflow.mul(10 ** 18).div(_ratePerTier[stoId][tier]); + Token_overflow = USD_overflow.mul(e18).div(_ratePerTier[stoId][tier]); USD_Tier = USD_Tier.sub(USD_overflow); Token_Tier = Token_Tier.sub(Token_overflow); - Token_counter = new BigNumber(0); + Token_counter = new BN(0); } - DAI_Tier = USD_Tier.toFixed(0); + DAI_Tier = USD_Tier; USD_remaining = USD_remaining.sub(USD_Tier); Tokens_total[tier] = Tokens_total[tier].sub(Token_Tier); Token_counter = Token_counter.sub(Token_Tier); @@ -543,17 +561,16 @@ contract("USDTieredSTO Sim", accounts => { investment_DAI = investment_USD; } else { // 4. ETH (consume up to cap then skip to next tier) - Token_Tier = new BigNumber.min([Tokens_total[tier], Token_counter]); - USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier].div(10 ** 18)); + Token_Tier = minBN(Tokens_total[tier], Token_counter); + USD_Tier = Token_Tier.mul(_ratePerTier[stoId][tier]).div(e18); if (USD_Tier.gte(USD_remaining)) { USD_overflow = USD_Tier.sub(USD_remaining); - Token_overflow = USD_overflow.mul(10 ** 18).div(_ratePerTier[stoId][tier]); + Token_overflow = USD_overflow.mul(e18).div(_ratePerTier[stoId][tier]); USD_Tier = USD_Tier.sub(USD_overflow); Token_Tier = Token_Tier.sub(Token_overflow); - Token_counter = new BigNumber(0); + Token_counter = new BN(0); } - ETH_Tier = new BigNumber(USD_Tier.mul(10 ** 18).toFixed(0)); - ETH_Tier = ETH_Tier.div(USDETH).toFixed(0); + ETH_Tier = USD_Tier.mul(e18).div(USDETH); USD_remaining = USD_remaining.sub(USD_Tier); Tokens_total[tier] = Tokens_total[tier].sub(Token_Tier); Token_counter = Token_counter.sub(Token_Tier); @@ -562,7 +579,7 @@ contract("USDTieredSTO Sim", accounts => { investment_ETH = investment_ETH.add(ETH_Tier); } } - tier++; + tier = tier + 1; } await processInvestment( @@ -584,9 +601,9 @@ contract("USDTieredSTO Sim", accounts => { async function investFAIL(_investor) { let isPoly = Math.random() >= 0.3; let isDAI = Math.random() >= 0.3; - let investment_POLY = new BigNumber(40 * 10 ** 18); // 10 USD = 40 POLY - let investment_ETH = new BigNumber(0.02 * 10 ** 18); // 10 USD = 0.02 ETH - let investment_DAI = new BigNumber(10 * 10 ** 18); // 10 USD = DAI DAI + let investment_POLY = new BN(40).mul(e18); // 10 USD = 40 POLY + let investment_ETH = new BN(20).mul(e16); // 10 USD = 0.02 ETH + let investment_DAI = new BN(10).mul(e18); // 10 USD = DAI DAI if (isPoly) { await I_PolyToken.getTokens(investment_POLY, _investor); @@ -598,7 +615,7 @@ contract("USDTieredSTO Sim", accounts => { await I_DaiToken.getTokens(investment_DAI, _investor); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: _investor }); await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, { from: _investor, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, I_DaiToken.address, { from: _investor, gasPrice: GAS_PRICE }) ); } else await catchRevert( @@ -620,23 +637,23 @@ contract("USDTieredSTO Sim", accounts => { Tokens_discount, tokensSold ) { - investment_Token = new BigNumber(investment_Token.toFixed(0)); - investment_USD = new BigNumber(investment_USD.toFixed(0)); - investment_POLY = new BigNumber(investment_POLY.toFixed(0)); - investment_DAI = new BigNumber(investment_DAI.toFixed(0)); - investment_ETH = new BigNumber(investment_ETH.toFixed(0)); + investment_Token = new BN(investment_Token); + investment_USD = new BN(investment_USD); + investment_POLY = new BN(investment_POLY); + investment_DAI = new BN(investment_DAI); + investment_ETH = new BN(investment_ETH); console.log(` ------------------- New Investment ------------------- Investor: ${_investor} - N-A USD Remaining: ${log_remaining.div(10 ** 18)} + N-A USD Remaining: ${log_remaining} Total Cap Remaining: ${Tokens_total} Discount Cap Remaining: ${Tokens_discount} - Total Tokens Sold: ${tokensSold.div(10 ** 18)} - Token Investment: ${investment_Token.div(10 ** 18)} - USD Investment: ${investment_USD.div(10 ** 18)} - POLY Investment: ${investment_POLY.div(10 ** 18)} - DAI Investment: ${investment_DAI.div(10 ** 18)} - ETH Investment: ${investment_ETH.div(10 ** 18)} + Total Tokens Sold: ${tokensSold} + Token Investment: ${investment_Token} + USD Investment: ${investment_USD} + POLY Investment: ${investment_POLY} + DAI Investment: ${investment_DAI} + ETH Investment: ${investment_ETH} ------------------------------------------------------ `); @@ -652,69 +669,68 @@ contract("USDTieredSTO Sim", accounts => { let init_TokenSupply = await I_SecurityToken.totalSupply(); let init_InvestorTokenBal = await I_SecurityToken.balanceOf(_investor); - let init_InvestorETHBal = new BigNumber(await web3.eth.getBalance(_investor)); + let init_InvestorETHBal = new BN(await web3.eth.getBalance(_investor)); let init_InvestorPOLYBal = await I_PolyToken.balanceOf(_investor); let init_InvestorDAIBal = await I_DaiToken.balanceOf(_investor); let init_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let init_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let init_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let init_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_STODAIBal = await I_DaiToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let init_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let init_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(0); let init_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(1); let init_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(2); - let init_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let init_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let init_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); let tx; - let gasCost = new BigNumber(0); + let gasCost = new BN(0); - if (isPoly && investment_POLY.gt(10)) { + if (isPoly && investment_POLY.gt(new BN(10))) { tx = await I_USDTieredSTO_Array[stoId].buyWithPOLY(_investor, investment_POLY, { from: _investor, gasPrice: GAS_PRICE }); - gasCost = new BigNumber(GAS_PRICE).mul(tx.receipt.gasUsed); + gasCost = new BN(GAS_PRICE).mul(new BN(tx.receipt.gasUsed)); console.log( - `buyWithPOLY: ${investment_Token.div(10 ** 18)} tokens for ${investment_POLY.div(10 ** 18)} POLY by ${_investor}` + `buyWithPOLY: ${investment_Token.div(e18)} tokens for ${investment_POLY.div(e18)} POLY by ${_investor}` .yellow ); - } else if (isDai && investment_DAI.gt(10)) { - tx = await I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, { from: _investor, gasPrice: GAS_PRICE }); - gasCost = new BigNumber(GAS_PRICE).mul(tx.receipt.gasUsed); + } else if (isDai && investment_DAI.gt(new BN(10))) { + tx = await I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, I_DaiToken.address, { from: _investor, gasPrice: GAS_PRICE }); + gasCost = new BN(GAS_PRICE).mul(new BN(tx.receipt.gasUsed)); console.log( - `buyWithUSD: ${investment_Token.div(10 ** 18)} tokens for ${investment_DAI.div(10 ** 18)} DAI by ${_investor}` + `buyWithUSD: ${investment_Token.div(e18)} tokens for ${investment_DAI.div(e18)} DAI by ${_investor}` .yellow ); - } else if (investment_ETH.gt(0)) { + } else if (investment_ETH.gt(new BN(0))) { tx = await I_USDTieredSTO_Array[stoId].buyWithETH(_investor, { from: _investor, value: investment_ETH, gasPrice: GAS_PRICE }); - gasCost = new BigNumber(GAS_PRICE).mul(tx.receipt.gasUsed); + gasCost = new BN(GAS_PRICE).mul(new BN(tx.receipt.gasUsed)); console.log( - `buyWithETH: ${investment_Token.div(10 ** 18)} tokens for ${investment_ETH.div(10 ** 18)} ETH by ${_investor}` + `buyWithETH: ${investment_Token.div(e18)} tokens for ${investment_ETH.div(e18)} ETH by ${_investor}` .yellow ); } - console.log(investment_POLY.toNumber()); let final_TokenSupply = await I_SecurityToken.totalSupply(); let final_InvestorTokenBal = await I_SecurityToken.balanceOf(_investor); - let final_InvestorETHBal = new BigNumber(await web3.eth.getBalance(_investor)); + let final_InvestorETHBal = new BN(await web3.eth.getBalance(_investor)); let final_InvestorPOLYBal = await I_PolyToken.balanceOf(_investor); let final_InvestorDAIBal = await I_DaiToken.balanceOf(_investor); let final_STOTokenSold = await I_USDTieredSTO_Array[stoId].getTokensSold(); - let final_STOETHBal = new BigNumber(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); + let final_STOETHBal = new BN(await web3.eth.getBalance(I_USDTieredSTO_Array[stoId].address)); let final_STOPOLYBal = await I_PolyToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_STODAIBal = await I_DaiToken.balanceOf(I_USDTieredSTO_Array[stoId].address); let final_RaisedUSD = await I_USDTieredSTO_Array[stoId].fundsRaisedUSD.call(); let final_RaisedETH = await I_USDTieredSTO_Array[stoId].fundsRaised.call(0); let final_RaisedPOLY = await I_USDTieredSTO_Array[stoId].fundsRaised.call(1); let final_RaisedDAI = await I_USDTieredSTO_Array[stoId].fundsRaised.call(2); - let final_WalletETHBal = new BigNumber(await web3.eth.getBalance(WALLET)); + let final_WalletETHBal = new BN(await web3.eth.getBalance(WALLET)); let final_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let final_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); @@ -722,223 +738,56 @@ contract("USDTieredSTO Sim", accounts => { // console.log('final_TokenSupply: '+final_TokenSupply.div(10**18).toNumber()); if (isPoly) { - assert.closeTo( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), - TOLERANCE, - "Token Supply not changed as expected" - ); - assert.closeTo( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), - TOLERANCE, - "Investor Token Balance not changed as expected" - ); - assert.closeTo( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost).toNumber(), - TOLERANCE, - "Investor ETH Balance not changed as expected" - ); - assert.closeTo( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.sub(investment_POLY).toNumber(), - TOLERANCE, - "Investor POLY Balance not changed as expected" - ); - assert.closeTo( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), - TOLERANCE, - "STO Token Sold not changed as expected" - ); - assert.closeTo( - final_STOETHBal.toNumber(), - init_STOETHBal.toNumber(), - TOLERANCE, - "STO ETH Balance not changed as expected" - ); - assert.closeTo( - final_STOPOLYBal.toNumber(), - init_STOPOLYBal.toNumber(), - TOLERANCE, - "STO POLY Balance not changed as expected" - ); - assert.closeTo( - final_RaisedUSD.toNumber(), - init_RaisedUSD.add(investment_USD).toNumber(), - TOLERANCE, - "Raised USD not changed as expected" - ); - assert.closeTo(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), TOLERANCE, "Raised ETH not changed as expected"); - assert.closeTo( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.add(investment_POLY).toNumber(), - TOLERANCE, - "Raised POLY not changed as expected" - ); - assert.closeTo( - final_WalletETHBal.toNumber(), - init_WalletETHBal.toNumber(), - TOLERANCE, - "Wallet ETH Balance not changed as expected" - ); - assert.closeTo( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.add(investment_POLY).toNumber(), - TOLERANCE, - "Wallet POLY Balance not changed as expected" - ); + assertIsNear(final_TokenSupply, init_TokenSupply.add(investment_Token), "Token Supply not changed as expected" ); + assertIsNear(final_InvestorTokenBal, init_InvestorTokenBal.add(investment_Token), "Investor Token Balance not changed as expected" ); + assertIsNear(final_InvestorETHBal, init_InvestorETHBal.sub(gasCost), "Investor ETH Balance not changed as expected" ); + assertIsNear(final_InvestorPOLYBal, init_InvestorPOLYBal.sub(investment_POLY), "Investor POLY Balance not changed as expected" ); + assertIsNear(final_STOTokenSold, init_STOTokenSold.add(investment_Token), "STO Token Sold not changed as expected" ); + assertIsNear(final_STOETHBal, init_STOETHBal, "STO ETH Balance not changed as expected" ); + assertIsNear(final_STOPOLYBal, init_STOPOLYBal, "STO POLY Balance not changed as expected" ); + assertIsNear(final_RaisedUSD, init_RaisedUSD.add(investment_USD), "Raised USD not changed as expected" ); + assertIsNear(final_RaisedETH, init_RaisedETH, "Raised ETH not changed as expected"); + assertIsNear(final_RaisedPOLY, init_RaisedPOLY.add(investment_POLY), "Raised POLY not changed as expected" ); + assertIsNear(final_WalletETHBal, init_WalletETHBal, "Wallet ETH Balance not changed as expected" ); + assertIsNear(final_WalletPOLYBal, init_WalletPOLYBal.add(investment_POLY), "Wallet POLY Balance not changed as expected" ); } else if (isDai) { - assert.closeTo( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), - TOLERANCE, - "Token Supply not changed as expected" - ); - assert.closeTo( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), - TOLERANCE, - "Investor Token Balance not changed as expected" - ); - assert.closeTo( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal.sub(gasCost).toNumber(), - TOLERANCE, - "Investor ETH Balance not changed as expected" - ); - assert.closeTo( - final_InvestorDAIBal.toNumber(), - init_InvestorDAIBal.sub(investment_DAI).toNumber(), - TOLERANCE, - "Investor DAI Balance not changed as expected" - ); - assert.closeTo( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), - TOLERANCE, - "STO Token Sold not changed as expected" - ); - assert.closeTo( - final_STOETHBal.toNumber(), - init_STOETHBal.toNumber(), - TOLERANCE, - "STO ETH Balance not changed as expected" - ); - assert.closeTo( - final_STODAIBal.toNumber(), - init_STODAIBal.toNumber(), - TOLERANCE, - "STO DAI Balance not changed as expected" - ); - assert.closeTo( - final_RaisedUSD.toNumber(), - init_RaisedUSD.add(investment_USD).toNumber(), - TOLERANCE, - "Raised USD not changed as expected" - ); - assert.closeTo(final_RaisedETH.toNumber(), init_RaisedETH.toNumber(), TOLERANCE, "Raised ETH not changed as expected"); - assert.closeTo( - final_RaisedDAI.toNumber(), - init_RaisedDAI.add(investment_DAI).toNumber(), - TOLERANCE, - "Raised DAI not changed as expected" - ); - assert.closeTo( - final_WalletETHBal.toNumber(), - init_WalletETHBal.toNumber(), - TOLERANCE, - "Wallet ETH Balance not changed as expected" - ); - assert.closeTo( - final_WalletDAIBal.toNumber(), - init_WalletDAIBal.add(investment_DAI).toNumber(), - TOLERANCE, - "Wallet DAI Balance not changed as expected" - ); + assertIsNear(final_TokenSupply, init_TokenSupply.add(investment_Token), "Token Supply not changed as expected" ); + assertIsNear(final_InvestorTokenBal, init_InvestorTokenBal.add(investment_Token), "Investor Token Balance not changed as expected" ); + assertIsNear(final_InvestorETHBal, init_InvestorETHBal.sub(gasCost), "Investor ETH Balance not changed as expected" ); + assertIsNear(final_InvestorDAIBal, init_InvestorDAIBal.sub(investment_DAI), "Investor DAI Balance not changed as expected" ); + assertIsNear(final_STOTokenSold, init_STOTokenSold.add(investment_Token), "STO Token Sold not changed as expected" ); + assertIsNear(final_STOETHBal, init_STOETHBal, "STO ETH Balance not changed as expected" ); + assertIsNear(final_STODAIBal, init_STODAIBal, "STO DAI Balance not changed as expected" ); + assertIsNear(final_RaisedUSD, init_RaisedUSD.add(investment_USD), "Raised USD not changed as expected" ); + assertIsNear(final_RaisedETH, init_RaisedETH, "Raised ETH not changed as expected"); + assertIsNear(final_RaisedDAI, init_RaisedDAI.add(investment_DAI), "Raised DAI not changed as expected" ); + assertIsNear(final_WalletETHBal, init_WalletETHBal, "Wallet ETH Balance not changed as expected" ); + assertIsNear(final_WalletDAIBal, init_WalletDAIBal.add(investment_DAI), "Wallet DAI Balance not changed as expected" ); } else { - assert.closeTo( - final_TokenSupply.toNumber(), - init_TokenSupply.add(investment_Token).toNumber(), - TOLERANCE, - "Token Supply not changed as expected" - ); - assert.closeTo( - final_InvestorTokenBal.toNumber(), - init_InvestorTokenBal.add(investment_Token).toNumber(), - TOLERANCE, - "Investor Token Balance not changed as expected" - ); - assert.closeTo( - final_InvestorETHBal.toNumber(), - init_InvestorETHBal - .sub(gasCost) - .sub(investment_ETH) - .toNumber(), - TOLERANCE, - "Investor ETH Balance not changed as expected" - ); - assert.closeTo( - final_InvestorPOLYBal.toNumber(), - init_InvestorPOLYBal.toNumber(), - TOLERANCE, - "Investor POLY Balance not changed as expected" - ); - assert.closeTo( - final_STOTokenSold.toNumber(), - init_STOTokenSold.add(investment_Token).toNumber(), - TOLERANCE, - "STO Token Sold not changed as expected" - ); - assert.closeTo( - final_STOETHBal.toNumber(), - init_STOETHBal.toNumber(), - TOLERANCE, - "STO ETH Balance not changed as expected" - ); - assert.closeTo( - final_STOPOLYBal.toNumber(), - init_STOPOLYBal.toNumber(), - TOLERANCE, - "STO POLY Balance not changed as expected" - ); - assert.closeTo( - final_RaisedUSD.toNumber(), - init_RaisedUSD.add(investment_USD).toNumber(), - TOLERANCE, - "Raised USD not changed as expected" - ); - assert.closeTo( - final_RaisedETH.toNumber(), - init_RaisedETH.add(investment_ETH).toNumber(), - TOLERANCE, - "Raised ETH not changed as expected" - ); - assert.closeTo( - final_RaisedPOLY.toNumber(), - init_RaisedPOLY.toNumber(), - TOLERANCE, - "Raised POLY not changed as expected" - ); - assert.closeTo( - final_WalletETHBal.toNumber(), - init_WalletETHBal.add(investment_ETH).toNumber(), - TOLERANCE, - "Wallet ETH Balance not changed as expected" - ); - assert.closeTo( - final_WalletPOLYBal.toNumber(), - init_WalletPOLYBal.toNumber(), - TOLERANCE, - "Wallet POLY Balance not changed as expected" - ); + assertIsNear(final_TokenSupply, init_TokenSupply.add(investment_Token), "Token Supply not changed as expected" ); + assertIsNear(final_InvestorTokenBal, init_InvestorTokenBal.add(investment_Token), "Investor Token Balance not changed as expected" ); + assertIsNear(final_InvestorETHBal, init_InvestorETHBal .sub(gasCost) .sub(investment_ETH) , "Investor ETH Balance not changed as expected" ); + assertIsNear(final_InvestorPOLYBal, init_InvestorPOLYBal, "Investor POLY Balance not changed as expected" ); + assertIsNear(final_STOTokenSold, init_STOTokenSold.add(investment_Token), "STO Token Sold not changed as expected" ); + assertIsNear(final_STOETHBal, init_STOETHBal, "STO ETH Balance not changed as expected" ); + assertIsNear(final_STOPOLYBal, init_STOPOLYBal, "STO POLY Balance not changed as expected" ); + assertIsNear(final_RaisedUSD, init_RaisedUSD.add(investment_USD), "Raised USD not changed as expected" ); + assertIsNear(final_RaisedETH, init_RaisedETH.add(investment_ETH), "Raised ETH not changed as expected" ); + assertIsNear(final_RaisedPOLY, init_RaisedPOLY, "Raised POLY not changed as expected" ); + assertIsNear(final_WalletETHBal, init_WalletETHBal.add(investment_ETH), "Wallet ETH Balance not changed as expected" ); + assertIsNear(final_WalletPOLYBal, init_WalletPOLYBal, "Wallet POLY Balance not changed as expected" ); } } }); }); }); -function near(x, y, message) { - assert.isAtMost(x, y); +function assertIsNear(a, b, reason) { + a = new BN(a); + b = new BN(b); + if (a.gt(b)) { + assert.isBelow(a.sub(b).toNumber(), 4, reason); + } else { + assert.isBelow(b.sub(a).toNumber(), 4, reason); + } } diff --git a/test/r_concurrent_STO.js b/test/r_concurrent_STO.js index 7912658a4..c8bf6c781 100644 --- a/test/r_concurrent_STO.js +++ b/test/r_concurrent_STO.js @@ -2,13 +2,12 @@ import latestTime from "./helpers/latestTime"; import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; -import { +import { setUpPolymathNetwork, deployDummySTOAndVerifyed, deployCappedSTOAndVerifyed, deployPresaleSTOAndVerified - } from "./helpers/createInstances"; - +} from "./helpers/createInstances"; // Import contract ABIs const CappedSTO = artifacts.require("./CappedSTO.sol"); @@ -16,12 +15,13 @@ const DummySTO = artifacts.require("./DummySTO.sol"); const PreSaleSTO = artifacts.require("./PreSaleSTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("Concurrent STO", accounts => { +contract("Concurrent STO", async (accounts) => { // Accounts variable declaration let account_polymath; let account_issuer; @@ -45,6 +45,9 @@ contract("Concurrent STO", accounts => { let I_SecurityToken; let I_PolyToken; let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; // STO instance declaration let I_CappedSTOFactory; @@ -56,8 +59,9 @@ contract("Concurrent STO", accounts => { let message = "Transaction Should Fail!"; // Initial fees - const initRegFee = web3.utils.toWei("250"); - const STOSetupCost = 200 * Math.pow(10, 18); + const initRegFee = new BN(web3.utils.toWei("1000")); + const STOSetupCost = web3.utils.toHex(200 * Math.pow(10, 18)); + const STOSetupCostPOLY = web3.utils.toHex(800 * Math.pow(10, 18)); // Module keys const transferManagerKey = 2; @@ -69,8 +73,12 @@ contract("Concurrent STO", accounts => { const DummySTOParameters = ["uint256", "uint256", "uint256", "string"]; const PresaleSTOParameters = ["uint256"]; + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; account_fundsReceiver = accounts[2]; @@ -92,14 +100,16 @@ contract("Concurrent STO", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - + // STEP 2: Deploy the STO Factories - [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, STOSetupCost); - [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, STOSetupCost); - [I_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, STOSetupCost); + [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(account_polymath, I_MRProxied, STOSetupCost); + [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, STOSetupCost); + [I_PreSaleSTOFactory] = await deployPresaleSTOAndVerified(account_polymath, I_MRProxied, STOSetupCost); // Printing all the contract addresses console.log(` @@ -137,13 +147,13 @@ contract("Concurrent STO", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.getTokens(initRegFee, account_issuer); await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_issuer }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: account_issuer }); - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: account_issuer }); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); @@ -151,17 +161,17 @@ contract("Concurrent STO", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(transferManagerKey))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should whitelist account_investor1", async () => { - let fromTime = latestTime(); - let toTime = latestTime() + duration.days(15); + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(15); let expiryTime = toTime + duration.days(100); let canBuyFromSTO = true; - let tx = await I_GeneralTransferManager.modifyWhitelist(account_investor1, fromTime, toTime, expiryTime, canBuyFromSTO, { + let tx = await I_GeneralTransferManager.modifyKYCData(account_investor1, fromTime, toTime, expiryTime, { from: account_issuer, gas: 500000 }); @@ -173,13 +183,13 @@ contract("Concurrent STO", accounts => { describe("Add STO and verify transfer", async () => { it("Should attach STO modules up to the max number, then fail", async () => { const MAX_MODULES = 10; - const startTime = latestTime() + duration.days(1); - const endTime = latestTime() + duration.days(90); - const cap = web3.utils.toWei("10000"); - const rate = 1000; + const startTime = await latestTime() + duration.days(1); + const endTime = await latestTime() + duration.days(90); + const cap = new BN(web3.utils.toWei("10000")); + const rate = new BN(web3.utils.toWei("1000")); const fundRaiseType = [0]; const budget = 0; - const maxCost = STOSetupCost; + const maxCost = STOSetupCostPOLY; const cappedBytesSig = encodeModuleCall(CappedSTOParameters, [ startTime, endTime, @@ -192,8 +202,8 @@ contract("Concurrent STO", accounts => { const presaleBytesSig = encodeModuleCall(PresaleSTOParameters, [endTime]); for (var STOIndex = 0; STOIndex < MAX_MODULES; STOIndex++) { - await I_PolyToken.getTokens(STOSetupCost, account_issuer); - await I_PolyToken.transfer(I_SecurityToken.address, STOSetupCost, { from: account_issuer }); + await I_PolyToken.getTokens(STOSetupCostPOLY, account_issuer); + await I_PolyToken.transfer(I_SecurityToken.address, STOSetupCostPOLY, { from: account_issuer }); switch (STOIndex % 3) { case 0: // Capped STO @@ -206,7 +216,7 @@ contract("Concurrent STO", accounts => { "CappedSTO", `Wrong STO module added at index ${STOIndex}` ); - I_STO_Array.push(CappedSTO.at(tx1.logs[3].args._module)); + I_STO_Array.push(await CappedSTO.at(tx1.logs[3].args._module)); break; case 1: // Dummy STO @@ -219,7 +229,7 @@ contract("Concurrent STO", accounts => { "DummySTO", `Wrong STO module added at index ${STOIndex}` ); - I_STO_Array.push(DummySTO.at(tx2.logs[3].args._module)); + I_STO_Array.push(await DummySTO.at(tx2.logs[3].args._module)); break; case 2: // Pre Sale STO @@ -232,7 +242,7 @@ contract("Concurrent STO", accounts => { "PreSaleSTO", `Wrong STO module added at index ${STOIndex}` ); - I_STO_Array.push(PreSaleSTO.at(tx3.logs[3].args._module)); + I_STO_Array.push(await PreSaleSTO.at(tx3.logs[3].args._module)); break; } } @@ -247,30 +257,34 @@ contract("Concurrent STO", accounts => { // Capped STO ETH await I_STO_Array[STOIndex].buyTokens(account_investor1, { from: account_investor1, - value: web3.utils.toWei("1", "ether") + value: new BN(web3.utils.toWei("1", "ether")) }); assert.equal(web3.utils.fromWei((await I_STO_Array[STOIndex].getRaised.call(0)).toString()), 1); assert.equal(await I_STO_Array[STOIndex].investorCount.call(), 1); break; case 1: // Dummy STO - await I_STO_Array[STOIndex].generateTokens(account_investor1, web3.utils.toWei("1000"), { from: account_issuer }); + await I_STO_Array[STOIndex].generateTokens(account_investor1, new BN(web3.utils.toWei("1000")), { from: account_issuer }); assert.equal(await I_STO_Array[STOIndex].investorCount.call(), 1); assert.equal( - (await I_STO_Array[STOIndex].investors.call(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_STO_Array[STOIndex].investors.call(account_investor1)) + .div(new BN(10).pow(new BN(18))) + .toNumber(), 1000 ); break; case 2: // Pre Sale STO - await I_STO_Array[STOIndex].allocateTokens(account_investor1, web3.utils.toWei("1000"), web3.utils.toWei("1"), 0, { + await I_STO_Array[STOIndex].allocateTokens(account_investor1, new BN(web3.utils.toWei("1000")), new BN(web3.utils.toWei("1")), new BN(0), { from: account_issuer }); assert.equal(web3.utils.fromWei((await I_STO_Array[STOIndex].getRaised.call(0)).toString()), 1); assert.equal(web3.utils.fromWei((await I_STO_Array[STOIndex].getRaised.call(1)).toString()), 0); assert.equal(await I_STO_Array[STOIndex].investorCount.call(), 1); assert.equal( - (await I_STO_Array[STOIndex].investors.call(account_investor1)).dividedBy(new BigNumber(10).pow(18)).toNumber(), + (await I_STO_Array[STOIndex].investors.call(account_investor1)) + .div(new BN(10).pow(new BN(18))) + .toNumber(), 1000 ); break; diff --git a/test/s_v130_to_v140_upgrade.js b/test/s_v130_to_v140_upgrade.js deleted file mode 100644 index 014b44ac2..000000000 --- a/test/s_v130_to_v140_upgrade.js +++ /dev/null @@ -1,479 +0,0 @@ -const Web3 = require("web3"); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -const BigNumber = require("bignumber.js"); - -import latestTime from "./helpers/latestTime"; -import { duration } from "./helpers/utils"; -import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; -import { setUpPolymathNetwork, deployCappedSTOAndVerifyed, deployGPMAndVerifyed } from "./helpers/createInstances"; - -const USDTieredSTOProxyFactory = artifacts.require("./USDTieredSTOProxyFactory.sol"); -const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); -const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); -const USDTieredSTO = artifacts.require("./USDTieredSTO.sol"); -const CappedSTO = artifacts.require("./CappedSTO.sol"); -const PolyOracle = artifacts.require("./PolyOracle.sol"); -const ETHOracle = artifacts.require("./MakerDAOOracle.sol"); -const SecurityToken = artifacts.require("./SecurityToken.sol"); -const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); -const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApprovalTransferManagerFactory.sol"); - -contract("Upgrade from v1.3.0 to v1.4.0", accounts => { - // Accounts Variable declaration - let POLYMATH; - let ISSUER1; - let ISSUER2; - let ISSUER3; - let MULTISIG; - - //const GAS_PRICE = 10000000000; // 10 GWEI - - let tx; - - // Initial fee for ticker registry and security token registry - const REGFEE = web3.utils.toWei("250"); - const STOSetupCost = 0; - const address_zero = "0x0000000000000000000000000000000000000000"; - - // Module key - const STOKEY = 3; - const TMKEY = 2; - - // SecurityToken 1 Details - const symbol1 = "TOK1"; - const name1 = "TOK1 Token"; - const tokenDetails1 = "This is equity type of issuance"; - - //SecurityToken 2 Details - const symbol2 = "TOK2"; - const name2 = "TOK2 Token"; - const tokenDetails2 = "This is equity type of issuance"; - - //SecurityToken 3 Details - const symbol3 = "TOK3"; - const name3 = "TOK3 Token"; - const tokenDetails3 = "This is equity type of issuance"; - - // Contract Instance Declaration - let I_PolymathRegistry; - let I_PolyToken; - let I_DaiToken; - let I_ModuleRegistry; - let I_ModuleRegistryProxy; - let I_GeneralTransferManagerFactory; - let I_GeneralPermissionManagerFactory; - let I_SecurityTokenRegistryProxy; - let I_FeatureRegistry; - let I_STFactory; - let I_MRProxied; - let I_STRProxied; - let I_STRProxiedNew; - - let I_SecurityTokenRegistry; - //let I_UpgradedSecurityTokenRegistry - - let I_SecurityToken1; - let I_SecurityToken2; - //let I_SecurityToken3; - - let I_USDTieredSTOFactory; - let I_USDTieredSTOProxyFactory; - let I_USDOracle; - let I_POLYOracle; - let I_USDTieredSTO; - - let I_CappedSTOFactory; - let I_UpgradedCappedSTOFactory; - let I_CappedSTO; - let I_ManualApprovalTransferManagerFactory; - - const STOParameters = ["uint256", "uint256", "uint256", "uint256", "uint8[]", "address"]; - // Prepare polymath network status - before(async () => { - // Accounts setup - POLYMATH = accounts[0]; - ISSUER1 = accounts[1]; - ISSUER2 = accounts[2]; - ISSUER3 = accounts[3]; - MULTISIG = accounts[4]; - - I_DaiToken = await PolyTokenFaucet.new({ from: POLYMATH }); - - // ----------- POLYMATH NETWORK Configuration ------------ - - let instances = await setUpPolymathNetwork(POLYMATH, ISSUER1); - - [ - I_PolymathRegistry, - I_PolyToken, - I_FeatureRegistry, - I_ModuleRegistry, - I_ModuleRegistryProxy, - I_MRProxied, - I_GeneralTransferManagerFactory, - I_STFactory, - I_SecurityTokenRegistry, - I_SecurityTokenRegistryProxy, - I_STRProxied - ] = instances; - - // STEP 4: Deploy the GeneralDelegateManagerFactory - [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(POLYMATH, I_MRProxied, I_PolyToken.address, 0); - - // STEP 5: Deploy the CappedSTOFactory - [I_CappedSTOFactory] = await deployCappedSTOAndVerifyed(POLYMATH, I_MRProxied, I_PolyToken.address, STOSetupCost); - - // Step 12: Mint tokens to ISSUERs - await I_PolyToken.getTokens(REGFEE * 2, ISSUER1); - await I_PolyToken.getTokens(REGFEE * 2, ISSUER2); - await I_PolyToken.getTokens(REGFEE * 2, ISSUER3); - - // Step 13: Register tokens - // (A) : TOK1 - await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER1 }); - tx = await I_STRProxied.registerTicker(ISSUER1, symbol1, name1, { from: ISSUER1 }); - assert.equal(tx.logs[0].args._owner, ISSUER1); - assert.equal(tx.logs[0].args._ticker, symbol1); - - // (B) : TOK2 - await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER2 }); - tx = await I_STRProxied.registerTicker(ISSUER2, symbol2, name2, { from: ISSUER2 }); - assert.equal(tx.logs[0].args._owner, ISSUER2); - assert.equal(tx.logs[0].args._ticker, symbol2); - - // (C) : TOK3 - await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER3 }); - tx = await I_STRProxied.registerTicker(ISSUER3, symbol3, name3, { from: ISSUER3 }); - assert.equal(tx.logs[0].args._owner, ISSUER3); - assert.equal(tx.logs[0].args._ticker, symbol3); - - // Step 14: Deploy tokens - // (A) : TOK1 - await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER1 }); - let tx = await I_STRProxied.generateSecurityToken(name1, symbol1, tokenDetails1, false, { from: ISSUER1 }); - assert.equal(tx.logs[1].args._ticker, symbol1, "SecurityToken doesn't get deployed"); - I_SecurityToken1 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - // (B) : TOK2 - await I_PolyToken.approve(I_STRProxied.address, REGFEE, { from: ISSUER2 }); - tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails2, false, { from: ISSUER2 }); - assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed"); - I_SecurityToken2 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - // Printing all the contract addresses - console.log(` - --------------------- Polymath Network Smart Contracts: --------------------- - PolymathRegistry: ${I_PolymathRegistry.address} - SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} - SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} - ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} - ModuleRegistry: ${I_ModuleRegistry.address} - FeatureRegistry: ${I_FeatureRegistry.address} - - STFactory: ${I_STFactory.address} - GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} - GeneralPermissionManagerFactory: ${I_GeneralPermissionManagerFactory.address} - - SecurityToken TOK1: ${I_SecurityToken1.address} - SecurityToken TOK2: ${I_SecurityToken2.address} - ----------------------------------------------------------------------------- - `); - }); - - describe("USDTieredSTOFactory deploy", async () => { - // Step 1: Deploy Oracles - // 1a - Deploy POLY Oracle - it("Should successfully deploy POLY Oracle and register on PolymathRegistry", async () => { - I_POLYOracle = await PolyOracle.new({ from: POLYMATH, value: web3.utils.toWei("1") }); - console.log(I_POLYOracle.address); - assert.notEqual( - I_POLYOracle.address.valueOf(), - address_zero, - "POLYOracle contract was not deployed" - ); - tx = await I_PolymathRegistry.changeAddress("PolyUsdOracle", I_POLYOracle.address, { from: POLYMATH }); - assert.equal(tx.logs[0].args._nameKey, "PolyUsdOracle"); - assert.equal(tx.logs[0].args._newAddress, I_POLYOracle.address); - }); - // 1b - Deploy ETH Oracle - it("Should successfully deploy ETH Oracle and register on PolymathRegistry", async () => { - I_USDOracle = await ETHOracle.new( - "0x216d678c14be600cb88338e763bb57755ca2b1cf", - address_zero, - "ETH", - { from: POLYMATH } - ); - assert.notEqual( - I_USDOracle.address.valueOf(), - address_zero, - "USDOracle contract was not deployed" - ); - tx = await I_PolymathRegistry.changeAddress("EthUsdOracle", I_USDOracle.address, { from: POLYMATH }); - assert.equal(tx.logs[0].args._nameKey, "EthUsdOracle"); - assert.equal(tx.logs[0].args._newAddress, I_USDOracle.address); - }); - }); - - describe("USDTieredSTOFactory deploy", async () => { - // Step 1: Deploy USDTieredSTOFactory\ - it("Should successfully deploy USDTieredSTOFactory", async () => { - I_USDTieredSTOProxyFactory = await USDTieredSTOProxyFactory.new(); - I_USDTieredSTOFactory = await USDTieredSTOFactory.new( - I_PolyToken.address, - STOSetupCost, - 0, - 0, - I_USDTieredSTOProxyFactory.address, - { from: POLYMATH } - ); - assert.notEqual( - I_USDTieredSTOFactory.address.valueOf(), - address_zero, - "USDTieredSTOFactory contract was not deployed" - ); - let setupCost = await I_USDTieredSTOFactory.setupCost({ from: POLYMATH }); - assert.equal(setupCost, STOSetupCost); - }); - // Step 2: Register and verify - it("Should successfully register and verify USDTieredSTOFactory contract", async () => { - let tx = await I_MRProxied.registerModule(I_USDTieredSTOFactory.address, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_USDTieredSTOFactory.address); - tx = await I_MRProxied.verifyModule(I_USDTieredSTOFactory.address, true, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_USDTieredSTOFactory.address); - assert.isTrue(tx.logs[0].args._verified); - }); - }); - - describe("CappedSTOFactory deploy", async () => { - // Step 1: Deploy new CappedSTOFactory - it("Should successfully deploy CappedSTOFactory", async () => { - I_UpgradedCappedSTOFactory = await CappedSTOFactory.new(I_PolyToken.address, STOSetupCost, 0, 0, { from: POLYMATH }); - assert.notEqual( - I_UpgradedCappedSTOFactory.address.valueOf(), - address_zero, - "CappedSTOFactory contract was not deployed" - ); - let setupCost = await I_UpgradedCappedSTOFactory.setupCost({ from: POLYMATH }); - assert.equal(setupCost, STOSetupCost); - }); - - // Step 2: Register and verify - it("Should successfully register and verify new CappedSTOFactory contract", async () => { - let tx = await I_MRProxied.registerModule(I_UpgradedCappedSTOFactory.address, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_UpgradedCappedSTOFactory.address); - tx = await I_MRProxied.verifyModule(I_UpgradedCappedSTOFactory.address, true, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_UpgradedCappedSTOFactory.address); - assert.isTrue(tx.logs[0].args._verified); - }); - - // Step 3: Unverify old CappedSTOFactory - it("Should successfully unverify old CappedSTOFactory contract", async () => { - let tx = await I_MRProxied.verifyModule(I_CappedSTOFactory.address, false, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_CappedSTOFactory.address); - assert.isFalse(tx.logs[0].args._verified); - }); - }); - - describe("ManualApprovalTransferManagerFactory deploy", async () => { - // Step 1: Deploy new ManualApprovalTransferManager - it("Should successfully deploy ManualApprovalTransferManagerFactory", async () => { - I_ManualApprovalTransferManagerFactory = await ManualApprovalTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { - from: POLYMATH - }); - assert.notEqual( - I_ManualApprovalTransferManagerFactory.address.valueOf(), - address_zero, - "ManualApprovalTransferManagerFactory contract was not deployed" - ); - }); - - // Step 2: Register and verify - it("Should successfully register and verify new ManualApprovalTransferManagerFactory contract", async () => { - let tx = await I_MRProxied.registerModule(I_ManualApprovalTransferManagerFactory.address, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_ManualApprovalTransferManagerFactory.address); - tx = await I_MRProxied.verifyModule(I_ManualApprovalTransferManagerFactory.address, true, { from: POLYMATH }); - assert.equal(tx.logs[0].args._moduleFactory, I_ManualApprovalTransferManagerFactory.address); - assert.isTrue(tx.logs[0].args._verified); - }); - }); - - describe("Change ownerships", async () => { - /* - // Step 1: SecurityTokenRegistry - it("Should successfully change ownership of new SecurityTokenRegistry contract", async() => { - let tx = await I_STRProxiedNew.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal(tx.logs[0].args.previousOwner, POLYMATH, "Previous owner was not Polymath account"); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New owner is not Multisig account"); - }); - */ - - // Step 2: Oracles - it("Should successfully change ownership of both Oracles contract", async () => { - let tx = await I_USDOracle.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal(tx.logs[0].args.previousOwner, POLYMATH, "Previous ETH Oracle owner was not Polymath account"); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New ETH Oracle owner is not Multisig account"); - - tx = await I_POLYOracle.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal(tx.logs[0].args.previousOwner, POLYMATH, "Previous POLY Oracle owner was not Polymath account"); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New POLY Oracle owner is not Multisig account"); - }); - - // Step 3: USDTieredSTOFactory - it("Should successfully change ownership of USDTieredSTOFactory contract", async () => { - let tx = await I_USDTieredSTOFactory.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal(tx.logs[0].args.previousOwner, POLYMATH, "Previous USDTieredSTOFactory owner was not Polymath account"); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New USDTieredSTOFactory owner is not Multisig account"); - }); - - // Step 3: CappedSTOFactory - it("Should successfully change ownership of CappedSTOFactory contract", async () => { - let tx = await I_UpgradedCappedSTOFactory.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal(tx.logs[0].args.previousOwner, POLYMATH, "Previous USDTieredSTOFactory owner was not Polymath account"); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New USDTieredSTOFactory owner is not Multisig account"); - }); - - // Step 4: ManualApprovalTransferManagerFactory - it("Should successfully change ownership of ManualApprovalTransferManagerFactory contract", async () => { - let tx = await I_ManualApprovalTransferManagerFactory.transferOwnership(MULTISIG, { from: POLYMATH }); - assert.equal( - tx.logs[0].args.previousOwner, - POLYMATH, - "Previous ManualApprovalTransferManagerFactory owner was not Polymath account" - ); - assert.equal(tx.logs[0].args.newOwner, MULTISIG, "New ManualApprovalTransferManagerFactory owner is not Multisig account"); - }); - }); - - describe("Polymath network status post migration", async () => { - // Launch STO for TOK1 - it("Should successfully launch USDTieredSTO for first security token", async () => { - let _startTime = latestTime() + duration.days(1); - let _endTime = _startTime + duration.days(180); - let _ratePerTier = [BigNumber(0.1).mul(10 ** 18), BigNumber(0.15).mul(10 ** 18), BigNumber(0.2).mul(10 ** 18)]; - let _ratePerTierDiscountPoly = [BigNumber(0), BigNumber(0), BigNumber(0)]; - let _tokensPerTierTotal = [BigNumber(100).mul(10 ** 18), BigNumber(200).mul(10 ** 18), BigNumber(300).mul(10 ** 18)]; - let _tokensPerTierDiscountPoly = [BigNumber(0), BigNumber(0), BigNumber(0)]; - let _nonAccreditedLimitUSD = new BigNumber(100).mul(10 ** 18); - let _minimumInvestmentUSD = new BigNumber(5).mul(10 ** 18); - let _fundRaiseTypes = [0, 1]; - let _wallet = ISSUER1; - let _reserveWallet = ISSUER1; - let _usdToken = I_DaiToken.address; - - let config = [ - _startTime, - _endTime, - _ratePerTier, - _ratePerTierDiscountPoly, - _tokensPerTierTotal, - _tokensPerTierDiscountPoly, - _nonAccreditedLimitUSD, - _minimumInvestmentUSD, - _fundRaiseTypes, - _wallet, - _reserveWallet, - _usdToken - ]; - - let functionSignature = { - name: "configure", - type: "function", - inputs: [ - { - type: "uint256", - name: "_startTime" - }, - { - type: "uint256", - name: "_endTime" - }, - { - type: "uint256[]", - name: "_ratePerTier" - }, - { - type: "uint256[]", - name: "_ratePerTierDiscountPoly" - }, - { - type: "uint256[]", - name: "_tokensPerTier" - }, - { - type: "uint256[]", - name: "_tokensPerTierDiscountPoly" - }, - { - type: "uint256", - name: "_nonAccreditedLimitUSD" - }, - { - type: "uint256", - name: "_minimumInvestmentUSD" - }, - { - type: "uint8[]", - name: "_fundRaiseTypes" - }, - { - type: "address", - name: "_wallet" - }, - { - type: "address", - name: "_reserveWallet" - }, - { - type: "address", - name: "_usdToken" - } - ] - }; - - let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); - - let tx = await I_SecurityToken1.addModule(I_USDTieredSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER1 }); - assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); - assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); - I_USDTieredSTO = USDTieredSTO.at(tx.logs[2].args._module); - }); - - /* - // Deploy TOK3 - it("Should successfully deploy third security token", async() => { - await I_PolyToken.approve(I_STRProxiedNew.address, REGFEE, { from: ISSUER3}); - tx = await I_STRProxiedNew.generateSecurityToken(name3, symbol3, tokenDetails3, false, { from: ISSUER3 }); - assert.equal(tx.logs[1].args._ticker, symbol3, "SecurityToken doesn't get deployed"); - I_SecurityToken3 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - }); - */ - - // Launch NewCappedSTO for TOK2 - it("Should successfully launch CappedSTO for third security token", async () => { - let startTime = latestTime() + duration.days(1); - let endTime = startTime + duration.days(30); - let cap = web3.utils.toWei("500000"); - let rate = 1000; - let fundRaiseType = 0; - let fundsReceiver = ISSUER3; - - let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, [fundRaiseType], fundsReceiver]); - - let tx = await I_SecurityToken2.addModule(I_UpgradedCappedSTOFactory.address, bytesSTO, 0, 0, { from: ISSUER2 }); - assert.equal(tx.logs[2].args._types[0], STOKEY, "CappedSTO doesn't get deployed"); - assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "CappedSTO", "CappedSTOFactory module was not added"); - }); - - // Attach ManualApprovalTransferManager module for TOK2 - it("Should successfully attach the ManualApprovalTransferManagerFactory with the second token", async () => { - const tx = await I_SecurityToken2.addModule(I_ManualApprovalTransferManagerFactory.address, "", 0, 0, { from: ISSUER2 }); - assert.equal(tx.logs[2].args._types[0].toNumber(), TMKEY, "ManualApprovalTransferManagerFactory doesn't get deployed"); - assert.equal( - web3.utils.toUtf8(tx.logs[2].args._name), - "ManualApprovalTransferManager", - "ManualApprovalTransferManagerFactory module was not added" - ); - I_ManualApprovalTransferManagerFactory = ManualApprovalTransferManagerFactory.at(tx.logs[2].args._module); - }); - }); -}); diff --git a/test/t_security_token_registry_proxy.js b/test/t_security_token_registry_proxy.js index 40b1cced8..bae86e140 100644 --- a/test/t_security_token_registry_proxy.js +++ b/test/t_security_token_registry_proxy.js @@ -9,12 +9,14 @@ const SecurityTokenRegistryMock = artifacts.require("./SecurityTokenRegistryMock const OwnedUpgradeabilityProxy = artifacts.require("./OwnedUpgradeabilityProxy.sol"); const STFactory = artifacts.require("./STFactory.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); +const STRGetter = artifacts.require("./STRGetter.sol"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("SecurityTokenRegistryProxy", accounts => { +contract("SecurityTokenRegistryProxy", async (accounts) => { let I_SecurityTokenRegistry; let I_SecurityTokenRegistryProxy; let I_GeneralTransferManagerFactory; @@ -28,6 +30,10 @@ contract("SecurityTokenRegistryProxy", accounts => { let I_SecurityToken; let I_ModuleRegistry; let I_FeatureRegistry; + let I_STRGetter; + let I_Getter; + let I_STGetter; + let stGetter; let account_polymath; let account_temp; @@ -35,7 +41,8 @@ contract("SecurityTokenRegistryProxy", accounts => { let account_polymath_new; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("250")); + const initRegFeePOLY = new BN(web3.utils.toWei("1000")); const version = "1.0.0"; const message = "Transaction Should Fail!"; @@ -46,6 +53,9 @@ contract("SecurityTokenRegistryProxy", accounts => { const decimals = 18; const transferManagerKey = 2; + + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; const STRProxyParameters = ["address", "address", "uint256", "uint256", "address", "address"]; async function readStorage(contractAddress, slot) { @@ -72,14 +82,16 @@ contract("SecurityTokenRegistryProxy", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ from: account_polymath }); await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, { from: account_polymath }); await I_MRProxied.updateFromRegistry({ from: account_polymath }); - + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -100,19 +112,20 @@ contract("SecurityTokenRegistryProxy", accounts => { // __upgradeabilityOwner -- index 13 it("Should attach the implementation and version", async () => { + I_STRGetter = await STRGetter.new({from: account_polymath}); let bytesProxy = encodeProxyCall(STRProxyParameters, [ I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, - I_PolyToken.address, - account_polymath + account_polymath, + I_STRGetter.address ]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }); - let c = OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); - assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistry.address); + let c = await OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); + assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistry.address.toLowerCase()); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -121,16 +134,17 @@ contract("SecurityTokenRegistryProxy", accounts => { "1.0.0" ); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + I_STRGetter = await STRGetter.at(I_SecurityTokenRegistryProxy.address); }); it("Verify the initialize data", async () => { assert.equal( - (await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("expiryLimit"))).toNumber(), + (await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("expiryLimit"))).toNumber(), 60 * 24 * 60 * 60, "Should equal to 60 days" ); assert.equal( - (await I_STRProxied.getUintValues.call(web3.utils.soliditySha3("tickerRegFee"))).toNumber(), + await I_STRProxied.getUintValue.call(web3.utils.soliditySha3("tickerRegFee")), web3.utils.toWei("250") ); }); @@ -138,24 +152,24 @@ contract("SecurityTokenRegistryProxy", accounts => { describe("Feed some data in storage", async () => { it("Register the ticker", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("1000"), token_owner); - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("8000")), token_owner); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); let tx = await I_STRProxied.registerTicker(token_owner, symbol, name, { from: token_owner }); assert.equal(tx.logs[0].args._owner, token_owner, "Owner should be the same as registered with the ticker"); assert.equal(tx.logs[0].args._ticker, symbol, "Same as the symbol registered in the registerTicker function call"); }); it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + await I_PolyToken.approve(I_STRProxied.address, initRegFeePOLY, { from: token_owner }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), transferManagerKey); @@ -175,7 +189,7 @@ contract("SecurityTokenRegistryProxy", accounts => { it("Should upgrade the version and implementation address -- Implemenation address should not be 0x", async () => { await catchRevert( - I_SecurityTokenRegistryProxy.upgradeTo("1.1.0", "0x00000000000000000000000000000000000000", { from: account_polymath }) + I_SecurityTokenRegistryProxy.upgradeTo("1.1.0", address_zero, { from: account_polymath }) ); }); @@ -195,7 +209,7 @@ contract("SecurityTokenRegistryProxy", accounts => { it("Should upgrade the version and the implementation address successfully", async () => { await I_SecurityTokenRegistryProxy.upgradeTo("1.1.0", I_SecurityTokenRegistryMock.address, { from: account_polymath }); - let c = OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); + let c = await OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -204,15 +218,17 @@ contract("SecurityTokenRegistryProxy", accounts => { "1.1.0", "Version mis-match" ); - assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistryMock.address, "Implemnted address is not matched"); + assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistryMock.address.toLowerCase(), "Implemnted address is not matched"); I_STRProxied = await SecurityTokenRegistryMock.at(I_SecurityTokenRegistryProxy.address); + I_Getter = await STRGetter.at(I_SecurityTokenRegistryProxy.address); }); }); describe("Execute functionality of the implementation contract on the earlier storage", async () => { it("Should get the previous data", async () => { - let _tokenAddress = await I_STRProxied.getSecurityTokenAddress.call(symbol); - let _data = await I_STRProxied.getSecurityTokenData.call(_tokenAddress); + + let _tokenAddress = await I_Getter.getSecurityTokenAddress.call(symbol); + let _data = await I_Getter.getSecurityTokenData.call(_tokenAddress); assert.equal(_data[0], symbol, "Symbol should match with registered symbol"); assert.equal(_data[1], token_owner, "Owner should be the deployer of token"); assert.equal(_data[2], tokenDetails, "Token details should matched with deployed ticker"); @@ -220,7 +236,7 @@ contract("SecurityTokenRegistryProxy", accounts => { it("Should alter the old storage", async () => { await I_STRProxied.changeTheDeployedAddress(symbol, account_temp, { from: account_polymath }); - let _tokenAddress = await I_STRProxied.getSecurityTokenAddress.call(symbol); + let _tokenAddress = await I_Getter.getSecurityTokenAddress.call(symbol); assert.equal(_tokenAddress, account_temp, "Should match with the changed address"); }); }); @@ -232,7 +248,7 @@ contract("SecurityTokenRegistryProxy", accounts => { it("Should change the ownership of the contract -- new address should not be 0x", async () => { await catchRevert( - I_SecurityTokenRegistryProxy.transferProxyOwnership("0x00000000000000000000000000000000000000", { from: account_polymath }) + I_SecurityTokenRegistryProxy.transferProxyOwnership(address_zero, { from: account_polymath }) ); }); @@ -245,7 +261,7 @@ contract("SecurityTokenRegistryProxy", accounts => { it("Should change the implementation contract and version by the new owner", async () => { I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ from: account_polymath }); await I_SecurityTokenRegistryProxy.upgradeTo("1.2.0", I_SecurityTokenRegistry.address, { from: account_polymath_new }); - let c = OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); + let c = await OwnedUpgradeabilityProxy.at(I_SecurityTokenRegistryProxy.address); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -254,16 +270,19 @@ contract("SecurityTokenRegistryProxy", accounts => { "1.2.0", "Version mis-match" ); - assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistry.address, "Implemnted address is not matched"); + assert.equal(await readStorage(c.address, 12), I_SecurityTokenRegistry.address.toLowerCase(), "Implemnted address is not matched"); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); }); - it("Should get the version", async() => { + it("Should get the version", async () => { assert.equal(await I_SecurityTokenRegistryProxy.version.call({ from: account_polymath_new }), "1.2.0"); }); - it("Should get the implementation address", async() => { - assert.equal(await I_SecurityTokenRegistryProxy.implementation.call({ from: account_polymath_new }), I_SecurityTokenRegistry.address); - }) + it("Should get the implementation address", async () => { + assert.equal( + await I_SecurityTokenRegistryProxy.implementation.call({ from: account_polymath_new }), + I_SecurityTokenRegistry.address + ); + }); }); }); diff --git a/test/u_module_registry_proxy.js b/test/u_module_registry_proxy.js index 6d00b8fe2..1dc2b8f18 100644 --- a/test/u_module_registry_proxy.js +++ b/test/u_module_registry_proxy.js @@ -9,18 +9,24 @@ const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); const STFactory = artifacts.require("./STFactory.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManagerLogic = artifacts.require("./GeneralTransferManager.sol"); const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager.sol"); +const STGetter = artifacts.require("./STGetter.sol"); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("ModuleRegistryProxy", accounts => { +contract("ModuleRegistryProxy", async (accounts) => { let I_SecurityTokenRegistry; let I_SecurityTokenRegistryProxy; let I_GeneralTransferManagerFactory; let I_GeneralPermissionManagerfactory; + let I_GeneralPermissionManagerLogic; let I_MockModuleRegistry; let I_STFactory; let I_PolymathRegistry; @@ -31,6 +37,9 @@ contract("ModuleRegistryProxy", accounts => { let I_SecurityToken; let I_ModuleRegistry; let I_FeatureRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; let account_polymath; let account_temp; @@ -38,10 +47,10 @@ contract("ModuleRegistryProxy", accounts => { let account_polymath_new; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); const version = "1.0.0"; const message = "Transaction Should Fail!"; const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; // SecurityToken Details for funds raise Type ETH const name = "Team"; const symbol = "SAP"; @@ -75,7 +84,9 @@ contract("ModuleRegistryProxy", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from: account_polymath}); @@ -106,8 +117,8 @@ contract("ModuleRegistryProxy", accounts => { it("Should attach the MR implementation and version", async () => { let bytesProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesProxy, { from: account_polymath }); - let c = OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); - assert.equal(await readStorage(c.address, 12), I_ModuleRegistry.address); + let c = await OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); + assert.equal(await readStorage(c.address, 12), I_ModuleRegistry.address.toLowerCase()); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -122,7 +133,13 @@ contract("ModuleRegistryProxy", accounts => { await I_MRProxied.updateFromRegistry({ from: account_polymath }); // STEP 4: Deploy the GeneralTransferManagerFactory - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + let I_GeneralTransferManagerLogic = await GeneralTransferManagerLogic.new( + address_zero, + address_zero, + { from: account_polymath } + ); + + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(new BN(0), new BN(0), I_GeneralTransferManagerLogic.address, I_PolymathRegistry.address, { from: account_polymath }); @@ -139,28 +156,28 @@ contract("ModuleRegistryProxy", accounts => { await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); // Step 3: Deploy the STFactory contract - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + I_STGetter = await STGetter.new(); + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, I_STGetter.address, { from: account_polymath }); - assert.notEqual( - I_STFactory.address.valueOf(), - address_zero, - "STFactory contract was not deployed" - ); + assert.notEqual(I_STFactory.address.valueOf(), address_zero, "STFactory contract was not deployed"); }); it("Verify the initialize data", async () => { assert.equal( - await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("owner")), + await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("owner")), account_polymath, "Should equal to right address" ); - assert.equal(await I_MRProxied.getAddressValues.call(web3.utils.soliditySha3("polymathRegistry")), I_PolymathRegistry.address); + assert.equal(await I_MRProxied.getAddressValue.call(web3.utils.soliditySha3("polymathRegistry")), I_PolymathRegistry.address); }); }); describe("Feed some data in storage", async () => { it("Register and verify the new module", async () => { - I_GeneralPermissionManagerfactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + I_GeneralPermissionManagerLogic = await GeneralPermissionManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: account_polymath }); + I_GeneralPermissionManagerfactory = await GeneralPermissionManagerFactory.new(new BN(0), new BN(0), I_GeneralPermissionManagerLogic.address, I_PolymathRegistry.address, { from: account_polymath }); @@ -187,7 +204,7 @@ contract("ModuleRegistryProxy", accounts => { it("Should upgrade the version and implementation address -- Implemenation address should not be 0x", async () => { await catchRevert( - I_ModuleRegistryProxy.upgradeTo("1.1.0", "0x00000000000000000000000000000000000000", { from: account_polymath }) + I_ModuleRegistryProxy.upgradeTo("1.1.0", address_zero, { from: account_polymath }) ); }); @@ -205,7 +222,7 @@ contract("ModuleRegistryProxy", accounts => { it("Should upgrade the version and the implementation address successfully", async () => { await I_ModuleRegistryProxy.upgradeTo("1.1.0", I_MockModuleRegistry.address, { from: account_polymath }); - let c = OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); + let c = await OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -214,7 +231,7 @@ contract("ModuleRegistryProxy", accounts => { "1.1.0", "Version mis-match" ); - assert.equal(await readStorage(c.address, 12), I_MockModuleRegistry.address, "Implemnted address is not matched"); + assert.equal(await readStorage(c.address, 12), I_MockModuleRegistry.address.toLowerCase(), "Implemnted address is not matched"); I_MRProxied = await MockModuleRegistry.at(I_ModuleRegistryProxy.address); }); }); @@ -222,7 +239,7 @@ contract("ModuleRegistryProxy", accounts => { describe("Execute functionality of the implementation contract on the earlier storage", async () => { it("Should get the previous data", async () => { let _data = await I_MRProxied.getReputationByFactory.call(I_GeneralTransferManagerFactory.address); - assert.equal(_data.length, 0, "Should give the original length"); + assert.equal(_data.length, new BN(0), "Should give the original length"); }); it("Should alter the old storage", async () => { @@ -241,7 +258,7 @@ contract("ModuleRegistryProxy", accounts => { it("Should change the ownership of the contract -- new address should not be 0x", async () => { await catchRevert( - I_ModuleRegistryProxy.transferProxyOwnership("0x00000000000000000000000000000000000000", { from: account_polymath }) + I_ModuleRegistryProxy.transferProxyOwnership(address_zero, { from: account_polymath }) ); }); @@ -254,7 +271,7 @@ contract("ModuleRegistryProxy", accounts => { it("Should change the implementation contract and version by the new owner", async () => { I_ModuleRegistry = await ModuleRegistry.new({ from: account_polymath }); await I_ModuleRegistryProxy.upgradeTo("1.2.0", I_ModuleRegistry.address, { from: account_polymath_new }); - let c = OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); + let c = await OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); assert.equal( web3.utils .toAscii(await readStorage(c.address, 11)) @@ -263,7 +280,7 @@ contract("ModuleRegistryProxy", accounts => { "1.2.0", "Version mis-match" ); - assert.equal(await readStorage(c.address, 12), I_ModuleRegistry.address, "Implemnted address is not matched"); + assert.equal(await readStorage(c.address, 12), I_ModuleRegistry.address.toLowerCase(), "Implemnted address is not matched"); I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); }); }); diff --git a/test/v_tracked_redemptions.js b/test/v_tracked_redemptions.js index 159564651..da8a29531 100644 --- a/test/v_tracked_redemptions.js +++ b/test/v_tracked_redemptions.js @@ -1,20 +1,20 @@ import latestTime from "./helpers/latestTime"; -import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; -import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; -import { encodeProxyCall } from "./helpers/encodeCall"; -import { catchRevert } from "./helpers/exceptions"; -import { setUpPolymathNetwork, deployRedemptionAndVerifyed } from "./helpers/createInstances"; +import {duration, promisifyLogWatch} from "./helpers/utils"; +import takeSnapshot, {increaseTime, revertToSnapshot} from "./helpers/time"; +import {catchRevert} from "./helpers/exceptions"; +import {deployRedemptionAndVerifyed, setUpPolymathNetwork} from "./helpers/createInstances"; const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const TrackedRedemption = artifacts.require("./TrackedRedemption"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const STGetter = artifacts.require("./STGetter.sol"); const Web3 = require("web3"); -const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port -contract("TrackedRedemption", accounts => { +contract("TrackedRedemption", async (accounts) => { // Accounts Variable declaration let account_polymath; let account_issuer; @@ -25,11 +25,6 @@ contract("TrackedRedemption", accounts => { let account_investor4; let account_temp; - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - let message = "Transaction Should Fail!"; // Contract Instance Declaration @@ -52,6 +47,9 @@ contract("TrackedRedemption", accounts => { let I_MRProxied; let I_PolymathRegistry; let P_TrackedRedemptionFactory; + let I_STRGetter; + let I_STGetter; + let stGetter; // SecurityToken Details const name = "Team"; @@ -68,10 +66,14 @@ contract("TrackedRedemption", accounts => { const burnKey = 5; // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; before(async () => { - // Accounts setup + currentTime = new BN(await latestTime()); account_polymath = accounts[0]; account_issuer = accounts[1]; @@ -99,13 +101,15 @@ contract("TrackedRedemption", accounts => { I_STFactory, I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, - I_STRProxied + I_STRProxied, + I_STRGetter, + I_STGetter ] = instances; - + // STEP 4: Deploy the TrackedRedemption - [I_TrackedRedemptionFactory] = await deployRedemptionAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [P_TrackedRedemptionFactory] = await deployRedemptionAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + [I_TrackedRedemptionFactory] = await deployRedemptionAndVerifyed(account_polymath, I_MRProxied, 0); + [P_TrackedRedemptionFactory] = await deployRedemptionAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); // Printing all the contract addresses console.log(` @@ -135,15 +139,15 @@ contract("TrackedRedemption", accounts => { it("Should generate the new security token with the same symbol as registered above", async () => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._types[0].toNumber(), 2); @@ -151,33 +155,35 @@ contract("TrackedRedemption", accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); }); it("Should successfully attach the paid TrackedRedemption with the security token", async () => { let snapId = await takeSnapshot(); - await I_PolyToken.getTokens(web3.utils.toWei("500"), I_SecurityToken.address); - const tx = await I_SecurityToken.addModule(P_TrackedRedemptionFactory.address, "", web3.utils.toWei("500"), 0, { from: token_owner }); + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000")), I_SecurityToken.address); + const tx = await I_SecurityToken.addModule(P_TrackedRedemptionFactory.address, "0x0", new BN(web3.utils.toWei("2000")), new BN(0), { + from: token_owner + }); assert.equal(tx.logs[3].args._types[0].toNumber(), burnKey, "TrackedRedemption doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[3].args._name).replace(/\u0000/g, ""), "TrackedRedemption", "TrackedRedemption module was not added" ); - I_TrackedRedemption = TrackedRedemption.at(tx.logs[3].args._module); + I_TrackedRedemption = await TrackedRedemption.at(tx.logs[3].args._module); await revertToSnapshot(snapId); }); it("Should successfully attach the TrackedRedemption with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_TrackedRedemptionFactory.address, "", 0, 0, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_TrackedRedemptionFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), burnKey, "TrackedRedemption doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), "TrackedRedemption", "TrackedRedemption module was not added" ); - I_TrackedRedemption = TrackedRedemption.at(tx.logs[2].args._module); + I_TrackedRedemption = await TrackedRedemption.at(tx.logs[2].args._module); }); }); @@ -185,12 +191,11 @@ contract("TrackedRedemption", accounts => { it("Buy some tokens for account_investor1 (1 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 500000 @@ -206,21 +211,20 @@ contract("TrackedRedemption", accounts => { // Jump time await increaseTime(5000); - // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + // issue some tokens + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); }); it("Buy some tokens for account_investor2 (2 ETH)", async () => { // Add the Investor in to the whitelist - let tx = await I_GeneralTransferManager.modifyWhitelist( + let tx = await I_GeneralTransferManager.modifyKYCData( account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(30))), { from: account_issuer, gas: 500000 @@ -233,24 +237,24 @@ contract("TrackedRedemption", accounts => { "Failed in adding the investor in whitelist" ); - // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("2", "ether"), { from: token_owner }); + // issue some tokens + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether")); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); }); it("Redeem some tokens - fail insufficient allowance", async () => { await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, { from: token_owner }); - await catchRevert(I_TrackedRedemption.redeemTokens(web3.utils.toWei("1", "ether"), { from: account_investor1 })); + await catchRevert(I_TrackedRedemption.redeemTokens(new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 })); }); it("Redeem some tokens", async () => { - await I_SecurityToken.approve(I_TrackedRedemption.address, web3.utils.toWei("1", "ether"), { from: account_investor1 }); - let tx = await I_TrackedRedemption.redeemTokens(web3.utils.toWei("1", "ether"), { from: account_investor1 }); + await I_SecurityToken.approve(I_TrackedRedemption.address, new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); + let tx = await I_TrackedRedemption.redeemTokens(new BN(web3.utils.toWei("1", "ether")), { from: account_investor1 }); console.log(JSON.stringify(tx.logs)); assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Mismatch address"); - assert.equal(tx.logs[0].args._value, web3.utils.toWei("1", "ether"), "Wrong value"); + assert.equal(web3.utils.fromWei(web3.utils.toBN(tx.logs[0].args._value)), 1, "Wrong value"); }); it("Get the init data", async () => { diff --git a/test/w_lockup_transfer_manager.js b/test/w_lockup_transfer_manager.js new file mode 100644 index 000000000..d7bc6b76f --- /dev/null +++ b/test/w_lockup_transfer_manager.js @@ -0,0 +1,1012 @@ +import latestTime from './helpers/latestTime'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall } from './helpers/encodeCall'; +import { setUpPolymathNetwork, deployLockupVolumeRTMAndVerified } from "./helpers/createInstances"; +import { catchRevert } from "./helpers/exceptions"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const LockUpTransferManager = artifacts.require('./LockUpTransferManager'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require('web3'); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('LockUpTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let P_LockUpTransferManagerFactory; + let I_SecurityTokenRegistryProxy; + let P_LockUpTransferManager; + let I_GeneralTransferManagerFactory; + let I_LockUpTransferManagerFactory; + let I_GeneralPermissionManager; + let I_LockUpTransferManager; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_MRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_SecurityToken_div; + let I_GeneralTransferManager_div; + let I_LockUpVolumeRestrictionTM_div; + let I_STGetter; + let stGetter; + let stGetter_div; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + const name2 = "Core"; + const symbol2 = "Core"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + let temp; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + let currentTime; + + before(async() => { + currentTime = new BN(await latestTime()); + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[7]; + account_investor2 = accounts[8]; + account_investor3 = accounts[9]; + + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 4(c): Deploy the LockUpVolumeRestrictionTMFactory + [I_LockUpTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, 0); + // STEP 4(d): Deploy the LockUpVolumeRestrictionTMFactory + [P_LockUpTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + + LockupVolumeRestrictionTransferManagerFactory: + ${I_LockUpTransferManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async() => { + + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + it("Should register another ticker before the generation of new security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol2, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol2.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails, true, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol2.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken_div = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter_div = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken_div.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter_div.getModulesByType(2))[0]; + I_GeneralTransferManager_div = GeneralTransferManager.at(moduleData); + }); + + + }); + + describe("Buy tokens using on-chain whitelist and test locking them up and attempting to transfer", async() => { + + it("Should Buy the tokens from non-divisible", async() => { + // Add the Investor in to the whitelist + console.log(account_investor1); + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor1, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei('2', 'ether')), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toString(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Should Buy some more tokens from non-divisible tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor2, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(account_investor2, web3.utils.toWei('10', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('10', 'ether') + ); + }); + + it("Should unsuccessfully attach the LockUpTransferManager factory with the security token -- failed because Token is not paid", async () => { + await I_PolyToken.getTokens(web3.utils.toWei("2000", "ether"), token_owner); + await catchRevert( + I_SecurityToken.addModule(P_LockUpTransferManagerFactory.address, "0x", new BN(web3.utils.toWei("2000", "ether")), 0, { from: token_owner }) + ) + }); + + it("Should successfully attach the LockUpTransferManager factory with the security token", async () => { + let snapId = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), {from: token_owner}); + console.log((await P_LockUpTransferManagerFactory.getSetupCost.call()).toString()); + const tx = await I_SecurityToken.addModule(P_LockUpTransferManagerFactory.address, "0x", new BN(web3.utils.toWei("2000", "ether")), new BN(0), { from: token_owner }); + assert.equal(tx.logs[3].args._types[0].toString(), transferManagerKey, "LockUpVolumeRestrictionTMFactory doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "LockUpTransferManager", + "LockUpTransferManager module was not added" + ); + P_LockUpTransferManager = await LockUpTransferManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the LockUpVolumeRestrictionTMFactory with the non-divisible security token", async () => { + const tx = await I_SecurityToken.addModule(I_LockUpTransferManagerFactory.address, "0x", 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toString(), transferManagerKey, "LockUpVolumeRestrictionTMFactory doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "LockUpTransferManager", + "LockUpTransferManager module was not added" + ); + I_LockUpTransferManager = await LockUpTransferManager.at(tx.logs[2].args._module); + }); + + it("Should successfully attach the LockUpVolumeRestrictionTMFactory with the divisible security token", async () => { + const tx = await I_SecurityToken_div.addModule(I_LockUpTransferManagerFactory.address, "0x", 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toString(), transferManagerKey, "LockUpVolumeRestrictionTMFactory doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "LockUpTransferManager", + "LockUpTransferManager module was not added" + ); + I_LockUpVolumeRestrictionTM_div = await LockUpTransferManager.at(tx.logs[2].args._module); + }); + + it("Add a new token holder", async() => { + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor3, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Add the Investor in to the whitelist + // Mint some tokens + await I_SecurityToken.issue(account_investor3, web3.utils.toWei('10', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toString(), + web3.utils.toWei('10', 'ether') + ); + }); + + it("Should pause the tranfers at transferManager level", async() => { + let tx = await I_LockUpTransferManager.pause({from: token_owner}); + }); + + it("Should still be able to transfer between existing token holders up to limit", async() => { + // Transfer Some tokens between the investor + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toString(), + web3.utils.toWei('3', 'ether') + ); + }); + + it("Should unpause the tranfers at transferManager level", async() => { + await I_LockUpTransferManager.unpause({from: token_owner}); + }); + + it("Should prevent the creation of a lockup with bad parameters where the lockupAmount is zero", async() => { + // create a lockup + // this will generate an exception because the lockupAmount is zero + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUser( + account_investor2, + 0, + currentTime.add(new BN(duration.seconds(1))), + new BN(duration.seconds(400000)), + new BN(duration.seconds(100000)), + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ) + ) + }); + + it("Should prevent the creation of a lockup with bad parameters where the releaseFrequencySeconds is zero", async() => { + // create a lockup + // this will generate an exception because the releaseFrequencySeconds is zero + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUser( + account_investor2, + new BN(web3.utils.toWei('1', 'ether')), + currentTime.add(new BN(duration.seconds(1))), + new BN(duration.seconds(400000)), + 0, + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is zero", async() => { + // create a lockup + // this will generate an exception because the lockUpPeriodSeconds is zero + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUser( + account_investor2, + new BN(web3.utils.toWei('1', 'ether')), + currentTime.add(new BN(duration.seconds(1))), + 0, + new BN(duration.seconds(100000)), + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ) + ); + }); + + it("Should add the lockup type -- fail because of bad owner", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpType( + new BN(web3.utils.toWei('12', 'ether')), + currentTime.add(new BN(duration.days(1))), + 60, + 20, + web3.utils.fromAscii("a_lockup"), + { + from: account_investor1 + } + ) + ); + }) + + it("Should add the new lockup type", async() => { + let tx = await I_LockUpTransferManager.addNewLockUpType( + new BN(web3.utils.toWei('12', 'ether')), + currentTime.add(new BN(duration.days(1))), + 60, + 20, + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ); + assert.equal((tx.logs[0].args._lockupAmount).toString(), web3.utils.toWei('12', 'ether')); + }); + + it("Should fail to add the creation of the lockup where lockupName is already exists", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUser( + account_investor1, + web3.utils.toWei('5', 'ether'), + currentTime.add(new BN(duration.seconds(1))), + new BN(duration.seconds(400000)), + new BN(duration.seconds(100000)), + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ) + ); + }) + + it("Should allow the creation of a lockup where the lockup amount is divisible" , async() => { + // create a lockup + currentTime = new BN(await latestTime()); + let tx = await I_LockUpVolumeRestrictionTM_div.addNewLockUpToUser( + account_investor1, + web3.utils.toWei('0.5', 'ether'), + currentTime.add(new BN(duration.seconds(1))), + new BN(duration.seconds(400000)), + new BN(duration.seconds(100000)), + web3.utils.fromAscii("a_lockup"), + { + from: token_owner + } + ); + assert.equal(tx.logs[1].args._userAddress, account_investor1); + assert.equal((tx.logs[0].args._lockupAmount).toString(), web3.utils.toWei('0.5', 'ether')); + }); + + it("Should allow the creation of a lockup where the lockup amount is prime no", async() => { + // create a lockup + let tx = await I_LockUpVolumeRestrictionTM_div.addNewLockUpToUser( + account_investor1, + new BN(web3.utils.toWei('64951', 'ether')), + currentTime.add(new BN(duration.seconds(1))), + new BN(duration.seconds(400000)), + new BN(duration.seconds(100000)), + web3.utils.fromAscii("b_lockup"), + { + from: token_owner + } + ); + assert.equal(tx.logs[1].args._userAddress, account_investor1); + assert.equal((tx.logs[0].args._lockupAmount).toString(), web3.utils.toWei('64951', 'ether')); + }); + + it("Should prevent the transfer of tokens in a lockup", async() => { + + let balance = await I_SecurityToken.balanceOf(account_investor2) + console.log("balance", balance.div(new BN(1).mul(new BN(10).pow(new BN(18)))).toString()); + // create a lockup for their entire balance + // over 12 seconds total, with 3 periods of 20 seconds each. + await I_LockUpTransferManager.addNewLockUpToUser( + account_investor2, + balance, + currentTime.add(new BN(duration.seconds(1))), + 60, + 20, + web3.utils.fromAscii("b_lockup"), + { + from: token_owner + } + ); + await increaseTime(2); + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("b_lockup")); + console.log("Amount get unlocked:", (tx[4].toString())); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); + }); + + it("Should prevent the transfer of tokens if the amount is larger than the amount allowed by lockups", async() => { + // wait 20 seconds + await increaseTime(duration.seconds(20)); + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("b_lockup")); + console.log("Amount get unlocked:", (tx[4].toString())); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }) + ); + }); + + it("Should allow the transfer of tokens in a lockup if a period has passed", async() => { + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("b_lockup")); + console.log("Amount get unlocked:", (tx[4].toString())); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + }); + + it("Should again transfer of tokens in a lockup if a period has passed", async() => { + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("b_lockup")); + console.log("Amount get unlocked:", (tx[4].toString())); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + }); + + it("Should allow the transfer of more tokens in a lockup if another period has passed", async() => { + + // wait 20 more seconds + 1 to get rid of same block time + await increaseTime(duration.seconds(21)); + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("b_lockup")); + console.log("Amount get unlocked:", (tx[4].toString())); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }); + }); + + it("Buy more tokens from secondary market to investor2", async() => { + // Mint some tokens + await I_SecurityToken.issue(account_investor2, web3.utils.toWei('5', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('10', 'ether') + ); + }) + + it("Should allow transfer for the tokens that comes from secondary market + unlocked tokens", async() => { + + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('6', 'ether') + ); + }); + + it("Should allow the transfer of all tokens in a lockup if the entire lockup has passed", async() => { + + let balance = await I_SecurityToken.balanceOf(account_investor2) + + // wait 20 more seconds + 1 to get rid of same block time + await increaseTime(duration.seconds(21)); + console.log((await I_LockUpTransferManager.getLockedTokenToUser.call(account_investor2)).toString()); + await I_SecurityToken.transfer(account_investor1, balance, { from: account_investor2 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('0', 'ether') + ); + }); + + it("Should fail to add the multiple lockups -- because array length mismatch", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3], + [web3.utils.toWei("6", "ether"), web3.utils.toWei("3", "ether")], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45], + [20, 15], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the multiple lockups -- because array length mismatch", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3, account_investor3], + [], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45], + [20, 15], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the multiple lockups -- because array length mismatch", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3, account_investor3], + [web3.utils.toWei("6", "ether"), web3.utils.toWei("3", "ether")], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45, 50], + [20, 15], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the multiple lockups -- because array length mismatch", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3, account_investor3], + [web3.utils.toWei("6", "ether"), web3.utils.toWei("3", "ether")], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45, 50], + [20, 15, 10], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the multiple lockups -- because array length mismatch", async() => { + await catchRevert( + I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3, account_investor3], + [web3.utils.toWei("6", "ether"), web3.utils.toWei("3", "ether")], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45], + [20, 15], + [web3.utils.fromAscii("c_lockup")], + { + from: token_owner + } + ) + ); + }); + + it("Should add the multiple lockup to a address", async() => { + currentTime = new BN(await latestTime()); + await I_LockUpTransferManager.addNewLockUpToUserMulti( + [account_investor3, account_investor3], + [web3.utils.toWei("6", "ether"), web3.utils.toWei("3", "ether")], + [currentTime.add(new BN(duration.seconds(1))), currentTime.add(new BN(duration.seconds(21)))], + [60, 45], + [20, 15], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: token_owner + } + ); + + await increaseTime(1); + let tx = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("c_lockup")); + let tx2 = await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("d_lockup")); + console.log("Total Amount get unlocked:", (tx[4].toString()) + (tx2[4].toString())); + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('2', 'ether'), { from: account_investor3 }) + ); + + }); + + it("Should transfer the tokens after period get passed", async() => { + // increase 20 sec that makes 1 period passed + // 2 from a period and 1 is already unlocked + await increaseTime(21); + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString())}`); + console.log(`\t Locked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString())}`); + console.log(`\t Unlocked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`UNLOCKED`))).toString())}`); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3'), { from: account_investor3 }) + }) + + it("Should check the balance of the locked tokens", async() => { + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString())}`); + console.log(`\t Locked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString())}`); + console.log(`\t Unlocked balance: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`UNLOCKED`))).toString())}`); + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString()), + web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString()) + ); + console.log(`\t Wrong partition: ${web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.toHex(`OCKED`))).toString())}`); + assert.equal( + web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.toHex(`OCKED`))).toString()), + 0 + ); + }); + + it("Should transfer the tokens after passing another period of the lockup", async() => { + // increase the 15 sec that makes first period of another lockup get passed + // allow 1 token to transfer + await increaseTime(15); + // first fail because 3 tokens are not in unlocked state + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3'), { from: account_investor3 }) + ) + let lockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString()); + let unlockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`UNLOCKED`))).toString()); + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString())}`); + console.log(`\t Locked balance: ${lockedBalance}`); + console.log(`\t Unlocked Balance: ${unlockedBalance}`); + assert.equal( + web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString()), + parseInt(lockedBalance) + parseInt(unlockedBalance) + ); + // second txn will pass because 1 token is in unlocked state + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1'), { from: account_investor3 }) + }); + + it("Should transfer the tokens from both the lockup simultaneously", async() => { + // Increase the 20 sec (+ 1 to mitigate the block time) that unlocked another 2 tokens from the lockup 1 and simultaneously unlocked 1 + // more token from the lockup 2 + await increaseTime(21); + + let lockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString()); + let unlockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`UNLOCKED`))).toString()); + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString())}`); + console.log(`\t Locked balance: ${lockedBalance}`); + console.log(` \t Unlocked Amount for lockup 1: ${web3.utils.fromWei(((await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("c_lockup")))[4]).toString())}`) + console.log(` \t Unlocked Amount for lockup 2: ${web3.utils.fromWei(((await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("d_lockup")))[4]).toString())}`) + console.log(`\t Unlocked Balance: ${unlockedBalance}`); + + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3'), { from: account_investor3 }) + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toString(), + web3.utils.toWei('3', 'ether') + ); + console.log("After transaction"); + lockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`LOCKED`))).toString()); + unlockedBalance = web3.utils.fromWei((await stGetter.balanceOfByPartition.call(account_investor3, web3.utils.utf8ToHex(`UNLOCKED`))).toString()); + console.log(`\t Total balance: ${web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString())}`); + console.log(`\t Locked balance: ${lockedBalance}`); + console.log(` \t Unlocked Amount for lockup 1: ${web3.utils.fromWei(((await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("c_lockup")))[4]).toString())}`) + console.log(` \t Unlocked Amount for lockup 2: ${web3.utils.fromWei(((await I_LockUpTransferManager.getLockUp.call(web3.utils.fromAscii("d_lockup")))[4]).toString())}`) + console.log(`\t Unlocked Balance: ${unlockedBalance}`); + }); + + it("Should remove multiple lockup --failed because of bad owner", async() => { + await catchRevert( + I_LockUpTransferManager.removeLockUpFromUserMulti( + [account_investor3, account_investor3], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("d_lockup")], + { + from: account_polymath + } + ) + ); + }); + + it("Should remove the multiple lockup -- failed because of invalid lockupname", async() => { + await catchRevert( + I_LockUpTransferManager.removeLockUpFromUserMulti( + [account_investor3, account_investor3], + [web3.utils.fromAscii("c_lockup"), web3.utils.fromAscii("e_lockup")], + { + from: account_polymath + } + ) + ); + }) + + it("Should remove the multiple lockup", async() => { + await I_LockUpTransferManager.removeLockUpFromUserMulti( + [account_investor3, account_investor3], + [web3.utils.fromAscii("d_lockup"), web3.utils.fromAscii("c_lockup")], + { + from: token_owner + } + ) + // do the free transaction now + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3'), { from: account_investor3 }) + }); + + it("Should fail to modify the lockup -- because of bad owner", async() => { + await I_SecurityToken.issue(account_investor3, web3.utils.toWei("9"), "0x0", {from: token_owner}); + + let tx = await I_LockUpTransferManager.addNewLockUpToUser( + account_investor3, + web3.utils.toWei("9"), + currentTime.add(new BN(duration.minutes(5))), + 60, + 20, + web3.utils.fromAscii("z_lockup"), + { + from: token_owner + } + ); + + await catchRevert( + // edit the lockup + I_LockUpTransferManager.modifyLockUpType( + web3.utils.toWei("9"), + currentTime.add(new BN(duration.seconds(1))), + 60, + 20, + web3.utils.fromAscii("z_lockup"), + { + from: account_polymath + } + ) + ) + }) + + it("Modify the lockup when startTime is in past -- failed because startTime is in the past", async() => { + await catchRevert( + // edit the lockup + I_LockUpTransferManager.modifyLockUpType( + web3.utils.toWei("9"), + currentTime.add(new BN(duration.seconds(50))), + 60, + 20, + web3.utils.fromAscii("z_lockup"), + { + from: token_owner + } + ) + ) + }) + + it("Modify the lockup when startTime is in past -- failed because of invalid index", async() => { + await catchRevert( + // edit the lockup + I_LockUpTransferManager.modifyLockUpType( + web3.utils.toWei("9"), + currentTime.add(new BN(duration.seconds(50))), + 60, + 20, + web3.utils.fromAscii("m_lockup"), + { + from: token_owner + } + ) + ) + }) + + it("should successfully modify the lockup", async() => { + // edit the lockup + currentTime = new BN(await latestTime()); + await I_LockUpTransferManager.modifyLockUpType( + web3.utils.toWei("9"), + currentTime.add(new BN(duration.seconds(50))), + 60, + 20, + web3.utils.fromAscii("z_lockup"), + { + from: token_owner + } + ); + }) + + it("Should prevent the transfer of tokens in an edited lockup", async() => { + + // balance here should be 12000000000000000000 (12e18 or 12 eth) + let balance = await I_SecurityToken.balanceOf(account_investor1) + + console.log("balance", balance.div(new BN(1).mul(new BN(10).pow(new BN(18)))).toString()); + + // create a lockup for their entire balance + // over 16 seconds total, with 4 periods of 4 seconds each. + await I_LockUpTransferManager.addNewLockUpToUser( + account_investor1, + balance, + currentTime.add(new BN(duration.minutes(5))), + 60, + 20, + web3.utils.fromAscii("f_lockup"), + { + from: token_owner + } + ); + + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) + ); + + let lockUp = await I_LockUpTransferManager.getLockUp(web3.utils.fromAscii("f_lockup")); + console.log(lockUp); + // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + assert.equal( + lockUp[0].div(new BN(1).mul(new BN(10).pow(new BN(18)))).toString(), + balance.div(new BN(1).mul(new BN(10).pow(new BN(18)))).toString() + ); + assert.equal(lockUp[2].toString(), 60); + assert.equal(lockUp[3].toString(), 20); + assert.equal(lockUp[4].toString(), 0); + + // edit the lockup + temp = currentTime.add(new BN(duration.seconds(1))); + await I_LockUpTransferManager.modifyLockUpType( + balance, + temp, + 60, + 20, + web3.utils.fromAscii("f_lockup"), + { + from: token_owner + } + ); + + // attempt a transfer + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) + ); + + // wait 20 seconds + await increaseTime(21); + + // transfer should succeed + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }); + + }); + + it("Modify the lockup during the lockup periods", async() => { + let balance = await I_SecurityToken.balanceOf(account_investor1) + let lockUp = await I_LockUpTransferManager.getLockUp(web3.utils.fromAscii("f_lockup")); + console.log(lockUp[4].div(new BN(1).mul(new BN(10).pow(new BN(18)))).toString()); + // edit the lockup + await I_LockUpTransferManager.modifyLockUpType( + balance, + currentTime.add(new BN(duration.days(10))), + 90, + 30, + web3.utils.fromAscii("f_lockup"), + { + from: token_owner + } + ); + }); + + it("Should remove the lockup multi", async() => { + await I_LockUpTransferManager.addNewLockUpTypeMulti( + [web3.utils.toWei("10"), web3.utils.toWei("16")], + [currentTime.add(new BN(duration.days(1))), currentTime.add(new BN(duration.days(1)))], + [50000, 50000], + [10000, 10000], + [web3.utils.fromAscii("k_lockup"), web3.utils.fromAscii("l_lockup")], + { + from: token_owner + } + ); + + // removing the lockup type + let tx = await I_LockUpTransferManager.removeLockupType(web3.utils.fromAscii("k_lockup"), {from: token_owner}); + assert.equal(web3.utils.toUtf8(tx.logs[0].args._lockupName), "k_lockup"); + + // attaching the lockup to a user + + await I_LockUpTransferManager.addLockUpByName(account_investor2, web3.utils.fromAscii("l_lockup"), {from: token_owner}); + + // try to delete the lockup but fail + + await catchRevert( + I_LockUpTransferManager.removeLockupType(web3.utils.fromAscii("l_lockup"), {from: token_owner}) + ); + }) + + it("Should succesfully get the non existed lockup value, it will give everything 0", async() => { + let data = await I_LockUpTransferManager.getLockUp(web3.utils.fromAscii("foo")); + assert.equal(data[0], 0); + }) + + it("Should get configuration function signature", async() => { + let sig = await I_LockUpTransferManager.getInitFunction.call(); + assert.equal(web3.utils.hexToNumber(sig), 0); + }); + + it("Should get the all lockups added by the issuer till now", async() => { + let tx = await I_LockUpTransferManager.getAllLockups.call(); + for (let i = 0; i < tx.length; i++) { + console.log(web3.utils.toUtf8(tx[i])); + } + }) + + it("Should get the permission", async() => { + let perm = await I_LockUpTransferManager.getPermissions.call(); + assert.equal(perm.length, 1); + assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ''), "ADMIN") + }); + + }); + + describe("LockUpTransferManager Transfer Manager Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_LockUpTransferManagerFactory.getSetupCost.call(),0); + assert.equal((await I_LockUpTransferManagerFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_LockUpTransferManagerFactory.getName.call()) + .replace(/\u0000/g, ''), + "LockUpTransferManager", + "Wrong Module added"); + assert.equal(await I_LockUpTransferManagerFactory.description.call(), + "Manage transfers using lock ups over time", + "Wrong Module added"); + assert.equal(await I_LockUpTransferManagerFactory.title.call(), + "LockUp Transfer Manager", + "Wrong Module added"); + assert.equal(await I_LockUpTransferManagerFactory.getInstructions.call(), + "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters.", + "Wrong Module added"); + assert.equal(await I_LockUpTransferManagerFactory.version.call(), "1.0.0"); + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_LockUpTransferManagerFactory.getTags.call(); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "LockUp"); + }); + }); + +}); diff --git a/test/w_lockup_volume_restriction_transfer_manager.js b/test/w_lockup_volume_restriction_transfer_manager.js deleted file mode 100644 index fe06f3f5c..000000000 --- a/test/w_lockup_volume_restriction_transfer_manager.js +++ /dev/null @@ -1,808 +0,0 @@ -import latestTime from './helpers/latestTime'; -import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; -import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; -import { encodeProxyCall } from './helpers/encodeCall'; -import { setUpPolymathNetwork, deployLockupVolumeRTMAndVerified } from "./helpers/createInstances"; -import { catchRevert } from "./helpers/exceptions"; - -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const VolumeRestrictionTransferManager = artifacts.require('./LockupVolumeRestrictionTM'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); - -const Web3 = require('web3'); -const BigNumber = require('bignumber.js'); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port - -contract('LockupVolumeRestrictionTransferManager', accounts => { - - // Accounts Variable declaration - let account_polymath; - let account_issuer; - let token_owner; - let account_investor1; - let account_investor2; - let account_investor3; - let account_investor4; - - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - - let message = "Transaction Should Fail!"; - - // Contract Instance Declaration - let P_VolumeRestrictionTransferManagerFactory; - let I_SecurityTokenRegistryProxy; - let P_VolumeRestrictionTransferManager; - let I_GeneralTransferManagerFactory; - let I_VolumeRestrictionTransferManagerFactory; - let I_GeneralPermissionManager; - let I_VolumeRestrictionTransferManager; - let I_GeneralTransferManager; - let I_ModuleRegistryProxy; - let I_ModuleRegistry; - let I_FeatureRegistry; - let I_SecurityTokenRegistry; - let I_STRProxied; - let I_MRProxied; - let I_STFactory; - let I_SecurityToken; - let I_PolyToken; - let I_PolymathRegistry; - - // SecurityToken Details - const name = "Team"; - const symbol = "sap"; - const tokenDetails = "This is equity type of issuance"; - const decimals = 18; - const contact = "team@polymath.network"; - - // Module key - const delegateManagerKey = 1; - const transferManagerKey = 2; - const stoKey = 3; - - // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); - - before(async() => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; - - token_owner = account_issuer; - - account_investor1 = accounts[7]; - account_investor2 = accounts[8]; - account_investor3 = accounts[9]; - - let instances = await setUpPolymathNetwork(account_polymath, token_owner); - - [ - I_PolymathRegistry, - I_PolyToken, - I_FeatureRegistry, - I_ModuleRegistry, - I_ModuleRegistryProxy, - I_MRProxied, - I_GeneralTransferManagerFactory, - I_STFactory, - I_SecurityTokenRegistry, - I_SecurityTokenRegistryProxy, - I_STRProxied - ] = instances; - - // STEP 4(c): Deploy the VolumeRestrictionTransferManager - [I_VolumeRestrictionTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); - // STEP 4(d): Deploy the VolumeRestrictionTransferManager - [P_VolumeRestrictionTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); - - // Printing all the contract addresses - console.log(` - --------------------- Polymath Network Smart Contracts: --------------------- - PolymathRegistry: ${I_PolymathRegistry.address} - SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} - SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} - ModuleRegistry: ${I_ModuleRegistry.address} - ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} - FeatureRegistry: ${I_FeatureRegistry.address} - - STFactory: ${I_STFactory.address} - GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} - - LockupVolumeRestrictionTransferManagerFactory: - ${I_VolumeRestrictionTransferManagerFactory.address} - ----------------------------------------------------------------------------- - `); - }); - - describe("Generate the SecurityToken", async() => { - - it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); - assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); - }); - - it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); - - // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({from: _blockNo}), 1); - - // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._types[0].toNumber(), 2); - assert.equal( - web3.utils.toAscii(log.args._name) - .replace(/\u0000/g, ''), - "GeneralTransferManager" - ); - }); - - it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - }); - - }); - - describe("Buy tokens using on-chain whitelist and test locking them up and attempting to transfer", async() => { - - it("Should Buy the tokens", async() => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Jump time - await increaseTime(5000); - - // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('2', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('2', 'ether') - ); - }); - - it("Should Buy some more tokens", async() => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei('10', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), - web3.utils.toWei('10', 'ether') - ); - }); - - it("Should unsuccessfully attach the VolumeRestrictionTransferManager factory with the security token -- failed because Token is not paid", async () => { - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - await catchRevert( - I_SecurityToken.addModule(P_VolumeRestrictionTransferManagerFactory.address, 0, web3.utils.toWei("500", "ether"), 0, { from: token_owner }) - ) - }); - - it("Should successfully attach the VolumeRestrictionTransferManager factory with the security token", async () => { - let snapId = await takeSnapshot(); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), {from: token_owner}); - const tx = await I_SecurityToken.addModule(P_VolumeRestrictionTransferManagerFactory.address, 0, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); - assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "VolumeRestrictionTransferManagerFactory doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[3].args._name) - .replace(/\u0000/g, ''), - "LockupVolumeRestrictionTM", - "VolumeRestrictionTransferManagerFactory module was not added" - ); - P_VolumeRestrictionTransferManager = VolumeRestrictionTransferManager.at(tx.logs[3].args._module); - await revertToSnapshot(snapId); - }); - - it("Should successfully attach the VolumeRestrictionTransferManager with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_VolumeRestrictionTransferManagerFactory.address, 0, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "VolumeRestrictionTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "LockupVolumeRestrictionTM", - "VolumeRestrictionTransferManager module was not added" - ); - I_VolumeRestrictionTransferManager = VolumeRestrictionTransferManager.at(tx.logs[2].args._module); - }); - - it("Add a new token holder", async() => { - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Add the Investor in to the whitelist - // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei('10', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), - web3.utils.toWei('10', 'ether') - ); - }); - - it("Should pause the tranfers at transferManager level", async() => { - let tx = await I_VolumeRestrictionTransferManager.pause({from: token_owner}); - }); - - it("Should still be able to transfer between existing token holders up to limit", async() => { - // Add the Investor in to the whitelist - // Mint some tokens - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('3', 'ether') - ); - }); - - it("Should unpause the tranfers at transferManager level", async() => { - await I_VolumeRestrictionTransferManager.unpause({from: token_owner}); - }); - - it("Should prevent the creation of a lockup with bad parameters where the totalAmount is zero", async() => { - // create a lockup - // this will generate an exception because the totalAmount is zero - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, 0, { from: token_owner }) - ) - }); - - it("Should prevent the creation of a lockup with bad parameters where the releaseFrequencySeconds is zero", async() => { - // create a lockup - // this will generate an exception because the releaseFrequencySeconds is zero - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 0, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - ); - }); - - it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is zero", async() => { - // create a lockup - // this will generate an exception because the lockUpPeriodSeconds is zero - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 0, 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - ); - }); - - - it("Should prevent the creation of a lockup with bad parameters where the total amount to be released is more granular than allowed by the token", async() => { - // create a lockup - // this will generate an exception because we're locking up 5e17 tokens but the granularity is 5e18 tokens - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, web3.utils.toWei('0.5', 'ether'), { from: token_owner }) - ); - }); - - it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is not evenly divisible by releaseFrequencySeconds", async() => { - - // balance should be 9000000000000000000 here (9 eth) - let balance = await I_SecurityToken.balanceOf(account_investor2) - - - // create a lockup - // over 17 seconds total, with 4 periods. - // this will generate an exception because 17 is not evenly divisble by 4. - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 17, 4, 0, balance, { from: token_owner }) - ); - }); - - it("Should prevent the creation of a lockup with bad parameters where the total amount being locked up isn't evenly divisible by the number of total periods", async() => { - - // create a lockup for a balance of 1 eth - // over 16e18 seconds total, with 4e18 periods of 4 seconds each. - // this will generate an exception because 16e18 / 4e18 = 4e18 but the token granularity is 1e18 and 1e18 % 4e18 != 0 - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, web3.utils.toWei('16', 'ether'), 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - ); - }); - - it("Should prevent the creation of a lockup with bad parameters where the amount to be released per period is too granular for the token", async() => { - - // balance should be 9000000000000000000 here (9 eth) - let balance = await I_SecurityToken.balanceOf(account_investor2) - - // create a lockup for their entire balance - // over 16 seconds total, with 4 periods of 4 seconds each. - // this will generate an exception because 9000000000000000000 / 4 = 2250000000000000000 but the token granularity is 1000000000000000000 - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, balance, { from: token_owner }) - ); - - }); - - it("Should prevent the transfer of tokens in a lockup", async() => { - - let balance = await I_SecurityToken.balanceOf(account_investor2) - console.log("balance", balance.dividedBy(new BigNumber(1).times(new BigNumber(10).pow(18))).toNumber()); - // create a lockup for their entire balance - // over 12 seconds total, with 3 periods of 4 seconds each. - await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 12, 4, 0, balance, { from: token_owner }); - - // read only - check if transfer will pass. it should return INVALID - let result = await I_VolumeRestrictionTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, false) - // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 - assert.equal(result.toString(), '0') - - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - ); - }); - - it("Should allow the transfer of tokens in a lockup if a period has passed", async() => { - - // wait 4 seconds - await increaseTime(duration.seconds(4)); - - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - }); - - it("Should prevent the transfer of tokens if the amount is larger than the amount allowed by lockups", async() => { - - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }) - ); - }); - - it("Should allow the transfer of more tokens in a lockup if another period has passed", async() => { - - // wait 4 more seconds - await increaseTime(4000); - - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - }); - - it("Should allow the transfer of all tokens in a lockup if the entire lockup has passed", async() => { - - let balance = await I_SecurityToken.balanceOf(account_investor2) - - // wait 4 more seconds - await increaseTime(4000); - - await I_SecurityToken.transfer(account_investor1, balance, { from: account_investor2 }); - }); - - it("Should prevent the transfer of tokens in an edited lockup", async() => { - - - // balance here should be 12000000000000000000 (12e18 or 12 eth) - let balance = await I_SecurityToken.balanceOf(account_investor1) - - // create a lockup for their entire balance - // over 16 seconds total, with 4 periods of 4 seconds each. - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 16, 4, 0, balance, { from: token_owner }); - - // let blockNumber = await web3.eth.getBlockNumber(); - // console.log('blockNumber',blockNumber) - let now = (await web3.eth.getBlock('latest')).timestamp - - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - - // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 1) - - let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor1, 0); - // console.log(lockUp); - // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount - assert.equal(lockUp[0].toString(), '16'); - assert.equal(lockUp[1].toString(), '4'); - assert.equal(lockUp[2].toNumber(), now); - assert.equal(lockUp[3].toString(), balance.toString()); - - // edit the lockup - await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor1, 0, 8, 4, 0, balance, { from: token_owner }); - - // attempt a transfer - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) - ); - - // wait 4 seconds - await new Promise(resolve => setTimeout(resolve, 4000)); - - // transfer should succeed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }); - - }); - - it("Should succesfully modify the lockup - fail because array index out of bound", async() => { - // balance here should be 12000000000000000000 (12e18 or 12 eth) - let balance = await I_SecurityToken.balanceOf(account_investor1); - await catchRevert( - I_VolumeRestrictionTransferManager.modifyLockUp(account_investor1, 8, 8, 4, 0, balance, { from: token_owner }) - ); - }) - - it("Should succesfully get the lockup - fail because array index out of bound", async() => { - await catchRevert( - I_VolumeRestrictionTransferManager.getLockUp(account_investor1, 9) - ); - }) - - it("Should be possible to remove a lockup -- couldn't transfer because of lock up", async() => { - - let acct1Balance = await I_SecurityToken.balanceOf(account_investor1) - - await catchRevert( - I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }) - ); - - // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 1) - - // remove the lockup - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor1, 0, { from: token_owner }); - - lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 0) - - let acct2BalanceBefore = await I_SecurityToken.balanceOf(account_investor2) - await I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }); - let acct2BalanceAfter = await I_SecurityToken.balanceOf(account_investor2) - - assert.equal(acct2BalanceAfter.sub(acct2BalanceBefore).toString(), acct1Balance.toString()) - }); - - it("Should try to remove the lockup --failed because of index is out of bounds", async() => { - await catchRevert( - I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 7, { from: token_owner }) - ); - }) - - it("Should be possible to create multiple lockups at once", async() => { - - let balancesBefore = {} - - // should be 12000000000000000000 - balancesBefore[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - - - // should be 10000000000000000000 - balancesBefore[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - - - let lockUpCountsBefore = {} - - // get lockups for acct 2 - lockUpCountsBefore[account_investor2] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCountsBefore[account_investor2], 1) // there's one old, expired lockup on acct already - - // get lockups for acct 3 - lockUpCountsBefore[account_investor3] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor3); - assert.equal(lockUpCountsBefore[account_investor3], 0) - - // create lockups for their entire balances - await I_VolumeRestrictionTransferManager.addLockUpMulti( - [account_investor2, account_investor3], - [24, 8], - [4, 4], - [0, 0], - [balancesBefore[account_investor2], balancesBefore[account_investor3]], - { from: token_owner } - ); - - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }) - ); - - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }) - ); - - let balancesAfter = {} - balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - assert.equal(balancesBefore[account_investor2].toString(), balancesAfter[account_investor2].toString()) - - balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - assert.equal(balancesBefore[account_investor3].toString(), balancesAfter[account_investor3].toString()) - - let lockUpCountsAfter = {} - - // get lockups for acct 2 - lockUpCountsAfter[account_investor2] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCountsAfter[account_investor2], 2); - - // get lockups for acct 3 - lockUpCountsAfter[account_investor3] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor3); - assert.equal(lockUpCountsAfter[account_investor3], 1); - - // wait 4 seconds - await increaseTime(4000); - - // try transfers again - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }); - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }); - - - balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - assert.equal(balancesBefore[account_investor2].sub(web3.utils.toWei('2', 'ether')).toString(), balancesAfter[account_investor2].toString()) - - balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - assert.equal(balancesBefore[account_investor3].sub(web3.utils.toWei('5', 'ether')).toString(), balancesAfter[account_investor3].toString()) - - }); - - it("Should revert if the parameters are bad when creating multiple lockups", async() => { - - await catchRevert( - // pass in the wrong number of params. txn should revert - I_VolumeRestrictionTransferManager.addLockUpMulti( - [account_investor2, account_investor3], - [16, 8], - [2], // this array should have 2 elements but it has 1, which should cause a revert - [0, 0], - [web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether')], - { from: token_owner } - ) - ); - }); - - it("Should be possible to create a lockup with a specific start time in the future", async() => { - - // remove all lockups for account 2 - let lockUpsLength = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpsLength, 2); - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - lockUpsLength = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpsLength, 0); - - let now = latestTime(); - - // balance here should be 10000000000000000000 - let balance = await I_SecurityToken.balanceOf(account_investor2) - - await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 100, 10, now + duration.seconds(4), balance, { from: token_owner }); - - // wait 4 seconds for the lockup to begin - await increaseTime(duration.seconds(4)); - - // try another transfer. it should also fail because the lockup has just begun - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - ); - - }); - - it("Should be possible to edit a lockup with a specific start time in the future", async() => { - - // edit the lockup - let now = latestTime(); - - // should be 10000000000000000000 - let balance = await I_SecurityToken.balanceOf(account_investor2) - - // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCount, 1) - - let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); - - // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount - assert.equal(lockUp[0].toString(), '100'); - assert.equal(lockUp[1].toString(), '10'); - assert.isAtMost(lockUp[2].toNumber(), now); - assert.equal(lockUp[3].toString(), balance.toString()); - - // edit the lockup - await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor2, 0, 8, 4, now + duration.seconds(4), balance, { from: token_owner }); - - // check and get the lockup again - lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCount, 1) - - lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); - - // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount - assert.equal(lockUp[0].toString(), '8'); - assert.equal(lockUp[1].toString(), '4'); - assert.isAtMost(lockUp[2].toNumber(), now + 4); - assert.equal(lockUp[3].toString(), balance.toString()); - - // try a transfer. it should fail because again, the lockup hasn't started yet. - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - ); - - // wait 4 seconds for the lockup to begin - await increaseTime(duration.seconds(4)); - - // try another transfer. it should fail because the lockup has just begun - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - ); - - // wait 4 seconds for the lockup's first period to elapse - await increaseTime(duration.seconds(4)); - - // try another transfer. it should pass - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); - - - // try another transfer without waiting for another period to pass. it should fail - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }) - ); - - // wait 4 seconds for the lockup's first period to elapse - await increaseTime(duration.seconds(4)); - - let lockUpBeforeVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); - // check if transfer will pass in read-only operation - let result = await I_VolumeRestrictionTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('5', 'ether'), 0, false) - // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 - assert.equal(result.toString(), '2') - let lockUpAfterVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); - - assert.equal(lockUpBeforeVerify[4].toString(), lockUpAfterVerify[4].toString()) - - // try another transfer. it should pass - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); - - // wait 4 seconds for the lockup's first period to elapse. but, we are all out of periods. - await increaseTime(duration.seconds(4)); - - // try one final transfer. this should fail because the user has already withdrawn their entire balance - await catchRevert( - I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - ); - }); - - it("Should be possible to stack lockups", async() => { - // should be 17000000000000000000 - let balance = await I_SecurityToken.balanceOf(account_investor1) - - // check and make sure that acct1 has no lockups so far - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount.toString(), 0) - - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 12, 4, 0, web3.utils.toWei('6', 'ether'), { from: token_owner }); - - // try to transfer 11 tokens that aren't locked up yet be locked up. should succeed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('11', 'ether'), { from: account_investor1 }); - - // try a transfer. it should fail because it's locked up from the first lockups - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - // wait 4 seconds for the lockup's first period to elapse. - await increaseTime(duration.seconds(4)); - - // should succeed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('2', 'ether'), { from: account_investor1 }); - - // send 8 back to investor1 so that we can lock them up - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('8', 'ether'), { from: account_investor2 }); - - // let's add another lockup to stack them - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 16, 4, 0, web3.utils.toWei('8', 'ether'), { from: token_owner }); - - // try a transfer. it should fail because it's locked up from both lockups - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - - // wait 4 seconds for the 1st lockup's second period to elapse, and the 2nd lockup's first period to elapse - await increaseTime(duration.seconds(4)); - - // should now be able to transfer 4, because of 2 allowed from the 1st lockup and 2 from the 2nd - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // try aother transfer. it should fail because it's locked up from both lockups again - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - - // wait 4 seconds for the 1st lockup's final period to elapse, and the 2nd lockup's second period to elapse - await increaseTime(duration.seconds(4)); - // should now be able to transfer 4, because of 2 allowed from the 1st lockup and 2 from the 2nd - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // wait 8 seconds for 2nd lockup's third and fourth periods to elapse - await increaseTime(duration.seconds(8)); - // should now be able to transfer 4, because there are 2 allowed per period in the 2nd lockup, and 2 periods have elapsed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // send the 3 back from acct2 that we sent over in the beginning of this test - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - - // try another transfer. it should pass because both lockups have been entirely used - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }); - - balance = await I_SecurityToken.balanceOf(account_investor1) - assert.equal(balance.toString(), web3.utils.toWei('2', 'ether')) - }); - - - it("Should get configuration function signature", async() => { - let sig = await I_VolumeRestrictionTransferManager.getInitFunction.call(); - assert.equal(web3.utils.hexToNumber(sig), 0); - }); - - - it("Should get the permission", async() => { - let perm = await I_VolumeRestrictionTransferManager.getPermissions.call(); - assert.equal(perm.length, 1); - // console.log(web3.utils.toAscii(perm[0]).replace(/\u0000/g, '')) - assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ''), "ADMIN") - }); - - }); - - describe("VolumeRestriction Transfer Manager Factory test cases", async() => { - - it("Should get the exact details of the factory", async() => { - assert.equal(await I_VolumeRestrictionTransferManagerFactory.getSetupCost.call(),0); - assert.equal((await I_VolumeRestrictionTransferManagerFactory.getTypes.call())[0],2); - assert.equal(web3.utils.toAscii(await I_VolumeRestrictionTransferManagerFactory.getName.call()) - .replace(/\u0000/g, ''), - "LockupVolumeRestrictionTM", - "Wrong Module added"); - assert.equal(await I_VolumeRestrictionTransferManagerFactory.description.call(), - "Manage transfers using lock ups over time", - "Wrong Module added"); - assert.equal(await I_VolumeRestrictionTransferManagerFactory.title.call(), - "Lockup Volume Restriction Transfer Manager", - "Wrong Module added"); - assert.equal(await I_VolumeRestrictionTransferManagerFactory.getInstructions.call(), - "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters.", - "Wrong Module added"); - assert.equal(await I_VolumeRestrictionTransferManagerFactory.version.call(), "1.0.0"); - }); - - it("Should get the tags of the factory", async() => { - let tags = await I_VolumeRestrictionTransferManagerFactory.getTags.call(); - assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Volume"); - }); - }); - -}); diff --git a/test/x_scheduled_checkpoints.js b/test/x_scheduled_checkpoints.js new file mode 100644 index 000000000..6f0818b35 --- /dev/null +++ b/test/x_scheduled_checkpoints.js @@ -0,0 +1,399 @@ +import latestTime from "./helpers/latestTime"; +import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; +import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; +import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; +import { setUpPolymathNetwork, deployScheduleCheckpointAndVerified } from "./helpers/createInstances"; + +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const ScheduledCheckpoint = artifacts.require("./ScheduledCheckpoint.sol"); +const STGetter = artifacts.require("./STGetter.sol") + +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("ScheduledCheckpoint", async (accounts) => { + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_SecurityTokenRegistryProxy; + let I_GeneralTransferManagerFactory; + let I_ScheduledCheckpointFactory; + let I_GeneralPermissionManager; + let I_ScheduledCheckpoint; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_MRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + + before(async () => { + currentTime = new BN(await latestTime()); + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[7]; + account_investor2 = accounts[8]; + account_investor3 = accounts[9]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STRGetter, + I_STGetter + ] = instances; + + // STEP 2: Deploy the ScheduleCheckpointModule + [I_ScheduledCheckpointFactory] = await deployScheduleCheckpointAndVerified(account_polymath, I_MRProxied, 0); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + + + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + }); + + describe("Buy tokens using on-chain whitelist", async () => { + it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { + await I_SecurityToken.changeGranularity(1, { from: token_owner }); + const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); + assert.equal(tx.logs[2].args._types[1].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "ScheduledCheckpoint", + "ScheduledCheckpoint module was not added" + ); + I_ScheduledCheckpoint = await ScheduledCheckpoint.at(tx.logs[2].args._module); + }); + + let startTime; + let interval; + it("Should create a daily checkpoint", async () => { + startTime = await latestTime() + 100; + interval = 24 * 60 * 60; + console.log("Creating scheduled CP: " + startTime, interval); + await I_ScheduledCheckpoint.addSchedule(web3.utils.fromAscii("CP1"), startTime, interval, { from: token_owner }); + console.log("2: " + await latestTime()); + }); + + it("Remove (temp) daily checkpoint", async () => { + let snap_Id = await takeSnapshot(); + await I_ScheduledCheckpoint.removeSchedule(web3.utils.fromAscii("CP1"), { from: token_owner }); + await revertToSnapshot(snap_Id); + }); + + it("Should Buy the tokens for account_investor1", async () => { + // Add the Investor in to the whitelist + console.log("3: " + await latestTime()); + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor1, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 6000000 + } + ); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor1.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + // Jump time + console.log("4: " + await latestTime()); + + await increaseTime(5000); + // We should be after the first scheduled checkpoint, and before the second + console.log("5: " + await latestTime()); + + assert.isTrue(await latestTime() > startTime); + assert.isTrue(await latestTime() <= startTime + interval); + console.log("6: " + await latestTime()); + + // Mint some tokens + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Should have checkpoint created with correct balances", async () => { + let cp1 = await I_ScheduledCheckpoint.getSchedule(web3.utils.fromAscii("CP1")); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); + assert.equal((await stGetter.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 1)).toNumber(), 0); + }); + + it("Should Buy some more tokens for account_investor2", async () => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor2, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 6000000 + } + ); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor2.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(await latestTime() > startTime); + assert.isTrue(await latestTime() <= startTime + interval); + + // Mint some tokens + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("No additional checkpoints created", async () => { + let cp1 = await I_ScheduledCheckpoint.getSchedule(web3.utils.fromAscii("CP1")); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); + assert.equal((await stGetter.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 1)).toNumber(), 0); + }); + + it("Add a new token holder - account_investor3", async () => { + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor3, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer, + gas: 6000000 + } + ); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor3.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + // Jump time + await increaseTime(interval); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(await latestTime() > startTime + interval); + assert.isTrue(await latestTime() <= startTime + 2 * interval); + + // Add the Investor in to the whitelist + // Mint some tokens + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("1", "ether")), "0x0", { from: token_owner }); + + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Should have new checkpoint created with correct balances", async () => { + let cp1 = await I_ScheduledCheckpoint.getSchedule(web3.utils.fromAscii("CP1")); + checkSchedule(cp1, "CP1", startTime, startTime + 2 * interval, interval, [1, 2], [startTime, startTime + interval], [1, 1]); + assert.equal((await stGetter.balanceOfAt(account_investor3, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 2)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Should have correct balances for investor 3 after new checkpoint", async () => { + // Jump time + await increaseTime(2 * interval); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(await latestTime() > startTime + 3 * interval); + assert.isTrue(await latestTime() <= startTime + 4 * interval); + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("0.5", "ether")), { from: account_investor1 }); + let cp1 = await I_ScheduledCheckpoint.getSchedule(web3.utils.fromAscii("CP1")); + checkSchedule( + cp1, + "CP1", + startTime, + startTime + 4 * interval, + interval, + [1, 2, 3], + [startTime, startTime + interval, startTime + 2 * interval], + [1, 1, 2] + ); + assert.equal((await stGetter.balanceOfAt(account_investor3, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 2)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + + assert.equal((await stGetter.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor2, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + + assert.equal((await stGetter.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor1, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + }); + + it("Manually update checkpoints", async () => { + await increaseTime(interval); + await I_ScheduledCheckpoint.updateAll({ from: token_owner }); + + let cp1 = await I_ScheduledCheckpoint.getSchedule(web3.utils.fromAscii("CP1")); + checkSchedule( + cp1, + "CP1", + startTime, + startTime + 5 * interval, + interval, + [1, 2, 3, 4], + [startTime, startTime + interval, startTime + 2 * interval, startTime + 4 * interval], + [1, 1, 2, 1] + ); + assert.equal((await stGetter.balanceOfAt(account_investor3, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 2)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor3, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor3, 4)).toString(), new BN(web3.utils.toWei("1.5", "ether")).toString()); + + assert.equal((await stGetter.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor2, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor2, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor2, 4)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + + assert.equal((await stGetter.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await stGetter.balanceOfAt(account_investor1, 2)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor1, 3)).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); + assert.equal((await stGetter.balanceOfAt(account_investor1, 4)).toString(), new BN(web3.utils.toWei("0.5", "ether")).toString()); + }); + + it("Should get the permission", async () => { + let perm = await I_ScheduledCheckpoint.getPermissions.call(); + assert.equal(perm.length, 0); + }); + }); +}); + +function checkSchedule(schedule, name, startTime, nextTime, interval, checkpoints, timestamps, periods) { + assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ""), name); + assert.equal(schedule[1].toNumber(), startTime); + assert.equal(schedule[2].toNumber(), nextTime); + assert.equal(schedule[3].toNumber(), interval); + assert.equal(schedule[4].length, checkpoints.length); + for (let i = 0; i < checkpoints.length; i++) { + assert.equal(schedule[4][i].toNumber(), checkpoints[i]); + } + assert.equal(schedule[5].length, timestamps.length); + for (let i = 0; i < timestamps.length; i++) { + assert.equal(schedule[5][i].toNumber(), timestamps[i]); + } + assert.equal(schedule[6].length, periods.length); + for (let i = 0; i < periods.length; i++) { + assert.equal(schedule[6][i].toNumber(), periods[i]); + } +} diff --git a/test/x_single_trade_volume_restriction.js b/test/x_single_trade_volume_restriction.js deleted file mode 100644 index 15c01e68c..000000000 --- a/test/x_single_trade_volume_restriction.js +++ /dev/null @@ -1,726 +0,0 @@ -import latestTime from './helpers/latestTime'; -import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; -import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; -import { encodeModuleCall } from './helpers/encodeCall'; -import {deploySingleTradeVolumeRMAndVerified, setUpPolymathNetwork } from "./helpers/createInstances"; -import { catchRevert } from "./helpers/exceptions"; - -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const SingleTradeVolumeRestrictionManager = artifacts.require('./SingleTradeVolumeRestrictionTM'); -const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); - -const Web3 = require('web3'); -const BigNumber = require('bignumber.js'); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port - -contract('SingleTradeVolumeRestrictionManager', accounts => { - - - - // Accounts Variable declaration - let account_polymath; - let account_issuer; - let token_owner; - let account_investor1; - let account_investor2; - let account_investor3; - let account_investor4; - let account_investor5; - let zero_address = '0x0000000000000000000000000000000000000000'; - - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - - let message = "Transaction Should Fail!"; - - // Contract Instance Declaration - let I_SecurityTokenRegistryProxy - let I_GeneralTransferManagerFactory; - let I_GeneralPermissionManager; - let I_GeneralTransferManager; - let I_SingleTradeVolumeRestrictionManagerFactory; - let I_SingleTradeVolumeRestrictionManager; - let P_SingleTradeVolumeRestrictionManagerFactory; - let P_SingleTradeVolumeRestrictionManager; - let I_SingleTradeVolumeRestrictionPercentageManager; - let I_ModuleRegistry; - let I_MRProxied; - let I_ModuleRegistryProxy; - let I_FeatureRegistry; - let I_SecurityTokenRegistry; - let I_STRProxied; - let I_STFactory; - let I_SecurityToken; - let I_PolyToken; - let I_PolymathRegistry; - - // SecurityToken Details - const name = "Team"; - const symbol = "sap"; - const tokenDetails = "This is equity type of issuance"; - const decimals = 18; - const contact = "team@polymath.network"; - const STVRParameters = ["bool", "uint256", "bool"]; - - // Module key - const delegateManagerKey = 1; - const transferManagerKey = 2; - const stoKey = 3; - - // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); - - before(async () => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; - - token_owner = account_issuer; - - account_investor1 = accounts[6]; - account_investor2 = accounts[7]; - account_investor3 = accounts[8]; - account_investor4 = accounts[9]; - account_investor5 = accounts[5]; - - let instances = await setUpPolymathNetwork(account_polymath, token_owner); - - [ - I_PolymathRegistry, - I_PolyToken, - I_FeatureRegistry, - I_ModuleRegistry, - I_ModuleRegistryProxy, - I_MRProxied, - I_GeneralTransferManagerFactory, - I_STFactory, - I_SecurityTokenRegistry, - I_SecurityTokenRegistryProxy, - I_STRProxied - ] = instances; - - // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory - [I_SingleTradeVolumeRestrictionManagerFactory] = await deploySingleTradeVolumeRMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [P_SingleTradeVolumeRestrictionManagerFactory] = await deploySingleTradeVolumeRMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); - - }); - - describe("Generate the SecurityToken", async () => { - it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { - from: token_owner - }); - let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { - from: token_owner - }); - assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); - }); - - it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { - from: token_owner - }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { - from: token_owner - }); - - // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ - from: _blockNo - }), 1); - - // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._types[0].toNumber(), 2); - assert.equal( - web3.utils.toAscii(log.args._name) - .replace(/\u0000/g, ''), - "GeneralTransferManager" - ); - }); - - it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - }); - }); - // - describe("Buy tokens using whitelist & manual approvals", async () => { - - it("Should Buy the tokens", async () => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Jump time - await increaseTime(5000); - - // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { - from: token_owner - }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('100', 'ether') - ); - }); - - it("Should Buy some more tokens", async () => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { - from: token_owner - }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), - web3.utils.toWei('1', 'ether') - ); - }); - // - it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { - let managerArgs = encodeModuleCall(STVRParameters, [true, 90, false]); - - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - await catchRevert( - I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner}) - ); - }); - - it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = encodeModuleCall(STVRParameters, [false, 90, false]); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); - - let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { - from: token_owner - }); - - assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[3].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestrictionTM", - "SingleTradeVolumeRestrictionManagerFactory module was not added" - ); - P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = encodeModuleCall(STVRParameters, [false, (7 * Math.pow(10, 16)).toString(), false]) - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "TransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestrictionTM", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { - let managerArgs = encodeModuleCall(STVRParameters, [true, 90, false]); - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestrictionTM", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it('should return get permissions', async () => { - let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); - assert.equal(permissions.length, 1, "Invalid Permissions"); - assert.equal( - web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), - "ADMIN", - 'Wrong permissions' - ); - }); - - it("Should allow the primary issuance", async() => { - let snapId = await takeSnapshot(); - await I_SingleTradeVolumeRestrictionManager.setAllowPrimaryIssuance(true, {from: token_owner}); - await catchRevert( - I_SingleTradeVolumeRestrictionManager.setAllowPrimaryIssuance(true, {from: token_owner}) - ) - await revertToSnapshot(snapId); - }) - - it("add exempt wallet -- Not authorised ", async () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]) - ); - - await catchRevert( - I_SingleTradeVolumeRestrictionManager.addExemptWallet(zero_address, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); - }); - - it("Should remove an exempt wallet", async () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]) - ); - // 0 address are not allowed to add - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeExemptWallet(zero_address, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { from: token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); - }); - - it('should set transfer limit for a wallet', async () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100) - ); - - // Transfer limits can't be set to 0 - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 0, { from: token_owner }) - ); - - // Transfer limit cannot be set in percentage - await catchRevert( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100, { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[4]); - assert.equal(tx.logs[0].args._amount, 100); - - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { from: token_owner }) - ); - // Transfer limit can not be set to more 0 - await catchRevert ( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { from: token_owner }) - ); - // Transfer limit in tokens can not be set for a manager that has transfer limit set as percentage - await catchRevert ( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInTokens(accounts[4], 1, { from: token_owner }) - ); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { from: token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); - assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); - }); - - it('should remove transfer limit for wallet', async () => { - // Non Admins cannot set/remove transfer limits - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4]) - ); - - // Non Admins cannot set/remove transfer limits - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[0], { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4], { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet removed"); - }); - - it("Should pause the tranfers at Manager level", async () => { - let tx = await I_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - }); - - it('Should be able to set a global transfer limit', async () => { - // only owner is allowed - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100 * 10 ** 18) - ); - //Cannot change global limit in percentage when set to tokens - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, { from: token_owner }) - ); - // Global limit cannot be set to 0 - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { - from: token_owner - }); - assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); - - //Global limit can be set by non-admins - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89) - ); - // cannot change global limit in tokens if transfer limit is set to percentage - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, { from: token_owner }) - ); - // Cannot set global limit in tokens to 0 - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, { from: token_owner }) - ); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { from: token_owner }); - assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); - // Global limit cannot be set to more than 100 - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(101 * 10 ** 16, { from: token_owner }) - ); - // Global limit in percentage cannot be set when limit is in tokens - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(10, { from: token_owner }) - ); - // Global limit in tokens cannot be set when limit is in percentage - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { from: token_owner }) - ); - }); - - it("Should perform batch updates", async () => { - let wallets = [accounts[0], accounts[1], accounts[2]]; - let tokenLimits = [1, 2, 3]; - let percentageLimits = [5, 6, 7]; - - // Exempt wallet multi cannot be empty wallet - await catchRevert( - P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { from: token_owner }) - ); - - // add exempt wallet multi - let tx = await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti(wallets, { - from: token_owner - }); - let logs = tx.logs.filter(log => log.event === 'ExemptWalletAdded'); - assert.equal(logs.length, wallets.length, "Batch Exempt wallets not added"); - for (let i = 0; i < logs.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); - } - - // Exempt wallet multi cannot be empty wallet - await catchRevert( - P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { from: token_owner }) - ); - - // remove exempt wallet multi - tx = await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti(wallets, { - from: token_owner - }) - logs = tx.logs.filter(log => log.event === 'ExemptWalletRemoved'); - assert.equal(logs.length, wallets.length, "Batch Exempt wallets not removed"); - - for (let i = 0; i < logs.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); - } - // wallets cannot be empty - await catchRevert( - P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([], tokenLimits, { from: token_owner }) - ); - // wallet array length dont match - await catchRevert( - P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([accounts[0]], tokenLimits, { from: token_owner }) - ); - - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti(wallets, tokenLimits, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event == 'TransferLimitInTokensSet'); - assert.equal(wallets.length, logs.length, "Transfer limit not set"); - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not set for wallet"); - assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); - } - // Wallets cannot be empty - await catchRevert( - P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti([], { from: token_owner }) - ); - tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti(wallets, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event === 'TransferLimitInTokensRemoved'); - assert.equal(logs.length, wallets.length, "Transfer limit not removed"); - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); - } - // wallets cannot be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { from: token_owner }) - ); - // wallets and amounts dont match be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { from: token_owner }) - ); - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageSet'); - assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); - - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); - assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); - } - // Wallets cannot be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti([], { from: token_owner }) - ); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti(wallets, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageRemoved'); - assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); - - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); - } - // Wallet should not be removed - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentage(wallets[0], { from: token_owner }) - ); - }) - - it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { - await I_SingleTradeVolumeRestrictionManager.unpause({ - from: token_owner - }) - await I_SingleTradeVolumeRestrictionPercentageManager.pause({ - from: token_owner - }) - await P_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer - } - ); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor4, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer - } - ); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor5, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer - } - ); - - - //setting a max of 5 tokens - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), { - from: token_owner - }) - // Transfer should have not happened - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) - ); - - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); - - // exempt wallet - await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { - from: token_owner - }); - await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); - - //special limits wallet - await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(account_investor5, web3.utils.toWei('5', 'ether'), { - from: token_owner - }); - - // Transfer should have not happened - await catchRevert( - I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), { from: account_investor5 }) - ); - - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { - from: account_investor5 - }) - assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) - }) - - it('should be able to transfer tokens (percentage transfer limit)', async () => { - await I_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor2 - }); - - - balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); - - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor3 - }); - - - balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor4 - }); - - balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor5 - }); - - await I_SingleTradeVolumeRestrictionPercentageManager.unpause({ - from: token_owner - }); - // // - await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, { - from: token_owner - }); - - // Transfer above limit happened - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), { from: account_investor1 }) - ); - - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) - - await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, { - from: token_owner - }); - // transfer happened above limit - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), { from: account_investor1 }) - ); - - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); - }); - - it('should change transfer limits to tokens', async () => { - // Should not change to percentage again - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { from: token_owner }) - ); - - - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToTokens(1, { - from: token_owner - }); - assert.equal(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), false, "Error Changing"); - assert.equal(tx.logs[0].args._amount.toNumber(), 1, "Transfer limit not changed"); - }) - - it('should change transfer limits to percentage', async () => { - // Should not change to tokens again - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeTransferLimitToTokens(1, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { - from: token_owner - }); - assert.ok(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), "Error Changing"); - assert.equal(tx.logs[0].args._percentage.toNumber(), 1, "Transfer limit not changed"); - }) - - - - }); - - describe("SingleTradeVolumeRestrictionManager Factory test cases", async () => { - - it("Should get the exact details of the factory", async () => { - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getSetupCost.call(), 0); - assert.equal((await I_SingleTradeVolumeRestrictionManagerFactory.getTypes.call())[0], 2); - let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); - assert.equal(name, "SingleTradeVolumeRestrictionTM", "Wrong Module added"); - let desc = await I_SingleTradeVolumeRestrictionManagerFactory.description.call(); - assert.equal(desc, "Imposes volume restriction on a single trade", "Wrong Module added"); - let title = await I_SingleTradeVolumeRestrictionManagerFactory.title.call(); - assert.equal(title, "Single Trade Volume Restriction Manager", "Wrong Module added"); - let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); - assert.equal(inst, "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens", "Wrong Module added"); - let version = await I_SingleTradeVolumeRestrictionManagerFactory.version.call(); - assert.equal(version, "1.0.0", "Version not correct"); - }); - - it("Should get the tags of the factory", async () => { - let tags = await I_SingleTradeVolumeRestrictionManagerFactory.getTags.call(); - assert.equal(web3.utils.toUtf8(tags[0]), "Single Trade"); - assert.equal(web3.utils.toUtf8(tags[1]), "Transfer"); - assert.equal(web3.utils.toUtf8(tags[2]), "Volume"); - }); - - - }); -}); diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js deleted file mode 100644 index 5fcc03a74..000000000 --- a/test/y_scheduled_checkpoints.js +++ /dev/null @@ -1,389 +0,0 @@ -import latestTime from './helpers/latestTime'; -import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; -import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; -import { encodeProxyCall, encodeModuleCall } from './helpers/encodeCall'; -import { setUpPolymathNetwork, deployScheduleCheckpointAndVerified } from "./helpers/createInstances"; - -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const ScheduledCheckpoint = artifacts.require('./ScheduledCheckpoint.sol'); - - -const Web3 = require('web3'); -const BigNumber = require('bignumber.js'); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port - -contract('ScheduledCheckpoint', accounts => { - - // Accounts Variable declaration - let account_polymath; - let account_issuer; - let token_owner; - let account_investor1; - let account_investor2; - let account_investor3; - let account_investor4; - - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - - let message = "Transaction Should Fail!"; - - // Contract Instance Declaration - let I_SecurityTokenRegistryProxy; - let I_GeneralTransferManagerFactory; - let I_ScheduledCheckpointFactory; - let I_GeneralPermissionManager; - let I_ScheduledCheckpoint; - let I_GeneralTransferManager; - let I_ModuleRegistryProxy; - let I_ModuleRegistry; - let I_FeatureRegistry; - let I_SecurityTokenRegistry; - let I_STRProxied; - let I_MRProxied; - let I_STFactory; - let I_SecurityToken; - let I_PolyToken; - let I_PolymathRegistry; - - // SecurityToken Details - const name = "Team"; - const symbol = "sap"; - const tokenDetails = "This is equity type of issuance"; - const decimals = 18; - const contact = "team@polymath.network"; - - // Module key - const delegateManagerKey = 1; - const transferManagerKey = 2; - const stoKey = 3; - - // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); - - before(async() => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; - - token_owner = account_issuer; - - account_investor1 = accounts[7]; - account_investor2 = accounts[8]; - account_investor3 = accounts[9]; - - // Step 1: Deploy the genral PM ecosystem - let instances = await setUpPolymathNetwork(account_polymath, token_owner); - - [ - I_PolymathRegistry, - I_PolyToken, - I_FeatureRegistry, - I_ModuleRegistry, - I_ModuleRegistryProxy, - I_MRProxied, - I_GeneralTransferManagerFactory, - I_STFactory, - I_SecurityTokenRegistry, - I_SecurityTokenRegistryProxy, - I_STRProxied - ] = instances; - - // STEP 2: Deploy the ScheduleCheckpointModule - [I_ScheduledCheckpointFactory] = await deployScheduleCheckpointAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); - - // Printing all the contract addresses - console.log(` - --------------------- Polymath Network Smart Contracts: --------------------- - PolymathRegistry: ${I_PolymathRegistry.address} - SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} - SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} - ModuleRegistry: ${I_ModuleRegistry.address} - ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} - FeatureRegistry: ${I_FeatureRegistry.address} - - STFactory: ${I_STFactory.address} - GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} - - - ----------------------------------------------------------------------------- - `); - }); - - describe("Generate the SecurityToken", async() => { - - it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); - assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); - }); - - it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); - - // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({from: _blockNo}), 1); - - // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._types[0].toNumber(), 2); - assert.equal( - web3.utils.toAscii(log.args._name) - .replace(/\u0000/g, ''), - "GeneralTransferManager" - ); - }); - - it("Should intialize the auto attached modules", async () => { - let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - - }); - - }); - - describe("Buy tokens using on-chain whitelist", async() => { - - it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { - await I_SecurityToken.changeGranularity(1, {from: token_owner}); - const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, "", 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); - assert.equal(tx.logs[2].args._types[1].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "ScheduledCheckpoint", - "ScheduledCheckpoint module was not added" - ); - I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); - }); - - let startTime; - let interval; - it("Should create a daily checkpoint", async () => { - startTime = latestTime() + 100; - interval = 24 * 60 * 60; - console.log("Creating scheduled CP: " + startTime, interval); - await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, {from: token_owner}); - console.log("2: " + latestTime()); - }); - - it("Remove (temp) daily checkpoint", async () => { - let snap_Id = await takeSnapshot(); - await I_ScheduledCheckpoint.removeSchedule("CP1", {from: token_owner}); - await revertToSnapshot(snap_Id); - }); - - it("Should Buy the tokens for account_investor1", async() => { - // Add the Investor in to the whitelist - console.log("3: " + latestTime()); - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Jump time - console.log("4: " + latestTime()); - - await increaseTime(5000); - // We should be after the first scheduled checkpoint, and before the second - console.log("5: " + latestTime()); - - assert.isTrue(latestTime() > startTime); - assert.isTrue(latestTime() <= startTime + interval); - console.log("6: " + latestTime()); - - // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('1', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('1', 'ether') - ); - }); - - it("Should have checkpoint created with correct balances", async() => { - let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); - }); - - it("Should Buy some more tokens for account_investor2", async() => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); - - // We should be after the first scheduled checkpoint, and before the second - assert.isTrue(latestTime() > startTime); - assert.isTrue(latestTime() <= startTime + interval); - - // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), - web3.utils.toWei('1', 'ether') - ); - }); - - it("No additional checkpoints created", async() => { - let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); - }); - - it("Add a new token holder - account_investor3", async() => { - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Jump time - await increaseTime(interval); - // We should be after the first scheduled checkpoint, and before the second - assert.isTrue(latestTime() > startTime + interval); - assert.isTrue(latestTime() <= startTime + (2 * interval)); - - // Add the Investor in to the whitelist - // Mint some tokens - await I_SecurityToken.mint(account_investor3, web3.utils.toWei('1', 'ether'), { from: token_owner }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), - web3.utils.toWei('1', 'ether') - ); - }); - - it("Should have new checkpoint created with correct balances", async() => { - let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (2 * interval), interval, [1, 2], [startTime, startTime + interval], [1, 1]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - }); - - it("Should have correct balances for investor 3 after new checkpoint", async() => { - // Jump time - await increaseTime(2 * interval); - // We should be after the first scheduled checkpoint, and before the second - assert.isTrue(latestTime() > startTime + (3 * interval)); - assert.isTrue(latestTime() <= startTime + (4 * interval)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('0.5', 'ether'), { from: account_investor1 }); - let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (4 * interval), interval, [1, 2, 3], [startTime, startTime + interval, startTime + (2 * interval)], [1, 1, 2]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - - }); - - it("Manually update checkpoints", async() => { - await increaseTime(interval); - await I_ScheduledCheckpoint.updateAll({from: token_owner}); - - let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (5 * interval), interval, [1, 2, 3, 4], [startTime, startTime + interval, startTime + (2 * interval), startTime + (4 * interval)], [1, 1, 2, 1]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 4)).toNumber(), web3.utils.toWei('1.5', 'ether')); - - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 4)).toNumber(), web3.utils.toWei('1', 'ether')); - - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 3)).toNumber(), web3.utils.toWei('1', 'ether')); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 4)).toNumber(), web3.utils.toWei('0.5', 'ether')); - - }); - - it("Should get the permission", async() => { - let perm = await I_ScheduledCheckpoint.getPermissions.call(); - assert.equal(perm.length, 0); - }); - - }); - -}); - -function checkSchedule(schedule, name, startTime, nextTime, interval, checkpoints, timestamps, periods) { - assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); - assert.equal(schedule[1].toNumber(), startTime); - assert.equal(schedule[2].toNumber(), nextTime); - assert.equal(schedule[3].toNumber(), interval); - assert.equal(schedule[4].length, checkpoints.length); - for (let i = 0; i < checkpoints.length; i++) { - assert.equal(schedule[4][i].toNumber(), checkpoints[i]); - } - assert.equal(schedule[5].length, timestamps.length); - for (let i = 0; i < timestamps.length; i++) { - assert.equal(schedule[5][i].toNumber(), timestamps[i]); - } - assert.equal(schedule[6].length, periods.length); - for (let i = 0; i < periods.length; i++) { - assert.equal(schedule[6][i].toNumber(), periods[i]); - } -} diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js new file mode 100644 index 000000000..e915aaa16 --- /dev/null +++ b/test/y_volume_restriction_tm.js @@ -0,0 +1,1699 @@ +import latestTime from './helpers/latestTime'; +import {signData} from './helpers/signData'; +import { pk } from './helpers/testprivateKey'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, deployVRTMAndVerifyed } from "./helpers/createInstances"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager.sol'); +const VolumeRestrictionTM = artifacts.require('./VolumeRestrictionTM.sol'); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require('web3'); +const BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('VolumeRestrictionTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + let account_delegate3; + // investor Details + let fromTime; + let toTime; + let expiryTime; + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_VolumeRestrictionTMFactory; + let P_VolumeRestrictionTMFactory; + let I_SecurityTokenRegistryProxy; + let P_VolumeRestrictionTM; + let I_GeneralTransferManagerFactory; + let I_VolumeRestrictionTM; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = web3.utils.toHex("Hello I am legit delegate"); + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + let tempAmount = new BN(0); + let tempArray = new Array(); + let tempArray3 = new Array(); + let tempArrayGlobal = new Array(); + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + const address_zero = "0x0000000000000000000000000000000000000000"; + + async function print(data, account) { + console.log(` + Latest timestamp: ${data[0].toString()} + SumOfLastPeriod: ${web3.utils.fromWei(data[1]).toString()} + Days Covered: ${data[2].toString()} + Latest timestamp daily: ${data[3].toString()} + Individual Total Trade on latestTimestamp : ${web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[0])) + .toString()} + Individual Total Trade on daily latestTimestamp : ${web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[3])) + .toString()} + `) + } + + async function getLatestTime() { + return new BN(await latestTime()); + } + + async function printRestrictedData(data) { + let investors = data[0]; + for (let i = 0 ; i < investors.length; i++) { + console.log(` + Token holder: ${data[0][i]} + Start Time: ${data[2][i].toString()} + Rolling Period In Days: ${data[3][i].toString()} + End Time : ${data[4][i].toString()} + Allowed Tokens: ${web3.utils.fromWei(data[1][i].toString())} + Type of Restriction: ${data[5][i].toString()} + `) + } + } + + async function calculateSum(rollingPeriod, tempArray) { + let sum = 0; + let start = 0; + if (tempArray.length >= rollingPeriod) + start = tempArray.length - rollingPeriod; + for (let i = start; i < tempArray.length; i++) { + sum += tempArray[i]; + } + return sum; + } + + before(async() => { + let newLatestTime = await getLatestTime(); + fromTime = newLatestTime; + toTime = newLatestTime; + expiryTime = toTime.add(new BN(duration.days(15))); + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[4]; + account_investor4 = accounts[3]; + account_delegate = accounts[7]; + account_delegate2 = accounts[6]; + account_delegate3 = accounts[5]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 5: Deploy the VolumeRestrictionTMFactory + [I_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, 0); + // STEP 6: Deploy the VolumeRestrictionTMFactory + [P_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + VolumeRestrictionTMFactory: ${I_VolumeRestrictionTMFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, true, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + }); + + describe("Attach the VRTM", async() => { + it("Deploy the VRTM and attach with the ST", async()=> { + let tx = await I_SecurityToken.addModule(I_VolumeRestrictionTMFactory.address, "0x0", new BN(0), new BN(0), {from: token_owner }); + assert.equal(tx.logs[2].args._moduleFactory, I_VolumeRestrictionTMFactory.address); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "VolumeRestrictionTM", + "VolumeRestrictionTMFactory doesn not added"); + I_VolumeRestrictionTM = await VolumeRestrictionTM.at(tx.logs[2].args._module); + }); + + it("Transfer some tokens to different account", async() => { + // Add tokens in to the whitelist + let newLatestTime = await getLatestTime(); + await I_GeneralTransferManager.modifyKYCDataMulti( + [account_investor1, account_investor2, account_investor3], + [newLatestTime, newLatestTime, newLatestTime], + [newLatestTime, newLatestTime, newLatestTime], + [newLatestTime.add(new BN(duration.days(60))), newLatestTime.add(new BN(duration.days(60))), newLatestTime.add(new BN(duration.days(60)))], + { + from: token_owner + } + ); + + // Mint some tokens and transferred to whitelisted addresses + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("40", "ether")), "0x0", {from: token_owner}); + await I_SecurityToken.issue(account_investor2, new BN(web3.utils.toWei("30", "ether")), "0x0", {from: token_owner}); + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("30", "ether")), "0x0", {from: token_owner}); + + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + let bal2 = await I_SecurityToken.balanceOf.call(account_investor2); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toString()).toString()), 40); + assert.equal(web3.utils.fromWei((bal2.toString()).toString()), 30); + + }); + + it("Should transfer the tokens freely without any restriction", async() => { + console.log( + await I_SecurityToken.canTransfer.call(account_investor3, new BN(web3.utils.toWei('5', 'ether')), "0x0", {from: account_investor1}) + ) + console.log(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor1)).toString())); + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei('5', 'ether')), { from: account_investor1 }); + let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toString()).toString()), 35); + }); + }) + + describe("Test for the addIndividualRestriction", async() => { + it("Should add the restriction -- failed because of bad owner", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("12")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: account_polymath + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid restriction type", async() => { + let newLatestTime = await getLatestTime(); + + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("12")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 3, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Invalid value of allowed tokens", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + 0, + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Percentage of tokens not within (0,100]", async() => { + let newLatestTime = await getLatestTime(); + + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + 0, + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 1, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Percentage of tokens not within (0,100]", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 1, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid dates", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.sub(new BN(duration.seconds(5))), + 3, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid dates", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.add(new BN(duration.days(2))), + 3, + newLatestTime.add(new BN(duration.days(1))), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.add(new BN(duration.days(2))), + 0, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.add(new BN(duration.days(2))), + 366, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("10")), + newLatestTime.add(new BN(duration.days(2))), + 3, + newLatestTime.add(new BN(duration.days(3))), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction succesfully", async() => { + let newLatestTime = await getLatestTime(); + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("12")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(5))), + 0, + { + from: token_owner + } + ); + assert.equal(tx.logs[0].args._holder, account_investor1); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0][0], account_investor1); + }); + + it("Should add the restriction for multiple investor -- failed because of bad owner", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3,4,5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: account_polymath + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3,4,5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3,4,5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: account_polymath + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3,4,5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3, 4, 5], + [newLatestTime.add(new BN(duration.days(5)))], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2))), newLatestTime.add(new BN(duration.seconds(2)))], + [3, 4, 5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor successfully", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [new BN(web3.utils.toWei("12")), new BN(web3.utils.toWei("10")), new BN(web3.utils.toWei("15"))], + [0, 0, 0], + [3, 4, 5], + [newLatestTime.add(new BN(duration.days(5))), newLatestTime.add(new BN(duration.days(6))), newLatestTime.add(new BN(duration.days(7)))], + [0,0,0], + { + from: token_owner + } + ); + assert.equal((await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor2))[2].toString(), 3); + assert.equal((await I_VolumeRestrictionTM.getIndividualRestriction.call(account_delegate3))[2].toString(), 4); + assert.equal((await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor4))[2].toString(), 5); + + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 4); + }); + + it("Should remove the restriction multi -- failed because of address is 0", async() => { + await catchRevert( + I_VolumeRestrictionTM.removeIndividualRestrictionMulti( + [address_zero, account_delegate3, account_investor4], + { + from: token_owner + } + ) + ); + }); + + it("Should successfully remove the restriction", async() => { + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor2, {from: token_owner}); + assert.equal((await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor2))[3].toString(), 0); + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 3); + for (let i = 0; i < data[0].length; i++) { + assert.notEqual(data[0][i], account_investor2); + } + }); + + it("Should remove the restriction -- failed because restriction not present anymore", async() => { + await catchRevert( + I_VolumeRestrictionTM.removeIndividualRestriction(account_investor2, {from: token_owner}) + ); + }); + + it("Should remove the restriction multi", async() => { + await I_VolumeRestrictionTM.removeIndividualRestrictionMulti( + [account_delegate3, account_investor4], + { + from: token_owner + } + ) + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 1); + }); + + it("Should add the restriction succesfully after the expiry of previous one for investor 1", async() => { + await increaseTime(duration.days(5.1)); + let newLatestTime = await getLatestTime(); + console.log( + `Estimated gas for addIndividualRestriction: + ${await I_VolumeRestrictionTM.addIndividualRestriction.estimateGas( + account_investor1, + new BN(web3.utils.toWei("12")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(6))), + 0, + { + from: token_owner + } + )} + `); + newLatestTime = await getLatestTime(); + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + new BN(web3.utils.toWei("12")), + newLatestTime.add(new BN(duration.seconds(2))), + 3, + newLatestTime.add(new BN(duration.days(6))), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[1].args._holder, account_investor1); + assert.equal(tx.logs[1].args._typeOfRestriction, 0); + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 1); + assert.equal(data[0][0], account_investor1); + }); + + it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { + await increaseTime(duration.seconds(10)); + await catchRevert( + I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("13")), { from: account_investor1}) + ); + }); + + it("Should succesfully transact the tokens by investor 1 just after the startTime", async() => { + // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false + let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, new BN(web3.utils.toWei('.3', "ether")), "0x0"); + assert.equal(result[0].toString(), 1); + // Perform the transaction + console.log(` + Gas estimation (Individual): ${await I_SecurityToken.transfer.estimateGas(account_investor3, new BN(web3.utils.toWei('.3', "ether")), {from: account_investor1})}` + ); + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei('.3')), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toString()).toString()), 34.7); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor1); + await print(data, account_investor1); + assert.equal( + web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor1, data[0])), + 0.3 + ); + assert.equal( + data[0].toString(), + (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor1))[1].toString() + ); + assert.equal(web3.utils.fromWei(data[1].toString()), 0.3); + tempArray.push(0.3); + }); + + it("Should fail to add the individual daily restriction -- Bad msg.sender", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei("6")), + newLatestTime.add(new BN(duration.seconds(1))), + newLatestTime.add(new BN(duration.days(4))), + 0, + { + from: account_investor1 + } + ) + ); + }) + + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei("6")), + newLatestTime.add(new BN(duration.seconds(1))), + newLatestTime.add(new BN(duration.days(4))), + 1, + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + 0, + newLatestTime.add(new BN(duration.seconds(1))), + newLatestTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei("6")), + newLatestTime.add(new BN(duration.days(5))), + newLatestTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the individual daily restriction for investor 3", async() => { + let newLatestTime = await getLatestTime(); + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei("6")), + newLatestTime.add(new BN(duration.seconds(1))), + newLatestTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + assert.equal((tx.logs[0].args._allowedTokens).toString(), new BN(web3.utils.toWei("6"))); + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 2); + assert.equal(data[0][1], account_investor3); + let dataRestriction = await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3); + console.log(` + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(18))).toString()} + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should transfer the tokens within the individual daily restriction limits", async() => { + // transfer 2 tokens as per the limit + await increaseTime(5); // increase 5 seconds to layoff the time gap + let startTime = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3})} + `) + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3}); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + await increaseTime(duration.minutes(15)); + + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor3})} + `) + // transfer the 4 tokens which is under the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor3}); + let newData = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(newData, account_investor3); + + assert.equal(newData[3].toString(), data[3].toString()); + assert.equal(data[3].toString(), startTime); + assert.equal(web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[3])) + , 6); + }); + + it("Should fail to transfer more tokens --because of the above limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei(".1")), {from: account_investor3}) + ); + }); + + it("Should try to send after the one day completion", async() => { + // increase the EVM time by one day + await increaseTime(duration.days(1)); + + let startTime = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3})} + `) + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3}); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + assert.equal(data[3].toString(), new BN(startTime).add(new BN(duration.days(1)))); + assert.equal(web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[3])) + , 2); + }); + + it("Should add the daily restriction on the investor 1", async() => { + let newLatestTime = await getLatestTime(); + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + new BN(5).mul(new BN(10).pow(new BN(16))), + 0, + newLatestTime.add(new BN(duration.days(4))), + 1, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor1); + assert.equal((tx.logs[0].args._typeOfRestriction).toString(), 1); + assert.equal(web3.utils.fromWei(new BN(tx.logs[0].args._allowedTokens)), 0.05); + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 3); + assert.equal(data[0][2], account_investor3); + assert.equal(data[0][0], account_investor1); + let dataRestriction = await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor1); + console.log(` + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(16))).toString()} % of TotalSupply + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should transfer tokens on the 2nd day by investor1 (Individual + Individual daily)", async() => { + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor1))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor1))[2].toString(); + + console.log(` + Gas estimation (Individual + Individual daily): ${await I_SecurityToken.transfer.estimateGas(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor1})}` + ); + + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei(bal1.toString()), 32.7); + tempArray.push(2); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor1); + await print(data, account_investor1); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor1, data[0])); + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray)); + assert.equal(data[2].toString(), 1); + assert.equal(data[3].toString(), + (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor1))[1].toString()); + assert.equal(amt, 2); + }); + + it("Should fail to transfer by investor 1 -- because voilating the individual daily", async() => { + // transfer 4 tokens -- voilate the daily restriction + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor1}) + ); + }); + + it("Should add the individual restriction to investor 3", async() => { + let newLatestTime = await getLatestTime(); + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor3, + new BN(1536).mul(new BN(10).pow(new BN(14))), // 15.36 tokens as totalsupply is 1000 + newLatestTime.add(new BN(duration.seconds(2))), + 6, + newLatestTime.add(new BN(duration.days(15))), + 1, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 1); + + let data = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(data); + assert.equal(data[0].length, 4); + assert.equal(data[0][2], account_investor3); + assert.equal(data[0][0], account_investor1); + }); + + it("Should transfer the token by the investor 3 with in the (Individual + Individual daily limit)", async() => { + await increaseTime(4); + // Allowed 4 tokens to transfer + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + let startTimeDaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + console.log(` + Gas estimation (Individual + Individual daily): ${await I_SecurityToken.transfer.estimateGas(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor3})}` + ); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor3}); + tempArray3.push(4); + // Check the balance of the investors + let bal2 = await I_SecurityToken.balanceOf.call(account_investor3); + // Verifying the balances + assert.equal(web3.utils.fromWei(((bal1.sub(bal2)).toString()).toString()), 4); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())) + .div(new BN(10).pow(new BN(18))).toString(); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), 4); + assert.equal(data[2].toString(), 0); + assert.equal(data[3].toString(), new BN(startTimeDaily).add(new BN(duration.days(1)))); + assert.equal(amt, 4); + }); + + it("Should fail during transferring more tokens by investor3 -- Voilating the daily Limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1")), {from: account_investor3}) + ); + }); + + it("Should remove the daily individual limit and transfer more tokens on a same day -- failed because of bad owner", async() => { + // remove the Individual daily restriction + await catchRevert( + I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor3, {from: account_investor4}) + ); + }) + + it("Should remove the daily individual limit and transfer more tokens on a same day", async() => { + // remove the Individual daily restriction + let tx = await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor3, {from: token_owner}); + assert.equal(tx.logs[0].args._holder, account_investor3); + let dataAdd = await I_VolumeRestrictionTM.getRestrictionData.call(); + await printRestrictedData(dataAdd); + assert.equal(dataAdd[0].length, 3); + assert.equal(dataAdd[0][0], account_investor1); + assert.equal(dataAdd[0][2], account_investor3); + + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + + // transfer more tokens on the same day + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("4")), {from: account_investor3}); + tempArray3[tempArray3.length -1] += 4; + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())) + .div(new BN(10).pow(new BN(18))).toString(); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), 8); + assert.equal(data[2].toString(), 0); + assert.equal(data[3].toString(), 0); + assert.equal(amt, 8); + }); + + it("Should add the new Individual daily restriction and transact the tokens", async() => { + let newLatestTime = await getLatestTime(); + // add new restriction + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei("2")), + newLatestTime.add(new BN(duration.days(1))), + newLatestTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + assert.equal((tx.logs[0].args._allowedTokens).toString(), new BN(web3.utils.toWei("2"))); + let dataRestriction = await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3); + console.log(` + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(18))).toString()} + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + // Increase the time by one day + await increaseTime(duration.days(1.1)); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3}); + tempArray3.push(2); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())) + .div(new BN(10).pow(new BN(18))).toString(); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 1); + assert.equal(data[3].toString(), dataRestriction[1].toString()); + assert.equal(amt, 2); + + // Fail to sell more tokens than the limit + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3}) + ); + }); + + it("Should fail to modify the individual daily restriction -- bad owner", async() => { + let newLatestTime = await getLatestTime(); + await catchRevert( + I_VolumeRestrictionTM.modifyIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei('3')), + newLatestTime, + newLatestTime.add(new BN(duration.days(5))), + 0, + { + from: account_polymath + } + ) + ); + }); + + it("Should modify the individual daily restriction", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.modifyIndividualDailyRestriction( + account_investor3, + new BN(web3.utils.toWei('3')), + 0, + newLatestTime.add(new BN(duration.days(5))), + 0, + { + from: token_owner + } + ); + + let dataRestriction = await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3); + console.log(` + *** Modify Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(18))).toString()} + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should allow to sell to transfer more tokens by investor3", async() => { + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("3")), {from: account_investor3}); + tempArray3[tempArray3.length -1] += 3; + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 1); + assert.equal(data[3].toString(), startTimedaily); + assert.equal(amt, 5); + }); + + it("Should allow to transact the tokens on the other day", async() => { + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + + await increaseTime(duration.days(1.1)); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2.36")), {from: account_investor3}); + tempArray3.push(2.36); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 2); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1)))); + assert.equal(amt, 2.36); + }); + + it("Should fail to transfer the tokens after completion of the total amount", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("0.3")), {from: account_investor3}) + ); + }) + + it("Should sell more tokens on the same day after changing the total supply", async() => { + await I_SecurityToken.issue(account_investor3, new BN(web3.utils.toWei("10")), "0x0", {from: token_owner}); + + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei(".50")), {from: account_investor3}); + tempArray3[tempArray3.length -1] += .50; + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 2); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1)))); + assert.equal(amt, 2.86); + }); + + it("Should fail to transact tokens more than the allowed in the second rolling period", async() => { + let newLatestTime = await getLatestTime(); + await increaseTime(duration.days(4)); + let i + for (i = 0; i < 3; i++) { + tempArray3.push(0); + } + console.log(`Diff Days: ${(newLatestTime - ((await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3))[0]).toString()) / 86400}`); + let allowedAmount = (tempArray3[0] + 1.1); + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei(allowedAmount.toString())), {from: account_investor3}) + ); + }) + + it("Should successfully to transact tokens in the second rolling period", async() => { + // Should transact freely tokens daily limit is also ended + + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + let allowedAmount = (tempArray3[0] + 1); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei(allowedAmount.toString())), {from: account_investor3}); + + tempArray3.push(allowedAmount); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 6); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1))).toString()); + assert.equal(amt, allowedAmount); + }); + + it("Should sell more tokens on the net day of rolling period", async() => { + await increaseTime(duration.days(3)); + + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + + tempArray3.push(0); + tempArray3.push(0); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("7")), {from: account_investor3}); + + tempArray3.push(7) + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 9); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1)))); + assert.equal(amt, 7); + }) + + it("Should transfer after the 5 days", async() => { + await increaseTime(duration.days(4.5)); + + for (let i = 0; i <3; i++) { + tempArray3.push(0); + } + + let startTime = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getIndividualDailyRestriction.call(account_investor3))[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3))[2].toString(); + + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("25")), {from: account_investor2}); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("8")), {from: account_investor3}); + tempArray3.push(8); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 13); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1)))); + assert.equal(amt, 8); + }); + + it("Should freely transfer the tokens after one day (completion of individual restriction)", async() => { + // increase one time + await increaseTime(duration.days(2)); + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("17")), {from: account_investor3}); + }); + }); + + describe("Test cases for the Default restrictions", async() => { + + it("Should add the investor 4 in the whitelist", async() => { + let newLatestTime = await getLatestTime(); + await I_GeneralTransferManager.modifyKYCData( + account_investor4, + newLatestTime, + newLatestTime, + newLatestTime.add(new BN(duration.days(30))), + { + from: token_owner + } + ); + }); + + it("Should issue some tokens to investor 4", async() => { + await I_SecurityToken.issue(account_investor4, new BN(web3.utils.toWei("20")), "0x0", {from: token_owner}); + }); + + it("Should add the default daily restriction successfully", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.addDefaultDailyRestriction( + new BN(275).mul(new BN(10).pow(new BN(14))), + 0, + newLatestTime.add(new BN(duration.days(3))), + 1, + { + from: token_owner + } + ); + + let dataRestriction = await I_VolumeRestrictionTM.getDefaultDailyRestriction.call(); + console.log(` + *** Add Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(16))).toString()} % of TotalSupply + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should fail to transfer above the daily limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("5")), {from: account_investor4}) + ) + }) + + it("Should transfer the token by investor 4", async() => { + let startTimedaily = (await I_VolumeRestrictionTM.getDefaultDailyRestriction.call())[1].toString(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("3.57")), {from: account_investor4}); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor4); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor4, data[3].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), 0); + assert.equal(data[1].toString(), 0); + assert.equal(data[2].toString(), 0); + assert.equal(data[3].toString(), startTimedaily); + assert.equal(amt, 3.57); + }); + + it("Should transfer the tokens freely after ending the default daily restriction", async() => { + await increaseTime(duration.days(3) + 10); + //sell tokens upto the limit + let tx = await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("5")), {from: account_investor4}); + assert.equal((tx.logs[0].args.value).toString(), new BN(web3.utils.toWei("5"))); + // Transfer the tokens again to investor 3 + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("40")), {from: account_investor2}); + }) + + it("Should successfully add the default restriction", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.addDefaultRestriction( + new BN(web3.utils.toWei("10")), + 0, + 5, + newLatestTime.add(new BN(duration.days(10))), + 0, + { + from: token_owner + } + ); + + let data = await I_VolumeRestrictionTM.getDefaultRestriction.call(); + assert.equal(data[0].toString(), new BN(web3.utils.toWei("10"))); + assert.equal(data[2].toString(), 5); + let dataRestriction = await I_VolumeRestrictionTM.getDefaultRestriction.call(); + console.log(` + *** Add Individual restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(18))).toString()} + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should transfer tokens on by investor 3 (comes under the Default restriction)", async() => { + await increaseTime(10); + tempArray3.length = 0; + let startTime = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getDefaultDailyRestriction.call())[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[2].toString(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("5")), {from: account_investor3}); + tempArray3.push(5); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 0); + assert.equal(data[3].toString(), 0); + assert.equal(amt, 5); + + // Transfer tokens on another day + await increaseTime(duration.days(1)); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("3")), {from: account_investor3}); + tempArray3.push(3); + + data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 1); + assert.equal(data[3].toString(), 0); + assert.equal(amt, 3); + }); + + it("Should fail to transfer more tokens than the available default limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("3")), {from: account_investor3}) + ); + }); + + it("Should able to transfer tokens in the next rolling period", async() => { + let newLatestTime = await getLatestTime(); + await increaseTime(duration.days(4.1)); + console.log(`*** Diff days: ${(newLatestTime - ((await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3))[0]).toString()) / 86400}`) + for (let i = 0; i < 3; i++) { + tempArray3.push(0); + } + + let startTime = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getDefaultDailyRestriction.call())[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[2].toString(); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("7")), {from: account_investor3}); + tempArray3.push(7); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 5); + assert.equal(data[3].toString(), 0); + assert.equal(amt, 7); + + // Try to transact more on the same day but fail + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("1")), {from: account_investor3}) + ); + }); + + it("Should add the daily default restriction again", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.addDefaultDailyRestriction( + new BN(web3.utils.toWei("2")), + 0, + newLatestTime.add(new BN(duration.days(3))), + 0, + { + from: token_owner + } + ); + + let dataRestriction = await I_VolumeRestrictionTM.getDefaultDailyRestriction.call(); + console.log(` + *** Add Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].div(new BN(10).pow(new BN(16))).toString()} + StartTime : ${dataRestriction[1].toString()} + Rolling Period in days : ${dataRestriction[2].toString()} + EndTime : ${dataRestriction[3].toString()} + Type of Restriction: ${dataRestriction[4].toString()} + `); + }); + + it("Should not able to transfer tokens more than the default daily restriction", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("3")), {from: account_investor3}) + ); + }); + + it("Should able to transfer tokens within the limit of (daily default + default) restriction", async() => { + await increaseTime(duration.days(1)); + let startTime = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[1].toString(); + let startTimedaily = (await I_VolumeRestrictionTM.getDefaultDailyRestriction.call())[1].toString(); + let rollingPeriod = (await I_VolumeRestrictionTM.getDefaultRestriction.call())[2].toString(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, new BN(web3.utils.toWei("2")), {from: account_investor3}); + tempArray3.push(2); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = web3.utils.fromWei(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toString())); + + // Verify the storage changes + assert.equal(data[0].toString(), new BN(startTime).add(new BN(duration.days(data[2].toString())))); + assert.equal(web3.utils.fromWei(data[1]), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toString(), 6); + assert.equal(data[3].toString(), new BN(startTimedaily).add(new BN(duration.days(1)))); + assert.equal(amt, 2); + }); + }) + + describe("Test for the exemptlist", async() => { + + it("Should add the token holder in the exemption list -- failed because of bad owner", async() => { + await catchRevert( + I_VolumeRestrictionTM.changeExemptWalletList(account_investor4, true, {from: account_polymath}) + ); + }); + + it("Should add the token holder in the exemption list", async() => { + await I_VolumeRestrictionTM.changeExemptWalletList(account_investor4, true, {from: token_owner}); + console.log(await I_VolumeRestrictionTM.getExemptAddress.call()); + let beforeBal = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor3, new BN(web3.utils.toWei("3")), {from: account_investor4}); + let afterBal = await I_SecurityToken.balanceOf.call(account_investor4); + let diff = beforeBal.sub(afterBal); + assert.equal(web3.utils.fromWei((diff.toString()).toString()), 3); + }); + + it("Should add multiple token holders to exemption list and check the getter value", async() => { + let holders = [account_investor1, account_investor3, account_investor2, account_delegate2]; + let change = [true, true, true, true]; + for (let i = 0; i < holders.length; i++) { + await I_VolumeRestrictionTM.changeExemptWalletList(holders[i], change[i], {from: token_owner}); + } + let data = await I_VolumeRestrictionTM.getExemptAddress.call(); + assert.equal(data.length, 5); + assert.equal(data[0], account_investor4); + assert.equal(data[1], account_investor1); + assert.equal(data[2], account_investor3); + assert.equal(data[3], account_investor2); + assert.equal(data[4], account_delegate2); + }); + + it("Should unexempt a particular address", async() => { + await I_VolumeRestrictionTM.changeExemptWalletList(account_investor1, false, {from: token_owner}); + let data = await I_VolumeRestrictionTM.getExemptAddress.call(); + assert.equal(data.length, 4); + assert.equal(data[0], account_investor4); + assert.equal(data[1], account_delegate2); + assert.equal(data[2], account_investor3); + assert.equal(data[3], account_investor2); + }); + + it("Should fail to unexempt the same address again", async() => { + await catchRevert( + I_VolumeRestrictionTM.changeExemptWalletList(account_investor1, false, {from: token_owner}) + ); + }); + + it("Should delete the last element of the exemption list", async() => { + await I_VolumeRestrictionTM.changeExemptWalletList(account_investor2, false, {from: token_owner}); + let data = await I_VolumeRestrictionTM.getExemptAddress.call(); + assert.equal(data.length, 3); + assert.equal(data[0], account_investor4); + assert.equal(data[1], account_delegate2); + assert.equal(data[2], account_investor3); + }); + + it("Should delete multiple investor from the exemption list", async() => { + let holders = [account_delegate2, account_investor4, account_investor3]; + let change = [false, false, false]; + for (let i = 0; i < holders.length; i++) { + await I_VolumeRestrictionTM.changeExemptWalletList(holders[i], change[i], {from: token_owner}); + } + let data = await I_VolumeRestrictionTM.getExemptAddress.call(); + assert.equal(data.length, 0); + }); + }); + + describe("Test for modify functions", async() => { + + it("Should add the individual restriction for multiple investor", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor3, account_delegate3], + [new BN(web3.utils.toWei("15")), new BN(1278).mul(new BN(10).pow(new BN(14)))], + [newLatestTime.add(new BN(duration.days(1))), newLatestTime.add(new BN(duration.days(2)))], + [15, 20], + [newLatestTime.add(new BN(duration.days(40))), newLatestTime.add(new BN(duration.days(60)))], + [0,1], + { + from: token_owner + } + ); + + let indi1 = await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3); + let indi2 = await I_VolumeRestrictionTM.getIndividualRestriction.call(account_delegate3); + + assert.equal(indi1[0].div(new BN(10).pow(new BN(18))).toString(), 15); + assert.equal(indi2[0].div(new BN(10).pow(new BN(14))).toString(), 1278); + + assert.equal(indi1[2].toString(), 15); + assert.equal(indi2[2].toString(), 20); + + assert.equal(indi1[4].toString(), 0); + assert.equal(indi2[4].toString(), 1); + }); + + it("Should modify the details before the starttime passed", async() => { + let newLatestTime = await getLatestTime(); + await I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( + [account_investor3, account_delegate3], + [new BN(1278).mul(new BN(10).pow(new BN(14))), new BN(web3.utils.toWei("15"))], + [newLatestTime.add(new BN(duration.days(1))), newLatestTime.add(new BN(duration.days(2)))], + [20, 15], + [newLatestTime.add(new BN(duration.days(40))), newLatestTime.add(new BN(duration.days(60)))], + [1,0], + { + from: token_owner + } + ); + + let indi1 = await I_VolumeRestrictionTM.getIndividualRestriction.call(account_investor3); + let indi2 = await I_VolumeRestrictionTM.getIndividualRestriction.call(account_delegate3); + + assert.equal(indi2[0].div(new BN(10).pow(new BN(18))).toString(), 15); + assert.equal(indi1[0].div(new BN(10).pow(new BN(14))).toString(), 1278); + + assert.equal(indi2[2].toString(), 15); + assert.equal(indi1[2].toString(), 20); + + assert.equal(indi2[4].toString(), 0); + assert.equal(indi1[4].toString(), 1); + }); + + }); + + describe("VolumeRestriction Transfer Manager Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_VolumeRestrictionTMFactory.getSetupCost.call(),0); + assert.equal((await I_VolumeRestrictionTMFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_VolumeRestrictionTMFactory.getName.call()) + .replace(/\u0000/g, ''), + "VolumeRestrictionTM", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.description.call(), + "Manage transfers based on the volume of tokens that needs to be transact", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.title.call(), + "Volume Restriction Transfer Manager", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.getInstructions.call(), + "Module used to restrict the volume of tokens traded by the token holders", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.version.call(), "1.0.0"); + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_VolumeRestrictionTMFactory.getTags.call(); + assert.equal(tags.length, 5); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Maximum Volume"); + }); + }); + +}); diff --git a/test/z_blacklist_transfer_manager.js b/test/z_blacklist_transfer_manager.js new file mode 100644 index 000000000..c07e73a9e --- /dev/null +++ b/test/z_blacklist_transfer_manager.js @@ -0,0 +1,995 @@ +import latestTime from './helpers/latestTime'; +import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall, encodeModuleCall } from './helpers/encodeCall'; +import { setUpPolymathNetwork, deployGPMAndVerifyed, deployBlacklistTMAndVerified } from "./helpers/createInstances"; +import { catchRevert } from "./helpers/exceptions"; + +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const BlacklistTransferManager = artifacts.require("./BlacklistTransferManager"); +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require('web3'); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('BlacklistTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_investor5; + + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_GeneralTransferManagerFactory; + let I_BlacklistTransferManagerFactory; + let I_GeneralPermissionManager; + let I_BlacklistTransferManager; + let P_BlacklistTransferManagerFactory; + let P_BlacklistTransferManager; + let I_GeneralTransferManager; + let I_ExchangeTransferManager; + let I_ModuleRegistry; + let I_ModuleRegistryProxy; + let I_MRProxied; + let I_STRProxied; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("1000"); + + // BlacklistTransferManager details + const holderCount = 2; // Maximum number of token holders + const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; + const MRProxyParameters = ['address', 'address']; + let bytesSTO = encodeModuleCall(['uint256'], [holderCount]); + let currentTime; + + before(async() => { + currentTime = new BN(await latestTime()); + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[7]; + account_investor2 = accounts[8]; + account_investor3 = accounts[9]; + account_investor4 = accounts[5]; + account_investor5 = accounts[6]; + + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 2: Deploy the GeneralDelegateManagerFactory + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, new BN(0)); + + // STEP 3(a): Deploy the PercentageTransferManager + [I_BlacklistTransferManagerFactory] = await deployBlacklistTMAndVerified(account_polymath, I_MRProxied, new BN(0)); + + // STEP 4(b): Deploy the PercentageTransferManager + [P_BlacklistTransferManagerFactory] = await deployBlacklistTMAndVerified(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500", "ether"))); + // ----------- POLYMATH NETWORK Configuration ------------ + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${I_GeneralPermissionManagerFactory.address} + + BlacklistTransferManagerFactory: ${I_BlacklistTransferManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async() => { + + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner}); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner}); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + + }); + + it("Should successfully attach the BlacklistTransferManager factory with the security token", async () => { + await I_PolyToken.getTokens(web3.utils.toWei("2000", "ether"), token_owner); + await catchRevert ( + I_SecurityToken.addModule(P_BlacklistTransferManagerFactory.address, bytesSTO, web3.utils.toWei("2000", "ether"), 0, { + from: token_owner + }) + ); + }); + + it("Should successfully attach the BlacklistTransferManager factory with the security token", async () => { + let snapId = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("2000", "ether"), {from: token_owner}); + const tx = await I_SecurityToken.addModule(P_BlacklistTransferManagerFactory.address, bytesSTO, web3.utils.toWei("2000", "ether"), 0, { from: token_owner }); + assert.equal(tx.logs[3].args._types[0].toString(), transferManagerKey, "BlacklistTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "BlacklistTransferManager", + "BlacklistTransferManagerFactory module was not added" + ); + P_BlacklistTransferManager = await BlacklistTransferManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the BlacklistTransferManager with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_BlacklistTransferManagerFactory.address, bytesSTO, 0, 0, { from: token_owner }); + console.log(tx); + assert.equal(tx.logs[2].args._types[0].toString(), transferManagerKey, "BlacklistTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "BlacklistTransferManager", + "BlacklistTransferManager module was not added" + ); + I_BlacklistTransferManager = await BlacklistTransferManager.at(tx.logs[2].args._module); + }); + + }); + + describe("Buy tokens using on-chain whitelist", async() => { + + it("Should Buy the tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor1, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(50))), + { + from: account_issuer + }); + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.issue(account_investor1, web3.utils.toWei('5', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toString(), + web3.utils.toWei('5', 'ether') + ); + + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor2, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(50))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(account_investor2, web3.utils.toWei('2', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor3, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(50))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(account_investor3, web3.utils.toWei('2', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toString(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor4, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(50))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor4.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(account_investor4, web3.utils.toWei('2', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor4)).toString(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor5, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(50))), + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor5.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(account_investor5, web3.utils.toWei('2', 'ether'), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor5)).toString(), + web3.utils.toWei('2', 'ether') + ); + }); + + + it("Should add the blacklist", async() => { + //Add the new blacklist + currentTime = new BN(await latestTime()); + let tx = await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("a_blacklist"), 20, { from: token_owner }); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._blacklistName), "a_blacklist", "Failed in adding the type in blacklist"); + }); + + it("Should fail in adding the blacklist as blacklist type already exist", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("a_blacklist"), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in adding the blacklist as the blacklist name is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii(""), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in adding the blacklist as the start date is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(0, currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in adding the blacklist as the dates are invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(4000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in adding the blacklist because only owner can add the blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { + from: account_investor1 + }) + ); + }); + + it("Should fail in adding the blacklist because repeat period is less than the difference of start time and end time", async() => { + await catchRevert( + I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(duration.days(2))), web3.utils.fromAscii("b_blacklist"), 1, { + from: token_owner + }) + ); + }); + + it("Should add the mutiple blacklist", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(2000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(8000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist")]; + let repeatTime = [15,30]; + let tx = await I_BlacklistTransferManager.addBlacklistTypeMulti(startTime, endTime, name, repeatTime, { from: token_owner }); + + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let blacklistName = event_data[i].args._blacklistName; + assert.equal(web3.utils.hexToUtf8(blacklistName), web3.utils.hexToUtf8(name[i]), "Failed in adding the blacklist"); + } + }); + + it("Should fail in adding the mutiple blacklist because only owner can add it", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(2000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(8000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist")]; + let repeatTime = [15,30]; + await catchRevert( + I_BlacklistTransferManager.addBlacklistTypeMulti(startTime, endTime, name, repeatTime, { + from: account_investor1 + }) + ); + }); + + it("Should fail in adding the mutiple blacklist because array lenth are different", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(2000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(8000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist"),web3.utils.fromAscii("w_blacklist")]; + let repeatTime = [15,30]; + await catchRevert( + I_BlacklistTransferManager.addBlacklistTypeMulti(startTime, endTime, name, repeatTime, { + from: token_owner + }) + ); + }); + + it("Should modify the blacklist", async() => { + //Modify the existing blacklist + let tx = await I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(2000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("a_blacklist"), 20, { from: token_owner }); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._blacklistName), "a_blacklist", "Failed in modifying the startdate of blacklist"); + + }); + + it("Should fail in modifying the blacklist as the name is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(2000)), currentTime.add(new BN(3000)), web3.utils.fromAscii(""), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in modifying the blacklist as the dates are invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(4000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { + from: token_owner + }) + ); + }); + + it("Should fail in modifying the blacklist as the repeat in days is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(2000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 0, { + from: token_owner + }) + ); + }); + + + it("Should fail in modifying the blacklist as only owner can modify the blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("a_blacklist"), 20, { + from: account_investor1 + }) + ); + }); + + it("Should fail in modifying the blacklist as blacklist type doesnot exist", async() => { + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { + from: token_owner + }) + ); + }); + + it("Should modify the mutiple blacklist", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(3000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(7000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist")]; + let repeatTime = [15,30]; + let tx = await I_BlacklistTransferManager.modifyBlacklistTypeMulti(startTime, endTime, name, repeatTime, { from: token_owner }); + + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let blacklistName = event_data[i].args._blacklistName; + assert.equal(web3.utils.hexToUtf8(blacklistName), web3.utils.hexToUtf8(name[i]), "Failed in adding the blacklist"); + } + }); + + it("Should fail in modifying the mutiple blacklist because only owner can add it", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(3000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(7000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist")]; + let repeatTime = [15,30]; + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistTypeMulti(startTime, endTime, name, repeatTime, { + from: account_investor1 + }) + ); + }); + + it("Should fail in modifying the mutiple blacklist because array length are different", async() => { + //Add the new blacklist + let startTime = [currentTime.add(new BN(3000)),currentTime.add(new BN(3000))]; + let endTime = [currentTime.add(new BN(5000)),currentTime.add(new BN(7000))]; + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist"),web3.utils.fromAscii("w_blacklist")]; + let repeatTime = [15,30]; + await catchRevert( + I_BlacklistTransferManager.modifyBlacklistTypeMulti(startTime, endTime, name, repeatTime, { + from: token_owner + }) + ); + }); + + it("Should add investor to the blacklist", async() => { + //Add investor to the existing blacklist + let tx = await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1, web3.utils.fromAscii("a_blacklist"), { from: token_owner }); + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor to the blacklist"); + + }); + + it("Should fail in adding the investor to the blacklist because only owner can add the investor", async() => { + await catchRevert( + I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("a_blacklist"), { + from: account_investor1 + }) + ); + }); + + it("Should fail in adding the investor to the blacklist as investor address is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.addInvestorToBlacklist("0x0000000000000000000000000000000000000000", web3.utils.fromAscii("a_blacklist"), { + from: token_owner + }) + ); + }); + + + it("Should fail in adding the investor to the non existing blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("b_blacklist"), { + from: token_owner + }) + ); + }); + + it("Should get the list of investors associated to blacklist", async() => { + let perm = await I_BlacklistTransferManager.getListOfAddresses.call(web3.utils.fromAscii("a_blacklist")); + assert.equal(perm.length, 1); + }); + + it("Should fail in getting the list of investors from the non existing blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.getListOfAddresses.call(web3.utils.fromAscii("b_blacklist")) + ); + }); + + it("Should investor be able to transfer token because current time is less than the blacklist start time", async() => { + //Trasfer tokens + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('3', 'ether') + ); + }); + + it("Should investor be able to transfer token as it is not in blacklist time period", async() => { + // Jump time + await increaseTime(duration.seconds(4000)); + + //Trasfer tokens + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('4', 'ether') + ); + }); + + it("Should fail in transfer the tokens as the investor in blacklist", async() => { + // Jump time + await increaseTime(duration.days(20) - 1500); + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { + from: account_investor1 + }) + ); + }); + + it("Should investor is able transfer the tokens- because BlacklistTransferManager is paused", async() => { + await I_BlacklistTransferManager.pause({from:token_owner}); + //Trasfer tokens + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toString(), + web3.utils.toWei('5', 'ether') + ); + }); + + it("Should investor fail in transfer token as it is in blacklist time period", async() => { + + await I_BlacklistTransferManager.unpause({from:token_owner}); + currentTime = new BN(await latestTime()); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(500)), currentTime.add(new BN(4000)), web3.utils.fromAscii("k_blacklist"), 8, { from: token_owner }); + + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("k_blacklist"), { from: token_owner }); + // Jump time + await increaseTime(3500); + + //Trasfer tokens + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { + from: account_investor2 + }) + ) + }); + + it("Should investor be able to transfer token as it is not in blacklist time period", async() => { + // Jump time + await increaseTime(1000); + + //Trasfer tokens + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toString(), + web3.utils.toWei('3', 'ether') + ); + }); + + it("Should investor fail in transfer token as it is in blacklist time period", async() => { + + // Jump time + await increaseTime(duration.days(8) - 1000); + //Trasfer tokens + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { + from: account_investor2 + }) + ); + }); + + it("Should investor fail in transfer token as it is in blacklist time period", async() => { + currentTime = new BN(await latestTime()); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(5000)), currentTime.add(new BN(8000)), web3.utils.fromAscii("l_blacklist"), 5, { from: token_owner }); + + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor3, web3.utils.fromAscii("l_blacklist"), { from: token_owner }); + // Jump time + await increaseTime(5500); + + //Trasfer tokens + await catchRevert( + I_SecurityToken.transfer(account_investor4, web3.utils.toWei('1', 'ether'), { + from: account_investor3 + }) + ); + }); + + it("Should investor be able to transfer token as it is not in blacklist time period", async() => { + // Jump time + await increaseTime(3000); + + //Trasfer tokens + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('1', 'ether'), { from: account_investor3 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor4)).toString(), + web3.utils.toWei('3', 'ether') + ); + }); + + it("Should investor fail in transfer token as it is in blacklist time period", async() => { + + // Jump time + await increaseTime(duration.days(5) - 3000); + //Trasfer tokens + await catchRevert( + I_SecurityToken.transfer(account_investor4, web3.utils.toWei('1', 'ether'), { + from: account_investor3 + }) + ); + }); + + + it("Should delete the blacklist type", async() => { + currentTime = new BN(await latestTime()); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { from: token_owner }); + let tx = await I_BlacklistTransferManager.deleteBlacklistType(web3.utils.fromAscii("b_blacklist"), { from: token_owner }); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._blacklistName), "b_blacklist", "Failed in deleting the blacklist"); + + }); + + it("Only owner have the permission to delete the blacklist type", async() => { + currentTime = new BN(await latestTime()); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("b_blacklist"), 20, { from: token_owner }); + await catchRevert( + I_BlacklistTransferManager.deleteBlacklistType(web3.utils.fromAscii("b_blacklist"), { + from: account_investor1 + }) + ); + }); + + it("Should fail in deleting the blacklist type as the blacklist has associated addresses", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteBlacklistType(web3.utils.fromAscii("a_blacklist"), { + from: token_owner + }) + ); + }); + + it("Should fail in deleting the blacklist type as the blacklist doesnot exist", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteBlacklistType(web3.utils.fromAscii("c_blacklist"), { + from: token_owner + }) + ); + }); + + it("Should delete the mutiple blacklist type", async() => { + let name = [web3.utils.fromAscii("y_blacklist"),web3.utils.fromAscii("z_blacklist")]; + let tx = await I_BlacklistTransferManager.deleteBlacklistTypeMulti(name, { from: token_owner }); + + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let blacklistName = event_data[i].args._blacklistName; + assert.equal(web3.utils.hexToUtf8(blacklistName), web3.utils.hexToUtf8(name[i]), "Failed in deleting the blacklist"); + } + + }); + + it("Should fail in deleting multiple blacklist type because only owner can do it.", async() => { + let name = [web3.utils.fromAscii("b_blacklist"),web3.utils.fromAscii("a_blacklist")]; + await catchRevert( + I_BlacklistTransferManager.deleteBlacklistTypeMulti(name, { + from: account_investor1 + }) + ); + }); + + it("Should delete the investor from all the associated blacklist", async() => { + currentTime = new BN(await latestTime()); + let data = await I_BlacklistTransferManager.getBlacklistNamesToUser.call(account_investor1); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("g_blacklist"), 20, { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + let tx = await I_BlacklistTransferManager.deleteInvestorFromAllBlacklist(account_investor1, { from: token_owner }); + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in deleting the investor from the blacklist"); + }); + + it("Only owner has the permission to delete the investor from all the blacklist type", async() => { + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1,web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromAllBlacklist(account_investor1, { + from: account_investor2 + }) + ) + }); + + it("Should fail in deleting the investor from all the associated blacklist as th address is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromAllBlacklist("0x0000000000000000000000000000000000000000", { + from: token_owner + }) + ); + }); + + it("Should fail in deleting the investor because investor is not associated to any blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromAllBlacklist(account_investor5, { + from: token_owner + }) + ); + }); + + it("Should delete the mutiple investor from all the associated blacklist", async() => { + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor5, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + let investor = [account_investor5,account_investor2]; + let tx = await I_BlacklistTransferManager.deleteInvestorFromAllBlacklistMulti(investor, { from: token_owner }); + let event_data = tx.logs; + assert.equal(event_data[0].args._investor, investor[0], "Failed in deleting the blacklist"); + assert.equal(event_data[1].args._investor, investor[1], "Failed in deleting the blacklist"); + assert.equal(event_data[2].args._investor, investor[1], "Failed in deleting the blacklist"); + }); + + it("Should fail in deleting the mutiple investor from all the associated blacklist because only owner can do it.", async() => { + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor5, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + let investor = [account_investor5,account_investor2]; + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromAllBlacklistMulti(investor, { + from: account_investor1 + }) + ); + }); + + it("Should delete the mutiple investor from particular associated blacklists", async() => { + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("s_blacklist"), 20, { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor5, web3.utils.fromAscii("s_blacklist"), { from: token_owner }); + // await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, "g_blacklist", { from: token_owner }); + let investor = [account_investor5,account_investor2]; + let blacklistName = [web3.utils.fromAscii("s_blacklist"),web3.utils.fromAscii("g_blacklist")]; + let tx = await I_BlacklistTransferManager.deleteMultiInvestorsFromBlacklistMulti(investor,blacklistName, { from: token_owner }); + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let investorName = event_data[i].args._investor; + assert.equal(investorName.toLowerCase(), investor[i].toLowerCase(), "Failed in deleting the blacklist"); + } + }); + + it("Should fail in deleting the mutiple investor from particular associated blacklist because only owner can do it.", async() => { + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor5, web3.utils.fromAscii("s_blacklist"), { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor2, web3.utils.fromAscii("g_blacklist"), { from: token_owner }); + let investor = [account_investor5,account_investor2]; + let blacklistName = [web3.utils.fromAscii("s_blacklist"),web3.utils.fromAscii("g_blacklist")]; + await catchRevert( + I_BlacklistTransferManager.deleteMultiInvestorsFromBlacklistMulti(investor,blacklistName, { + from: account_investor1 + }) + ); + }); + + it("Should fail in deleting the mutiple investor from particular associated blacklist because array length is incorrect.", async() => { + let investor = [account_investor5]; + let blacklistName = [web3.utils.fromAscii("s_blacklist"),web3.utils.fromAscii("g_blacklist")]; + await catchRevert( + I_BlacklistTransferManager.deleteMultiInvestorsFromBlacklistMulti(investor,blacklistName, { + from: token_owner + }) + ); + }); + + it("Should delete the investor from the blacklist type", async() => { + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("f_blacklist"), 20, { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor5, web3.utils.fromAscii("f_blacklist"), { from: token_owner }); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(500)), currentTime.add(new BN(8000)), web3.utils.fromAscii("q_blacklist"), 10, { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1, web3.utils.fromAscii("q_blacklist"), { from: token_owner }); + let tx = await I_BlacklistTransferManager.deleteInvestorFromBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { from: token_owner }); + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in deleting the investor from the blacklist"); + + }); + + it("Only owner can delete the investor from the blacklist type", async() => { + await I_BlacklistTransferManager.addInvestorToBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { from: token_owner }); + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { + from: account_investor2 + }) + ); + }); + + it("Should fail in deleting the investor because the investor address is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromBlacklist("0x0000000000000000000000000000000000000000", web3.utils.fromAscii("f_blacklist"), { + from: token_owner + }) + ); + }); + + it("Should fail in deleting the investor because the investor is not associated to blacklist", async() => { + await I_BlacklistTransferManager.deleteInvestorFromBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { from: token_owner }); + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromBlacklist(account_investor1, web3.utils.fromAscii("f_blacklist"), { + from: token_owner + }) + ); + }); + + it("Should fail in deleting the investor because the blacklist name is invalid", async() => { + await catchRevert( + I_BlacklistTransferManager.deleteInvestorFromBlacklist(account_investor1, web3.utils.fromAscii(""), { + from: token_owner + }) + ); + }); + + it("Should add investor and new blacklist type", async() => { + let tx = await I_BlacklistTransferManager.addInvestorToNewBlacklist(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("c_blacklist"), 20, account_investor3, { from: token_owner }); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._blacklistName), "c_blacklist", "Failed in adding the blacklist"); + assert.equal(tx.logs[1].args._investor, account_investor3, "Failed in adding the investor to blacklist"); + + }); + + it("Should fail in adding the investor and new blacklist type", async() => { + await catchRevert( + I_BlacklistTransferManager.addInvestorToNewBlacklist(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("c_blacklist"), 20, account_investor3, { + from: account_investor2 + }) + ); + }); + + it("Should add mutiple investor to blacklist", async() => { + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("d_blacklist"), 20, { from: token_owner }); + let investor = [account_investor4,account_investor5]; + let tx = await I_BlacklistTransferManager.addInvestorToBlacklistMulti([account_investor4,account_investor5], web3.utils.fromAscii("d_blacklist"), { from: token_owner }); + + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let user = event_data[i].args._investor; + assert.equal(user, investor[i], "Failed in adding the investor to blacklist"); + } + + }); + + it("Should fail in adding the mutiple investor to the blacklist", async() => { + await catchRevert( + I_BlacklistTransferManager.addInvestorToBlacklistMulti([account_investor4,account_investor5], web3.utils.fromAscii("b_blacklist"), { + from: account_investor1 + }) + ); + }); + + it("Should add mutiple investor to the mutiple blacklist", async() => { + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("m_blacklist"), 20, { from: token_owner }); + await I_BlacklistTransferManager.addBlacklistType(currentTime.add(new BN(1000)), currentTime.add(new BN(3000)), web3.utils.fromAscii("n_blacklist"), 20, { from: token_owner }); + let investor = [account_investor4,account_investor5]; + let blacklistName =[web3.utils.fromAscii("m_blacklist"),web3.utils.fromAscii("n_blacklist")]; + let tx = await I_BlacklistTransferManager.addMultiInvestorToBlacklistMulti(investor, blacklistName, { from: token_owner }); + + let event_data = tx.logs; + for (var i = 0; i < event_data.length; i++) { + let user = event_data[i].args._investor; + let blacklist = event_data[i].args._blacklistName; + assert.equal(user, investor[i], "Failed in adding the investor to blacklist"); + assert.equal(web3.utils.hexToUtf8(blacklist), web3.utils.hexToUtf8(blacklistName[i]), "Failed in adding the investor to blacklist"); + } + + }); + + it("Should fail in adding the mutiple investor to the mutiple blacklist because only owner can do it.", async() => { + let investor = [account_investor4,account_investor5]; + let blacklistName = [ web3.utils.fromAscii("m_blacklist"), web3.utils.fromAscii("n_blacklist")]; + await I_BlacklistTransferManager.deleteMultiInvestorsFromBlacklistMulti(investor,blacklistName, { from: token_owner }); + await catchRevert( + I_BlacklistTransferManager.addMultiInvestorToBlacklistMulti(investor, blacklistName, { + from: account_investor1 + }) + ); + }); + + it("Should fail in adding mutiple investor to the mutiple blacklist because array length is not same", async() => { + let investor = [account_investor4,account_investor5]; + let blacklistName =[web3.utils.fromAscii("m_blacklist")]; + await catchRevert( + I_BlacklistTransferManager.addMultiInvestorToBlacklistMulti(investor, blacklistName, { + from: token_owner + }) + ); + }); + + it("Should get the init function", async() => { + let byte = await I_BlacklistTransferManager.getInitFunction.call(); + assert.equal(web3.utils.toAscii(byte).replace(/\u0000/g, ''), 0); + }); + + it("Should get the permission", async() => { + let perm = await I_BlacklistTransferManager.getPermissions.call(); + assert.equal(perm.length, 1); + }); + }); + + describe("Test cases for the factory", async() => { + it("Should get the exact details of the factory", async() => { + assert.equal(await I_BlacklistTransferManagerFactory.setupCost.call(),0); + assert.equal((await I_BlacklistTransferManagerFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_BlacklistTransferManagerFactory.getName.call()) + .replace(/\u0000/g, ''), + "BlacklistTransferManager", + "Wrong Module added"); + assert.equal(await I_BlacklistTransferManagerFactory.description.call(), + "Automate blacklist to restrict selling", + "Wrong Module added"); + assert.equal(await I_BlacklistTransferManagerFactory.title.call(), + "Blacklist Transfer Manager", + "Wrong Module added"); + assert.equal(await I_BlacklistTransferManagerFactory.getInstructions.call(), + "Allows an issuer to blacklist the addresses.", + "Wrong Module added"); + assert.equal(await I_BlacklistTransferManagerFactory.version.call(), + "2.1.0", + "Wrong Module added"); + + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_BlacklistTransferManagerFactory.getTags.call(); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''),"Blacklist"); + }); + }); + +}); diff --git a/test/z_fuzz_test_adding_removing_modules_ST.js b/test/z_fuzz_test_adding_removing_modules_ST.js new file mode 100644 index 000000000..e527aed32 --- /dev/null +++ b/test/z_fuzz_test_adding_removing_modules_ST.js @@ -0,0 +1,313 @@ +import latestTime from './helpers/latestTime'; +import {signData} from './helpers/signData'; +import { pk } from './helpers/testprivateKey'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, + deployGPMAndVerifyed, + deployCountTMAndVerifyed, + deployLockupVolumeRTMAndVerified, + deployPercentageTMAndVerified, + deployManualApprovalTMAndVerifyed +} from "./helpers/createInstances"; +import { encodeModuleCall } from "./helpers/encodeCall"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); + +// modules for test +const CountTransferManager = artifacts.require("./CountTransferManager"); +const ManualApprovalTransferManager = artifacts.require('./ManualApprovalTransferManager'); +const VolumeRestrictionTransferManager = artifacts.require('./LockUpTransferManager'); +const PercentageTransferManager = artifacts.require('./PercentageTransferManager'); +const STGetter = artifacts.require("./STGetter.sol"); + + +const Web3 = require('web3'); +const BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('GeneralPermissionManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let P_GeneralPermissionManagerFactory; + let I_SecurityTokenRegistryProxy; + let P_GeneralPermissionManager; + let I_GeneralTransferManagerFactory; + let I_GeneralPermissionManager; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; + + + //Define all modules for test + let I_CountTransferManagerFactory; + let I_CountTransferManager; + + let I_ManualApprovalTransferManagerFactory; + let I_ManualApprovalTransferManager; + + let I_VolumeRestrictionTransferManagerFactory; + let I_VolumeRestrictionTransferManager; + + let I_PercentageTransferManagerFactory; + let I_PercentageTransferManager; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = "Hello I am legit delegate"; + const STVRParameters = ["bool", "uint256", "bool"]; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("1000"); + + let _details = "details holding for test"; + let testRepeat = 20; + + // define factories and modules for fuzz test + var factoriesAndModules = [ + { factory: 'I_CountTransferManagerFactory', module: 'CountTransferManager'}, + { factory: 'I_ManualApprovalTransferManagerFactory', module: 'ManualApprovalTransferManager'}, + { factory: 'I_VolumeRestrictionTransferManagerFactory', module: 'VolumeRestrictionTransferManager'}, + { factory: 'I_PercentageTransferManagerFactory', module: 'PercentageTransferManager'}, + ]; + + let totalModules = factoriesAndModules.length; + let bytesSTO; + + + before(async () => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[5]; + account_investor4 = accounts[6]; + account_delegate = accounts[7]; + // account_delegate2 = accounts[6]; + + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 5: Deploy the GeneralDelegateManagerFactory + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + // STEP 6: Deploy the GeneralDelegateManagerFactory + [P_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, web3.utils.toWei("500")); + + [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, 0); + + // Deploy Modules + [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, 0); + [I_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, 0); + [I_VolumeRestrictionTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, 0); + [I_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified(account_polymath, I_MRProxied, 0); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${I_GeneralPermissionManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + + it("Should successfully attach the General permission manager factory with the security token -- failed because Token is not paid", async () => { + let errorThrown = false; + await I_PolyToken.getTokens(web3.utils.toWei("2000", "ether"), token_owner); + await catchRevert( + I_SecurityToken.addModule(P_GeneralPermissionManagerFactory.address, "0x0", web3.utils.toWei("2000", "ether"), 0, { from: token_owner }) + ); + }); + + it("Should successfully attach the General permission manager factory with the security token", async () => { + let snapId = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("2000", "ether"), { from: token_owner }); + const tx = await I_SecurityToken.addModule( + P_GeneralPermissionManagerFactory.address, + "0x0", + web3.utils.toWei("2000", "ether"), + 0, + { from: token_owner } + ); + assert.equal(tx.logs[3].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name).replace(/\u0000/g, ""), + "GeneralPermissionManager", + "GeneralPermissionManagerFactory module was not added" + ); + P_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the General permission manager factory with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "GeneralPermissionManager", + "GeneralPermissionManagerFactory module was not added" + ); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); + }); + }); + + + + describe("adding and removing different modules", async () => { + + it("should pass test for randomly adding and removing modules ", async () => { + + console.log("1"); + // fuzz test loop over total times of testRepeat + for (var i = 0; i < testRepeat; i++) { + + console.log("1.2"); + + // choose a random module with in the totalMoudules available + let random = factoriesAndModules[Math.floor(Math.random() * Math.floor(totalModules))]; + let randomFactory = eval(random.factory); + let randomModule = eval(random.module); + console.log("choosen factory "+ random.factory); + console.log("choosen module "+ random.module); + + //calculate the data needed for different modules + if (random.module == 'CountTransferManager' || random.module == 'ManualApprovalTransferManager' || random.module == 'VolumeRestrictionTransferManager' ){ + const holderCount = 2; // Maximum number of token holders + bytesSTO = encodeModuleCall(["uint256"], [holderCount]); + } else if (random.module == 'PercentageTransferManager'){ + console.log("PTM 01"); + const holderPercentage = 70 * 10**16; + bytesSTO = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'uint256', + name: '_maxHolderPercentage' + },{ + type: 'bool', + name: '_allowPrimaryIssuance' + } + ] + }, [holderPercentage, false]); + console.log("encoded."); + } else { + console.log("no data defined for choosen module "+random.module); + } + + // attach it to the ST + let tx = await I_SecurityToken.addModule(randomFactory.address, bytesSTO, 0, 0, { from: token_owner }); + console.log("1.3"); + let randomModuleInstance = await randomModule.at(tx.logs[2].args._module); + console.log("successfully attached module " + randomModuleInstance.address); + + // remove it from the ST + tx = await I_SecurityToken.archiveModule(randomModuleInstance.address, { from: token_owner }); + console.log("1.4"); + tx = await I_SecurityToken.removeModule(randomModuleInstance.address, { from: token_owner }); + console.log("successfully removed module " + randomModuleInstance.address); + + } + }) + }); + +}); diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js new file mode 100644 index 000000000..007a09849 --- /dev/null +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -0,0 +1,725 @@ +import latestTime from './helpers/latestTime'; +import {signData} from './helpers/signData'; +import { pk } from './helpers/testprivateKey'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, deployVRTMAndVerifyed } from "./helpers/createInstances"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager.sol'); +const VolumeRestrictionTM = artifacts.require('./VolumeRestrictionTM.sol'); +const STGetter = artifacts.require("./STGetter.sol"); +const Web3 = require('web3'); +const BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('VolumeRestrictionTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + let account_delegate3; + // investor Details + let fromTime = currentTime; + let toTime = currentTime; + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_VolumeRestrictionTMFactory; + let P_VolumeRestrictionTMFactory; + let I_SecurityTokenRegistryProxy; + let P_VolumeRestrictionTM; + let I_GeneralTransferManagerFactory; + let I_VolumeRestrictionTM; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = "Hello I am legit delegate"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + let tempAmount = new BN(0); + let tempArray = new Array(); + let tempArray3 = new Array(); + let tempArrayGlobal = new Array(); + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("1000"); + + async function print(data, account) { + console.log(` + Latest timestamp: ${data[0].toString()} + SumOfLastPeriod: ${data[1].dividedBy(new BN(10).pow(18)).toString()} + Days Covered: ${data[2].toString()} + Latest timestamp daily: ${data[3].toString()} + Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[0])) + .dividedBy(new BN(10).pow(18)).toString()} + Individual Total Trade on daily latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[3])) + .dividedBy(new BN(10).pow(18)).toString()} + `) + } + + async function calculateSum(rollingPeriod, tempArray) { + let sum = 0; + let start = 0; + if (tempArray.length >= rollingPeriod) + start = tempArray.length - rollingPeriod; + for (let i = start; i < tempArray.length; i++) { + sum += tempArray[i]; + } + return sum; + } + + async function printIR(data) { + console.log(` + Allowed Tokens : ${web3.utils.fromWei(data[0])} + StartTime : ${data[1].toString()} + Rolling Period : ${data[2].toString()} + EndTime : ${data[3].toString()} + Restriction Type: ${data[4].toString() == 0 ? "Fixed" : "Percentage"} + `) + } + let currentTime; + before(async() => { + // Accounts setup + currentTime = new BN(await latestTime()); + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[4]; + account_investor4 = accounts[3]; + account_delegate = accounts[7]; + account_delegate2 = accounts[6]; + account_delegate3 = accounts[5]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 5: Deploy the VolumeRestrictionTMFactory + [I_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, 0); + // STEP 6: Deploy the VolumeRestrictionTMFactory + [P_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, web3.utils.toWei("500")); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + VolumeRestrictionTMFactory: ${I_VolumeRestrictionTMFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, true, { from: token_owner }); + console.log(tx.logs); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + }); + + describe("Attach the VRTM", async() => { + it("Deploy the VRTM and attach with the ST", async()=> { + let tx = await I_SecurityToken.addModule(I_VolumeRestrictionTMFactory.address, "0x0", 0, 0, {from: token_owner }); + assert.equal(tx.logs[2].args._moduleFactory, I_VolumeRestrictionTMFactory.address); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "VolumeRestrictionTM", + "VolumeRestrictionTMFactory doesn not added"); + I_VolumeRestrictionTM = await VolumeRestrictionTM.at(tx.logs[2].args._module); + }); + + it("Transfer some tokens to different account", async() => { + // Add tokens in to the whitelist + currentTime = new BN(await latestTime()); + await I_GeneralTransferManager.modifyKYCDataMulti( + [account_investor1, account_investor2, account_investor3], + [currentTime, currentTime, currentTime], + [currentTime, currentTime, currentTime], + [currentTime.add(new BN(duration.days(60))), currentTime.add(new BN(duration.days(60))), currentTime.add(new BN(duration.days(60)))], + { + from: token_owner + } + ); + + // Mint some tokens and transferred to whitelisted addresses + await I_SecurityToken.issue(account_investor1, web3.utils.toWei("100", "ether"), "0x0", {from: token_owner}); + await I_SecurityToken.issue(account_investor2, web3.utils.toWei("30", "ether"), "0x0", {from: token_owner}); + await I_SecurityToken.issue(account_investor3, web3.utils.toWei("30", "ether"), "0x0", {from: token_owner}); + + }); + + }); + + describe("Fuzz test", async () => { + + it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { + // let snapId = await takeSnapshot(); + + var testRepeat = 0; + + for (var i = 0; i < testRepeat; i++) { + + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if ( individualRestrictTotalAmount == 0 ) { + individualRestrictTotalAmount = 1; + } + + var dailyRestrictionAmount = Math.floor(Math.random() * 10); + if ( dailyRestrictionAmount == 0 ) { + dailyRestrictionAmount = 1; + } + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + currentTime = new BN(await latestTime()); + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + currentTime.add(new BN(duration.seconds(2))), + rollingPeriod, + currentTime.add(new BN(duration.days(3))), + 0, + { + from: token_owner + } + ); + currentTime = new BN(await latestTime()); + console.log("b"); + tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + web3.utils.toWei(dailyRestrictionAmount.toString()), + currentTime.add(new BN(duration.seconds(1))), + currentTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ); + + console.log("c"); + var txNumber = 10; // define fuzz test amount for tx within 24 hrs + currentTime = new BN(await latestTime()); + for (var j=0; j individualRestrictTotalAmount || accumulatedTxValue > dailyRestrictionAmount) { + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + + } else if (accumulatedTxValue <= individualRestrictTotalAmount && accumulatedTxValue <= dailyRestrictionAmount) { + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("2"); + } + + // await revertToSnapshot(snapId); + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); + } + }); + + it("Should work with fuzz test for individual restriction and general restriction", async() => { + // let snapId = await takeSnapshot(); + var testRepeat = 0; + + for (var i = 0; i < testRepeat; i++) { + + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if (individualRestrictTotalAmount == 0 ) { + individualRestrictTotalAmount = 1; + } + var defaultRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + currentTime = new BN(await latestTime()); + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + currentTime.add(new BN(duration.seconds(1))), + rollingPeriod, + currentTime.add(new BN(duration.days(3))), + 0, + { + from: token_owner + } + ); + currentTime = new BN(await latestTime()); + console.log("b"); + tx = await I_VolumeRestrictionTM.addDefaultRestriction( + account_investor1, + currentTime.add(new BN(duration.seconds(1))), + rollingPeriod, + currentTime.add(new BN(duration.days(4))), + 0, + { + from: token_owner + } + ); + currentTime = new BN(await latestTime()); + console.log("c"); + var txNumber = 10; // define fuzz test amount for tx + + for (var j = 0; j < txNumber; j++) { + await increaseTime(duration.seconds(5)); + currentTime = new BN(await latestTime()); + console.log("2"); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * 10); + var accumulatedTxValue = transactionAmount + sumOfLastPeriod; + + console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " defaultRestrictionAmount is " + defaultRestrictionAmount); + + + // check against daily and total restrictions to determine if the transaction should pass or not + if (accumulatedTxValue > individualRestrictTotalAmount || accumulatedTxValue > defaultRestrictionAmount) { + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + } else if (accumulatedTxValue <= individualRestrictTotalAmount) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("3"); + }; + + + // remove individual restriction and it should fall to default restriction + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("individual restriction now removed --> fall back to default restriction"); + + for (var j=0; j defaultRestrictionAmount) { + console.log("tx should fail"); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + console.log("tx failed as expected due to over limit"); + } else if ( accumulatedTxValue <= defaultRestrictionAmount ) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("5"); + } + + + // await revertToSnapshot(snapId); + await I_VolumeRestrictionTM.removeDefaultRestriction(account_investor1, {from: token_owner}); + } + + }); + + + + it("Should work with fuzz test for randomly adding / removing individual daily restriction and perform multipel transactions", async() => { + + + var testRepeat = 0; + var txNumber = 10; + var dailyRestriction = false; + var startTime = 1; + var sumOfLastPeriod = 0; + var accumulatedTimeIncrease = 0; + var dailyLimitUsed = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + for (var i = 0; i < testRepeat; i++) { + + // randomly add or removing existing daily restriction + var random_action = Math.random() >= 0.5; // true -> add false -> remove + + if (dailyRestriction === false && random_action === true) { + console.log("1"); + + var dailyRestrictionAmount = Math.floor(Math.random() * 10); + if (dailyRestrictionAmount === 0) { + dailyRestrictionAmount = 1; + } + + // add daily restriction + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + web3.utils.toWei(dailyRestrictionAmount.toString()), + currentTime.add(new BN(duration.seconds(startTime))), + currentTime.add(new BN(duration.days(50))), + 0, + { + from: token_owner + } + ); + + dailyRestriction = true; + + console.log("added daily restriction"); + } else if (dailyRestriction === true && random_action === false) { + console.log("2"); + // remove daily restriction + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + + dailyRestriction = false; + } + + // perform multiple transactions + + for (var j = 0; j < txNumber; j++) { + var timeIncreaseBetweenTx = Math.floor(Math.random() * 10) * 3600; + + await increaseTime(duration.seconds(timeIncreaseBetweenTx)); + currentTime = new BN(await latestTime()); + accumulatedTimeIncrease = timeIncreaseBetweenTx + accumulatedTimeIncrease; + console.log("4"); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * 10); + + // check today's limit + var dayNumber = Math.floor(accumulatedTimeIncrease/(24*3600)) + 1; + + var todayLimitUsed = dailyLimitUsed[dayNumber]; + + console.log("todayLimitUsed is " + todayLimitUsed + " transactionAmount is " + transactionAmount + " dayNumber is " + dayNumber + " dailyRestrictionAmount is " + dailyRestrictionAmount); + + // check against daily and total restrictions to determine if the transaction should pass or not + if ((todayLimitUsed + transactionAmount) > dailyRestrictionAmount) { + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + } else if ((todayLimitUsed + transactionAmount) <= dailyRestrictionAmount) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + dailyLimitUsed[dayNumber] = dailyLimitUsed[dayNumber] + transactionAmount; + + console.log("tx succeeded"); + } + console.log("5"); + } + + if (dailyRestriction === true) { + + // remove daily restriction + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + } + + } + + }); + + + + + it("should work in all cases if a sender is added in the exception list", async () => { + var testRepeat = 0; + + for (var i = 0; i < testRepeat; i++) { + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if (individualRestrictTotalAmount === 0 ) { + individualRestrictTotalAmount = 1; + } + var defaultRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + currentTime.add(new BN(duration.seconds(1))), + rollingPeriod, + currentTime.add(new BN(duration.days(3))), + 0, + { + from: token_owner + } + ); + + tx = await I_VolumeRestrictionTM.changeExemptWalletList(account_investor1, true, {from: token_owner}); + + console.log("b"); + + var txNumber = 10; // define fuzz test amount for tx + + for (var j = 0; j < txNumber; j++) { + await increaseTime(duration.seconds(5)); + currentTime = new BN(await latestTime()); + console.log("2"); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * 10); + var accumulatedTxValue = transactionAmount + sumOfLastPeriod; + + console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " defaultRestrictionAmount is " + defaultRestrictionAmount); + + // check against daily and total restrictions to determine if the transaction should pass or not + if (accumulatedTxValue > individualRestrictTotalAmount) { + console.log("tx should fail but still succeed due to investor in exempt list"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + console.log("tx passed as expected"); + } else if (accumulatedTxValue <= individualRestrictTotalAmount) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("3" + txNumber); + }; + + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + } + }); + + it("Should work if IR is modified", async () => { + + console.log(`\t\t Starting of the IR modification test case`.blue); + + var testRepeat = 1; + + for (var i = 0; i < testRepeat; i++) { + console.log("\t\t fuzzer number " + i); + let precision = 100; + var individualRestrictionTotalAmount = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log(`\t\t Add individual restriction with TotalAmount: ${individualRestrictionTotalAmount}\n`.green); + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictionTotalAmount.toString()), + currentTime.add(new BN(duration.days(2))), + rollingPeriod, + currentTime.add(new BN(duration.days(5))), + 0, + { + from: token_owner + } + ); + + console.log(`\t\t Restriction successfully added \n`); + + var txNumber = 10; // define fuzz test amount for tx + + for (var j = 0; j < txNumber; j++) { + console.log(`\t\t Test number: ${j}\n`); + + // modify IR + var newIR = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); + console.log("Original Restriction"); + printIR(await I_VolumeRestrictionTM.getIndividualRestriction(account_investor1, {from: token_owner})); + currentTime = new BN(await latestTime()); + console.log(`\t\t Modification of the IR with new startTime: ${currentTime + duration.days(1+j)} and new total amount: ${newIR} `.green); + + await I_VolumeRestrictionTM.modifyIndividualRestriction( + account_investor1, + web3.utils.toWei(newIR.toString()), + currentTime.add(new BN(duration.days(1+j))), + rollingPeriod, + currentTime.add(new BN(duration.days(5+j))), + 0, + { from: token_owner } + ); + console.log("Modified Restriction"); + printIR(await I_VolumeRestrictionTM.getIndividualRestriction(account_investor1, {from: token_owner})); + console.log(`\t\t Successfully IR modified`); + let snapId = await takeSnapshot(); + await increaseTime(duration.days(2+j)); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); + + // check against daily and total restrictions to determine if the transaction should pass or not + console.log("Transaction Amount: " + transactionAmount); + console.log("newIR Amount: " + newIR); + console.log("currentTime: " + await latestTime()); + if (transactionAmount > newIR) { + console.log("\t\t Tx should fail"); + + await catchRevert ( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("\t\t Tx failed as expected"); + } else if (transactionAmount <= newIR) { + + console.log("\t\t Tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + console.log("\t\t Tx succeeded"); + } + await revertToSnapshot(snapId); + console.log("\t\t Finished test number "+j); + }; + + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("\t\t Removed daily restriction"); + } + + }); + + }); + +}); diff --git a/test/z_general_permission_manager_fuzzer.js b/test/z_general_permission_manager_fuzzer.js new file mode 100644 index 000000000..b74fd254b --- /dev/null +++ b/test/z_general_permission_manager_fuzzer.js @@ -0,0 +1,729 @@ +import latestTime from "./helpers/latestTime"; +import { signData } from "./helpers/signData"; +import { pk } from "./helpers/testprivateKey"; +import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; +import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; +import { catchRevert } from "./helpers/exceptions"; +import { + setUpPolymathNetwork, + deployGPMAndVerifyed, + deployCountTMAndVerifyed, + deployLockupVolumeRTMAndVerified, + deployPercentageTMAndVerified, + deployManualApprovalTMAndVerifyed +} from "./helpers/createInstances"; +import { encodeModuleCall } from "./helpers/encodeCall"; + +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const CountTransferManager = artifacts.require("./CountTransferManager"); +const VolumeRestrictionTransferManager = artifacts.require("./VolumeRestrictionTM"); +const PercentageTransferManager = artifacts.require("./PercentageTransferManager"); +const ManualApprovalTransferManager = artifacts.require("./ManualApprovalTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("GeneralPermissionManager Fuzz", async (accounts) => { + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let P_GeneralPermissionManagerFactory; + let I_SecurityTokenRegistryProxy; + let P_GeneralPermissionManager; + let I_GeneralTransferManagerFactory; + let I_VolumeRestrictionTransferManagerFactory; + let I_PercentageTransferManagerFactory; + let I_PercentageTransferManager; + let I_VolumeRestrictionTransferManager; + let I_GeneralPermissionManager; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + let I_CountTransferManagerFactory; + let I_CountTransferManager; + let I_SingleTradeVolumeRestrictionManagerFactory; + let I_SingleTradeVolumeRestrictionManager; + let I_SingleTradeVolumeRestrictionPercentageManager; + let P_SingleTradeVolumeRestrictionManager; + let P_SingleTradeVolumeRestrictionManagerFactory; + let I_ManualApprovalTransferManagerFactory; + let I_ManualApprovalTransferManager; + let I_STRGetter; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const managerDetails = web3.utils.fromAscii("Hello"); + const STVRParameters = ["bool", "uint256", "bool"]; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + // CountTransferManager details + const holderCount = 2; // Maximum number of token holders + let bytesSTO = encodeModuleCall(["uint256"], [holderCount]); + + let _details = "details holding for test"; + let _description = "some description"; + let testRepeat = 20; + + // permission manager fuzz test + let perms = ["ADMIN", "WHITELIST", "FLAGS", "TRANSFER_APPROVAL"]; + let totalPerms = perms.length; + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + + before(async () => { + currentTime = new BN(await latestTime()); + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[5]; + account_investor4 = accounts[6]; + account_delegate = accounts[7]; + // account_delegate2 = accounts[6]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STRGetter, + I_STGetter + ] = instances; + + // STEP 5: Deploy the GeneralDelegateManagerFactory + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + // STEP 6: Deploy the GeneralDelegateManagerFactory + [P_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); + + // Deploy Modules + [I_CountTransferManagerFactory] = await deployCountTMAndVerifyed(account_polymath, I_MRProxied, 0); + + [I_VolumeRestrictionTransferManagerFactory] = await deployLockupVolumeRTMAndVerified(account_polymath, I_MRProxied, 0); + + [I_PercentageTransferManagerFactory] = await deployPercentageTMAndVerified(account_polymath, I_MRProxied, 0); + + [I_ManualApprovalTransferManagerFactory] = await deployManualApprovalTMAndVerifyed(account_polymath, I_MRProxied, 0); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${I_GeneralPermissionManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + + it("Should successfully attach the General permission manager factory with the security token -- failed because Token is not paid", async () => { + let errorThrown = false; + await I_PolyToken.getTokens(new BN(web3.utils.toWei("2000", "ether")), token_owner); + await catchRevert( + I_SecurityToken.addModule(P_GeneralPermissionManagerFactory.address, "0x", new BN(web3.utils.toWei("2000", "ether")), new BN(0), { + from: token_owner + }) + ); + }); + + it("Should successfully attach the General permission manager factory with the security token - paid module", async () => { + let snapId = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); + const tx = await I_SecurityToken.addModule( + P_GeneralPermissionManagerFactory.address, + "0x", + new BN(web3.utils.toWei("2000", "ether")), + new BN(0), + { from: token_owner } + ); + assert.equal(tx.logs[3].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name).replace(/\u0000/g, ""), + "GeneralPermissionManager", + "GeneralPermissionManagerFactory module was not added" + ); + P_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the General permission manager factory with the security token - free module", async () => { + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "GeneralPermissionManager", + "GeneralPermissionManagerFactory module was not added" + ); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); + }); + }); + + describe("fuzz test for general transfer manager", async () => { + it("should pass fuzz test for changeIssuanceAddress(), changeSigningAddress() ", async () => { + console.log("1"); + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ((await I_GeneralPermissionManager.checkPermission(accounts[j], I_GeneralTransferManager.address, web3.utils.fromAscii("FLAGS"))) === true) { + await I_GeneralPermissionManager.changePermission(accounts[j], I_GeneralTransferManager.address, web3.utils.fromAscii("FLAGS"), false, { + from: token_owner + }); + } else if ( + (await I_GeneralPermissionManager.checkPermission(accounts[j], I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"))) === true + ) { + await I_GeneralPermissionManager.changePermission(accounts[j], I_GeneralTransferManager.address, web3.utils.fromAscii("WHITELIST"), false, { + from: token_owner + }); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + let fromTime = await latestTime(); + let toTime = await latestTime() + duration.days(20); + let expiryTime = toTime + duration.days(10); + + await I_GeneralPermissionManager.changePermission(accounts[j], I_GeneralTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + + let currentAllowAllTransferStats = await I_GeneralTransferManager.allowAllTransfers(); + let currentAllowAllWhitelistTransfersStats = await I_GeneralTransferManager.allowAllWhitelistTransfers(); + let currentAllowAllWhitelistIssuancesStats = await I_GeneralTransferManager.allowAllWhitelistIssuances(); + let currentAllowAllBurnTransfersStats = await I_GeneralTransferManager.allowAllBurnTransfers(); + console.log("2"); + // let userPerm = await I_GeneralPermissionManager.checkPermission(accounts[j], I_GeneralTransferManager.address, 'FLAGS'); + if (randomPerms === "FLAGS") { + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " about to start"); + await I_GeneralTransferManager.changeIssuanceAddress(accounts[j], { from: accounts[j] }); + assert.equal(await I_GeneralTransferManager.issuanceAddress(), accounts[j]); + + await I_GeneralTransferManager.changeSigningAddress(accounts[j], { from: accounts[j] }); + assert.equal(await I_GeneralTransferManager.signingAddress(), accounts[j]); + + await I_GeneralTransferManager.changeAllowAllTransfers(!currentAllowAllTransferStats, { from: accounts[j] }); + assert.equal(await I_GeneralTransferManager.allowAllTransfers(), !currentAllowAllTransferStats); + + await I_GeneralTransferManager.changeAllowAllWhitelistTransfers(!currentAllowAllWhitelistTransfersStats, { + from: accounts[j] + }); + assert.equal(await I_GeneralTransferManager.allowAllWhitelistTransfers(), !currentAllowAllWhitelistTransfersStats); + + await I_GeneralTransferManager.changeAllowAllWhitelistIssuances(!currentAllowAllWhitelistIssuancesStats, { + from: accounts[j] + }); + assert.equal(await I_GeneralTransferManager.allowAllWhitelistIssuances(), !currentAllowAllWhitelistIssuancesStats); + + await I_GeneralTransferManager.changeAllowAllBurnTransfers(!currentAllowAllBurnTransfersStats, { from: accounts[j] }); + assert.equal(await I_GeneralTransferManager.allowAllBurnTransfers(), !currentAllowAllBurnTransfersStats); + + console.log( + "Test number " + + i + + " with account " + + j + + " and perm " + + randomPerms + + " for functions with require perm FLAGS passed" + ); + } else { + await catchRevert(I_GeneralTransferManager.changeIssuanceAddress(accounts[j], { from: accounts[j] })); + await catchRevert(I_GeneralTransferManager.changeSigningAddress(accounts[j], { from: accounts[j] })); + await catchRevert( + I_GeneralTransferManager.changeAllowAllTransfers(!currentAllowAllTransferStats, { from: accounts[j] }) + ); + await catchRevert( + I_GeneralTransferManager.changeAllowAllWhitelistTransfers(!currentAllowAllWhitelistTransfersStats, { + from: accounts[j] + }) + ); + await catchRevert( + I_GeneralTransferManager.changeAllowAllWhitelistIssuances(!currentAllowAllWhitelistIssuancesStats, { + from: accounts[j] + }) + ); + await catchRevert( + I_GeneralTransferManager.changeAllowAllBurnTransfers(!currentAllowAllBurnTransfersStats, { from: accounts[j] }) + ); + console.log( + "Test number " + + i + + " with account " + + j + + " and perm " + + randomPerms + + " for functions require perm FLAGS failed as expected" + ); + } + + console.log("3"); + if (randomPerms === "WHITELIST") { + let tx = await I_GeneralTransferManager.modifyKYCData(accounts[j], fromTime, toTime, expiryTime, { + from: accounts[j] + }); + assert.equal(tx.logs[0].args._investor, accounts[j]); + console.log("3.1"); + let tx2 = await I_GeneralTransferManager.modifyKYCDataMulti( + [accounts[3], accounts[4]], + [fromTime, fromTime], + [toTime, toTime], + [expiryTime, expiryTime], + { from: accounts[j] } + ); + console.log(tx2.logs[1].args); + assert.equal(tx2.logs[1].args._investor, accounts[4]); + console.log("3.2"); + } else { + console.log("3.3"); + await catchRevert( + I_GeneralTransferManager.modifyKYCData(accounts[j], fromTime, toTime, expiryTime, { from: accounts[j] }) + ); + console.log("3.4"); + await catchRevert( + I_GeneralTransferManager.modifyKYCDataMulti( + [accounts[3], accounts[4]], + [fromTime, fromTime], + [toTime, toTime], + [expiryTime, expiryTime], + { from: accounts[j] } + ) + ); + console.log("3.5"); + } + } + console.log("4"); + await I_GeneralTransferManager.changeIssuanceAddress("0x0000000000000000000000000000000000000000", { from: token_owner }); + }); + }); + + describe("fuzz test for count transfer manager", async () => { + it("Should successfully attach the CountTransferManager with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_CountTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "CountTransferManager", + "CountTransferManager module was not added" + ); + I_CountTransferManager = await CountTransferManager.at(tx.logs[2].args._module); + }); + + it("should pass fuzz test for changeHolderCount()", async () => { + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ((await I_GeneralPermissionManager.checkPermission(accounts[j], I_CountTransferManager.address, web3.utils.fromAscii("ADMIN"))) === true) { + await I_GeneralPermissionManager.changePermission(accounts[j], I_CountTransferManager.address, web3.utils.fromAscii("ADMIN"), false, { + from: token_owner + }); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + await I_GeneralPermissionManager.changePermission(accounts[j], I_CountTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + if (randomPerms === "ADMIN") { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should pass"); + await I_CountTransferManager.changeHolderCount(i + 1, { from: accounts[j] }); + assert.equal((await I_CountTransferManager.maxHolderCount()).toNumber(), i + 1); + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " passed"); + } else { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should failed"); + await catchRevert(I_CountTransferManager.changeHolderCount(i + 1, { from: accounts[j] })); + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " failed as expected"); + } + } + }); + }); + + describe("fuzz test for percentage transfer manager", async () => { + // PercentageTransferManager details + const holderPercentage = 70 * 10 ** 16; // Maximum number of token holders + + let bytesSTO = web3.eth.abi.encodeFunctionCall( + { + name: "configure", + type: "function", + inputs: [ + { + type: "uint256", + name: "_maxHolderPercentage" + }, + { + type: "bool", + name: "_allowPrimaryIssuance" + } + ] + }, + [holderPercentage, false] + ); + + it("Should successfully attach the percentage transfer manager with the security token", async () => { + console.log("1"); + const tx = await I_SecurityToken.addModule(I_PercentageTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), { from: token_owner }); + I_PercentageTransferManager = await PercentageTransferManager.at(tx.logs[2].args._module); + }); + + it("should pass fuzz test for modifyWhitelist with perm WHITELIST", async () => { + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ( + (await I_GeneralPermissionManager.checkPermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii("WHITELIST"))) === + true + ) { + await I_GeneralPermissionManager.changePermission( + accounts[j], + I_PercentageTransferManager.address, + web3.utils.fromAscii("WHITELIST"), + false, + { from: token_owner } + ); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + await I_GeneralPermissionManager.changePermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + + //try add multi lock ups + if (randomPerms === "WHITELIST") { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should pass"); + await I_PercentageTransferManager.modifyWhitelist(account_investor3, 1, { from: accounts[j] }); + console.log("Test number " + i + " with account " + j + " and perm WHITELIST passed as expected"); + } else { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should failed"); + await catchRevert(I_PercentageTransferManager.modifyWhitelist(account_investor3, 1, { from: accounts[j] })); + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " failed as expected"); + } + } + }); + + it("should pass fuzz test for modifyWhitelistMulti with perm WHITELIST", async () => { + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ( + (await I_GeneralPermissionManager.checkPermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii("WHITELIST"))) === + true + ) { + await I_GeneralPermissionManager.changePermission( + accounts[j], + I_PercentageTransferManager.address, + web3.utils.fromAscii("WHITELIST"), + false, + { from: token_owner } + ); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + await I_GeneralPermissionManager.changePermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + + if (randomPerms === "WHITELIST") { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should pass"); + await I_PercentageTransferManager.modifyWhitelistMulti([account_investor3, account_investor4], [0, 1], { + from: accounts[j] + }); + console.log("Test number " + i + " with account " + j + " and perm WHITELIST passed as expected"); + } else { + // console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should failed"); + await catchRevert( + I_PercentageTransferManager.modifyWhitelistMulti([account_investor3, account_investor4], [0, 1], { + from: accounts[j] + }) + ); + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " failed as expected"); + } + } + }); + + it("should pass fuzz test for setAllowPrimaryIssuance with perm ADMIN", async () => { + // let snapId = await takeSnapshot(); + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ( + (await I_GeneralPermissionManager.checkPermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii("ADMIN"))) === true + ) { + await I_GeneralPermissionManager.changePermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii("ADMIN"), false, { + from: token_owner + }); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + await I_GeneralPermissionManager.changePermission(accounts[j], I_PercentageTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + + let primaryIssuanceStat = await I_PercentageTransferManager.allowPrimaryIssuance({ from: token_owner }); + + if (randomPerms === "ADMIN") { + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should pass"); + await I_PercentageTransferManager.setAllowPrimaryIssuance(!primaryIssuanceStat, { from: accounts[j] }); + console.log("Test number " + i + " with account " + j + " and perm ADMIN passed as expected"); + } else { + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should failed"); + await catchRevert(I_PercentageTransferManager.setAllowPrimaryIssuance(!primaryIssuanceStat, { from: accounts[j] })); + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " failed as expected"); + } + // await revertToSnapshot(snapId); + } + }); + }); + + describe("fuzz test for manual approval transfer manager", async () => { + it("Should successfully attach the ManualApprovalTransferManager with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_ManualApprovalTransferManagerFactory.address, "0x0", new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "ManualApprovalTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "ManualApprovalTransferManager", + "ManualApprovalTransferManager module was not added" + ); + I_ManualApprovalTransferManager = await ManualApprovalTransferManager.at(tx.logs[2].args._module); + }); + + it("should pass fuzz test for addManualApproval & revokeManualApproval with perm TRANSFER_APPROVAL", async () => { + let tx; + // fuzz test loop over total times of testRepeat, inside each loop, we use a variable j to randomly choose an account out of the 10 default accounts + for (var i = 2; i < testRepeat; i++) { + let snapId = await takeSnapshot(); + + var j = Math.floor(Math.random() * 10); + if (j === 1 || j === 0) { + j = 2; + } // exclude account 1 & 0 because they might come with default perms + + // add account as a Delegate if it is not + if ((await I_GeneralPermissionManager.checkDelegate(accounts[j])) !== true) { + await I_GeneralPermissionManager.addDelegate(accounts[j], web3.utils.fromAscii(_details), { from: token_owner }); + } + + // target permission should alaways be false for each test before assigning + if ( + (await I_GeneralPermissionManager.checkPermission( + accounts[j], + I_ManualApprovalTransferManager.address, + web3.utils.fromAscii("TRANSFER_APPROVAL") + )) === true + ) { + await I_GeneralPermissionManager.changePermission( + accounts[j], + I_ManualApprovalTransferManager.address, + web3.utils.fromAscii("TRANSFER_APPROVAL"), + false, + { from: token_owner } + ); + } + + // assign a random perm + let randomPerms = perms[Math.floor(Math.random() * Math.floor(totalPerms))]; + await I_GeneralPermissionManager.changePermission(accounts[j], I_ManualApprovalTransferManager.address, web3.utils.fromAscii(randomPerms), true, { + from: token_owner + }); + + if (randomPerms === "TRANSFER_APPROVAL") { + console.log("Test number " + i + " with account " + j + " and perm TRANSFER_APPROVAL " + " should pass"); + let nextTime = await latestTime() + duration.days(1); + await I_ManualApprovalTransferManager.addManualApproval( + account_investor1, + account_investor4, + new BN(web3.utils.toWei("2", "ether")), + nextTime, + web3.utils.fromAscii(_details), + { from: accounts[j] } + ); + + console.log("2"); + tx = await I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, { + from: accounts[j] + }); + assert.equal(tx.logs[0].args._from, account_investor1); + assert.equal(tx.logs[0].args._to, account_investor4); + assert.equal(tx.logs[0].args._addedBy, accounts[j]); + + console.log("3"); + console.log("Test number " + i + " with account " + j + " and perm TRANSFER_APPROVAL passed as expected"); + } else { + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " should failed"); + let nextTime = await latestTime() + duration.days(1); + await catchRevert( + I_ManualApprovalTransferManager.addManualApproval( + account_investor1, + account_investor4, + new BN(web3.utils.toWei("2", "ether")), + nextTime, + web3.utils.fromAscii(_details), + { from: accounts[j] } + ) + ); + + nextTime = await latestTime() + duration.days(1); + await I_ManualApprovalTransferManager.addManualApproval( + account_investor1, + account_investor4, + new BN(web3.utils.toWei("2", "ether")), + nextTime, + web3.utils.fromAscii(_details), + { from: token_owner } + ); + + await catchRevert( + I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, account_investor4, { + from: accounts[j] + }) + ); + + console.log("Test number " + i + " with account " + j + " and perm " + randomPerms + " failed as expected"); + } + + await revertToSnapshot(snapId); + } + }); + }); +}); diff --git a/test/z_vesting_escrow_wallet.js b/test/z_vesting_escrow_wallet.js new file mode 100644 index 000000000..02ce65b8f --- /dev/null +++ b/test/z_vesting_escrow_wallet.js @@ -0,0 +1,1222 @@ +import {deployGPMAndVerifyed, deployVestingEscrowWalletAndVerifyed, setUpPolymathNetwork} from "./helpers/createInstances"; +import latestTime from "./helpers/latestTime"; +import {duration as durationUtil, latestBlock, promisifyLogWatch} from "./helpers/utils"; +import {catchRevert} from "./helpers/exceptions"; +import {increaseTime} from "./helpers/time"; +import {encodeModuleCall} from "./helpers/encodeCall"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const VestingEscrowWallet = artifacts.require('./VestingEscrowWallet.sol'); +const STGetter = artifacts.require("./STGetter.sol"); +const Web3 = require('web3'); +const BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));// Hardcoded development port + +contract('VestingEscrowWallet', accounts => { + + const CREATED = 0; + const STARTED = 1; + const COMPLETED = 2; + + // Accounts Variable declaration + let account_polymath; + let token_owner; + let wallet_admin; + let account_beneficiary1; + let account_beneficiary2; + let account_beneficiary3; + + let beneficiaries; + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_SecurityTokenRegistryProxy; + let I_GeneralPermissionManagerFactory; + let I_GeneralTransferManagerFactory; + let I_VestingEscrowWalletFactory; + let I_GeneralPermissionManager; + let I_VestingEscrowWallet; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_MRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = web3.utils.toHex("Hello I am legit delegate"); + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + + before(async () => { + currentTime = new BN(await latestTime()); + // Accounts setup + account_polymath = accounts[0]; + token_owner = accounts[1]; + wallet_admin = accounts[2]; + + account_beneficiary1 = accounts[6]; + account_beneficiary2 = accounts[7]; + account_beneficiary3 = accounts[8]; + + beneficiaries = [ + account_beneficiary1, + account_beneficiary2, + account_beneficiary3 + ]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 2: Deploy the GeneralDelegateManagerFactory + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + + // STEP 3: Deploy the VestingEscrowWallet + [I_VestingEscrowWalletFactory] = await deployVestingEscrowWalletAndVerifyed(account_polymath, I_MRProxied, 0); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${I_GeneralPermissionManagerFactory.address} + + I_VestingEscrowWalletFactory: ${I_VestingEscrowWalletFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async() => { + + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toString(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + + }); + + it("Should successfully attach the General permission manager factory with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toString(), delegateManagerKey, "General Permission Manager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "GeneralPermissionManager", + "GeneralPermissionManagerFactory module was not added" + ); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); + }); + + it("Should successfully attach the VestingEscrowWallet with the security token", async () => { + let bytesData = encodeModuleCall( + ["address"], + [token_owner] + ); + + await I_SecurityToken.changeGranularity(1, {from: token_owner}); + const tx = await I_SecurityToken.addModule(I_VestingEscrowWalletFactory.address, bytesData, 0, 0, { from: token_owner }); + + assert.equal(tx.logs[2].args._types[0].toString(), 6, "VestingEscrowWallet doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "VestingEscrowWallet", + "VestingEscrowWallet module was not added" + ); + I_VestingEscrowWallet = await VestingEscrowWallet.at(tx.logs[2].args._module); + }); + + it("Should Buy the tokens for token_owner", async() => { + // Add the Investor in to the whitelist + let tx = await I_GeneralTransferManager.modifyKYCData( + token_owner, + currentTime, + currentTime, + currentTime.add(new BN(durationUtil.days(10))), + { + from: token_owner, + gas: 6000000 + } + ); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), token_owner.toLowerCase(), "Failed in adding the token_owner in whitelist"); + + // Mint some tokens + await I_SecurityToken.issue(token_owner, web3.utils.toHex(web3.utils.toWei('1', 'ether')), "0x0", { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(token_owner)).toString(), + web3.utils.toWei('1', 'ether') + ); + + }); + + it("Should whitelist investors", async() => { + // Add the Investor in to the whitelist + let tx = await I_GeneralTransferManager.modifyKYCDataMulti( + [I_VestingEscrowWallet.address, account_beneficiary1, account_beneficiary2, account_beneficiary3], + [currentTime, currentTime, currentTime, currentTime], + [currentTime, currentTime, currentTime, currentTime], + [currentTime.add(new BN(durationUtil.days(10))), currentTime.add(new BN(durationUtil.days(10))), currentTime.add(new BN(durationUtil.days(10))), currentTime.add(new BN(durationUtil.days(10)))], + { + from: token_owner, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor, I_VestingEscrowWallet.address); + assert.equal(tx.logs[1].args._investor, account_beneficiary1); + assert.equal(tx.logs[2].args._investor, account_beneficiary2); + assert.equal(tx.logs[3].args._investor, account_beneficiary3); + }); + + it("Should successfully add the delegate", async() => { + let tx = await I_GeneralPermissionManager.addDelegate(wallet_admin, delegateDetails, { from: token_owner}); + assert.equal(tx.logs[0].args._delegate, wallet_admin); + }); + + it("Should provide the permission", async() => { + let tx = await I_GeneralPermissionManager.changePermission( + wallet_admin, + I_VestingEscrowWallet.address, + web3.utils.toHex("ADMIN"), + true, + {from: token_owner} + ); + assert.equal(tx.logs[0].args._delegate, wallet_admin); + }); + + it("Should get the permission", async () => { + let perm = await I_VestingEscrowWallet.getPermissions.call(); + assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ""), "ADMIN"); + }); + + it("Should get the tags of the factory", async () => { + let tags = await I_VestingEscrowWalletFactory.getTags.call(); + assert.equal(tags.length, 2); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ""), "Vested"); + assert.equal(web3.utils.toAscii(tags[1]).replace(/\u0000/g, ""), "Escrow Wallet"); + }); + + it("Should get the instructions of the factory", async () => { + assert.equal( + (await I_VestingEscrowWalletFactory.getInstructions.call()).replace(/\u0000/g, ""), + "Issuer can deposit tokens to the contract and create the vesting schedule for the given address (Affiliate/Employee). These address can withdraw tokens according to there vesting schedule." + ); + }); + + }); + + describe("Depositing and withdrawing tokens", async () => { + + it("Should not be able to change treasury wallet -- fail because address is invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.changeTreasuryWallet(address_zero, {from: token_owner}) + ); + }); + + it("Should not be able to deposit -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.changeTreasuryWallet(account_beneficiary1, {from: account_beneficiary1}) + ); + }); + + it("Should change treasury wallet", async () => { + const tx = await I_VestingEscrowWallet.changeTreasuryWallet(account_beneficiary1, {from: token_owner}); + + assert.equal(tx.logs[0].args._newWallet, account_beneficiary1); + assert.equal(tx.logs[0].args._oldWallet, token_owner); + let treasuryWallet = await I_VestingEscrowWallet.treasuryWallet.call(); + assert.equal(treasuryWallet, account_beneficiary1); + + await I_VestingEscrowWallet.changeTreasuryWallet(token_owner, {from: token_owner}); + }); + + it("Should fail to deposit zero amount of tokens", async () => { + await catchRevert( + I_VestingEscrowWallet.depositTokens(0, {from: token_owner}) + ); + }); + + it("Should not be able to deposit -- fail because of permissions check", async () => { + let numberOfTokens = 25000; + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, { from: token_owner }); + await catchRevert( + I_VestingEscrowWallet.depositTokens(25000, {from: account_beneficiary1}) + ); + }); + + it("Should deposit tokens for new vesting schedules", async () => { + let numberOfTokens = 25000; + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, { from: token_owner }); + const tx = await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + + assert.equal(tx.logs[0].args._numberOfTokens, numberOfTokens); + + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + assert.equal(unassignedTokens, numberOfTokens); + + let balance = await I_SecurityToken.balanceOf.call(I_VestingEscrowWallet.address); + assert.equal(balance.toString(), numberOfTokens); + }); + + it("Should not be able to withdraw tokens to a treasury -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.sendToTreasury(10, {from: account_beneficiary1}) + ); + }); + + it("Should not be able to withdraw tokens to a treasury -- fail because of zero amount", async () => { + await catchRevert( + I_VestingEscrowWallet.sendToTreasury(0, {from: wallet_admin}) + ); + }); + + it("Should not be able to withdraw tokens to a treasury -- fail because amount is greater than unassigned tokens", async () => { + let numberOfTokens = 25000 * 2; + await catchRevert( + I_VestingEscrowWallet.sendToTreasury(numberOfTokens, {from: wallet_admin}) + ); + }); + + it("Should withdraw tokens to a treasury", async () => { + let numberOfTokens = 25000; + const tx = await I_VestingEscrowWallet.sendToTreasury(numberOfTokens, {from: wallet_admin}); + + assert.equal(tx.logs[0].args._numberOfTokens, numberOfTokens); + + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + assert.equal(unassignedTokens, 0); + + let balance = await I_SecurityToken.balanceOf.call(I_VestingEscrowWallet.address); + assert.equal(balance.toString(), 0); + }); + + it("Should not be able to push available tokens -- fail because of permissions check", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-01"); + let numberOfTokens = 75000; + let duration = new BN(durationUtil.seconds(30)); + let frequency = new BN(durationUtil.seconds(10)); + let timeShift = new BN(durationUtil.seconds(100)); + let startTime = currentTime.add(timeShift); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, { from: token_owner }); + await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.addSchedule(account_beneficiary3, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + await increaseTime(durationUtil.seconds(110)); + + await catchRevert( + I_VestingEscrowWallet.pushAvailableTokens(account_beneficiary3, {from: account_beneficiary1}) + ); + }); + + it("Should not be able to remove template -- fail because template is used", async () => { + await catchRevert( + I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-01"), {from: wallet_admin}) + ); + }); + + it("Should push available tokens to the beneficiary address", async () => { + let numberOfTokens = 75000; + const tx = await I_VestingEscrowWallet.pushAvailableTokens(account_beneficiary3, {from: wallet_admin}); + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[0].args._numberOfTokens.toString(), numberOfTokens / 3); + + let balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), numberOfTokens / 3); + + await I_SecurityToken.transfer(token_owner, balance, {from: account_beneficiary3}); + }); + + it("Should fail to modify vesting schedule -- fail because schedule already started", async () => { + let templateName = web3.utils.toHex("template-01"); + let startTime = currentTime + 100; + await catchRevert( + I_VestingEscrowWallet.modifySchedule(account_beneficiary3, templateName, startTime, {from: wallet_admin}) + ); + + await I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary3, {from: wallet_admin}); + await I_VestingEscrowWallet.removeTemplate(templateName, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + it("Should fail to modify vesting schedule -- fail because date in the past", async () => { + await catchRevert( + I_VestingEscrowWallet.modifySchedule(account_beneficiary3, web3.utils.toHex("template-01"), currentTime - 1000, {from: wallet_admin}) + ); + }); + + it("Should withdraw available tokens to the beneficiary address", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-02"); + let numberOfTokens = 33000; + let duration = new BN(durationUtil.seconds(30)); + let frequency = new BN(durationUtil.seconds(10)); + let timeShift = new BN(durationUtil.seconds(100)); + let startTime = currentTime.add(timeShift); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, { from: token_owner }); + await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.addSchedule(account_beneficiary3, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + await increaseTime(durationUtil.seconds(130)); + + const tx = await I_VestingEscrowWallet.pullAvailableTokens({from: account_beneficiary3}); + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[0].args._numberOfTokens.toString(), numberOfTokens); + + let balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), numberOfTokens); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary3, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, COMPLETED); + + await I_SecurityToken.transfer(token_owner, balance, {from: account_beneficiary3}); + await I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary3, {from: wallet_admin}); + await I_VestingEscrowWallet.removeTemplate(templateName, {from: wallet_admin}); + }); + + it("Should withdraw available tokens 2 times by 3 schedules to the beneficiary address", async () => { + currentTime = new BN(await latestTime()); + let schedules = [ + { + templateName: web3.utils.toHex("template-1-01"), + numberOfTokens: new BN(100000), + duration: new BN(durationUtil.minutes(4)), + frequency: new BN(durationUtil.minutes(1)) + }, + { + templateName: web3.utils.toHex("template-1-02"), + numberOfTokens: new BN(30000), + duration: new BN(durationUtil.minutes(6)), + frequency: new BN(durationUtil.minutes(1)) + }, + { + templateName: web3.utils.toHex("template-1-03"), + numberOfTokens: new BN(2000), + duration: new BN(durationUtil.minutes(10)), + frequency: new BN(durationUtil.minutes(1)) + } + ]; + + let timeShift = new BN(durationUtil.seconds(100)); + let totalNumberOfTokens = getTotalNumberOfTokens(schedules); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + for (let i = 0; i < schedules.length; i++) { + let templateName = schedules[i].templateName; + let numberOfTokens = schedules[i].numberOfTokens; + let duration = schedules[i].duration; + let frequency = schedules[i].frequency; + let startTime = currentTime.add(timeShift); + await I_VestingEscrowWallet.addSchedule(account_beneficiary3, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + } + let stepCount = 6; + await increaseTime(durationUtil.minutes(stepCount) + durationUtil.seconds(100)); + + let numberOfTokens = 100000 + (30000 / 6 * stepCount) + (2000 / 10 * stepCount); + const tx = await I_VestingEscrowWallet.pullAvailableTokens({from: account_beneficiary3}); + + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[0].args._numberOfTokens.toString(), 100000); + assert.equal(tx.logs[1].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[1].args._numberOfTokens.toString(), 30000 / 6 * stepCount); + assert.equal(tx.logs[2].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[2].args._numberOfTokens.toString(), 2000 / 10 * stepCount); + + let balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), numberOfTokens); + + stepCount = 4; + await increaseTime(durationUtil.minutes(stepCount) + durationUtil.seconds(100)); + + const tx2 = await I_VestingEscrowWallet.pullAvailableTokens({from: account_beneficiary3}); + assert.equal(tx2.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx2.logs[0].args._numberOfTokens.toString(), 2000 / 10 * stepCount); + + balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), totalNumberOfTokens); + + await I_SecurityToken.transfer(token_owner, balance, {from: account_beneficiary3}); + await I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary3, {from: wallet_admin}); + for (let i = 0; i < schedules.length; i++) { + await I_VestingEscrowWallet.removeTemplate(schedules[i].templateName, {from: wallet_admin}); + } + }); + + }); + + describe("Adding, modifying and revoking vesting schedule", async () => { + let template_2_01 = web3.utils.toHex("template-2-01"); + + let schedules = [ + { + templateName: web3.utils.toHex("template-2-01"), + numberOfTokens: 100000, + duration: new BN(durationUtil.years(4)), + frequency: new BN(durationUtil.years(1)), + // startTime: currentTime.add(new BN(durationUtil.days(1))) + }, + { + templateName: web3.utils.toHex("template-2-02"), + numberOfTokens: 30000, + duration: new BN(durationUtil.weeks(6)), + frequency: new BN(durationUtil.weeks(1)), + // startTime: currentTime.add(new BN(durationUtil.days(2))) + }, + { + templateName: web3.utils.toHex("template-2-03"), + numberOfTokens: 2000, + duration: new BN(durationUtil.days(10)), + frequency: new BN(durationUtil.days(2)), + // startTime: currentTime.add(new BN(durationUtil.days(3))) + } + ]; + + it("Should fail to add vesting schedule to the beneficiary address -- fail because address in invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.addSchedule(address_zero, template_2_01, 100000, 4, 1, currentTime.add(new BN(durationUtil.days(1))), {from: wallet_admin}) + ); + }); + + it("Should fail to add vesting schedule to the beneficiary address -- fail because start date in the past", async () => { + await catchRevert( + I_VestingEscrowWallet.addSchedule(account_beneficiary1, template_2_01, 100000, 4, 1, currentTime.add(new BN(durationUtil.days(1))), {from: wallet_admin}) + ); + }); + + it("Should fail to add vesting schedule to the beneficiary address -- fail because number of tokens is 0", async () => { + await catchRevert( + I_VestingEscrowWallet.addSchedule(account_beneficiary1, template_2_01, 0, 4, 1, currentTime.add(new BN(durationUtil.days(1))), {from: wallet_admin}) + ); + }); + + it("Should fail to add vesting schedule to the beneficiary address -- fail because duration can't be divided entirely by frequency", async () => { + await catchRevert( + I_VestingEscrowWallet.addSchedule(account_beneficiary1, template_2_01, 100000, 4, 3, currentTime.add(new BN(durationUtil.days(1))), {from: wallet_admin}) + ); + }); + + it("Should fail to add vesting schedule to the beneficiary address -- fail because number of tokens can't be divided entirely by period count", async () => { + await catchRevert( + I_VestingEscrowWallet.addSchedule(account_beneficiary1, template_2_01, 5, 4, 1, currentTime.add(new BN(durationUtil.days(1))), {from: wallet_admin}) + ); + }); + + it("Should fail to get vesting schedule -- fail because address is invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.getSchedule(address_zero, template_2_01) + ); + }); + + it("Should fail to get vesting schedule -- fail because schedule not found", async () => { + + await catchRevert( + I_VestingEscrowWallet.getSchedule(account_beneficiary1, template_2_01) + ); + }); + + it("Should fail to get count of vesting schedule -- fail because address is invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.getScheduleCount(address_zero) + ); + }); + + it("Should not be able to add schedule -- fail because of permissions check", async () => { + let templateName = schedules[0].templateName; + let numberOfTokens = schedules[0].numberOfTokens; + let duration = schedules[0].duration; + let frequency = schedules[0].frequency; + let startTime = currentTime.add(new BN(durationUtil.days(1))); + currentTime = new BN(await latestTime()); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + await catchRevert( + I_VestingEscrowWallet.addSchedule(account_beneficiary1, templateName, numberOfTokens, duration, frequency, startTime, {from: account_beneficiary1}) + ); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + it("Should add vesting schedule to the beneficiary address", async () => { + currentTime = new BN(await latestTime()); + let templateName = schedules[0].templateName; + let numberOfTokens = schedules[0].numberOfTokens; + let duration = schedules[0].duration; + let frequency = schedules[0].frequency; + let startTime = currentTime.add(new BN(durationUtil.days(1))); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + const tx = await I_VestingEscrowWallet.addSchedule(account_beneficiary1, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + + checkTemplateLog(tx.logs[0], templateName, numberOfTokens, duration, frequency); + checkScheduleLog(tx.logs[1], account_beneficiary1, templateName, startTime); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary1); + assert.equal(scheduleCount, 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary1, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, CREATED); + + let templates = await I_VestingEscrowWallet.getTemplateNames.call(account_beneficiary1); + assert.equal(web3.utils.hexToUtf8(templates[0]), web3.utils.hexToUtf8(templateName)); + }); + + it("Should add vesting schedule without depositing to the beneficiary address", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-2-01-2"); + let numberOfTokens = schedules[0].numberOfTokens; + let duration = schedules[0].duration; + let frequency = schedules[0].frequency; + let startTime = currentTime.add(new BN(durationUtil.days(1))); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, {from: token_owner}); + const tx = await I_VestingEscrowWallet.addSchedule(account_beneficiary1, templateName, numberOfTokens, duration, frequency, startTime, {from: token_owner}); + + checkTemplateLog(tx.logs[0], templateName, numberOfTokens, duration, frequency); + assert.equal(tx.logs[1].args._numberOfTokens, numberOfTokens); + checkScheduleLog(tx.logs[2], account_beneficiary1, templateName, startTime); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary1); + assert.equal(scheduleCount, 2); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary1, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, CREATED); + + await I_VestingEscrowWallet.revokeSchedule(account_beneficiary1, templateName, {from: wallet_admin}); + await I_VestingEscrowWallet.removeTemplate(templateName, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + it("Should fail to modify vesting schedule -- fail because schedule not found", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-2-03"); + let startTime = currentTime.add(new BN(durationUtil.days(1))); + await catchRevert( + I_VestingEscrowWallet.modifySchedule(account_beneficiary1, templateName, startTime, {from: wallet_admin}) + ); + }); + + it("Should not be able to modify schedule -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.modifySchedule(account_beneficiary1, web3.utils.toHex("template-2-01"), currentTime.add(new BN(100)), {from: account_beneficiary1}) + ); + }); + + it("Should modify vesting schedule for the beneficiary's address", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-2-01"); + let numberOfTokens = schedules[0].numberOfTokens; + let duration = schedules[0].duration; + let frequency = schedules[0].frequency; + let startTime = currentTime.add(new BN(durationUtil.days(2))); + const tx = await I_VestingEscrowWallet.modifySchedule(account_beneficiary1, templateName, startTime, {from: wallet_admin}); + + checkScheduleLog(tx.logs[0], account_beneficiary1, templateName, startTime); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary1); + assert.equal(scheduleCount.toString(), 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary1, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, CREATED); + }); + + it("Should not be able to revoke schedule -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeSchedule(account_beneficiary1, web3.utils.toHex("template-2-01"), {from: account_beneficiary1}) + ); + }); + + it("Should revoke vesting schedule from the beneficiary address", async () => { + let templateName = web3.utils.toHex("template-2-01"); + const tx = await I_VestingEscrowWallet.revokeSchedule(account_beneficiary1, templateName, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary1); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._templateName), web3.utils.hexToUtf8(templateName)); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary1); + assert.equal(scheduleCount, 0); + + await I_VestingEscrowWallet.removeTemplate(templateName, {from: wallet_admin}) + }); + + it("Should fail to revoke vesting schedule -- fail because address is invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeSchedule(address_zero, web3.utils.toHex("template-2-01"), {from: wallet_admin}) + ); + }); + + it("Should fail to revoke vesting schedule -- fail because schedule not found", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeSchedule(account_beneficiary1, web3.utils.toHex("template-2-02"), {from: wallet_admin}) + ); + }); + + it("Should fail to revoke vesting schedules -- fail because address is invalid", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeAllSchedules(address_zero, {from: wallet_admin}) + ); + }); + + it("Should add 3 vesting schedules to the beneficiary address", async () => { + currentTime = new BN(await latestTime()); + let startTimes = [ + currentTime.add(new BN(durationUtil.days(1))), + currentTime.add(new BN(durationUtil.days(2))), + currentTime.add(new BN(durationUtil.days(3))) + ]; + let totalNumberOfTokens = getTotalNumberOfTokens(schedules); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + for (let i = 0; i < schedules.length; i++) { + let templateName = schedules[i].templateName; + let numberOfTokens = schedules[i].numberOfTokens; + let duration = schedules[i].duration; + let frequency = schedules[i].frequency; + let startTime = startTimes[i]; + const tx = await I_VestingEscrowWallet.addSchedule(account_beneficiary2, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + + checkTemplateLog(tx.logs[0], templateName, numberOfTokens, duration, frequency); + checkScheduleLog(tx.logs[1], account_beneficiary2, templateName, startTime); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary2); + assert.equal(scheduleCount, i + 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary2, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, CREATED); + } + }); + + it("Should not be able to revoke schedules -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary1, {from: account_beneficiary1}) + ); + }); + + it("Should revoke 1 of 3 vesting schedule from the beneficiary address", async () => { + let templateName = schedules[1].templateName; + const tx = await I_VestingEscrowWallet.revokeSchedule(account_beneficiary2, templateName, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary2); + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._templateName), web3.utils.hexToUtf8(templateName)); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary2); + assert.equal(scheduleCount, 2); + }); + + it("Should revoke 2 vesting schedules from the beneficiary address", async () => { + const tx = await I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary2, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary2); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary2); + assert.equal(scheduleCount, 0); + }); + + it("Should push available tokens during revoking vesting schedule", async () => { + currentTime = new BN(await latestTime()); + let schedules = [ + { + templateName: web3.utils.toHex("template-3-01"), + numberOfTokens: new BN(100000), + duration: new BN(durationUtil.minutes(4)), + frequency: new BN(durationUtil.minutes(1)) + }, + { + templateName: web3.utils.toHex("template-3-02"), + numberOfTokens: new BN(30000), + duration: new BN(durationUtil.minutes(6)), + frequency: new BN(durationUtil.minutes(1)) + }, + { + templateName: web3.utils.toHex("template-3-03"), + numberOfTokens: new BN(2000), + duration: new BN(durationUtil.minutes(10)), + frequency: new BN(durationUtil.minutes(1)) + } + ]; + + let totalNumberOfTokens = getTotalNumberOfTokens(schedules); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + for (let i = 0; i < schedules.length; i++) { + let templateName = schedules[i].templateName; + let numberOfTokens = schedules[i].numberOfTokens; + let duration = schedules[i].duration; + let frequency = schedules[i].frequency; + let startTime = currentTime.add(new BN(100)); + await I_VestingEscrowWallet.addSchedule(account_beneficiary3, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); + } + let stepCount = 3; + await increaseTime(durationUtil.minutes(stepCount) + durationUtil.seconds(100)); + + const tx = await I_VestingEscrowWallet.revokeSchedule(account_beneficiary3, web3.utils.toHex("template-3-01"), {from: wallet_admin}); + assert.equal(tx.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx.logs[0].args._numberOfTokens.toString(), 100000 / 4 * stepCount); + + let balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), 100000 / 4 * stepCount); + + stepCount = 7; + await increaseTime(durationUtil.minutes(stepCount)); + + const tx2 = await I_VestingEscrowWallet.revokeAllSchedules(account_beneficiary3, {from: wallet_admin}); + assert.equal(tx2.logs[0].args._beneficiary, account_beneficiary3); + assert.equal(tx2.logs[0].args._numberOfTokens.toString(), 2000); + assert.equal(tx2.logs[1].args._beneficiary, account_beneficiary3); + assert.equal(tx2.logs[1].args._numberOfTokens.toString(), 30000); + + for (let i = 0; i < schedules.length; i++) { + await I_VestingEscrowWallet.removeTemplate(schedules[i].templateName, {from: wallet_admin}); + } + + balance = await I_SecurityToken.balanceOf.call(account_beneficiary3); + assert.equal(balance.toString(), totalNumberOfTokens - 100000 / 4); + + await I_SecurityToken.transfer(token_owner, balance, {from: account_beneficiary3}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + }); + + describe("Adding, using and removing templates", async () => { + let schedules = [ + { + templateName: web3.utils.toHex("template-4-01"), + numberOfTokens: 100000, + duration: new BN(durationUtil.years(4)), + frequency: new BN(durationUtil.years(1)), + // startTime: currentTime.add(new BN(durationUtil.days(1))) + }, + { + templateName: web3.utils.toHex("template-4-02"), + numberOfTokens: 30000, + duration: new BN(durationUtil.weeks(6)), + frequency: new BN(durationUtil.weeks(1)), + // startTime: currentTime.add(new BN(durationUtil.days(2))) + }, + { + templateName: web3.utils.toHex("template-4-03"), + numberOfTokens: 2000, + duration: new BN(durationUtil.days(10)), + frequency: new BN(durationUtil.days(2)), + // startTime: currentTime.add(new BN(durationUtil.days(3))) + } + ]; + + it("Should not be able to add template -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.addTemplate(web3.utils.toHex("template-4-01"), 25000, 4, 1, {from: account_beneficiary1}) + ); + }); + + it("Should not be able to add template -- fail because of invalid name", async () => { + await catchRevert( + I_VestingEscrowWallet.addTemplate(web3.utils.toHex(""), 25000, 4, 1, {from: wallet_admin}) + ); + }); + + it("Should add 3 Templates", async () => { + let oldTemplateCount = await I_VestingEscrowWallet.getTemplateCount.call(); + for (let i = 0; i < schedules.length; i++) { + let templateName = schedules[i].templateName; + let numberOfTokens = schedules[i].numberOfTokens; + let duration = schedules[i].duration; + let frequency = schedules[i].frequency; + const tx = await I_VestingEscrowWallet.addTemplate(templateName, numberOfTokens, duration, frequency, {from: wallet_admin}); + + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._name), web3.utils.hexToUtf8(templateName)); + assert.equal(tx.logs[0].args._numberOfTokens.toString(), numberOfTokens); + assert.equal(tx.logs[0].args._duration.toString(), duration); + assert.equal(tx.logs[0].args._frequency.toString(), frequency); + } + let templateNames = await I_VestingEscrowWallet.getAllTemplateNames.call(); + + for (let i = 0, j = oldTemplateCount; i < schedules.length; i++, j++) { + assert.equal(web3.utils.hexToUtf8(templateNames[j]), web3.utils.hexToUtf8(schedules[i].templateName)); + } + }); + + it("Should not be able to add template -- fail because template already exists", async () => { + await catchRevert( + I_VestingEscrowWallet.addTemplate(web3.utils.toHex("template-4-01"), 25000, 4, 1, {from: wallet_admin}) + ); + }); + + it("Should not be able to remove template -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-4-02"), {from: account_beneficiary1}) + ); + }); + + it("Should not be able to remove template -- fail because template not found", async () => { + await catchRevert( + I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-444-02"), {from: wallet_admin}) + ); + }); + + it("Should remove template", async () => { + const tx = await I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-4-02"), {from: wallet_admin}); + + assert.equal(web3.utils.hexToUtf8(tx.logs[0].args._name), "template-4-02"); + }); + + it("Should fail to add vesting schedule from template -- fail because template not found", async () => { + await catchRevert( + I_VestingEscrowWallet.addScheduleFromTemplate(account_beneficiary1, web3.utils.toHex("template-4-02"), currentTime, {from: wallet_admin}) + ); + }); + + it("Should not be able to add schedule from template -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.addScheduleFromTemplate(account_beneficiary1, web3.utils.toHex("template-4-01"), currentTime, {from: account_beneficiary1}) + ); + }); + + it("Should not be able to add vesting schedule from template -- fail because template not found", async () => { + await catchRevert( + I_VestingEscrowWallet.addScheduleFromTemplate(account_beneficiary1, web3.utils.toHex("template-777"), currentTime + 100, {from: wallet_admin}) + ); + }); + + it("Should add vesting schedule from template", async () => { + let templateName = schedules[2].templateName; + let numberOfTokens = schedules[2].numberOfTokens; + let duration = schedules[2].duration; + let frequency = schedules[2].frequency; + let startTime = currentTime.add(new BN(durationUtil.days(3))); + currentTime = new BN(await latestTime()); + await I_SecurityToken.approve(I_VestingEscrowWallet.address, numberOfTokens, { from: token_owner }); + await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); + const tx = await I_VestingEscrowWallet.addScheduleFromTemplate(account_beneficiary1, templateName, startTime, {from: wallet_admin}); + + checkScheduleLog(tx.logs[0], account_beneficiary1, templateName, startTime); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(account_beneficiary1); + assert.equal(scheduleCount, 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(account_beneficiary1, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, CREATED); + + await I_VestingEscrowWallet.revokeSchedule(account_beneficiary1, templateName, {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + it("Should not be able to add vesting schedule from template -- fail because template already added", async () => { + let templateName = schedules[2].templateName; + await catchRevert( + I_VestingEscrowWallet.addScheduleFromTemplate(account_beneficiary1, templateName, currentTime.add(new BN(100)), {from: wallet_admin}) + ); + }); + + it("Should fail to remove template", async () => { + await catchRevert( + I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-4-02"), {from: wallet_admin}) + ); + }); + + it("Should remove 2 Templates", async () => { + let templateCount = await I_VestingEscrowWallet.getTemplateCount.call({from: wallet_admin}); + + await I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-4-01"), {from: wallet_admin}); + await I_VestingEscrowWallet.removeTemplate(web3.utils.toHex("template-4-03"), {from: wallet_admin}); + + let templateCountAfterRemoving = await I_VestingEscrowWallet.getTemplateCount.call({from: wallet_admin}); + assert.equal(templateCount - templateCountAfterRemoving, 2); + }); + + }); + + describe("Tests for multi operations", async () => { + + let templateNames = [web3.utils.toHex("template-5-01"), web3.utils.toHex("template-5-02"), web3.utils.toHex("template-5-03")]; + + it("Should not be able to add schedules to the beneficiaries -- fail because of permissions check", async () => { + currentTime = new BN(await latestTime()); + let startTime = currentTime.add(new BN(100)); + let startTimes = [startTime, startTime, startTime]; + await catchRevert( + I_VestingEscrowWallet.addScheduleMulti(beneficiaries, templateNames, [10000, 10000, 10000], [4, 4, 4], [1, 1, 1], startTimes, {from: account_beneficiary1}) + ); + }); + + it("Should not be able to add schedules to the beneficiaries -- fail because of arrays sizes mismatch", async () => { + let startTime = currentTime.add(new BN(100)); + let startTimes = [startTime, startTime, startTime]; + let totalNumberOfTokens = 60000; + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + await catchRevert( + I_VestingEscrowWallet.addScheduleMulti(beneficiaries, templateNames, [20000, 30000, 10000], [4, 4], [1, 1, 1], startTimes, {from: wallet_admin}) + ); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + it("Should add schedules for 3 beneficiaries", async () => { + let numberOfTokens = [15000, 15000, 15000]; + let durations = [new BN(durationUtil.seconds(50)), new BN(durationUtil.seconds(50)), new BN(durationUtil.seconds(50))]; + let frequencies = [new BN(durationUtil.seconds(10)), new BN(durationUtil.seconds(10)), new BN(durationUtil.seconds(10))]; + let timeShift = new BN(durationUtil.seconds(100)); + let startTimes = [currentTime.add(timeShift), currentTime.add(timeShift), currentTime.add(timeShift)]; + + let totalNumberOfTokens = 60000; + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + + let tx = await I_VestingEscrowWallet.addScheduleMulti(beneficiaries, templateNames, numberOfTokens, durations, frequencies, startTimes, {from: wallet_admin}); + + for (let i = 0; i < beneficiaries.length; i++) { + let templateName = templateNames[i]; + let beneficiary = beneficiaries[i]; + checkTemplateLog(tx.logs[i* 2], templateName, numberOfTokens[i], durations[i], frequencies[i]); + checkScheduleLog(tx.logs[i * 2 + 1], beneficiary, templateName, startTimes[i]); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(beneficiary); + assert.equal(scheduleCount, 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(beneficiary, templateName); + checkSchedule(schedule, numberOfTokens[i], durations[i], frequencies[i], startTimes[i], CREATED); + } + }); + + it("Should not be able modify vesting schedule for 3 beneficiary's addresses -- fail because of arrays sizes mismatch", async () => { + let timeShift = new BN(durationUtil.seconds(100)); + let startTimes = [currentTime.add(timeShift), currentTime.add(timeShift), currentTime.add(timeShift)]; + + await catchRevert( + I_VestingEscrowWallet.modifyScheduleMulti(beneficiaries, [web3.utils.toHex("template-5-01")], startTimes, {from: wallet_admin}) + ); + }); + + it("Should not be able to modify schedules for the beneficiaries -- fail because of permissions check", async () => { + let timeShift = new BN(durationUtil.seconds(100)); + let startTimes = [currentTime.add(timeShift), currentTime.add(timeShift), currentTime.add(timeShift)]; + + await catchRevert( + I_VestingEscrowWallet.modifyScheduleMulti(beneficiaries, templateNames, startTimes, {from: account_beneficiary1}) + ); + }); + + it("Should modify vesting schedule for 3 beneficiary's addresses", async () => { + let numberOfTokens = [new BN(15000), new BN(15000), new BN(15000)]; + let durations = [new BN(durationUtil.seconds(50)), new BN(durationUtil.seconds(50)), new BN(durationUtil.seconds(50))]; + let frequencies = [new BN(durationUtil.seconds(10)), new BN(durationUtil.seconds(10)), new BN(durationUtil.seconds(10))]; + let timeShift = new BN(durationUtil.seconds(100)); + let startTimes = [currentTime.add(timeShift), currentTime.add(timeShift), currentTime.add(timeShift)]; + + const tx = await I_VestingEscrowWallet.modifyScheduleMulti(beneficiaries, templateNames, startTimes, {from: wallet_admin}); + await increaseTime(110); + + for (let i = 0; i < beneficiaries.length; i++) { + let log = tx.logs[i]; + let beneficiary = beneficiaries[i]; + checkScheduleLog(log, beneficiary, templateNames[i], startTimes[i]); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(beneficiary); + assert.equal(scheduleCount, 1); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(beneficiary, templateNames[i]); + checkSchedule(schedule, numberOfTokens[i], durations[i], frequencies[i], startTimes[i], STARTED); + } + }); + + it("Should not be able to send available tokens to the beneficiaries addresses -- fail because of array size", async () => { + await catchRevert( + I_VestingEscrowWallet.pushAvailableTokensMulti(new BN(0), new BN(3), {from: wallet_admin}) + ); + }); + + it("Should not be able to send available tokens to the beneficiaries -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.pushAvailableTokensMulti(new BN(0), new BN(2), {from: account_beneficiary1}) + ); + }); + + it("Should send available tokens to the beneficiaries addresses", async () => { + const tx = await I_VestingEscrowWallet.pushAvailableTokensMulti(0, 2, {from: wallet_admin}); + + for (let i = 0; i < beneficiaries.length; i++) { + let log = tx.logs[i]; + let beneficiary = beneficiaries[i]; + assert.equal(log.args._numberOfTokens.toString(), 3000); + + let balance = await I_SecurityToken.balanceOf.call(beneficiary); + assert.equal(balance.toString(), 3000); + + await I_SecurityToken.transfer(token_owner, balance, {from: beneficiary}); + await I_VestingEscrowWallet.revokeAllSchedules(beneficiary, {from: wallet_admin}); + await I_VestingEscrowWallet.removeTemplate(templateNames[i], {from: wallet_admin}); + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + } + }); + + it("Should not be able to add schedules from template to the beneficiaries -- fail because of permissions check", async () => { + let templateName = web3.utils.toHex("template-6-01"); + let numberOfTokens = 18000; + let duration = durationUtil.weeks(3); + let frequency = durationUtil.weeks(1); + let templateNames = [templateName, templateName, templateName]; + let startTime = currentTime.add(new BN(durationUtil.seconds(100))); + let startTimes = [startTime, startTime, startTime]; + + let totalNumberOfTokens = numberOfTokens * 3; + await I_SecurityToken.approve(I_VestingEscrowWallet.address, totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.depositTokens(totalNumberOfTokens, {from: token_owner}); + await I_VestingEscrowWallet.addTemplate(templateName, numberOfTokens, duration, frequency, {from: wallet_admin}); + + await catchRevert( + I_VestingEscrowWallet.addScheduleFromTemplateMulti(beneficiaries, templateNames, startTimes, {from: account_beneficiary1}) + ); + }); + + it("Should add schedules from template for 3 beneficiaries", async () => { + currentTime = new BN(await latestTime()); + let templateName = web3.utils.toHex("template-6-01"); + let numberOfTokens = 18000; + let duration = durationUtil.weeks(3); + let frequency = durationUtil.weeks(1); + let templateNames = [templateName, templateName, templateName]; + let startTime = currentTime.add(new BN(durationUtil.seconds(100))); + let startTimes = [startTime, startTime, startTime]; + + let tx = await I_VestingEscrowWallet.addScheduleFromTemplateMulti(beneficiaries, templateNames, startTimes, {from: wallet_admin}); + for (let i = 0; i < beneficiaries.length; i++) { + let log = tx.logs[i]; + let beneficiary = beneficiaries[i]; + checkScheduleLog(log, beneficiary, templateName, startTimes[i]); + + let schedule = await I_VestingEscrowWallet.getSchedule.call(beneficiary, templateName); + checkSchedule(schedule, numberOfTokens, duration, frequency, startTimes[i], CREATED); + } + }); + + it("Should not be able to revoke schedules of the beneficiaries -- fail because of permissions check", async () => { + await catchRevert( + I_VestingEscrowWallet.revokeSchedulesMulti(beneficiaries, {from: account_beneficiary1}) + ); + }); + + it("Should revoke vesting schedule from the 3 beneficiary's addresses", async () => { + const tx = await I_VestingEscrowWallet.revokeSchedulesMulti(beneficiaries, {from: wallet_admin}); + + for (let i = 0; i < beneficiaries.length; i++) { + let log = tx.logs[i]; + let beneficiary = beneficiaries[i]; + assert.equal(log.args._beneficiary, beneficiary); + + let scheduleCount = await I_VestingEscrowWallet.getScheduleCount.call(beneficiary); + assert.equal(scheduleCount, 0); + } + + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + await I_VestingEscrowWallet.sendToTreasury(unassignedTokens, {from: wallet_admin}); + }); + + }); + +}); + +function checkTemplateLog(log, templateName, numberOfTokens, duration, frequency) { + assert.equal(web3.utils.hexToUtf8(log.args._name), web3.utils.hexToUtf8(templateName)); + assert.equal(log.args._numberOfTokens.toString(), numberOfTokens); + assert.equal(log.args._duration.toString(), duration); + assert.equal(log.args._frequency.toString(), frequency); +} + +function checkScheduleLog(log, beneficiary, templateName, startTime) { + assert.equal(log.args._beneficiary, beneficiary); + assert.equal(web3.utils.hexToUtf8(log.args._templateName), web3.utils.hexToUtf8(templateName)); + assert.equal(log.args._startTime.toString(), startTime); +} + +function checkSchedule(schedule, numberOfTokens, duration, frequency, startTime, state) { + assert.equal(schedule[0].toString(), numberOfTokens); + assert.equal(schedule[1].toString(), duration); + assert.equal(schedule[2].toString(), frequency); + assert.equal(schedule[3].toString(), startTime); + assert.equal(schedule[5].toString(), state); +} + +function getTotalNumberOfTokens(schedules) { + let numberOfTokens = new BN(0); + for (let i = 0; i < schedules.length; i++) { + numberOfTokens = numberOfTokens.add(new BN(schedules[i].numberOfTokens)); + } + return numberOfTokens; +} diff --git a/test/za_datastore.js b/test/za_datastore.js new file mode 100644 index 000000000..526583c21 --- /dev/null +++ b/test/za_datastore.js @@ -0,0 +1,476 @@ +import latestTime from "./helpers/latestTime"; +import { catchRevert } from "./helpers/exceptions"; +import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; +import { setUpPolymathNetwork } from "./helpers/createInstances"; +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const DataStore = artifacts.require("./DataStore.sol"); + +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("Data store", async (accounts) => { + // Accounts Variable declaration + let account_polymath; + let token_owner; + + // Contract Instance Declaration + let I_GeneralTransferManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_DataStore; + let I_ModuleRegistryProxy; + let I_MRProxied; + let I_STRGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const contact = "team@polymath.network"; + const key = "0x41"; + const key2 = "0x42"; + const bytes32data = "0x4200000000000000000000000000000000000000000000000000000000000000"; + const bytes32data2 = "0x4400000000000000000000000000000000000000000000000000000000000000"; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + const address_zero = "0x0000000000000000000000000000000000000000"; + const address_one = "0x0000000000000000000000000000000000000001"; + const address_two = "0x0000000000000000000000000000000000000002"; + + before(async () => { + account_polymath = accounts[0]; + token_owner = accounts[1]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STRGetter + ] = instances; + + + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', { filter: { transactionHash: tx.transactionHash } }))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); + }); + + it("Should fetch data store address", async () => { + I_DataStore = await DataStore.at(await I_SecurityToken.dataStore()); + }); + }); + + describe("Should attach to security token securely", async () => { + it("Should be attached to a security token upon deployment", async () => { + assert.equal(await I_DataStore.securityToken(), I_SecurityToken.address, "Incorrect Security Token attached"); + }); + + it("Should not allow non-issuer to change security token address", async () => { + await catchRevert(I_DataStore.setSecurityToken(address_one, { from: account_polymath })); + }); + + it("Should allow issuer to change security token address", async () => { + let snapId = await takeSnapshot(); + await I_DataStore.setSecurityToken(address_one, { from: token_owner }); + assert.equal(await I_DataStore.securityToken(), address_one, "Incorrect Security Token attached"); + await revertToSnapshot(snapId); + assert.equal(await I_DataStore.securityToken(), I_SecurityToken.address, "Incorrect Security Token attached"); + }); + }); + + describe("Should set data correctly", async () => { + it("Should set and fetch uint256 correctly", async () => { + await catchRevert(I_DataStore.setUint256("0x0", 1, { from: token_owner })); + await I_DataStore.setUint256(key, 1, { from: token_owner }); + assert.equal((await I_DataStore.getUint256(key)).toNumber(), 1, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bytes32 correctly", async () => { + await I_DataStore.setBytes32(key, bytes32data, { from: token_owner }); + assert.equal(await I_DataStore.getBytes32(key), bytes32data, "Incorrect Data Inserted"); + }); + + it("Should set and fetch address correctly", async () => { + await I_DataStore.setAddress(key, address_one, { from: token_owner }); + assert.equal(await I_DataStore.getAddress(key), address_one, "Incorrect Data Inserted"); + }); + + it("Should set and fetch string correctly", async () => { + await I_DataStore.setString(key, name, { from: token_owner }); + assert.equal(await I_DataStore.getString(key), name, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bytes correctly", async () => { + await I_DataStore.setBytes(key, bytes32data, { from: token_owner }); + assert.equal(await I_DataStore.getBytes(key), bytes32data, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bool correctly", async () => { + await I_DataStore.setBool(key, true, { from: token_owner }); + assert.equal(await I_DataStore.getBool(key), true, "Incorrect Data Inserted"); + }); + + it("Should set and fetch uint256 array correctly", async () => { + let arr = [1, 2]; + await I_DataStore.setUint256Array(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getUint256Array(key); + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let arrElement2 = await I_DataStore.getUint256ArrayElement(key, 1); + assert.equal(arr2[0].toNumber(), arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1].toNumber(), arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2.toNumber(), arr[1], "Incorrect array element"); + }); + + it("Should set and fetch bytes32 array correctly", async () => { + let arr = [bytes32data, bytes32data2]; + await I_DataStore.setBytes32Array(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getBytes32Array(key); + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let arrElement2 = await I_DataStore.getBytes32ArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should set and fetch address array correctly", async () => { + let arr = [address_zero, address_one]; + await I_DataStore.setAddressArray(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getAddressArray(key); + let arrLen = await I_DataStore.getAddressArrayLength(key); + let arrElement2 = await I_DataStore.getAddressArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should set and fetch bool array correctly", async () => { + let arr = [false, true]; + await I_DataStore.setBoolArray(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getBoolArray(key); + let arrLen = await I_DataStore.getBoolArrayLength(key); + let arrElement2 = await I_DataStore.getBoolArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should insert uint256 into Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + await I_DataStore.insertUint256(key, new BN(10), { from: token_owner }); + let arrElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber()); + let arrElements = await I_DataStore.getUint256ArrayElements(key, 0, arrLen.toNumber()); + assert.equal(arrElement.toNumber(), arrElements[arrLen.toNumber()].toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement.toNumber(), 10, "Incorrect array element"); + }); + + it("Should insert bytes32 into Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + await I_DataStore.insertBytes32(key, bytes32data, { from: token_owner }); + let arrElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber()); + let arrElements = await I_DataStore.getBytes32ArrayElements(key, 0, arrLen.toNumber()); + assert.equal(arrElement, arrElements[arrLen.toNumber()]); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, bytes32data, "Incorrect array element"); + }); + + it("Should insert address into Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + await I_DataStore.insertAddress(key, address_one, { from: token_owner }); + let arrElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber()); + let arrElements = await I_DataStore.getAddressArrayElements(key, 0, arrLen.toNumber()); + assert.equal(arrElement, arrElements[arrLen.toNumber()]); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, address_one, "Incorrect array element"); + }); + + it("Should insert bool into Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + await I_DataStore.insertBool(key, true, { from: token_owner }); + let arrElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber()); + let arrElements = await I_DataStore.getBoolArrayElements(key, 0, arrLen.toNumber()); + assert.equal(arrElement, arrElements[arrLen.toNumber()]); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, true, "Incorrect array element"); + }); + + it("Should delete uint256 from Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteUint256(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement.toNumber(), (await I_DataStore.getUint256ArrayElement(key, indexToDelete)).toNumber(), "Incorrect array element"); + }); + + it("Should delete bytes32 from Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteBytes32(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getBytes32ArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should delete address from Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteAddress(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getAddressArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should delete bool from Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteBool(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getBoolArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should set and fetch multiple uint256 correctly", async () => { + await catchRevert(I_DataStore.setUint256Multi([key], [1,2], { from: token_owner })); + await I_DataStore.setUint256Multi([key, key2], [1,2], { from: token_owner }); + assert.equal((await I_DataStore.getUint256(key)).toNumber(), 1, "Incorrect Data Inserted"); + assert.equal((await I_DataStore.getUint256(key2)).toNumber(), 2, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple bytes32 correctly", async () => { + await I_DataStore.setBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: token_owner }); + assert.equal(await I_DataStore.getBytes32(key), bytes32data, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getBytes32(key2), bytes32data2, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple address correctly", async () => { + await I_DataStore.setAddressMulti([key, key2], [address_one, address_two], { from: token_owner }); + assert.equal(await I_DataStore.getAddress(key), address_one, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getAddress(key2), address_two, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple bool correctly", async () => { + await I_DataStore.setBoolMulti([key, key2], [true, true], { from: token_owner }); + assert.equal(await I_DataStore.getBool(key), true, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getBool(key2), true, "Incorrect Data Inserted"); + }); + + it("Should insert multiple uint256 into multiple Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let arrLen2 = await I_DataStore.getUint256ArrayLength(key2); + await I_DataStore.insertUint256Multi([key, key2], [10, 20], { from: token_owner }); + let arrElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getUint256ArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement.toNumber(), 10, "Incorrect array element"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement2.toNumber(), 20, "Incorrect array element"); + }); + + it("Should insert multiple bytes32 into multiple Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let arrLen2 = await I_DataStore.getBytes32ArrayLength(key2); + await I_DataStore.insertBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: token_owner }); + let arrElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getBytes32ArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, bytes32data, "Incorrect array element"); + assert.equal(arrElement2, bytes32data2, "Incorrect array element"); + }); + + it("Should insert multiple address into multiple Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + let arrLen2 = await I_DataStore.getAddressArrayLength(key2); + await I_DataStore.insertAddressMulti([key, key2], [address_one, address_two], { from: token_owner }); + let arrElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getAddressArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, address_one, "Incorrect array element"); + assert.equal(arrElement2, address_two, "Incorrect array element"); + }); + + it("Should insert multiple bool into multiple Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + let arrLen2 = await I_DataStore.getBoolArrayLength(key2); + await I_DataStore.insertBoolMulti([key, key2], [true, true], { from: token_owner }); + let arrElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getBoolArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, true, "Incorrect array element"); + assert.equal(arrElement2, true, "Incorrect array element"); + }); + }); + + describe("Should not allow unautohrized modification to data", async () => { + it("Should not allow unauthorized addresses to modify uint256", async () => { + await catchRevert(I_DataStore.setUint256(key, new BN(1), { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes32", async () => { + await catchRevert(I_DataStore.setBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify address", async () => { + await catchRevert(I_DataStore.setAddress(key, address_one, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify string", async () => { + await catchRevert(I_DataStore.setString(key, name, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes", async () => { + await catchRevert(I_DataStore.setBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bool", async () => { + await catchRevert(I_DataStore.setBool(key, true, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify uint256 array", async () => { + let arr = [1, 2]; + await catchRevert(I_DataStore.setUint256Array(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes32 array", async () => { + let arr = [bytes32data, bytes32data2]; + await catchRevert(I_DataStore.setBytes32Array(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify address array", async () => { + let arr = [address_zero, address_one]; + await catchRevert(I_DataStore.setAddressArray(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bool array", async () => { + let arr = [false, true]; + await catchRevert(I_DataStore.setBoolArray(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert uint256 into Array", async () => { + await catchRevert(I_DataStore.insertUint256(key, new BN(10), { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert bytes32 into Array", async () => { + await catchRevert(I_DataStore.insertBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert address into Array", async () => { + await catchRevert(I_DataStore.insertAddress(key, address_one, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert bool into Array", async () => { + await catchRevert(I_DataStore.insertBool(key, true, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete uint256 from Array", async () => { + await catchRevert(I_DataStore.deleteUint256(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete bytes32 from Array", async () => { + await catchRevert(I_DataStore.deleteBytes32(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete address from Array", async () => { + await catchRevert(I_DataStore.deleteAddress(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete bool from Array", async () => { + await catchRevert(I_DataStore.deleteBool(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple uint256", async () => { + await catchRevert(I_DataStore.setUint256Multi([key, key2], [1,2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple bytes32", async () => { + await catchRevert(I_DataStore.setBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple address", async () => { + await catchRevert(I_DataStore.setAddressMulti([key, key2], [address_one, address_two], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple bool", async () => { + await catchRevert(I_DataStore.setBoolMulti([key, key2], [true, true], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple uint256 into multiple Array", async () => { + await catchRevert(I_DataStore.insertUint256Multi([key, key2], [10, 20], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple bytes32 into multiple Array", async () => { + await catchRevert(I_DataStore.insertBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple address into multiple Array", async () => { + await catchRevert(I_DataStore.insertAddressMulti([key, key2], [address_one, address_two], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple bool into multiple Array", async () => { + await catchRevert(I_DataStore.insertBoolMulti([key, key2], [true, true], { from: account_polymath })); + }); + }); +}); diff --git a/test/zb_signed_transfer_manager.js b/test/zb_signed_transfer_manager.js new file mode 100644 index 000000000..7dbb4f2e6 --- /dev/null +++ b/test/zb_signed_transfer_manager.js @@ -0,0 +1,297 @@ +import latestTime from "./helpers/latestTime"; +import { duration, promisifyLogWatch, latestBlock } from "./helpers/utils"; +import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; +import { getSignSTMData } from "./helpers/signData"; +import { pk } from "./helpers/testprivateKey"; +import { encodeProxyCall, encodeModuleCall } from "./helpers/encodeCall"; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, deployGPMAndVerifyed, deploySignedTMAndVerifyed} from "./helpers/createInstances"; + +const DummySTO = artifacts.require("./DummySTO.sol"); +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const SignedTransferManager = artifacts.require("./SignedTransferManager"); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require("web3"); +const BigNumber = require("bignumber.js"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("SignedTransferManager", accounts => { + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let I_GeneralTransferManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_GeneralPermissionManager; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_STRProxied; + let I_MRProxied; + let I_DummySTO; + let I_PolyToken; + let I_PolymathRegistry; + let I_SignedTransferManagerFactory; + let P_SignedTransferManagerFactory; + let I_SignedTransferManager; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("1000"); + + let currentTime; + + before(async () => { + // Accounts setup + currentTime = new BN(await latestTime()); + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[6]; + account_investor4 = accounts[7]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STGetter + ] = instances; + + // STEP 2: Deploy the GeneralPermissionManagerFactory + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + // STEP 3: Deploy the SignedTransferManagerFactory + [I_SignedTransferManagerFactory] = await deploySignedTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + // STEP 4: Deploy the Paid SignedTransferManagerFactory + [P_SignedTransferManagerFactory] = await deploySignedTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500", "ether")); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + ManualApprovalTransferManagerFactory: ${I_SignedTransferManagerFactory.address} + + + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toAscii(log.args._name).replace(/\u0000/g, ""), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await stGetter.getModulesByType(2))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + }); + + + describe("signed transfer manager tests", async () => { + + it("Should Buy the tokens", async () => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyKYCData( + account_investor1, + currentTime, + currentTime, + currentTime.add(new BN(duration.days(10))), + { + from: account_issuer + } + ); + + assert.equal( + tx.logs[0].args._investor.toLowerCase(), + account_investor1.toLowerCase(), + "Failed in adding the investor in whitelist" + ); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.issue(account_investor1, new BN(web3.utils.toWei("2", "ether")), "0x0", { from: token_owner }); + + assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toString(), new BN(web3.utils.toWei("2", "ether")).toString()); + }); + + + it("Should successfully attach the SignedTransferManager with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_SignedTransferManagerFactory.address, "0x0",new BN(0),new BN(0), { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "SignedTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "SignedTransferManager", + "SignedTransferManager module was not added" + ); + I_SignedTransferManager = await SignedTransferManager.at(tx.logs[2].args._module); + }); + + it("should fail to transfer because transaction is not verified yet.", async () => { + await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 })); + }); + + it("should successfully add multiple signers to signersList", async () => { + await I_SignedTransferManager.updateSigners([account_investor3, account_investor4, token_owner], [true, true, true], {from: token_owner}); + + assert.equal(await I_SignedTransferManager.checkSigner(account_investor3), true); + assert.equal(await I_SignedTransferManager.checkSigner(account_investor4), true); + assert.equal(await I_SignedTransferManager.checkSigner(token_owner), true); + }); + + it("should fail to change signers stats without permission", async () => { + await catchRevert(I_SignedTransferManager.updateSigners([account_investor3], [false], {from: account_investor2})); + }); + + + it("should allow to invalidate siganture if sender is the signer and is in the signer list", async () => { + let oneeth = new BN(web3.utils.toWei("1", "ether")); + let signer = web3.eth.accounts.create(); + await web3.eth.personal.importRawKey(signer.privateKey, ""); + await web3.eth.personal.unlockAccount(signer.address, "", 6000); + await web3.eth.sendTransaction({ from: token_owner, to: signer.address, value: oneeth }); + + await I_SignedTransferManager.updateSigners([signer.address], [true], {from: token_owner}); + + let nonce = new BN(10); + let expiry = new BN(currentTime.add(new BN(duration.days(100)))); + let data = await getSignSTMData( + I_SignedTransferManager.address, + nonce, + expiry, + account_investor1, + account_investor2, + oneeth, + signer.privateKey + ); + + assert.equal(await I_SignedTransferManager.checkSignatureValidity(data), true); + await I_SignedTransferManager.invalidateSignature(account_investor1, account_investor2, oneeth, data, {from: signer.address}); + assert.equal(await I_SignedTransferManager.checkSignatureValidity(data), false); + }); + + it("should allow transfer with valid sig", async () => { + let signer = web3.eth.accounts.create(); + await I_SignedTransferManager.updateSigners([signer.address], [true], {from: token_owner}); + let oneeth = new BN(web3.utils.toWei("1", "ether")); + let nonce = new BN(10); + let expiry = new BN(currentTime.add(new BN(duration.days(100)))); + let data = await getSignSTMData( + I_SignedTransferManager.address, + nonce, + expiry, + account_investor1, + account_investor2, + oneeth, + signer.privateKey + ); + + let balance11 = await I_SecurityToken.balanceOf(account_investor1); + let balance21 = await I_SecurityToken.balanceOf(account_investor2); + + assert.equal(await I_SignedTransferManager.checkSignatureValidity(data), true); + + await I_SecurityToken.transferWithData(account_investor2, oneeth, data, {from: account_investor1}); + + assert.equal(await I_SignedTransferManager.checkSignatureValidity(data), false); + await catchRevert(I_SecurityToken.transferWithData(account_investor2, oneeth, data, {from: account_investor1})); + + assert.equal(balance11.sub(oneeth).toString(), (await I_SecurityToken.balanceOf(account_investor1)).toString()); + assert.equal(balance21.add(oneeth).toString(), (await I_SecurityToken.balanceOf(account_investor2)).toString()); + + + }); + + it("should not allow transfer if the signer is not on the signer list", async () => { + let signer = web3.eth.accounts.create(); + let oneeth = new BN(web3.utils.toWei("1", "ether")); + let nonce = new BN(10); + let expiry = new BN(currentTime.add(new BN(duration.days(100)))); + let data = await getSignSTMData( + I_SignedTransferManager.address, + nonce, + expiry, + account_investor1, + account_investor2, + oneeth, + signer.privateKey + ); + + await catchRevert(I_SecurityToken.transferWithData(account_investor2, oneeth, data, {from: account_investor1})); + }); + }); +}); diff --git a/truffle-ci.js b/truffle-ci.js new file mode 100644 index 000000000..008baa8ec --- /dev/null +++ b/truffle-ci.js @@ -0,0 +1,38 @@ +require('babel-register'); +require('babel-polyfill'); + +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*', // Match any network id + gas: 7900000, + }, + coverage: { + host: "localhost", + network_id: "*", + port: 8545, // <-- If you change this, also set the port option in .solcover.js. + gas: 0xfffffffffff, // <-- Use this high gas value + gasPrice: 0x01 // <-- Use this low gas price + } + }, + compilers: { + solc: { + version: "native", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + } + }, + mocha: { + enableTimeouts: false, + reporter: "mocha-junit-reporter", + reporterOptions: { + mochaFile: './test-results/mocha/results.xml' + } + } +}; diff --git a/truffle-config-gas.js b/truffle-config-gas.js new file mode 100644 index 000000000..edc6c325b --- /dev/null +++ b/truffle-config-gas.js @@ -0,0 +1,81 @@ +require('babel-register'); +require('babel-polyfill'); +const fs = require('fs'); +const NonceTrackerSubprovider = require("web3-provider-engine/subproviders/nonce-tracker") + +const HDWalletProvider = require("truffle-hdwallet-provider"); + +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*', // Match any network id + gas: 7900000, + }, + mainnet: { + host: 'localhost', + port: 8545, + network_id: '1', // Match any network id + gas: 7900000, + gasPrice: 10000000000 + }, + ropsten: { + // provider: new HDWalletProvider(privKey, "http://localhost:8545"), + host: 'localhost', + port: 8545, + network_id: '3', // Match any network id + gas: 4500000, + gasPrice: 150000000000 + }, + rinkeby: { + // provider: new HDWalletProvider(privKey, "http://localhost:8545"), + host: 'localhost', + port: 8545, + network_id: '4', // Match any network id + gas: 7500000, + gasPrice: 10000000000 + }, + kovan: { + provider: () => { + const key = fs.readFileSync('./privKey').toString(); + let wallet = new HDWalletProvider(key, "https://kovan.infura.io/") + var nonceTracker = new NonceTrackerSubprovider() + wallet.engine._providers.unshift(nonceTracker) + nonceTracker.setEngine(wallet.engine) + return wallet + }, + network_id: '42', // Match any network id + gas: 7900000, + gasPrice: 5000000000 + }, + coverage: { + host: "localhost", + network_id: "*", + port: 8545, // <-- If you change this, also set the port option in .solcover.js. + gas: 0xfffffffff , // <-- Use this high gas value + gasPrice: 0x01 // <-- Use this low gas price + } + }, + compilers: { + solc: { + version: "native", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + } + }, + mocha: { + enableTimeouts: false, + reporter: 'eth-gas-reporter', + reporterOptions : { + currency: 'USD', + gasPrice: 5, + onlyCalledMethods: true, + showTimeSpent: true + } + } +}; diff --git a/truffle-config.js b/truffle-config.js index c993327ea..21bfd6097 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -3,7 +3,7 @@ require('babel-polyfill'); const fs = require('fs'); const NonceTrackerSubprovider = require("web3-provider-engine/subproviders/nonce-tracker") -const HDWalletProvider = require("truffle-hdwallet-provider-privkey"); +const HDWalletProvider = require("truffle-hdwallet-provider"); module.exports = { networks: { @@ -53,15 +53,20 @@ module.exports = { host: "localhost", network_id: "*", port: 8545, // <-- If you change this, also set the port option in .solcover.js. - gas: 0xfffffffffff, // <-- Use this high gas value + gas: 0xfffffffff , // <-- Use this high gas value gasPrice: 0x01 // <-- Use this low gas price } }, - solc: { - optimizer: { - enabled: true, - runs: 200, - }, + compilers: { + solc: { + version: "native", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + } }, mocha: { enableTimeouts: false diff --git a/upgrade.js b/upgrade.js new file mode 100644 index 000000000..af0561dac --- /dev/null +++ b/upgrade.js @@ -0,0 +1,19 @@ +const fs = require('fs'); +const glob = require("glob"); + +let regex = new RegExp('((const|let|var) (.*) = artifacts.require(.)*)', 'gi'); +let m; + +glob("test/**/*.js", function (er, files) { + files.forEach(function(filename) { + fs.readFile(filename, 'utf-8', function(err, content) { + if (err) { + return console.log(err); + } + content = content.replace(regex, '$1\n$3.numberFormat = "BN";'); + fs.writeFile(filename, content, 'utf8', function (err) { + if (err) return console.log(err); + }); + }); + }); +}) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index aba68e85b..91971f5ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,129 +18,113 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@soldoc/markdown@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@soldoc/markdown/-/markdown-0.1.0.tgz#9f85be75049af9721b5129f133d52dafbf5f671e" +"@types/concat-stream@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.0.tgz#394dbe0bb5fee46b38d896735e8b68ef2390d00d" + integrity sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0= + dependencies: + "@types/node" "*" -"@soldoc/soldoc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@soldoc/soldoc/-/soldoc-0.4.3.tgz#24ffee9264228e1c3edd61fd3162d63587954933" +"@types/form-data@0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha1-yayFsqX9GENbjIXZ7LUObWyJP/g= dependencies: - "@soldoc/markdown" "^0.1.0" - chalk "^2.3.1" - deep-assign "^2.0.0" - fs-extra "^5.0.0" - shelljs "^0.8.1" - solc "^0.4.19" - valid-url "^1.0.9" - yargs "^11.0.0" + "@types/node" "*" + +"@types/node@*": + version "11.9.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.4.tgz#ceb0048a546db453f6248f2d1d95e937a6f00a14" + integrity sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA== -"@types/node@^10.3.2": - version "10.12.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.2.tgz#d77f9faa027cadad9c912cd47f4f8b07b0fb0864" - integrity sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ== +"@types/node@^8.0.0": + version "8.10.40" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.40.tgz#4314888d5cd537945d73e9ce165c04cc550144a4" + integrity sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ== + +"@types/node@^9.3.0", "@types/node@^9.4.1": + version "9.6.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.42.tgz#96fd9c8cf15fbf2c16fe525fc2be97c49cdd0c2f" + integrity sha512-SpeVQJFekfnEaZZO1yl4je/36upII36L7gOT4DBx51B1GeAB45mmDb3a5OBQB+ZeFxVVOP37r8Owsl940G/fBg== + +"@types/qs@^6.2.31": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.5.1.tgz#a38f69c62528d56ba7bd1f91335a8004988d72f7" + integrity sha512-mNhVdZHdtKHMMxbqzNK3RzkBcN1cux3AvuCYGTvjEIQT2uheH3eCAyYsbMbh2Bq8nXkeOWs1kyDiF7geWRFQ4Q== abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= + +abi-decoder@^1.0.8: + version "1.2.0" + resolved "https://registry.yarnpkg.com/abi-decoder/-/abi-decoder-1.2.0.tgz#c42882dbb91b444805f0cd203a87a5cc3c22f4a8" + integrity sha512-y2OKSEW4gf2838Eavc56vQY9V46zaXkf3Jl1WpTfUBbzAVrXSr4JRZAAWv55Tv9s5WNz1rVgBgz5d2aJIL1QCg== + dependencies: + web3 "^0.18.4" abstract-leveldown@~2.6.0: version "2.6.3" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== dependencies: xtend "~4.0.0" abstract-leveldown@~2.7.1: version "2.7.2" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== dependencies: xtend "~4.0.0" accepts@~1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= dependencies: mime-types "~2.1.18" negotiator "0.6.1" -acorn-dynamic-import@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" - dependencies: - acorn "^4.0.3" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^4.0.3: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - -acorn@^5.0.0: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - -acorn@^6.0.2: - version "6.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754" - integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg== - -aes-js@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" - -aes-js@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.1.tgz#89fd1f94ae51b4c72d62466adc1a7323ff52f072" - -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" + integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== -ajv-keywords@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" +acorn@^6.0.7: + version "6.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.0.tgz#b0a3be31752c97a0f7013c5f4903b71a05db6818" + integrity sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw== -ajv@^5.1.1, ajv@^5.2.2, ajv@^5.3.0: +ajv@^5.2.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0: - version "6.5.4" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.4.tgz#247d5274110db653706b550fcc2b797ca28cfc59" +ajv@^6.5.5, ajv@^6.9.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1" + integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - ambi@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/ambi/-/ambi-2.5.0.tgz#7c8e372be48891157e7cea01cb6f9143d1f74220" + integrity sha1-fI43K+SIkRV+fOoBy2+RQ9H3QiA= dependencies: editions "^1.1.1" typechecker "^4.3.0" @@ -148,54 +132,67 @@ ambi@^2.2.0: amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -ansi-escapes@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-regex@^2.0.0, ansi-regex@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" + integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" any-promise@1.3.0, any-promise@^1.0.0, any-promise@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== dependencies: micromatch "^2.1.5" normalize-path "^2.0.0" -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -203,71 +200,56 @@ are-we-there-yet@~1.1.2: argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" -arguments-extended@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/arguments-extended/-/arguments-extended-0.0.3.tgz#6107e4917d0eb6f0a4dd66320fc15afc72ef4946" - dependencies: - extended "~0.0.3" - is-extended "~0.0.8" - arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= dependencies: arr-flatten "^1.0.1" arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - -array-extended@~0.0.3, array-extended@~0.0.4, array-extended@~0.0.5: - version "0.0.11" - resolved "https://registry.yarnpkg.com/array-extended/-/array-extended-0.0.11.tgz#d7144ae748de93ca726f121009dbff1626d164bd" - dependencies: - arguments-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== dependencies: bn.js "^4.0.0" inherits "^2.0.1" @@ -276,30 +258,34 @@ asn1.js@^4.0.0: asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - dependencies: - util "0.10.3" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + integrity sha1-GdOGodntxufByF04iu28xW0zYC0= async-eventemitter@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== dependencies: async "^2.4.0" @@ -312,40 +298,54 @@ async-eventemitter@ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== async@1.x, async@^1.4.2, async@~1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.4.1, async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== dependencies: - lodash "^4.17.10" + lodash "^4.17.11" + +async@~0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= async@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" + integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k= asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= dependencies: chalk "^1.1.3" esutils "^2.0.2" @@ -354,6 +354,7 @@ babel-code-frame@^6.26.0: babel-core@^6.0.14, babel-core@^6.26.0: version "6.26.3" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== dependencies: babel-code-frame "^6.26.0" babel-generator "^6.26.0" @@ -378,6 +379,7 @@ babel-core@^6.0.14, babel-core@^6.26.0: babel-generator@^6.26.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== dependencies: babel-messages "^6.23.0" babel-runtime "^6.26.0" @@ -391,6 +393,7 @@ babel-generator@^6.26.0: babel-helper-bindify-decorators@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" + integrity sha1-FMGeXxQte0fxmlJDHlKxzLxAozA= dependencies: babel-runtime "^6.22.0" babel-traverse "^6.24.1" @@ -399,6 +402,7 @@ babel-helper-bindify-decorators@^6.24.1: babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= dependencies: babel-helper-explode-assignable-expression "^6.24.1" babel-runtime "^6.22.0" @@ -407,6 +411,7 @@ babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: babel-helper-call-delegate@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= dependencies: babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" @@ -416,6 +421,7 @@ babel-helper-call-delegate@^6.24.1: babel-helper-define-map@^6.24.1: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= dependencies: babel-helper-function-name "^6.24.1" babel-runtime "^6.26.0" @@ -425,6 +431,7 @@ babel-helper-define-map@^6.24.1: babel-helper-explode-assignable-expression@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= dependencies: babel-runtime "^6.22.0" babel-traverse "^6.24.1" @@ -433,6 +440,7 @@ babel-helper-explode-assignable-expression@^6.24.1: babel-helper-explode-class@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" + integrity sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes= dependencies: babel-helper-bindify-decorators "^6.24.1" babel-runtime "^6.22.0" @@ -442,6 +450,7 @@ babel-helper-explode-class@^6.24.1: babel-helper-function-name@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= dependencies: babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" @@ -452,6 +461,7 @@ babel-helper-function-name@^6.24.1: babel-helper-get-function-arity@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -459,6 +469,7 @@ babel-helper-get-function-arity@^6.24.1: babel-helper-hoist-variables@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -466,6 +477,7 @@ babel-helper-hoist-variables@^6.24.1: babel-helper-optimise-call-expression@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -473,6 +485,7 @@ babel-helper-optimise-call-expression@^6.24.1: babel-helper-regex@^6.24.1: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= dependencies: babel-runtime "^6.26.0" babel-types "^6.26.0" @@ -481,6 +494,7 @@ babel-helper-regex@^6.24.1: babel-helper-remap-async-to-generator@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= dependencies: babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" @@ -491,6 +505,7 @@ babel-helper-remap-async-to-generator@^6.24.1: babel-helper-replace-supers@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= dependencies: babel-helper-optimise-call-expression "^6.24.1" babel-messages "^6.23.0" @@ -502,6 +517,7 @@ babel-helper-replace-supers@^6.24.1: babel-helpers@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= dependencies: babel-runtime "^6.22.0" babel-template "^6.24.1" @@ -509,50 +525,61 @@ babel-helpers@^6.24.1: babel-messages@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= dependencies: babel-runtime "^6.22.0" babel-plugin-check-es2015-constants@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= dependencies: babel-runtime "^6.22.0" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= babel-plugin-syntax-async-generators@^6.5.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + integrity sha1-a8lj67FuzLrmuStZbrfzXDQqi5o= babel-plugin-syntax-class-properties@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + integrity sha1-1+sjt5oxf4VDlixQW4J8fWysJ94= babel-plugin-syntax-decorators@^6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + integrity sha1-MSVjtNvePMgGzuPkFszurd0RrAs= babel-plugin-syntax-dynamic-import@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + integrity sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo= babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= babel-plugin-transform-async-generator-functions@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" + integrity sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds= dependencies: babel-helper-remap-async-to-generator "^6.24.1" babel-plugin-syntax-async-generators "^6.5.0" @@ -561,6 +588,7 @@ babel-plugin-transform-async-generator-functions@^6.24.1: babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= dependencies: babel-helper-remap-async-to-generator "^6.24.1" babel-plugin-syntax-async-functions "^6.8.0" @@ -569,6 +597,7 @@ babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async- babel-plugin-transform-class-properties@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + integrity sha1-anl2PqYdM9NvN7YRqp3vgagbRqw= dependencies: babel-helper-function-name "^6.24.1" babel-plugin-syntax-class-properties "^6.8.0" @@ -578,6 +607,7 @@ babel-plugin-transform-class-properties@^6.24.1: babel-plugin-transform-decorators@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" + integrity sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0= dependencies: babel-helper-explode-class "^6.24.1" babel-plugin-syntax-decorators "^6.13.0" @@ -588,18 +618,21 @@ babel-plugin-transform-decorators@^6.24.1: babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es2015-block-scoping@^6.24.1: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= dependencies: babel-runtime "^6.26.0" babel-template "^6.26.0" @@ -610,6 +643,7 @@ babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es20 babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-classes@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= dependencies: babel-helper-define-map "^6.24.1" babel-helper-function-name "^6.24.1" @@ -624,6 +658,7 @@ babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-cla babel-plugin-transform-es2015-computed-properties@^6.22.0, babel-plugin-transform-es2015-computed-properties@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= dependencies: babel-runtime "^6.22.0" babel-template "^6.24.1" @@ -631,12 +666,14 @@ babel-plugin-transform-es2015-computed-properties@^6.22.0, babel-plugin-transfor babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-duplicate-keys@^6.22.0, babel-plugin-transform-es2015-duplicate-keys@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -644,12 +681,14 @@ babel-plugin-transform-es2015-duplicate-keys@^6.22.0, babel-plugin-transform-es2 babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-function-name@^6.22.0, babel-plugin-transform-es2015-function-name@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= dependencies: babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" @@ -658,12 +697,14 @@ babel-plugin-transform-es2015-function-name@^6.22.0, babel-plugin-transform-es20 babel-plugin-transform-es2015-literals@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= dependencies: babel-plugin-transform-es2015-modules-commonjs "^6.24.1" babel-runtime "^6.22.0" @@ -672,6 +713,7 @@ babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015 babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: version "6.26.2" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== dependencies: babel-plugin-transform-strict-mode "^6.24.1" babel-runtime "^6.26.0" @@ -681,6 +723,7 @@ babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-e babel-plugin-transform-es2015-modules-systemjs@^6.23.0, babel-plugin-transform-es2015-modules-systemjs@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= dependencies: babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" @@ -689,6 +732,7 @@ babel-plugin-transform-es2015-modules-systemjs@^6.23.0, babel-plugin-transform-e babel-plugin-transform-es2015-modules-umd@^6.23.0, babel-plugin-transform-es2015-modules-umd@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= dependencies: babel-plugin-transform-es2015-modules-amd "^6.24.1" babel-runtime "^6.22.0" @@ -697,6 +741,7 @@ babel-plugin-transform-es2015-modules-umd@^6.23.0, babel-plugin-transform-es2015 babel-plugin-transform-es2015-object-super@^6.22.0, babel-plugin-transform-es2015-object-super@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= dependencies: babel-helper-replace-supers "^6.24.1" babel-runtime "^6.22.0" @@ -704,6 +749,7 @@ babel-plugin-transform-es2015-object-super@^6.22.0, babel-plugin-transform-es201 babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015-parameters@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= dependencies: babel-helper-call-delegate "^6.24.1" babel-helper-get-function-arity "^6.24.1" @@ -715,6 +761,7 @@ babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015- babel-plugin-transform-es2015-shorthand-properties@^6.22.0, babel-plugin-transform-es2015-shorthand-properties@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -722,12 +769,14 @@ babel-plugin-transform-es2015-shorthand-properties@^6.22.0, babel-plugin-transfo babel-plugin-transform-es2015-spread@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-sticky-regex@^6.22.0, babel-plugin-transform-es2015-sticky-regex@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= dependencies: babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" @@ -736,18 +785,21 @@ babel-plugin-transform-es2015-sticky-regex@^6.22.0, babel-plugin-transform-es201 babel-plugin-transform-es2015-template-literals@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= dependencies: babel-runtime "^6.22.0" babel-plugin-transform-es2015-unicode-regex@^6.22.0, babel-plugin-transform-es2015-unicode-regex@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= dependencies: babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" @@ -756,6 +808,7 @@ babel-plugin-transform-es2015-unicode-regex@^6.22.0, babel-plugin-transform-es20 babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= dependencies: babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" babel-plugin-syntax-exponentiation-operator "^6.8.0" @@ -764,6 +817,7 @@ babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-e babel-plugin-transform-object-rest-spread@^6.22.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY= dependencies: babel-plugin-syntax-object-rest-spread "^6.8.0" babel-runtime "^6.26.0" @@ -771,12 +825,14 @@ babel-plugin-transform-object-rest-spread@^6.22.0: babel-plugin-transform-regenerator@^6.22.0, babel-plugin-transform-regenerator@^6.24.1: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= dependencies: regenerator-transform "^0.10.0" babel-plugin-transform-strict-mode@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" @@ -784,6 +840,7 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-polyfill@6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= dependencies: babel-runtime "^6.26.0" core-js "^2.5.0" @@ -792,6 +849,7 @@ babel-polyfill@6.26.0: babel-preset-env@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-syntax-trailing-function-commas "^6.22.0" @@ -827,6 +885,7 @@ babel-preset-env@^1.7.0: babel-preset-es2015@6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" + integrity sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk= dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-transform-es2015-arrow-functions "^6.22.0" @@ -856,6 +915,7 @@ babel-preset-es2015@6.24.1: babel-preset-stage-2@6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" + integrity sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE= dependencies: babel-plugin-syntax-dynamic-import "^6.18.0" babel-plugin-transform-class-properties "^6.24.1" @@ -865,6 +925,7 @@ babel-preset-stage-2@6.24.1: babel-preset-stage-3@6.24.1, babel-preset-stage-3@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" + integrity sha1-g2raCp56f6N8sTj7kyb4eTSkg5U= dependencies: babel-plugin-syntax-trailing-function-commas "^6.22.0" babel-plugin-transform-async-generator-functions "^6.24.1" @@ -875,6 +936,7 @@ babel-preset-stage-3@6.24.1, babel-preset-stage-3@^6.24.1: babel-register@6.26.0, babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= dependencies: babel-core "^6.26.0" babel-runtime "^6.26.0" @@ -887,6 +949,7 @@ babel-register@6.26.0, babel-register@^6.26.0: babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" @@ -894,6 +957,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: babel-template@^6.24.1, babel-template@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= dependencies: babel-runtime "^6.26.0" babel-traverse "^6.26.0" @@ -904,6 +968,7 @@ babel-template@^6.24.1, babel-template@^6.26.0: babel-traverse@^6.24.1, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= dependencies: babel-code-frame "^6.26.0" babel-messages "^6.23.0" @@ -918,6 +983,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0: babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= dependencies: babel-runtime "^6.26.0" esutils "^2.0.2" @@ -927,6 +993,7 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: babelify@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= dependencies: babel-core "^6.0.14" object-assign "^4.0.0" @@ -934,28 +1001,36 @@ babelify@^7.3.0: babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base-x@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.4.tgz#94c1788736da065edb1d68808869e357c977fa77" + version "3.0.5" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.5.tgz#d3ada59afed05b921ab581ec3112e6444ba0795a" + integrity sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA== dependencies: safe-buffer "^5.0.1" -base64-js@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" - base64-js@^1.0.2: version "1.3.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" class-utils "^0.3.5" @@ -968,33 +1043,19 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - -bignumber.js@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-5.0.0.tgz#fbce63f09776b3000a83185badcde525daf34833" - integrity sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg== - bignumber.js@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" + integrity sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA== -bignumber.js@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-5.0.0.tgz#fbce63f09776b3000a83185badcde525daf34833" - -bignumber.js@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-6.0.0.tgz#bbfa047644609a5af093e9cbd83b0461fa3f6002" - -"bignumber.js@git+https://github.com/debris/bignumber.js#master": - version "2.0.7" - resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9" +bignumber.js@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.0.2.tgz#d8c4e1874359573b1ef03011a2d861214aeef137" + integrity sha512-EiuvFrnbv0jFixEQ9f58jo7X0qI2lNGIr/MxntmVzQc5JUweDSh8y8hbTCAomFtqwUPIOWcLXP0VEOSZTG7FFw== "bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2": version "2.0.7" @@ -1005,22 +1066,28 @@ bignumber.js@^6.0.0: resolved "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934" binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" + version "1.13.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.0.tgz#9523e001306a32444b907423f1de2164222f6ab1" + integrity sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw== -bindings@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" +bindings@^1.2.1, bindings@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.4.0.tgz#909efa49f2ebe07ecd3cb136778f665052040127" + integrity sha512-7znEVX22Djn+nYjxCWKDne0RRloa9XfYa84yk3s+HkE3LpDYZmhArYr9O9huBoHY3/oXispx5LorIX7Sl2CgSQ== + dependencies: + file-uri-to-path "1.0.0" bip66@^1.1.3: version "1.1.5" resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= dependencies: safe-buffer "^5.0.1" bitcore-lib@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.15.0.tgz#f924be13869f2aab7e04aeec5642ad3359b6cec2" + integrity sha512-AeXLWhiivF6CDFzrABZHT4jJrflyylDWTi32o30rF92HW9msfuKpjzrHtFKYGa9w0kNVv5HABQjCB3OEav4PhQ== dependencies: bn.js "=4.11.8" bs58 "=4.0.1" @@ -1029,16 +1096,30 @@ bitcore-lib@^0.15.0: inherits "=2.0.1" lodash "=4.17.4" +bitcore-lib@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.16.0.tgz#a2c3ec1108cdb90386f728282ab833e0c77c9533" + integrity sha512-CEtcrPAH2gwgaMN+OPMJc18TBEak1+TtzMyafrqrIbK9PIa3kat195qBJhC0liJSHRiRr6IE2eLcXeIFFs+U8w== + dependencies: + bn.js "=4.11.8" + bs58 "=4.0.1" + buffer-compare "=1.1.1" + elliptic "=6.4.0" + inherits "=2.0.1" + lodash "=4.17.11" + bitcore-mnemonic@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bitcore-mnemonic/-/bitcore-mnemonic-1.5.0.tgz#c7e785beb6bf0616ed4992785dc3658670425a39" + version "1.7.0" + resolved "https://registry.yarnpkg.com/bitcore-mnemonic/-/bitcore-mnemonic-1.7.0.tgz#253295a773135e1a0b455871de614996afc8f5e1" + integrity sha512-1JV1okgz9Vv+Y4fG2m3ToR+BGdKA6tSoqjepIxA95BZjW6YaeopVW4iOe/dY9dnkZH4+LA2AJ4YbDE6H3ih3Yw== dependencies: - bitcore-lib "^0.15.0" - unorm "^1.3.3" + bitcore-lib "^0.16.0" + unorm "^1.4.1" bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" + integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" @@ -1046,32 +1127,39 @@ bl@^1.0.0: block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= dependencies: inherits "~2.0.0" bluebird@^2.9.34: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= -bluebird@^3.4.6, bluebird@^3.5.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a" +bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== bn.js@4.11.6: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= -bn.js@=4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.4.0, bn.js@^4.8.0: +bn.js@=4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== bn.js@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.2.0.tgz#12162bc2ae71fc40a5626c33438f3a875cd37625" + integrity sha1-EhYrwq5x/EClYmwzQ486h1zTdiU= body-parser@1.18.3, body-parser@^1.16.0: version "1.18.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= dependencies: bytes "3.0.0" content-type "~1.0.4" @@ -1085,17 +1173,20 @@ body-parser@1.18.3, body-parser@^1.16.0: type-is "~1.6.16" borc@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/borc/-/borc-2.0.3.tgz#08845ea73a6d3211120928ee3929f8dc2de9f52e" + version "2.1.0" + resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.0.tgz#2def2fc69868633b965a9750e7f210d778190303" + integrity sha512-hKTxeYt3AIzIG45epJHv8xJYSF0ktp7nZgFsqi5cPzoL3T8qKMPeUlqydORy6j3NWZvRDANx30PjpTmGho69Gw== dependencies: - bignumber.js "^7.2.1" + bignumber.js "^8.0.1" commander "^2.15.0" ieee754 "^1.1.8" - json-text-sequence "^0.1" + iso-url "~0.4.4" + json-text-sequence "~0.1.0" brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -1103,14 +1194,16 @@ brace-expansion@^1.1.7: braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= dependencies: expand-range "^1.8.1" preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.0, braces@^2.3.1: +braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -1126,18 +1219,22 @@ braces@^2.3.0, braces@^2.3.1: brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" + integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" @@ -1149,6 +1246,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6: browserify-cipher@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" @@ -1157,6 +1255,7 @@ browserify-cipher@^1.0.0: browserify-des@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== dependencies: cipher-base "^1.0.1" des.js "^1.0.0" @@ -1166,19 +1265,23 @@ browserify-des@^1.0.0: browserify-rsa@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= dependencies: bn.js "^4.1.0" randombytes "^2.0.1" -browserify-sha3@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.1.tgz#3ff34a3006ef15c0fb3567e541b91a2340123d11" +browserify-sha3@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.4.tgz#086c47b8c82316c9d47022c26185954576dd8e26" + integrity sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY= dependencies: - js-sha3 "^0.3.1" + js-sha3 "^0.6.1" + safe-buffer "^5.1.1" browserify-sign@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= dependencies: bn.js "^4.1.1" browserify-rsa "^4.0.0" @@ -1188,48 +1291,35 @@ browserify-sign@^4.0.0: inherits "^2.0.1" parse-asn1 "^5.0.0" -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - dependencies: - pako "~1.0.5" - browserslist@^3.2.6: version "3.2.8" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== dependencies: caniuse-lite "^1.0.30000844" electron-to-chromium "^1.3.47" -bs58@=4.0.1, bs58@^4.0.0, bs58@^4.0.1: +bs58@=4.0.1, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= dependencies: base-x "^3.0.2" -bs58@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d" - -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - bson@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.0.tgz#bee57d1fb6a87713471af4e32bcae36de814b5b0" + integrity sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA== buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== buffer-alloc@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== dependencies: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" @@ -1237,65 +1327,59 @@ buffer-alloc@^1.2.0: buffer-compare@=1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-compare/-/buffer-compare-1.1.1.tgz#5be7be853af89198d1f4ddc090d1d66a48aef596" + integrity sha1-W+e+hTr4kZjR9N3AkNHWakiu9ZY= buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@^3.0.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" - dependencies: - base64-js "0.0.8" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@^4.3.0, buffer@^4.9.0: +buffer@^4.9.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.0.5: +buffer@^5.0.5, buffer@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" @@ -1307,53 +1391,43 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" +callsites@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" + integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= caminte@0.3.7: version "0.3.7" resolved "https://registry.yarnpkg.com/caminte/-/caminte-0.3.7.tgz#ec1ec0457664a0f092643b7c646c457d5cd6f693" + integrity sha1-7B7ARXZkoPCSZDt8ZGxFfVzW9pM= dependencies: bluebird "^3.4.6" uuid "^3.0.1" caniuse-lite@^1.0.30000844: - version "1.0.30000890" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000890.tgz#86a18ffcc65d79ec6a437e985761b8bf1c4efeaf" + version "1.0.30000938" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz#b64bf1427438df40183fce910fe24e34feda7a3f" + integrity sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -1361,27 +1435,36 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +"charenc@>= 0.0.1", charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= checkpoint-store@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= dependencies: functional-red-black-tree "^1.0.1" chokidar@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -1394,52 +1477,33 @@ chokidar@^1.6.0: optionalDependencies: fsevents "^1.0.0" -chokidar@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" - dependencies: - anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" - glob-parent "^3.1.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - lodash.debounce "^4.0.8" - normalize-path "^2.1.1" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.5" - optionalDependencies: - fsevents "^1.2.2" - -chownr@^1.0.1: +chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" define-property "^0.2.5" isobject "^3.0.0" static-extend "^0.1.1" -cli-color@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.3.0.tgz#cd2ec212efbd1a0eeb5b017f17d4e2d15e91420f" +cli-color@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.4.0.tgz#7d10738f48526824f8fe7da51857cb0f572fe01f" + integrity sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w== dependencies: ansi-regex "^2.1.1" d "1" @@ -1451,24 +1515,29 @@ cli-color@^1.2.0: cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" +cli-table3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -1477,6 +1546,7 @@ cliui@^3.2.0: cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== dependencies: string-width "^2.1.1" strip-ansi "^4.0.0" @@ -1485,25 +1555,22 @@ cliui@^4.0.0: clone@2.x, clone@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -coinstring@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/coinstring/-/coinstring-2.3.0.tgz#cdb63363a961502404a25afb82c2e26d5ff627a4" - dependencies: - bs58 "^2.0.1" - create-hash "^1.1.1" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -1511,135 +1578,145 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= colors@^1.1.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" - -combined-stream@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" + version "1.3.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" + integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== -combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== dependencies: delayed-stream "~1.0.0" commander@2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== -commander@^2.11.0, commander@^2.14.1, commander@^2.15.0, commander@^2.8.1, commander@^2.9.0: +commander@^2.14.1, commander@^2.15.0, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== commander@~2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= dependencies: graceful-readlink ">= 1.0.0" compare-versions@^3.0.1: version "3.4.0" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26" + integrity sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg== component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0: +concat-stream@^1.4.6, concat-stream@^1.5.1, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" -console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - dependencies: - date-now "^0.1.4" - console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== convert-source-map@^1.5.1: version "1.6.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= cookiejar@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js@^2.4.0, core-js@^2.5.0: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + version "2.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" + integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cors@^2.8.1: - version "2.8.4" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" vary "^1" @@ -1647,6 +1724,7 @@ cors@^2.8.1: coveralls@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.2.tgz#f5a0bcd90ca4e64e088b710fa8dda640aea4884f" + integrity sha512-Tv0LKe/MkBOilH2v7WBiTBdudg2ChfGbdXafc/s330djpF3zKOmuehTeRwjXWc7pzfj9FrDUTA7tEx6Div8NFw== dependencies: growl "~> 1.10.0" js-yaml "^3.11.0" @@ -1658,13 +1736,15 @@ coveralls@^3.0.1: create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== dependencies: bn.js "^4.1.0" elliptic "^6.0.0" -create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: +create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" inherits "^2.0.1" @@ -1675,6 +1755,7 @@ create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== dependencies: cipher-base "^1.0.3" create-hash "^1.1.0" @@ -1683,16 +1764,26 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cron-parser@^2.4.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.6.0.tgz#ae2514ceda9ccb540256e201bdd23ae814e03674" +cron-parser@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.7.3.tgz#12603f89f5375af353a9357be2543d3172eac651" + integrity sha512-t9Kc7HWBWPndBzvbdQ1YG9rpPRB37Tb/tTviziUOh1qs3TARGh3b1p+tnkOHNe1K5iI3oheBPgLqwotMM7+lpg== dependencies: is-nan "^1.2.1" - moment-timezone "^0.5.0" + moment-timezone "^0.5.23" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= dependencies: lru-cache "^4.0.1" shebang-command "^1.2.0" @@ -1709,9 +1800,15 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -crypto-browserify@3.12.0, crypto-browserify@^3.11.0: +"crypt@>= 0.0.1", crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +crypto-browserify@3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" @@ -1728,96 +1825,91 @@ crypto-browserify@3.12.0, crypto-browserify@^3.11.0: crypto-js@^3.1.4, crypto-js@^3.1.5: version "3.1.8" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.8.tgz#715f070bf6014f2ae992a98b3929258b713f08d5" - -crypto-js@^3.1.9-1: - version "3.1.9-1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" + integrity sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU= crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= csextends@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/csextends/-/csextends-1.2.0.tgz#6374b210984b54d4495f29c99d3dd069b80543e5" + integrity sha512-S/8k1bDTJIwuGgQYmsRoE+8P+ohV32WhQ0l4zqrc0XDdxOhjQQD7/wTZwCzoZX53jSX3V/qwjT+OkPTxWQcmjg== cycle@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" + integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8= dependencies: es5-ext "^0.10.9" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" -date-extended@~0.0.3: - version "0.0.6" - resolved "https://registry.yarnpkg.com/date-extended/-/date-extended-0.0.6.tgz#23802d57dd1bf7818813fe0c32e851a86da267c9" - dependencies: - array-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - death@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg= debug@*, debug@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" -debug@^3.0.1, debug@^3.1.0: +debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" -decamelize@^1.0.0, decamelize@^1.1.1: +decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -declare.js@~0.0.4: - version "0.0.8" - resolved "https://registry.yarnpkg.com/declare.js/-/declare.js-0.0.8.tgz#0478adff9564c004f51df73d8bc134019d28dcde" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= decompress-response@^3.2.0, decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== dependencies: file-type "^5.2.0" is-stream "^1.1.0" @@ -1826,6 +1918,7 @@ decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: decompress-tarbz2@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== dependencies: decompress-tar "^4.1.0" file-type "^6.1.0" @@ -1836,6 +1929,7 @@ decompress-tarbz2@^4.0.0: decompress-targz@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== dependencies: decompress-tar "^4.1.1" file-type "^5.2.0" @@ -1844,6 +1938,7 @@ decompress-targz@^4.0.0: decompress-unzip@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= dependencies: file-type "^3.8.0" get-stream "^2.2.0" @@ -1853,6 +1948,7 @@ decompress-unzip@^4.0.1: decompress@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" + integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" @@ -1863,51 +1959,58 @@ decompress@^4.0.0: pify "^2.3.0" strip-dirs "^2.0.0" -deep-assign@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-2.0.0.tgz#ebe06b1f07f08dae597620e3dd1622f371a1c572" - dependencies: - is-obj "^1.0.0" +deep-equal@~0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d" + integrity sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0= deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deferred-leveldown@~1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== dependencies: abstract-leveldown "~2.6.0" define-properties@^1.1.1, define-properties@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" isobject "^3.0.1" @@ -1915,38 +2018,32 @@ define-property@^2.0.2: defined@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= delimit-stream@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" + integrity sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs= depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -1954,28 +2051,34 @@ des.js@^1.0.0: destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= dependencies: repeating "^2.0.0" detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= diff@3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" + integrity sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww== -diff@3.5.0: +diff@3.5.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" @@ -1984,27 +2087,27 @@ diffie-hellman@^5.0.0: doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= dependencies: esutils "^2.0.2" isarray "^1.0.0" -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= drbg.js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= dependencies: browserify-aes "^1.0.6" create-hash "^1.1.2" @@ -2013,51 +2116,50 @@ drbg.js@^1.0.1: duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= eachr@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/eachr/-/eachr-2.0.4.tgz#466f7caa10708f610509e32c807aafe57fc122bf" + integrity sha1-Rm98qhBwj2EFCeMsgHqv5X/BIr8= dependencies: typechecker "^2.0.8" ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" -editions@^1.1.1, editions@^1.3.3, editions@^1.3.4: +editions@^1.1.1, editions@^1.3.3: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" + integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== -editions@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/editions/-/editions-2.0.2.tgz#54fdac6fb24b0a1a72ffc1ba0126c10602c3e0bd" +editions@^2.1.0, editions@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/editions/-/editions-2.1.3.tgz#727ccf3ec2c7b12dcc652c71000f16c4824d6f7d" + integrity sha512-xDZyVm0A4nLgMNWVVLJvcwMjI80ShiH/27RyLiCnW1L273TcJIA25C4pwJ33AWV01OX6UriP35Xu+lH4S7HWQw== dependencies: - errlop "^1.0.2" - semver "^5.5.0" + errlop "^1.1.1" + semver "^5.6.0" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.47: - version "1.3.78" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.78.tgz#ecb72b5b166ba6598efb384461d63cad74678ebf" - -elliptic@6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - inherits "^2.0.1" + version "1.3.113" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9" + integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g== elliptic@=6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8= dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -2070,6 +2172,7 @@ elliptic@=6.4.0: elliptic@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-3.1.0.tgz#c21682ef762769b56a74201609105da11d5f60cc" + integrity sha1-whaC73YnabVqdCAWCRBdoR1fYMw= dependencies: bn.js "^2.0.3" brorand "^1.0.1" @@ -2079,6 +2182,7 @@ elliptic@^3.1.0: elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: version "6.4.1" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" + integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -2088,122 +2192,107 @@ elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== dependencies: once "^1.4.0" -enhanced-resolve@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - object-assign "^4.0.1" - tapable "^0.2.7" - eol@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" + integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== -errlop@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/errlop/-/errlop-1.0.3.tgz#dba29c90cf832c3d2ce469fe515d7e5eef2c6676" +errlop@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/errlop/-/errlop-1.1.1.tgz#d9ae4c76c3e64956c5d79e6e035d6343bfd62250" + integrity sha512-WX7QjiPHhsny7/PQvrhS5VMizXXKoKCS3udaBp8gjlARdbn+XmK300eKBAAN0hGyRaTCtRpOaxK+xFVPUJ3zkw== dependencies: - editions "^1.3.4" + editions "^2.1.2" -errno@^0.1.3, errno@~0.1.1: +errno@~0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== dependencies: prr "~1.0.1" error-ex@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.5.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== dependencies: - es-to-primitive "^1.1.1" + es-to-primitive "^1.2.0" function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" + has "^1.0.3" + is-callable "^1.1.4" is-regex "^1.0.4" + object-keys "^1.0.12" -es-to-primitive@^1.1.1: +es-to-primitive@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" is-symbol "^1.0.2" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.46" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572" + version "0.10.47" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.47.tgz#d24232e1380daad5449a817be19bde9729024a11" + integrity sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" next-tick "1" -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: +es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= dependencies: d "1" es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: +es6-symbol@^3.1.1, es6-symbol@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= dependencies: d "1" es5-ext "~0.10.14" -es6-weak-map@^2.0.1, es6-weak-map@^2.0.2: +es6-weak-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + integrity sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8= dependencies: d "1" es5-ext "^0.10.14" @@ -2213,14 +2302,17 @@ es6-weak-map@^2.0.1, es6-weak-map@^2.0.2: escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= dependencies: esprima "^2.7.1" estraverse "^1.9.1" @@ -2229,59 +2321,55 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-standard@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz#87ee0d3c9d95382dc761958cbb23da9eea31e0ba" +eslint-config-standard@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" + integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ== -eslint-import-resolver-node@^0.3.1: +eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== dependencies: debug "^2.6.9" resolve "^1.5.0" -eslint-module-utils@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" +eslint-module-utils@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz#546178dab5e046c8b562bbb50705e2456d7bda49" + integrity sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w== dependencies: debug "^2.6.8" - pkg-dir "^1.0.0" + pkg-dir "^2.0.0" eslint-plugin-es@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.3.1.tgz#5acb2565db4434803d1d46a9b4cbc94b345bd028" - integrity sha512-9XcVyZiQRVeFjqHw8qHNDAZcQLqaHlOGGpeYqzYh8S4JYCWTCO3yzyen8yVmA5PratfzTRWDwCOFphtDEG+w/w== + version "1.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" + integrity sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw== dependencies: eslint-utils "^1.3.0" - regexpp "^2.0.0" + regexpp "^2.0.1" eslint-plugin-import@^2.10.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8" + version "2.16.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz#97ac3e75d0791c4fac0e15ef388510217be7f66f" + integrity sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A== dependencies: contains-path "^0.1.0" - debug "^2.6.8" + debug "^2.6.9" doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.2.0" - has "^1.0.1" - lodash "^4.17.4" - minimatch "^3.0.3" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.3.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" read-pkg-up "^2.0.0" - resolve "^1.6.0" + resolve "^1.9.0" -eslint-plugin-node@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz#bf19642298064379315d7a4b2a75937376fa05e4" +eslint-plugin-node@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz#55ae3560022863d141fa7a11799532340a685964" + integrity sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w== dependencies: eslint-plugin-es "^1.3.1" eslint-utils "^1.3.1" @@ -2290,17 +2378,20 @@ eslint-plugin-node@^6.0.1: resolve "^1.8.1" semver "^5.5.0" -eslint-plugin-promise@^3.7.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621" +eslint-plugin-promise@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2" + integrity sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg== -eslint-plugin-standard@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz#2a9e21259ba4c47c02d53b2d0c9135d4b1022d47" +eslint-plugin-standard@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" + integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA== -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -2313,97 +2404,107 @@ eslint-utils@^1.3.0, eslint-utils@^1.3.1: eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== -eslint@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" +eslint@^5.8.0: + version "5.14.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.14.1.tgz#490a28906be313685c55ccd43a39e8d22efc04ba" + integrity sha512-CyUMbmsjxedx8B0mr79mNOqetvkbij/zrXnFeK2zc3pGRn3/tibjiNAv/3UxFEyfMDjh+ZqTrJrEGBFiGfD5Og== dependencies: "@babel/code-frame" "^7.0.0" - ajv "^6.5.3" + ajv "^6.9.1" chalk "^2.1.0" cross-spawn "^6.0.5" debug "^4.0.1" - doctrine "^2.1.0" + doctrine "^3.0.0" eslint-scope "^4.0.0" eslint-utils "^1.3.1" eslint-visitor-keys "^1.0.0" - espree "^4.0.0" + espree "^5.0.1" esquery "^1.0.1" esutils "^2.0.2" - file-entry-cache "^2.0.0" + file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" glob "^7.1.2" globals "^11.7.0" ignore "^4.0.6" + import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.1.0" - is-resolvable "^1.1.0" + inquirer "^6.2.2" js-yaml "^3.12.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.17.5" + lodash "^4.17.11" minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" path-is-inside "^1.0.2" - pluralize "^7.0.0" progress "^2.0.0" regexpp "^2.0.1" - require-uncached "^1.0.3" semver "^5.5.1" strip-ansi "^4.0.0" strip-json-comments "^2.0.1" - table "^5.0.2" + table "^5.2.3" text-table "^0.2.0" -espree@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== dependencies: - acorn "^6.0.2" + acorn "^6.0.7" acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== dependencies: estraverse "^4.0.0" esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== dependencies: estraverse "^4.1.0" estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= eth-block-tracker@^2.2.2: version "2.3.1" resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.3.1.tgz#ab6d177e5b50128fa06d7ae9e0489c7484bac95e" + integrity sha512-NamWuMBIl8kmkJFVj8WzGatySTzQPQag4Xr677yFxdVtIxACFbL/dQowk0MzEqIKk93U1TwY3MjVU6mOcwZnKA== dependencies: async-eventemitter ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c eth-query "^2.1.0" @@ -2414,13 +2515,75 @@ eth-block-tracker@^2.2.2: pify "^2.3.0" tape "^4.6.3" -eth-lib@0.1.27, eth-lib@^0.1.26: - version "0.1.27" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - keccakjs "^0.2.1" + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-gas-reporter@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.1.12.tgz#6b761e05c33ae85be47840dd07468ab51d473dd8" + integrity sha512-Ao5uiXSA5Ep5fi/YvGCsFJMelMKj0fMJkAvWYzPVe1h3Mg9Z7X3Rs51ovG9izFZH7wSqnqydiC6SKDhZWpxK2g== + dependencies: + abi-decoder "^1.0.8" + cli-table3 "^0.5.0" + colors "^1.1.2" + lodash "^4.17.4" + mocha "^4.1.0" + req-cwd "^2.0.0" + request "^2.83.0" + request-promise-native "^1.0.5" + sha1 "^1.1.1" + shelljs "^0.7.8" + solidity-parser-antlr "^0.2.10" + sync-request "^6.0.0" + +eth-json-rpc-infura@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.0.tgz#62c3f516b51351038c32a548704467cec113ca8f" + integrity sha512-FLcpdxPRVBCUc7yoE+wHGvyYg2lATedP+/q7PsKvaSzQpJbgTG4ZjLnyrLanxDr6M1k/dSNa6V5QnILwjUKJcw== + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + tape "^4.8.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + +eth-lib@0.1.27, eth-lib@^0.1.26: + version "0.1.27" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" + integrity sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + keccakjs "^0.2.1" nano-json-stream-parser "^0.1.2" servify "^0.1.12" ws "^3.0.0" @@ -2429,6 +2592,7 @@ eth-lib@0.1.27, eth-lib@^0.1.26: eth-lib@0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + integrity sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco= dependencies: bn.js "^4.11.6" elliptic "^6.4.0" @@ -2437,6 +2601,7 @@ eth-lib@0.2.7: eth-lightwallet@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/eth-lightwallet/-/eth-lightwallet-3.0.1.tgz#297022932aa568f4e4eb0873bff257f5e5b78709" + integrity sha512-79vVCETy+4l1b6wuOWwjqPW3Bom5ZK46BgkUNwaXhiMG1rrMRHjpjYEWMqH0JHeCzOzB4HBIFz7eK1/4s6w5nA== dependencies: bitcore-lib "^0.15.0" bitcore-mnemonic "^1.5.0" @@ -2450,9 +2615,10 @@ eth-lightwallet@^3.0.1: tweetnacl "0.13.2" web3 "0.20.2" -eth-query@^2.1.0: +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= dependencies: json-rpc-random-id "^1.0.0" xtend "^4.0.1" @@ -2460,13 +2626,34 @@ eth-query@^2.1.0: eth-sig-util@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" + integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA= dependencies: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +eth-tx-summary@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.3.tgz#a52d7215616888e012fbc083b3eacd28f3e64764" + integrity sha512-1gZpA5fKarJOVSb5OUlPnhDQuIazqAkI61zlVvf5LdG47nEgw+/qhyZnuj3CUdE/TLTKuRzPLeyXLjaB4qWTRQ== + dependencies: + async "^2.1.2" + bn.js "^4.11.8" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "2.3.4" + through2 "^2.0.3" + treeify "^1.0.1" + web3-provider-engine "^13.3.2" + ethereum-bridge@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/ethereum-bridge/-/ethereum-bridge-0.6.1.tgz#53c93ed7c0e21752a91e5f089a5997e1d6fea228" + integrity sha512-yDTivI85618BoLI71yNRzW6iVcVN2rjnviCIzs0QOCOENj4XpYQhMDGhdqDi8XWDdzTd0Ja/Canuuh3vfE2IcA== dependencies: async "^2.4.1" borc "^2.0.2" @@ -2497,21 +2684,24 @@ ethereum-bridge@^0.6.1: ethereum-common@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= ethereumjs-abi@0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.4.tgz#9ba1bb056492d00c27279f6eccd4d58275912c1a" + integrity sha1-m6G7BWSS0AwnJ59uzNTVgnWRLBo= dependencies: bn.js "^4.10.0" ethereumjs-util "^4.3.0" ethereumjs-abi@^0.6.5, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": - version "0.6.5" - resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799" + version "0.6.6" + resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215" dependencies: bn.js "^4.10.0" ethereumjs-util "^5.0.0" @@ -2519,14 +2709,16 @@ ethereumjs-abi@^0.6.5, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereu ethereumjs-account@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== dependencies: ethereumjs-util "^5.0.0" rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-block@^1.2.2, ethereumjs-block@~1.7.0: +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0, ethereumjs-block@~1.7.0: version "1.7.1" resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== dependencies: async "^2.0.1" ethereum-common "0.2.0" @@ -2534,25 +2726,33 @@ ethereumjs-block@^1.2.2, ethereumjs-block@~1.7.0: ethereumjs-util "^5.0.0" merkle-patricia-tree "^2.1.2" -ethereumjs-common@~0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-0.4.1.tgz#27690a24a817b058cc3a2aedef9392e8d7d63984" +ethereumjs-block@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.0.tgz#8c6c3ab4a5eff0a16d9785fbeedbe643f4dbcbef" + integrity sha512-Ye+uG/L2wrp364Zihdlr/GfC3ft+zG8PdHcRtsBFNNH1CkOhxOwdB8friBU85n89uRZ9eIMAywCq0F4CwT1wAw== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.1.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.1.0.tgz#5ec9086c314d619d8f05e79a0525829fcb0e93cb" + integrity sha512-LUmYkKV/HcZbWRyu3OU9YOevsH3VJDXtI6kEd8VZweQec+JjDGKCmAVKUyzhYUHqxRJu7JNALZ3A/b3NXOP6tA== ethereumjs-testrpc-sc@6.1.6: version "6.1.6" resolved "https://registry.yarnpkg.com/ethereumjs-testrpc-sc/-/ethereumjs-testrpc-sc-6.1.6.tgz#290595380b5182814564d4aa38f35b7788aab070" + integrity sha512-iv2qiGBFgk9mn5Nq2enX8dG5WQ7Lk+FCqpnxfPfH4Ns8KLPwttmNOy264nh3SXDJJvcQwz/XnlLteDQVILotbg== dependencies: source-map-support "^0.5.3" -ethereumjs-testrpc@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz#7a0b87bf3670f92f607f98fa6a78801d9741b124" - dependencies: - webpack "^3.0.0" - -ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.1, ethereumjs-tx@^1.3.3, ethereumjs-tx@^1.3.4: +ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.1, ethereumjs-tx@^1.3.3: version "1.3.7" resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== dependencies: ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" @@ -2560,6 +2760,7 @@ ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.1, ethereumjs-tx@ ethereumjs-util@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" + integrity sha1-PpQosxfuvaPXJg2FT93alUsfG8Y= dependencies: bn.js "^4.8.0" create-hash "^1.1.2" @@ -2567,9 +2768,10 @@ ethereumjs-util@^4.3.0: rlp "^2.0.0" secp256k1 "^3.0.1" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.3, ethereumjs-util@^5.2.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" + integrity sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA== dependencies: bn.js "^4.11.0" create-hash "^1.1.2" @@ -2579,75 +2781,73 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-vm@^2.0.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.4.0.tgz#244f1e35f2755e537a13546111d1a4c159d34b13" +ethereumjs-util@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" + integrity sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "0.1.6" + keccak "^1.0.2" + rlp "^2.0.0" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +ethereumjs-vm@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.3.4.tgz#f635d7cb047571a1840a6e9a74d29de4488f8ad6" + integrity sha512-Y4SlzNDqxrCO58jhp98HdnZVdjOqB+HC0hoU+N/DEp1aU+hFkRX/nru5F7/HkQRPIlA6aJlQp/xIA6xZs1kspw== dependencies: async "^2.1.2" async-eventemitter "^0.2.2" + ethereum-common "0.2.0" ethereumjs-account "^2.0.3" ethereumjs-block "~1.7.0" - ethereumjs-common "~0.4.0" - ethereumjs-util "^5.2.0" + ethereumjs-util "^5.1.3" fake-merkle-patricia-tree "^1.0.1" functional-red-black-tree "^1.0.1" merkle-patricia-tree "^2.1.2" - rustbn.js "~0.2.0" + rustbn.js "~0.1.1" safe-buffer "^5.1.1" -ethereumjs-wallet@^0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda" - dependencies: - aes-js "^3.1.1" - bs58check "^2.1.2" - ethereumjs-util "^5.2.0" - hdkey "^1.0.0" - safe-buffer "^5.1.2" - scrypt.js "^0.2.0" - utf8 "^3.0.0" - uuid "^3.3.2" - -ethers@^3.0.15: - version "3.0.29" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.29.tgz#ce8139955b4ed44456eb6764b089bb117c86775d" - dependencies: - "@types/node" "^10.3.2" - aes-js "3.0.0" - bn.js "^4.4.0" - elliptic "6.3.3" - hash.js "1.1.3" - js-sha3 "0.5.7" - scrypt-js "2.0.4" - setimmediate "1.0.4" - uuid "2.0.1" - xmlhttprequest "1.8.0" - -ethjs-abi@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.1.8.tgz#cd288583ed628cdfadaf8adefa3ba1dbcbca6c18" +ethereumjs-vm@^2.0.2, ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== dependencies: - bn.js "4.11.6" - js-sha3 "0.5.5" - number-to-bn "1.7.0" + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= dependencies: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@^0.1.3: +ethjs-util@0.1.6, ethjs-util@^0.1.3: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== dependencies: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -event-emitter@^0.3.5, event-emitter@~0.3.5: +event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= dependencies: d "1" es5-ext "~0.10.14" @@ -2655,18 +2855,17 @@ event-emitter@^0.3.5, event-emitter@~0.3.5: eventemitter3@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.1.1.tgz#47786bdaa087caf7b1b75e73abc5c7d540158cd0" - -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-R3hr2qCHyvext15zq8XH1UAVjNA= events@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" safe-buffer "^5.1.1" @@ -2674,6 +2873,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= dependencies: cross-spawn "^5.0.1" get-stream "^3.0.0" @@ -2686,12 +2886,14 @@ execa@^0.7.0: expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= dependencies: is-posix-bracket "^0.1.0" expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -2704,12 +2906,14 @@ expand-brackets@^2.1.4: expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= dependencies: fill-range "^2.1.0" express@^4.14.0: version "4.16.4" resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== dependencies: accepts "~1.3.5" array-flatten "1.1.1" @@ -2745,12 +2949,14 @@ express@^4.14.0: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -2758,28 +2964,19 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - -extended@0.0.6, extended@~0.0.3: - version "0.0.6" - resolved "https://registry.yarnpkg.com/extended/-/extended-0.0.6.tgz#7fb8bf7b9dae397586e48570acfd642c78e50669" - dependencies: - extender "~0.0.5" - -extender@~0.0.5: - version "0.0.10" - resolved "https://registry.yarnpkg.com/extender/-/extender-0.0.10.tgz#589c07482be61a1460b6d81f9c24aa67e8f324cd" - dependencies: - declare.js "~0.0.4" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extendr@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/extendr/-/extendr-2.1.0.tgz#301aa0bbea565f4d2dc8f570f2a22611a8527b56" + integrity sha1-MBqgu+pWX00tyPVw8qImEahSe1Y= dependencies: typechecker "~2.0.1" -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" +external-editor@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" + integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" @@ -2788,12 +2985,14 @@ external-editor@^2.0.4: extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= dependencies: is-extglob "^1.0.0" extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" define-property "^1.0.0" @@ -2807,96 +3006,109 @@ extglob@^2.0.4: extract-opts@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/extract-opts/-/extract-opts-2.2.0.tgz#1fa28eba7352c6db480f885ceb71a46810be6d7d" + integrity sha1-H6KOunNSxttID4hc63GkaBC+bX0= dependencies: typechecker "~2.0.1" extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= eyes@0.1.x: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= fake-merkle-patricia-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= dependencies: checkpoint-store "^1.1.0" -fast-csv@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-2.4.1.tgz#bd7dd268391f729367b59445b8dd0ad026881b26" - dependencies: - extended "0.0.6" - is-extended "0.0.10" - object-extended "0.0.7" - string-extended "0.0.8" - fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= dependencies: pend "~1.2.0" fetch-ponyfill@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= dependencies: node-fetch "~1.7.1" figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" + flat-cache "^2.0.1" file-type@^3.8.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= file-type@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha1-LdvqfHP/42No365J3DOMBYwritY= file-type@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= fill-range@^2.1.0: version "2.2.4" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== dependencies: is-number "^2.1.0" isobject "^2.0.0" @@ -2907,6 +3119,7 @@ fill-range@^2.1.0: fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -2916,6 +3129,7 @@ fill-range@^4.0.0: finalhandler@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== dependencies: debug "2.6.9" encodeurl "~1.0.2" @@ -2928,6 +3142,7 @@ finalhandler@1.1.1: find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -2935,41 +3150,52 @@ find-up@^1.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== for-each@^0.3.2, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== dependencies: is-callable "^1.1.3" for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= dependencies: for-in "^1.0.1" forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" +form-data@^2.2.0, form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" @@ -2978,24 +3204,29 @@ form-data@~2.3.2: forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" @@ -3006,21 +3237,15 @@ fs-extra@^0.30.0: fs-extra@^2.0.0, fs-extra@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35" + integrity sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU= dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" -fs-extra@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -3029,12 +3254,14 @@ fs-extra@^5.0.0: fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== dependencies: minipass "^2.2.1" fs-promise@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-2.0.3.tgz#f64e4f854bcf689aa8bddcba268916db3db46854" + integrity sha1-9k5PhUvPaJqovdy6JokW2z20aFQ= dependencies: any-promise "^1.3.0" fs-extra "^2.0.0" @@ -3044,14 +3271,17 @@ fs-promise@^2.0.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fs@0.0.1-security: - version "0.0.1-security" - resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" +fs@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.2.tgz#e1f244ef3933c1b2a64bd4799136060d0f5914f8" + integrity sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg= -fsevents@^1.0.0, fsevents@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" +fsevents@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" + integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== dependencies: nan "^2.9.2" node-pre-gyp "^0.10.0" @@ -3059,6 +3289,7 @@ fsevents@^1.0.0, fsevents@^1.2.2: fstream@^1.0.2, fstream@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -3068,20 +3299,24 @@ fstream@^1.0.2, fstream@^1.0.8: function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -ganache-cli@^6.1.8: +ganache-cli@6.1.8: version "6.1.8" resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.8.tgz#49a8a331683a9652183f82ef1378d17e1814fcd3" + integrity sha512-yXzteu4SIgUL31mnpm9j+x6dpHUw0p/nsRVkcySKq0w+1vDxH9jMErP1QhZAJuTVE6ni4nfvGSNkaQx5cD3jfg== dependencies: source-map-support "^0.5.3" gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -3095,10 +3330,17 @@ gauge@~2.7.3: get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= get-stream@^2.2.0: version "2.3.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= dependencies: object-assign "^4.0.1" pinkie-promise "^2.0.0" @@ -3106,20 +3348,24 @@ get-stream@^2.2.0: get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= dependencies: glob-parent "^2.0.0" is-glob "^2.0.0" @@ -3127,19 +3373,14 @@ glob-base@^0.3.0: glob-parent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= dependencies: is-glob "^2.0.0" -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3151,6 +3392,7 @@ glob@7.1.2: glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= dependencies: inflight "^1.0.4" inherits "2" @@ -3158,9 +3400,10 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@~7.1.2: +glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@~7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3172,6 +3415,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@~7.1.2: glob@~6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= dependencies: inflight "^1.0.4" inherits "2" @@ -3182,32 +3426,25 @@ glob@~6.0.4: global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= dependencies: min-document "^2.19.0" process "~0.5.1" globals@^11.7.0: - version "11.8.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.8.0.tgz#c1ef45ee9bed6badf0663c5cb90e8d1adec1321d" + version "11.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.11.0.tgz#dcf93757fa2de5486fbeed7118538adf789e9c2e" + integrity sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw== globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== got@7.1.0, got@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== dependencies: decompress-response "^3.2.0" duplexer3 "^0.1.4" @@ -3225,24 +3462,29 @@ got@7.1.0, got@^7.1.0: url-to-options "^1.0.1" graceful-fs@*, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= growl@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" + integrity sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q== growl@1.10.5, "growl@~> 1.10.0": version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== handlebars@^4.0.1: - version "4.0.12" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" + version "4.1.0" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" + integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== dependencies: async "^2.5.0" optimist "^0.6.1" @@ -3253,53 +3495,64 @@ handlebars@^4.0.1: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== dependencies: - ajv "^5.3.0" + ajv "^6.5.5" har-schema "^2.0.0" has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== dependencies: has-symbol-support-x "^1.4.1" has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -3308,6 +3561,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -3316,57 +3570,48 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.1, has@~1.0.3: +has@^1.0.1, has@^1.0.3, has@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hash-base@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" -hash.js@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" - integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.0" - hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hdkey@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29" - dependencies: - coinstring "^2.0.0" - safe-buffer "^5.1.1" - secp256k1 "^3.0.1" - he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -3375,6 +3620,7 @@ hmac-drbg@^1.0.0: home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.1" @@ -3382,10 +3628,24 @@ home-or-tmp@^2.0.0: hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + +http-basic@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-7.0.0.tgz#82f0a506be942732ec8deebee80e746ef5736dba" + integrity sha1-gvClBr6UJzLsje6+6A50bvVzbbo= + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/node" "^9.4.1" + caseless "~0.12.0" + concat-stream "^1.4.6" + http-response-object "^3.0.1" + parse-cache-control "^1.0.1" http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= dependencies: depd "~1.1.2" inherits "2.0.3" @@ -3395,22 +3655,28 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: http-https@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-response-object@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.1.tgz#90174d44c27b5e797cf6efe51a043bc889ae64bf" + integrity sha512-6L0Fkd6TozA8kFSfh9Widst0wfza3U1Ex2RjJ6zNDK0vR1U1auUR6jY4Nn2Xl7CCy0ikFmxW1XcspVpb9RvwTg== + dependencies: + "@types/node" "^9.3.0" http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" sshpk "^1.7.0" -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - i18n@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.8.3.tgz#2d8cf1c24722602c2041d01ba6ae5eaa51388f0e" + integrity sha1-LYzxwkciYCwgQdAbpq5eqlE4jw4= dependencies: debug "*" make-plural "^3.0.3" @@ -3419,35 +3685,51 @@ i18n@^0.8.3: mustache "*" sprintf-js ">=1.0.3" +i@0.3.x: + version "0.3.6" + resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d" + integrity sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0= + iconv-lite@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.4, ieee754@^1.1.8: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== ignore-walk@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== dependencies: minimatch "^3.0.4" -ignore@^3.3.3, ignore@^3.3.6: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.5.tgz#c663c548d6ce186fb33616a8ccb5d46e56bdbbf9" + integrity sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA== ignorefs@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ignorefs/-/ignorefs-1.2.0.tgz#da59fb858976e4a5e43702ccd1f282fdbc9e5756" + integrity sha1-2ln7hYl25KXkNwLM0fKC/byeV1Y= dependencies: editions "^1.3.3" ignorepatterns "^1.1.0" @@ -3455,22 +3737,30 @@ ignorefs@^1.0.0: ignorepatterns@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ignorepatterns/-/ignorepatterns-1.1.0.tgz#ac8f436f2239b5dfb66d5f0d3a904a87ac67cc5e" + integrity sha1-rI9DbyI5td+2bV8NOpBKh6xnzF4= immediate@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + integrity sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" @@ -3478,106 +3768,118 @@ inflight@^1.0.4: inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inherits@2.0.1, inherits@=2.0.1: +inherits@=2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" +inquirer@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" + integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA== dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" + ansi-escapes "^3.2.0" + chalk "^2.4.2" cli-cursor "^2.1.0" cli-width "^2.0.0" - external-editor "^3.0.0" + external-editor "^3.0.3" figures "^2.0.0" - lodash "^4.17.10" + lodash "^4.17.11" mute-stream "0.0.7" run-async "^2.2.0" - rxjs "^6.1.0" + rxjs "^6.4.0" string-width "^2.1.0" - strip-ansi "^4.0.0" + strip-ansi "^5.0.0" through "^2.3.6" interpret@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + version "1.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" + integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= ipaddr.js@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= dependencies: binary-extensions "^1.0.0" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.3, is-callable@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -3586,6 +3888,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" is-data-descriptor "^1.0.0" @@ -3594,227 +3897,216 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= is-equal-shallow@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= dependencies: is-primitive "^2.0.0" is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" -is-extended@0.0.10, is-extended@~0.0.3, is-extended@~0.0.8: - version "0.0.10" - resolved "https://registry.yarnpkg.com/is-extended/-/is-extended-0.0.10.tgz#244e140df75bb1c9a3106f412ff182fb534a6d62" - dependencies: - extended "~0.0.3" - is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= dependencies: number-is-nan "^1.0.0" is-fn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-function@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= dependencies: is-extglob "^1.0.0" -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - dependencies: - is-extglob "^2.1.1" - is-hex-prefixed@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= is-nan@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2" + integrity sha1-n69ltvttskt/XAYoR16nH5iEAeI= dependencies: define-properties "^1.1.1" is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= dependencies: kind-of "^3.0.2" is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-number@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== is-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - dependencies: - path-is-inside "^1.0.1" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= is-promise@^2.1, is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= dependencies: has "^1.0.1" -is-resolvable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-symbol@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== dependencies: has-symbols "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +iso-url@~0.4.4: + version "0.4.6" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-0.4.6.tgz#45005c4af4984cad4f8753da411b41b74cf0a8a6" + integrity sha512-YQO7+aIe6l1aSJUKOx+Vrv08DlhZeLFIVfehG2L29KLSEb9RszqPXilxJRVpp57px36BddKR5ZsebacO5qG0tg== isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - -isomorphic-fetch@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs= dependencies: abbrev "1.0.x" async "1.x" @@ -3834,37 +4126,35 @@ istanbul@^0.4.5: isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== dependencies: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" -js-sha3@0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a" - -js-sha3@0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - -js-sha3@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" +js-sha3@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.6.1.tgz#5b89f77a7477679877f58c4a075240934b1f95c0" + integrity sha1-W4n3enR3Z5h39YxKB1JAk0sflcA= js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= js-yaml@3.x, js-yaml@^3.11.0, js-yaml@^3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + version "3.12.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" + integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -3872,22 +4162,22 @@ js-yaml@3.x, js-yaml@^3.11.0, js-yaml@^3.12.0: jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-loader@^0.5.4: - version "0.5.7" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" - -json-rpc-engine@^3.6.0: +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: version "3.8.0" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" + integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== dependencies: async "^2.0.1" babel-preset-env "^1.7.0" @@ -3899,68 +4189,82 @@ json-rpc-engine@^3.6.0: json-rpc-error@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" + integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= dependencies: inherits "^2.0.1" json-rpc-random-id@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= dependencies: jsonify "~0.0.0" json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json-text-sequence@^0.1: +json-text-sequence@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2" + integrity sha1-py8hfcSvxGKf/1/rME3BvVGi89I= dependencies: delimit-stream "0.1.0" -json5@^0.5.0, json5@^0.5.1: +json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" @@ -3970,6 +4274,7 @@ jsprim@^1.2.2: keccak@^1.0.2: version "1.4.0" resolved "https://registry.yarnpkg.com/keccak/-/keccak-1.4.0.tgz#572f8a6dbee8e7b3aa421550f9e6408ca2186f80" + integrity sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw== dependencies: bindings "^1.2.1" inherits "^2.0.3" @@ -3977,71 +4282,79 @@ keccak@^1.0.2: safe-buffer "^5.1.0" keccakjs@^0.2.0, keccakjs@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.1.tgz#1d633af907ef305bbf9f2fa616d56c44561dfa4d" + version "0.2.3" + resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.3.tgz#5e4e969ce39689a3861f445d7752ee3477f9fe72" + integrity sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg== dependencies: - browserify-sha3 "^0.0.1" - sha3 "^1.1.0" + browserify-sha3 "^0.0.4" + sha3 "^1.2.2" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= optionalDependencies: graceful-fs "^4.1.9" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= dependencies: invert-kv "^1.0.0" lcov-parse@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" + integrity sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM= level-codec@~7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== level-errors@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== dependencies: errno "~0.1.1" level-errors@~1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== dependencies: errno "~0.1.1" level-iterator-stream@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= dependencies: inherits "^2.0.1" level-errors "^1.0.3" @@ -4051,6 +4364,7 @@ level-iterator-stream@~1.3.0: level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= dependencies: readable-stream "~1.0.15" xtend "~2.1.1" @@ -4058,6 +4372,7 @@ level-ws@0.0.0: levelup@^1.2.1: version "1.3.9" resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== dependencies: deferred-leveldown "~1.2.1" level-codec "~7.0.0" @@ -4070,6 +4385,7 @@ levelup@^1.2.1: levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -4077,6 +4393,7 @@ levn@^0.3.0, levn@~0.3.0: load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -4087,27 +4404,17 @@ load-json-file@^1.0.0: load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" pify "^2.0.0" strip-bom "^3.0.0" -loader-runner@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" - -loader-utils@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" path-exists "^3.0.0" @@ -4115,44 +4422,44 @@ locate-path@^2.0.0: lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - -lodash@4.x, lodash@^4.13.1, lodash@^4.14.2, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5: +lodash@4.x, lodash@=4.17.11, lodash@^4.14.2, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= log-driver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" + integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== long-timeout@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514" + integrity sha1-lyHXiLR+C8taJMLivuGg2lXatRQ= -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lru-cache@^4.0.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" yallist "^2.1.2" @@ -4160,66 +4467,87 @@ lru-cache@^4.0.1: lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= dependencies: es5-ext "~0.10.2" ltgt@~2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" make-plural@^3.0.3, make-plural@~3.0.3: version "3.0.6" resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-3.0.6.tgz#2033a03bac290b8f3bb91258f65b9df7e8b01ca7" + integrity sha1-IDOgO6wpC487uRJY9lud9+iwHKc= optionalDependencies: minimist "^1.2.0" map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" math-interval-parser@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-interval-parser/-/math-interval-parser-1.1.0.tgz#dbeda5b06b3249973c6df6170fde2386f0afd893" + integrity sha1-2+2lsGsySZc8bfYXD94jhvCv2JM= dependencies: xregexp "^2.0.0" math-random@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== dependencies: hash-base "^3.0.0" inherits "^2.0.1" safe-buffer "^5.1.2" +md5@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= dependencies: mimic-fn "^1.0.0" memdown@^1.0.0: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= dependencies: abstract-leveldown "~2.7.1" functional-red-black-tree "^1.0.1" @@ -4231,6 +4559,7 @@ memdown@^1.0.0: memoizee@^0.4.14: version "0.4.14" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" + integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== dependencies: d "1" es5-ext "^0.10.45" @@ -4241,24 +4570,20 @@ memoizee@^0.4.14: next-tick "1" timers-ext "^0.1.5" -memory-fs@^0.4.0, memory-fs@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merkle-patricia-tree@^2.1.2: +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== dependencies: async "^1.4.2" ethereumjs-util "^5.0.0" @@ -4272,6 +4597,7 @@ merkle-patricia-tree@^2.1.2: messageformat@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-0.3.1.tgz#e58fff8245e9b3971799e5b43db58b3e9417f5a2" + integrity sha1-5Y//gkXps5cXmeW0PbWLPpQX9aI= dependencies: async "~1.5.2" glob "~6.0.4" @@ -4282,10 +4608,12 @@ messageformat@^0.3.1: methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= dependencies: arr-diff "^2.0.0" array-unique "^0.2.1" @@ -4301,9 +4629,10 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -4322,80 +4651,96 @@ micromatch@^3.1.10, micromatch@^3.1.4: miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== dependencies: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.36.0: - version "1.36.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" +mime-db@~1.38.0: + version "1.38.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" + integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.18, mime-types@~2.1.19: - version "2.1.20" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + version "2.1.22" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" + integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: - mime-db "~1.37.0" + mime-db "~1.38.0" mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= dependencies: dom-walk "^0.1.0" minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= minimist@^1.2.0, minimist@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -minipass@^2.2.1, minipass@^2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957" +minipass@^2.2.1, minipass@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" -minizlib@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.1.tgz#6734acc045a46e61d596a43bb9d9cd326e19cc42" +minizlib@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== dependencies: minipass "^2.2.1" mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" @@ -4403,18 +4748,32 @@ mixin-deep@^1.2.0: mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= dependencies: mkdirp "*" -mkdirp@*, mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@*, mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" +mocha-junit-reporter@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.18.0.tgz#9209a3fba30025ae3ae5e6bfe7f9c5bc3c2e8ee2" + integrity sha512-y3XuqKa2+HRYtg0wYyhW/XsLm2Ps+pqf9HaTAt7+MVUAKFJaNAHOrNseTZo9KCxjfIbxUWwckP5qCDDPUmjSWA== + dependencies: + debug "^2.2.0" + md5 "^2.1.0" + mkdirp "~0.5.1" + strip-ansi "^4.0.0" + xml "^1.0.0" + mocha@^4.0.1, mocha@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" + integrity sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA== dependencies: browser-stdout "1.3.0" commander "2.11.0" @@ -4430,6 +4789,7 @@ mocha@^4.0.1, mocha@^4.1.0: mocha@^5.0.1: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== dependencies: browser-stdout "1.3.1" commander "2.15.1" @@ -4444,53 +4804,69 @@ mocha@^5.0.1: supports-color "5.4.0" mock-fs@^4.1.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.7.0.tgz#9f17e219cacb8094f4010e0a8c38589e2b33c299" + version "4.8.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.8.0.tgz#eb0784ceba4b34c91a924a5112eeb36e1b5a9e29" + integrity sha512-Gwj4KnJOW15YeTJKO5frFd/WDO5Mc0zxXqL9oHx3+e9rBqW8EVARqQHSaIXznUdljrD6pvbNGW2ZGXKPEfYJfw== -moment-timezone@^0.5.0: - version "0.5.21" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.21.tgz#3cba247d84492174dbf71de2a9848fa13207b845" +moment-timezone@^0.5.23: + version "0.5.23" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" + integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== dependencies: moment ">= 2.9.0" "moment@>= 2.9.0", moment@^2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== mout@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" + integrity sha1-ujYR318OWx/7/QEWa48C0fX6K5k= ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== multihashes@^0.4.5: version "0.4.14" resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.14.tgz#774db9a161f81a8a27dc60788f91248e020f5244" + integrity sha512-V/g/EIN6nALXfS/xHUAgtfPP3mn3sPIF/i9beuGKf25QXS2QZYCpeVJbDPEannkz32B2fihzCe2D/KMrbcmefg== dependencies: bs58 "^4.0.1" varint "^5.0.0" mustache@*: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.0.0.tgz#3de22dd9ba38152f7355399a953dd4528c403338" + version "3.0.1" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.0.1.tgz#873855f23aa8a95b150fb96d9836edbc5a1d248a" + integrity sha512-jFI/4UVRsRYdUbuDTKT7KzfOp7FiD5WzYmmwNwXyUVypC0xjoTL78Fqc0jHUPIvvGD+6DQSPHIt1NE7D1ArsqA== mustache@^2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5" + integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ== mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +mute-stream@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== mz@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: any-promise "^1.0.0" object-assign "^4.0.1" @@ -4499,18 +4875,22 @@ mz@^2.6.0: nan@2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== -nan@^2.0.8, nan@^2.2.1, nan@^2.3.3, nan@^2.9.2: - version "2.11.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766" +nan@^2.0.8, nan@^2.11.0, nan@^2.2.1, nan@^2.3.3, nan@^2.9.2: + version "2.12.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" + integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -4527,10 +4907,17 @@ nanomatch@^1.2.9: natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +ncp@1.0.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" + integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= needle@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" + integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== dependencies: debug "^2.1.2" iconv-lite "^0.4.4" @@ -4539,14 +4926,12 @@ needle@^2.2.1: negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - -neo-async@^2.5.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= nice-try@^1.0.4: version "1.0.5" @@ -4556,52 +4941,33 @@ nice-try@^1.0.4: node-async-loop@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/node-async-loop/-/node-async-loop-1.2.2.tgz#c5870299bf6477b780c88b431aa5b37733f55a3d" + integrity sha1-xYcCmb9kd7eAyItDGqWzdzP1Wj0= node-cache@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-4.2.0.tgz#48ac796a874e762582692004a376d26dfa875811" + integrity sha512-obRu6/f7S024ysheAjoYFEEBqqDWv4LOMNJEuO8vMeEw2AT4z+NCzO4hlc2lhI4vATzbCQv6kke9FVdx0RbCOw== dependencies: clone "2.x" lodash "4.x" -node-fetch@^1.0.1, node-fetch@~1.7.1: +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@~1.7.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== dependencies: encoding "^0.1.11" is-stream "^1.0.1" -node-libs-browser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^1.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.0" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" - node-pre-gyp@^0.10.0: version "0.10.3" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + integrity sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A== dependencies: detect-libc "^1.0.2" mkdirp "^0.5.1" @@ -4615,48 +4981,55 @@ node-pre-gyp@^0.10.0: tar "^4" node-schedule@^1.2.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-1.3.0.tgz#e7a7e816a7f2550d5b170bd106e765db28bdf030" + version "1.3.2" + resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-1.3.2.tgz#d774b383e2a6f6ade59eecc62254aea07cd758cb" + integrity sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw== dependencies: - cron-parser "^2.4.0" + cron-parser "^2.7.3" long-timeout "0.1.1" sorted-array-functions "^1.0.0" nopt@3.x, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= dependencies: abbrev "1" nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= dependencies: abbrev "1" osenv "^0.1.4" normalize-package-data@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" + resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: +normalize-path@^2.0.0, normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" npm-bundled@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-packlist@^1.1.6: - version "1.1.12" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a" + version "1.3.0" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.3.0.tgz#7f01e8e44408341379ca98cfd756e7b29bd2626c" + integrity sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -4664,12 +5037,14 @@ npm-packlist@^1.1.6: npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -4679,10 +5054,12 @@ npmlog@^4.0.2: number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= number-to-bn@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= dependencies: bn.js "4.11.6" strip-hex-prefix "1.0.0" @@ -4690,48 +5067,48 @@ number-to-bn@1.7.0: oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" -object-extended@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/object-extended/-/object-extended-0.0.7.tgz#84fd23f56b15582aeb3e88b05cb55d2432d68a33" - dependencies: - array-extended "~0.0.4" - extended "~0.0.3" - is-extended "~0.0.3" - object-inspect@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== object-keys@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + version "1.1.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" + integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= dependencies: for-own "^0.1.4" is-extendable "^0.1.1" @@ -4739,40 +5116,47 @@ object.omit@^2.0.0: object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" oboe@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.3.tgz#2b4865dbd46be81225713f4e9bfe4bcf4f680a4f" + integrity sha1-K0hl29Rr6BIlcT9Om/5Lz09oCk8= dependencies: http-https "^1.0.0" on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" -openzeppelin-solidity@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-1.10.0.tgz#d77eee6653f5958d051318a61ba0b436f92216c0" +openzeppelin-solidity@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.1.2.tgz#94e2bb92b60e91abb22c6fe27d983d92850fb324" + integrity sha512-1ggh+AZFpMAgGfgnVMQ8dwYawjD2QN4xuWkQS4FUbeUz1fnCKJpguUl2cyadyfDYjBq1XJ6MA6VkzYpTZtJMqw== optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" @@ -4780,6 +5164,7 @@ optimist@^0.6.1: optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.4" @@ -4791,24 +5176,24 @@ optionator@^0.8.1, optionator@^0.8.2: original-require@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-DxMEcVhM0zURxew4yNWSE/msXiA= os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-locale@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= dependencies: lcid "^1.0.0" os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== dependencies: execa "^0.7.0" lcid "^1.0.0" @@ -4817,10 +5202,12 @@ os-locale@^2.0.0: os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -4828,50 +5215,67 @@ osenv@^0.1.4: p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-timeout@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= -pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" +parent-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.0.tgz#df250bdc5391f4a085fb589dad761f5ad6b865b5" + integrity sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA== + dependencies: + callsites "^3.0.0" parse-asn1@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-cache-control@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha1-juqz5U+laSD+Fro493+iGqzC104= parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= dependencies: glob-base "^0.3.0" is-dotfile "^1.0.0" @@ -4881,6 +5285,7 @@ parse-glob@^3.0.4: parse-headers@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" + integrity sha1-aug6eqJanZtwCswoaYzR8e1+lTY= dependencies: for-each "^0.3.2" trim "0.0.1" @@ -4888,58 +5293,61 @@ parse-headers@^2.0.0: parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5: +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -4948,12 +5356,14 @@ path-type@^1.0.0: path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= dependencies: pify "^2.0.0" pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -4964,104 +5374,152 @@ pbkdf2@^3.0.3: pegjs@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" + integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0= pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= dependencies: - find-up "^1.0.0" + find-up "^2.1.0" -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" +pkginfo@0.3.x: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" + integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= + +pkginfo@0.x.x: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" + integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= pragma-singleton@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pragma-singleton/-/pragma-singleton-1.0.3.tgz#6894317bb8d47157e59de2a4a009db7e6f63e30e" + integrity sha1-aJQxe7jUcVflneKkoAnbfm9j4w4= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@^1.14.3: - version "1.14.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895" +prettier@^1.15.3: + version "1.16.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" + integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== process@~0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-to-callback@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= dependencies: is-fn "^1.0.0" set-immediate-shim "^1.0.1" +promise@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.0.2.tgz#9dcd0672192c589477d56891271bdc27547ae9f0" + integrity sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw== + dependencies: + asap "~2.0.6" + +prompt@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prompt/-/prompt-1.0.0.tgz#8e57123c396ab988897fb327fd3aedc3e735e4fe" + integrity sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4= + dependencies: + colors "^1.1.2" + pkginfo "0.x.x" + read "1.0.x" + revalidator "0.1.x" + utile "0.3.x" + winston "2.1.x" + prop-types@^15.6.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== dependencies: - loose-envify "^1.3.1" + loose-envify "^1.4.0" object-assign "^4.1.1" + react-is "^16.8.1" proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== dependencies: forwarded "~0.1.2" ipaddr.js "1.8.0" @@ -5069,18 +5527,22 @@ proxy-addr@~2.0.4: prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24: - version "1.1.29" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" +psl@^1.1.24, psl@^1.1.28: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" @@ -5089,41 +5551,39 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@6.5.2, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +qs@^6.4.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" + integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA== query-string@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== dependencies: decode-uri-component "^0.2.0" object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - randomatic@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.0.tgz#36f2ca708e9e567f5ed2ec01949026d50aa10116" + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== dependencies: is-number "^4.0.0" kind-of "^6.0.0" @@ -5132,12 +5592,14 @@ randomatic@^3.0.0: randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" @@ -5145,14 +5607,17 @@ randomfill@^1.0.3: randomhex@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" + integrity sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU= range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= raw-body@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== dependencies: bytes "3.0.0" http-errors "1.6.3" @@ -5162,6 +5627,7 @@ raw-body@2.3.3: rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" ini "~1.3.0" @@ -5169,26 +5635,34 @@ rc@^1.2.7: strip-json-comments "~2.0.1" react-dom@^16.2.0: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" + version "16.8.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.2.tgz#7c8a69545dd554d45d66442230ba04a6a0a3c3d3" + integrity sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.10.0" + scheduler "^0.13.2" + +react-is@^16.8.1: + version "16.8.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.2.tgz#09891d324cad1cb0c1f2d91f70a71a4bee34df0f" + integrity sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q== react@^16.2.0: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42" + version "16.8.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.2.tgz#83064596feaa98d9c2857c4deae1848b542c9c0c" + integrity sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.10.0" + scheduler "^0.13.2" read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -5196,6 +5670,7 @@ read-pkg-up@^1.0.1: read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= dependencies: find-up "^2.0.0" read-pkg "^2.0.0" @@ -5203,6 +5678,7 @@ read-pkg-up@^2.0.0: read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" @@ -5211,23 +5687,33 @@ read-pkg@^1.0.0: read-pkg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= dependencies: load-json-file "^2.0.0" normalize-package-data "^2.3.2" path-type "^2.0.0" +read@1.0.x: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + readable-stream@^1.0.33: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5240,6 +5726,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable readable-stream@~1.0.15: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -5249,36 +5736,38 @@ readable-stream@~1.0.15: readdirp@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== dependencies: graceful-fs "^4.1.11" micromatch "^3.1.10" readable-stream "^2.0.2" -readline-sync@^1.4.9: - version "1.4.9" - resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.9.tgz#3eda8e65f23cd2a17e61301b1f0003396af5ecda" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== regenerator-runtime@^0.10.5: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== dependencies: babel-runtime "^6.18.0" babel-types "^6.19.0" @@ -5287,23 +5776,27 @@ regenerator-transform@^0.10.0: regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== dependencies: is-equal-shallow "^0.1.3" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= dependencies: regenerate "^1.2.1" regjsgen "^0.2.0" @@ -5312,61 +5805,95 @@ regexpu-core@^2.0.0: regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= dependencies: jsesc "~0.5.0" remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" req-cwd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-1.0.1.tgz#0d73aeae9266e697a78f7976019677e76acf0fff" + integrity sha1-DXOurpJm5penj3l2AZZ352rPD/8= dependencies: req-from "^1.0.1" +req-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" + integrity sha1-1AgrTURZgDZkD7c93qAe1T20nrw= + dependencies: + req-from "^2.0.0" + req-from@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/req-from/-/req-from-1.0.1.tgz#bf81da5147947d32d13b947dc12a58ad4587350e" + integrity sha1-v4HaUUeUfTLRO5R9wSpYrUWHNQ4= dependencies: resolve-from "^2.0.0" -request-promise-core@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" +req-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70" + integrity sha1-10GI5H+TeW9Kpx327jWuaJ8+DnA= + dependencies: + resolve-from "^3.0.0" + +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + dependencies: + lodash "^4.17.11" + +request-promise-native@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== dependencies: - lodash "^4.13.1" + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" request-promise@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.2.tgz#d1ea46d654a6ee4f8ee6a4fea1018c22911904b4" + version "4.2.4" + resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.4.tgz#1c5ed0d71441e38ad58c7ce4ea4ea5b06d54b310" + integrity sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg== dependencies: bluebird "^3.5.0" - request-promise-core "1.1.1" - stealthy-require "^1.1.0" - tough-cookie ">=2.3.3" + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" -request@^2.67.0, request@^2.79.0, request@^2.81.0, request@^2.85.0, request@^2.88.0: +request@^2.67.0, request@^2.79.0, request@^2.81.0, request@^2.83.0, request@^2.85.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -5392,53 +5919,59 @@ request@^2.67.0, request@^2.79.0, request@^2.81.0, request@^2.85.0, request@^2.8 require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.6, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - dependencies: - path-parse "^1.0.5" - -resolve@~1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0, resolve@~1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== dependencies: - path-parse "^1.0.5" + path-parse "^1.0.6" restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" signal-exit "^3.0.2" @@ -5446,103 +5979,121 @@ restore-cursor@^2.0.0: resumer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= dependencies: through "~2.3.4" ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" +revalidator@0.1.x: + version "0.1.8" + resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" + integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs= -rimraf@2, rimraf@^2.2.8, rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" +rimraf@2, rimraf@2.6.3, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: - glob "^7.0.5" + glob "^7.1.3" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== dependencies: hash-base "^3.0.0" inherits "^2.0.1" rlp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.1.0.tgz#e4f9886d5a982174f314543831e36e1a658460f9" + version "2.2.2" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.2.tgz#e6677b83cca9105371d930e01d8ffc1263139d05" + integrity sha512-Ng2kJEN731Sfv4ZAY2i0ytPMc0BbJKBsVNl0QZY8LxOWSwd+1xpg+fpSRfaMn0heHU447s6Kgy8qfHZR0XTyVw== dependencies: + bn.js "^4.11.1" safe-buffer "^5.1.1" run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= dependencies: is-promise "^2.1.0" +rustbn.js@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.1.2.tgz#979fa0f9562216dd667c9d2cd179ae5d13830eff" + integrity sha512-bAkNqSHYdJdFsBC7Z11JgzYktL31HIpB2o70jZcGiL1U1TVtPyvaVhDrGWwS8uZtaqwW2k6NOPGZCqW/Dgh5Lg== + rustbn.js@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" +rxjs@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504" + integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-event-emitter@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== dependencies: events "^3.0.0" safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" safe@^0.4.5: version "0.4.6" resolved "https://registry.yarnpkg.com/safe/-/safe-0.4.6.tgz#1d5580cf2635c5cb940ea48fb5081ae3c25b1be1" + integrity sha1-HVWAzyY1xcuUDqSPtQga48JbG+E= safefs@^3.1.2: version "3.2.2" resolved "https://registry.yarnpkg.com/safefs/-/safefs-3.2.2.tgz#8170c1444d7038e08caea05a374fae2fa349e15c" + integrity sha1-gXDBRE1wOOCMrqBaN0+uL6NJ4Vw= dependencies: graceful-fs "*" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== scandirectory@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/scandirectory/-/scandirectory-2.5.0.tgz#6ce03f54a090b668e3cbedbf20edf9e310593e72" + integrity sha1-bOA/VKCQtmjjy+2/IO354xBZPnI= dependencies: ignorefs "^1.0.0" safefs "^3.1.2" taskgroup "^4.0.5" -schedule@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8" +scheduler@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.2.tgz#969eaee2764a51d2e97b20a60963b2546beff8fa" + integrity sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -5550,14 +6101,12 @@ schedule@^0.5.0: scrypt-async@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/scrypt-async/-/scrypt-async-1.3.1.tgz#a11fd6fac981b4b823ee01dee0221169500ddae9" + integrity sha1-oR/W+smBtLgj7gHe4CIRaVAN2uk= -scrypt-js@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" - -scrypt.js@0.2.0, scrypt.js@^0.2.0: +scrypt.js@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada" + integrity sha1-r40UZbcemZARC+38WTuUeeA6ito= dependencies: scrypt "^6.0.2" scryptsy "^1.2.1" @@ -5565,18 +6114,21 @@ scrypt.js@0.2.0, scrypt.js@^0.2.0: scrypt@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" + integrity sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0= dependencies: nan "^2.0.8" scryptsy@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM= dependencies: pbkdf2 "^3.0.3" secp256k1@^3.0.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.5.2.tgz#f95f952057310722184fe9c914e6b71281f2f2ae" + version "3.6.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.6.2.tgz#da835061c833c74a12f75c73d2ec2e980f00dc1f" + integrity sha512-90nYt7yb0LmI4A2jJs1grglkTAXrBwxYAjP9bpeKjvJKOjG2fOeH/YI/lchDMIvjrOasd5QXwvV2jwN168xNng== dependencies: bindings "^1.2.1" bip66 "^1.1.3" @@ -5590,24 +6142,29 @@ secp256k1@^3.0.1: seek-bzip@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" + integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= dependencies: commander "~2.8.1" semaphore@>=1.0.1, semaphore@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== dependencies: debug "2.6.9" depd "~1.1.2" @@ -5626,6 +6183,7 @@ send@0.16.2: serve-static@1.13.2: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" @@ -5635,6 +6193,7 @@ serve-static@1.13.2: servify@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== dependencies: body-parser "^1.16.0" cors "^2.8.1" @@ -5645,14 +6204,17 @@ servify@^0.1.12: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= set-value@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -5662,58 +6224,71 @@ set-value@^0.4.3: set-value@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" - -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" -sha3@^1.1.0: +sha1@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg= + dependencies: + charenc ">= 0.0.1" + crypt ">= 0.0.1" + +sha3@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.2.tgz#a66c5098de4c25bc88336ec8b4817d005bca7ba9" + integrity sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k= dependencies: nan "2.10.0" shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shelljs@^0.7.4: +shelljs@^0.7.4, shelljs@^0.7.8: version "0.7.8" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= dependencies: glob "^7.0.0" interpret "^1.0.0" rechoir "^0.6.2" -shelljs@^0.8.1, shelljs@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.2.tgz#345b7df7763f4c2340d584abb532c5f752ca9e35" +shelljs@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" + integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -5722,14 +6297,17 @@ shelljs@^0.8.1, shelljs@^0.8.2: signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= simple-concat@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= simple-get@^2.7.0: version "2.8.1" resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== dependencies: decompress-response "^3.3.0" once "^1.3.1" @@ -5738,16 +6316,21 @@ simple-get@^2.7.0: slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" isobject "^3.0.0" @@ -5756,12 +6339,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" debug "^2.2.0" @@ -5775,39 +6360,46 @@ snapdragon@^0.8.1: sol-digger@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/sol-digger/-/sol-digger-0.0.2.tgz#406c4a9d31e269e7f88eb1c2ea101318e5e09025" + integrity sha1-QGxKnTHiaef4jrHC6hATGOXgkCU= sol-explore@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/sol-explore/-/sol-explore-1.6.1.tgz#b59f073c69fe332560d5a10c32ba8ca7f2986cfb" + integrity sha1-tZ8HPGn+MyVg1aEMMrqMp/KYbPs= sol-explore@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/sol-explore/-/sol-explore-1.6.2.tgz#43ae8c419fd3ac056a05f8a9d1fb1022cd41ecc2" + integrity sha1-Q66MQZ/TrAVqBfip0fsQIs1B7MI= sol-merger@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/sol-merger/-/sol-merger-0.1.2.tgz#1f12500f42d427dc0ec8e4c113392acd8a6f62d9" - dependencies: - bluebird "^3.5.0" - cli-color "^1.2.0" - commander "^2.11.0" - debug "^3.0.1" - fs-extra "^4.0.2" + version "0.1.3" + resolved "https://registry.yarnpkg.com/sol-merger/-/sol-merger-0.1.3.tgz#184284ba4811aebe8950f510df4e8218f568b35f" + integrity sha512-mEirUbl1mZJt2iNBqptsBpxb8n7ZD0trNlnV/+CBAQH8TIFhHIKXdBE8ykD1v+8My18sq7GqHYPmpHE9ckB2Jw== + dependencies: + bluebird "^3.5.3" + cli-color "^1.4.0" + commander "^2.19.0" + debug "^3.2.6" + fs-extra "^7.0.1" glob "^7.1.2" -solc@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" +solc@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.5.0.tgz#2deb2ae992acac3afb909f85c38d00f01dcb335e" + integrity sha512-mdLHDl9WeYrN+FIKcMc9PlPfnA9DG9ur5QpCDKcv6VC4RINAsTF4EMuXMZMKoQTvZhtLyJIVH/BZ+KU830Z8Xg== dependencies: fs-extra "^0.30.0" + keccak "^1.0.2" memorystream "^0.3.1" - require-from-string "^1.1.0" - semver "^5.3.0" - yargs "^4.7.1" + require-from-string "^2.0.0" + semver "^5.5.0" + yargs "^11.0.0" -solc@^0.4.19, solc@^0.4.2, solc@^0.4.24: +solc@^0.4.2: version "0.4.25" resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.25.tgz#06b8321f7112d95b4b903639b1138a4d292f5faa" + integrity sha512-jU1YygRVy6zatgXrLY2rRm7HW1d7a8CkkEgNJwvH2VLpWhMFsMdWcJn6kUqZwcSz/Vm+w89dy7Z/aB5p6AFTrg== dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -5818,6 +6410,7 @@ solc@^0.4.19, solc@^0.4.2, solc@^0.4.24: solidity-coverage@^0.5.11: version "0.5.11" resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.5.11.tgz#1ee45f6d98b75a615aadb8f9aa7db4a2b32258e7" + integrity sha512-qikdsSi6+9XbfvwA0aI7HUVpF9fIFNqRWTw23M89GMDY+b6Gj0wWU9IngJS0fimoZIAdEp3bfChxvpfVcrUesg== dependencies: death "^1.1.0" ethereumjs-testrpc-sc "6.1.6" @@ -5833,6 +6426,7 @@ solidity-coverage@^0.5.11: solidity-docgen@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/solidity-docgen/-/solidity-docgen-0.1.0.tgz#f3a56ff074e8c7d832af3a3d462c3b5abf0f64cb" + integrity sha512-F7ufNWmlP5c5hIi66Ijv9tc+HNosyO7ijWq6pRtyBR1WqyJBH/0DJkD6QZI8HkE8p6LEXiPKxGBWbAeVT9Nu9g== dependencies: commander "^2.14.1" lodash "^4.17.5" @@ -5842,9 +6436,15 @@ solidity-docgen@^0.1.0: react-dom "^16.2.0" shelljs "^0.8.1" +solidity-parser-antlr@^0.2.10: + version "0.2.15" + resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.2.15.tgz#4be687a0a53da268c6a07398e0cfb3168896d610" + integrity sha512-EzRI8/TR/ljTXkZAyAjb0w4G20wH2XM7pcNf87ifdV825AbUv7DkY7HmiZCTj6NeKtIx8Y1s0NZWPbj+JTp8Zw== + solidity-parser-sc@0.4.11: version "0.4.11" resolved "https://registry.yarnpkg.com/solidity-parser-sc/-/solidity-parser-sc-0.4.11.tgz#86734c9205537007f4d6201b57176e41696ee607" + integrity sha512-1kV5iC7m3CtMDfmHaVNwz2saSGQVIuF16rIxU417Al38MVCWHMQQ5vT6cmLsNwDe60S74auobWij9vNawSeOyw== dependencies: mocha "^4.1.0" pegjs "^0.10.0" @@ -5853,27 +6453,31 @@ solidity-parser-sc@0.4.11: solium-plugin-security@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/solium-plugin-security/-/solium-plugin-security-0.1.1.tgz#2a87bcf8f8c3abf7d198e292e4ac080284e3f3f6" + integrity sha512-kpLirBwIq4mhxk0Y/nn5cQ6qdJTI+U1LO3gpoNIcqNaW+sI058moXBe2UiHs+9wvF9IzYD49jcKhFTxcR9u9SQ== solium@^1.1.6: - version "1.1.8" - resolved "https://registry.yarnpkg.com/solium/-/solium-1.1.8.tgz#35d30a15c572a233ce8a90226d6cfccb762fadb7" + version "1.2.3" + resolved "https://registry.yarnpkg.com/solium/-/solium-1.2.3.tgz#88167ea6c8f7d1b68b59d3a6a78d563ca455ecdd" + integrity sha512-xMM/RcsUyZ3OTZWXwNXQGKdQdM2IqwqdRWJMUYFp6YBkd0Zzm4KTxyy0/KSazTaLk+OlmUE19OlSDiAN/GhhAQ== dependencies: ajv "^5.2.2" chokidar "^1.6.0" colors "^1.1.2" commander "^2.9.0" + diff "^3.5.0" eol "^0.9.1" js-string-escape "^1.0.1" lodash "^4.14.2" sol-digger "0.0.2" sol-explore "1.6.1" solium-plugin-security "0.1.1" - solparse "2.2.5" + solparse "2.2.8" text-table "^0.2.0" -solparse@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/solparse/-/solparse-2.2.5.tgz#72709c867cd6bfc50ec2325f4b81d2b3ea365d99" +solparse@2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/solparse/-/solparse-2.2.8.tgz#d13e42dbed95ce32f43894f5ec53f00d14cf9f11" + integrity sha512-Tm6hdfG72DOxD40SD+T5ddbekWglNWjzDRSNq7ZDIOHVsyaJSeeunUuWNj4DE7uDrJK3tGQuX0ZTDZWNYsGPMA== dependencies: mocha "^4.0.1" pegjs "^0.10.0" @@ -5882,14 +6486,12 @@ solparse@2.2.5: sorted-array-functions@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz#43265b21d6e985b7df31621b1c11cc68d8efc7c3" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg== source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== dependencies: atob "^2.1.1" decode-uri-component "^0.2.0" @@ -5900,12 +6502,14 @@ source-map-resolve@^0.5.0: source-map-support@^0.4.15: version "0.4.18" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== dependencies: source-map "^0.5.6" source-map-support@^0.5.3: - version "0.5.9" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" + version "0.5.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" + integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -5913,24 +6517,29 @@ source-map-support@^0.5.3: source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: +source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= dependencies: amdefine ">=0.0.4" spdx-correct@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -5938,35 +6547,42 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== spdx-expression-parse@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f" + version "3.0.3" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" + integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" sprintf-js@>=1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.1.tgz#b79a089a732e346c6e0714830f36285cd38191a2" + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -5981,10 +6597,12 @@ sshpk@^1.7.0: stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -5992,52 +6610,32 @@ static-extend@^0.1.1: "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= statuses@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== stdio@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/stdio/-/stdio-0.2.7.tgz#a1c57da10fe1cfaa0c3bf683c9d0743d1b660839" + integrity sha1-ocV9oQ/hz6oMO/aDydB0PRtmCDk= -stealthy-require@^1.1.0: +stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - -stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - -string-extended@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/string-extended/-/string-extended-0.0.8.tgz#741957dff487b0272a79eec5a44f239ee6f17ccd" - dependencies: - array-extended "~0.0.5" - date-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -6046,107 +6644,135 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.0.0.tgz#5a1690a57cc78211fffd9bf24bbe24d090604eb1" + integrity sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.0.0" + string.prototype.trim@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" + integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= dependencies: define-properties "^1.1.2" es-abstract "^1.5.0" function-bind "^1.0.2" -string_decoder@^1.0.0, string_decoder@~1.1.1: +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" + integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow== + dependencies: + ansi-regex "^4.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== dependencies: is-natural-number "^4.0.1" strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-hex-prefix@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= dependencies: is-hex-prefixed "1.0.0" strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= supports-color@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + integrity sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ== dependencies: has-flag "^2.0.0" supports-color@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== dependencies: has-flag "^3.0.0" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^3.1.0: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= dependencies: has-flag "^1.0.0" -supports-color@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" swarm-js@0.1.37: version "0.1.37" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.37.tgz#27d485317a340bbeec40292af783cc10acfa4663" + integrity sha512-G8gi5fcXP/2upwiuOShJ258sIufBVztekgobr3cVgYXObZwJ5AXLqZn52AI+/ffft29pJexF9WNdUxjlkVehoQ== dependencies: bluebird "^3.5.0" buffer "^5.0.5" @@ -6162,33 +6788,47 @@ swarm-js@0.1.37: tar.gz "^1.0.5" xhr-request-promise "^0.1.2" -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" +sync-request@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.0.0.tgz#db867eccc4ed31bbcb9fa3732393a3413da582ed" + integrity sha512-jGNIAlCi9iU4X3Dm4oQnNQshDD3h0/1A7r79LyqjbjUnj69sX6mShAXlhRXgImsfVKtTcnra1jfzabdZvp+Lmw== dependencies: - ajv "^6.5.3" - lodash "^4.17.10" - slice-ansi "1.0.0" - string-width "^2.1.1" + http-response-object "^3.0.1" + sync-rpc "^1.2.1" + then-request "^6.0.0" -tapable@^0.2.7: - version "0.2.8" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" +sync-rpc@^1.2.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.4.tgz#24bcbdb2ffcb98f23690c15b304660085cdd206c" + integrity sha512-Iug+t1ICVFenUcTnDu8WXFnT+k8IVoLKGh8VA3eXUtl2Rt9SjKX3YEv33OenABqpTPL9QEaHv1+CNn2LK8vMow== + dependencies: + get-port "^3.1.0" -tape@^4.4.0, tape@^4.6.3: - version "4.9.1" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.9.1.tgz#1173d7337e040c76fbf42ec86fcabedc9b3805c9" +table@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2" + integrity sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ== + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tape@^4.4.0, tape@^4.6.3, tape@^4.8.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.10.1.tgz#f73be60888dcb120f08b57f947af65a829506a5f" + integrity sha512-G0DywYV1jQeY3axeYnXUOt6ktnxS9OPJh97FGR3nrua8lhWi1zPflLxcAHavZ7Jf3qUfY7cxcVIVFa4mY2IY1w== dependencies: deep-equal "~1.0.1" defined "~1.0.0" for-each "~0.3.3" function-bind "~1.1.1" - glob "~7.1.2" + glob "~7.1.3" has "~1.0.3" inherits "~2.0.3" minimist "~1.2.0" object-inspect "~1.6.0" - resolve "~1.7.1" + resolve "~1.10.0" resumer "~0.0.0" string.prototype.trim "~1.1.2" through "~2.3.8" @@ -6196,6 +6836,7 @@ tape@^4.4.0, tape@^4.6.3: tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== dependencies: bl "^1.0.0" buffer-alloc "^1.2.0" @@ -6208,6 +6849,7 @@ tar-stream@^1.5.2: tar.gz@^1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/tar.gz/-/tar.gz-1.0.7.tgz#577ef2c595faaa73452ef0415fed41113212257b" + integrity sha512-uhGatJvds/3diZrETqMj4RxBR779LKlIE74SsMcn5JProZsfs9j0QBwWO1RW+IWNJxS2x8Zzra1+AW6OQHWphg== dependencies: bluebird "^2.9.34" commander "^2.8.1" @@ -6218,19 +6860,21 @@ tar.gz@^1.0.5: tar@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= dependencies: block-stream "*" fstream "^1.0.2" inherits "2" tar@^4: - version "4.4.6" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== dependencies: - chownr "^1.0.1" + chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.3" - minizlib "^1.1.0" + minipass "^2.3.4" + minizlib "^1.1.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" yallist "^3.0.2" @@ -6238,6 +6882,7 @@ tar@^4: taskgroup@^4.0.5, taskgroup@^4.2.0: version "4.3.1" resolved "https://registry.yarnpkg.com/taskgroup/-/taskgroup-4.3.1.tgz#7de193febd768273c457730497024d512c27915a" + integrity sha1-feGT/r12gnPEV3MElwJNUSwnkVo= dependencies: ambi "^2.2.0" csextends "^1.0.3" @@ -6245,36 +6890,61 @@ taskgroup@^4.0.5, taskgroup@^4.2.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +then-request@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.0.tgz#2cab198e48f2d8e79c8c1ed260198368a4a0bcba" + integrity sha512-xA+7uEMc+jsQIoyySJ93Ad08Kuqnik7u6jLS5hR91Z3smAoCfL3M8/MqMlobAa9gzBfO9pA88A/AntfepkkMJQ== + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/form-data" "0.0.33" + "@types/node" "^8.0.0" + "@types/qs" "^6.2.31" + caseless "~0.12.0" + concat-stream "^1.6.0" + form-data "^2.2.0" + http-basic "^7.0.0" + http-response-object "^3.0.1" + promise "^8.0.0" + qs "^6.4.0" thenify-all@^1.0.0, thenify-all@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= dependencies: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": version "3.3.0" resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= dependencies: any-promise "^1.0.0" -through@^2.3.6, through@~2.3.4, through@~2.3.8: +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - -timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - dependencies: - setimmediate "^1.0.4" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= timers-ext@^0.1.5: version "0.1.7" resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== dependencies: es5-ext "~0.10.46" next-tick "1" @@ -6282,6 +6952,7 @@ timers-ext@^0.1.5: tingodb@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/tingodb/-/tingodb-0.6.1.tgz#f63336259af7dfa6c90dfe2556a0dfb0d4eede59" + integrity sha1-9jM2JZr336bJDf4lVqDfsNTu3lk= dependencies: lodash "^4.17.5" safe "^0.4.5" @@ -6292,30 +6963,31 @@ tingodb@^0.6.1: tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -6323,114 +6995,101 @@ to-regex-range@^2.1.0: to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" extend-shallow "^3.0.2" regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@>=2.3.3, tough-cookie@~2.4.3: +tough-cookie@^2.3.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: psl "^1.1.24" punycode "^1.4.1" tree-kill@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" + version "1.2.1" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a" + integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q== + +treeify@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= trim@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= -truffle-blockchain-utils@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/truffle-blockchain-utils/-/truffle-blockchain-utils-0.0.5.tgz#a4e5c064dadd69f782a137f3d276d21095da7a47" - -truffle-contract-schema@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/truffle-contract-schema/-/truffle-contract-schema-2.0.1.tgz#9bf821d32e26e674ba15eb5d40f96b10b1c9d568" - dependencies: - ajv "^5.1.1" - crypto-js "^3.1.9-1" - debug "^3.1.0" - -truffle-contract@^3.0.4: - version "3.0.6" - resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-3.0.6.tgz#2ef6fc32d7faafa9f4aed8e50001a9fdea342192" - dependencies: - ethjs-abi "0.1.8" - truffle-blockchain-utils "^0.0.5" - truffle-contract-schema "^2.0.1" - truffle-error "^0.0.3" - web3 "0.20.6" - -truffle-error@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/truffle-error/-/truffle-error-0.0.3.tgz#4bf55242e14deee1c7194932709182deff2c97ca" - -truffle-hdwallet-provider-privkey@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/truffle-hdwallet-provider-privkey/-/truffle-hdwallet-provider-privkey-0.1.0.tgz#9417047a74ad37d923df926154b6486ffb57f6c9" - dependencies: - ethereumjs-tx "^1.3.4" - ethereumjs-wallet "^0.6.0" - web3 "^0.20.6" - web3-provider-engine "^13.8.0" - -truffle-wallet-provider@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/truffle-wallet-provider/-/truffle-wallet-provider-0.0.5.tgz#db59ce6fa1c558766011137509a94dfca8d1408e" +truffle-hdwallet-provider@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/truffle-hdwallet-provider/-/truffle-hdwallet-provider-1.0.4.tgz#f00ea8dbf8c243dad551f3f68813e09d159c8174" + integrity sha512-Z9LzA+SMH2kH52WJEcIoIbo+OY6aO4/uVCxqEIo0J+pJ4COuGLlLkM74pA6JAEf8Dr9o7sDtxMvuA7qbgjPhvQ== dependencies: - ethereumjs-wallet "^0.6.0" - web3 "^0.18.2" - web3-provider-engine "^8.4.0" + any-promise "^1.3.0" + bindings "^1.3.1" + websocket "^1.0.28" -truffle@4.1.14: - version "4.1.14" - resolved "https://registry.yarnpkg.com/truffle/-/truffle-4.1.14.tgz#8d2c298e29abf9b1e486e44ff9faca6d34bb9030" +truffle@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.4.tgz#fc68cb6a6a35b46a7ca69eca7b64d161b491db3d" + integrity sha512-pZYFbU10Hb6PiTalJm+dB6s1qIZjE5qc0ux5fIgQ7Nj24zDrlYmOYruP3yhuqywwzr3PUHGPxr6hXuje0BFYoA== dependencies: + app-module-path "^2.2.0" mocha "^4.1.0" original-require "1.0.1" - solc "0.4.24" + solc "0.5.0" tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.13.2.tgz#453161770469d45cd266c36404e2bc99a8fa9944" + integrity sha1-RTFhdwRp1FzSZsNkBOK8maj6mUQ= tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-is@~1.6.16: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== dependencies: media-typer "0.3.0" mime-types "~2.1.18" @@ -6438,77 +7097,67 @@ type-is@~1.6.16: typechecker@^2.0.8: version "2.1.0" resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-2.1.0.tgz#d1c2093a54ff8a19f58cff877eeaa54f2242d383" + integrity sha1-0cIJOlT/ihn1jP+HfuqlTyJC04M= typechecker@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.6.0.tgz#d245d9c2df21147d5e2a942fff170b68ece73c87" + version "4.7.0" + resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.7.0.tgz#5249f427358f45b7250c4924fd4d01ed9ba435e9" + integrity sha512-4LHc1KMNJ6NDGO+dSM/yNfZQRtp8NN7psYrPHUblD62Dvkwsp3VShsbM78kOgpcmMkRTgvwdKOTjctS+uMllgQ== dependencies: - editions "^2.0.2" + editions "^2.1.0" typechecker@~2.0.1: version "2.0.8" resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-2.0.8.tgz#e83da84bb64c584ccb345838576c40b0337db82e" + integrity sha1-6D2oS7ZMWEzLNFg4V2xAsDN9uC4= -typedarray-to-buffer@^3.1.2: +typedarray-to-buffer@^3.1.2, typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -uglify-js@^2.8.29: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" + integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== dependencies: commander "~2.17.1" source-map "~0.6.1" -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -uglifyjs-webpack-plugin@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" - dependencies: - source-map "^0.5.6" - uglify-js "^2.8.29" - webpack-sources "^1.0.1" - ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== unbzip2-stream@^1.0.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz#7854da51622a7e63624221196357803b552966a1" + version "1.3.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" + integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== dependencies: - buffer "^3.0.1" - through "^2.3.6" + buffer "^5.2.1" + through "^2.3.8" underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= underscore@^1.8.3: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= dependencies: arr-union "^3.1.0" get-value "^2.0.6" @@ -6518,108 +7167,106 @@ union-value@^1.0.0: universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -unorm@^1.3.3: +unorm@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.4.1.tgz#364200d5f13646ca8bcd44490271335614792300" + integrity sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA= unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.0.5: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= dependencies: prepend-http "^1.0.1" url-set-query@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - dependencies: - punycode "1.3.2" - querystring "0.2.0" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== utf8@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.1.tgz#2e01db02f7d8d0944f77104f1609eb0c304cf768" + integrity sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g= utf8@^2.1.1, utf8@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" - -utf8@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY= util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - dependencies: - inherits "2.0.1" - -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" +utile@0.3.x: + version "0.3.0" + resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a" + integrity sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo= dependencies: - inherits "2.0.3" + async "~0.9.0" + deep-equal "~0.2.1" + i "0.3.x" + mkdirp "0.x.x" + ncp "1.0.x" + rimraf "2.x.x" utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= uuid@^3.0.1, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - -valid-url@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" @@ -6627,36 +7274,26 @@ validate-npm-package-license@^3.0.1: varint@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.0.tgz#d826b89f7490732fabc0c0ed693ed475dcb29ebf" + integrity sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8= vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" extsprintf "^1.2.0" -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - dependencies: - indexof "0.0.1" - -watchpack@^1.4.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - dependencies: - chokidar "^2.0.2" - graceful-fs "^4.1.2" - neo-async "^2.5.0" - watchr@~2.4.13: version "2.4.13" resolved "https://registry.yarnpkg.com/watchr/-/watchr-2.4.13.tgz#d74847bb4d6f90f61fe2c74f9f68662aa0e07601" + integrity sha1-10hHu01vkPYf4sdPn2hmKqDgdgE= dependencies: eachr "^2.0.2" extendr "^2.1.0" @@ -6667,78 +7304,87 @@ watchr@~2.4.13: taskgroup "^4.2.0" typechecker "^2.0.8" -web3-bzz@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.34.tgz#068d37777ab65e5c60f8ec8b9a50cfe45277929c" +web3-bzz@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.35.tgz#9d5e1362b3db2afd77d65619b7cd46dd5845c192" + integrity sha512-BhAU0qhlr8zltm4gs/+P1gki2VkxHJaM2Rrh4DGesDW0lzwufRoNvWFlwx1bKHoFPWNbSmm9PRkHOYOINL/Tgw== dependencies: got "7.1.0" swarm-js "0.1.37" underscore "1.8.3" -web3-core-helpers@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" +web3-core-helpers@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.35.tgz#d681d218a0c6e3283ee1f99a078ab9d3eef037f1" + integrity sha512-APOu3sEsamyqWt//8o4yq9KF25/uqGm+pQShson/sC4gKzmfJB07fLo2ond0X30E8fIqAPeVCotPXQxGciGUmA== dependencies: underscore "1.8.3" - web3-eth-iban "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-eth-iban "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-core-method@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.34.tgz#ec163c8a2c490fa02a7ec15559fa7307fc7cc6dd" +web3-core-method@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.35.tgz#fc10e2d546cf4886038e6130bd5726b0952a4e5f" + integrity sha512-jidImCide8q0GpfsO4L73qoHrbkeWgwU3uOH5DKtJtv0ccmG086knNMRgryb/o9ZgetDWLmDEsJnHjBSoIwcbA== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-core-promievent "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-core-promievent@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.34.tgz#a4f4fa6784bb293e82c60960ae5b56a94cd03edc" +web3-core-promievent@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.35.tgz#4f1b24737520fa423fee3afee110fbe82bcb8691" + integrity sha512-GvqXqKq07OmHuVi5uNRg6k79a1/CI0ViCC+EtNv4CORHtDRmYEt5Bvdv6z6FJEiaaQkD0lKbFwNhLxutx7HItw== dependencies: any-promise "1.3.0" eventemitter3 "1.1.1" -web3-core-requestmanager@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.34.tgz#01f8f6cf2ae6b6f0b70c38bae1ef741b5bab215c" +web3-core-requestmanager@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.35.tgz#2b77cbf6303720ad68899b39fa7f584dc03dbc8f" + integrity sha512-S+zW2h17ZZQU9oe3yaCJE0E7aJS4C3Kf4kGPDv+nXjW0gKhQQhgVhw1Doq/aYQGqNSWJp7f1VHkz5gQWwg6RRg== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-providers-http "1.0.0-beta.34" - web3-providers-ipc "1.0.0-beta.34" - web3-providers-ws "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-providers-http "1.0.0-beta.35" + web3-providers-ipc "1.0.0-beta.35" + web3-providers-ws "1.0.0-beta.35" -web3-core-subscriptions@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.34.tgz#9fed144033f221c3cf21060302ffdaf5ef2de2de" +web3-core-subscriptions@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.35.tgz#c1b76a2ad3c6e80f5d40b8ba560f01e0f4628758" + integrity sha512-gXzLrWvcGkGiWq1y33Z4Y80XI8XMrwowiQJkrPSjQ81K5PBKquOGwcMffLaKcwdmEy/NpsOXDeFo3eLE1Ghvvw== dependencies: eventemitter3 "1.1.1" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" -web3-core@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.34.tgz#121be8555e9fb00d2c5d05ddd3381d0c9e46987e" +web3-core@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.35.tgz#0c44d3c50d23219b0b1531d145607a9bc7cd4b4f" + integrity sha512-ayGavbgVk4KL9Y88Uv411fBJ0SVgVfKhKEBweKYzmP0zOqneMzWt6YsyD1n6kRvjAbqA0AfUPEOKyMNjcx2tjw== dependencies: - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-requestmanager "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-requestmanager "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-abi@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" +web3-eth-abi@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.35.tgz#2eb9c1c7c7233db04010defcb192293e0db250e6" + integrity sha512-KUDC+EtFFYG8z01ZleKrASdjj327/rtWHzEt6RWsEj7bBa0bGp9nEh+nqdZx/Sdgz1O8tnfFzJlrRcXpfr1vGg== dependencies: bn.js "4.11.6" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-accounts@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz#e09142eeecc797ac3459b75e9b23946d3695f333" +web3-eth-accounts@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.35.tgz#7d0e5a69f510dc93874471599eb7abfa9ddf3e63" + integrity sha512-duIgRsfht/0kAW/eQ0X9lKtVIykbETrnM2H7EnvplCzPHtQLodpib4o9JXfh9n6ZDgdDC7cuJoiVB9QJg089ew== dependencies: any-promise "1.3.0" crypto-browserify "3.12.0" @@ -6746,69 +7392,75 @@ web3-eth-accounts@1.0.0-beta.34: scrypt.js "0.2.0" underscore "1.8.3" uuid "2.0.1" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth-contract@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz#9dbb38fae7643a808427a20180470ec7415c91e6" +web3-eth-contract@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.35.tgz#5276242d8a3358d9f1ce92b71575c74f9015935c" + integrity sha512-foPohOg5O1UCGKGZOIs+kQK5IZdV2QQ7pAWwNxH8WHplUA+fre1MurXNpoxknUmH6mYplFhXjqgYq2MsrBpHrA== dependencies: underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-eth-iban@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-promievent "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-eth-abi "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-eth-iban@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.35.tgz#5aa10327a9abb26bcfc4ba79d7bad18a002b332c" + integrity sha512-H5wkcNcAIc+h/WoDIKv7ZYmrM2Xqu3O7jBQl1IWo73EDVQji+AoB2i3J8tuwI1yZRInRwrfpI3Zuwuf54hXHmQ== dependencies: bn.js "4.11.6" - web3-utils "1.0.0-beta.34" + web3-utils "1.0.0-beta.35" -web3-eth-personal@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.34.tgz#9afba167342ebde5420bcd5895c3f6c34388f205" +web3-eth-personal@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.35.tgz#ecac95b7a53d04a567447062d5cae5f49879e89f" + integrity sha512-AcM9nnlxu7ZRRxPvkrFB9eLxMM4A2cPfj2aCg21Wb2EpMnhR+b/O1cT33k7ApRowoMpM+T9M8vx2oPNwXfaCOQ== dependencies: - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3-eth@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.34.tgz#74086000850c6fe6f535ef49837d6d4bb6113268" +web3-eth@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.35.tgz#c52c804afb95e6624b6f5e72a9af90fbf5005b68" + integrity sha512-04mcb2nGPXThawuuYICPOxv0xOHofvQKsjZeIq+89nyOC8DQMGTAErDkGyMHQYtjpth5XDhic0wuEsA80AmFZA== dependencies: underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-eth-accounts "1.0.0-beta.34" - web3-eth-contract "1.0.0-beta.34" - web3-eth-iban "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-net@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.34.tgz#427cea2f431881449c8e38d523290f173f9ff63d" - dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3-provider-engine@^13.8.0: + web3-core "1.0.0-beta.35" + web3-core-helpers "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-eth-abi "1.0.0-beta.35" + web3-eth-accounts "1.0.0-beta.35" + web3-eth-contract "1.0.0-beta.35" + web3-eth-iban "1.0.0-beta.35" + web3-eth-personal "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-net@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.35.tgz#5c6688e0dea71fcd910ee9dc5437b94b7f6b3354" + integrity sha512-bbwaQ/KohGjIJ6HAKbZ6KrklCAaG6/B7hIbAbVLSFLxF+Yz9lmAgQYaDInpidpC/NLb3WOmcbRF+P77J4qMVIA== + dependencies: + web3-core "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" + +web3-provider-engine@^13.3.2: version "13.8.0" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz#4c7c1ad2af5f1fe10343b8a65495879a2f9c00df" + integrity sha512-fZXhX5VWwWpoFfrfocslyg6P7cN3YWPG/ASaevNfeO80R+nzgoPUBXcWQekSGSsNDkeRTis4aMmpmofYf1TNtQ== dependencies: async "^2.5.0" clone "^2.0.0" @@ -6830,60 +7482,72 @@ web3-provider-engine@^13.8.0: xhr "^2.2.0" xtend "^4.0.1" -web3-provider-engine@^8.4.0: - version "8.6.1" - resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-8.6.1.tgz#4d86e19e30caaf97df351511ec0f60136e5b30eb" +web3-provider-engine@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.1.0.tgz#91590020f8b8c1b65846321310cbfdb039090fc6" + integrity sha512-vGZtqhSUzGTiMGhJXNnB/aRDlrPZLhLnBZ2NPArkZtr8XSrwg9m08tw4+PuWg5za0TJuoE/vuPQc501HddZZWw== dependencies: - async "^2.1.2" + async "^2.5.0" + backoff "^2.5.0" clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" ethereumjs-block "^1.2.2" ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.0.1" - ethereumjs-vm "^2.0.2" - isomorphic-fetch "^2.2.0" - request "^2.67.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" semaphore "^1.0.3" - solc "^0.4.2" - tape "^4.4.0" - web3 "^0.16.0" + ws "^5.1.1" xhr "^2.2.0" xtend "^4.0.1" -web3-providers-http@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz#e561b52bbb43766282007d40285bfe3550c27e7a" +web3-providers-http@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.35.tgz#92059d9d6de6e9f82f4fae30b743efd841afc1e1" + integrity sha512-DcIMFq52Fb08UpWyZ3ZlES6NsNqJnco4hBS/Ej6eOcASfuUayPI+GLkYVZsnF3cBYqlH+DOKuArcKSuIxK7jIA== dependencies: - web3-core-helpers "1.0.0-beta.34" - xhr2 "0.1.4" + web3-core-helpers "1.0.0-beta.35" + xhr2-cookies "1.1.0" -web3-providers-ipc@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.34.tgz#a1b77f1a306d73649a9c039052e40cb71328d00a" +web3-providers-ipc@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.35.tgz#031afeb10fade2ebb0ef2fb82f5e58c04be842d9" + integrity sha512-iB0FG0HcpUnayfa8pn4guqEQ4Y1nrroi/jffdtQgFkrNt0sD3fMSwwC0AbmECqj3tDLl0e1slBR0RENll+ZF0g== dependencies: oboe "2.1.3" underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" -web3-providers-ws@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.34.tgz#7de70f1b83f2de36476772156becfef6e3516eb3" +web3-providers-ws@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.35.tgz#5d38603fd450243a26aae0ff7f680644e77fa240" + integrity sha512-Cx64NgDStynKaUGDIIOfaCd0fZusL8h5avKTkdTjUu2aHhFJhZoVBGVLhoDtUaqZGWIZGcBJOoVf2JkGUOjDRQ== dependencies: underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.35" websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" -web3-shh@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.34.tgz#975061d71eaec42ccee576f7bd8f70f03844afe0" +web3-shh@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.35.tgz#7e4a585f8beee0c1927390937c6537748a5d1a58" + integrity sha512-8qSonk/x0xabERS9Sr6AIADN/Ty+5KwARkkGIfSYHKqFpdMDz+76F7cUCxtoCZoS8K04xgZlDKYe0TJXLYA0Fw== dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-net "1.0.0-beta.34" + web3-core "1.0.0-beta.35" + web3-core-method "1.0.0-beta.35" + web3-core-subscriptions "1.0.0-beta.35" + web3-net "1.0.0-beta.35" -web3-utils@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" +web3-utils@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.35.tgz#ced9e1df47c65581c441c5f2af76b05a37a273d7" + integrity sha512-Dq6f0SOKj3BDFRgOPnE6ALbzBDCKVIW8mKWVf7tGVhTDHf+wQaWwQSC3aArFSqdExB75BPBPyDpuMTNszhljpA== dependencies: bn.js "4.11.6" eth-lib "0.1.27" @@ -6896,6 +7560,7 @@ web3-utils@1.0.0-beta.34: web3@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/web3/-/web3-0.19.1.tgz#e763d5b1107c4bc24abd4f8cbee1ba3659e6eb31" + integrity sha1-52PVsRB8S8JKvU+MvuG6Nlnm6zE= dependencies: bignumber.js "^4.0.2" crypto-js "^3.1.4" @@ -6906,6 +7571,7 @@ web3@0.19.1: web3@0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.2.tgz#c54dac5fc0e377399c04c1a6ecbb12e4513278d6" + integrity sha1-xU2sX8DjdzmcBMGm7LsS5FEyeNY= dependencies: bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" crypto-js "^3.1.4" @@ -6913,40 +7579,23 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" -web3@0.20.6: - version "0.20.6" - resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" - dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2 "*" - xmlhttprequest "*" - -web3@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" - dependencies: - web3-bzz "1.0.0-beta.34" - web3-core "1.0.0-beta.34" - web3-eth "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-shh "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - -web3@^0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/web3/-/web3-0.16.0.tgz#a4554175cd462943035b1f1d39432f741c6b6019" +web3@1.0.0-beta.35: + version "1.0.0-beta.35" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.35.tgz#6475095bd451a96e50a32b997ddee82279292f11" + integrity sha512-xwDmUhvTcHQvvNnOPcPZZgCxKUsI2e+GbHy7JkTK3/Rmnutazy8x7fsAXT9myw7V1qpi3GgLoZ3fkglSUbg1Mg== dependencies: - bignumber.js "git+https://github.com/debris/bignumber.js#master" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xmlhttprequest "*" + web3-bzz "1.0.0-beta.35" + web3-core "1.0.0-beta.35" + web3-eth "1.0.0-beta.35" + web3-eth-personal "1.0.0-beta.35" + web3-net "1.0.0-beta.35" + web3-shh "1.0.0-beta.35" + web3-utils "1.0.0-beta.35" -web3@^0.18.2, web3@^0.18.4: +web3@^0.18.4: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" + integrity sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0= dependencies: bignumber.js "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" crypto-js "^3.1.4" @@ -6954,49 +7603,15 @@ web3@^0.18.2, web3@^0.18.4: xhr2 "*" xmlhttprequest "*" -web3@^0.20.6: - version "0.20.7" - resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.7.tgz#1605e6d81399ed6f85a471a4f3da0c8be57df2f7" +websocket@^1.0.28: + version "1.0.28" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3" + integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA== dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2-cookies "^1.1.0" - xmlhttprequest "*" - -webpack-sources@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^3.0.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" - dependencies: - acorn "^5.0.0" - acorn-dynamic-import "^2.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - async "^2.1.2" - enhanced-resolve "^3.4.0" - escope "^3.6.0" - interpret "^1.0.0" - json-loader "^0.5.4" - json5 "^0.5.1" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - mkdirp "~0.5.0" - node-libs-browser "^2.0.0" - source-map "^0.5.3" - supports-color "^4.2.1" - tapable "^0.2.7" - uglifyjs-webpack-plugin "^0.4.6" - watchpack "^1.4.0" - webpack-sources "^1.0.1" - yargs "^8.0.2" + debug "^2.2.0" + nan "^2.11.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" "websocket@git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible": version "1.0.26" @@ -7007,41 +7622,57 @@ webpack@^3.0.0: typedarray-to-buffer "^3.1.2" yaeti "^0.0.6" -whatwg-fetch@>=0.10.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@^1.1.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +winston@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.1.1.tgz#3c9349d196207fd1bdff9d4bc43ef72510e3a12e" + integrity sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4= + dependencies: + async "~1.0.0" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + pkginfo "0.3.x" + stack-trace "0.0.x" winston@^2.3.1: version "2.4.4" resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.4.tgz#a01e4d1d0a103cf4eada6fc1f886b3110d71c34b" + integrity sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q== dependencies: async "~1.0.0" colors "1.0.x" @@ -7050,21 +7681,20 @@ winston@^2.3.1: isstream "0.1.x" stack-trace "0.0.x" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -7072,30 +7702,42 @@ wrap-ansi@^2.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== dependencies: mkdirp "^0.5.1" ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" ultron "~1.1.0" +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + xhr-request-promise@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz#343c44d1ee7726b8648069682d0f840c83b4261d" + integrity sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0= dependencies: xhr-request "^1.0.1" xhr-request@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== dependencies: buffer-to-arraybuffer "^0.0.5" object-assign "^4.1.1" @@ -7105,87 +7747,101 @@ xhr-request@^1.0.1: url-set-query "^1.0.0" xhr "^2.0.4" -xhr2-cookies@^1.1.0: +xhr2-cookies@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= dependencies: cookiejar "^2.1.1" -xhr2@*, xhr2@0.1.4: +xhr2@*: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" + integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8= xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: version "2.5.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" + integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== dependencies: global "~4.3.0" is-function "^1.0.1" parse-headers "^2.0.0" xtend "^4.0.0" -xmlhttprequest@*, xmlhttprequest@1.8.0: +xml@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + +xmlhttprequest@*: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= xregexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= xtend@~2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= dependencies: object-keys "~0.4.0" y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^3.0.0, yallist@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== yargs-parser@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= dependencies: camelcase "^3.0.0" lodash.assign "^4.0.6" -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - dependencies: - camelcase "^4.1.0" - yargs-parser@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" + integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ== dependencies: camelcase "^4.1.0" yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= dependencies: camelcase "^4.1.0" yargs@^10.0.3: version "10.1.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" + integrity sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig== dependencies: cliui "^4.0.0" decamelize "^1.1.1" @@ -7203,6 +7859,7 @@ yargs@^10.0.3: yargs@^11.0.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" + integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== dependencies: cliui "^4.0.0" decamelize "^1.1.1" @@ -7220,6 +7877,7 @@ yargs@^11.0.0: yargs@^4.6.0, yargs@^4.7.1: version "4.8.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= dependencies: cliui "^3.2.0" decamelize "^1.1.1" @@ -7236,36 +7894,10 @@ yargs@^4.6.0, yargs@^4.7.1: y18n "^3.2.1" yargs-parser "^2.4.1" -yargs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0"