Skip to content

Commit 923926e

Browse files
authored
Merge pull request #613 from PolymathNetwork/cli-dividend-fixes
[CLI] Dividend fixes
2 parents c604925 + a5b6c32 commit 923926e

File tree

6 files changed

+222
-79
lines changed

6 files changed

+222
-79
lines changed

CLI/commands/dividends_manager.js

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const gbl = require('./common/global');
66
const contracts = require('./helpers/contract_addresses');
77
const abis = require('./helpers/contract_abis');
88
const csvParse = require('./helpers/csv');
9+
const BigNumber = require('bignumber.js');
910
const { table } = require('table')
1011

1112
const EXCLUSIONS_DATA_CSV = `${__dirname}/../data/Checkpoint/exclusions_data.csv`;
@@ -146,7 +147,7 @@ async function dividendsManager() {
146147
if (currentDividends.length > 0) {
147148
options.push('Manage existing dividends');
148149
}
149-
options.push('Create new dividends');
150+
options.push('Create new dividends', 'Reclaim ETH or ERC20 tokens from contract');
150151

151152
let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' });
152153
let selected = index != -1 ? options[index] : 'RETURN';
@@ -178,6 +179,9 @@ async function dividendsManager() {
178179
case 'Create new dividends':
179180
await createDividends();
180181
break;
182+
case 'Reclaim ETH or ERC20 tokens from contract':
183+
await reclaimFromContract();
184+
break;
181185
case 'RETURN':
182186
return;
183187
}
@@ -238,9 +242,12 @@ async function manageExistingDividend(dividendIndex) {
238242
let dividend = await currentDividendsModule.methods.dividends(dividendIndex).call();
239243
let dividendTokenAddress = gbl.constants.ADDRESS_ZERO;
240244
let dividendTokenSymbol = 'ETH';
245+
let dividendTokenDecimals = 18;
241246
if (dividendsType === 'ERC20') {
242247
dividendTokenAddress = await currentDividendsModule.methods.dividendTokens(dividendIndex).call();
243248
dividendTokenSymbol = await getERC20TokenSymbol(dividendTokenAddress);
249+
let erc20token = new web3.eth.Contract(abis.erc20(), dividendTokenAddress);
250+
dividendTokenDecimals = await erc20token.methods.decimals().call();
244251
}
245252
let progress = await currentDividendsModule.methods.getDividendProgress(dividendIndex).call();
246253
let investorArray = progress[0];
@@ -266,12 +273,12 @@ async function manageExistingDividend(dividendIndex) {
266273
console.log(`- Maturity: ${moment.unix(dividend.maturity).format('MMMM Do YYYY, HH:mm:ss')}`);
267274
console.log(`- Expiry: ${moment.unix(dividend.expiry).format('MMMM Do YYYY, HH:mm:ss')}`);
268275
console.log(`- At checkpoint: ${dividend.checkpointId}`);
269-
console.log(`- Amount: ${web3.utils.fromWei(dividend.amount)} ${dividendTokenSymbol}`);
270-
console.log(`- Claimed amount: ${web3.utils.fromWei(dividend.claimedAmount)} ${dividendTokenSymbol}`);
276+
console.log(`- Amount: ${dividend.amount / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}`);
277+
console.log(`- Claimed amount: ${dividend.claimedAmount / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}`);
271278
console.log(`- Taxes:`);
272-
console.log(` To withhold: ${web3.utils.fromWei(taxesToWithHeld)} ${dividendTokenSymbol}`);
273-
console.log(` Withheld to-date: ${web3.utils.fromWei(dividend.totalWithheld)} ${dividendTokenSymbol}`);
274-
console.log(` Withdrawn to-date: ${web3.utils.fromWei(dividend.totalWithheldWithdrawn)} ${dividendTokenSymbol}`);
279+
console.log(` To withhold: ${taxesToWithHeld / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}`);
280+
console.log(` Withheld to-date: ${dividend.totalWithheld / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}`);
281+
console.log(` Withdrawn to-date: ${dividend.totalWithheldWithdrawn / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}`);
275282
console.log(`- Total investors: ${investorArray.length}`);
276283
console.log(` Have already claimed: ${claimedInvestors} (${investorArray.length - excludedInvestors !== 0 ? claimedInvestors / (investorArray.length - excludedInvestors) * 100 : 0}%)`);
277284
console.log(` Excluded: ${excludedInvestors} `);
@@ -300,6 +307,7 @@ async function manageExistingDividend(dividendIndex) {
300307
showReport(
301308
web3.utils.hexToUtf8(dividend.name),
302309
dividendTokenSymbol,
310+
dividendTokenDecimals,
303311
dividend.amount, // Total amount of dividends sent
304312
dividend.totalWithheld, // Total amount of taxes withheld
305313
dividend.claimedAmount, // Total amount of dividends distributed
@@ -314,14 +322,17 @@ async function manageExistingDividend(dividendIndex) {
314322
await pushDividends(dividendIndex, dividend.checkpointId);
315323
break;
316324
case 'Explore account':
317-
await exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol);
325+
await exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol, dividendTokenDecimals);
318326
break;
319327
case 'Withdraw withholding':
320-
await withdrawWithholding(dividendIndex, dividendTokenSymbol);
328+
await withdrawWithholding(dividendIndex, dividendTokenSymbol, dividendTokenDecimals);
321329
break;
322330
case 'Reclaim expired dividends':
323-
await reclaimedDividend(dividendIndex, dividendTokenSymbol);
331+
await reclaimedDividend(dividendIndex, dividendTokenSymbol, dividendTokenDecimals);
324332
return;
333+
case 'Reclaim ETH or ERC20 tokens from contract':
334+
await reclaim
335+
break;
325336
case 'RETURN':
326337
return;
327338
}
@@ -376,6 +387,7 @@ async function createDividends() {
376387
let dividendName = readlineSync.question(`Enter a name or title to indetify this dividend: `);
377388
let dividendToken = gbl.constants.ADDRESS_ZERO;
378389
let dividendSymbol = 'ETH';
390+
let dividendTokenDecimals = 18;
379391
let token;
380392
if (dividendsType === 'ERC20') {
381393
do {
@@ -390,17 +402,18 @@ async function createDividends() {
390402
if (erc20Symbol != null) {
391403
token = new web3.eth.Contract(abis.erc20(), dividendToken);
392404
dividendSymbol = erc20Symbol;
405+
dividendTokenDecimals = await token.methods.decimals().call();
393406
} else {
394407
console.log(chalk.red(`${dividendToken} is not a valid ERC20 token address!!`));
395408
}
396409
} while (dividendSymbol === 'ETH');
397410
}
398411
let dividendAmount = readlineSync.question(`How much ${dividendSymbol} would you like to distribute to token holders? `);
399412

400-
let dividendAmountBN = new web3.utils.BN(dividendAmount);
401-
let issuerBalance = new web3.utils.BN(web3.utils.fromWei(await getBalance(Issuer.address, dividendToken)));
413+
let dividendAmountBN = new BigNumber(dividendAmount).times(Math.pow(10, dividendTokenDecimals));
414+
let issuerBalance = new BigNumber(await getBalance(Issuer.address, dividendToken));
402415
if (issuerBalance.lt(dividendAmountBN)) {
403-
console.log(chalk.red(`You have ${issuerBalance} ${dividendSymbol}.You need ${dividendAmountBN.sub(issuerBalance)} ${dividendSymbol} more!`));
416+
console.log(chalk.red(`You have ${issuerBalance / Math.pow(10, dividendTokenDecimals)} ${dividendSymbol}. You need ${(dividendAmountBN - issuerBalance) / Math.pow(10, dividendTokenDecimals)} ${dividendSymbol} more!`));
404417
} else {
405418
let checkpointId = await selectCheckpoint(true); // If there are no checkpoints, it must create a new one
406419
let now = Math.floor(Date.now() / 1000);
@@ -412,21 +425,21 @@ async function createDividends() {
412425

413426
let createDividendAction;
414427
if (dividendsType == 'ERC20') {
415-
let approveAction = token.methods.approve(currentDividendsModule._address, web3.utils.toWei(dividendAmountBN));
428+
let approveAction = token.methods.approve(currentDividendsModule._address, dividendAmountBN);
416429
await common.sendTransaction(approveAction);
417430
if (checkpointId > 0) {
418431
if (useDefaultExcluded) {
419-
createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), checkpointId, web3.utils.toHex(dividendName));
432+
createDividendAction = currentDividendsModule.methods.createDividendWithCheckpoint(maturityTime, expiryTime, token.options.address, dividendAmountBN, checkpointId, web3.utils.toHex(dividendName));
420433
} else {
421434
let excluded = getExcludedFromDataFile();
422-
createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), checkpointId, excluded[0], web3.utils.toHex(dividendName));
435+
createDividendAction = currentDividendsModule.methods.createDividendWithCheckpointAndExclusions(maturityTime, expiryTime, token.options.address, dividendAmountBN, checkpointId, excluded[0], web3.utils.toHex(dividendName));
423436
}
424437
} else {
425438
if (useDefaultExcluded) {
426-
createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), web3.utils.toHex(dividendName));
439+
createDividendAction = currentDividendsModule.methods.createDividend(maturityTime, expiryTime, token.options.address, dividendAmountBN, web3.utils.toHex(dividendName));
427440
} else {
428441
let excluded = getExcludedFromDataFile();
429-
createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, token.options.address, web3.utils.toWei(dividendAmountBN), excluded[0], web3.utils.toHex(dividendName));
442+
createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, token.options.address, dividendAmountBN, excluded[0], web3.utils.toHex(dividendName));
430443
}
431444
}
432445
let receipt = await common.sendTransaction(createDividendAction);
@@ -448,7 +461,7 @@ async function createDividends() {
448461
createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, excluded, web3.utils.toHex(dividendName));
449462
}
450463
}
451-
let receipt = await common.sendTransaction(createDividendAction, { value: web3.utils.toWei(dividendAmountBN) });
464+
let receipt = await common.sendTransaction(createDividendAction, { value: dividendAmountBN });
452465
let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'EtherDividendDeposited');
453466
console.log(`
454467
Dividend ${ event._dividendIndex} deposited`
@@ -457,6 +470,33 @@ Dividend ${ event._dividendIndex} deposited`
457470
}
458471
}
459472

473+
async function reclaimFromContract() {
474+
let options = ['ETH', 'ERC20'];
475+
let index = readlineSync.keyInSelect(options, 'What do you want to reclaim?', { cancel: 'RETURN' });
476+
let selected = index != -1 ? options[index] : 'RETURN';
477+
switch (selected) {
478+
case 'ETH':
479+
let ethBalance = await web3.eth.getBalance(currentDividendsModule.options.address);
480+
console.log(chalk.yellow(`Current ETH balance: ${web3.utils.fromWei(ethBalance)} ETH`));
481+
let reclaimETHAction = currentDividendsModule.methods.reclaimETH();
482+
await common.sendTransaction(reclaimETHAction);
483+
console.log(chalk.green('ETH has been reclaimed succesfully!'));
484+
break;
485+
case 'ERC20':
486+
let erc20Address = readlineSync.question('Enter the ERC20 token address to reclaim (POLY = ' + polyToken.options.address + '): ', {
487+
limit: function (input) {
488+
return web3.utils.isAddress(input);
489+
},
490+
limitMessage: "Must be a valid address",
491+
defaultInput: polyToken.options.address
492+
});
493+
let reclaimERC20Action = currentDividendsModule.methods.reclaimERC20(erc20Address);
494+
await common.sendTransaction(reclaimERC20Action);
495+
console.log(chalk.green('ERC20 has been reclaimed succesfully!'));
496+
break
497+
}
498+
}
499+
460500
function showInvestors(investorsArray, claimedArray, excludedArray) {
461501
let dataTable = [['Investor', 'Has claimed', 'Is excluded']];
462502
for (let i = 0; i < investorsArray.length; i++) {
@@ -470,7 +510,7 @@ function showInvestors(investorsArray, claimedArray, excludedArray) {
470510
console.log(table(dataTable));
471511
}
472512

473-
function showReport(_name, _tokenSymbol, _amount, _witthheld, _claimed, _investorArray, _claimedArray, _excludedArray, _withheldArray, _amountArray) {
513+
function showReport(_name, _tokenSymbol, _tokenDecimals, _amount, _witthheld, _claimed, _investorArray, _claimedArray, _excludedArray, _withheldArray, _amountArray) {
474514
let title = `${_name.toUpperCase()} DIVIDEND REPORT`;
475515
let dataTable =
476516
[[
@@ -485,10 +525,10 @@ function showReport(_name, _tokenSymbol, _amount, _witthheld, _claimed, _investo
485525
let investor = _investorArray[i];
486526
let excluded = _excludedArray[i];
487527
let withdrawn = _claimedArray[i] ? 'YES' : 'NO';
488-
let amount = !excluded ? web3.utils.fromWei(web3.utils.toBN(_amountArray[i]).add(web3.utils.toBN(_withheldArray[i]))) : 0;
489-
let withheld = !excluded ? web3.utils.fromWei(_withheldArray[i]) : 'NA';
528+
let amount = !excluded ? new BigNumber(_amountArray[i]).plus(_withheldArray[i]).div((Math.pow(10, _tokenDecimals))) : 0;
529+
let withheld = !excluded ? parseFloat(_withheldArray[i]) / Math.pow(10, _tokenDecimals) : 'NA';
490530
let withheldPercentage = (!excluded) ? (withheld !== '0' ? parseFloat(withheld) / parseFloat(amount) * 100 : 0) : 'NA';
491-
let received = !excluded ? web3.utils.fromWei(_amountArray[i]) : 0;
531+
let received = !excluded ? parseFloat(_amountArray[i]) / Math.pow(10, _tokenDecimals) : 0;
492532
dataTable.push([
493533
investor,
494534
amount,
@@ -501,9 +541,9 @@ function showReport(_name, _tokenSymbol, _amount, _witthheld, _claimed, _investo
501541
console.log(chalk.yellow(`-----------------------------------------------------------------------------------------------------------------------------------------------------------`));
502542
console.log(title.padStart((50 - title.length) / 2, '*').padEnd((50 - title.length) / 2, '*'));
503543
console.log();
504-
console.log(`- Total amount of dividends sent: ${web3.utils.fromWei(_amount)} ${_tokenSymbol} `);
505-
console.log(`- Total amount of taxes withheld: ${web3.utils.fromWei(_witthheld)} ${_tokenSymbol} `);
506-
console.log(`- Total amount of dividends distributed: ${web3.utils.fromWei(_claimed)} ${_tokenSymbol} `);
544+
console.log(`- Total amount of dividends sent: ${parseFloat(_amount) / Math.pow(10, _tokenDecimals)} ${_tokenSymbol} `);
545+
console.log(`- Total amount of taxes withheld: ${parseFloat(_witthheld) / Math.pow(10, _tokenDecimals)} ${_tokenSymbol} `);
546+
console.log(`- Total amount of dividends distributed: ${parseFloat(_claimed) / Math.pow(10, _tokenDecimals)} ${_tokenSymbol} `);
507547
console.log(`- Total amount of investors: ${_investorArray.length} `);
508548
console.log();
509549
console.log(table(dataTable));
@@ -536,7 +576,7 @@ async function pushDividends(dividendIndex, checkpointId) {
536576
}
537577
}
538578

539-
async function exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol) {
579+
async function exploreAccount(dividendIndex, dividendTokenAddress, dividendTokenSymbol, dividendTokenDecimals) {
540580
let account = readlineSync.question('Enter address to explore: ', {
541581
limit: function (input) {
542582
return web3.utils.isAddress(input);
@@ -553,33 +593,33 @@ async function exploreAccount(dividendIndex, dividendTokenAddress, dividendToken
553593

554594
console.log();
555595
console.log(`Security token balance: ${web3.utils.fromWei(securityTokenBalance)} ${tokenSymbol} `);
556-
console.log(`Dividend token balance: ${web3.utils.fromWei(dividendTokenBalance)} ${dividendTokenSymbol} `);
596+
console.log(`Dividend token balance: ${dividendTokenBalance / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol} `);
557597
console.log(`Is excluded: ${isExcluded ? 'YES' : 'NO'} `);
558598
if (!isExcluded) {
559599
console.log(`Has claimed: ${hasClaimed ? 'YES' : 'NO'} `);
560600
if (!hasClaimed) {
561-
console.log(`Dividends available: ${web3.utils.fromWei(dividendBalance)} ${dividendTokenSymbol} `);
562-
console.log(`Tax withheld: ${web3.utils.fromWei(dividendTax)} ${dividendTokenSymbol} `);
601+
console.log(`Dividends available: ${dividendBalance / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol} `);
602+
console.log(`Tax withheld: ${dividendTax / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol} `);
563603
}
564604
}
565605
console.log();
566606
}
567607

568-
async function withdrawWithholding(dividendIndex, dividendTokenSymbol) {
608+
async function withdrawWithholding(dividendIndex, dividendTokenSymbol, dividendTokenDecimals) {
569609
let action = currentDividendsModule.methods.withdrawWithholding(dividendIndex);
570610
let receipt = await common.sendTransaction(action);
571611
let eventName = dividendsType === 'ERC20' ? 'ERC20DividendWithholdingWithdrawn' : 'EtherDividendWithholdingWithdrawn';
572612
let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, eventName);
573-
console.log(chalk.green(`Successfully withdrew ${web3.utils.fromWei(event._withheldAmount)} ${dividendTokenSymbol} from dividend ${event._dividendIndex} tax withholding.`));
613+
console.log(chalk.green(`Successfully withdrew ${event._withheldAmount / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol} from dividend ${event._dividendIndex} tax withholding.`));
574614
}
575615

576-
async function reclaimedDividend(dividendIndex, dividendTokenSymbol) {
616+
async function reclaimedDividend(dividendIndex, dividendTokenSymbol, dividendTokenDecimals) {
577617
let action = currentDividendsModule.methods.reclaimDividend(dividendIndex);
578618
let receipt = await common.sendTransaction(action);
579619
let eventName = dividendsType === 'ERC20' ? 'ERC20DividendReclaimed' : 'EtherDividendReclaimed';
580620
let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, eventName);
581621
console.log(`
582-
Reclaimed amount ${ web3.utils.fromWei(event._claimedAmount)} ${dividendTokenSymbol}
622+
Reclaimed amount ${event._claimedAmount / Math.pow(10, dividendTokenDecimals)} ${dividendTokenSymbol}
583623
to account ${ event._claimer} `
584624
);
585625
}
@@ -699,7 +739,7 @@ async function selectDividend(dividends) {
699739
let result = null;
700740
let options = dividends.map(function (d) {
701741
return `${d.name}
702-
Amount: ${web3.utils.fromWei(d.amount)} ${d.tokenSymbol}
742+
Amount: ${parseFloat(d.amount) / Math.pow(10, d.tokenDecimals)} ${d.tokenSymbol}
703743
Status: ${isExpiredDividend(d) ? 'Expired' : hasRemaining(d) ? 'In progress' : 'Completed'}
704744
Token: ${d.tokenSymbol}
705745
Created: ${moment.unix(d.created).format('MMMM Do YYYY, HH:mm:ss')}
@@ -715,7 +755,7 @@ async function selectDividend(dividends) {
715755
}
716756

717757
async function getDividends() {
718-
function DividendData(_index, _created, _maturity, _expiry, _amount, _claimedAmount, _name, _tokenSymbol) {
758+
function DividendData(_index, _created, _maturity, _expiry, _amount, _claimedAmount, _name, _tokenSymbol, _tokenDecimals) {
719759
this.index = _index;
720760
this.created = _created;
721761
this.maturity = _maturity;
@@ -724,6 +764,7 @@ async function getDividends() {
724764
this.claimedAmount = _claimedAmount;
725765
this.name = _name;
726766
this.tokenSymbol = _tokenSymbol;
767+
this.tokenDecimals = _tokenDecimals;
727768
}
728769

729770
let dividends = [];
@@ -736,9 +777,12 @@ async function getDividends() {
736777
let nameArray = dividendsData.names;
737778
for (let i = 0; i < nameArray.length; i++) {
738779
let tokenSymbol = 'ETH';
780+
let dividendTokenDecimals = 18;
739781
if (dividendsType === 'ERC20') {
740782
let tokenAddress = await currentDividendsModule.methods.dividendTokens(i).call();
741783
tokenSymbol = await getERC20TokenSymbol(tokenAddress);
784+
let erc20token = new web3.eth.Contract(abis.erc20(), tokenAddress);
785+
dividendTokenDecimals = await erc20token.methods.decimals().call();
742786
}
743787
dividends.push(
744788
new DividendData(
@@ -749,7 +793,8 @@ async function getDividends() {
749793
amountArray[i],
750794
claimedAmountArray[i],
751795
web3.utils.hexToUtf8(nameArray[i]),
752-
tokenSymbol
796+
tokenSymbol,
797+
dividendTokenDecimals
753798
)
754799
);
755800
}

0 commit comments

Comments
 (0)