@@ -10,6 +10,7 @@ const csvParse = require('./helpers/csv');
1010///////////////////
1111// Constants
1212const WHITELIST_DATA_CSV = './CLI/data/Transfer/GTM/whitelist_data.csv' ;
13+ const PERCENTAGE_WHITELIST_DATA_CSV = './CLI/data/Transfer/PercentageTM/whitelist_data.csv' ;
1314
1415// App flow
1516let tokenSymbol ;
@@ -184,7 +185,7 @@ async function configExistingModules(tmModules) {
184185 let options = tmModules . map ( m => `${ m . name } at ${ m . address } ` ) ;
185186 let index = readlineSync . keyInSelect ( options , 'Which module do you want to config? ' , { cancel : 'Return' } ) ;
186187 console . log ( 'Selected:' , index != - 1 ? options [ index ] : 'Return' , '\n' ) ;
187- let moduleNameSelected = tmModules [ index ] . name ;
188+ let moduleNameSelected = index != - 1 ? tmModules [ index ] . name : 'Return' ;
188189
189190 switch ( moduleNameSelected ) {
190191 case 'GeneralTransferManager' :
@@ -197,16 +198,15 @@ async function configExistingModules(tmModules) {
197198 currentTransferManager . setProvider ( web3 . currentProvider ) ;
198199 await manualApprovalTransferManager ( ) ;
199200 break ;
200- case 'PercentageTransferManager' :
201- //await percentageTransferManager();
202- console . log ( chalk . red ( `
203- *********************************
204- This option is not yet available.
205- *********************************`
206- ) ) ;
207- break ;
208201 case 'CountTransferManager' :
209- //await countTransferManager();
202+ currentTransferManager = new web3 . eth . Contract ( abis . countTransferManager ( ) , tmModules [ index ] . address ) ;
203+ currentTransferManager . setProvider ( web3 . currentProvider ) ;
204+ await countTransferManager ( ) ;
205+ break ;
206+ case 'PercentageTransferManager' :
207+ currentTransferManager = new web3 . eth . Contract ( abis . percentageTransferManager ( ) , tmModules [ index ] . address ) ;
208+ currentTransferManager . setProvider ( web3 . currentProvider ) ;
209+ await percentageTransferManager ( ) ;
210210 break ;
211211 case 'SingleTradeVolumeRestrictionTM' :
212212 //currentTransferManager = new web3.eth.Contract(abis.singleTradeVolumeRestrictionTM(), tmModules[index].address);
@@ -230,26 +230,34 @@ async function configExistingModules(tmModules) {
230230}
231231
232232async function addTransferManagerModule ( ) {
233- let options = [ 'GeneralTransferManager' , 'ManualApprovalTransferManager' /*, 'PercentageTransferManager',
234- 'CountTransferManager', 'SingleTradeVolumeRestrictionTM', 'LookupVolumeRestrictionTM'*/ ] ;
233+ let options = [
234+ 'GeneralTransferManager' ,
235+ 'ManualApprovalTransferManager' ,
236+ 'CountTransferManager' ,
237+ 'PercentageTransferManager' ,
238+ //'SingleTradeVolumeRestrictionTM',
239+ //'LookupVolumeRestrictionTM'*/
240+ ] ;
235241
236242 let index = readlineSync . keyInSelect ( options , 'Which Transfer Manager module do you want to add? ' , { cancel : 'Return' } ) ;
237243 if ( index != - 1 && readlineSync . keyInYNStrict ( `Are you sure you want to add ${ options [ index ] } module?` ) ) {
238244 let bytes = web3 . utils . fromAscii ( '' , 16 ) ;
239245 switch ( options [ index ] ) {
240- case 'PercentageTransferManager' :
241- console . log ( chalk . red ( `
242- *********************************
243- This option is not yet available.
244- *********************************`
245- ) ) ;
246- break ;
247246 case 'CountTransferManager' :
248- console . log ( chalk . red ( `
249- *********************************
250- This option is not yet available.
251- *********************************`
252- ) ) ;
247+ let maxHolderCount = readlineSync . question ( 'Enter the maximum no. of holders the SecurityToken is allowed to have: ' ) ;
248+ let configureCountTM = abis . countTransferManager ( ) . find ( o => o . name === 'configure' && o . type === 'function' ) ;
249+ bytes = web3 . eth . abi . encodeFunctionCall ( configureCountTM , [ maxHolderCount ] ) ;
250+ break ;
251+ case 'PercentageTransferManager' :
252+ let maxHolderPercentage = toWeiPercentage ( readlineSync . question ( 'Enter the maximum amount of tokens in percentage that an investor can hold: ' , {
253+ limit : function ( input ) {
254+ return ( parseInt ( input ) > 0 && parseInt ( input ) <= 100 ) ;
255+ } ,
256+ limitMessage : "Must be greater than 0 and less than 100"
257+ } ) ) ;
258+ let allowPercentagePrimaryIssuance = readlineSync . keyInYNStrict ( `Do you want to ignore transactions which are part of the primary issuance? ` ) ;
259+ let configurePercentageTM = abis . percentageTransferManager ( ) . find ( o => o . name === 'configure' && o . type === 'function' ) ;
260+ bytes = web3 . eth . abi . encodeFunctionCall ( configurePercentageTM , [ maxHolderPercentage , allowPercentagePrimaryIssuance ] ) ;
253261 break ;
254262 case 'SingleTradeVolumeRestrictionTM' :
255263 /*
@@ -511,7 +519,7 @@ async function modifyWhitelistInBatch() {
511519 console . log ( `Batch ${ batch + 1 } - Attempting to modify whitelist to accounts: \n\n` , investorArray [ batch ] , '\n' ) ;
512520 let action = await currentTransferManager . methods . modifyWhitelistMulti ( investorArray [ batch ] , fromTimesArray [ batch ] , toTimesArray [ batch ] , expiryTimeArray [ batch ] , canBuyFromSTOArray [ batch ] ) ;
513521 let receipt = await common . sendTransaction ( action ) ;
514- console . log ( chalk . green ( 'Whitelist transaction was successful.' ) ) ;
522+ console . log ( chalk . green ( 'Modify whitelist transaction was successful.' ) ) ;
515523 console . log ( `${ receipt . gasUsed } gas used.Spent: ${ web3 . utils . fromWei ( ( new web3 . utils . BN ( receipt . gasUsed ) ) . mul ( new web3 . utils . BN ( defaultGasPrice ) ) ) } ETH` ) ;
516524 }
517525}
@@ -691,6 +699,134 @@ async function getManualBlocking(_from, _to) {
691699 return result ;
692700}
693701
702+ async function countTransferManager ( ) {
703+ console . log ( chalk . blue ( `Count Transfer Manager at ${ currentTransferManager . options . address } ` ) , '\n' ) ;
704+
705+ // Show current data
706+ let displayMaxHolderCount = await currentTransferManager . methods . maxHolderCount ( ) . call ( ) ;
707+
708+ console . log ( `- Max holder count: ${ displayMaxHolderCount } ` ) ;
709+
710+ let options = [ 'Change max holder count' ]
711+ let index = readlineSync . keyInSelect ( options , 'What do you want to do?' , { cancel : 'Return' } ) ;
712+ let optionSelected = options [ index ] ;
713+ console . log ( 'Selected:' , index != - 1 ? optionSelected : 'Return' , '\n' ) ;
714+ switch ( optionSelected ) {
715+ case 'Change max holder count' :
716+ let maxHolderCount = readlineSync . question ( 'Enter the maximum no. of holders the SecurityToken is allowed to have: ' ) ;
717+ let changeHolderCountAction = currentTransferManager . methods . changeHolderCount ( maxHolderCount ) ;
718+ let changeHolderCountReceipt = await common . sendTransaction ( changeHolderCountAction ) ;
719+ let changeHolderCountEvent = common . getEventFromLogs ( currentTransferManager . _jsonInterface , changeHolderCountReceipt . logs , 'ModifyHolderCount' ) ;
720+ console . log ( chalk . green ( `Max holder count has been set to ${ changeHolderCountEvent . _newHolderCount } sucessfully!` ) ) ;
721+ break ;
722+ }
723+ }
724+
725+ async function percentageTransferManager ( ) {
726+ console . log ( chalk . blue ( `Percentage Transfer Manager at ${ currentTransferManager . options . address } ` ) , '\n' ) ;
727+
728+ // Show current data
729+ let displayMaxHolderPercentage = await currentTransferManager . methods . maxHolderPercentage ( ) . call ( ) ;
730+ let displayAllowPrimaryIssuance = await currentTransferManager . methods . allowPrimaryIssuance ( ) . call ( ) ;
731+
732+ console . log ( `- Max holder percentage: ${ fromWeiPercentage ( displayMaxHolderPercentage ) } %` ) ;
733+ console . log ( `- Allow primary issuance: ${ displayAllowPrimaryIssuance ? `YES` : `NO` } ` ) ;
734+
735+ let options = [ 'Change max holder percentage' , 'Check if investor is whitelisted' , 'Modify whitelist' , 'Modify whitelist from CSV' ] ;
736+ if ( displayAllowPrimaryIssuance ) {
737+ options . push ( 'Disallow primary issuance' ) ;
738+ } else {
739+ options . push ( 'Allow primary issuance' ) ;
740+ }
741+ let index = readlineSync . keyInSelect ( options , 'What do you want to do?' , { cancel : 'Return' } ) ;
742+ let optionSelected = options [ index ] ;
743+ console . log ( 'Selected:' , index != - 1 ? optionSelected : 'Return' , '\n' ) ;
744+ switch ( optionSelected ) {
745+ case 'Change max holder percentage' :
746+ let maxHolderPercentage = toWeiPercentage ( readlineSync . question ( 'Enter the maximum amount of tokens in percentage that an investor can hold: ' , {
747+ limit : function ( input ) {
748+ return ( parseInt ( input ) > 0 && parseInt ( input ) <= 100 ) ;
749+ } ,
750+ limitMessage : "Must be greater than 0 and less than 100"
751+ } ) ) ;
752+ let changeHolderPercentageAction = currentTransferManager . methods . changeHolderPercentage ( maxHolderPercentage ) ;
753+ let changeHolderPercentageReceipt = await common . sendTransaction ( changeHolderPercentageAction ) ;
754+ let changeHolderPercentageEvent = common . getEventFromLogs ( currentTransferManager . _jsonInterface , changeHolderPercentageReceipt . logs , 'ModifyHolderPercentage' ) ;
755+ console . log ( chalk . green ( `Max holder percentage has been set to ${ fromWeiPercentage ( changeHolderPercentageEvent . _newHolderPercentage ) } successfully!` ) ) ;
756+ break ;
757+ case 'Check if investor is whitelisted' :
758+ let investorToCheck = readlineSync . question ( 'Enter the address of the investor: ' , {
759+ limit : function ( input ) {
760+ return web3 . utils . isAddress ( input ) ;
761+ } ,
762+ limitMessage : "Must be a valid address"
763+ } ) ;
764+ let isWhitelisted = await currentTransferManager . methods . whitelist ( investorToCheck ) . call ( ) ;
765+ if ( isWhitelisted ) {
766+ console . log ( chalk . green ( `${ investorToCheck } is whitelisted!` ) ) ;
767+ } else {
768+ console . log ( chalk . yellow ( `${ investorToCheck } is not whitelisted!` ) ) ;
769+ }
770+ break ;
771+ case 'Modify whitelist' :
772+ let valid = ! ! readlineSync . keyInSelect ( [ 'Remove investor from whitelist' , 'Add investor to whitelist' ] , 'How do you want to do? ' , { cancel : false } ) ;
773+ let investorToWhitelist = readlineSync . question ( 'Enter the address of the investor: ' , {
774+ limit : function ( input ) {
775+ return web3 . utils . isAddress ( input ) ;
776+ } ,
777+ limitMessage : "Must be a valid address"
778+ } ) ;
779+ let modifyWhitelistAction = currentTransferManager . methods . modifyWhitelist ( investorToWhitelist , valid ) ;
780+ let modifyWhitelistReceipt = await common . sendTransaction ( modifyWhitelistAction ) ;
781+ let modifyWhitelistEvent = common . getEventFromLogs ( currentTransferManager . _jsonInterface , modifyWhitelistReceipt . logs , 'ModifyWhitelist' ) ;
782+ if ( modifyWhitelistEvent . _valid ) {
783+ console . log ( chalk . green ( `${ modifyWhitelistEvent . _investor } has been added to the whitelist sucessfully!` ) ) ;
784+ } else {
785+ console . log ( chalk . green ( `${ modifyWhitelistEvent . _investor } has been removed from the whitelist sucessfully!` ) ) ;
786+ }
787+ break ;
788+ case 'Modify whitelist from CSV' :
789+ let csvFilePath = readlineSync . question ( `Enter the path for csv data file (${ PERCENTAGE_WHITELIST_DATA_CSV } ): ` , {
790+ defaultInput : PERCENTAGE_WHITELIST_DATA_CSV
791+ } ) ;
792+ let batchSize = readlineSync . question ( `Enter the max number of records per transaction or batch size (${ gbl . constants . DEFAULT_BATCH_SIZE } ): ` , {
793+ limit : function ( input ) {
794+ return parseInt ( input ) > 0 ;
795+ } ,
796+ limitMessage : 'Must be greater than 0' ,
797+ defaultInput : gbl . constants . DEFAULT_BATCH_SIZE
798+ } ) ;
799+ let parsedData = csvParse ( csvFilePath ) ;
800+ let validData = parsedData . filter ( row => web3 . utils . isAddress ( row [ 0 ] ) && typeof row [ 1 ] === 'boolean' ) ;
801+ let invalidRows = parsedData . filter ( row => ! validData . includes ( row ) ) ;
802+ if ( invalidRows . length > 0 ) {
803+ console . log ( chalk . red ( `The following lines from csv file are not valid: ${ invalidRows . map ( r => parsedData . indexOf ( r ) + 1 ) . join ( ',' ) } ` ) ) ;
804+ }
805+ let batches = common . splitIntoBatches ( validData , batchSize ) ;
806+ let [ investorArray , isWhitelistedArray ] = common . transposeBatches ( batches ) ;
807+ for ( let batch = 0 ; batch < batches . length ; batch ++ ) {
808+ console . log ( `Batch ${ batch + 1 } - Attempting to modify whitelist accounts:\n\n` , investorArray [ batch ] , '\n' ) ;
809+ let action = await currentTransferManager . methods . modifyWhitelistMulti ( investorArray [ batch ] , isWhitelistedArray [ batch ] ) ;
810+ let receipt = await common . sendTransaction ( action ) ;
811+ console . log ( chalk . green ( 'Modify whitelist transaction was successful.' ) ) ;
812+ console . log ( `${ receipt . gasUsed } gas used. Spent: ${ web3 . utils . fromWei ( ( new web3 . utils . BN ( receipt . gasUsed ) ) . mul ( new web3 . utils . BN ( defaultGasPrice ) ) ) } ETH` ) ;
813+ }
814+ break ;
815+ case 'Allow primary issuance' :
816+ case 'Disallow primary issuance' :
817+ let setAllowPrimaryIssuanceAction = currentTransferManager . methods . setAllowPrimaryIssuance ( ! displayAllowPrimaryIssuance ) ;
818+ let setAllowPrimaryIssuanceReceipt = await common . sendTransaction ( setAllowPrimaryIssuanceAction ) ;
819+ let setAllowPrimaryIssuanceEvent = common . getEventFromLogs ( currentTransferManager . _jsonInterface , setAllowPrimaryIssuanceReceipt . logs , 'SetAllowPrimaryIssuance' ) ;
820+ if ( setAllowPrimaryIssuanceEvent . _allowPrimaryIssuance ) {
821+ console . log ( chalk . green ( `Transactions which are part of the primary issuance will be ignored!` ) ) ;
822+ } else {
823+ console . log ( chalk . green ( `Transactions which are part of the primary issuance will NOT be ignored!` ) ) ;
824+ }
825+ break ;
826+
827+ }
828+ }
829+
694830async function singleTradeVolumeRestrictionTM ( ) {
695831 console . log ( chalk . blue ( `Single Trade Volume Restriction Transfer Manager at ${ currentTransferManager . options . address } ` ) ) ;
696832 console . log ( ) ;
0 commit comments