-
-
Save labiak/2206c85eec9f70a6657dec4b57b54c2f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.24; | |
contract ERC20Basic { | |
function totalSupply() public view returns (uint256); | |
function balanceOf(address _who) public view returns (uint256); | |
function transfer(address _to, uint256 _value) public returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
} | |
library SafeMath { | |
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { | |
if (_a == 0) { | |
return 0; | |
} | |
c = _a * _b; | |
assert(c / _a == _b); | |
return c; | |
} | |
function div(uint256 _a, uint256 _b) internal pure returns (uint256) { | |
return _a / _b; | |
} | |
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { | |
assert(_b <= _a); | |
return _a - _b; | |
} | |
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { | |
c = _a + _b; | |
assert(c >= _a); | |
return c; | |
} | |
} | |
contract BasicToken is ERC20Basic { | |
using SafeMath for uint256; | |
mapping(address => uint256) internal balances; | |
uint256 internal totalSupply_; | |
function totalSupply() public view returns (uint256) { | |
return totalSupply_; | |
} | |
function transfer(address _to, uint256 _value) public returns (bool) { | |
require(_value <= balances[msg.sender]); | |
require(_to != address(0)); | |
balances[msg.sender] = balances[msg.sender].sub(_value); | |
balances[_to] = balances[_to].add(_value); | |
emit Transfer(msg.sender, _to, _value); | |
return true; | |
} | |
function balanceOf(address _owner) public view returns (uint256) { | |
return balances[_owner]; | |
} | |
} | |
contract ERC20 is ERC20Basic { | |
function allowance(address _owner, address _spender) public view returns (uint256); | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool); | |
function approve(address _spender, uint256 _value) public returns (bool); | |
event Approval( | |
address indexed owner, | |
address indexed spender, | |
uint256 value | |
); | |
} | |
contract StandardToken is ERC20, BasicToken { | |
mapping(address => mapping(address => uint256)) internal allowed; | |
function transferFrom( | |
address _from, | |
address _to, | |
uint256 _value | |
) public | |
returns (bool) { | |
require(_value <= balances[_from]); | |
require(_value <= allowed[_from][msg.sender]); | |
require(_to != address(0)); | |
balances[_from] = balances[_from].sub(_value); | |
balances[_to] = balances[_to].add(_value); | |
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); | |
emit Transfer(_from, _to, _value); | |
return true; | |
} | |
function approve(address _spender, uint256 _value) public returns (bool) { | |
allowed[msg.sender][_spender] = _value; | |
emit Approval(msg.sender, _spender, _value); | |
return true; | |
} | |
function allowance( | |
address _owner, | |
address _spender | |
) public | |
view | |
returns (uint256) { | |
return allowed[_owner][_spender]; | |
} | |
function increaseApproval( | |
address _spender, | |
uint256 _addedValue | |
) public | |
returns (bool) { | |
allowed[msg.sender][_spender] = ( | |
allowed[msg.sender][_spender].add(_addedValue)); | |
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); | |
return true; | |
} | |
function decreaseApproval( | |
address _spender, | |
uint256 _subtractedValue | |
) public | |
returns (bool) { | |
uint256 oldValue = allowed[msg.sender][_spender]; | |
if (_subtractedValue >= oldValue) { | |
allowed[msg.sender][_spender] = 0; | |
} else { | |
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); | |
} | |
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); | |
return true; | |
} | |
} | |
contract Ownable { | |
address public owner; | |
event OwnershipRenounced(address indexed previousOwner); | |
event OwnershipTransferred( | |
address indexed previousOwner, | |
address indexed newOwner | |
); | |
constructor() public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner() { | |
require(msg.sender == owner); | |
_; | |
} | |
function renounceOwnership() public onlyOwner { | |
emit OwnershipRenounced(owner); | |
owner = address(0); | |
} | |
function transferOwnership(address _newOwner) public onlyOwner { | |
_transferOwnership(_newOwner); | |
} | |
function _transferOwnership(address _newOwner) internal { | |
require(_newOwner != address(0)); | |
emit OwnershipTransferred(owner, _newOwner); | |
owner = _newOwner; | |
} | |
} | |
contract MintableToken is StandardToken, Ownable { | |
event Mint(address indexed to, uint256 amount); | |
event MintFinished(); | |
bool public mintingFinished = false; | |
modifier canMint() { | |
require(!mintingFinished); | |
_; | |
} | |
modifier hasMintPermission() { | |
require(msg.sender == owner); | |
_; | |
} | |
function mint( | |
address _to, | |
uint256 _amount | |
) public | |
hasMintPermission | |
canMint | |
returns (bool) { | |
totalSupply_ = totalSupply_.add(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
emit Mint(_to, _amount); | |
emit Transfer(address(0), _to, _amount); | |
return true; | |
} | |
function finishMinting() public onlyOwner canMint returns (bool) { | |
mintingFinished = true; | |
emit MintFinished(); | |
return true; | |
} | |
} | |
contract DetailedERC20 is ERC20 { | |
string public name; | |
string public symbol; | |
uint8 public decimals; | |
constructor(string _name, string _symbol, uint8 _decimals) public { | |
name = _name; | |
symbol = _symbol; | |
decimals = _decimals; | |
} | |
} | |
contract WAS_Token is MintableToken, DetailedERC20 { | |
uint256 public totalSupplyLimit = 260260676; | |
constructor() DetailedERC20("Wasi", "WAS", 0) public { | |
} | |
function burnUnsoldTokens() public onlyOwner { | |
balances[owner] = 0; | |
} | |
} | |
contract WAS_CrowdsaleReservations { | |
using SafeMath for uint256; | |
uint256 public reserveTokensTeamPercent = 5; // 13013033 tokens | |
uint256 public reservedTokensTeam; | |
uint256 public tokensCrowdsalePurchased; | |
uint256 public reservedTokensCrowdsalePurchase; | |
constructor(uint256 _totalSupply) public { | |
calculateReservations(_totalSupply); | |
} | |
function calculateReservations(uint256 _totalSupply) private { | |
reservedTokensTeam = _totalSupply.mul(reserveTokensTeamPercent).div(100); | |
reservedTokensCrowdsalePurchase = 50 * (10 ** 6); | |
// 50m | |
} | |
} | |
library SafeERC20 { | |
function safeTransfer( | |
ERC20Basic _token, | |
address _to, | |
uint256 _value | |
) | |
internal { | |
require(_token.transfer(_to, _value)); | |
} | |
function safeTransferFrom( | |
ERC20 _token, | |
address _from, | |
address _to, | |
uint256 _value | |
) | |
internal { | |
require(_token.transferFrom(_from, _to, _value)); | |
} | |
function safeApprove( | |
ERC20 _token, | |
address _spender, | |
uint256 _value | |
) | |
internal { | |
require(_token.approve(_spender, _value)); | |
} | |
} | |
contract Crowdsale { | |
using SafeMath for uint256; | |
using SafeERC20 for ERC20; | |
ERC20 public token; | |
address public wallet; | |
uint256 public rate; | |
uint256 public weiRaised; | |
event TokenPurchase( | |
address indexed purchaser, | |
address indexed beneficiary, | |
uint256 value, | |
uint256 amount | |
); | |
constructor(uint256 _rate, address _wallet, ERC20 _token) public { | |
require(_rate > 0); | |
require(_wallet != address(0)); | |
require(_token != address(0)); | |
rate = _rate; | |
wallet = _wallet; | |
token = _token; | |
} | |
function() external payable { | |
buyTokens(msg.sender); | |
} | |
function buyTokens(address _beneficiary) public payable { | |
uint256 weiAmount = msg.value; | |
_preValidatePurchase(_beneficiary, weiAmount); | |
uint256 tokens = _getTokenAmount(weiAmount); | |
weiRaised = weiRaised.add(weiAmount); | |
_processPurchase(_beneficiary, tokens); | |
emit TokenPurchase( | |
msg.sender, | |
_beneficiary, | |
weiAmount, | |
tokens | |
); | |
_updatePurchasingState(_beneficiary, weiAmount); | |
_forwardFunds(); | |
_postValidatePurchase(_beneficiary, weiAmount); | |
} | |
function _preValidatePurchase( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal { | |
require(_beneficiary != address(0)); | |
require(_weiAmount != 0); | |
} | |
function _postValidatePurchase( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal { | |
} | |
function _deliverTokens( | |
address _beneficiary, | |
uint256 _tokenAmount | |
) | |
internal { | |
token.safeTransfer(_beneficiary, _tokenAmount); | |
} | |
function _processPurchase( | |
address _beneficiary, | |
uint256 _tokenAmount | |
) | |
internal { | |
_deliverTokens(_beneficiary, _tokenAmount); | |
} | |
function _updatePurchasingState( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal { | |
} | |
function _getTokenAmount(uint256 _weiAmount) | |
internal view returns (uint256) { | |
return _weiAmount.mul(rate); | |
} | |
function _forwardFunds() internal { | |
wallet.transfer(msg.value); | |
} | |
} | |
contract TimedCrowdsale is Crowdsale { | |
using SafeMath for uint256; | |
uint256 public openingTime; | |
uint256 public closingTime; | |
modifier onlyWhileOpen { | |
require(block.timestamp >= openingTime && block.timestamp <= closingTime); | |
_; | |
} | |
constructor(uint256 _openingTime, uint256 _closingTime) public { | |
require(_openingTime >= block.timestamp); | |
require(_closingTime >= _openingTime); | |
openingTime = _openingTime; | |
closingTime = _closingTime; | |
} | |
function hasClosed() public view returns (bool) { | |
return block.timestamp > closingTime; | |
} | |
function _preValidatePurchase( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal | |
onlyWhileOpen { | |
super._preValidatePurchase(_beneficiary, _weiAmount); | |
} | |
} | |
contract FinalizableCrowdsale is Ownable, TimedCrowdsale { | |
using SafeMath for uint256; | |
bool public isFinalized = false; | |
event Finalized(); | |
function finalize() public onlyOwner { | |
require(!isFinalized); | |
require(hasClosed()); | |
finalization(); | |
emit Finalized(); | |
isFinalized = true; | |
} | |
function finalization() internal { | |
} | |
} | |
contract WAS_Crowdsale_Stages is FinalizableCrowdsale, WAS_CrowdsaleReservations { | |
uint256[] rateETH; | |
uint256[] openingTimings; | |
uint256[] closingTimings; | |
uint256[] stagePurchaseTokensMinimum; | |
modifier onlyWhileOpen { | |
bool stageRunning; | |
uint256 stageIdx; | |
(stageRunning, stageIdx) = currentStage(); | |
require(stageRunning, "no stage is currently running"); | |
_; | |
} | |
constructor(uint256[] _rateETH, uint256[] _openingTimings, uint256[] _closingTimings) | |
TimedCrowdsale(_openingTimings[0], _closingTimings[_closingTimings.length - 1]) public { | |
validateRates(_rateETH); | |
validateOpeningAndClosingTimings(_openingTimings, _closingTimings); | |
rateETH = _rateETH; | |
openingTimings = _openingTimings; | |
closingTimings = _closingTimings; | |
stagePurchaseTokensMinimum = [0, 100]; | |
} | |
function hasOpened() public view returns (bool) { | |
return now >= openingTime; | |
} | |
function currentRateETH() public view returns (uint256) { | |
bool stageRunning; | |
uint256 stageIdx; | |
(stageRunning, stageIdx) = currentStage(); | |
require(stageRunning, "no stage is currently running"); | |
return rateETH[stageIdx]; | |
} | |
function currentDiscount(uint256 _tokenPurchasedAmount) public view returns (uint256) { | |
bool stageRunning; | |
uint256 stageIdx; | |
(stageRunning, stageIdx) = currentStage(); | |
require(stageRunning, "no stage is currently running"); | |
return discountPercentForStage(stageIdx, _tokenPurchasedAmount); | |
} | |
function currentStage() public view returns (bool, uint256) { | |
for (uint256 i = 0; i < closingTimings.length; i ++) { | |
if (block.timestamp <= closingTimings[i] && block.timestamp >= openingTimings[i]) { | |
return (true, i); | |
} | |
} | |
return (false, 0); | |
} | |
function updateStageRateETH(uint256 _stage, uint256 _rateETH) public onlyOwner { | |
rateETH[_stage] = _rateETH; | |
} | |
function updateOpeningAndClosingTimings(uint256[] _openingTimings, uint256[] _closingTimings) public onlyOwner { | |
validateOpeningAndClosingTimings(_openingTimings, _closingTimings); | |
openingTimings = _openingTimings; | |
closingTimings = _closingTimings; | |
} | |
function validateRates(uint256[] _rateETH) private pure { | |
for (uint256 i = 0; i < _rateETH.length; i ++) { | |
require(_rateETH[i] > 0, "rate must be > 0"); | |
} | |
} | |
function validateOpeningAndClosingTimings(uint256[] _openingTimings, uint256[] _closingTimings) private view { | |
require(_openingTimings.length == _closingTimings.length, "timings length differs"); | |
require(_openingTimings[0] > block.timestamp, "should open in future"); | |
for (uint256 i = 0; i < _openingTimings.length; i ++) { | |
require(_closingTimings[i] > _openingTimings[i], "closing time of stage must be > opening time of same stage"); | |
if (i > 0) { | |
require(_openingTimings[i] > _openingTimings[i - 1], "opening time must be > than previous opening time"); | |
} | |
} | |
} | |
function discountPercentForStage(uint256 _stage, uint256 _tokenPurchasedAmount) private pure returns (uint256) { | |
if (_stage == 0) { | |
return 30; | |
} else if (_stage == 1) { | |
if (_tokenPurchasedAmount >= 15000) { | |
return 10; | |
} else if (_tokenPurchasedAmount >= 10000) { | |
return 7; | |
} else if (_tokenPurchasedAmount >= 5000) { | |
return 5; | |
} | |
} | |
return 0; | |
} | |
} | |
contract Pausable is Ownable { | |
event Pause(); | |
event Unpause(); | |
bool public paused = false; | |
modifier whenNotPaused() { | |
require(!paused); | |
_; | |
} | |
modifier whenPaused() { | |
require(paused); | |
_; | |
} | |
function pause() public onlyOwner whenNotPaused { | |
paused = true; | |
emit Pause(); | |
} | |
function unpause() public onlyOwner whenPaused { | |
paused = false; | |
emit Unpause(); | |
} | |
} | |
library Roles { | |
struct Role { | |
mapping(address => bool) bearer; | |
} | |
function add(Role storage _role, address _addr) | |
internal { | |
_role.bearer[_addr] = true; | |
} | |
function remove(Role storage _role, address _addr) | |
internal { | |
_role.bearer[_addr] = false; | |
} | |
function check(Role storage _role, address _addr) | |
internal | |
view { | |
require(has(_role, _addr)); | |
} | |
function has(Role storage _role, address _addr) | |
internal | |
view | |
returns (bool) { | |
return _role.bearer[_addr]; | |
} | |
} | |
contract RBAC { | |
using Roles for Roles.Role; | |
mapping(string => Roles.Role) private roles; | |
event RoleAdded(address indexed operator, string role); | |
event RoleRemoved(address indexed operator, string role); | |
function checkRole(address _operator, string _role) public | |
view { | |
roles[_role].check(_operator); | |
} | |
function hasRole(address _operator, string _role) public | |
view | |
returns (bool) { | |
return roles[_role].has(_operator); | |
} | |
function addRole(address _operator, string _role) | |
internal { | |
roles[_role].add(_operator); | |
emit RoleAdded(_operator, _role); | |
} | |
function removeRole(address _operator, string _role) | |
internal { | |
roles[_role].remove(_operator); | |
emit RoleRemoved(_operator, _role); | |
} | |
modifier onlyRole(string _role) { | |
checkRole(msg.sender, _role); | |
_; | |
} | |
} | |
contract Whitelist is Ownable, RBAC { | |
string public constant ROLE_WHITELISTED = "whitelist"; | |
modifier onlyIfWhitelisted(address _operator) { | |
checkRole(_operator, ROLE_WHITELISTED); | |
_; | |
} | |
function addAddressToWhitelist(address _operator) public | |
onlyOwner { | |
addRole(_operator, ROLE_WHITELISTED); | |
} | |
function whitelist(address _operator) public | |
view | |
returns (bool) { | |
return hasRole(_operator, ROLE_WHITELISTED); | |
} | |
function addAddressesToWhitelist(address[] _operators) public | |
onlyOwner { | |
for (uint256 i = 0; i < _operators.length; i++) { | |
addAddressToWhitelist(_operators[i]); | |
} | |
} | |
function removeAddressFromWhitelist(address _operator) public | |
onlyOwner { | |
removeRole(_operator, ROLE_WHITELISTED); | |
} | |
function removeAddressesFromWhitelist(address[] _operators) public | |
onlyOwner { | |
for (uint256 i = 0; i < _operators.length; i++) { | |
removeAddressFromWhitelist(_operators[i]); | |
} | |
} | |
} | |
contract WhitelistedCrowdsale is Whitelist, Crowdsale { | |
function _preValidatePurchase( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal | |
onlyIfWhitelisted(_beneficiary) { | |
super._preValidatePurchase(_beneficiary, _weiAmount); | |
} | |
} | |
contract Destructible is Ownable { | |
function destroy() public onlyOwner { | |
selfdestruct(owner); | |
} | |
function destroyAndSend(address _recipient) public onlyOwner { | |
selfdestruct(_recipient); | |
} | |
} | |
contract WAS_Crowdsale is WhitelistedCrowdsale, Destructible, Pausable, WAS_Crowdsale_Stages { | |
WAS_Token token; | |
constructor(address _wallet, ERC20 _token, uint256[] _rateETH, uint256[] _openingTimings, uint256[] _closingTimings) | |
Crowdsale(1, _wallet, _token) | |
WAS_Crowdsale_Stages(_rateETH, _openingTimings, _closingTimings) | |
WAS_CrowdsaleReservations(WAS_Token(_token).totalSupplyLimit()) public { | |
require(_token != address(0), "token address cannt be 0"); | |
token = WAS_Token(_token); | |
} | |
function mintTotalSupplyAndTeam(address _teamAddress) public onlyOwner { | |
require(_teamAddress != address(0), "team address can not be 0"); | |
require(reservedTokensTeam > 0, "team tokens can not be 0"); | |
token.mint(_teamAddress, reservedTokensTeam); | |
uint256 purchaseTokens = token.totalSupplyLimit().sub(reservedTokensTeam); | |
token.mint(address(this), purchaseTokens); | |
token.finishMinting(); | |
} | |
function manualTransfer(address _to, uint256 _tokenAmount) public onlyOwner { | |
bool stageRunning; | |
uint256 stageIdx; | |
(stageRunning, stageIdx) = currentStage(); | |
require(stageRunning, "no stage is currently running"); | |
require(stageIdx == 0, "manual transfer allowed during first stage only"); | |
require(tokensCrowdsalePurchased.add(_tokenAmount) <= reservedTokensCrowdsalePurchase, "not enough tokens to manually transfer"); | |
tokensCrowdsalePurchased = tokensCrowdsalePurchased.add(_tokenAmount); | |
token.transfer(_to, _tokenAmount); | |
} | |
function destroy() public onlyOwner { | |
require(hasClosed(), "crowdsale must be closed"); | |
require(isFinalized, "crowdsale must be finalized"); | |
selfdestruct(owner); | |
} | |
function destroyAndSend(address _recipient) public onlyOwner { | |
require(hasClosed(), "crowdsale must be closed"); | |
require(isFinalized, "crowdsale must be finalized"); | |
selfdestruct(_recipient); | |
} | |
function finalization() internal { | |
uint256 unpurchasedTokens = token.balanceOf(address(this)); | |
token.transfer(owner, unpurchasedTokens); | |
super.finalization(); | |
} | |
function _preValidatePurchase( | |
address _beneficiary, | |
uint256 _weiAmount | |
) | |
internal | |
whenNotPaused() | |
onlyWhileOpen() { | |
super._preValidatePurchase(_beneficiary, _weiAmount); | |
} | |
function _getTokenAmount(uint256 _weiAmount) | |
internal view returns (uint256) { | |
uint256 rateETH = currentRateETH(); | |
uint256 tokens = _weiAmount.mul(rateETH).div(uint256(10 ** 18)); | |
uint256 discount = currentDiscount(tokens); | |
if (discount > 0) { | |
tokens = tokens.add(tokens.mul(discount).div(100)); | |
} | |
return tokens; | |
} | |
function _processPurchase( | |
address _beneficiary, | |
uint256 _tokenAmount | |
) | |
internal { | |
bool stageRunning; | |
uint256 stageIdx; | |
(stageRunning, stageIdx) = currentStage(); | |
require(stageRunning, "no stage is currently running"); | |
require(_tokenAmount >= stagePurchaseTokensMinimum[stageIdx], "token amount is less than minimum for stage"); | |
require(tokensCrowdsalePurchased.add(_tokenAmount) <= reservedTokensCrowdsalePurchase, "not enough tokens to purchase"); | |
tokensCrowdsalePurchased = tokensCrowdsalePurchased.add(_tokenAmount); | |
super._processPurchase(_beneficiary, _tokenAmount); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment