diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index da3532e32..13b2b2372 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -15,6 +15,7 @@ let countTransferManagerABI; let percentageTransferManagerABI; let lockUpTransferManagerABI; let volumeRestrictionTMABI; +let restrictedPartialSaleTMABI; let generalPermissionManagerABI; let polyTokenABI; let cappedSTOFactoryABI; @@ -46,6 +47,7 @@ try { percentageTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PercentageTransferManager.json`).toString()).abi; blacklistTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/BlacklistTransferManager.json`).toString()).abi; volumeRestrictionTMABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/VolumeRestrictionTM.json`).toString()).abi; + restrictedPartialSaleTMABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/RestrictedPartialSaleTM.json`).toString()).abi; lockUpTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/LockUpTransferManager.json`).toString()).abi; generalPermissionManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralPermissionManager.json`).toString()).abi; polyTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).abi; @@ -118,6 +120,9 @@ module.exports = { volumeRestrictionTM: function () { return volumeRestrictionTMABI; }, + restrictedPartialSaleTM: function () { + return restrictedPartialSaleTMABI; + }, generalPermissionManager: function () { return generalPermissionManagerABI; }, diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 7ee86e526..9b1197eca 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -34,6 +34,7 @@ const MODIFY_LOCKUP_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/modify_lo const DELETE_LOCKUP_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/delete_lockup_data.csv`; const ADD_LOCKUP_INVESTOR_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/add_lockup_investor_data.csv`; const REMOVE_LOCKUP_INVESTOR_DATA_CSV = `${__dirname}/../data/Transfer/LockupTM/remove_lockup_investor_data.csv`; +const CHANGE_EXEMPT_LIST_DATA_CSV = `${__dirname}/../data/Transfer/RPSTM/change_exempt_list_data.csv`; const RESTRICTION_TYPES = ['Fixed', 'Percentage']; @@ -365,6 +366,11 @@ async function configExistingModules(tmModules) { currentTransferManager.setProvider(web3.currentProvider); await volumeRestrictionTM(); break; + case 'RestrictedPartialSaleTM': + currentTransferManager = new web3.eth.Contract(abis.restrictedPartialSaleTM(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await restrictedPartialSaleTM(); + break; } } @@ -385,6 +391,10 @@ async function addTransferManagerModule() { moduleAbi = abis.percentageTransferManager(); getInitializeData = getPercentageTMInitializeData; break; + case 'RestrictedPartialSaleTM': + moduleAbi = abis.restrictedPartialSaleTM(); + getInitializeData = getRestrictedPartialSaleTM; + break; } await common.addModule(securityToken, polyToken, moduleList[index].factoryAddress, moduleAbi, getInitializeData); } @@ -405,6 +415,13 @@ function getCountTMInitializeData(moduleABI) { return bytes; } +function getRestrictedPartialSaleTM(moduleABI) { + const treasuryWallet = input.readAddress('Enter the Ethereum address of the treasury wallet to be exempted (or leave empty to use treasury wallet from ST): ', gbl.constants.ADDRESS_ZERO); + const configureRPSTM = moduleABI.find(o => o.name === 'configure' && o.type === 'function'); + const bytes = web3.eth.abi.encodeFunctionCall(configureRPSTM, [treasuryWallet]); + return bytes; +} + async function generalTransferManager() { console.log('\n', chalk.blue(`General Transfer Manager at ${currentTransferManager.options.address}`), '\n'); @@ -2652,6 +2669,81 @@ async function removeLockupsFromInvestorsInBatch() { } } +async function restrictedPartialSaleTM() { + console.log('\n', chalk.blue(`Restriction Partial Sale Transfer Manager at ${currentTransferManager.options.address}`, '\n')); + + let exemptedAddresses = await currentTransferManager.methods.getExemptAddresses().call(); + console.log(`- Exempted addresses: ${exemptedAddresses.length}`); + + let options = []; + if (exemptedAddresses.length > 0) { + options.push('Show exempted addresses'); + } + options.push( + 'Verify transfer', + 'Change exempt wallet', + 'Change multiple exemptions', + 'Check if account is exempted' + ); + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Show exempted addresses': + showExemptedAddresses(exemptedAddresses); + break; + case 'Verify transfer': + await verifyTransfer(true, false); + break; + case 'Change exempt wallet': + await changeExemptWallet(); + break; + case 'Change multiple exemptions': + await changeExemptWalletsInBatch(); + break; + case 'Check if account is exempted': + await checkIfExempted(); + break; + case 'RETURN': + return; + } + + await restrictedPartialSaleTM(); +} + +async function changeExemptWalletsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${CHANGE_EXEMPT_LIST_DATA_CSV}): `, { + defaultInput: CHANGE_EXEMPT_LIST_DATA_CSV + }); + let batchSize = input.readNumberGreaterThan(0, `Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, gbl.constants.DEFAULT_BATCH_SIZE); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && typeof row[1] === 'boolean'); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, exemptedArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to change the exempted status to the following accounts: \n\n`, holderArray[batch], '\n'); + let action = currentTransferManager.methods.changeExemptWalletListMulti(holderArray[batch], exemptedArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Change exempt list transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function checkIfExempted() { + let account = input.readAddress('Enter the account to check: '); + let isExempted = await currentTransferManager.methods.exemptIndex(account).call(); + if (isExempted !== '0') { + console.log(chalk.yellow(`${account} is exepmted.`)); + } else { + console.log(chalk.green(`${account} is not exepmted.`)); + } +} + /* // Copied from tests function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, pk) { diff --git a/CLI/data/Transfer/RPSTM/change_exempt_list_data.csv b/CLI/data/Transfer/RPSTM/change_exempt_list_data.csv new file mode 100644 index 000000000..39e350811 --- /dev/null +++ b/CLI/data/Transfer/RPSTM/change_exempt_list_data.csv @@ -0,0 +1,8 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,true +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,true +0xac297053173b02b02a737d47f7b4a718e5b170ef,true +0x49fc0b78238dab644698a90fa351b4c749e123d2,true +0x10223927009b8add0960359dd90d1449415b7ca9,true +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,true +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,true +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,true \ No newline at end of file