forked from PolymathNetwork/polymath-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CappedSTO.sol
322 lines (277 loc) · 11.2 KB
/
CappedSTO.sol
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
pragma solidity ^0.4.24;
import "./ISTO.sol";
import "../../interfaces/IST20.sol";
import "openzeppelin-solidity/contracts/ReentrancyGuard.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* @title STO module for standard capped crowdsale
*/
contract CappedSTO is ISTO, ReentrancyGuard {
using SafeMath for uint256;
// Determine whether users can invest on behalf of a beneficiary
bool public allowBeneficialInvestments = false;
// Address where funds are collected and tokens are issued to
address public wallet;
// How many token units a buyer gets per wei / base unit of POLY
uint256 public rate;
// Amount of funds raised
uint256 public fundsRaised;
uint256 public investorCount;
// Amount of tokens sold
uint256 public tokensSold;
//How many tokens this STO will be allowed to sell to investors
uint256 public cap;
mapping (address => uint256) public investors;
/**
* Event for token purchase logging
* @param purchaser who paid for the tokens
* @param beneficiary who got the tokens
* @param value weis paid for purchase
* @param amount amount of tokens purchased
*/
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
event SetAllowBeneficialInvestments(bool _allowed);
constructor (address _securityToken, address _polyAddress) public
IModule(_securityToken, _polyAddress)
{
}
//////////////////////////////////
/**
* @notice fallback function ***DO NOT OVERRIDE***
*/
function () external payable {
buyTokens(msg.sender);
}
/**
* @notice Function used to intialize the contract variables
* @param _startTime Unix timestamp at which offering get started
* @param _endTime Unix timestamp at which offering get ended
* @param _cap Maximum No. of tokens for sale
* @param _rate Token units a buyer gets per wei / base unit of POLY
* @param _fundRaiseType Type of currency used to collect the funds
* @param _fundsReceiver Ethereum account address to hold the funds
*/
function configure(
uint256 _startTime,
uint256 _endTime,
uint256 _cap,
uint256 _rate,
uint8 _fundRaiseType,
address _fundsReceiver
)
public
onlyFactory
{
require(_rate > 0, "Rate of token should be greater than 0");
require(_fundsReceiver != address(0), "Zero address is not permitted");
require(_startTime >= now && _endTime > _startTime, "Date parameters are not valid");
require(_cap > 0, "Cap should be greater than 0");
startTime = _startTime;
endTime = _endTime;
cap = _cap;
rate = _rate;
wallet = _fundsReceiver;
_check(_fundRaiseType);
}
/**
* @notice This function returns the signature of configure function
*/
function getInitFunction() public pure returns (bytes4) {
return bytes4(keccak256("configure(uint256,uint256,uint256,uint256,uint8,address)"));
}
/**
* @notice Function to set allowBeneficialInvestments (allow beneficiary to be different to funder)
* @param _allowBeneficialInvestments Boolean to allow or disallow beneficial investments
*/
function changeAllowBeneficialInvestments(bool _allowBeneficialInvestments) public onlyOwner {
require(_allowBeneficialInvestments != allowBeneficialInvestments, "Does not change value");
allowBeneficialInvestments = _allowBeneficialInvestments;
emit SetAllowBeneficialInvestments(allowBeneficialInvestments);
}
/**
* @notice low level token purchase ***DO NOT OVERRIDE***
* @param _beneficiary Address performing the token purchase
*/
function buyTokens(address _beneficiary) public payable nonReentrant {
if (!allowBeneficialInvestments) {
require(_beneficiary == msg.sender, "Beneficiary must match funding provider");
}
require(!paused);
require(fundRaiseType[uint8(FundRaiseType.ETH)], "ETH should be the mode of investment");
uint256 weiAmount = msg.value;
_processTx(_beneficiary, weiAmount);
_forwardFunds();
_postValidatePurchase(_beneficiary, weiAmount);
}
/**
* @notice low level token purchase
* @param _investedPOLY Amount of POLY invested
*/
function buyTokensWithPoly(uint256 _investedPOLY) public nonReentrant{
require(!paused);
require(fundRaiseType[uint8(FundRaiseType.POLY)], "POLY should be the mode of investment");
require(verifyInvestment(msg.sender, _investedPOLY), "Not valid Investment");
_processTx(msg.sender, _investedPOLY);
_forwardPoly(msg.sender, wallet, _investedPOLY);
_postValidatePurchase(msg.sender, _investedPOLY);
}
/**
* @notice Checks whether the cap has been reached.
* @return bool Whether the cap was reached
*/
function capReached() public view returns (bool) {
return tokensSold >= cap;
}
/**
* @notice Return ETH raised by the STO
*/
function getRaisedEther() public view returns (uint256) {
if (fundRaiseType[uint8(FundRaiseType.ETH)])
return fundsRaised;
else
return 0;
}
/**
* @notice Return POLY raised by the STO
*/
function getRaisedPOLY() public view returns (uint256) {
if (fundRaiseType[uint8(FundRaiseType.POLY)])
return fundsRaised;
else
return 0;
}
/**
* @notice Return the total no. of investors
*/
function getNumberInvestors() public view returns (uint256) {
return investorCount;
}
/**
* @notice Return the total no. of tokens sold
*/
function getTokensSold() public view returns (uint256) {
return tokensSold;
}
/**
* @notice Return the permissions flag that are associated with STO
*/
function getPermissions() public view returns(bytes32[]) {
bytes32[] memory allPermissions = new bytes32[](0);
return allPermissions;
}
/**
* @notice Return the STO details
*/
function getSTODetails() public view returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool) {
return (
startTime,
endTime,
cap,
rate,
fundsRaised,
investorCount,
tokensSold,
(fundRaiseType[uint8(FundRaiseType.POLY)])
);
}
// -----------------------------------------
// Internal interface (extensible)
// -----------------------------------------
/**
* Processing the purchase as well as verify the required validations
* @param _beneficiary Address performing the token purchase
* @param _investedAmount Value in wei involved in the purchase
*/
function _processTx(address _beneficiary, uint256 _investedAmount) internal {
_preValidatePurchase(_beneficiary, _investedAmount);
// calculate token amount to be created
uint256 tokens = _getTokenAmount(_investedAmount);
// update state
fundsRaised = fundsRaised.add(_investedAmount);
tokensSold = tokensSold.add(tokens);
_processPurchase(_beneficiary, tokens);
emit TokenPurchase(msg.sender, _beneficiary, _investedAmount, tokens);
_updatePurchasingState(_beneficiary, _investedAmount);
}
/**
* @notice Validation of an incoming purchase.
Use require statements to revert state when conditions are not met. Use super to concatenate validations.
* @param _beneficiary Address performing the token purchase
* @param _investedAmount Value in wei involved in the purchase
*/
function _preValidatePurchase(address _beneficiary, uint256 _investedAmount) internal view {
require(_beneficiary != address(0), "Beneficiary address should not be 0x");
require(_investedAmount != 0, "Amount invested should not be equal to 0");
require(tokensSold.add(_getTokenAmount(_investedAmount)) <= cap, "Investment more than cap is not allowed");
require(now >= startTime && now <= endTime, "Offering is closed/Not yet started");
}
/**
* @notice Validation of an executed purchase.
Observe state and use revert statements to undo rollback when valid conditions are not met.
*/
function _postValidatePurchase(address /*_beneficiary*/, uint256 /*_investedAmount*/) internal pure {
// optional override
}
/**
* @notice Source of tokens.
Override this method to modify the way in which the crowdsale ultimately gets and sends its tokens.
* @param _beneficiary Address performing the token purchase
* @param _tokenAmount Number of tokens to be emitted
*/
function _deliverTokens(address _beneficiary, uint256 _tokenAmount) internal {
require(IST20(securityToken).mint(_beneficiary, _tokenAmount), "Error in minting the tokens");
}
/**
* @notice Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens.
* @param _beneficiary Address receiving the tokens
* @param _tokenAmount Number of tokens to be purchased
*/
function _processPurchase(address _beneficiary, uint256 _tokenAmount) internal {
if (investors[_beneficiary] == 0) {
investorCount = investorCount + 1;
}
investors[_beneficiary] = investors[_beneficiary].add(_tokenAmount);
_deliverTokens(_beneficiary, _tokenAmount);
}
/**
* @notice Override for extensions that require an internal state to check for validity
(current user contributions, etc.)
*/
function _updatePurchasingState(address /*_beneficiary*/, uint256 /*_investedAmount*/) internal pure {
// optional override
}
/**
* @notice Override to extend the way in which ether is converted to tokens.
* @param _investedAmount Value in wei to be converted into tokens
* @return Number of tokens that can be purchased with the specified _investedAmount
*/
function _getTokenAmount(uint256 _investedAmount) internal view returns (uint256) {
return _investedAmount.mul(rate);
}
/**
* @notice Determines how ETH is stored/forwarded on purchases.
*/
function _forwardFunds() internal {
wallet.transfer(msg.value);
}
/**
* @notice Internal function used to check the type of fund raise currency
* @param _fundRaiseType Type of currency used to collect the funds
*/
function _check(uint8 _fundRaiseType) internal {
require(_fundRaiseType == 0 || _fundRaiseType == 1, "Not a valid fundraise type");
fundRaiseType[_fundRaiseType] = true;
if (_fundRaiseType == uint8(FundRaiseType.POLY)) {
require(address(polyToken) != address(0), "Address of the polyToken should not be 0x");
}
}
/**
* @notice Internal function used to forward the POLY raised to beneficiary address
* @param _beneficiary Address of the funds reciever
* @param _to Address who wants to ST-20 tokens
* @param _fundsAmount Amount invested by _to
*/
function _forwardPoly(address _beneficiary, address _to, uint256 _fundsAmount) internal {
polyToken.transferFrom(_beneficiary, _to, _fundsAmount);
}
}