-
Notifications
You must be signed in to change notification settings - Fork 151
Expand file tree
/
Copy pathEtherDividendCheckpoint.sol
More file actions
218 lines (205 loc) · 9.28 KB
/
EtherDividendCheckpoint.sol
File metadata and controls
218 lines (205 loc) · 9.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
pragma solidity ^0.4.24;
import "./DividendCheckpoint.sol";
import "../../interfaces/IOwnable.sol";
/**
* @title Checkpoint module for issuing ether dividends
*/
contract EtherDividendCheckpoint is DividendCheckpoint {
using SafeMath for uint256;
event EtherDividendDeposited(
address indexed _depositor,
uint256 _checkpointId,
uint256 _created,
uint256 _maturity,
uint256 _expiry,
uint256 _amount,
uint256 _totalSupply,
uint256 _dividendIndex,
bytes32 indexed _name
);
event EtherDividendClaimed(address indexed _payee, uint256 _dividendIndex, uint256 _amount, uint256 _withheld);
event EtherDividendReclaimed(address indexed _claimer, uint256 _dividendIndex, uint256 _claimedAmount);
event EtherDividendClaimFailed(address indexed _payee, uint256 _dividendIndex, uint256 _amount, uint256 _withheld);
event EtherDividendWithholdingWithdrawn(address indexed _claimer, uint256 _dividendIndex, uint256 _withheldAmount);
/**
* @notice Constructor
* @param _securityToken Address of the security token
*/
constructor (address _securityToken) public
Module(_securityToken)
{
}
/**
* @notice Creates a dividend and checkpoint for the dividend, using global list of excluded addresses
* @param _maturity Time from which dividend can be paid
* @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer
* @param _name Name/title for identification
*/
function createDividend(uint256 _maturity, uint256 _expiry, bytes32 _name) external payable withPerm(MANAGE) {
createDividendWithExclusions(_maturity, _expiry, excluded, _name);
}
/**
* @notice Creates a dividend with a provided checkpoint, using global list of excluded addresses
* @param _maturity Time from which dividend can be paid
* @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer
* @param _checkpointId Id of the checkpoint from which to issue dividend
* @param _name Name/title for identification
*/
function createDividendWithCheckpoint(
uint256 _maturity,
uint256 _expiry,
uint256 _checkpointId,
bytes32 _name
)
external
payable
withPerm(MANAGE)
{
_createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, excluded, _name);
}
/**
* @notice Creates a dividend and checkpoint for the dividend, specifying explicit excluded addresses
* @param _maturity Time from which dividend can be paid
* @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer
* @param _excluded List of addresses to exclude
* @param _name Name/title for identification
*/
function createDividendWithExclusions(
uint256 _maturity,
uint256 _expiry,
address[] _excluded,
bytes32 _name
)
public
payable
withPerm(MANAGE)
{
uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint();
_createDividendWithCheckpointAndExclusions(_maturity, _expiry, checkpointId, _excluded, _name);
}
/**
* @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses
* @param _maturity Time from which dividend can be paid
* @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer
* @param _checkpointId Id of the checkpoint from which to issue dividend
* @param _excluded List of addresses to exclude
* @param _name Name/title for identification
*/
function createDividendWithCheckpointAndExclusions(
uint256 _maturity,
uint256 _expiry,
uint256 _checkpointId,
address[] _excluded,
bytes32 _name
)
public
payable
withPerm(MANAGE)
{
_createDividendWithCheckpointAndExclusions(_maturity, _expiry, _checkpointId, _excluded, _name);
}
/**
* @notice Creates a dividend with a provided checkpoint, specifying explicit excluded addresses
* @param _maturity Time from which dividend can be paid
* @param _expiry Time until dividend can no longer be paid, and can be reclaimed by issuer
* @param _checkpointId Id of the checkpoint from which to issue dividend
* @param _excluded List of addresses to exclude
* @param _name Name/title for identification
*/
function _createDividendWithCheckpointAndExclusions(
uint256 _maturity,
uint256 _expiry,
uint256 _checkpointId,
address[] _excluded,
bytes32 _name
)
internal
{
require(_excluded.length <= EXCLUDED_ADDRESS_LIMIT, "Too many addresses excluded");
require(_expiry > _maturity, "Expiry is before maturity");
/*solium-disable-next-line security/no-block-members*/
require(_expiry > now, "Expiry is in the past");
require(msg.value > 0, "No dividend sent");
require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId());
require(_name[0] != 0);
uint256 dividendIndex = dividends.length;
uint256 currentSupply = ISecurityToken(securityToken).totalSupplyAt(_checkpointId);
uint256 excludedSupply = 0;
dividends.push(
Dividend(
_checkpointId,
now, /*solium-disable-line security/no-block-members*/
_maturity,
_expiry,
msg.value,
0,
0,
false,
0,
0,
_name
)
);
for (uint256 j = 0; j < _excluded.length; j++) {
require (_excluded[j] != address(0), "Invalid address");
require(!dividends[dividendIndex].dividendExcluded[_excluded[j]], "duped exclude address");
excludedSupply = excludedSupply.add(ISecurityToken(securityToken).balanceOfAt(_excluded[j], _checkpointId));
dividends[dividendIndex].dividendExcluded[_excluded[j]] = true;
}
dividends[dividendIndex].totalSupply = currentSupply.sub(excludedSupply);
/*solium-disable-next-line security/no-block-members*/
emit EtherDividendDeposited(msg.sender, _checkpointId, now, _maturity, _expiry, msg.value, currentSupply, dividendIndex, _name);
}
/**
* @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 {
(uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee);
_dividend.claimed[_payee] = true;
uint256 claimAfterWithheld = claim.sub(withheld);
if (claimAfterWithheld > 0) {
/*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);
emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld);
} else {
_dividend.claimed[_payee] = false;
emit EtherDividendClaimFailed(_payee, _dividendIndex, claim, withheld);
}
}
}
/**
* @notice Issuer can reclaim remaining unclaimed dividend amounts, for expired dividends
* @param _dividendIndex Dividend to reclaim
*/
function reclaimDividend(uint256 _dividendIndex) external withPerm(MANAGE) {
require(_dividendIndex < dividends.length, "Incorrect dividend index");
/*solium-disable-next-line security/no-block-members*/
require(now >= dividends[_dividendIndex].expiry, "Dividend expiry is in the future");
require(!dividends[_dividendIndex].reclaimed, "Dividend is already claimed");
Dividend storage dividend = dividends[_dividendIndex];
dividend.reclaimed = true;
uint256 remainingAmount = dividend.amount.sub(dividend.claimedAmount);
address owner = IOwnable(securityToken).owner();
owner.transfer(remainingAmount);
emit EtherDividendReclaimed(owner, _dividendIndex, remainingAmount);
}
/**
* @notice Allows issuer to withdraw withheld tax
* @param _dividendIndex Dividend to withdraw from
*/
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;
address owner = IOwnable(securityToken).owner();
owner.transfer(remainingWithheld);
emit EtherDividendWithholdingWithdrawn(owner, _dividendIndex, remainingWithheld);
}
}