Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Withholding tax on dividends #277

Merged
merged 32 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
35c85d5
WIP
adamdossa Jun 26, 2018
7b48537
Add withholding tax to ether dividends
adamdossa Jun 26, 2018
2de1fd9
Improve testing
adamdossa Jun 27, 2018
318393f
Add excluded addresses for Ether dividends
adamdossa Jun 29, 2018
282ea45
ETH withholding CLI
Jul 3, 2018
ebd1772
Merge remote-tracking branch 'origin/master' into withholding
Jul 3, 2018
212bcbb
Merge branch 'master' into withholding_tax_on_dividends
pabloruiz55 Jul 3, 2018
3f258f3
fixed exception on CLI while creating dividend
Jul 3, 2018
33f8552
deleted console log
pabloruiz55 Jul 3, 2018
9b0e46b
log withholding in ETH Balance
Jul 4, 2018
7550c03
add address dividend blacklist
Jul 4, 2018
b1b4504
Merge branch 'withholding_tax_on_dividends' of https://github.com/Pol…
Jul 4, 2018
55c9fe3
CONTRIBUTING.md
CPSTL Sep 6, 2018
3e4bcbf
add versioning in the script
SatyamSB Sep 10, 2018
9fecf95
Merge branch 'master' into docs-script-improve
SatyamSB Sep 13, 2018
d7a6abd
minor fix
SatyamSB Sep 13, 2018
7b3e7db
Merge pull request #252 from PolymathNetwork/docs-script-improve
satyamakgec Sep 13, 2018
78fd6a2
Update to security vulnerability section
CPSTL Sep 18, 2018
0e74472
Merge branch 'master' into CPSTL-patch-2
SatyamSB Sep 18, 2018
8ff6817
Merge pull request #250 from PolymathNetwork/CPSTL-patch-2
satyamakgec Sep 18, 2018
1750892
Merge branch 'development-1.5.0' into withholding_tax_on_dividends
adamdossa Sep 24, 2018
4f5f286
Some refactoring
adamdossa Sep 24, 2018
83dc32f
Fix erc20 dividend tests
adamdossa Sep 24, 2018
b67a7fd
Merge branch 'master' into withholding_tax_on_dividends
adamdossa Sep 25, 2018
134fd56
Update Change Log
adamdossa Sep 25, 2018
f2bf188
Merge branch 'development-1.5.0' into withholding_tax_on_dividends
adamdossa Sep 25, 2018
d5ab479
Updates
adamdossa Sep 25, 2018
1bf3733
Fixes and more tests
adamdossa Sep 25, 2018
7ea9246
More fixes
adamdossa Sep 25, 2018
e6c4926
Get correct balances for historical checkpoints
adamdossa Sep 26, 2018
bf5116d
Revert claimedAmount on failed send
adamdossa Sep 26, 2018
bb69c80
Add test case for failed ETH send
adamdossa Sep 26, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ cache:
- node_modules
matrix:
fast_finish: true
before_install:
- echo -ne '\n' | sudo add-apt-repository ppa:ethereum/ethereum
- sudo apt-get -y update
- sudo apt-get -y install solc
before_script:
- truffle version
script:
- npm run test
- npm run docs
notifications:
slack:
secure: W4FZSabLrzF74f317hutolEHnlq2GBlQxU6b85L5XymrjgLEhlgE16c5Qz7Emoyt6le6PXL+sfG2ujJc3XYys/6hppgrHSAasuJnKCdQNpmMZ9BNyMs6WGkmB3enIf3K/FLXb26AQdwpQdIXuOeJUTf879u+YoiZV0eZH8d3+fsIOyovq9N6X5pKOpDM9iT8gGB4t7fie7xf51s+iUaHxyO9G7jDginZ4rBXHcU7mxCub9z+Z1H8+kCTnPWaF+KKVEXx4Z0nI3+urboD7E4OIP02LwrThQls2CppA3X0EoesTcdvj/HLErY/JvsXIFiFEEHZzB1Wi+k2TiOeLcYwEuHIVij+HPxxlJNX/j8uy01Uk8s4rd+0EhvfdKHJqUKqxH4YN2npcKfHEss7bU3y7dUinXQfYShW5ZewHdvc7pnnxBTfhvmdi64HdNrXAPq+s1rhciH7MmnU+tsm4lhrpr+FBuHzUMA9fOCr7b0SQytZEgWpiUls88gdbh3yG8TjyZxmZJGx09cwEP0q7VoH0UwFh7mIu5XmYdd5tWUhavTiO7YV8cUPn7MvwMsTltB3YBpF/fB26L7ka8zBhCsjm9prW6SVYU/dyO3m91VeZtO/zJFHRDA6Q58JGVW2rgzO39z193qC1EGRXqTie96VwAAtNg8+hRb+bI/CWDVzSPc=
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 withholding tax to ether & erc20 dividends
* Generalised MakerDAO oracle to allow different instances referencing different currencies
* Added DAI as a fundraising currency to USDTieredSTO
* `transferTickerOwnership()` function is introduced in `TickerRegistry` to transfer the ticker ownership after the registeration #191.
Expand Down
46 changes: 46 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Contributing Guidelines

## Do you have a question?

[Check out our Gitter](https://gitter.im/PolymathNetwork/Lobby)

See also frequently asked questions tagged with Polymath on Stack Exchange and Reddit.

# Please Report bugs!

Do not open an issue on Github if you think your discovered bug could be a security-relevant vulnerability. In the case of the discovery of high security venerabilities, pleasse email us the issue privately at team@polymath.network.

Otherwise, just create a new issue in our repository and follow the template below:

[Issue template to make things easier for you](https://github.com/PolymathNetwork/polymath-core/blob/master/.github/ISSUE_TEMPLATE/feature_request.md)


# Contribute!

If you would like to contribute to Polymath-core, please fork it, fix bugs or implement features, and propose a [pull request](https://github.com/PolymathNetwork/polymath-core/blob/master/PULL_REQUEST_TEMPLATE.md)

Please, refer to the Coding Guide on our [Developer Portal](https://developers.polymath.network/) for more details about hacking on Polymath.

# Contributing

When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

Please note we have a code of conduct, please follow it in all your interactions with the project.

# Pull Request Process

[Template](https://github.com/PolymathNetwork/polymath-core/blob/master/PULL_REQUEST_TEMPLATE.md)

# Code Styleguide

The polymath-core repo follows the [Solidity style guide](https://solidity.readthedocs.io/en/v0.4.24/style-guide.html)

# Code of Conduct

[Community Standards](https://github.com/PolymathNetwork/polymath-core/blob/master/CODE_OF_CONDUCT.md)

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community feel safe and respected.

# Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
214 changes: 214 additions & 0 deletions contracts/modules/Checkpoint/DividendCheckpoint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
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;
}

}
Loading