diff --git a/.solcover.js b/.solcover.js index 47f4c173b..32cb416b2 100644 --- a/.solcover.js +++ b/.solcover.js @@ -2,7 +2,7 @@ module.exports = { norpc: true, port: 8545, copyPackages: ['openzeppelin-solidity'], - testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.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` --network coverage', deepSkip: true, skipFiles: ['external', 'flat'] }; diff --git a/CHANGELOG.md b/CHANGELOG.md index 045db881c..ca2181f6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. [__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__ ## Added +* Added `name` field to dividends struct in DividendCheckpoint. #295 +* Added `getTagsByType`, `getTagsByTypeAndToken`, `getModulesByType`, `getModulesByTypeAndToken` to MR * Added `getTokensByOwner` to STR * Added withholding tax to ether & erc20 dividends * Generalised MakerDAO oracle to allow different instances referencing different currencies @@ -24,10 +26,11 @@ All notable changes to this project will be documented in this file. ## Fixed * 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 - -## Removed + +## Removed * Remove `swarmHash` from the `registerTicker(), addCustomTicker(), generateSecurityToken(), addCustomSecurityToken()` functions of TickerRegistry.sol and SecurityTokenRegistry.sol. #230 -* Remove `Log` prefix from all the event present in the ecosystem. +* Remove `Log` prefix from all the event present in the ecosystem. +* Removed `addTagByModuleType` & `removeTagsByModuleType` from MR. ====== diff --git a/CLI/commands/ST20Generator.js b/CLI/commands/ST20Generator.js index 1f15dc4c2..1b7fd034b 100644 --- a/CLI/commands/ST20Generator.js +++ b/CLI/commands/ST20Generator.js @@ -9,8 +9,6 @@ var common = require('./common/common_functions'); var global = require('./common/global'); let securityTokenRegistryAddress; -let cappedSTOFactoryAddress; -let usdTieredSTOFactoryAddress; /////////////////// // Crowdsale params @@ -18,9 +16,13 @@ let tokenName; let tokenSymbol; let selectedSTO; -const STO_KEY = 3; -const REG_FEE_KEY = 'tickerRegFee'; -const LAUNCH_FEE_KEY = 'stLaunchFee'; +const MODULES_TYPES = { + PERMISSION: 1, + TRANSFER: 2, + STO: 3, + DIVIDENDS: 4 +} + const cappedSTOFee = 20000; const usdTieredSTOFee = 100000; const tokenDetails = ""; @@ -37,8 +39,6 @@ let usdToken; let securityToken; let generalTransferManager; let currentSTO; -let cappedSTOFactory; -let usdTieredSTOFactory; // App flow let _tokenConfig; @@ -90,16 +90,6 @@ async function setup(){ let usdTokenAddress = await contracts.usdToken(); usdToken = new web3.eth.Contract(polytokenABI, usdTokenAddress); usdToken.setProvider(web3.currentProvider); - - cappedSTOFactoryAddress = await contracts.cappedSTOFactoryAddress(); - let cappedSTOFactoryABI = abis.cappedSTOFactory(); - cappedSTOFactory = new web3.eth.Contract(cappedSTOFactoryABI, cappedSTOFactoryAddress); - cappedSTOFactory.setProvider(web3.currentProvider); - - usdTieredSTOFactoryAddress = await contracts.usdTieredSTOFactoryAddress(); - let usdTieredSTOFactoryABI = abis.usdTieredSTOFactory(); - usdTieredSTOFactory = new web3.eth.Contract(usdTieredSTOFactoryABI, usdTieredSTOFactoryAddress); - usdTieredSTOFactory.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."); @@ -111,7 +101,8 @@ async function step_ticker_reg(){ console.log('\n\x1b[34m%s\x1b[0m',"Token Creation - Symbol Registration"); let available = false; - let regFee = web3.utils.fromWei(await securityTokenRegistry.methods.getUintValues(web3.utils.soliditySha3(REG_FEE_KEY)).call()); + let regFee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); + let isDeployed; 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`)); @@ -123,6 +114,7 @@ async function step_ticker_reg(){ } let details = await securityTokenRegistry.methods.getTickerDetails(tokenSymbol).call(); + isDeployed = details[4]; if (new BigNumber(details[1]).toNumber() == 0) { available = true; await approvePoly(securityTokenRegistryAddress, regFee); @@ -135,18 +127,20 @@ async function step_ticker_reg(){ } } - 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, 'LogChangeTickerOwnership'); - console.log(chalk.green(`Ownership trasferred successfully. The new owner is ${event._newOwner}`)); - process.exit(0); + 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); + } } } @@ -160,7 +154,7 @@ async function step_token_deploy(){ } else { console.log('\n\x1b[34m%s\x1b[0m',"Token Creation - Token Deployment"); - let launchFee = web3.utils.fromWei(await securityTokenRegistry.methods.getUintValues(web3.utils.soliditySha3(LAUNCH_FEE_KEY)).call()); + 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')) { @@ -187,7 +181,7 @@ async function step_token_deploy(){ 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, 'LogNewSecurityToken'); + 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); @@ -195,7 +189,7 @@ async function step_token_deploy(){ } async function step_Wallet_Issuance(){ - let result = await securityToken.methods.getModulesByType(STO_KEY).call(); + 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 { @@ -275,7 +269,7 @@ 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(STO_KEY).call(); + 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(); @@ -442,9 +436,10 @@ async function cappedSTO_launch() { ] }, [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, 'LogModuleAdded'); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); console.log(`STO deployed at address: ${event._module}`); STO_Address = event._module; @@ -826,9 +821,10 @@ async function usdTieredSTO_launch() { 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, 'LogModuleAdded'); + let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'ModuleAdded'); console.log(`STO deployed at address: ${event._module}`); STO_Address = event._module; diff --git a/CLI/commands/TickerRollForward.js b/CLI/commands/TickerRollForward.js index 732eb1b97..97bd1f519 100644 --- a/CLI/commands/TickerRollForward.js +++ b/CLI/commands/TickerRollForward.js @@ -13,8 +13,6 @@ var abis = require('./helpers/contract_abis'); let remoteNetwork = process.argv.slice(2)[0]; //batch size ///////////////////////// GLOBAL VARS ///////////////////////// -const REG_FEE_KEY = 'tickerRegFee'; - let ticker_data = []; let registered_tickers = []; let failed_tickers = []; @@ -80,7 +78,7 @@ 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.getUintValues(web3.utils.soliditySha3(REG_FEE_KEY)).call()); + let fee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); let totalFee = BigNumber(ticker_data.length).mul(fee); if (totalFee.gt(polyBalance)) { diff --git a/CLI/commands/common/common_functions.js b/CLI/commands/common/common_functions.js index 07de69fec..dbf3aa8dd 100644 --- a/CLI/commands/common/common_functions.js +++ b/CLI/commands/common/common_functions.js @@ -50,7 +50,12 @@ module.exports = { sendTransaction: async function (from, action, gasPrice, value, factor) { if (typeof factor === 'undefined') factor = 1.2; + let block = await web3.eth.getBlock("latest"); + let networkGasLimit = block.gasLimit; + 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} ----`)); let nonce = await web3.eth.getTransactionCount(from.address); diff --git a/CLI/commands/contract_manager.js b/CLI/commands/contract_manager.js index 9f8cf6812..594522f6f 100644 --- a/CLI/commands/contract_manager.js +++ b/CLI/commands/contract_manager.js @@ -13,11 +13,6 @@ var global = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); -const OWNER_KEY = 'owner'; -const REG_FEE_KEY = 'tickerRegFee'; -const LAUNCH_FEE_KEY = 'stLaunchFee'; -const EXPIRY_LIMIT_KEY = 'expiryLimit'; - // App flow let currentContract = null; @@ -79,7 +74,7 @@ async function selectContract() { async function strActions() { console.log('\n\x1b[34m%s\x1b[0m',"Security Token Registry - Main menu"); - let contractOwner = await currentContract.methods.getAddressValues(web3.utils.soliditySha3(OWNER_KEY)).call(); + let contractOwner = await currentContract.methods.owner().call(); if (contractOwner != Issuer.address) { console.log(chalk.red(`You are not the owner of this contract. Current owner is ${contractOwner}`)); @@ -117,7 +112,7 @@ async function strActions() { console.log(chalk.green(`Ticker has been updated successfuly`)); break; case 'Remove Ticker': - let tickerToRemove = readlineSync.question('Enter the token symbol that you want to add or modify: '); + let tickerToRemove = readlineSync.question('Enter the token symbol that you want to remove: '); let tickerToRemoveDetails = await currentContract.methods.getTickerDetails(tickerToRemove).call(); if (tickerToRemoveDetails[1] == 0) { console.log(chalk.yellow(`${ticker} does not exist.`)); @@ -168,30 +163,30 @@ async function strActions() { console.log(chalk.green(`Security Token has been updated successfuly`)); break; case 'Change Expiry Limit': - let currentExpiryLimit = await currentContract.methods.getUintValues(web3.utils.soliditySha3(EXPIRY_LIMIT_KEY)).call(); + 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 changeExpiryLimitAction = currentContract.methods.changeExpiryLimit(newExpiryLimit); let changeExpiryLimitReceipt = await common.sendTransaction(Issuer, changeExpiryLimitAction, defaultGasPrice); - let changeExpiryLimitEvent = common.getEventFromLogs(currentContract._jsonInterface, changeExpiryLimitReceipt.logs, 'LogChangeExpiryLimit'); + let changeExpiryLimitEvent = common.getEventFromLogs(currentContract._jsonInterface, changeExpiryLimitReceipt.logs, 'ChangeExpiryLimit'); console.log(chalk.green(`Expiry limit was changed successfuly. New limit is ${Math.floor(parseInt(changeExpiryLimitEvent._newExpiry)/60/60/24)} days\n`)); break; case 'Change registration fee': - let currentRegFee = web3.utils.fromWei(await currentContract.methods.getUintValues(web3.utils.soliditySha3(REG_FEE_KEY)).call()); + let currentRegFee = web3.utils.fromWei(await currentContract.methods.getTickerRegistrationFee().call()); 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 changeRegFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeRegFeeReceipt.logs, 'LogChangeTickerRegistrationFee'); + let changeRegFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeRegFeeReceipt.logs, 'ChangeTickerRegistrationFee'); console.log(chalk.green(`Fee was changed successfuly. New fee is ${web3.utils.fromWei(changeRegFeeEvent._newFee)} POLY\n`)); break; case 'Change ST launch fee': - let currentLaunchFee = web3.utils.fromWei(await currentContract.methods.getUintValues(web3.utils.soliditySha3(LAUNCH_FEE_KEY)).call()); + let currentLaunchFee = web3.utils.fromWei(await currentContract.methods.getSecurityTokenLaunchFee().call()); 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 changeLaunchFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeLaunchFeeReceipt.logs, 'LogChangeSecurityLaunchFee'); + let changeLaunchFeeEvent = common.getEventFromLogs(currentContract._jsonInterface, changeLaunchFeeReceipt.logs, 'ChangeSecurityLaunchFee'); console.log(chalk.green(`Fee was changed successfuly. New fee is ${web3.utils.fromWei(changeLaunchFeeEvent._newFee)} POLY\n`)); break; case 'CANCEL': diff --git a/CLI/commands/dividends_manager.js b/CLI/commands/dividends_manager.js index ec4c61baf..a757b78ef 100644 --- a/CLI/commands/dividends_manager.js +++ b/CLI/commands/dividends_manager.js @@ -14,7 +14,12 @@ var global = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); -const STO_KEY = 3; +const MODULES_TYPES = { + PERMISSION: 1, + TRANSFER: 2, + STO: 3, + DIVIDENDS: 4 +} // App flow let tokenSymbol; @@ -95,7 +100,7 @@ async function start_explorer(){ let currentCheckpoint = await securityToken.methods.currentCheckpointId().call(); console.log(chalk.yellow(`\nToken is at checkpoint: ${currentCheckpoint}`)); - let options = ['Mint tokens', 'Transfer tokens', 'Create checkpoint', 'Create dividends'] + let options = ['Mint tokens', 'Transfer tokens', 'Create checkpoint', 'Set default exclusions for dividends', 'Tax holding settings', 'Create dividends'] if (currentCheckpoint > 0) { options.push('Explore account at checkpoint', 'Explore total supply at checkpoint') @@ -108,73 +113,74 @@ async function start_explorer(){ } let index = readlineSync.keyInSelect(options, 'What do you want to do?'); - console.log('Selected:', index != -1 ? options[index] : 'Cancel', '\n'); - switch (index) { - case 0: - // Mint tokens + 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 1: - // Transfer tokens + 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 2: - // Create checkpoint + case 'Create checkpoint': let createCheckpointAction = securityToken.methods.createCheckpoint(); await common.sendTransaction(Issuer, createCheckpointAction, defaultGasPrice); break; - case 3: - // Create Dividends - let dividend = readlineSync.question(`How much ${dividendsType} would you like to distribute to token holders?: `); + 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(dividend, checkpointId); + await createDividends(divName, dividend, checkpointId); break; - case 4: - // Explore account at checkpoint + case 'Explore account at checkpoint': let _address = readlineSync.question('Enter address to explore: '); let _checkpoint = await selectCheckpoint(false); await exploreAddress(_address, _checkpoint); break; - case 5: - // Explore total supply at checkpoint + case 'Explore total supply at checkpoint': let _checkpoint2 = await selectCheckpoint(false); await exploreTotalSupply(_checkpoint2); break; - break; - case 6: - // Push dividends to account - let _dividend = await selectDividend({valid: true, expired: false, reclaimed: false}); + 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 7: - //explore balance + case `Explore ${dividendsType} balance`: let _address3 = readlineSync.question('Enter address to explore: '); let _dividend3 = await selectDividend(); if (_dividend3 !== null) { - let divsBalance = await currentDividendsModule.methods.calculateDividend(_dividend3.index, _address3).call(); + 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(divsBalance)} ${dividendsType} + Dividends owned: ${web3.utils.fromWei(dividendBalance)} ${dividendsType} + Tax withheld: ${web3.utils.fromWei(dividendTax)} ${dividendsType} `); } break; - case 8: - // Reclaimed dividends after expiry + case 'Reclaim expired dividends': let _dividend4 = await selectDividend({expired: true, reclaimed: false}); if (_dividend4 !== null) { await reclaimedDividend(_dividend4); } break; - case -1: + case 'Cancel': process.exit(0); break; } @@ -188,7 +194,7 @@ 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 { - let result = await securityToken.methods.getModulesByType(STO_KEY).call(); + let result = await securityToken.methods.getModulesByType(MODULES_TYPES.STO).call(); if (result.length > 0) { console.log(chalk.red("Minting is not possible - STO is attached to Security Token")); } else { @@ -248,33 +254,116 @@ async function exploreTotalSupply(checkpoint){ console.log(`TotalSupply is: ${totalSupplyAt} (Using totalSupplyAt - checkpoint ${checkpoint})`); } -async function createDividends(dividend, checkpointId) { +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.`)); + 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 event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'SetDefaultExcludedAddresses'); + console.log(chalk.green(`Exclusions were successfuly set.`)); + showExcluded(event._excluded); + } +} + +async function taxHoldingMenu() { + await addDividendsModule(); + + 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}.`)); + 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.`)); + } + break; + case 'Return to main menu': + break; + } +} + +async function createDividends(name, dividend, checkpointId) { await addDividendsModule(); 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.`); 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) { - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend), checkpointId); + 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 { - createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, polyToken._address, web3.utils.toWei(dividend)); + 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)); + } } let receipt = await common.sendTransaction(Issuer, createDividendAction, defaultGasPrice); let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'ERC20DividendDeposited'); - console.log(` - Dividend ${event._dividendIndex} deposited` - ); + console.log(chalk.green(`Dividend ${event._dividendIndex} deposited`)); } else if (dividendsType == 'ETH') { if (checkpointId > 0) { - createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, checkpointId); + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, checkpointId, web3.utils.toHex(name)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, checkpointId, excluded, web3.utils.toHex(name)); + } } else { - createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime); + if (useDefaultExcluded) { + createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, web3.utils.toHex(name)); + } else { + let excluded = getExcludedFromDataFile(); + createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, excluded, web3.utils.toHex(name)); + } } let receipt = await common.sendTransaction(Issuer, createDividendAction, defaultGasPrice, web3.utils.toWei(dividend)); let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'EtherDividendDeposited'); @@ -307,7 +396,8 @@ async function pushDividends(dividend, account){ for (const event of successEvents) { console.log(` Claimed ${web3.utils.fromWei(event._amount)} ${dividendsType} - to account ${event._payee}` + to account ${event._payee} + ${web3.utils.fromWei(event._withheld)} ${dividendsType} of tax withheld` ); } } @@ -382,19 +472,20 @@ async function isDividendsModuleAttached() { async function addDividendsModule() { if (!(await isDividendsModuleAttached())) { - let dividendsFactoryAddress; + let dividendsFactoryName; let dividendsModuleABI; if (dividendsType == 'POLY') { - dividendsFactoryAddress = await contracts.erc20DividendCheckpointFactoryAddress(); + dividendsFactoryName = 'ERC20DividendCheckpoint'; dividendsModuleABI = abis.erc20DividendCheckpoint(); } else if (dividendsType == 'ETH') { - dividendsFactoryAddress = await contracts.etherDividendCheckpointFactoryAddress(); + 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, 'LogModuleAdded'); + 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); @@ -416,18 +507,12 @@ async function selectCheckpoint(includeCreate) { async function getCheckpoints() { let result = []; - /* - let currentCheckpoint = await securityToken.methods.currentCheckpointId().call(); - for (let index = 1; index <= currentCheckpoint; index++) { - result.push(checkpoint(index).call()); - } - */ - - let events = await securityToken.getPastEvents('LogCheckpointCreated', { fromBlock: 0}); - for (let event of events) { + + let checkPointsTimestamps = await securityToken.methods.getCheckpointTimes().call(); + for (let index = 0; index < checkPointsTimestamps.length; index++) { let checkpoint = {}; - checkpoint.id = event.returnValues._checkpointId; - checkpoint.timestamp = moment.unix(event.returnValues._timestamp).format('MMMM Do YYYY, HH:mm:ss'); + checkpoint.id = index + 1; + checkpoint.timestamp = moment.unix(checkPointsTimestamps[index]).format('MMMM Do YYYY, HH:mm:ss'); result.push(checkpoint); } @@ -449,16 +534,25 @@ async function selectDividend(filter) { 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); + } } if (dividends.length > 0) { let options = dividends.map(function(d) { - return `Created: ${moment.unix(d.created).format('MMMM Do YYYY, HH:mm:ss')} + return `${web3.utils.toAscii(d.name)} + 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} - At checkpoint: ${d.checkpointId}` + Withheld: ${web3.utils.fromWei(d.dividendWithheld)} ${dividendsType} + Withheld claimed: ${web3.utils.fromWei(d.dividendWithheldReclaimed)} ${dividendsType}` }); let index = readlineSync.keyInSelect(options, 'Select a dividend:'); @@ -467,7 +561,8 @@ async function selectDividend(filter) { } } 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}\n`)) + console.log(chalk.red(`Requirements: Valid: ${filter.valid} - Expired: ${filter.expired} - Reclaimed: ${filter.reclaimed} + WithRemainingWithheld: ${filter.withRemainingWithheld} - WithRemaining: ${filter.withRemaining}\n`)) } return result; @@ -489,6 +584,24 @@ async function getDividends() { return result; } +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; +} + +function showExcluded(excluded) { + if (excluded.length > 0) { + console.log('Current default excluded addresses:') + excluded.map(function (address) { console.log(' ', address) }); + } else { + console.log('There are not default excluded addresses.') + } + console.log(); +} + module.exports = { executeApp: async function(type, remoteNetwork) { return executeApp(type, remoteNetwork); diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index 116dbfd34..a505579d2 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -1,6 +1,7 @@ let polymathRegistryABI; let securityTokenRegistryABI; let featureRegistryABI; +let moduleRegistryABI; let securityTokenABI; let stoInterfaceABI; let cappedSTOABI; @@ -11,12 +12,14 @@ let cappedSTOFactoryABI; let usdTieredSTOFactoryABI; let erc20DividendCheckpointABI; let etherDividendCheckpointABI; -let ownable; +let ownableABI; +let moduleFactoryABI; 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; @@ -27,7 +30,8 @@ try { 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; - ownable = JSON.parse(require('fs').readFileSync('./build/contracts/Ownable.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; } catch (err) { console.log('\x1b[31m%s\x1b[0m',"Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); return; @@ -43,6 +47,9 @@ module.exports = { featureRegistry: function () { return featureRegistryABI; }, + moduleRegistry: function () { + return moduleRegistryABI; + }, securityToken: function () { return securityTokenABI; }, @@ -74,6 +81,9 @@ module.exports = { return etherDividendCheckpointABI; }, ownable: function () { - return ownable; + return ownableABI; + }, + moduleFactory: function () { + return moduleFactoryABI; } } \ No newline at end of file diff --git a/CLI/commands/helpers/contract_addresses.js b/CLI/commands/helpers/contract_addresses.js index b4963e272..b6dca4eeb 100644 --- a/CLI/commands/helpers/contract_addresses.js +++ b/CLI/commands/helpers/contract_addresses.js @@ -39,7 +39,7 @@ async function getModuleRegistry() { if (typeof _moduleRegistry === 'undefined') { let polymathRegistry = await getPolymathRegistry(); let moduleRegistryAddress = await polymathRegistry.methods.getAddress("ModuleRegistry").call(); - let moduleRegistryAbi = abis.moduleRegistryAbi(); + let moduleRegistryAbi = abis.moduleRegistry(); _moduleRegistry = new web3.eth.Contract(moduleRegistryAbi, moduleRegistryAddress); _moduleRegistry.setProvider(web3.currentProvider); } @@ -77,45 +77,26 @@ module.exports = { else return JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).networks[networkId].address; }, - cappedSTOFactoryAddress: async function() { - let networkId = await web3.eth.net.getId(); - if (networkId == 1) - return "0x2aa1b133f464ac08f66c2f702581d014e4603d31"; - else if (networkId == 42) - return "0x4527f1629b1d32ad8b900edebb766967c9c78715"; // Updated to 1.4.0 - else - return JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).networks[networkId].address; - }, - daiToken: async function() { - //TODO: Add a proper test DAI token here - let polymathRegistry = await getPolymathRegistry(); - return await polymathRegistry.methods.getAddress("PolyToken").call(); - }, - usdTieredSTOFactoryAddress: async function() { - let networkId = await web3.eth.net.getId(); - if (networkId == 1) - throw new Error("Not implemented"); - else if (networkId == 42) - return "0xcee7b602b6fc093c76f1bfcb05af6df7a9d39725"; // Updated to poly_oracle deployment - else - return JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTOFactory.json').toString()).networks[networkId].address; - }, - etherDividendCheckpointFactoryAddress: async function() { - let networkId = await web3.eth.net.getId(); - if (networkId == 1) - return "0x0da7ed8789348ac40937cf6ae8ff521eee43816c"; - else if (networkId == 42) - return "0x870a07d45b0f4c5653fc29a4cb0697a01e0224b1"; - else - return JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpointFactory.json').toString()).networks[networkId].address; - }, - erc20DividendCheckpointFactoryAddress: async function() { - let networkId = await web3.eth.net.getId(); - if (networkId == 1) - return "0x6950096964b7adae34d5a3d1792fe73afbe9ddbc"; - else if (networkId == 42) - return "0x7e823f5df6ed1bb6cc005c692febc6aedf3b8889"; - else - return JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpointFactory.json').toString()).networks[networkId].address; + 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(); + while (result == null && counter < availableModules.length) { + let moduleFactory = new web3.eth.Contract(moduleFactoryABI, availableModules[counter]); + let currentName = web3.utils.toAscii(await moduleFactory.methods.name().call()); + if (currentName.localeCompare(moduleName) == 0) { + result = moduleFactory.options.address; + } + counter++; + } + + if (result == null) { + throw new Error(`Module factory named ${moduleName} was not found.`); + } + + return result; } }; diff --git a/CLI/commands/strMigrator.js b/CLI/commands/strMigrator.js index d0538dbad..a2ba4bdf0 100644 --- a/CLI/commands/strMigrator.js +++ b/CLI/commands/strMigrator.js @@ -80,7 +80,7 @@ function step_instance_toSTR(toStrAddress){ async function step_get_deployed_tokens(securityTokenRegistry) { let tokens = []; - let events = await securityTokenRegistry.getPastEvents('LogNewSecurityToken', { fromBlock: 0}); + let events = await securityTokenRegistry.getPastEvents('NewSecurityToken', { fromBlock: 0}); if (events.length == 0) { console.log("No security token events were emitted."); } else { diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 9d9b46f29..0769f88aa 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -85,7 +85,7 @@ async function start_explorer() { }); let setControllerAction = securityToken.methods.setController(controllerAddress); let setControllerReceipt = await common.sendTransaction(Issuer, setControllerAction, defaultGasPrice); - let setControllerEvent = common.getEventFromLogs(securityToken._jsonInterface, setControllerReceipt.logs, 'LogSetController'); + let setControllerEvent = common.getEventFromLogs(securityToken._jsonInterface, setControllerReceipt.logs, 'SetController'); console.log(chalk.green(`New controller is ${setControllerEvent._newController}`)); break; case 'Force Transfer': @@ -114,7 +114,7 @@ async function start_explorer() { let data = 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)); let forceTransferReceipt = await common.sendTransaction(Issuer, forceTransferAction, defaultGasPrice, 0, 1.5); - let forceTransferEvent = common.getEventFromLogs(securityToken._jsonInterface, forceTransferReceipt.logs, 'LogForceTransfer'); + 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._amount)} ${tokenSymbol} from ${forceTransferEvent._from} to ${forceTransferEvent._to} Verified transfer: ${forceTransferEvent._verifyTransfer} diff --git a/CLI/commands/whitelist.js b/CLI/commands/whitelist.js index 26f4af32a..903ef40ec 100644 --- a/CLI/commands/whitelist.js +++ b/CLI/commands/whitelist.js @@ -185,7 +185,7 @@ async function setInvestors() { let investorData_Events = new Array(); let investorObjectLookup = {}; - let event_data = await generalTransferManager.getPastEvents('LogModifyWhitelist', { + let event_data = await generalTransferManager.getPastEvents('ModifyWhitelist', { fromBlock: 0, toBlock: 'latest' }, function (error, events) { diff --git a/CLI/data/dividendsExclusions_data.csv b/CLI/data/dividendsExclusions_data.csv new file mode 100644 index 000000000..b418985eb --- /dev/null +++ b/CLI/data/dividendsExclusions_data.csv @@ -0,0 +1,6 @@ +0xa26cc0567e29fda3c77369791e926b4c46456fcb +0x97c09f763a699a51cb947f95e602c08f7bcba202 +0x308a3ea530e5b160f1f7115f2ddb30cb44d8b54d +0xbc1bfb5d90692854672febe76ca5379dea1015e4 +0x7442dcd2eb074ea6dbfaa8338300bc86238c2aae +0x0a519b4b6501f92e8f516230b97aca83257b0c01 \ No newline at end of file diff --git a/contracts/ModuleRegistry.sol b/contracts/ModuleRegistry.sol index 94878303d..f0249e2fd 100644 --- a/contracts/ModuleRegistry.sol +++ b/contracts/ModuleRegistry.sol @@ -33,8 +33,6 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { // contains the list of verified modules mapping (address => bool) public verified; - // Contains the list of the available tags corresponding to each module type - mapping (uint8 => bytes32[]) public availableTags; */ /////////// @@ -62,15 +60,27 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { - require(msg.sender == getAddress(Encoder.getKey("owner"))); + require(msg.sender == owner()); _; } /** * @notice Modifier to make a function callable only when the contract is not paused. */ + modifier whenNotPausedOrOwner() { + if (msg.sender == owner()) + _; + else { + require(!isPaused(), "Already paused"); + _; + } + } + + /** + * @notice Modifier to make a function callable only when the contract is not paused and ignore is msg.sender is owner. + */ modifier whenNotPaused() { - require(!getBool(Encoder.getKey("paused")), "Already paused"); + require(!isPaused(), "Already paused"); _; } @@ -78,7 +88,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @notice Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { - require(getBool(Encoder.getKey("paused")), "Should not be paused"); + require(isPaused(), "Should not be paused"); _; } @@ -94,7 +104,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { function initialize(address _polymathRegistry, address _owner) external payable { require(!getBool(Encoder.getKey("initialised"))); - require(_owner != address(0) && _polymathRegistry != address(0), "0x address is in-valid"); + require(_owner != address(0) && _polymathRegistry != address(0), "0x address is invalid"); set(Encoder.getKey("polymathRegistry"), _polymathRegistry); set(Encoder.getKey("owner"), _owner); set(Encoder.getKey("initialised"), true); @@ -116,22 +126,33 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { } else { require(getBool(Encoder.getKey('verified', _moduleFactory)), "ModuleFactory must be verified"); } - uint8[] memory _latestVersion = ISecurityToken(msg.sender).getVersion(); - uint8[] memory _lowerBound = IModuleFactory(_moduleFactory).getLowerSTVersionBounds(); - uint8[] memory _upperBound = IModuleFactory(_moduleFactory).getUpperSTVersionBounds(); - require(VersionUtils.compareLowerBound(_lowerBound, _latestVersion), "Version should not be below the lower bound of ST version requirement"); - require(VersionUtils.compareUpperBound(_upperBound, _latestVersion), "Version should not be above the upper bound of ST version requirement"); + require(_isCompatibleModule(_moduleFactory, msg.sender), "Version should within the compatible range of ST"); require(getUint(Encoder.getKey('registry',_moduleFactory)) != 0, "ModuleFactory type should not be 0"); pushArray(Encoder.getKey('reputation', _moduleFactory), msg.sender); emit ModuleUsed(_moduleFactory, msg.sender); } } + function _isCompatibleModule(address _moduleFactory, address _securityToken) internal view returns(bool) { + uint8[] memory _latestVersion = ISecurityToken(_securityToken).getVersion(); + uint8[] memory _lowerBound = IModuleFactory(_moduleFactory).getLowerSTVersionBounds(); + uint8[] memory _upperBound = IModuleFactory(_moduleFactory).getUpperSTVersionBounds(); + bool _isLowerAllowed = VersionUtils.compareLowerBound(_lowerBound, _latestVersion); + bool _isUpperAllowed = VersionUtils.compareUpperBound(_upperBound, _latestVersion); + return (_isLowerAllowed && _isUpperAllowed); + } + /** * @notice Called by the ModuleFactory owner to register new modules for SecurityTokens to use * @param _moduleFactory is the address of the module factory to be registered */ - function registerModule(address _moduleFactory) external whenNotPaused { + function registerModule(address _moduleFactory) external whenNotPausedOrOwner { + if (IFeatureRegistry(getAddress(Encoder.getKey('featureRegistry'))).getFeatureStatus("customModulesAllowed")) { + require(msg.sender == IOwnable(_moduleFactory).owner() || msg.sender == getAddress(Encoder.getKey('owner')), + "msg.sender must be the Module Factory owner or registry curator"); + } else { + require(msg.sender == getAddress(Encoder.getKey("owner")), "Only owner allowed to register modules"); + } require(getUint(Encoder.getKey('registry', _moduleFactory)) == 0, "Module factory should not be pre-registered"); IModuleFactory moduleFactory = IModuleFactory(_moduleFactory); uint8 moduleType = moduleFactory.getType(); @@ -146,7 +167,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @notice Called by the ModuleFactory owner or registry curator to delete a ModuleFactory from the registry * @param _moduleFactory is the address of the module factory to be deleted from the registry */ - function removeModule(address _moduleFactory) external whenNotPaused { + function removeModule(address _moduleFactory) external whenNotPausedOrOwner { uint256 moduleType = getUint(Encoder.getKey('registry', _moduleFactory)); require(moduleType != 0, "Module factory should be registered"); @@ -160,7 +181,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { // pop from array and re-order if (index != last) { // moduleList[moduleType][index] = temp; - setArrayIndexValue(Encoder.getKey('moduleList', moduleType), index, temp); + setArrayIndexValue(Encoder.getKey('moduleList', moduleType), index, temp); set(Encoder.getKey('moduleListIndex', temp), index); } deleteArrayAddress(Encoder.getKey('moduleList', moduleType), last); @@ -184,48 +205,61 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { * @param _moduleFactory is the address of the module factory to be verified * @return bool */ - function verifyModule(address _moduleFactory, bool _verified) external onlyOwner returns(bool) { + function verifyModule(address _moduleFactory, bool _verified) external onlyOwner { require(getUint(Encoder.getKey('registry', _moduleFactory)) != uint256(0), "Module factory must be registered"); set(Encoder.getKey('verified', _moduleFactory), _verified); emit ModuleVerified(_moduleFactory, _verified); - return true; } /** - * @notice Adds a list of tags for the specified Module Factory - * @dev This function is susceptible to hit the block gas limit if too many tags get added. - * @param _moduleType is the module type. - * @param _tag is the list of tags to add. + * @notice Returns all the tags related to the a module type which are valid for the given token + * @param _moduleType is the module type + * @param _securityToken is the token + * @return list of tags + * @return corresponding list of module factories */ - function addTagByModuleType(uint8 _moduleType, bytes32[] _tag) external onlyOwner { - for (uint8 i = 0; i < _tag.length; i++) { - pushArray(Encoder.getKey('availableTags', uint256(_moduleType)), _tag[i]); - } - } + function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[], address[]) { + address[] memory modules = getModulesByTypeAndToken(_moduleType, _securityToken); + return _tagsByModules(modules); + } /** - * @notice Removes the tag for specified Module Factory - * @dev This function is susceptible to hit the block gas limit if too many tags get removed. - * @param _moduleType is the module type. - * @param _removedTags is the list of tags to remove + * @notice Returns all the tags related to the a module type which are valid for the given token + * @param _moduleType is the module type + * @return list of tags + * @return corresponding list of module factories */ - function removeTagByModuleType(uint8 _moduleType, bytes32[] _removedTags) external onlyOwner { - for (uint8 i = 0; i < getArrayBytes32(Encoder.getKey('availableTags', uint256(_moduleType))).length; i++) { - for (uint8 j = 0; j < _removedTags.length; j++) { - if (getArrayBytes32(Encoder.getKey('availableTags', uint256(_moduleType)))[i] == _removedTags[j]) { - deleteArrayBytes32(Encoder.getKey('availableTags', uint256(_moduleType)), uint256(i)); - } - } - } + function getTagsByType(uint8 _moduleType) external view returns(bytes32[], address[]) { + address[] memory modules = getModulesByType(_moduleType); + return _tagsByModules(modules); } /** - * @notice Returns all the tags related to the functionality of the entered Module Factory. - * @param _moduleType is the module type - * @return bytes32 array + * @notice Returns all the tags related to the modules provided + * @param _modules modules to return tags for + * @return list of tags + * @return corresponding list of module factories */ - function getTagByModuleType(uint8 _moduleType) public view returns(bytes32[]) { - return getArrayBytes32(Encoder.getKey('availableTags', uint256(_moduleType))); + function _tagsByModules(address[] _modules) internal view returns(bytes32[], address[]) { + uint256 counter = 0; + uint256 i; + uint256 j; + for (i = 0; i < _modules.length; i++) { + counter = counter + IModuleFactory(_modules[i]).getTags().length; + } + bytes32[] memory tags = new bytes32[](counter); + address[] memory modules = new address[](counter); + bytes32[] memory tempTags; + counter = 0; + for (i = 0; i < _modules.length; i++) { + tempTags = IModuleFactory(_modules[i]).getTags(); + for (j = 0; j < tempTags.length; j++) { + tags[counter] = tempTags[j]; + modules[counter] = _modules[i]; + counter++; + } + } + return (tags, modules); } /** @@ -233,7 +267,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 getReputationOfFactory(address _factoryAddress) external view returns(address[]) { + function getReputationByFactory(address _factoryAddress) external view returns(address[]) { return getArrayAddress(Encoder.getKey('reputation', _factoryAddress)); } @@ -242,7 +276,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 getModuleListOfType(uint8 _moduleType) external view returns(address[]) { + function getModulesByType(uint8 _moduleType) public view returns(address[]) { return getArrayAddress(Encoder.getKey('moduleList', uint256(_moduleType))); } @@ -252,31 +286,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 getAvailableModulesOfType(uint8 _moduleType, address _securityToken) external view returns (address[]) { + function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) public view returns (address[]) { 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"); uint256 counter = 0; for (uint256 i = 0; i < _len; i++) { - if (IFeatureRegistry(getAddress(Encoder.getKey('featureRegistry'))).getFeatureStatus("customModulesAllowed")) { + if (_isCustomModuleAllowed) { if (IOwnable(_addressList[i]).owner() == IOwnable(_securityToken).owner() || getBool(Encoder.getKey('verified', _addressList[i]))) - counter++; + if(_isCompatibleModule(_addressList[i], _securityToken)) + counter++; } else if (getBool(Encoder.getKey('verified', _addressList[i]))) { - counter ++; + if(_isCompatibleModule(_addressList[i], _securityToken)) + counter++; } } address[] memory _tempArray = new address[](counter); counter = 0; for (uint256 j = 0; j < _len; j++) { - if (IFeatureRegistry(getAddress(Encoder.getKey('featureRegistry'))).getFeatureStatus("customModulesAllowed")) { + if (_isCustomModuleAllowed) { if (IOwnable(_addressList[j]).owner() == IOwnable(_securityToken).owner() || getBool(Encoder.getKey('verified', _addressList[j]))) { - _tempArray[counter] = _addressList[j]; - counter ++; + if(_isCompatibleModule(_addressList[j], _securityToken)) { + _tempArray[counter] = _addressList[j]; + counter ++; + } } } else if (getBool(Encoder.getKey('verified', _addressList[j]))) { - _tempArray[counter] = _addressList[j]; - counter ++; + if(_isCompatibleModule(_addressList[j], _securityToken)) { + _tempArray[counter] = _addressList[j]; + counter ++; + } } } return _tempArray; @@ -290,7 +331,7 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { require(_tokenContract != address(0)); IERC20 token = IERC20(_tokenContract); uint256 balance = token.balanceOf(address(this)); - require(token.transfer(getAddress(Encoder.getKey("owner")), balance)); + require(token.transfer(owner(), balance)); } /** @@ -319,4 +360,19 @@ contract ModuleRegistry is IModuleRegistry, EternalStorage { set(Encoder.getKey('polyToken'), IPolymathRegistry(_polymathRegistry).getAddress("PolyToken")); } + /** + * @notice Get the owner of the contract + * @return address owner + */ + function owner() public view returns(address) { + return getAddress(Encoder.getKey("owner")); + } + + /** + * @notice Check whether the contract operations is paused or not + * @return bool + */ + function isPaused() public view returns(bool) { + return getBool(Encoder.getKey("paused")); + } } diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index de7b93894..438e2eef5 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -78,8 +78,6 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // Emit when ownership of the ticker gets changed event ChangeTickerOwnership(string _ticker, address indexed _oldOwner, address indexed _newOwner); - // Emit when a ticker details is modified - event ModifyTickerDetails(address _owner, string _ticker, string _name, uint256 _registrationDate, uint256 _expiryDate, bool _status); // Emit at the time of launching a new security token event NewSecurityToken( string _ticker, @@ -88,7 +86,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { address indexed _owner, uint256 _addedAt, address _registrant, - bool _fromAdmin + bool _fromAdmin, + uint256 _registrationFee ); // Emit after ticker registration event RegisterTicker( @@ -97,7 +96,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { string _name, uint256 indexed _registrationDate, uint256 indexed _expiryDate, - bool _fromAdmin + bool _fromAdmin, + uint256 _registrationFee ); ///////////////////////////// @@ -108,36 +108,44 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { - require(msg.sender == getAddress(Encoder.getKey("owner"))); + require(msg.sender == owner()); _; } /** * @notice Modifier to make a function callable only when the contract is not paused. */ + modifier whenNotPausedOrOwner() { + if (msg.sender == owner()) + _; + else { + require(!isPaused(), "Already paused"); + _; + } + } + + /** + * @notice Modifier to make a function callable only when the contract is not paused and ignore is msg.sender is owner. + */ modifier whenNotPaused() { - require(!getBool(Encoder.getKey("paused")), "Already paused"); + require(!isPaused(), "Already paused"); _; } + /** * @notice Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { - require(getBool(Encoder.getKey("paused")), "Should not be paused"); + require(isPaused(), "Should not be paused"); _; } + ///////////////////////////// // Initialization ///////////////////////////// - // Constructor - constructor () public - { - - } - /** * @notice initializes instance of STR * @param _polymathRegistry is the address of the Polymath Registry @@ -149,9 +157,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function initialize(address _polymathRegistry, address _STFactory, uint256 _stLaunchFee, uint256 _tickerRegFee, address _polyToken, address _owner) payable external { require(!getBool(Encoder.getKey("initialised"))); - require(_STFactory != address(0) && _polyToken != address(0) && _owner != address(0) && _polymathRegistry != address(0), "0x address is in-valid"); + require(_STFactory != address(0) && _polyToken != address(0) && _owner != address(0) && _polymathRegistry != address(0), "Invalid address"); require(_stLaunchFee != 0 && _tickerRegFee != 0, "Fees should not be 0"); - // address polyToken = _polyToken; set(Encoder.getKey("polyToken"), _polyToken); set(Encoder.getKey("stLaunchFee"), _stLaunchFee); set(Encoder.getKey("tickerRegFee"), _tickerRegFee); @@ -175,20 +182,30 @@ 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 whenNotPaused { + function registerTicker(address _owner, string _ticker, string _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 - if (getUint(Encoder.getKey("tickerRegFee")) > 0) - require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), getUint(Encoder.getKey("tickerRegFee"))), "Sufficent allowance is not provided"); + uint256 tickerFee = getTickerRegistrationFee(); + if (tickerFee > 0) + require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), tickerFee), "Insufficent allowance"); string memory ticker = Util.upper(_ticker); - require(_tickerAvailable(ticker), "Ticker is already reserved"); + require(_tickerAvailable(ticker), "Ticker is reserved"); // Check whether ticker was previously registered (and expired) - address previousOwner = getAddress(Encoder.getKey("registeredTickers_owner", _ticker)); + address previousOwner = _tickerOwner(ticker); if (previousOwner != address(0)) { - _deleteTickerOwnership(previousOwner, _ticker); + _deleteTickerOwnership(previousOwner, ticker); } - _addTicker(_owner, ticker, _tokenName, now, now.add(getUint(Encoder.getKey("expiryLimit"))), false, false); + _addTicker(_owner, ticker, _tokenName, now, now.add(getExpiryLimit()), false, false, tickerFee); + } + + /** + * @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 { + _setTickerOwnership(_owner, _ticker); + _storeTickerDetails(_ticker, _owner, _registrationDate, _expiryDate, _tokenName, _status); + emit RegisterTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _fromAdmin, _fee); } /** @@ -205,7 +222,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { 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"); - require(_owner != address(0), "Address should not be 0x"); + require(_owner != address(0), "Invalid address"); string memory ticker = Util.upper(_ticker); _modifyTicker(_owner, ticker, _tokenName, _registrationDate, _expiryDate, _status); } @@ -214,20 +231,22 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @notice Internal -- Modifies the ticker details. */ function _modifyTicker(address _owner, string _ticker, string _tokenName, uint256 _registrationDate, uint256 _expiryDate, bool _status) internal { - address currentOwner = getAddress(Encoder.getKey("registeredTickers_owner", _ticker)); - if (currentOwner == address(0) && _registrationDate == 0 && _expiryDate == 0) { - _addTicker(_owner, _ticker, _tokenName, now, now.add(getUint(Encoder.getKey("expiryLimit"))), _status, true); - return; + address currentOwner = _tickerOwner(_ticker); + if (currentOwner != address(0)) { + _deleteTickerOwnership(currentOwner, _ticker); } - // If ticker exists, and is registered to a different owner, switch over - if ((currentOwner != address(0)) && (currentOwner != _owner)) { - _transferTickerOwnership(currentOwner, _owner, _ticker); - } - if (getBool(Encoder.getKey("registeredTickers_status", _ticker)) && !_status) { + if (_tickerStatus(_ticker) && !_status) { set(Encoder.getKey("tickerToSecurityToken", _ticker), address(0)); } - _storeTickerDetails(_ticker, _owner, _registrationDate, _expiryDate, _tokenName, _status); - emit RegisterTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, true); + // 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"); + } + _addTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _status, true, uint256(0)); + } + + function _tickerOwner(string _ticker) internal view returns(address) { + return getAddress(Encoder.getKey("registeredTickers_owner", _ticker)); } /** @@ -236,12 +255,12 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function removeTicker(string _ticker) external onlyOwner { string memory ticker = Util.upper(_ticker); - address owner = getAddress(Encoder.getKey("registeredTickers_owner", ticker)); - require(owner != address(0), "Ticker does not exist"); + address owner = _tickerOwner(ticker); + require(owner != address(0), "Ticker doesn't exist"); _deleteTickerOwnership(owner, ticker); set(Encoder.getKey("tickerToSecurityToken", ticker), address(0)); _storeTickerDetails(ticker, address(0), 0, 0, "", false); - emit TickerRemoved(_ticker, now, msg.sender); + emit TickerRemoved(ticker, now, msg.sender); } /** @@ -250,8 +269,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @return bool */ function _tickerAvailable(string _ticker) internal view returns(bool) { - if (getAddress(Encoder.getKey("registeredTickers_owner", _ticker)) != address(0)) { - if (now > getUint(Encoder.getKey("registeredTickers_expiryDate", _ticker)) && !getBool(Encoder.getKey("registeredTickers_status", _ticker))) { + if (_tickerOwner(_ticker) != address(0)) { + if ((now > getUint(Encoder.getKey("registeredTickers_expiryDate", _ticker))) && !_tickerStatus(_ticker)) { return true; } else return false; @@ -259,13 +278,8 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { return true; } - /** - * @notice Internal - Sets the details of the ticker - */ - function _addTicker(address _owner, string _ticker, string _tokenName, uint256 _registrationDate, uint256 _expiryDate, bool _status, bool _fromAdmin) internal { - _setTickerOwner(_owner, _ticker); - _storeTickerDetails(_ticker, _owner, _registrationDate, _expiryDate, _tokenName, _status); - emit RegisterTicker(_owner, _ticker, _tokenName, _registrationDate, _expiryDate, _fromAdmin); + function _tickerStatus(string _ticker) internal returns(bool) { + return getBool(Encoder.getKey("registeredTickers_status", _ticker)); } /** @@ -273,13 +287,15 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _owner is the address of the owner of the ticker * @param _ticker is the ticker symbol */ - function _setTickerOwner(address _owner, string _ticker) internal { - uint256 length = uint256(getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length); - pushArray(Encoder.getKey("userToTickers", _owner), Util.stringToBytes32(_ticker)); + function _setTickerOwnership(address _owner, string _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); - if (!getBool(Encoder.getKey("seenUsers", _owner))) { + bytes32 seenKey = Encoder.getKey("seenUsers", _owner); + if (!getBool(seenKey)) { pushArray(Encoder.getKey("activeUsers"), _owner); - set(Encoder.getKey("seenUsers", _owner), true); + set(seenKey, true); } } @@ -287,16 +303,21 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @notice Internal - Stores the ticker details */ function _storeTickerDetails(string _ticker, address _owner, uint256 _registrationDate, uint256 _expiryDate, string _tokenName, bool _status) internal { - if (getAddress(Encoder.getKey("registeredTickers_owner", _ticker)) != _owner) - set(Encoder.getKey("registeredTickers_owner", _ticker), _owner); - if (getUint(Encoder.getKey("registeredTickers_registrationDate", _ticker)) != _registrationDate) - set(Encoder.getKey("registeredTickers_registrationDate", _ticker), _registrationDate); - if (getUint(Encoder.getKey("registeredTickers_expiryDate", _ticker)) != _expiryDate) - set(Encoder.getKey("registeredTickers_expiryDate", _ticker), _expiryDate); - if (Encoder.getKey(getString(Encoder.getKey("registeredTickers_tokenName", _ticker))) != Encoder.getKey(_tokenName)) - set(Encoder.getKey("registeredTickers_tokenName", _ticker), _tokenName); - if (getBool(Encoder.getKey("registeredTickers_status", _ticker)) != _status) - set(Encoder.getKey("registeredTickers_status", _ticker), _status); + bytes32 key = Encoder.getKey("registeredTickers_owner", _ticker); + if (getAddress(key) != _owner) + set(key, _owner); + key = Encoder.getKey("registeredTickers_registrationDate", _ticker); + if (getUint(key) != _registrationDate) + set(key, _registrationDate); + key = Encoder.getKey("registeredTickers_expiryDate", _ticker); + if (getUint(key) != _expiryDate) + set(key, _expiryDate); + key = Encoder.getKey("registeredTickers_tokenName", _ticker); + if (Encoder.getKey(getString(key)) != Encoder.getKey(_tokenName)) + set(key, _tokenName); + key = Encoder.getKey("registeredTickers_status", _ticker); + if (getBool(key) != _status) + set(key, _status); } /** @@ -304,41 +325,32 @@ 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 whenNotPaused { + function transferTickerOwnership(address _newOwner, string _ticker) external whenNotPausedOrOwner { string memory ticker = Util.upper(_ticker); - require(_newOwner != address(0), "Address should not be 0x"); - require(getAddress(Encoder.getKey("registeredTickers_owner", ticker)) == msg.sender, "Not authorised"); - _transferTickerOwnership(msg.sender, _newOwner, ticker); - set(Encoder.getKey("registeredTickers_owner", ticker), _newOwner); - } - - /** - * @notice Internal - Transfers the control of ticker to a newOwner - * @param _oldOwner is the previous owner - * @param _newOwner is the the new owner - * @param _ticker is the ticker symbol - */ - function _transferTickerOwnership(address _oldOwner, address _newOwner, string _ticker) internal { - if(getBool(Encoder.getKey("registeredTickers_status", _ticker))) - require(IOwnable(getAddress(Encoder.getKey("tickerToSecurityToken", _ticker))).owner() == _newOwner, "If the token exists, the ticker can only be transferred to its owner"); - - _deleteTickerOwnership(_oldOwner, _ticker); - _setTickerOwner(_newOwner, _ticker); - emit ChangeTickerOwnership(_ticker, _oldOwner, _newOwner); + 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"); + _deleteTickerOwnership(msg.sender, ticker); + _setTickerOwnership(_newOwner, ticker); + set(ownerKey, _newOwner); + emit ChangeTickerOwnership(ticker, msg.sender, _newOwner); } /** * @notice Internal - Removes the owner of a ticker */ function _deleteTickerOwnership(address _owner, string _ticker) internal { - uint256 _index = uint256(getUint(Encoder.getKey("tickerIndex", _ticker))); - assert(_index < getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length); - // deleting the _index from the data strucutre userToTickers[_oldowner][_index]; - deleteArrayBytes32(Encoder.getKey("userToTickers", _owner), _index); - - if (getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length > _index) { - bytes32 switchedTicker = getArrayBytes32(Encoder.getKey("userToTickers", _owner))[_index]; - set(Encoder.getKey("tickerIndex", Util.bytes32ToString(switchedTicker)), _index); + uint256 index = uint256(getUint(Encoder.getKey("tickerIndex", _ticker))); + bytes32 ownerKey = Encoder.getKey("userToTickers", _owner); + bytes32[] memory tickers = getArrayBytes32(ownerKey); + assert(index < tickers.length); + assert(_tickerOwner(_ticker) == _owner); + deleteArrayBytes32(ownerKey, index); + if (getArrayBytes32(ownerKey).length > index) { + bytes32 switchedTicker = getArrayBytes32(ownerKey)[index]; + set(Encoder.getKey("tickerIndex", Util.bytes32ToString(switchedTicker)), index); } } @@ -348,8 +360,9 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function changeExpiryLimit(uint256 _newExpiry) external onlyOwner { require(_newExpiry >= 1 days, "Expiry should >= 1 day"); - emit ChangeExpiryLimit(getUint(Encoder.getKey('expiryLimit')), _newExpiry); - set(Encoder.getKey('expiryLimit'), _newExpiry); + bytes32 key = Encoder.getKey('expiryLimit'); + emit ChangeExpiryLimit(getUint(key), _newExpiry); + set(key, _newExpiry); } /** @@ -359,12 +372,12 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { function getTickersByOwner(address _owner) external view returns(bytes32[]) { uint counter = 0; // accessing the data structure userTotickers[_owner].length - uint _len = getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length; - bytes32[] memory tempList = new bytes32[](_len); - for (uint i = 0; i < _len; i++) { - string memory _ticker = Util.bytes32ToString(getArrayBytes32(Encoder.getKey("userToTickers", _owner))[i]); - if (getUint(Encoder.getKey("registeredTickers_expiryDate", _ticker)) >= now || getBool(Encoder.getKey("registeredTickers_status", _ticker))) { - tempList[counter] = getArrayBytes32(Encoder.getKey("userToTickers", _owner))[i]; + 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]); + if (getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now || _tickerStatus(ticker)) { + tempList[counter] = tickers[i]; counter ++; } } @@ -424,14 +437,16 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function getTickerDetails(string _ticker) external view returns (address, uint256, uint256, string, bool) { string memory ticker = Util.upper(_ticker); - if (getBool(Encoder.getKey("registeredTickers_status", ticker)) == true || getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) > now) { + bool tickerStatus = _tickerStatus(ticker); + uint256 expiryDate = getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)); + if ((tickerStatus == true) || (expiryDate > now)) { return ( - getAddress(Encoder.getKey("registeredTickers_owner", ticker)), + _tickerOwner(ticker), getUint(Encoder.getKey("registeredTickers_registrationDate", ticker)), - getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)), + expiryDate, getString(Encoder.getKey("registeredTickers_tokenName", ticker)), - getBool(Encoder.getKey("registeredTickers_status", ticker)) + tickerStatus ); } else return (address(0), uint256(0), uint256(0), "", false); @@ -448,18 +463,18 @@ 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 whenNotPaused { + function generateSecurityToken(string _name, string _ticker, string _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"); + set(statusKey, true); + require(_tickerOwner(ticker) == msg.sender, "Not authorised"); + require(getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker gets expired"); - require(getBool(Encoder.getKey("registeredTickers_status", ticker)) != true, "Ticker already deployed"); - require(getAddress(Encoder.getKey("registeredTickers_owner", ticker)) == msg.sender, "Ticker and token should have same owner"); - require(getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker should not have expired"); - - set(Encoder.getKey("registeredTickers_status", ticker), true); - - if (getUint(Encoder.getKey("stLaunchFee")) > 0) - require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), getUint(Encoder.getKey("stLaunchFee"))), "Sufficent allowance is not provided"); + uint256 launchFee = getSecurityTokenLaunchFee(); + if (launchFee > 0) + require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), launchFee), "Insufficient allowance"); address newSecurityTokenAddress = ISTFactory(getSTFactoryAddress()).deployToken( _name, @@ -473,7 +488,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { _storeSecurityTokenData(newSecurityTokenAddress, ticker, _tokenDetails, now); set(Encoder.getKey("tickerToSecurityToken", ticker), newSecurityTokenAddress); - emit NewSecurityToken(ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false); + emit NewSecurityToken(ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, launchFee); } /** @@ -491,11 +506,16 @@ 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"); - // If ticker didn't previously exist, it will be created - _modifyTicker(_owner, ticker, _name, getUint(Encoder.getKey("registeredTickers_registrationDate", ticker)), getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)), true); + uint256 registrationTime = getUint(Encoder.getKey("registeredTickers_registrationDate", ticker)); + uint256 expiryTime = getUint(Encoder.getKey("registeredTickers_expiryDate", ticker)); + if (registrationTime == 0) { + registrationTime = now; + expiryTime = registrationTime.add(getExpiryLimit()); + } 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); + emit NewSecurityToken(ticker, _name, _securityToken, _owner, _deployedAt, msg.sender, true, getSecurityTokenLaunchFee()); } /** @@ -553,8 +573,9 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function transferOwnership(address _newOwner) external onlyOwner { require(_newOwner != address(0)); - emit OwnershipTransferred(getAddress(Encoder.getKey("owner")), _newOwner); - set(Encoder.getKey("owner"), _newOwner); + bytes32 key = Encoder.getKey("owner"); + emit OwnershipTransferred(getAddress(key), _newOwner); + set(key, _newOwner); } /** @@ -578,9 +599,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _tickerRegFee is the registration fee in POLY tokens (base 18 decimals) */ function changeTickerRegistrationFee(uint256 _tickerRegFee) external onlyOwner { - require(getUint(Encoder.getKey('tickerRegFee')) != _tickerRegFee); - emit ChangeTickerRegistrationFee(getUint(Encoder.getKey('tickerRegFee')), _tickerRegFee); - set(Encoder.getKey('tickerRegFee'), _tickerRegFee); + bytes32 key = Encoder.getKey('tickerRegFee'); + uint256 fee = getUint(key); + require(fee != _tickerRegFee); + emit ChangeTickerRegistrationFee(fee, _tickerRegFee); + set(key, _tickerRegFee); } /** @@ -588,9 +611,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _stLaunchFee is the registration fee in POLY tokens (base 18 decimals) */ function changeSecurityLaunchFee(uint256 _stLaunchFee) external onlyOwner { - require(getUint(Encoder.getKey("stLaunchFee")) != _stLaunchFee); - emit ChangeSecurityLaunchFee(getUint(Encoder.getKey("stLaunchFee")), _stLaunchFee); - set(Encoder.getKey("stLaunchFee"), _stLaunchFee); + bytes32 key = Encoder.getKey('stLaunchFee'); + uint256 fee = getUint(key); + require(fee != _stLaunchFee); + emit ChangeSecurityLaunchFee(fee, _stLaunchFee); + set(key, _stLaunchFee); } /** @@ -601,7 +626,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { require(_tokenContract != address(0)); IERC20 token = IERC20(_tokenContract); uint256 balance = token.balanceOf(address(this)); - require(token.transfer(getAddress(Encoder.getKey("owner")), balance)); + require(token.transfer(owner(), balance)); } /** @@ -654,5 +679,44 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { set(Encoder.getKey("polyToken"), _newAddress); } + /** + * @notice Gets the security token launch fee + * @return Fee amount + */ + function getSecurityTokenLaunchFee() public view returns(uint256) { + return getUint(Encoder.getKey("stLaunchFee")); + } + + /** + * @notice Gets the ticker registration fee + * @return Fee amount + */ + function getTickerRegistrationFee() public view returns(uint256) { + return getUint(Encoder.getKey("tickerRegFee")); + } + + /** + * @notice Gets the expiry limit + * @return Expiry limit + */ + function getExpiryLimit() public view returns(uint256) { + return getUint(Encoder.getKey("expiryLimit")); + } + + /** + * @notice Check whether the registry is paused or not + * @return bool + */ + function isPaused() public view returns(bool) { + return getBool(Encoder.getKey("paused")); + } + + /** + * @notice Gets the owner of the contract + * @return address owner + */ + function owner() public view returns(address) { + return getAddress(Encoder.getKey("owner")); + } } diff --git a/contracts/interfaces/IModuleRegistry.sol b/contracts/interfaces/IModuleRegistry.sol index 59763bd57..a2742fb7c 100644 --- a/contracts/interfaces/IModuleRegistry.sol +++ b/contracts/interfaces/IModuleRegistry.sol @@ -23,60 +23,68 @@ interface IModuleRegistry { */ function removeModule(address _moduleFactory) external; - /** - * @notice Use to get all the tags releated to the functionality of the Module Factory. - * @param _moduleType Type of module - */ - function getTagByModuleType(uint8 _moduleType) external view returns(bytes32[]); - /** * @notice Called by Polymath to verify modules for SecurityToken to use. * @notice A module can not be used by an ST unless first approved/verified by Polymath * @notice (The only exception to this is that the author of the module is the owner of the ST) * @param _moduleFactory is the address of the module factory to be registered - * @return bool */ - function verifyModule(address _moduleFactory, bool _verified) external returns(bool); + function verifyModule(address _moduleFactory, bool _verified) external; /** - * @notice Add the tag for specified Module Factory - * @param _moduleType Type of module. - * @param _tag List of tags + * @notice Used to get the reputation of a Module Factory + * @param _factoryAddress address of the Module Factory + * @return address array which has the list of securityToken's uses that module factory */ - function addTagByModuleType(uint8 _moduleType, bytes32[] _tag) external; + function getReputationByFactory(address _factoryAddress) external view returns(address[]); - /** - * @notice remove a tag for a Module Factory - * @param _moduleType Type of module. - * @param _removedTags List of tags - */ - function removeTagByModuleType(uint8 _moduleType, bytes32[] _removedTags) external; + /** + * @notice Returns all the tags related to the a module type which are valid for the given token + * @param _moduleType is the module type + * @param _securityToken is the token + * @return list of tags + * @return corresponding list of module factories + */ + function getTagsByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns(bytes32[], address[]); /** - * @notice Used to get the reputation of a Module Factory - * @param _factoryAddress address of the Module Factory - * @return address array which has the list of securityToken's uses that module factory + * @notice Returns all the tags related to the a module type which are valid for the given token + * @param _moduleType is the module type + * @return list of tags + * @return corresponding list of module factories */ - function getReputationOfFactory(address _factoryAddress) external view returns(address[]); + function getTagsByType(uint8 _moduleType) external view returns(bytes32[], address[]); /** - * @notice Use to get the list of Module Factory addresses for a given module type + * @notice Returns the list of addresses of Module Factory of a particular type * @param _moduleType Type of Module - * @return address array thal contains the list of addresses of module factory contracts. + * @return address array that contains the list of addresses of module factory contracts. */ - function getModuleListOfType(uint8 _moduleType) external view returns(address[]); + function getModulesByType(uint8 _moduleType) external view returns(address[]); /** - * @notice Use to get the list of available Module factory addresses for a particular type - * @param _moduleType Type of Module - * @param _securityToken Address of securityToken + * @notice Returns the list of available Module factory addresses of a particular type for a given token. + * @param _moduleType is the module type to look for + * @param _securityToken is the address of SecurityToken * @return address array that contains the list of available addresses of module factory contracts. */ - function getAvailableModulesOfType(uint8 _moduleType, address _securityToken) external view returns (address[]); + function getModulesByTypeAndToken(uint8 _moduleType, address _securityToken) external view returns (address[]); /** * @notice Use to get the latest contract address of the regstries */ function updateFromRegistry() external; + /** + * @notice Get the owner of the contract + * @return address owner + */ + function owner() external view returns(address); + + /** + * @notice Check whether the contract operations is paused or not + * @return bool + */ + function isPaused() external view returns(bool); + } diff --git a/contracts/interfaces/IOwnable.sol b/contracts/interfaces/IOwnable.sol index 15849e181..de8a8d14f 100644 --- a/contracts/interfaces/IOwnable.sol +++ b/contracts/interfaces/IOwnable.sol @@ -10,7 +10,7 @@ interface IOwnable { /** * @dev Returns owner */ - function owner() external returns (address); + function owner() external view returns (address); /** * @dev Allows the current owner to relinquish control of the contract. diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index 33e38201c..c36f45270 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -19,15 +19,15 @@ interface ISecurityToken { event Approval(address indexed owner, address indexed spender, uint256 value); //transfer, transferFrom must respect use respect the result of verifyTransfer - function verifyTransfer(address _from, address _to, uint256 _amount) external returns (bool success); + function verifyTransfer(address _from, address _to, uint256 _value) external returns (bool success); /** * @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 _amount is the amount of tokens that will be minted to the investor + * @param _value is the amount of tokens that will be minted to the investor */ - function mint(address _investor, uint256 _amount) external returns (bool success); + function mint(address _investor, uint256 _value) external returns (bool success); /** * @notice Burn function used to burn the securityToken @@ -35,7 +35,14 @@ interface ISecurityToken { */ function burn(uint256 _value) external returns (bool success); - event Minted(address indexed to, uint256 amount); + /** + * @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 token that get burned + */ + function burnFrom(address _from, uint256 _value) external returns (bool success); + + event Minted(address indexed _to, uint256 _value); event Burnt(address indexed _burner, uint256 _value); // Permissions this to a Permission module, which has a key of 1 @@ -118,9 +125,9 @@ interface ISecurityToken { /** * @notice allows the owner to withdraw unspent POLY stored by them on the ST. * @dev Owner can transfer POLY to the ST which will be used to pay for modules that require a POLY fee. - * @param _amount amount of POLY to withdraw + * @param _value amount of POLY to withdraw */ - function withdrawPoly(uint256 _amount) external; + function withdrawPoly(uint256 _value) external; /** * @notice allows owner to approve more POLY to one of the modules @@ -168,10 +175,10 @@ interface ISecurityToken { * @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 _amounts A list of the amount of tokens to mint to corresponding addresses from _investor[] list + * @param _values A list of the amount of tokens to mint to corresponding addresses from _investor[] list * @return success */ - function mintMulti(address[] _investors, uint256[] _amounts) external returns (bool success); + function mintMulti(address[] _investors, uint256[] _values) external returns (bool success); /** * @notice used to set the token Burner address. It can only be called by the owner diff --git a/contracts/interfaces/ISecurityTokenRegistry.sol b/contracts/interfaces/ISecurityTokenRegistry.sol index 444fb5375..2bacd39d7 100644 --- a/contracts/interfaces/ISecurityTokenRegistry.sol +++ b/contracts/interfaces/ISecurityTokenRegistry.sol @@ -84,7 +84,7 @@ interface ISecurityTokenRegistry { /** * @notice get Protocol version */ - function getProtocolVersion() public view returns(uint8[]); + function getProtocolVersion() external view returns(uint8[]); /** * @notice Use to get the ticker list as per the owner @@ -159,4 +159,34 @@ interface ISecurityTokenRegistry { */ function updatePolyTokenAddress(address _newAddress) external; + /** + * @notice Gets the security token launch fee + * @return Fee amount + */ + function getSecurityTokenLaunchFee() external view returns(uint256); + + /** + * @notice Gets the ticker registration fee + * @return Fee amount + */ + function getTickerRegistrationFee() external view returns(uint256); + + /** + * @notice Gets the expiry limit + * @return Expiry limit + */ + function getExpiryLimit() external view returns(uint256); + + /** + * @notice Check whether the registry is paused or not + * @return bool + */ + function isPaused() external view returns(bool); + + /** + * @notice Gets the owner of the contract + * @return address owner + */ + function owner() external view returns(address); + } diff --git a/contracts/libraries/KindMath.sol b/contracts/libraries/KindMath.sol new file mode 100644 index 000000000..a9cce4546 --- /dev/null +++ b/contracts/libraries/KindMath.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.4.24; + +// Copied from OpenZeppelin and modified to be friendlier + +/** + * @title KindMath + * @dev Math operations with safety checks that throw on error + */ +library KindMath { + + /** + * @dev Multiplies two numbers, throws on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { + // 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; + } + + 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; + } + + /** + * @dev Subtracts two numbers, throws 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; + } + + /** + * @dev Adds two numbers, throws on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256 c) { + c = a + b; + require(c >= a, "add overflow"); + return c; + } +} diff --git a/contracts/mocks/MockModuleRegistry.sol b/contracts/mocks/MockModuleRegistry.sol new file mode 100644 index 000000000..23af59d58 --- /dev/null +++ b/contracts/mocks/MockModuleRegistry.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.4.24; + +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 { + for (uint8 i = 0; i < _tokens.length; i++) { + pushArray(Encoder.getKey('reputation', _moduleFactory), _tokens[i]); + } + } + + +} diff --git a/contracts/mocks/ModuleRegistryMock.sol b/contracts/mocks/ModuleRegistryMock.sol deleted file mode 100644 index 18551dd20..000000000 --- a/contracts/mocks/ModuleRegistryMock.sol +++ /dev/null @@ -1,18 +0,0 @@ -pragma solidity ^0.4.24; - -import "../ModuleRegistry.sol"; - -/** - * @title Registry contract for issuers to register their security tokens - */ -contract ModuleRegistryMock is ModuleRegistry { - - /// @notice It is dummy functionality - /// Alert! Alert! Do not use it for the mainnet release - function addMoreTags(uint8 _moduleType, bytes32[] _tag) public onlyOwner { - for (uint8 i = 0; i < _tag.length; i++) { - pushArray(Encoder.getKey('availableTags', uint256(_moduleType)), _tag[i]); - } - } - -} \ No newline at end of file diff --git a/contracts/mocks/SecurityTokenRegistryMock.sol b/contracts/mocks/SecurityTokenRegistryMock.sol index e2b9bf2fd..78faa60d1 100644 --- a/contracts/mocks/SecurityTokenRegistryMock.sol +++ b/contracts/mocks/SecurityTokenRegistryMock.sol @@ -9,9 +9,8 @@ contract SecurityTokenRegistryMock is SecurityTokenRegistry { /// @notice It is dummy functionality /// Alert! Alert! Do not use it for the mainnet release - function changeTheDeployedAddress(string _ticker, address _newSecurityTokenAddress) public onlyOwner { - string memory __ticker = Util.upper(_ticker); - set(Encoder.getKey("tickerToSecurityToken", __ticker), _newSecurityTokenAddress); + function changeTheDeployedAddress(string _ticker, address _newSecurityTokenAddress) public { + set(Encoder.getKey("tickerToSecurityToken", _ticker), _newSecurityTokenAddress); } } diff --git a/contracts/modules/Burn/IBurn.sol b/contracts/modules/Burn/IBurn.sol new file mode 100644 index 000000000..3af836f40 --- /dev/null +++ b/contracts/modules/Burn/IBurn.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.24; + +/** + * @title Interface to be implemented by all checkpoint modules + */ +interface IBurn { + +} diff --git a/contracts/modules/Burn/TrackedRedemption.sol b/contracts/modules/Burn/TrackedRedemption.sol new file mode 100644 index 000000000..f69d8a86a --- /dev/null +++ b/contracts/modules/Burn/TrackedRedemption.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.4.24; + +import "./IBurn.sol"; +import "../Module.sol"; +import "../../interfaces/ISecurityToken.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/** + * @title Burn module for burning tokens and keeping track of burnt amounts + */ +contract TrackedRedemption is IBurn, Module { + using SafeMath for uint256; + + mapping (address => uint256) redeemedTokens; + + event Redeemed(address _investor, 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 + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + /** + * @notice Redeem tokens and track redemptions + * @param _value The number of tokens to redeem + */ + function redeemTokens(uint256 _value) public { + require(ISecurityToken(securityToken).burnFrom(msg.sender, _value), "Unable to redeem tokens"); + redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value); + emit Redeemed(msg.sender, _value, now); + } + + /** + * @notice Return the permissions flag that are associated with CountTransferManager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](0); + return allPermissions; + } +} diff --git a/contracts/modules/Burn/TrackedRedemptionFactory.sol b/contracts/modules/Burn/TrackedRedemptionFactory.sol new file mode 100644 index 000000000..94003286b --- /dev/null +++ b/contracts/modules/Burn/TrackedRedemptionFactory.sol @@ -0,0 +1,100 @@ +pragma solidity ^0.4.24; + +import "./TrackedRedemption.sol"; +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 + */ + constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "TrackedRedemption"; + title = "Tracked Redemption"; + description = "Track token redemptions"; + 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"); + address trackedRedemption = new TrackedRedemption(msg.sender, address(polyToken)); + emit GenerateModuleFromFactory(address(trackedRedemption), getName(), address(this), msg.sender, setupCost, now); + return address(trackedRedemption); + } + + /** + * @notice Type of the Module factory + */ + function getType() public view returns(uint8) { + return 5; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() public view returns(string) { + return description; + } + + /** + * @notice Get the version of the Module + */ + function getVersion() external view returns(string) { + return version; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() public view returns(string) { + return title; + } + + /** + * @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() public view returns(string) { + 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() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Redemption"; + availableTags[1] = "Tracked"; + return availableTags; + } + +} diff --git a/contracts/modules/Checkpoint/DividendCheckpoint.sol b/contracts/modules/Checkpoint/DividendCheckpoint.sol index e427509ab..fc70a9fde 100644 --- a/contracts/modules/Checkpoint/DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/DividendCheckpoint.sol @@ -1,214 +1,227 @@ -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"; - - 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 - } - - // 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 SetExcludedAddresses(address[] _excluded, uint256 _timestamp); - - modifier validDividendIndex(uint256 _dividendIndex) { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - require(now >= dividends[_dividendIndex].maturity, "Dividend maturity is in the future"); - require(now < dividends[_dividendIndex].expiry, "Dividend expiry is in the past"); - require(!dividends[_dividendIndex].reclaimed, "Dividend has been reclaimed by issuer"); - _; - } - - /** - * @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 Function to set withholding tax rates for investors - * @param _investors addresses of investor - * @param _withholding withholding tax for individual investors (multiplied by 10**16) - */ - function setWithholding(address[] _investors, uint256[] _withholding) public onlyOwner { - require(_investors.length == _withholding.length, "Mismatched input lengths"); - for (uint256 i = 0; i < _investors.length; i++) { - require(_withholding[i] <= 10**18); - withholdingTax[_investors[i]] = _withholding[i]; - } - } - - /** - * @notice Function to clear and set list of excluded addresses used for future dividends - * @param _excluded addresses of investor - */ - function setExcluded(address[] _excluded) public onlyOwner { - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many excluded addresses"); - excluded = _excluded; - emit SetExcludedAddresses(excluded, now); - } - - /** - * @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 onlyOwner { - require(_withholding <= 10**18); - 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]; - uint256 numberInvestors = ISecurityToken(securityToken).getInvestorsLength(); - for (uint256 i = _start; i < Math.min256(numberInvestors, _start.add(_iterations)); i++) { - address payee = ISecurityToken(securityToken).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 by msg.sender"); - 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 unit256 - */ - function calculateDividend(uint256 _dividendIndex, address _payee) public view returns(uint256, uint256) { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - 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 STO - * @return bytes32 array - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = DISTRIBUTE; - return allPermissions; - } - -} +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"; + + 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, "Incorrect dividend index"); + require(now >= dividends[_dividendIndex].maturity, "Dividend maturity is in the future"); + require(now < dividends[_dividendIndex].expiry, "Dividend expiry is in the past"); + require(!dividends[_dividendIndex].reclaimed, "Dividend has been reclaimed by issuer"); + _; + } + + /** + * @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 Function to clear and set list of excluded addresses used for future dividends + * @param _excluded addresses of investor + */ + function setDefaultExcluded(address[] _excluded) public onlyOwner { + require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many excluded addresses"); + excluded = _excluded; + emit SetDefaultExcludedAddresses(excluded, now); + } + + /** + * @notice Function to set withholding tax rates for investors + * @param _investors addresses of investor + * @param _withholding withholding tax for individual investors (multiplied by 10**16) + */ + function setWithholding(address[] _investors, uint256[] _withholding) public onlyOwner { + require(_investors.length == _withholding.length, "Mismatched input lengths"); + 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 onlyOwner { + require(_withholding <= 10**18, "Incorrect withholding tax"); + 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]; + uint256 numberInvestors = ISecurityToken(securityToken).getInvestorsLength(); + for (uint256 i = _start; i < Math.min256(numberInvestors, _start.add(_iterations)); i++) { + address payee = ISecurityToken(securityToken).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 by msg.sender"); + 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, "Incorrect dividend index"); + 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 STO + * @return bytes32 array + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = DISTRIBUTE; + return allPermissions; + } + +} diff --git a/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol b/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol index ad61b24a8..60d84a41b 100644 --- a/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol @@ -1,157 +1,206 @@ -pragma solidity ^0.4.24; - -import "./DividendCheckpoint.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); - 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 - */ - function createDividend(uint256 _maturity, uint256 _expiry, address _token, uint256 _amount) external onlyOwner { - createDividendWithExclusions(_maturity, _expiry, _token, _amount, excluded); - } - - /** - * @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 - */ - function createDividendWithCheckpoint(uint256 _maturity, uint256 _expiry, address _token, uint256 _amount, uint256 _checkpointId) external onlyOwner { - createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, _checkpointId, excluded); - } - - /** - * @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 - */ - function createDividendWithExclusions(uint256 _maturity, uint256 _expiry, address _token, uint256 _amount, address[] _excluded) public onlyOwner { - uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); - createDividendWithCheckpointAndExclusions(_maturity, _expiry, _token, _amount, checkpointId, _excluded); - } - - /** - * @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 - */ - function createDividendWithCheckpointAndExclusions(uint256 _maturity, uint256 _expiry, address _token, uint256 _amount, uint256 _checkpointId, address[] _excluded) public onlyOwner { - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); - require(_expiry > _maturity, "Expiry is before maturity"); - require(_expiry > now, "Expiry is in the past"); - require(_amount > 0, "No dividend sent"); - require(_token != address(0), "0x not valid token"); - require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId(), "Invalid checkpoint"); - require(IERC20(_token).transferFrom(msg.sender, address(this), _amount), "Unable to transfer tokens for dividend"); - uint256 dividendIndex = dividends.length; - uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); - uint256 excludedSupply = 0; - for (uint256 i = 0; i < _excluded.length; i++) { - excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[i], _checkpointId)); - } - dividends.push( - Dividend( - _checkpointId, - now, - _maturity, - _expiry, - _amount, - 0, - currentSupply.sub(excludedSupply), - false, - 0, - 0 - ) - ); - for (uint256 j = 0; j < _excluded.length; j++) { - dividends[dividends.length - 1].dividendExcluded[_excluded[j]] = true; - } - dividendTokens[dividendIndex] = _token; - emit ERC20DividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, _token, _amount, currentSupply, 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 { - (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), "Unable to transfer tokens"); - _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 onlyOwner { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); - require(!dividends[_dividendIndex].reclaimed, "Dividend already claimed"); - dividends[_dividendIndex].reclaimed = true; - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); - require(IERC20(dividendTokens[_dividendIndex]).transfer(msg.sender, remainingAmount), "Unable to transfer tokens"); - emit ERC20DividendReclaimed(msg.sender, _dividendIndex, dividendTokens[_dividendIndex], remainingAmount); - } - - /** - * @notice Allows issuer to withdraw withheld tax - * @param _dividendIndex Dividend to withdraw from - */ - function withdrawWithholding(uint256 _dividendIndex) external onlyOwner { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); - dividend.dividendWithheldReclaimed = dividend.dividendWithheld; - require(IERC20(dividendTokens[_dividendIndex]).transfer(msg.sender, remainingWithheld), "Unable to transfer tokens"); - emit ERC20DividendWithholdingWithdrawn(msg.sender, _dividendIndex, dividendTokens[_dividendIndex], remainingWithheld); - } - -} +pragma solidity ^0.4.24; + +import "./DividendCheckpoint.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 onlyOwner { + 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 onlyOwner { + _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 onlyOwner { + 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 + onlyOwner + { + _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 + { + require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); + require(_expiry > _maturity, "Expiry is before maturity"); + require(_expiry > now, "Expiry is in the past"); + require(_amount > 0, "No dividend sent"); + require(_token != address(0), "0x not valid token"); + require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId(), "Invalid checkpoint"); + require(IERC20(_token).transferFrom(msg.sender, address(this), _amount), "Unable to transfer tokens for dividend"); + require(_name[0] != 0); + uint256 dividendIndex = dividends.length; + uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); + uint256 excludedSupply = 0; + for (uint256 i = 0; i < _excluded.length; i++) { + excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[i], _checkpointId)); + } + dividends.push( + Dividend( + _checkpointId, + now, + _maturity, + _expiry, + _amount, + 0, + currentSupply.sub(excludedSupply), + false, + 0, + 0, + _name + ) + ); + for (uint256 j = 0; j < _excluded.length; j++) { + dividends[dividends.length - 1].dividendExcluded[_excluded[j]] = true; + } + 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 { + 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), "Unable to transfer tokens"); + _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 onlyOwner { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); + require(!dividends[_dividendIndex].reclaimed, "Dividend already claimed"); + dividends[_dividendIndex].reclaimed = true; + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); + require(IERC20(dividendTokens[_dividendIndex]).transfer(msg.sender, remainingAmount), "Unable to transfer tokens"); + emit ERC20DividendReclaimed(msg.sender, _dividendIndex, dividendTokens[_dividendIndex], remainingAmount); + } + + /** + * @notice Allows issuer to withdraw withheld tax + * @param _dividendIndex Dividend to withdraw from + */ + function withdrawWithholding(uint256 _dividendIndex) external onlyOwner { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); + dividend.dividendWithheldReclaimed = dividend.dividendWithheld; + require(IERC20(dividendTokens[_dividendIndex]).transfer(msg.sender, remainingWithheld), "Unable to transfer tokens"); + emit ERC20DividendWithholdingWithdrawn(msg.sender, _dividendIndex, dividendTokens[_dividendIndex], remainingWithheld); + } + +} diff --git a/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol b/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol index d789ad5e3..f71f601c6 100644 --- a/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol +++ b/contracts/modules/Checkpoint/ERC20DividendCheckpointFactory.sol @@ -1,100 +1,100 @@ -pragma solidity ^0.4.24; - -import "./ERC20DividendCheckpoint.sol"; -import "../ModuleFactory.sol"; - -/** - * @title Factory for deploying ERC20DividendCheckpoint module - */ -contract ERC20DividendCheckpointFactory 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 = "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)); - } - - /** - * @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 erc20DividendCheckpoint = new ERC20DividendCheckpoint(msg.sender, address(polyToken)); - emit GenerateModuleFromFactory(erc20DividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); - return erc20DividendCheckpoint; - } - - /** - * @notice Type of the Module factory - */ - function getType() public view returns(uint8) { - return 4; - } - - /** - * @notice Get the name of the Module - */ - function getName() public view returns(bytes32) { - return name; - } - - /** - * @notice Get the description of the Module - */ - function getDescription() public view returns(string) { - return description; - } - - /** - * @notice Get the title of the Module - */ - function getTitle() public 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() public view returns(string) { - return "Create a ERC20 dividend which will be paid out to token holders proportional to their balances at the point the dividend is created"; - } - - /** - * @notice Get the tags related to the module factory - */ - function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](3); - availableTags[0] = "ERC20"; - availableTags[1] = "Dividend"; - availableTags[2] = "Checkpoint"; - return availableTags; - } -} +pragma solidity ^0.4.24; + +import "./ERC20DividendCheckpoint.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying ERC20DividendCheckpoint module + */ +contract ERC20DividendCheckpointFactory 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 = "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)); + } + + /** + * @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 erc20DividendCheckpoint = new ERC20DividendCheckpoint(msg.sender, address(polyToken)); + emit GenerateModuleFromFactory(erc20DividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); + return erc20DividendCheckpoint; + } + + /** + * @notice Type of the Module factory + */ + function getType() public view returns(uint8) { + return 4; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() public view returns(string) { + return description; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() public 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() public view returns(string) { + return "Create a ERC20 dividend which will be paid out to token holders proportional to their balances at the point the dividend is created"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](3); + availableTags[0] = "ERC20"; + availableTags[1] = "Dividend"; + availableTags[2] = "Checkpoint"; + return availableTags; + } +} diff --git a/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol b/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol index 1d17e1abb..d8ecb2e9a 100644 --- a/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/EtherDividendCheckpoint.sol @@ -1,147 +1,183 @@ -pragma solidity ^0.4.24; - -import "./DividendCheckpoint.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); - 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 - */ - function createDividend(uint256 _maturity, uint256 _expiry) payable external onlyOwner { - createDividendWithExclusions(_maturity, _expiry, excluded); - } - - /** - * @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 - */ - function createDividendWithCheckpoint(uint256 _maturity, uint256 _expiry, uint256 _checkpointId) payable external onlyOwner { - createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, excluded); - } - - /** - * @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 - */ - function createDividendWithExclusions(uint256 _maturity, uint256 _expiry, address[] _excluded) payable public onlyOwner { - uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); - createDividendWithCheckpointAndExclusions(_maturity, _expiry, checkpointId, _excluded); - } - - /** - * @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 - */ - function createDividendWithCheckpointAndExclusions(uint256 _maturity, uint256 _expiry, uint256 _checkpointId, address[] _excluded) payable public onlyOwner { - require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded"); - require(_expiry > _maturity, "Expiry is before maturity"); - require(_expiry > now, "Expiry is in the past"); - require(msg.value > 0, "No dividend sent"); - require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId()); - uint256 dividendIndex = dividends.length; - uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); - uint256 excludedSupply = 0; - for (uint256 i = 0; i < _excluded.length; i++) { - excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[i], _checkpointId)); - } - dividends.push( - Dividend( - _checkpointId, - now, - _maturity, - _expiry, - msg.value, - 0, - currentSupply.sub(excludedSupply), - false, - 0, - 0 - ) - ); - for (uint256 j = 0; j < _excluded.length; j++) { - dividends[dividends.length - 1].dividendExcluded[_excluded[j]] = true; - } - emit EtherDividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, msg.value, currentSupply, 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 { - (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); - _dividend.claimed[_payee] = true; - uint256 claimAfterWithheld = claim.sub(withheld); - if (claimAfterWithheld > 0) { - 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 onlyOwner { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); - require(!dividends[_dividendIndex].reclaimed, "Dividend already claimed"); - Dividend storage dividend = dividends[_dividendIndex]; - dividend.reclaimed = true; - uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); - msg.sender.transfer(remainingAmount); - emit EtherDividendReclaimed(msg.sender, _dividendIndex, remainingAmount); - } - - /** - * @notice Allows issuer to withdraw withheld tax - * @param _dividendIndex Dividend to withdraw from - */ - function withdrawWithholding(uint256 _dividendIndex) external onlyOwner { - require(_dividendIndex < dividends.length, "Incorrect dividend index"); - Dividend storage dividend = dividends[_dividendIndex]; - uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); - dividend.dividendWithheldReclaimed = dividend.dividendWithheld; - msg.sender.transfer(remainingWithheld); - emit EtherDividendWithholdingWithdrawn(msg.sender, _dividendIndex, remainingWithheld); - } - -} +pragma solidity ^0.4.24; + +import "./DividendCheckpoint.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) payable external onlyOwner { + 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) payable external onlyOwner { + _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) payable public onlyOwner { + 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 + ) + payable + public + onlyOwner + { + _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"); + 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; + for (uint256 i = 0; i < _excluded.length; i++) { + excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[i], _checkpointId)); + } + dividends.push( + Dividend( + _checkpointId, + now, + _maturity, + _expiry, + msg.value, + 0, + currentSupply.sub(excludedSupply), + false, + 0, + 0, + _name + ) + ); + for (uint256 j = 0; j < _excluded.length; j++) { + dividends[dividends.length - 1].dividendExcluded[_excluded[j]] = true; + } + 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) { + 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 onlyOwner { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future"); + require(!dividends[_dividendIndex].reclaimed, "Dividend already claimed"); + Dividend storage dividend = dividends[_dividendIndex]; + dividend.reclaimed = true; + uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount); + msg.sender.transfer(remainingAmount); + emit EtherDividendReclaimed(msg.sender, _dividendIndex, remainingAmount); + } + + /** + * @notice Allows issuer to withdraw withheld tax + * @param _dividendIndex Dividend to withdraw from + */ + function withdrawWithholding(uint256 _dividendIndex) external onlyOwner { + require(_dividendIndex < dividends.length, "Incorrect dividend index"); + Dividend storage dividend = dividends[_dividendIndex]; + uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed); + dividend.dividendWithheldReclaimed = dividend.dividendWithheld; + msg.sender.transfer(remainingWithheld); + emit EtherDividendWithholdingWithdrawn(msg.sender, _dividendIndex, remainingWithheld); + } + +} diff --git a/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol b/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol index 146691d82..d1e8ac50b 100644 --- a/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol +++ b/contracts/modules/Checkpoint/EtherDividendCheckpointFactory.sol @@ -1,100 +1,100 @@ -pragma solidity ^0.4.24; - -import "./EtherDividendCheckpoint.sol"; -import "../ModuleFactory.sol"; - -/** - * @title Factory for deploying EtherDividendCheckpoint module - */ -contract EtherDividendCheckpointFactory 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 = "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)); - } - - /** - * @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 ethDividendCheckpoint = new EtherDividendCheckpoint(msg.sender, address(polyToken)); - emit GenerateModuleFromFactory(ethDividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); - return ethDividendCheckpoint; - } - - /** - * @notice Type of the Module factory - */ - function getType() public view returns(uint8) { - return 4; - } - - /** - * @notice Get the name of the Module - */ - function getName() public view returns(bytes32) { - return name; - } - - /** - * @notice Get the description of the Module - */ - function getDescription() public view returns(string) { - return description; - } - - /** - * @notice Get the title of the Module - */ - function getTitle() public 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() public view returns(string) { - return "Create a dividend which will be paid out to token holders proportional to their balances at the point the dividend is created"; - } - - /** - * @notice Get the tags related to the module factory - */ - function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](3); - availableTags[0] = "ETH"; - availableTags[1] = "Checkpoint"; - availableTags[2] = "Dividend"; - return availableTags; - } -} +pragma solidity ^0.4.24; + +import "./EtherDividendCheckpoint.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying EtherDividendCheckpoint module + */ +contract EtherDividendCheckpointFactory 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 = "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)); + } + + /** + * @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 ethDividendCheckpoint = new EtherDividendCheckpoint(msg.sender, address(polyToken)); + emit GenerateModuleFromFactory(ethDividendCheckpoint, getName(), address(this), msg.sender, setupCost, now); + return ethDividendCheckpoint; + } + + /** + * @notice Type of the Module factory + */ + function getType() public view returns(uint8) { + return 4; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() public view returns(string) { + return description; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() public 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() public view returns(string) { + return "Create a dividend which will be paid out to token holders proportional to their balances at the point the dividend is created"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](3); + availableTags[0] = "ETH"; + availableTags[1] = "Checkpoint"; + availableTags[2] = "Dividend"; + return availableTags; + } +} diff --git a/contracts/modules/Checkpoint/ICheckpoint.sol b/contracts/modules/Checkpoint/ICheckpoint.sol index 214093131..3e567ba7b 100644 --- a/contracts/modules/Checkpoint/ICheckpoint.sol +++ b/contracts/modules/Checkpoint/ICheckpoint.sol @@ -1,8 +1,8 @@ -pragma solidity ^0.4.24; - -/** - * @title Interface to be implemented by all checkpoint modules - */ -interface ICheckpoint { - -} +pragma solidity ^0.4.24; + +/** + * @title Interface to be implemented by all checkpoint modules + */ +interface ICheckpoint { + +} diff --git a/contracts/oracles/MakerDAOOracle.sol b/contracts/oracles/MakerDAOOracle.sol index ab7795007..63d8b73cb 100644 --- a/contracts/oracles/MakerDAOOracle.sol +++ b/contracts/oracles/MakerDAOOracle.sol @@ -13,9 +13,9 @@ contract MakerDAOOracle is IOracle, Ownable { bool public manualOverride; uint256 public manualPrice; - event LogChangeMedianizer(address _newMedianizer, address _oldMedianizer, uint256 _now); - event LogSetManualPrice(uint256 _oldPrice, uint256 _newPrice, uint256 _time); - event LogSetManualOverride(bool _override, uint256 _time); + event ChangeMedianizer(address _newMedianizer, address _oldMedianizer, uint256 _now); + event SetManualPrice(uint256 _oldPrice, uint256 _newPrice, uint256 _time); + event SetManualOverride(bool _override, uint256 _time); /** * @notice Creates a new Maker based oracle @@ -35,7 +35,7 @@ contract MakerDAOOracle is IOracle, Ownable { */ function changeMedianier(address _medianizer) public onlyOwner { require(_medianizer != address(0), "0x not allowed"); - emit LogChangeMedianizer(_medianizer, medianizer, now); + emit ChangeMedianizer(_medianizer, medianizer, now); medianizer = _medianizer; } @@ -78,7 +78,7 @@ contract MakerDAOOracle is IOracle, Ownable { * @param _price Price to set */ function setManualPrice(uint256 _price) public onlyOwner { - emit LogSetManualPrice(manualPrice, _price, now); + emit SetManualPrice(manualPrice, _price, now); manualPrice = _price; } @@ -88,7 +88,7 @@ contract MakerDAOOracle is IOracle, Ownable { */ function setManualOverride(bool _override) public onlyOwner { manualOverride = _override; - emit LogSetManualOverride(_override, now); + emit SetManualOverride(_override, now); } } diff --git a/contracts/oracles/PolyOracle.sol b/contracts/oracles/PolyOracle.sol index e5328ea74..005d1677b 100644 --- a/contracts/oracles/PolyOracle.sol +++ b/contracts/oracles/PolyOracle.sol @@ -26,10 +26,10 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { bool public freezeOracle; - event LogPriceUpdated(uint256 _price, uint256 _oldPrice, bytes32 _queryId, uint256 _time); - event LogNewOraclizeQuery(uint256 _time, bytes32 _queryId, string _query); - event LogAdminSet(address _admin, bool _valid, uint256 _time); - event LogStalePriceUpdate(bytes32 _queryId, uint256 _time, string _result); + 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 StalePriceUpdate(bytes32 _queryId, uint256 _time, string _result); modifier isAdminOrOwner { require(admin[msg.sender] || msg.sender == owner, "Address is not admin or owner"); @@ -55,7 +55,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { require(!ignoreRequestIds[_requestId], "Ignoring requestId"); if (requestIds[_requestId] < latestUpdate) { // Result is stale, probably because it was received out of order - emit LogStalePriceUpdate(_requestId, requestIds[_requestId], _result); + emit StalePriceUpdate(_requestId, requestIds[_requestId], _result); return; } require(requestIds[_requestId] >= latestUpdate, "Result is stale"); @@ -67,7 +67,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { require(newPOLYUSD >= POLYUSD.sub(bound), "Result is too small"); } latestUpdate = requestIds[_requestId]; - emit LogPriceUpdated(newPOLYUSD, POLYUSD, _requestId, latestUpdate); + emit PriceUpdated(newPOLYUSD, POLYUSD, _requestId, latestUpdate); POLYUSD = newPOLYUSD; } @@ -83,7 +83,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { requestId = oraclize_query(oracleQueryType, oracleURL, gasLimit); requestIds[requestId] = now; maximumScheduledUpdated = now; - emit LogNewOraclizeQuery(now, requestId, oracleURL); + emit NewOraclizeQuery(now, requestId, oracleURL); } else { require(oraclize_getPrice(oracleQueryType, gasLimit) * _times.length <= address(this).balance, "Insufficient Funds"); for (uint256 i = 0; i < _times.length; i++) { @@ -93,7 +93,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { if (maximumScheduledUpdated < requestIds[requestId]) { maximumScheduledUpdated = requestIds[requestId]; } - emit LogNewOraclizeQuery(_times[i], requestId, oracleURL); + emit NewOraclizeQuery(_times[i], requestId, oracleURL); } } if (latestScheduledUpdate < maximumScheduledUpdated) { @@ -117,7 +117,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { uint256 scheduledTime = _startTime + (i * _interval); requestId = oraclize_query(scheduledTime, oracleQueryType, oracleURL, gasLimit); requestIds[requestId] = scheduledTime; - emit LogNewOraclizeQuery(scheduledTime, requestId, oracleURL); + emit NewOraclizeQuery(scheduledTime, requestId, oracleURL); } if (latestScheduledUpdate < requestIds[requestId]) { latestScheduledUpdate = requestIds[requestId]; @@ -129,7 +129,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { * @param _price POLYUSD price */ function setPOLYUSD(uint256 _price) onlyOwner public { - emit LogPriceUpdated(_price, POLYUSD, 0, now); + emit PriceUpdated(_price, POLYUSD, 0, now); POLYUSD = _price; latestUpdate = now; } @@ -220,7 +220,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable { */ function setAdmin(address _admin, bool _valid) onlyOwner public { admin[_admin] = _valid; - emit LogAdminSet(_admin, _valid, now); + emit AdminSet(_admin, _valid, now); } /** diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 137ffc30b..8d73a9e00 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -8,7 +8,6 @@ import "../interfaces/IModuleRegistry.sol"; import "../interfaces/IFeatureRegistry.sol"; import "../modules/TransferManager/ITransferManager.sol"; import "../modules/PermissionManager/IPermissionManager.sol"; -import "../interfaces/ITokenBurner.sol"; import "../RegistryUpdater.sol"; import "../libraries/Util.sol"; import "openzeppelin-solidity/contracts/ReentrancyGuard.sol"; @@ -42,8 +41,10 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr uint8 public constant PERMISSIONMANAGER_KEY = 1; uint8 public constant TRANSFERMANAGER_KEY = 2; - uint8 public constant STO_KEY = 3; + uint8 public constant MINT_KEY = 3; uint8 public constant CHECKPOINT_KEY = 4; + uint8 public constant BURN_KEY = 5; + uint256 public granularity; // Value of current checkpoint @@ -55,9 +56,6 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // List of token holders address[] public investors; - // Reference to token burner contract - ITokenBurner public tokenBurner; - // Use to temporarily halt all transactions bool public transfersFrozen; @@ -99,6 +97,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // Records added module names - module list should be order agnostic! mapping (bytes32 => address[]) names; + // List of investors mapping (address => bool) public investorListed; // Emit at the time when module get added @@ -133,15 +132,16 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // Change the STR address in the event of a upgrade event ChangeSTRAddress(address indexed _oldAddress, address indexed _newAddress); // Events to log minting and burning - event Minted(address indexed to, uint256 amount); - event Burnt(address indexed _burner, uint256 _value); + event Minted(address indexed _to, uint256 _value); + event Burnt(address indexed _from, uint256 _value); // 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 _amount, bool _verifyTransfer, bytes _data); + 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) { + function _isModule(address _module, uint8 _type) internal view returns (bool) { require(modulesToData[_module].module == _module, "Address mismatch"); require(modulesToData[_module].moduleType == _type, "Type mismatch"); require(!modulesToData[_module].isArchived, "Module archived"); @@ -150,7 +150,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr // Require msg.sender to be the specified module type modifier onlyModule(uint8 _type) { - require(isModule(msg.sender, _type)); + require(_isModule(msg.sender, _type)); _; } @@ -159,18 +159,18 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr if (msg.sender == owner) { _; } else { - require(isModule(msg.sender, _type)); + require(_isModule(msg.sender, _type)); _; } } - modifier checkGranularity(uint256 _amount) { - require(_amount % granularity == 0, "Unable to modify token balances at this granularity"); + modifier checkGranularity(uint256 _value) { + require(_value % granularity == 0, "Incorrect granularity"); _; } modifier isMintingAllowed() { - require(!mintingFrozen, "Minting is permanently frozen"); + require(!mintingFrozen, "Minting is frozen"); _; } @@ -183,8 +183,8 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @notice Revert if called by account which is not a controller */ modifier onlyController() { - require(msg.sender == controller); - require(!controllerDisabled); + require(msg.sender == controller, "Caller not controller"); + require(!controllerDisabled, "Controller disabled"); _; } @@ -250,14 +250,14 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr uint8 moduleType = moduleFactory.getType(); /* require(modules[moduleType].length < MAX_MODULES, "Limit of MAX MODULES is reached"); */ uint256 moduleCost = moduleFactory.getSetupCost(); - require(moduleCost <= _maxCost, "Max Cost is always be greater than module cost"); + require(moduleCost <= _maxCost, "Module cost too high"); //Approve fee for module - require(ERC20(polyToken).approve(_moduleFactory, moduleCost), "Not able to approve the module cost"); + require(ERC20(polyToken).approve(_moduleFactory, moduleCost), "Insufficient funds for cost"); //Creates instance of module from factory address module = moduleFactory.deploy(_data); require(modulesToData[module].module == address(0), "Module already exists"); //Approve ongoing budget - require(ERC20(polyToken).approve(module, _budget), "Not able to approve the budget"); + require(ERC20(polyToken).approve(module, _budget), "Insufficient funds for budget"); //Add to SecurityToken module map bytes32 moduleName = moduleFactory.getName(); modulesToData[module] = ModuleData(moduleName, module, _moduleFactory, false, moduleType, modules[moduleType].length, names[moduleName].length); @@ -272,7 +272,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _module address of module to archive */ function archiveModule(address _module) external onlyOwner { - require(!modulesToData[_module].isArchived, "Module not unarchived"); + require(!modulesToData[_module].isArchived, "Module already archived"); require(modulesToData[_module].module != address(0), "Module missing"); emit ModuleArchived(modulesToData[_module].moduleType, _module, now); modulesToData[_module].isArchived = true; @@ -283,7 +283,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _module address of module to unarchive */ function unarchiveModule(address _module) external onlyOwner { - require(modulesToData[_module].isArchived, "Module not archived"); + require(modulesToData[_module].isArchived, "Module already unarchived"); emit ModuleUnarchived(modulesToData[_module].moduleType, _module, now); modulesToData[_module].isArchived = false; } @@ -361,10 +361,10 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr /** * @notice allows the owner to withdraw unspent POLY stored by them on the ST. * @dev Owner can transfer POLY to the ST which will be used to pay for modules that require a POLY fee. - * @param _amount amount of POLY to withdraw + * @param _value amount of POLY to withdraw */ - function withdrawPoly(uint256 _amount) external onlyOwner { - require(ERC20(polyToken).transfer(owner, _amount), "In-sufficient balance"); + function withdrawPoly(uint256 _value) external onlyOwner { + require(ERC20(polyToken).transfer(owner, _value), "Insufficient balance"); } /** @@ -526,10 +526,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @return bool success */ function transfer(address _to, uint256 _value) public returns (bool success) { - _adjustInvestorCount(msg.sender, _to, _value); - require(_verifyTransfer(msg.sender, _to, _value, true), "Transfer is not valid"); - _adjustBalanceCheckpoints(msg.sender); - _adjustBalanceCheckpoints(_to); + require(_updateTransfer(msg.sender, _to, _value), "Transfer is not valid"); require(super.transfer(_to, _value)); return true; } @@ -541,13 +538,17 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _value value of transfer * @return bool success */ - function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { + function transferFrom(address _from, address _to, uint256 _value) public returns(bool) { + require(_updateTransfer(_from, _to, _value), "Transfer is not valid"); + require(super.transferFrom(_from, _to, _value)); + return true; + } + + function _updateTransfer(address _from, address _to, uint256 _value) internal returns(bool) { _adjustInvestorCount(_from, _to, _value); - require(_verifyTransfer(_from, _to, _value, true), "Transfer is not valid"); _adjustBalanceCheckpoints(_from); _adjustBalanceCheckpoints(_to); - require(super.transferFrom(_from, _to, _value)); - return true; + return _verifyTransfer(_from, _to, _value, true); } /** @@ -555,11 +556,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @dev TransferManager module has a key of 2 * @param _from sender of transfer * @param _to receiver of transfer - * @param _amount value of transfer + * @param _value value of transfer * @param _isTransfer whether transfer is being executed * @return bool */ - function _verifyTransfer(address _from, address _to, uint256 _amount, bool _isTransfer) internal checkGranularity(_amount) returns (bool) { + function _verifyTransfer(address _from, address _to, uint256 _value, bool _isTransfer) internal checkGranularity(_value) returns (bool) { if (!transfersFrozen) { if (modules[TRANSFERMANAGER_KEY].length == 0) { return true; @@ -573,7 +574,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr module = modules[TRANSFERMANAGER_KEY][i]; if (!modulesToData[module].isArchived) { unarchived = true; - ITransferManager.Result valid = ITransferManager(module).verifyTransfer(_from, _to, _amount, _isTransfer); + ITransferManager.Result valid = ITransferManager(module).verifyTransfer(_from, _to, _value, _isTransfer); if (valid == ITransferManager.Result.INVALID) { isInvalid = true; } @@ -596,11 +597,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @dev TransferManager module has a key of 2 * @param _from sender of transfer * @param _to receiver of transfer - * @param _amount value of transfer + * @param _value value of transfer * @return bool */ - function verifyTransfer(address _from, address _to, uint256 _amount) public returns (bool) { - return _verifyTransfer(_from, _to, _amount, false); + function verifyTransfer(address _from, address _to, uint256 _value) public returns (bool) { + return _verifyTransfer(_from, _to, _value, false); } /** @@ -616,19 +617,19 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @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 _amount Number of tokens be minted + * @param _value Number of tokens be minted * @return success */ - function mint(address _investor, uint256 _amount) public onlyModuleOrOwner(STO_KEY) checkGranularity(_amount) isMintingAllowed() returns (bool success) { + function mint(address _investor, uint256 _value) public onlyModuleOrOwner(MINT_KEY) checkGranularity(_value) isMintingAllowed() returns (bool success) { require(_investor != address(0), "Investor address should not be 0x"); - _adjustInvestorCount(address(0), _investor, _amount); - require(_verifyTransfer(address(0), _investor, _amount, true), "Transfer is not valid"); + _adjustInvestorCount(address(0), _investor, _value); + require(_verifyTransfer(address(0), _investor, _value, true), "Transfer is not valid"); _adjustBalanceCheckpoints(_investor); _adjustTotalSupplyCheckpoints(); - totalSupply_ = totalSupply_.add(_amount); - balances[_investor] = balances[_investor].add(_amount); - emit Minted(_investor, _amount); - emit Transfer(address(0), _investor, _amount); + totalSupply_ = totalSupply_.add(_value); + balances[_investor] = balances[_investor].add(_value); + emit Minted(_investor, _value); + emit Transfer(address(0), _investor, _value); return true; } @@ -636,13 +637,13 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @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 _amounts A list of number of tokens get minted and transfer to corresponding address of the investor from _investor[] list + * @param _values A list of number of tokens get minted and transfer to corresponding address of the investor from _investor[] list * @return success */ - function mintMulti(address[] _investors, uint256[] _amounts) external onlyModuleOrOwner(STO_KEY) returns (bool success) { - require(_investors.length == _amounts.length, "Mis-match in the length of the arrays"); + function mintMulti(address[] _investors, uint256[] _values) external onlyModuleOrOwner(MINT_KEY) returns (bool success) { + require(_investors.length == _values.length, "Incorrect inputs"); for (uint256 i = 0; i < _investors.length; i++) { - mint(_investors[i], _amounts[i]); + mint(_investors[i], _values[i]); } return true; } @@ -666,35 +667,39 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr return true; } } + + return false; } - /** - * @notice used to set the token Burner address. It only be called by the owner - * @param _tokenBurner Address of the token burner contract - */ - function setTokenBurner(address _tokenBurner) external onlyOwner { - tokenBurner = ITokenBurner(_tokenBurner); + function _burn(address _from, uint256 _value) internal returns (bool) { + require(_value <= balances[_from], "Value too high"); + require(_updateTransfer(_from, address(0), _value), "Burn is not valid"); + _adjustTotalSupplyCheckpoints(); + balances[_from] = balances[_from].sub(_value); + totalSupply_ = totalSupply_.sub(_value); + emit Burnt(_from, _value); + emit Transfer(_from, address(0), _value); + return true; } /** * @notice Burn function used to burn the securityToken - * @param _value No. of token that get burned - */ - function burn(uint256 _value) checkGranularity(_value) public returns (bool) { - _adjustInvestorCount(msg.sender, address(0), _value); - require(tokenBurner != address(0), "Token Burner contract address is not set yet"); - require(_verifyTransfer(msg.sender, address(0), _value, true), "Transfer is not valid"); - require(_value <= balances[msg.sender], "Value should no be greater than the balance of msg.sender"); - _adjustBalanceCheckpoints(msg.sender); - _adjustTotalSupplyCheckpoints(); - // no need to require value <= totalSupply, since that would imply the - // sender's balance is greater than the totalSupply, which *should* be an assertion failure + * @param _value No. of tokens that get burned + */ + function burn(uint256 _value) checkGranularity(_value) onlyModule(BURN_KEY) public returns (bool) { + require(_burn(msg.sender, _value), "Invalid burn"); + return true; + } - balances[msg.sender] = balances[msg.sender].sub(_value); - require(tokenBurner.burn(msg.sender, _value), "Token burner process is not validated"); - totalSupply_ = totalSupply_.sub(_value); - emit Burnt(msg.sender, _value); - emit Transfer(msg.sender, address(0), _value); + /** + * @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 + */ + function burnFrom(address _from, uint256 _value) checkGranularity(_value) onlyModule(BURN_KEY) public returns (bool) { + require(_value <= allowed[_from][msg.sender], "Value too high"); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + require(_burn(_from, _value), "Invalid burn"); return true; } @@ -807,13 +812,9 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _data data attached to the transfer by controller to emit in event */ function forceTransfer(address _from, address _to, uint256 _value, bytes _data) public onlyController returns(bool) { - _adjustInvestorCount(_from, _to, _value); - bool verified = _verifyTransfer(_from, _to, _value, true); - _adjustBalanceCheckpoints(_from); - _adjustBalanceCheckpoints(_to); - require(_to != address(0)); require(_value <= balances[_from]); + bool verified = _updateTransfer(_from, _to, _value); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); @@ -822,6 +823,24 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr return true; } + /** + * @notice Use 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 attached to the transfer by controller to emit in event + */ + function forceBurn(address _from, uint256 _value, bytes _data) public onlyController returns(bool) { + require(_value <= balances[_from], "Value too high"); + bool verified = _updateTransfer(_from, address(0), _value); + _adjustTotalSupplyCheckpoints(); + balances[_from] = balances[_from].sub(_value); + totalSupply_ = totalSupply_.sub(_value); + emit ForceBurn(msg.sender, _from, _value, verified, _data); + emit Burnt(_from, _value); + emit Transfer(_from, address(0), _value); + return true; + } + /** * @notice Use to get the version of the securityToken */ diff --git a/demo/checkpoint/ethExplorer.js b/demo/checkpoint/ethExplorer.js deleted file mode 100644 index a32bd6b18..000000000 --- a/demo/checkpoint/ethExplorer.js +++ /dev/null @@ -1,543 +0,0 @@ -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 BigNumber = require('bignumber.js') -var chalk = require('chalk'); -var common = require('../common/common_functions'); - -var contracts = require("../helpers/contract_addresses"); -let tickerRegistryAddress = contracts.tickerRegistryAddress(); -let securityTokenRegistryAddress = contracts.securityTokenRegistryAddress(); -let cappedSTOFactoryAddress = contracts.cappedSTOFactoryAddress(); -let etherDividendCheckpointFactoryAddress = contracts.etherDividendCheckpointFactoryAddress(); - -let tickerRegistryABI; -let securityTokenRegistryABI; -let securityTokenABI; -let cappedSTOABI; -let generalTransferManagerABI; -try{ - tickerRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/TickerRegistry.json').toString()).abi; - securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityTokenRegistry.json').toString()).abi; - securityTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityToken.json').toString()).abi; - cappedSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTO.json').toString()).abi; - generalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralTransferManager.json').toString()).abi; - etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpoint.json').toString()).abi; -}catch(err){ - console.log('\x1b[31m%s\x1b[0m',"Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); - return; -} - - -const DEFAULT_GAS_PRICE = 80000000000; -const Web3 = require('web3'); - -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")); -} - -let tokenSymbol; -let securityToken; - -async function executeApp() { - - accounts = await web3.eth.getAccounts(); - Issuer = accounts[0]; - - setup(); - -}; - -async function setup(){ - try { - tickerRegistry = new web3.eth.Contract(tickerRegistryABI,tickerRegistryAddress); - tickerRegistry.setProvider(web3.currentProvider); - securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI,securityTokenRegistryAddress); - securityTokenRegistry.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."); - return; - } - - start_explorer(); - -} - -async function start_explorer(){ - - let tokenDeployed = false; - let tokenDeployedAddress; - if(!tokenSymbol){ - tokenSymbol = readlineSync.question('Enter the token symbol: '); - // Let's check if token has already been deployed, if it has, skip to STO - await securityTokenRegistry.methods.getSecurityTokenAddress(tokenSymbol).call({from: Issuer}, function(error, result){ - if(result != "0x0000000000000000000000000000000000000000"){ - securityToken = new web3.eth.Contract(securityTokenABI,result); - } - }); - } - - let checkpointNum = await securityToken.methods.currentCheckpointId().call({ from: Issuer }); - console.log("Token is at checkpoint:",checkpointNum); - - // Get the GTM - await securityToken.methods.getModule(2, 0).call({ from: Issuer }, function (error, result) { - generalTransferManagerAddress = result[1]; - }); - generalTransferManager = new web3.eth.Contract(generalTransferManagerABI, generalTransferManagerAddress); - generalTransferManager.setProvider(web3.currentProvider); - - await securityToken.methods.getModuleByName(4, web3.utils.toHex("EtherDividendCheckpoint")).call({ from: Issuer }, function (error, result) { - etherDividendCheckpointAddress = result[1]; - console.log("Dividends module address is:",etherDividendCheckpointAddress); - if(etherDividendCheckpointAddress != "0x0000000000000000000000000000000000000000"){ - etherDividendCheckpoint = new web3.eth.Contract(etherDividendCheckpointABI, etherDividendCheckpointAddress); - etherDividendCheckpoint.setProvider(web3.currentProvider); - } - }); - - let options = ['Mint tokens', 'Transfer tokens', 'Explore account at checkpoint', - 'Explore total supply at checkpoint', 'Create checkpoint', 'Issue Dividends', - 'Issue Dividends at a checkpoint', 'Tax Withholding', 'Push dividends to account', - 'Pull dividends to account', 'Explore ETH balance', 'Reclaimed dividends after expiry', 'Exit']; - let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false}); - console.log("Selected:",options[index]); - switch(index){ - case 0: - let _to = readlineSync.question('Enter beneficiary of minting: '); - let _amount = readlineSync.question('Enter amount of tokens to mint: '); - await mintTokens(_to,_amount); - break; - case 1: - let _to2 = readlineSync.question('Enter beneficiary of tranfer: '); - let _amount2 = readlineSync.question('Enter amount of tokens to transfer: '); - await transferTokens(_to2,_amount2); - break; - case 2: - let _address = readlineSync.question('Enter address to explore: '); - let _checkpoint = readlineSync.question('At checkpoint: '); - await exploreAddress(_address,_checkpoint); - break; - case 3: - let _checkpoint2 = readlineSync.question('Explore total supply at checkpoint: '); - await exploreTotalSupply(_checkpoint2); - break; - case 4: - //Create new checkpoint - await securityToken.methods.createCheckpoint().send({ from: Issuer}); - break; - case 5: - //Create dividends - let ethDividend = readlineSync.question('How much eth would you like to distribute to token holders?: '); - await createDividends(ethDividend); - break; - case 6: - //Create dividends - let _ethDividend = readlineSync.question('How much eth would you like to distribute to token holders?: '); - let _checkpointId = readlineSync.question(`Enter the checkpoint on which you want to distribute dividend: `); - let currentCheckpointId = await securityToken.methods.currentCheckpointId().call(); - if (currentCheckpointId >= _checkpointId) { - await createDividendWithCheckpoint(_ethDividend, _checkpointId); - } else { - console.log(`Future checkpoint are not allowed to create the dividends`); - } - break; - case 7: - await withholdingTax(); - break; - case 8: - //Create dividends - let _checkpoint3 = readlineSync.question('Distribute dividends at checkpoint: '); - let _address2 = readlineSync.question('Enter address to push dividends to (ex- add1,add2,add3,...): '); - await pushDividends(_checkpoint3,_address2); - break; - case 9: - let _checkpoint7 = readlineSync.question('Distribute dividends at checkpoint: '); - await pullDividends(_checkpoint7); - break; - case 10: - //explore eth balance - let _checkpoint4 = readlineSync.question('Enter checkpoint to explore: '); - let _address3 = readlineSync.question('Enter address to explore: '); - let _dividendIndex = await etherDividendCheckpoint.methods.getDividendIndex(_checkpoint4).call(); - if (_dividendIndex.length == 1) { - let div = await etherDividendCheckpoint.methods.dividends(_dividendIndex[0]).call(); - let res = await etherDividendCheckpoint.methods.calculateDividend(_dividendIndex[0],_address3).call({ from: Issuer}); - let claim = new BigNumber(res[0]); - let withheld = new BigNumber(res[1]); - let percent = withheld.dividedBy(claim).times(100); - console.log(` - ETH Balance: ${web3.utils.fromWei((await web3.eth.getBalance(_address3)).toString(),"ether")} ETH - Dividend total size: ${web3.utils.fromWei((div.amount).toString(),"ether")} ETH - Dividends owed to investor at checkpoint ${_checkpoint4}: ${web3.utils.fromWei((claim).toString(),"ether")} ETH - Dividends withheld at checkpoint ${_checkpoint4}: ${web3.utils.fromWei((withheld).toString(),"ether")} ETH - Tax withholding percentage: ${percent}% - `) - } else { - console.log("Sorry Future checkpoints are not allowed"); - } - break; - case 11: - let _checkpoint5 = readlineSync.question('Enter the checkpoint to explore: '); - await reclaimedDividend(_checkpoint5); - break; - case 12: - process.exit(); - } - - //Restart - start_explorer(); - -} - -async function createDividends(ethDividend){ - // Get the Dividends module - await securityToken.methods.getModuleByName(4, web3.utils.toHex("EtherDividendCheckpoint")).call({ from: Issuer }, function (error, result) { - etherDividendCheckpointAddress = result[1]; - }); - if(etherDividendCheckpointAddress != "0x0000000000000000000000000000000000000000"){ - etherDividendCheckpoint = new web3.eth.Contract(etherDividendCheckpointABI, etherDividendCheckpointAddress); - etherDividendCheckpoint.setProvider(web3.currentProvider); - }else{ - let addModuleAction = securityToken.methods.addModule(etherDividendCheckpointFactoryAddress, web3.utils.fromAscii('', 16), 0, 0); - let GAS = await common.estimateGas(addModuleAction, Issuer, 1.2); - await addModuleAction.send({ from: Issuer, gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! The transaction was successfully completed. - Module deployed at address: ${receipt.events.LogModuleAdded.returnValues._module} - Review it on Etherscan. - TxHash: ${receipt.transactionHash}\n` - ); - - etherDividendCheckpoint = new web3.eth.Contract(etherDividendCheckpointABI, receipt.events.LogModuleAdded.returnValues._module); - etherDividendCheckpoint.setProvider(web3.currentProvider); - }) - .on('error', console.error); - } - - let time = (await web3.eth.getBlock('latest')).timestamp; - let expiryTime = readlineSync.question('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = '+(time+duration.minutes(10))+' ): '); - let blacklist = readlineSync.question('Enter an address to blacklist from dividend distribution: '); - if(expiryTime == "") expiryTime = time+duration.minutes(10); - //Send eth dividends - let createDividendAction = etherDividendCheckpoint.methods.createDividend(time, expiryTime, [blacklist]); - GAS = await common.estimateGas(createDividendAction, Issuer, 1.2, web3.utils.toWei(ethDividend,"ether")); - await createDividendAction.send({ from: Issuer, value: web3.utils.toWei(ethDividend,"ether"), gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - ${receipt.events} - TxHash: ${receipt.transactionHash}\n` - ); - }) -} - -async function withholdingTax() { - let GAS - let options = ['Set a % to withhold from the dividends sent to an address', 'Withdraw all withheld dividends', 'Return to main menu']; - let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false}); - console.log("Selected:",options[index]); - switch (index) { - case 0: - let addressT = readlineSync.question('Enter the address of the investor for which to withhold dividends: '); - let percentT = readlineSync.question('Enter the percentage of dividends to withhold (number between 0-100): '); - percentT = web3.utils.toWei((percentT * 10).toString(), 'milli'); - GAS = Math.round(1.2 * (await etherDividendCheckpoint.methods.setWithholdingFixed([addressT], percentT).estimateGas({from: Issuer}))); - console.log(chalk.black.bgYellowBright(`---- Transaction executed: setWithholdingFixed - Gas limit provided: ${GAS} ----`)); - await etherDividendCheckpoint.methods.setWithholdingFixed([addressT], percentT).send({from: Issuer, gas: GAS, gasPrice: DEFAULT_GAS_PRICE }) - .on('receipt', function(receipt){ - console.log(chalk.green(`\nSuccessfully set tax withholding of ${web3.utils.fromWei(percentT, 'milli')/10}% for ${addressT}.`)); - }); - break; - case 1: - // Withdraw - let checkpointId = readlineSync.question('Enter the checkpoint at which to withdraw: '); - let dividendIndex = await etherDividendCheckpoint.methods.getDividendIndex(checkpointId).call(); - if (dividendIndex.length == 1) { - GAS = Math.round(1.2 * (await etherDividendCheckpoint.methods.withdrawWithholding(dividendIndex[0]).estimateGas({from: Issuer}))); - console.log(chalk.black.bgYellowBright(`---- Transaction executed: withdrawWithholding - Gas limit provided: ${GAS} ----`)); - await etherDividendCheckpoint.methods.withdrawWithholding(dividendIndex[0]).send({from: Issuer, gas: GAS, gasPrice: DEFAULT_GAS_PRICE }) - .on('receipt', function(receipt){ - let val = receipt.events.EtherDividendWithholdingWithdrawn.returnValues._withheldAmount; - console.log(chalk.green(`\nSuccessfully withdrew ${val} ETH from checkpoint ${checkpointId} and dividend ${dividendIndex[0]} tax withholding to ${Issuer}.`)); - }); - }else{ - console.log(`\nCheckpoint doesn't exist`); - } - break; - case 2: - return; - break; - } -} - -async function createDividendWithCheckpoint(ethDividend, _checkpointId) { - - // Get the Dividends module - await securityToken.methods.getModuleByName(4, web3.utils.toHex("EtherDividendCheckpoint")).call({ from: Issuer }, function (error, result) { - etherDividendCheckpointAddress = result[1]; - }); - if(etherDividendCheckpointAddress != "0x0000000000000000000000000000000000000000"){ - etherDividendCheckpoint = new web3.eth.Contract(etherDividendCheckpointABI, etherDividendCheckpointAddress); - etherDividendCheckpoint.setProvider(web3.currentProvider); - }else{ - let addModuleAction = securityToken.methods.addModule(etherDividendCheckpointFactoryAddress, web3.utils.fromAscii('', 16), 0, 0); - let GAS = await common.estimateGas(addModuleAction, Issuer, 1.2); - await addModuleAction.send({ from: Issuer, gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! The transaction was successfully completed. - Module deployed at address: ${receipt.events.LogModuleAdded.returnValues._module} - Review it on Etherscan. - TxHash: ${receipt.transactionHash}\n` - ); - - etherDividendCheckpoint = new web3.eth.Contract(etherDividendCheckpointABI, receipt.events.LogModuleAdded.returnValues._module); - etherDividendCheckpoint.setProvider(web3.currentProvider); - }) - .on('error', console.error); - } - - let time = (await web3.eth.getBlock('latest')).timestamp; - let expiryTime = readlineSync.question('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = '+(time+duration.minutes(10))+' ): '); - if(expiryTime == "") expiryTime = time+duration.minutes(10); - let _dividendStatus = await etherDividendCheckpoint.methods.getDividendIndex(_checkpointId).call(); - if (_dividendStatus.length != 1) { - //Send eth dividends - let createDividendWithCheckpointAction = etherDividendCheckpoint.methods.createDividendWithCheckpoint(time, expiryTime, _checkpointId, []); - let GAS = await common.estimateGas(createDividendWithCheckpointAction, Issuer, 1.2, web3.utils.toWei(ethDividend,"ether")); - await createDividendWithCheckpointAction.send({ from: Issuer, value: web3.utils.toWei(ethDividend,"ether"), gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! Dividend is created successfully. - CheckpointId: ${receipt.events.EtherDividendDeposited.returnValues._checkpointId} - TxHash: ${receipt.transactionHash}\n` - ); - }) - .on('error', console.error); - } else { - console.log(chalk.blue(`\nDividends are already distributed at checkpoint '${_checkpointId}'. Not allowed to re-create\n`)); - } -} - -async function pushDividends(checkpoint, account){ - let accs = account.split(','); - let dividend = await etherDividendCheckpoint.methods.getDividendIndex(checkpoint).call(); - if(dividend.length == 1) { - let pushDividendPaymentToAddressesAction = etherDividendCheckpoint.methods.pushDividendPaymentToAddresses(dividend[0], accs); - let GAS = await common.estimateGas(pushDividendPaymentToAddressesAction, Issuer, 1.2); - await pushDividendPaymentToAddressesAction.send({ from: Issuer, gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! Dividends are pushed successfully - TxHash: ${receipt.transactionHash}\n` - ); - }) - } else { - console.log(`Checkpoint is not yet created. Please enter the pre-created checkpoint`); - } - - -} - -async function pullDividends(checkpointId) { - let dividend = await etherDividendCheckpoint.methods.getDividendIndex(checkpointId).call(); - if(dividend.length == 1) { - try { - let pullDividendPaymentAction = etherDividendCheckpoint.methods.pullDividendPayment(dividend[0]); - let GAS = await common.estimateGas(pullDividendPaymentAction, Issuer, 1.2); - await pullDividendPaymentAction.send({ from: Issuer, gas: GAS }) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Amount: ${web3.utils.fromWei(receipt.events.EtherDividendClaimed.returnValues._amount, "ether")} ETH - Payee: ${receipt.events.EtherDividendClaimed.returnValues._payee} - TxHash: ${receipt.transactionHash}\n` - ); - }) - .on('error', console.error); - } catch(error) { - console.log(error.message); - } - } else { - console.log(`Checkpoint is not yet created. Please enter the pre-created checkpoint`); - } -} - -async function exploreAddress(address, checkpoint){ - let balance = await securityToken.methods.balanceOf(address).call({from: Issuer}); - balance = web3.utils.fromWei(balance,"ether"); - console.log("Balance of",address,"is:",balance,"(Using balanceOf)"); - - let balanceAt = await securityToken.methods.balanceOfAt(address,checkpoint).call({from: Issuer}); - balanceAt = web3.utils.fromWei(balanceAt,"ether"); - console.log("Balance of",address,"is:",balanceAt,"(Using balanceOfAt - checkpoint",checkpoint,")"); -} - -async function exploreTotalSupply(checkpoint){ - let totalSupply = await securityToken.methods.totalSupply().call({from: Issuer}); - totalSupply = web3.utils.fromWei(totalSupply,"ether"); - console.log("TotalSupply is:",totalSupply,"(Using totalSupply)"); - - let totalSupplyAt = await securityToken.methods.totalSupplyAt(checkpoint).call({from: Issuer}); - totalSupplyAt = web3.utils.fromWei(totalSupplyAt,"ether"); - console.log("totalSupply is:",totalSupplyAt,"(Using totalSupplyAt - checkpoint",checkpoint,")"); -} - - -async function transferTokens(address, amount){ - - let modifyWhitelistAction = generalTransferManager.methods.modifyWhitelist(address, Math.floor(Date.now()/1000), Math.floor(Date.now()/1000), Math.floor(Date.now()/1000 + 31536000), true); - let GAS = await common.estimateGas(modifyWhitelistAction, Issuer, 1.2); - let whitelistTransaction = await modifyWhitelistAction.send({ from: Issuer, gas: GAS}); - - try{ - let transferAction = securityToken.methods.transfer(address,web3.utils.toWei(amount,"ether")); - GAS = await common.estimateGas(transferAction, Issuer, 1.2); - await transferAction.send({ from: Issuer, gas: GAS}) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! The transaction was successfully completed. - - Account ${receipt.events.Transfer.returnValues.from} - transfered ${web3.utils.fromWei(receipt.events.Transfer.returnValues.value,"ether")} tokens - to account ${receipt.events.Transfer.returnValues.to} - - Review it on Etherscan. - TxHash: ${receipt.transactionHash}\n` - ); - }); - - }catch (err){ - console.log(err); - console.log("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.") - return; - } -} - -async function mintTokens(address, amount){ - let isSTOAttached; - let modifyWhitelistAction = generalTransferManager.methods.modifyWhitelist(address,Math.floor(Date.now()/1000),Math.floor(Date.now()/1000),Math.floor(Date.now()/1000 + 31536000),true); - let GAS = await common.estimateGas(modifyWhitelistAction, Issuer, 1.2); - let whitelistTransaction = await modifyWhitelistAction.send({ from: Issuer, gas: GAS}); - let _flag = await securityToken.methods.finishedIssuerMinting().call(); - await securityToken.methods.getModule(3, 0).call({from: Issuer}, function(error, result) { - isSTOAttached = result[1] == "0x0000000000000000000000000000000000000000"? false : true; - }); - if (isSTOAttached || _flag) { - console.log("\n"); - console.log("***************************") - console.log("Minting is Finished"); - console.log("***************************\n") - return; - } - try{ - let mintAction = securityToken.methods.mint(address,web3.utils.toWei(amount,"ether")); - GAS = await common.estimateGas(mintAction, Issuer, 1.2); - await mintAction.send({ from: Issuer, gas: GAS}) - .on('transactionHash', function(hash){ - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! The transaction was successfully completed. - - Minted ${web3.utils.fromWei(receipt.events.Transfer.returnValues.value,"ether")} tokens - to account ${receipt.events.Transfer.returnValues.to} - - Review it on Etherscan. - TxHash: ${receipt.transactionHash}\n` - ); - }); - - }catch (err){ - console.log(err); - console.log("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.") - return; - } -} - -async function reclaimedDividend(checkpointId) { - let dividendIndex = await etherDividendCheckpoint.methods.getDividendIndex(checkpointId).call(); - if (dividendIndex.length == 1) { - let reclaimDividendAction = etherDividendCheckpoint.methods.reclaimDividend(dividendIndex[0]); - let GAS = await common.estimateGas(reclaimDividendAction, Issuer, 1.2); - await reclaimDividendAction.send({from: Issuer, gas: GAS}) - .on("transactionHash", function(hash) { - console.log(` - Your transaction is being processed. Please wait... - TxHash: ${hash}\n` - ); - }) - .on('receipt', function(receipt){ - console.log(` - Congratulations! The transaction was successfully completed. - - Claimed Amount ${web3.utils.fromWei(receipt.events.EtherDividendReclaimed.returnValues._claimedAmount,"ether")} ETH - to account ${receipt.events.EtherDividendReclaimed.returnValues._claimer} - - Review it on Etherscan. - TxHash: ${receipt.transactionHash}\n` - ); - }) - .on('error', console.error); - }else{ - console.log(`\nCheckpoint doesn't exist`); - } -} - -executeApp(); diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 9b9c5eb64..97124b804 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -164,6 +164,29 @@ const functionSignatureProxyMR = { // 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]); + 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. @@ -227,29 +250,6 @@ const functionSignatureProxyMR = { // 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(() => { - // 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]); - 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(() => { // 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}) diff --git a/scripts/test.sh b/scripts/test.sh index 1ab73ed74..d24ce6b0f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Global variable +# Global variable bridge_pid # Exit script as soon as a command fails. @@ -63,28 +63,34 @@ start_testrpc() { if ! [ -z "${TRAVIS_PULL_REQUEST+x}" ] && [ "$TRAVIS_PULL_REQUEST" != false ]; then node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port "$testrpc_port" "${accounts[@]}" > /dev/null & else - node_modules/.bin/ganache-cli --gasLimit 0xfffffffffff "${accounts[@]}" > /dev/null & + node_modules/.bin/ganache-cli --gasLimit 8000000 "${accounts[@]}" > /dev/null & fi - + testrpc_pid=$! } if testrpc_running; then echo "Using existing testrpc instance" - bridge_running - if bridge_running; then - echo "Using existing ethereum-bridge instance" - else - echo "Runnning the new ethereum-bridge instance" - start_bridge + # Do not start ethereum bridge unless it is a cron job from travis + if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then + bridge_running + if bridge_running; then + echo "Using existing ethereum-bridge instance" + else + echo "Runnning the new ethereum-bridge instance" + start_bridge + fi fi else echo "Starting our own testrpc instance" start_testrpc - echo "Starting our own ethereum-bridge instance" - sleep 10 - start_bridge + # Do not start ethereum bridge unless it is a cron job from travis + if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; 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 @@ -94,5 +100,10 @@ if ! [ -z "${TRAVIS_PULL_REQUEST+x}" ] && [ "$TRAVIS_PULL_REQUEST" != false ]; t cat coverage/lcov.info | node_modules/.bin/coveralls fi else - node_modules/.bin/truffle test `ls test/*.js` + # 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` + else + node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` + fi fi diff --git a/test/a_poly_oracle.js b/test/a_poly_oracle.js index 9eb6c9b56..f727b4014 100644 --- a/test/a_poly_oracle.js +++ b/test/a_poly_oracle.js @@ -84,9 +84,9 @@ let requestIds = new Array(); let tx = await I_PolyOracle.schedulePriceUpdatesFixed([],{from: owner, value:web3.utils.toWei("1")}); assert.isAtMost(tx.logs[0].args._time.toNumber(), latestTime()); // await increaseTime(50); - const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.LogPriceUpdated({ fromBlock: blockNo }), 1); + const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 1); // const log = await logNewPriceWatcher; - assert.equal(logNewPriceWatcher.event, 'LogPriceUpdated', 'LogPriceUpdated not emitted.') + 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') @@ -107,9 +107,9 @@ let requestIds = new Array(); } // Wait for the callback to be invoked by oraclize and the event to be emitted - const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.LogPriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); const log = await logNewPriceWatcher; - assert.equal(log.event, 'LogPriceUpdated', 'LogPriceUpdated not emitted.') + 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') }); @@ -137,9 +137,9 @@ let requestIds = new Array(); assert.isAtMost(time.toNumber(), latestTime() + ((i + 1) * 30)); } // Wait for the callback to be invoked by oraclize and the event to be emitted - const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.LogPriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); const log = await logNewPriceWatcher; - assert.equal(log.event, 'LogPriceUpdated', 'LogPriceUpdated not emitted.') + 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'); latestPrice = log.args._price; @@ -229,9 +229,9 @@ let requestIds = new Array(); assert.isAtMost(time.toNumber(), timeScheduling[i]); } - const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.LogPriceUpdated({ fromBlock: blockNo }), 2); + const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: blockNo }), 2); - assert.equal(logNewPriceWatcher.event, 'LogPriceUpdated', 'LogPriceUpdated not emitted.') + 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'); // assert.isTrue(false); @@ -319,8 +319,8 @@ let requestIds = new Array(); 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.LogPriceUpdated({ fromBlock: blockNo }), 1); - assert.equal(logNewPriceWatcher.event, 'LogPriceUpdated', 'LogPriceUpdated not emitted.') + const logNewPriceWatcher = await promisifyLogWatch(I_PolyOracle.PriceUpdated({ fromBlock: 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'); // assert.isTrue(false); diff --git a/test/b_capped_sto.js b/test/b_capped_sto.js index 36f5bd41e..ec216c637 100644 --- a/test/b_capped_sto.js +++ b/test/b_capped_sto.js @@ -139,7 +139,7 @@ contract('CappedSTO', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -177,20 +177,6 @@ contract('CappedSTO', accounts => { "CappedSTOFactory contract was not deployed" ); - // STEP 7: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: token_owner }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -224,6 +210,23 @@ contract('CappedSTO', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 7: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + console.log(await I_MRProxied.owner()); + console.log(account_polymath); + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (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 }); + + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -255,7 +258,7 @@ 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, gas: 85000000 }); + 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"); @@ -295,7 +298,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, 0, [E_fundRaiseType], account_fundsReceiver]); let errorThrown = false; try { - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); } catch(error) { console.log(` tx revert -> Rate is ${0}. Test Passed Successfully`.grey); errorThrown = true; @@ -312,7 +315,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, 0, [E_fundRaiseType], account_fundsReceiver]); let errorThrown = false; try { - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); } catch(error) { console.log(`Tx Failed because of rate is ${0}. Test Passed Successfully`); errorThrown = true; @@ -325,7 +328,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [ Math.floor(Date.now()/1000 + 100000), Math.floor(Date.now()/1000 + 1000), cap, rate, [E_fundRaiseType], account_fundsReceiver]); let errorThrown = false; try { - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); } catch(error) { errorThrown = true; console.log(` tx revert -> StartTime is greater than endTime. Test Passed Successfully`.grey); @@ -340,7 +343,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [ startTime, endTime, 0, rate, [E_fundRaiseType], account_fundsReceiver]); let errorThrown = false; try { - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); } catch(error) { console.log(`Tx Failed because the Cap is equal to ${0}. Test Passed Successfully`); errorThrown = true; @@ -353,7 +356,7 @@ contract('CappedSTO', accounts => { startTime_ETH1 = latestTime() + duration.days(1); endTime_ETH1 = startTime_ETH1 + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime_ETH1, endTime_ETH1, cap, rate, [E_fundRaiseType], account_fundsReceiver]); - const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 45000000 }); + const tx = await I_SecurityToken_ETH.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); assert.equal(tx.logs[3].args._type, stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name),"CappedSTO","CappedSTOFactory module was not added"); @@ -873,7 +876,7 @@ 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, gas:85000000 }); + 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"); @@ -901,7 +904,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime_POLY1, endTime_POLY1, P_cap, P_rate, [P_fundRaiseType], account_fundsReceiver]); - const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); assert.equal(tx.logs[3].args._type, stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name),"CappedSTO","CappedSTOFactory module was not added"); @@ -1259,7 +1262,7 @@ contract('CappedSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime_POLY2, endTime_POLY2, P_cap, P_rate, [P_fundRaiseType], account_fundsReceiver]); - const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken_POLY.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); assert.equal(tx.logs[3].args._type, stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name),"CappedSTO","CappedSTOFactory module was not added"); diff --git a/test/c_checkpoints.js b/test/c_checkpoints.js index 0f317765e..a9094c6c5 100644 --- a/test/c_checkpoints.js +++ b/test/c_checkpoints.js @@ -105,7 +105,7 @@ contract('Checkpoints', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -133,16 +133,6 @@ contract('Checkpoints', accounts => { "GeneralDelegateManagerFactory contract was not deployed" ); - // STEP 6: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - // Step 7: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -176,6 +166,16 @@ contract('Checkpoints', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 6: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -205,7 +205,7 @@ 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, gas: 85000000 }); + 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"); diff --git a/test/d_count_transfer_manager.js b/test/d_count_transfer_manager.js index 309e4bdaa..75c938939 100644 --- a/test/d_count_transfer_manager.js +++ b/test/d_count_transfer_manager.js @@ -117,9 +117,9 @@ contract('CountTransferManager', accounts => { "0x0000000000000000000000000000000000000000", "FeatureRegistry contract was not deployed", ); - + // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -164,24 +164,6 @@ contract('CountTransferManager', accounts => { "CountTransferManagerFactory contract was not deployed" ); - // STEP 8: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the CountTransferManagerFactory - await I_MRProxied.registerModule(I_CountTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the Paid CountTransferManagerFactory - await I_MRProxied.registerModule(P_CountTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_CountTransferManagerFactory.address, true, { from: account_polymath }); - // Step 9: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); @@ -207,8 +189,8 @@ contract('CountTransferManager', accounts => { I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); - I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + // Step 12: update the registries addresses from the PolymathRegistry contract await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); @@ -216,6 +198,23 @@ contract('CountTransferManager', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 8: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the CountTransferManagerFactory + await I_MRProxied.registerModule(I_CountTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the Paid CountTransferManagerFactory + await I_MRProxied.registerModule(P_CountTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_CountTransferManagerFactory.address, true, { from: account_polymath }); // Printing all the contract addresses console.log(` @@ -248,7 +247,7 @@ 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, gas: 85000000 }); + 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"); diff --git a/test/e_erc20_dividends.js b/test/e_erc20_dividends.js index 1ffd20041..cdd1fa98d 100644 --- a/test/e_erc20_dividends.js +++ b/test/e_erc20_dividends.js @@ -41,6 +41,7 @@ contract('ERC20DividendCheckpoint', accounts => { let expiryTime = toTime + duration.days(15); let message = "Transaction Should Fail!"; + let dividendName = "0x546573744469766964656e640000000000000000000000000000000000000000"; // Contract Instance Declaration let I_GeneralPermissionManagerFactory; @@ -113,7 +114,7 @@ contract('ERC20DividendCheckpoint', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -157,24 +158,6 @@ contract('ERC20DividendCheckpoint', accounts => { "ERC20DividendCheckpointFactory contract was not deployed" ); - // STEP 8: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the ERC20DividendCheckpointFactory - await I_MRProxied.registerModule(I_ERC20DividendCheckpointFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_ERC20DividendCheckpointFactory.address, true, { from: account_polymath }); - - // (C) : Register the Paid ERC20DividendCheckpointFactory - await I_MRProxied.registerModule(P_ERC20DividendCheckpointFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_ERC20DividendCheckpointFactory.address, true, { from: account_polymath }); - // Step 9: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); @@ -208,6 +191,24 @@ contract('ERC20DividendCheckpoint', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 8: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the ERC20DividendCheckpointFactory + await I_MRProxied.registerModule(I_ERC20DividendCheckpointFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_ERC20DividendCheckpointFactory.address, true, { from: account_polymath }); + + // (C) : Register the Paid ERC20DividendCheckpointFactory + await I_MRProxied.registerModule(P_ERC20DividendCheckpointFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_ERC20DividendCheckpointFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -239,7 +240,7 @@ 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, gas: 85000000 }); + 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"); @@ -364,7 +365,7 @@ contract('ERC20DividendCheckpoint', accounts => { let expiry = latestTime() + duration.days(10); await I_PolyToken.getTokens(web3.utils.toWei('1.5', 'ether'), token_owner); try { - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because allowance = 0`.grey); ensureException(error); @@ -379,7 +380,7 @@ contract('ERC20DividendCheckpoint', accounts => { let expiry = latestTime() - duration.days(10); await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); try { - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because maturity > expiry`.grey); ensureException(error); @@ -393,7 +394,7 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime() - duration.days(2); let expiry = latestTime() - duration.days(1); try { - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because now > expiry`.grey); ensureException(error); @@ -407,7 +408,7 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); try { - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, 0, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, 0, web3.utils.toWei('1.5', 'ether'), dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because token address is 0x`.grey); ensureException(error); @@ -421,7 +422,7 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); try { - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, 0, {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, 0, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because amount < 0`.grey); ensureException(error); @@ -434,8 +435,9 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime() + duration.days(1); let expiry = latestTime() + duration.days(10); - let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, 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"); }); it("Investor 1 transfers his token balance to investor 2", async() => { @@ -519,12 +521,28 @@ contract('ERC20DividendCheckpoint', accounts => { ); }); - it("Create new dividend", async() => { + 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 tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), {from: token_owner}); + let errorThrown = false; + try { + await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), '', {from: token_owner}); + } catch(error) { + console.log(` tx -> failed because dividend name is empty`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + + 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); + // approved in above test + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('1.5', 'ether'), dividendName, {from: token_owner}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 2, "Dividend should be created at checkpoint 1"); }); @@ -581,7 +599,7 @@ contract('ERC20DividendCheckpoint', accounts => { }); it("Exclude account_temp using global exclusion list", async() => { - await I_ERC20DividendCheckpoint.setExcluded([account_temp], {from: token_owner}); + await I_ERC20DividendCheckpoint.setDefaultExcluded([account_temp], {from: token_owner}); }); it("Create another new dividend", async() => { @@ -589,7 +607,7 @@ contract('ERC20DividendCheckpoint', accounts => { 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 tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('10', 'ether'), {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividend(maturity, expiry, I_PolyToken.address, web3.utils.toWei('10', 'ether'), dividendName, {from: token_owner}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 3, "Dividend should be created at checkpoint 2"); }); @@ -655,7 +673,7 @@ contract('ERC20DividendCheckpoint', accounts => { it("Delete global exclusion list", async() => { - await I_ERC20DividendCheckpoint.setExcluded([], {from: token_owner}); + await I_ERC20DividendCheckpoint.setDefaultExcluded([], {from: token_owner}); }); @@ -676,7 +694,7 @@ contract('ERC20DividendCheckpoint', accounts => { console.log((await I_SecurityToken.currentCheckpointId()).toNumber()); await I_PolyToken.getTokens(web3.utils.toWei('20', 'ether'), token_owner); try { - tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, {from: token_owner}); + tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because allowance is not provided`.grey); ensureException(error); @@ -693,7 +711,7 @@ contract('ERC20DividendCheckpoint', accounts => { let expiry = latestTime() - duration.days(10); await I_PolyToken.approve(I_ERC20DividendCheckpoint.address, web3.utils.toWei('20', 'ether'), {from: token_owner}); try { - tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, {from: token_owner}); + tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because maturity > expiry`.grey); ensureException(error); @@ -709,7 +727,7 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime() - duration.days(5); let expiry = latestTime() - duration.days(2); try { - tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, {from: token_owner}); + tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 4, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because now > expiry`.grey); ensureException(error); @@ -723,7 +741,7 @@ contract('ERC20DividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(2); try { - tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 5, {from: token_owner}); + tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, I_PolyToken.address, web3.utils.toWei('20', 'ether'), 5, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because checkpoint id > current checkpoint`.grey); ensureException(error); @@ -741,7 +759,7 @@ contract('ERC20DividendCheckpoint', accounts => { 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 tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, I_PolyToken.address, web3.utils.toWei('10', 'ether'), 4, [account_investor1], {from: token_owner}); + let tx = await I_ERC20DividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, I_PolyToken.address, 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"); }); diff --git a/test/f_ether_dividends.js b/test/f_ether_dividends.js index 00a8852a2..dec4be72f 100644 --- a/test/f_ether_dividends.js +++ b/test/f_ether_dividends.js @@ -41,6 +41,7 @@ contract('EtherDividendCheckpoint', accounts => { let expiryTime = toTime + duration.days(15); let message = "Transaction Should Fail!"; + let dividendName = "0x546573744469766964656e640000000000000000000000000000000000000000"; // Contract Instance Declaration let I_GeneralPermissionManagerFactory; @@ -113,7 +114,7 @@ contract('EtherDividendCheckpoint', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -157,24 +158,6 @@ contract('EtherDividendCheckpoint', accounts => { "EtherDividendCheckpointFactory contract was not deployed" ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the EtherDividendCheckpointFactory - await I_MRProxied.registerModule(I_EtherDividendCheckpointFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_EtherDividendCheckpointFactory.address, true, { from: account_polymath }); - - // (C) : Register the Paid EtherDividendCheckpointFactory - await I_MRProxied.registerModule(P_EtherDividendCheckpointFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_EtherDividendCheckpointFactory.address, true, { from: account_polymath }); - // Step 6: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -184,23 +167,23 @@ contract('EtherDividendCheckpoint', accounts => { "0x0000000000000000000000000000000000000000", "STFactory contract was not deployed", ); - + // Step 7: Deploy the SecurityTokenRegistry contract - + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); - + assert.notEqual( I_SecurityTokenRegistry.address.valueOf(), "0x0000000000000000000000000000000000000000", "SecurityTokenRegistry contract was not deployed", ); - + // Step 8: Deploy the proxy and attach the implementation contract to it. I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - + // Step 9: update the registries addresses from the PolymathRegistry contract await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); @@ -208,6 +191,24 @@ contract('EtherDividendCheckpoint', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the EtherDividendCheckpointFactory + await I_MRProxied.registerModule(I_EtherDividendCheckpointFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_EtherDividendCheckpointFactory.address, true, { from: account_polymath }); + + // (C) : Register the Paid EtherDividendCheckpointFactory + await I_MRProxied.registerModule(P_EtherDividendCheckpointFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_EtherDividendCheckpointFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -239,7 +240,7 @@ 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, gas: 85000000 }); + 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"); @@ -364,7 +365,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); try { - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner}); } catch(error) { console.log(` tx -> failed because msg.value = 0`.grey); ensureException(error); @@ -378,7 +379,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() - duration.days(10); try { - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); } catch(error) { console.log(` tx -> failed because maturity > expiry`.grey); ensureException(error); @@ -392,7 +393,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime() - duration.days(2); let expiry = latestTime() - duration.days(1); try { - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); } catch(error) { console.log(` tx -> failed because now > expiry`.grey); ensureException(error); @@ -405,11 +406,26 @@ contract('EtherDividendCheckpoint', accounts => { await I_EtherDividendCheckpoint.setWithholdingFixed([account_investor2], BigNumber(20*10**16), {from: token_owner}); }); + it("Should fail in creating the dividend", async() => { + let errorThrown = false; + let maturity = latestTime() + duration.days(1); + let expiry = latestTime() + duration.days(10); + try { + await I_EtherDividendCheckpoint.createDividend(maturity, expiry, '', {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); + } catch(error) { + console.log(` tx -> failed because dividend name is empty`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + it("Create new dividend", async() => { let maturity = latestTime() + duration.days(1); let expiry = latestTime() + duration.days(10); - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner, value: 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() => { @@ -510,7 +526,7 @@ contract('EtherDividendCheckpoint', accounts => { it("Create new dividend", async() => { let maturity = latestTime() + duration.days(1); let expiry = latestTime() + duration.days(10); - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner, value: web3.utils.toWei('1.5', 'ether')}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 2, "Dividend should be created at checkpoint 2"); }); @@ -576,7 +592,7 @@ contract('EtherDividendCheckpoint', accounts => { it("Create another new dividend", async() => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); - let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividend(maturity, expiry, dividendName, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 3, "Dividend should be created at checkpoint 3"); }); @@ -662,7 +678,7 @@ contract('EtherDividendCheckpoint', accounts => { let expiry = latestTime() + duration.days(2); let tx = await I_SecurityToken.createCheckpoint({from: token_owner}); try { - tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, {from: token_owner, value: 0}); + tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, {from: token_owner, value: 0}); } catch(error) { console.log(` tx -> failed because msg.value is 0`.grey); ensureException(error); @@ -676,7 +692,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() - duration.days(10); try { - tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); + tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); } catch(error) { console.log(` tx -> failed because maturity > expiry`.grey); ensureException(error); @@ -690,7 +706,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime() - duration.days(5); let expiry = latestTime() - duration.days(2); try { - tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); + tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 4, dividendName, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); } catch(error) { console.log(` tx -> failed because now > expiry`.grey); ensureException(error); @@ -704,7 +720,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(2); try { - tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 5, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); + tx = await I_EtherDividendCheckpoint.createDividendWithCheckpoint(maturity, expiry, 5, dividendName, {from: token_owner, value: web3.utils.toWei('11', 'ether')}); } catch(error) { console.log(` tx -> failed because checkpoint id > current checkpoint`.grey); ensureException(error); @@ -717,7 +733,7 @@ contract('EtherDividendCheckpoint', accounts => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); let tx = await I_SecurityToken.createCheckpoint({from: token_owner}); - tx = await I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, 4, [account_investor1], {from: token_owner, value: web3.utils.toWei('10', 'ether')}); + tx = await I_EtherDividendCheckpoint.createDividendWithCheckpointAndExclusions(maturity, expiry, 4, [account_investor1], dividendName, {from: token_owner, value: web3.utils.toWei('10', 'ether')}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 4, "Dividend should be created at checkpoint 4"); }); @@ -890,7 +906,7 @@ contract('EtherDividendCheckpoint', accounts => { it("Create another new dividend", async() => { let maturity = latestTime(); let expiry = latestTime() + duration.days(10); - let tx = await I_EtherDividendCheckpoint.createDividendWithExclusions(maturity, expiry, [], {from: token_owner, value: web3.utils.toWei('12', 'ether')}); + let tx = await I_EtherDividendCheckpoint.createDividendWithExclusions(maturity, expiry, [], dividendName, {from: token_owner, value: web3.utils.toWei('12', 'ether')}); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 6, "Dividend should be created at checkpoint 6"); }); diff --git a/test/g_general_permission_manager.js b/test/g_general_permission_manager.js index 118dca77f..3b7299ca2 100644 --- a/test/g_general_permission_manager.js +++ b/test/g_general_permission_manager.js @@ -123,7 +123,7 @@ contract('GeneralPermissionManager', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -171,24 +171,6 @@ contract('GeneralPermissionManager', accounts => { "DummySTOFactory contract was not deployed" ); - // STEP 8: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the Paid GeneralDelegateManagerFactory - await I_MRProxied.registerModule(P_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); - // Step 8: Deploy the STFactory contract @@ -223,6 +205,24 @@ contract('GeneralPermissionManager', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 8: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the Paid GeneralDelegateManagerFactory + await I_MRProxied.registerModule(P_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -254,7 +254,7 @@ 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, gas: 85000000 }); + 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"); diff --git a/test/h_general_transfer_manager.js b/test/h_general_transfer_manager.js index cda17f0cb..36eccdbb0 100644 --- a/test/h_general_transfer_manager.js +++ b/test/h_general_transfer_manager.js @@ -124,7 +124,7 @@ contract('GeneralTransferManager', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -163,20 +163,6 @@ contract('GeneralTransferManager', accounts => { "DummySTOFactory contract was not deployed" ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -186,23 +172,23 @@ contract('GeneralTransferManager', accounts => { "0x0000000000000000000000000000000000000000", "STFactory contract was not deployed", ); - + // Step 9: Deploy the SecurityTokenRegistry contract - + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); - + assert.notEqual( I_SecurityTokenRegistry.address.valueOf(), "0x0000000000000000000000000000000000000000", "SecurityTokenRegistry contract was not deployed", ); - + // Step 10: Deploy the proxy and attach the implementation contract to it. I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - + // Step 11: update the registries addresses from the PolymathRegistry contract await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); @@ -210,6 +196,20 @@ contract('GeneralTransferManager', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -241,7 +241,7 @@ 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, gas: 85000000 }); + 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"); diff --git a/test/i_Issuance.js b/test/i_Issuance.js index d2691674e..4cfe448d3 100644 --- a/test/i_Issuance.js +++ b/test/i_Issuance.js @@ -120,14 +120,14 @@ contract('Issuance', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, {from: account_polymath}); I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); - + // STEP 4: Deploy the GeneralTransferManagerFactory I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); @@ -158,20 +158,6 @@ contract('Issuance', accounts => { "CappedSTOFactory contract was not deployed" ); - // STEP 7: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: token_owner }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -205,6 +191,20 @@ contract('Issuance', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 7: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (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 }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- diff --git a/test/j_manual_approval_transfer_manager.js b/test/j_manual_approval_transfer_manager.js index b9517b8c9..9036435f8 100644 --- a/test/j_manual_approval_transfer_manager.js +++ b/test/j_manual_approval_transfer_manager.js @@ -117,7 +117,7 @@ contract('ManualApprovalTransferManager', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -169,28 +169,6 @@ contract('ManualApprovalTransferManager', accounts => { "CountTransferManagerFactory contract was not deployed" ); - // STEP 9: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the ManualApprovalTransferManagerFactory - await I_MRProxied.registerModule(I_ManualApprovalTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_ManualApprovalTransferManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the ManualApprovalTransferManagerFactory - await I_MRProxied.registerModule(P_ManualApprovalTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_ManualApprovalTransferManagerFactory.address, true, { from: account_polymath }); - - // (D) : Register the CountTransferManagerFactory - await I_MRProxied.registerModule(I_CountTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, true, { from: account_polymath }); - // Step 10: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -224,6 +202,28 @@ contract('ManualApprovalTransferManager', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 9: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the ManualApprovalTransferManagerFactory + await I_MRProxied.registerModule(I_ManualApprovalTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_ManualApprovalTransferManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the ManualApprovalTransferManagerFactory + await I_MRProxied.registerModule(P_ManualApprovalTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_ManualApprovalTransferManagerFactory.address, true, { from: account_polymath }); + + // (D) : Register the CountTransferManagerFactory + await I_MRProxied.registerModule(I_CountTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -255,7 +255,7 @@ 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, gas: 60000000 }); + 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"); diff --git a/test/k_module_registry.js b/test/k_module_registry.js index 6843db491..a7ed18719 100644 --- a/test/k_module_registry.js +++ b/test/k_module_registry.js @@ -129,7 +129,7 @@ contract('ModuleRegistry', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); assert.notEqual( @@ -178,7 +178,7 @@ contract('ModuleRegistry', accounts => { await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - + assert.notEqual( I_FeatureRegistry.address.valueOf(), @@ -261,7 +261,7 @@ contract('ModuleRegistry', accounts => { await I_MRProxied.pause({from: account_polymath}); let errorThrown = false; try { - let tx = await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, {from: account_polymath}); + let tx = await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, {from: account_delegate}); } catch(error) { console.log(` tx -> revert because already registered modules are not allowed`); errorThrown = true; @@ -276,11 +276,11 @@ contract('ModuleRegistry', accounts => { assert.equal(tx.logs[0].args._moduleFactory, I_GeneralTransferManagerFactory.address, "Should be the same address"); assert.equal(tx.logs[0].args._owner, account_polymath, "Should be the right owner"); - let _list = await I_MRProxied.getModuleListOfType(transferManagerKey); + let _list = await I_MRProxied.getModulesByType(transferManagerKey); assert.equal(_list.length, 1, "Length should be 1"); assert.equal(_list[0], I_GeneralTransferManagerFactory.address); - let _reputation = await I_MRProxied.getReputationOfFactory(I_GeneralTransferManagerFactory.address); + let _reputation = await I_MRProxied.getReputationByFactory(I_GeneralTransferManagerFactory.address); assert.equal(_reputation.length, 0); }); @@ -368,7 +368,7 @@ 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}); @@ -384,7 +384,7 @@ contract('ModuleRegistry', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); let errorThrown = false; try { - const tx = await I_SecurityToken.addModule(I_CappedSTOFactory1.address, bytesSTO, 0, 0, { from: token_owner, gas: 60000000 }); + const tx = await I_SecurityToken.addModule(I_CappedSTOFactory1.address, bytesSTO, 0, 0, { from: token_owner}); } catch(error) { errorThrown = true; console.log(` tx revert -> Module is un-verified`.grey); @@ -392,54 +392,42 @@ contract('ModuleRegistry', accounts => { } assert.ok(errorThrown, message); }); - - it("Should fail to add module because custom modules not allowed", async() => { + + 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 }); - + assert.notEqual( I_CappedSTOFactory2.address.valueOf(), "0x0000000000000000000000000000000000000000", "CappedSTOFactory contract was not deployed" ); - - let tx = await I_MRProxied.registerModule(I_CappedSTOFactory2.address, { from: token_owner }); - - assert.equal( - tx.logs[0].args._moduleFactory, - I_CappedSTOFactory2.address, - "CappedSTOFactory is not registerd successfully" - ); - - assert.equal(tx.logs[0].args._owner, token_owner); - - startTime = latestTime() + duration.seconds(5000); - endTime = startTime + duration.days(30); - let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - + + let errorThrown = false; try { - tx = await I_SecurityToken.addModule(I_CappedSTOFactory2.address, bytesSTO, 0, 0, { from: token_owner, gas: 60000000 }); + let tx = await I_MRProxied.registerModule(I_CappedSTOFactory2.address, { from: token_owner }); } catch(error) { errorThrown = true; console.log(` tx revert -> Module is un-verified`.grey); ensureException(error); } assert.ok(errorThrown, message); + }); - + it("Should switch customModulesAllowed to true", async() => { assert.equal(false, await I_FeatureRegistry.getFeatureStatus.call("customModulesAllowed"), "Custom modules should be dissabled by default."); let tx = await I_FeatureRegistry.setFeatureStatus("customModulesAllowed", true, { from: account_polymath }); assert.equal(true, await I_FeatureRegistry.getFeatureStatus.call("customModulesAllowed"), "Custom modules should be switched to true."); }); - + it("Should successfully add module because custom modules switched on", async() => { startTime = latestTime() + duration.seconds(5000); endTime = startTime + duration.days(30); let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); - - let tx = await I_SecurityToken.addModule(I_CappedSTOFactory2.address, bytesSTO, 0, 0, { from: token_owner, gas: 60000000 }); - + 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}); + assert.equal(tx.logs[2].args._type, stoKey, "CappedSTO doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name) @@ -447,10 +435,10 @@ contract('ModuleRegistry', accounts => { "CappedSTO", "CappedSTOFactory module was not added" ); - let _reputation = await I_MRProxied.getReputationOfFactory.call(I_CappedSTOFactory2.address); + let _reputation = await I_MRProxied.getReputationByFactory.call(I_CappedSTOFactory2.address); assert.equal(_reputation.length, 1); }); - + it("Should successfully add verified module", async() => { I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from: account_polymath}); await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, {from: account_polymath}); @@ -461,7 +449,7 @@ contract('ModuleRegistry', accounts => { 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}); - await I_MRProxied.registerModule(I_TestSTOFactory.address, {from: token_owner}); + 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 let id = await takeSnapshot(); @@ -513,60 +501,49 @@ contract('ModuleRegistry', accounts => { }); - describe("Test cases for the tag functions", async() => { - - it("Should fail in adding the tag. Because msg.sender is not the owner", async() => { - let errorThrown = false; - try { - await I_MRProxied.addTagByModuleType(3,["Non-Refundable","Capped","ETH","POLY"],{from: account_temp}); - } catch(error) { - console.log(` tx revert -> msg.sender should be account_polymath`.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - }); - - it("Should successfully add the tag", async() => { - await I_MRProxied.addTagByModuleType(3,["Non-Refundable","Capped","ETH","POLY"],{from: account_polymath}); - let tags = await I_MRProxied.getTagByModuleType.call(3); - assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''),"Non-Refundable"); - }); - - it("Should fail in removing the tag from the list", async() => { - let errorThrown = false; - try { - await I_MRProxied.removeTagByModuleType(3,["Capped", "ETH"], {from: account_investor1}); - } catch(error) { - console.log(` tx revert -> msg.sender should be account_polymath`.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - }); - - it("Should remove the tag from the list", async() => { - await I_MRProxied.removeTagByModuleType(3,["Capped", "ETH"], {from:account_polymath}); - let tags = await I_MRProxied.getTagByModuleType.call(3); - assert.equal(web3.utils.toAscii(tags[1]).replace(/\u0000/g, ''),"POLY"); - }); - }); - - describe("Test case for the getAvailableModulesOfType()", async() => { + describe("Test case for the getModulesByTypeAndToken()", async() => { it("Should get the list of available modules when the customModulesAllowed", async() => { - let _list = await I_MRProxied.getAvailableModulesOfType.call(3, I_SecurityToken.address); + let _list = await I_MRProxied.getModulesByTypeAndToken.call(3, I_SecurityToken.address); assert.equal(_list[0], I_CappedSTOFactory2.address); - assert.equal(_list[1], I_TestSTOFactory.address); }) it("Should get the list of available modules when the customModulesAllowed is not allowed", async() => { await I_FeatureRegistry.setFeatureStatus("customModulesAllowed", false, { from: account_polymath }); - let _list = await I_MRProxied.getAvailableModulesOfType.call(3, I_SecurityToken.address); - assert.equal(_list[0], I_TestSTOFactory.address); + let _list = await I_MRProxied.getModulesByTypeAndToken.call(3, I_SecurityToken.address); + assert.equal(_list.length, 0); }) }) + describe("Test cases for getters", async() => { + + it("Check getter - ", async() => { + console.log("getModulesByType:") + for (let i = 0; i < 5; i++) { + let _list = await I_MRProxied.getModulesByType.call(i); + console.log("Type: " + i + ":" + _list); + } + console.log("getModulesByTypeAndToken:") + for (let i = 0; i < 5; i++) { + let _list = await I_MRProxied.getModulesByTypeAndToken.call(i, I_SecurityToken.address); + console.log("Type: " + i + ":" + _list); + } + console.log("getTagsByType:") + for (let i = 0; i < 5; i++) { + let _list = await I_MRProxied.getTagsByType.call(i); + console.log("Type: " + i + ":" + _list[1]); + console.log("Type: " + i + ":" + _list[0].map(x => web3.utils.toAscii(x))); + } + console.log("getTagsByTypeAndToken:") + for (let i = 0; i < 5; i++) { + let _list = await I_MRProxied.getTagsByTypeAndToken.call(i, I_SecurityToken.address); + console.log("Type: " + i + ":" + _list[1]); + console.log("Type: " + i + ":" + _list[0].map(x => web3.utils.toAscii(x))); + } + }) + + }); + describe("Test cases for removeModule()", async() => { it("Should fail if msg.sender not curator or owner", async() => { @@ -580,59 +557,59 @@ contract('ModuleRegistry', accounts => { } assert.ok(errorThrown, message); }); - + it("Should successfully remove module and delete data if msg.sender is curator", async() => { let snap = await takeSnapshot(); - - let sto1 = (await I_MRProxied.getModuleListOfType.call(3))[0]; - let sto2 = (await I_MRProxied.getModuleListOfType.call(3))[1]; - + + let sto1 = (await I_MRProxied.getModulesByType.call(3))[0]; + let sto2 = (await I_MRProxied.getModulesByType.call(3))[1]; + assert.equal(sto1,I_CappedSTOFactory1.address); assert.equal(sto2,I_CappedSTOFactory2.address); - assert.equal((await I_MRProxied.getModuleListOfType.call(3)).length, 3); - + assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 3); + let tx = await I_MRProxied.removeModule(sto1, { from: account_polymath }); - + assert.equal(tx.logs[0].args._moduleFactory, sto1, "Event is not properly emitted for _moduleFactory"); assert.equal(tx.logs[0].args._decisionMaker, account_polymath, "Event is not properly emitted for _decisionMaker"); - - let sto2_end = (await I_MRProxied.getModuleListOfType.call(3))[1]; - + + let sto2_end = (await I_MRProxied.getModulesByType.call(3))[1]; + // re-ordering assert.equal(sto2_end,sto2); // delete related data assert.equal(await I_MRProxied.getUintValues.call(web3.utils.soliditySha3("registry", sto1)), 0); - assert.equal(await I_MRProxied.getReputationOfFactory.call(sto1), 0); - assert.equal((await I_MRProxied.getModuleListOfType.call(3)).length, 2); + assert.equal(await I_MRProxied.getReputationByFactory.call(sto1), 0); + assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 2); assert.equal(await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("verified", sto1)), false); - + await revertToSnapshot(snap); }); - + it("Should successfully remove module and delete data if msg.sender is owner", async() => { - let sto1 = (await I_MRProxied.getModuleListOfType.call(3))[0]; - let sto2 = (await I_MRProxied.getModuleListOfType.call(3))[1]; - + let sto1 = (await I_MRProxied.getModulesByType.call(3))[0]; + let sto2 = (await I_MRProxied.getModulesByType.call(3))[1]; + assert.equal(sto1,I_CappedSTOFactory1.address); assert.equal(sto2,I_CappedSTOFactory2.address); - assert.equal((await I_MRProxied.getModuleListOfType.call(3)).length, 3); - + assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 3); + let tx = await I_MRProxied.removeModule(sto2, { from: token_owner }); - + assert.equal(tx.logs[0].args._moduleFactory, sto2, "Event is not properly emitted for _moduleFactory"); assert.equal(tx.logs[0].args._decisionMaker, token_owner, "Event is not properly emitted for _decisionMaker"); - - let sto1_end = (await I_MRProxied.getModuleListOfType.call(3))[0]; - + + let sto1_end = (await I_MRProxied.getModulesByType.call(3))[0]; + // 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.getReputationOfFactory.call(sto2), 0); - assert.equal((await I_MRProxied.getModuleListOfType.call(3)).length, 2); + assert.equal(await I_MRProxied.getReputationByFactory.call(sto2), 0); + assert.equal((await I_MRProxied.getModulesByType.call(3)).length, 2); assert.equal(await I_MRProxied.getBoolValues.call(web3.utils.soliditySha3("verified", sto2)), false); }); - + it("Should fail if module already removed", async() => { let errorThrown = false; try { @@ -644,7 +621,7 @@ contract('ModuleRegistry', accounts => { } assert.ok(errorThrown, message); }); - + }); describe("Test cases for IRegistry functionality", async() => { @@ -704,4 +681,4 @@ contract('ModuleRegistry', accounts => { }); }); -}); \ No newline at end of file +}); diff --git a/test/l_percentage_transfer_manager.js b/test/l_percentage_transfer_manager.js index 23ec5b8d4..4e81888f4 100644 --- a/test/l_percentage_transfer_manager.js +++ b/test/l_percentage_transfer_manager.js @@ -122,14 +122,14 @@ contract('PercentageTransferManager', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, {from: account_polymath}); I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); - + // STEP 4(a): Deploy the GeneralTransferManagerFactory I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); @@ -167,24 +167,6 @@ contract('PercentageTransferManager', accounts => { ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the PercentageTransferManagerFactory - await I_MRProxied.registerModule(I_PercentageTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_PercentageTransferManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the Paid PercentageTransferManagerFactory - await I_MRProxied.registerModule(P_PercentageTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(P_PercentageTransferManagerFactory.address, true, { from: account_polymath }); - // Step 6: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -218,6 +200,24 @@ contract('PercentageTransferManager', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the PercentageTransferManagerFactory + await I_MRProxied.registerModule(I_PercentageTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_PercentageTransferManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the Paid PercentageTransferManagerFactory + await I_MRProxied.registerModule(P_PercentageTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(P_PercentageTransferManagerFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -249,7 +249,7 @@ 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, gas: 60000000 }); + 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"); diff --git a/test/m_presale_sto.js b/test/m_presale_sto.js index c14156f2d..862885bcf 100644 --- a/test/m_presale_sto.js +++ b/test/m_presale_sto.js @@ -112,7 +112,7 @@ contract('PreSaleSTO', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -150,20 +150,6 @@ contract('PreSaleSTO', accounts => { "PreSaleSTOFactory contract was not deployed" ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_PreSaleSTOFactory.address, { from: token_owner }); - await I_MRProxied.verifyModule(I_PreSaleSTOFactory.address, true, { from: account_polymath }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -197,6 +183,20 @@ contract('PreSaleSTO', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_PreSaleSTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_PreSaleSTOFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -229,7 +229,7 @@ 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, gas:60000000 }); + 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"); @@ -256,7 +256,7 @@ contract('PreSaleSTO', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [0]); let errorThrown = false; try { - const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner, gas: 26000000 }); + const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner }); } catch(error) { console.log(` tx revert -> Rate is ${0}. Test Passed Successfully`.grey); errorThrown = true; @@ -269,7 +269,7 @@ contract('PreSaleSTO', accounts => { endTime = 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, gas: 26000000 }); + const tx = await I_SecurityToken.addModule(I_PreSaleSTOFactory.address, bytesSTO, 0, 0, { from: token_owner }); assert.equal(tx.logs[2].args._type, stoKey, "PreSaleSTO doesn't get deployed"); assert.equal( @@ -333,7 +333,7 @@ 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, {from: account_issuer, gas: 60000000}); + await I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether'), 0, {from: account_issuer }); assert.equal( (await I_PreSaleSTO.getRaised.call(0)) @@ -350,7 +350,7 @@ contract('PreSaleSTO', accounts => { it("Should allocate the tokens -- failed due to msg.sender is not pre sale admin", async () => { let errorThrown = false; try { - await I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether'), 0, {from: account_fundsReceiver, gas: 60000000}); + await I_PreSaleSTO.allocateTokens(account_investor1, web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether'), 0, {from: account_fundsReceiver }); } catch(error) { console.log(` tx revert -> msg.sender is not pre sale admin`.grey); errorThrown = true; @@ -392,7 +392,7 @@ contract('PreSaleSTO', accounts => { assert.equal(tx2.logs[0].args._investor, account_investor3, "Failed in adding the investor in whitelist"); - await I_PreSaleSTO.allocateTokensMulti([account_investor2, account_investor3], [web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether')], [0,0], [web3.utils.toWei('1000', 'ether'), web3.utils.toWei('1000', 'ether')], {from: account_issuer, gas: 60000000}); + await I_PreSaleSTO.allocateTokensMulti([account_investor2, account_investor3], [web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether')], [0,0], [web3.utils.toWei('1000', 'ether'), web3.utils.toWei('1000', 'ether')], {from: account_issuer }); assert.equal( (await I_PreSaleSTO.getRaised.call(1)) diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index a53378c8b..69d9623ad 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -131,7 +131,7 @@ contract('SecurityTokenRegistry', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -160,16 +160,6 @@ contract('SecurityTokenRegistry', accounts => { ); - // STEP 4: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - // Step 6: Deploy the STversionProxy contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -190,9 +180,6 @@ contract('SecurityTokenRegistry', accounts => { "TestSTOFactory contract was not deployed" ); - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: token_owner }); - // Step 9: Deploy the SecurityTokenRegistry I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); @@ -203,7 +190,7 @@ contract('SecurityTokenRegistry', accounts => { "SecurityTokenRegistry contract was not deployed", ); - // Step 9 (a): Deploy the proxy + // Step 9 (a): Deploy the proxy I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); @@ -230,6 +217,19 @@ contract('SecurityTokenRegistry', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 4: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); + console.log(` --------------------- Polymath Network Smart Contracts: --------------------- PolymathRegistry: ${PolymathRegistry.address} @@ -485,7 +485,7 @@ contract('SecurityTokenRegistry', accounts => { let errorThrown = false; await I_PolyToken.approve(I_STRProxied.address, 0, { from: token_owner}); try { - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas:60000000 }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> POLY allowance not provided for registration fee`.grey); errorThrown = true; @@ -499,7 +499,7 @@ contract('SecurityTokenRegistry', accounts => { await I_STRProxied.pause({ from: account_polymath}); await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner}); try { - await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas:60000000 }); + await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> Registration is paused`.grey); errorThrown = true; @@ -512,7 +512,7 @@ contract('SecurityTokenRegistry', accounts => { let errorThrown = false; await I_STRProxied.unpause({ from: account_polymath}); try { - await I_STRProxied.generateSecurityToken(name, "", tokenDetails, false, { from: token_owner, gas:60000000 }); + await I_STRProxied.generateSecurityToken(name, "", tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> Zero ticker length is not allowed`.grey); errorThrown = true; @@ -524,7 +524,7 @@ contract('SecurityTokenRegistry', accounts => { it("Should fail to generate the securityToken -- Because name length is 0", async() => { let errorThrown = false; try { - await I_STRProxied.generateSecurityToken("", symbol, tokenDetails, false, { from: token_owner, gas:60000000 }); + await I_STRProxied.generateSecurityToken("", symbol, tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> 0 name length is not allowed`.grey); errorThrown = true; @@ -536,7 +536,7 @@ contract('SecurityTokenRegistry', accounts => { it("Should fail to generate the securityToken -- Because msg.sender is not the rightful owner of the ticker", async() => { let errorThrown = false; try { - await I_STRProxied.generateSecurityToken("", symbol, tokenDetails, false, { from: account_temp, gas:60000000 }); + await I_STRProxied.generateSecurityToken("", symbol, tokenDetails, false, { from: account_temp }); } catch(error) { console.log(` tx revert -> Because msg.sender is not the rightful owner of the ticker`.grey); errorThrown = true; @@ -547,7 +547,7 @@ contract('SecurityTokenRegistry', accounts => { 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, gas:60000000 }); + 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"); @@ -568,7 +568,7 @@ contract('SecurityTokenRegistry', accounts => { it("Should fail to generate the SecurityToken when token is already deployed with the same symbol", async() => { let errorThrown = false; try { - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas:60000000 }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> Because ticker is already in use`.grey); errorThrown = true; @@ -608,7 +608,7 @@ contract('SecurityTokenRegistry', accounts => { 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(); - let tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails, false, { from: token_owner, gas:60000000 }); + 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"); @@ -739,9 +739,15 @@ contract('SecurityTokenRegistry', accounts => { // 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); + console.log(tickersListArray); await I_STRProxied.registerTicker(account_temp, "LOG", "LOGAN", { from : account_temp }); + tickersListArray = await I_STRProxied.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(), {from: account_polymath}); + tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); + console.log(tickersListArray); assert.equal(tx.logs[1].args._ticker, "LOG", "Symbol should match with the registered symbol"); assert.equal(tx.logs[1].args._securityTokenAddress, dummy_token,`Address of the SecurityToken should be matched with the input value of addCustomSecurityToken`); let symbolDetails = await I_STRProxied.getTickerDetails("LOG"); @@ -751,6 +757,17 @@ contract('SecurityTokenRegistry', accounts => { it("Should successfully generate the custom token", async() => { // Fulfilling the TickerStatus.NN condition + // let errorThrown = false; + // try { + // await I_STRProxied.modifySecurityToken("LOGAN2", "LOG2", account_temp, dummy_token, "I am custom ST", latestTime(), {from: account_polymath}); + // } catch(error) { + // console.log(` tx revert -> because ticker not registered`.grey); + // errorThrown = true; + // ensureException(error); + // } + // assert.ok(errorThrown, message); + // await I_STRProxied.modifyTicker(account_temp, "LOG2", "LOGAN2", latestTime(), latestTime() + 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(), {from: account_polymath}); assert.equal(tx.logs[1].args._ticker, "LOG2", "Symbol should match with the registered symbol"); assert.equal(tx.logs[1].args._securityTokenAddress, dummy_token, `Address of the SecurityToken should be matched with the input value of addCustomSecurityToken`); @@ -833,8 +850,7 @@ contract('SecurityTokenRegistry', accounts => { it("Should change the details of the existing ticker", async() => { let tx = await I_STRProxied.modifyTicker(token_owner, "ETH", "Ether", latestTime(), (latestTime() + duration.minutes(10)), false, {from: account_polymath}); - assert.equal(tx.logs[0].args._oldOwner, account_temp); - assert.equal(tx.logs[0].args._newOwner, token_owner); + assert.equal(tx.logs[0].args._owner, token_owner); }); }); @@ -1014,7 +1030,7 @@ contract('SecurityTokenRegistry', accounts => { let errorThrown = false; await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner}); try { - await I_STRProxied.generateSecurityToken("Polymath", "POLY", tokenDetails, false, { from: token_owner, gas:60000000 }); + await I_STRProxied.generateSecurityToken("Polymath", "POLY", tokenDetails, false, { from: token_owner }); } catch(error) { console.log(` tx revert -> failed because of old launch fee`.grey); errorThrown = true; @@ -1025,7 +1041,7 @@ contract('SecurityTokenRegistry', accounts => { it("Should launch the the securityToken", async() => { await I_PolyToken.approve(I_STRProxied.address, web3.utils.toWei("500"), { from: token_owner}); - let tx = await I_STRProxied.generateSecurityToken("Polymath", "POLY", tokenDetails, false, { from: token_owner, gas:60000000 }); + 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"); @@ -1083,7 +1099,8 @@ contract('SecurityTokenRegistry', accounts => { let tickersList = await I_STRProxied.getTickersByOwner.call(token_owner); assert.equal(tickersList.length, 4); let tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); - assert.equal(tickersListArray.length, 2); + console.log(tickersListArray); + assert.equal(tickersListArray.length, 3); }); }); @@ -1120,6 +1137,58 @@ 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 }); + 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.toAscii(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 }); + 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.toAscii(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 }); + 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.toAscii(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.toAscii(x))); + }); + + it("Should modify ticker 1", async() => { + let tx = await I_STRProxied.modifyTicker(account_temp, "TOK1", "TOKEN 1", latestTime(), (latestTime() + 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.toAscii(x))); + }) + + it("Should modify ticker 3", async() => { + let tx = await I_STRProxied.modifyTicker(account_temp, "TOK3", "TOKEN 3", latestTime(), (latestTime() + 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.toAscii(x))); + }) + + }); describe("Test cases for IRegistry functionality", async() => { describe("Test cases for reclaiming funds", async() => { diff --git a/test/o_security_token.js b/test/o_security_token.js index 8e4223f7d..1217c6c22 100644 --- a/test/o_security_token.js +++ b/test/o_security_token.js @@ -139,7 +139,7 @@ contract('SecurityToken', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -179,18 +179,6 @@ contract('SecurityToken', accounts => { // STEP 5: Register the Modules with the ModuleRegistry contract - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: token_owner }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); - // Step 6: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); @@ -223,6 +211,18 @@ contract('SecurityToken', accounts => { await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (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 }); + // Printing all the contract addresses console.log(` @@ -255,7 +255,7 @@ 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, gas:60000000 }); + 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"); @@ -444,7 +444,7 @@ contract('SecurityToken', accounts => { let bytesSTO = encodeModuleCall(STOParameters, [startTime, endTime, cap, rate, fundRaiseType, account_fundsReceiver]); let errorThrown = false; try { - let tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 60000000 }); + let tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); } catch (error) { console.log(` tx revert -> not enough poly in contract`); errorThrown = true; @@ -461,7 +461,7 @@ contract('SecurityToken', accounts => { await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCost, { from: token_owner}); let errorThrown = false; try { - let tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, web3.utils.toWei("1000","ether"), 0, { from: token_owner, gas: 60000000 }); + let tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, web3.utils.toWei("1000","ether"), 0, { from: token_owner }); } catch (error) { console.log(` tx revert -> max cost too small`); errorThrown = true; @@ -478,7 +478,7 @@ contract('SecurityToken', accounts => { await I_PolyToken.getTokens(cappedSTOSetupCost, token_owner); await I_PolyToken.transfer(I_SecurityToken.address, cappedSTOSetupCost, { from: token_owner}); - const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner, gas: 60000000 }); + const tx = await I_SecurityToken.addModule(I_CappedSTOFactory.address, bytesSTO, maxCost, 0, { from: token_owner }); assert.equal(tx.logs[3].args._type, stoKey, "CappedSTO doesn't get deployed"); assert.equal(web3.utils.toUtf8(tx.logs[3].args._name), "CappedSTO", "CappedSTOFactory module was not added"); @@ -1106,36 +1106,6 @@ contract('SecurityToken', accounts => { await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), {from: account_temp}); }); - it("Should fail to call the burn the tokens because token burner contract is not set", async() => { - let errorThrown = false; - try { - await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp }); - } catch(error) { - console.log(' tx revert -> Token burner contract is not set'.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - }); - - it("Should fail to call the burn the tokens because TM does not allow it", async ()=> { - // Deploy the token burner contract - I_TokenBurner = await TokenBurner.new(I_SecurityToken.address, { from: token_owner }); - - await I_SecurityToken.setTokenBurner(I_TokenBurner.address, { from: token_owner }); - assert.equal(await I_SecurityToken.tokenBurner.call(), I_TokenBurner.address); - let errorThrown = false; - try { - await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp }); - } catch(error) { - console.log(' tx revert -> Token burner contract is not set'.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - - }); - it("Should check that the list of investors is correct", async ()=> { // Hardcode list of expected accounts based on transfers above let investorsLength = await I_SecurityToken.getInvestorsLength(); @@ -1148,27 +1118,76 @@ contract('SecurityToken', accounts => { assert.equal(investor, expectedAccounts[i]); } }); + it("Should fail to set controller status because msg.sender not owner", async() => { + let errorThrown = false; + try { + await I_SecurityToken.setController(account_controller, {from: account_controller}); + } catch (error) { + console.log(` tx revert -> msg.sender not owner`.grey); + errorThrown = true; + ensureException(error); + } + assert.ok(errorThrown, message); + }); - it("Should burn the tokens", async ()=> { - let errorThrown = false; - await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, {from : token_owner}); - let currentInvestorCount = await I_SecurityToken.investorCount(); - let currentBalance = await I_SecurityToken.balanceOf(account_temp); - try { - let tx = await I_SecurityToken.burn(currentBalance + web3.utils.toWei("500", "ether"), { from: account_temp }); - } catch(error) { - console.log(` tx revert -> value is greater than its current balance`.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - }); + it("Should successfully set controller", async() => { + let tx1 = await I_SecurityToken.setController(account_controller, {from: token_owner}); + + // check event + assert.equal(address_zero, tx1.logs[0].args._oldController, "Event not emitted as expected"); + assert.equal(account_controller, tx1.logs[0].args._newController, "Event not emitted as expected"); + + let tx2 = await I_SecurityToken.setController(address_zero, {from: token_owner}); + + // check event + assert.equal(account_controller, tx2.logs[0].args._oldController, "Event not emitted as expected"); + assert.equal(address_zero, tx2.logs[0].args._newController, "Event not emitted as expected"); + + let tx3 = await I_SecurityToken.setController(account_controller, {from: token_owner}); + + // check event + assert.equal(address_zero, tx3.logs[0].args._oldController, "Event not emitted as expected"); + assert.equal(account_controller, tx3.logs[0].args._newController, "Event not emitted as expected"); + + // check status + let controller = await I_SecurityToken.controller.call(); + assert.equal(account_controller, controller, "Status not set correctly"); + }); + + it("Should force burn the tokens - value too high", async ()=> { + let errorThrown = false; + await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, {from : token_owner}); + let currentInvestorCount = await I_SecurityToken.investorCount(); + let currentBalance = await I_SecurityToken.balanceOf(account_temp); + try { + let tx = await I_SecurityToken.forceBurn(account_temp, currentBalance + web3.utils.toWei("500", "ether"), "", { from: account_controller }); + } catch(error) { + console.log(` tx revert -> value is greater than its current balance`.grey); + errorThrown = true; + ensureException(error); + } + assert.ok(errorThrown, message); + }); + it("Should force burn the tokens - wrong caller", async ()=> { + let errorThrown = false; + await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, {from : token_owner}); + let currentInvestorCount = await I_SecurityToken.investorCount(); + let currentBalance = await I_SecurityToken.balanceOf(account_temp); + try { + let tx = await I_SecurityToken.forceBurn(account_temp, currentBalance, "", { from: token_owner }); + } catch(error) { + console.log(` tx revert -> not owner`.grey); + errorThrown = true; + ensureException(error); + } + assert.ok(errorThrown, message); + }); it("Should burn the tokens", async ()=> { let currentInvestorCount = await I_SecurityToken.investorCount(); let currentBalance = await I_SecurityToken.balanceOf(account_temp); // console.log(currentInvestorCount.toString(), currentBalance.toString()); - let tx = await I_SecurityToken.burn(currentBalance, { from: account_temp }); + 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.investorCount(); @@ -1243,42 +1262,6 @@ contract('SecurityToken', accounts => { describe("Force Transfer", async() => { - it("Should fail to set controller status because msg.sender not owner", async() => { - let errorThrown = false; - try { - await I_SecurityToken.setController(account_controller, {from: account_controller}); - } catch (error) { - console.log(` tx revert -> msg.sender not owner`.grey); - errorThrown = true; - ensureException(error); - } - assert.ok(errorThrown, message); - }); - - it("Should successfully set controller", async() => { - let tx1 = await I_SecurityToken.setController(account_controller, {from: token_owner}); - - // check event - assert.equal(address_zero, tx1.logs[0].args._oldController, "Event not emitted as expected"); - assert.equal(account_controller, tx1.logs[0].args._newController, "Event not emitted as expected"); - - let tx2 = await I_SecurityToken.setController(address_zero, {from: token_owner}); - - // check event - assert.equal(account_controller, tx2.logs[0].args._oldController, "Event not emitted as expected"); - assert.equal(address_zero, tx2.logs[0].args._newController, "Event not emitted as expected"); - - let tx3 = await I_SecurityToken.setController(account_controller, {from: token_owner}); - - // check event - assert.equal(address_zero, tx3.logs[0].args._oldController, "Event not emitted as expected"); - assert.equal(account_controller, tx3.logs[0].args._newController, "Event not emitted as expected"); - - // check status - let controller = await I_SecurityToken.controller.call(); - assert.equal(account_controller, controller, "Status not set correctly"); - }); - it("Should fail to forceTransfer because not approved controller", async() => { let errorThrown1 = false; try { @@ -1332,11 +1315,12 @@ contract('SecurityToken', accounts => { assert.equal(start_investorCount.add(1).toNumber(), end_investorCount.toNumber(), "Investor count not changed"); assert.equal(start_balInv1.sub(web3.utils.toWei("10", "ether")).toNumber(), end_balInv1.toNumber(), "Investor balance not changed"); assert.equal(start_balInv2.add(web3.utils.toWei("10", "ether")).toNumber(), end_balInv2.toNumber(), "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._amount, "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"); diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 0f0fbd6e7..1acc0ef7d 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -225,7 +225,7 @@ contract('USDTieredSTO', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from: POLYMATH}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from: POLYMATH}); @@ -266,20 +266,6 @@ contract('USDTieredSTO', accounts => { "USDTieredSTOFactory contract was not deployed" ); - // STEP 7: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); - - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_USDTieredSTOFactory.address, { from: ISSUER }); - await I_MRProxied.verifyModule(I_USDTieredSTOFactory.address, true, { from: POLYMATH }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : POLYMATH }); @@ -313,6 +299,21 @@ contract('USDTieredSTO', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: POLYMATH}); await I_MRProxied.updateFromRegistry({from: POLYMATH}); + + // STEP 7: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_USDTieredSTOFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_USDTieredSTOFactory.address, true, { from: POLYMATH }); + // 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 diff --git a/test/q_usd_tiered_sto_sim.js b/test/q_usd_tiered_sto_sim.js index f8ce2c1ea..cd00ccb8d 100644 --- a/test/q_usd_tiered_sto_sim.js +++ b/test/q_usd_tiered_sto_sim.js @@ -195,7 +195,7 @@ contract('USDTieredSTO Sim', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from: POLYMATH}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from: POLYMATH}); @@ -228,29 +228,16 @@ contract('USDTieredSTO Sim', accounts => { I_USDTieredSTOProxyFactory = await USDTieredSTOProxyFactory.new({ from: POLYMATH }); // STEP 6: Deploy the USDTieredSTOFactory - + I_USDTieredSTOFactory = await USDTieredSTOFactory.new(I_PolyToken.address, STOSetupCost, 0, 0, I_USDTieredSTOProxyFactory.address, { from: ISSUER }); - + assert.notEqual( I_USDTieredSTOFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", "USDTieredSTOFactory contract was not deployed" ); - - - // STEP 7: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); - // (C) : Register the STOFactory - await I_MRProxied.registerModule(I_USDTieredSTOFactory.address, { from: ISSUER }); - await I_MRProxied.verifyModule(I_USDTieredSTOFactory.address, true, { from: POLYMATH }); // Step 8: Deploy the STFactory contract @@ -285,6 +272,20 @@ contract('USDTieredSTO Sim', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: POLYMATH}); await I_MRProxied.updateFromRegistry({from: POLYMATH}); + // STEP 7: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); + + // (C) : Register the STOFactory + await I_MRProxied.registerModule(I_USDTieredSTOFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_USDTieredSTOFactory.address, true, { from: POLYMATH }); + // 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 diff --git a/test/r_concurrent_STO.js b/test/r_concurrent_STO.js index 91b7ab7d5..f01a36436 100644 --- a/test/r_concurrent_STO.js +++ b/test/r_concurrent_STO.js @@ -78,7 +78,7 @@ contract('Concurrent STO', accounts => { const MRProxyParameters = ['address', 'address']; const DummySTOParameters = ['uint256', 'uint256', 'uint256', 'string']; const PresaleSTOParameters = ['uint256']; - + before(async() => { // Accounts setup account_polymath = accounts[0]; @@ -106,7 +106,7 @@ contract('Concurrent STO', accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -146,25 +146,6 @@ contract('Concurrent STO', accounts => { "CappedSTOFactory contract was not deployed" ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the STO Factories - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: account_issuer }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); - - await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_issuer }); - await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); - - await I_MRProxied.registerModule(I_PreSaleSTOFactory.address, { from: account_issuer }); - await I_MRProxied.verifyModule(I_PreSaleSTOFactory.address, true, { from: account_polymath }); // Step 8: Deploy the STFactory contract @@ -199,6 +180,26 @@ contract('Concurrent STO', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the STO Factories + await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: account_polymath }); + + await I_MRProxied.registerModule(I_DummySTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_DummySTOFactory.address, true, { from: account_polymath }); + + await I_MRProxied.registerModule(I_PreSaleSTOFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_PreSaleSTOFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- @@ -238,7 +239,7 @@ contract('Concurrent STO', accounts => { 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, gas: 85000000 }); + 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); diff --git a/test/s_v130_to_v140_upgrade.js b/test/s_v130_to_v140_upgrade.js index 6c1fa9102..4db50ca9f 100644 --- a/test/s_v130_to_v140_upgrade.js +++ b/test/s_v130_to_v140_upgrade.js @@ -164,19 +164,6 @@ contract('Upgrade from v1.3.0 to v1.4.0', accounts => { "CappedSTOFactory contract was not deployed" ); - // STEP 6: Register the Modules with the ModuleRegistry contract - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); - - // (C) : Register the CappedSTOFactory - await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: POLYMATH }); - await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: POLYMATH }); - // Step 8: Deploy the STFactory contract I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : POLYMATH }); assert.notEqual( @@ -220,6 +207,19 @@ contract('Upgrade from v1.3.0 to v1.4.0', accounts => { await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_STRProxied.address, {from: POLYMATH}); await I_MRProxied.updateFromRegistry({from: POLYMATH}); + // STEP 6: Register the Modules with the ModuleRegistry contract + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: POLYMATH }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: POLYMATH }); + + // (C) : Register the CappedSTOFactory + await I_MRProxied.registerModule(I_CappedSTOFactory.address, { from: POLYMATH }); + await I_MRProxied.verifyModule(I_CappedSTOFactory.address, true, { from: POLYMATH }); + // Step 12: Mint tokens to ISSUERs await I_PolyToken.getTokens(REGFEE * 2, ISSUER1); await I_PolyToken.getTokens(REGFEE * 2, ISSUER2); diff --git a/test/t_security_token_registry_proxy.js b/test/t_security_token_registry_proxy.js index dc258f670..2666f7158 100644 --- a/test/t_security_token_registry_proxy.js +++ b/test/t_security_token_registry_proxy.js @@ -83,7 +83,7 @@ contract ("SecurityTokenRegistryProxy", accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -103,10 +103,6 @@ contract ("SecurityTokenRegistryProxy", accounts => { // Register the Modules with the ModuleRegistry contract - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - // Step 3: Deploy the STFactory contract @@ -135,6 +131,10 @@ contract ("SecurityTokenRegistryProxy", accounts => { await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); await I_MRProxied.updateFromRegistry({from: account_polymath}); + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + // Printing all the contract addresses console.log(` @@ -185,7 +185,7 @@ contract ("SecurityTokenRegistryProxy", 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, gas: 85000000 }); + 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"); diff --git a/test/u_module_registry_proxy.js b/test/u_module_registry_proxy.js index 82583d594..b6fcc5db5 100644 --- a/test/u_module_registry_proxy.js +++ b/test/u_module_registry_proxy.js @@ -5,7 +5,7 @@ const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryProxy.sol"); const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); -const ModuleRegistryMock = artifacts.require("./ModuleRegistryMock.sol"); +const MockModuleRegistry = artifacts.require("./MockModuleRegistry.sol"); const OwnedUpgradeabilityProxy = artifacts.require('./OwnedUpgradeabilityProxy.sol'); const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const ModuleRegistry = artifacts.require('./ModuleRegistry.sol') @@ -26,7 +26,7 @@ contract ("ModuleRegistryProxy", accounts => { let I_SecurityTokenRegistryProxy; let I_GeneralTransferManagerFactory; let I_GeneralPermissionManagerfactory; - let I_ModuleRegistryMock; + let I_MockModuleRegistry; let I_STFactory; let I_PolymathRegistry; let I_ModuleRegistryProxy; @@ -85,7 +85,7 @@ contract ("ModuleRegistryProxy", accounts => { }); // STEP 3: Deploy the ModuleRegistry - + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); // Step 3 (b): Deploy the proxy and attach the implementation contract to it I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); @@ -160,7 +160,6 @@ contract ("ModuleRegistryProxy", accounts => { // Step 3: Deploy the STFactory contract - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); assert.notEqual( @@ -194,20 +193,15 @@ contract ("ModuleRegistryProxy", accounts => { }); - it("Should add the tags successfuly", async() => { - await I_MRProxied.addTagByModuleType(3,["Non-Refundable","Capped","ETH","POLY"],{from: account_polymath}); - let tags = await I_MRProxied.getTagByModuleType.call(3); - assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''),"Non-Refundable"); - }) }) describe("Upgrade the imlplementation address", async() => { it("Should upgrade the version and implementation address -- fail bad owner", async() => { let errorThrown = false; - I_ModuleRegistryMock = await ModuleRegistryMock.new({from: account_polymath}); + I_MockModuleRegistry = await MockModuleRegistry.new({from: account_polymath}); try { - await I_ModuleRegistryProxy.upgradeTo("1.1.0", I_ModuleRegistryMock.address, {from: account_temp}); + await I_ModuleRegistryProxy.upgradeTo("1.1.0", I_MockModuleRegistry.address, {from: account_temp}); } catch(error) { console.log(` tx -> revert bad owner of the proxy contract`); errorThrown = true; @@ -255,7 +249,7 @@ contract ("ModuleRegistryProxy", accounts => { it("Should upgrade the version and implementation address -- same version as previous is not allowed", async() => { let errorThrown = false; try { - await I_ModuleRegistryProxy.upgradeTo("1.0.0", I_ModuleRegistryMock.address, {from: account_polymath}); + await I_ModuleRegistryProxy.upgradeTo("1.0.0", I_MockModuleRegistry.address, {from: account_polymath}); } catch(error) { console.log(` tx -> revert same version as previous is not allowed`); errorThrown = true; @@ -267,7 +261,7 @@ contract ("ModuleRegistryProxy", accounts => { it("Should upgrade the version and implementation address -- empty version string is not allowed", async() => { let errorThrown = false; try { - await I_ModuleRegistryProxy.upgradeTo("", I_ModuleRegistryMock.address, {from: account_polymath}); + await I_ModuleRegistryProxy.upgradeTo("", I_MockModuleRegistry.address, {from: account_polymath}); } catch(error) { console.log(` tx -> revert empty version string is not allowed`); errorThrown = true; @@ -277,27 +271,25 @@ contract ("ModuleRegistryProxy", accounts => { }); it("Should upgrade the version and the implementation address successfully", async() => { - await I_ModuleRegistryProxy.upgradeTo("1.1.0", I_ModuleRegistryMock.address, {from: account_polymath}); + await I_ModuleRegistryProxy.upgradeTo("1.1.0", I_MockModuleRegistry.address, {from: account_polymath}); let c = OwnedUpgradeabilityProxy.at(I_ModuleRegistryProxy.address); assert.equal((web3.utils.toAscii(await readStorage(c.address, 11)).replace(/\u0000/g, '')).replace(/\n/, ''), "1.1.0", "Version mis-match"); - assert.equal(await readStorage(c.address, 12), I_ModuleRegistryMock.address, "Implemnted address is not matched"); - I_MRProxied = await ModuleRegistryMock.at(I_ModuleRegistryProxy.address); + assert.equal(await readStorage(c.address, 12), I_MockModuleRegistry.address, "Implemnted address is not matched"); + I_MRProxied = await MockModuleRegistry.at(I_ModuleRegistryProxy.address); }); }); describe("Execute functionality of the implementation contract on the earlier storage", async() => { it("Should get the previous data", async() => { - let _data = await I_MRProxied.getTagByModuleType.call(3); - assert.equal(web3.utils.toUtf8(_data[0]), "Non-Refundable"); - assert.equal(web3.utils.toUtf8(_data[1]), "Capped"); - assert.equal(web3.utils.toUtf8(_data[2]), "ETH"); + let _data = await I_MRProxied.getReputationByFactory.call(I_GeneralTransferManagerFactory.address); + assert.equal(_data.length, 0, "Should give the original length"); }); it("Should alter the old storage", async() => { - await I_MRProxied.addMoreTags(3, ["DAI", "USDTiered"], {from: account_polymath}); - let _data = await I_MRProxied.getTagByModuleType.call(3); - assert.equal(_data.length, 6, "Should give the updated length"); + await I_MRProxied.addMoreReputation(I_GeneralTransferManagerFactory.address, [account_polymath, account_temp], {from: account_polymath}); + let _data = await I_MRProxied.getReputationByFactory.call(I_GeneralTransferManagerFactory.address); + assert.equal(_data.length, 2, "Should give the updated length"); }); }) diff --git a/test/v_tracked_redemptions.js b/test/v_tracked_redemptions.js new file mode 100644 index 000000000..95f3303ae --- /dev/null +++ b/test/v_tracked_redemptions.js @@ -0,0 +1,379 @@ +import latestTime from './helpers/latestTime'; +import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall } from './helpers/encodeCall'; + +const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') +const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); +const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); +const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); +const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); +const STFactory = artifacts.require('./STFactory.sol'); +const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); +const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const TrackedRedemptionFactory = artifacts.require('./TrackedRedemptionFactory.sol'); +const TrackedRedemption = artifacts.require('./TrackedRedemption'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.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('TrackedRedemption', 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_temp; + + // 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_TrackedRedemptionFactory; + let I_GeneralPermissionManager; + let I_TrackedRedemption; + let I_GeneralTransferManager; + let I_ExchangeTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_MRProxied; + 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"; + let snapId; + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + const checkpointKey = 4; + const burnKey = 5; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("250"); + const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; + const MRProxyParameters = ['address', 'address']; + + 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_temp = accounts[2]; + + // ----------- POLYMATH NETWORK Configuration ------------ + + // 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); + + // Step 2: Deploy the FeatureRegistry + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, + { + from: account_polymath + }); + + // STEP 3: Deploy the ModuleRegistry + + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); + // Step 3 (b): Deploy the proxy and attach the implementation contract to it + I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); + let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); + await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, {from: account_polymath}); + I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); + + // STEP 4: Deploy the GeneralTransferManagerFactory + + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); + + // STEP 5: Deploy the GeneralDelegateManagerFactory + + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); + + // STEP 4: Deploy the TrackedRedemption + I_TrackedRedemptionFactory = await TrackedRedemptionFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + assert.notEqual( + I_TrackedRedemptionFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "TrackedRedemptionFactory contract was not deployed" + ); + + + // Step 6: Deploy the STFactory contract + + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); + + assert.notEqual( + I_STFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STFactory contract was not deployed", + ); + + // Step 7: Deploy the SecurityTokenRegistry contract + + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); + + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); + + // Step 8: Deploy the proxy and attach the implementation contract to it. + I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); + let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + + // Step 9: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); + await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); + await I_MRProxied.updateFromRegistry({from: account_polymath}); + + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the TrackedRedemptionFactory + await I_MRProxied.registerModule(I_TrackedRedemptionFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_TrackedRedemptionFactory.address, true, { from: account_polymath }); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${PolymathRegistry.address} + SecurityTokenRegistryProxy: ${SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${SecurityTokenRegistry.address} + ModuleRegistry: ${ModuleRegistry.address} + ModuleRegistryProxy: ${ModuleRegistryProxy.address} + FeatureRegistry: ${FeatureRegistry.address} + + STFactory: ${STFactory.address} + GeneralTransferManagerFactory: ${GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${GeneralPermissionManagerFactory.address} + + TrackedRedemptionFactory: ${I_TrackedRedemptionFactory.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._type.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.modules(2, 0); + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + + }); + + 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 }); + assert.equal(tx.logs[2].args._type.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); + }); + }); + + describe("Make Redemptions", async() => { + + it("Buy some tokens for account_investor1 (1 ETH)", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(30), + true, + { + from: account_issuer, + gas: 500000 + }); + + 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('1', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); + + it("Buy some tokens for account_investor2 (2 ETH)", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(30), + true, + { + from: account_issuer, + gas: 500000 + }); + + 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('2', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Redeem some tokens - fail insufficient allowance", async() => { + await I_GeneralTransferManager.changeAllowAllBurnTransfers(true, {from : token_owner}); + + let errorThrown = false; + try { + let tx = await I_TrackedRedemption.redeemTokens(web3.utils.toWei('1', 'ether'), {from: account_investor1}); + } catch(error) { + console.log(` tx -> failed insufficent allowance`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + + 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}); + 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"); + }); + + it("Get the init data", async() => { + let tx = await I_TrackedRedemption.getInitFunction.call(); + assert.equal(web3.utils.toAscii(tx).replace(/\u0000/g, ''),0); + }); + + it("Should get the listed permissions", async() => { + let tx = await I_TrackedRedemption.getPermissions.call(); + assert.equal(tx.length,0); + }); + + describe("Test cases for the TrackedRedemptionFactory", async() => { + it("should get the exact details of the factory", async() => { + assert.equal((await I_TrackedRedemptionFactory.setupCost.call()).toNumber(), 0); + assert.equal(await I_TrackedRedemptionFactory.getType.call(), 5); + assert.equal(web3.utils.toAscii(await I_TrackedRedemptionFactory.getName.call()) + .replace(/\u0000/g, ''), + "TrackedRedemption", + "Wrong Module added"); + assert.equal(await I_TrackedRedemptionFactory.getDescription.call(), + "Track token redemptions", + "Wrong Module added"); + assert.equal(await I_TrackedRedemptionFactory.getTitle.call(), + "Tracked Redemption", + "Wrong Module added"); + assert.equal(await I_TrackedRedemptionFactory.getInstructions.call(), + "Allows an investor to redeem security tokens which are tracked by this module", + "Wrong Module added"); + let tags = await I_TrackedRedemptionFactory.getTags.call(); + assert.equal(tags.length, 2); + + }); + }); + + }); + +});