Skip to content

Commit

Permalink
Merge branch 'dev-2.1.0' into Vesting-Escrow-Wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
VictorVicente authored Jan 3, 2019
2 parents 3b78ff7 + c85c599 commit 0ecdf79
Show file tree
Hide file tree
Showing 8 changed files with 947 additions and 478 deletions.
1,179 changes: 720 additions & 459 deletions CLI/commands/dividends_manager.js

Large diffs are not rendered by default.

File renamed without changes.
10 changes: 10 additions & 0 deletions CLI/data/Checkpoint/tax_withholding_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,0.5
0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,1
0xac297053173b02b02a737d47f7b4a718e5b170ef,2
0x49fc0b78238dab644698a90fa351b4c749e123d2,10
0x10223927009b8add0960359dd90d1449415b7ca9,15
0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,50
0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,0
0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,23
0x56be93088141b16ebaa9416122fd1d928da25ecf,45
0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,67
133 changes: 132 additions & 1 deletion contracts/modules/Checkpoint/DividendCheckpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module {
validDividendIndex(_dividendIndex)
{
Dividend storage dividend = dividends[_dividendIndex];
address[] memory investors = ISecurityToken(securityToken).getInvestors();
uint256 checkpointId = dividend.checkpointId;
address[] memory investors = ISecurityToken(securityToken).getInvestorsAt(checkpointId);
uint256 numberInvestors = Math.min256(investors.length, _start.add(_iterations));
for (uint256 i = _start; i < numberInvestors; i++) {
address payee = investors[i];
Expand Down Expand Up @@ -226,6 +227,136 @@ contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module {
*/
function withdrawWithholding(uint256 _dividendIndex) external;

/**
* @notice Get static dividend data
* @return uint256[] timestamp of dividends creation
* @return uint256[] timestamp of dividends maturity
* @return uint256[] timestamp of dividends expiry
* @return uint256[] amount of dividends
* @return uint256[] claimed amount of dividends
* @return bytes32[] name of dividends
*/
function getDividendsData() external view returns (
uint256[] memory createds,
uint256[] memory maturitys,
uint256[] memory expirys,
uint256[] memory amounts,
uint256[] memory claimedAmounts,
bytes32[] memory names)
{
createds = new uint256[](dividends.length);
maturitys = new uint256[](dividends.length);
expirys = new uint256[](dividends.length);
amounts = new uint256[](dividends.length);
claimedAmounts = new uint256[](dividends.length);
names = new bytes32[](dividends.length);
for (uint256 i = 0; i < dividends.length; i++) {
(createds[i], maturitys[i], expirys[i], amounts[i], claimedAmounts[i], names[i]) = getDividendData(i);
}
}

/**
* @notice Get static dividend data
* @return uint256 timestamp of dividend creation
* @return uint256 timestamp of dividend maturity
* @return uint256 timestamp of dividend expiry
* @return uint256 amount of dividend
* @return uint256 claimed amount of dividend
* @return bytes32 name of dividend
*/
function getDividendData(uint256 _dividendIndex) public view returns (
uint256 created,
uint256 maturity,
uint256 expiry,
uint256 amount,
uint256 claimedAmount,
bytes32 name)
{
created = dividends[_dividendIndex].created;
maturity = dividends[_dividendIndex].maturity;
expiry = dividends[_dividendIndex].expiry;
amount = dividends[_dividendIndex].amount;
claimedAmount = dividends[_dividendIndex].claimedAmount;
name = dividends[_dividendIndex].name;
}

/**
* @notice Retrieves list of investors, their claim status and whether they are excluded
* @param _dividendIndex Dividend to withdraw from
* @return address[] list of investors
* @return bool[] whether investor has claimed
* @return bool[] whether investor is excluded
* @return uint256[] amount of withheld tax
* @return uint256[] investor balance
* @return uint256[] amount to be claimed including withheld tax
*/
function getDividendProgress(uint256 _dividendIndex) external view returns (
address[] memory investors,
bool[] memory resultClaimed,
bool[] memory resultExcluded,
uint256[] memory resultWithheld,
uint256[] memory resultBalance,
uint256[] memory resultAmount)
{
require(_dividendIndex < dividends.length, "Invalid dividend");
//Get list of Investors
Dividend storage dividend = dividends[_dividendIndex];
uint256 checkpointId = dividend.checkpointId;
investors = ISecurityToken(securityToken).getInvestorsAt(checkpointId);
resultClaimed = new bool[](investors.length);
resultExcluded = new bool[](investors.length);
resultWithheld = new uint256[](investors.length);
resultBalance = new uint256[](investors.length);
resultAmount = new uint256[](investors.length);
for (uint256 i; i < investors.length; i++) {
resultClaimed[i] = dividend.claimed[investors[i]];
resultExcluded[i] = dividend.dividendExcluded[investors[i]];
resultBalance[i] = ISecurityToken(securityToken).balanceOfAt(investors[i], dividend.checkpointId);
if (!resultExcluded[i]) {
resultWithheld[i] = dividend.withheld[investors[i]];
resultAmount[i] = resultBalance[i].mul(dividend.amount).div(dividend.totalSupply);
}
}
}

/**
* @notice Retrieves list of investors, their balances, and their current withholding tax percentage
* @param _checkpointId Checkpoint Id to query for
* @return address[] list of investors
* @return uint256[] investor balances
* @return uint256[] investor withheld percentages
*/
function getCheckpointData(uint256 _checkpointId) external view returns (address[] memory investors, uint256[] memory balances, uint256[] memory withholdings) {
require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId(), "Invalid checkpoint");
investors = ISecurityToken(securityToken).getInvestorsAt(_checkpointId);
balances = new uint256[](investors.length);
withholdings = new uint256[](investors.length);
for (uint256 i; i < investors.length; i++) {
balances[i] = ISecurityToken(securityToken).balanceOfAt(investors[i], _checkpointId);
withholdings[i] = withholdingTax[investors[i]];
}
}

/**
* @notice Checks whether an address is excluded from claiming a dividend
* @param _dividendIndex Dividend to withdraw from
* @return bool whether the address is excluded
*/
function isExcluded(address _investor, uint256 _dividendIndex) external view returns (bool) {
require(_dividendIndex < dividends.length, "Invalid dividend");
return dividends[_dividendIndex].dividendExcluded[_investor];
}

/**
* @notice Checks whether an address has claimed a dividend
* @param _dividendIndex Dividend to withdraw from
* @return bool whether the address has claimed
*/
function isClaimed(address _investor, uint256 _dividendIndex) external view returns (bool) {
require(_dividendIndex < dividends.length, "Invalid dividend");
return dividends[_dividendIndex].claimed[_investor];
}

/**
* @notice Return the permissions flag that are associated with this module
* @return bytes32 array
Expand Down
10 changes: 4 additions & 6 deletions contracts/modules/Checkpoint/DividendCheckpointStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pragma solidity ^0.4.24;
*/
contract DividendCheckpointStorage {

uint256 public EXCLUDED_ADDRESS_LIMIT = 50;
uint256 public EXCLUDED_ADDRESS_LIMIT = 150;
bytes32 public constant DISTRIBUTE = "DISTRIBUTE";
bytes32 public constant MANAGE = "MANAGE";
bytes32 public constant CHECKPOINT = "CHECKPOINT";
Expand All @@ -21,10 +21,11 @@ contract DividendCheckpointStorage {
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;
uint256 totalWithheld;
uint256 totalWithheldWithdrawn;
mapping (address => bool) claimed; // List of addresses which have claimed dividend
mapping (address => bool) dividendExcluded; // List of addresses which cannot claim dividends
mapping (address => uint256) withheld; // Amount of tax withheld from claim
bytes32 name; // Name/title - used for identification
}

Expand All @@ -37,7 +38,4 @@ contract DividendCheckpointStorage {
// 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;

}
10 changes: 6 additions & 4 deletions contracts/modules/Checkpoint/ERC20DividendCheckpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,10 @@ contract ERC20DividendCheckpoint is ERC20DividendCheckpointStorage, DividendChec
uint256 claimAfterWithheld = claim.sub(withheld);
if (claimAfterWithheld > 0) {
require(IERC20(dividendTokens[_dividendIndex]).transfer(_payee, claimAfterWithheld), "transfer failed");
_dividend.dividendWithheld = _dividend.dividendWithheld.add(withheld);
investorWithheld[_payee] = investorWithheld[_payee].add(withheld);
if (withheld > 0) {
_dividend.totalWithheld = _dividend.totalWithheld.add(withheld);
_dividend.withheld[_payee] = withheld;
}
emit ERC20DividendClaimed(_payee, _dividendIndex, dividendTokens[_dividendIndex], claim, withheld);
}
}
Expand Down Expand Up @@ -271,8 +273,8 @@ contract ERC20DividendCheckpoint is ERC20DividendCheckpointStorage, DividendChec
function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) {
require(_dividendIndex < dividends.length, "Invalid dividend");
Dividend storage dividend = dividends[_dividendIndex];
uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed);
dividend.dividendWithheldReclaimed = dividend.dividendWithheld;
uint256 remainingWithheld = dividend.totalWithheld.sub(dividend.totalWithheldWithdrawn);
dividend.totalWithheldWithdrawn = dividend.totalWithheld;
address owner = IOwnable(securityToken).owner();
require(IERC20(dividendTokens[_dividendIndex]).transfer(owner, remainingWithheld), "transfer failed");
emit ERC20DividendWithholdingWithdrawn(owner, _dividendIndex, dividendTokens[_dividendIndex], remainingWithheld);
Expand Down
12 changes: 7 additions & 5 deletions contracts/modules/Checkpoint/EtherDividendCheckpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "../../interfaces/IOwnable.sol";
*/
contract EtherDividendCheckpoint is DividendCheckpoint {
using SafeMath for uint256;

event EtherDividendDeposited(
address indexed _depositor,
uint256 _checkpointId,
Expand Down Expand Up @@ -176,8 +176,10 @@ contract EtherDividendCheckpoint is DividendCheckpoint {
/*solium-disable-next-line security/no-send*/
if (_payee.send(claimAfterWithheld)) {
_dividend.claimedAmount = _dividend.claimedAmount.add(claim);
_dividend.dividendWithheld = _dividend.dividendWithheld.add(withheld);
investorWithheld[_payee] = investorWithheld[_payee].add(withheld);
if (withheld > 0) {
_dividend.totalWithheld = _dividend.totalWithheld.add(withheld);
_dividend.withheld[_payee] = withheld;
}
emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld);
} else {
_dividend.claimed[_payee] = false;
Expand Down Expand Up @@ -210,8 +212,8 @@ contract EtherDividendCheckpoint is DividendCheckpoint {
function withdrawWithholding(uint256 _dividendIndex) external withPerm(MANAGE) {
require(_dividendIndex < dividends.length, "Incorrect dividend index");
Dividend storage dividend = dividends[_dividendIndex];
uint256 remainingWithheld = dividend.dividendWithheld.sub(dividend.dividendWithheldReclaimed);
dividend.dividendWithheldReclaimed = dividend.dividendWithheld;
uint256 remainingWithheld = dividend.totalWithheld.sub(dividend.totalWithheldWithdrawn);
dividend.totalWithheldWithdrawn = dividend.totalWithheld;
address owner = IOwnable(securityToken).owner();
owner.transfer(remainingWithheld);
emit EtherDividendWithholdingWithdrawn(owner, _dividendIndex, remainingWithheld);
Expand Down
Loading

0 comments on commit 0ecdf79

Please sign in to comment.