Transaction Hash:
Block:
7807583 at May-22-2019 04:43:57 AM +UTC
Transaction Fee:
0.00031056 ETH
$0.78
Gas Used:
31,056 Gas / 10 Gwei
Emitted Events:
101 |
Crowdsale.TokenRateChanged( newRate=2888, timestamp=1558500237, byStaff=[Sender] 0xb0f7bc2b504e1e229ba5e661d33e6e2367e334d8 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 5,338.64394321520627313 Eth | 5,338.64425377520627313 Eth | 0.00031056 | |
0xb0F7BC2b...367e334D8 |
11.050481457 Eth
Nonce: 139
|
11.050170897 Eth
Nonce: 140
| 0.00031056 | ||
0xD45b6c68...3C0B2b00c | (433Token: Token Sale) |
Execution Trace
Crowdsale.changeTokenRate( _tokenRate=2888 )
-
Staff.CALL( )
changeTokenRate[Crowdsale (ln:616)]
TokenRateChanged[Crowdsale (ln:619)]
File 1 of 2: Crowdsale
File 2 of 2: Staff
pragma solidity ^0.4.13; library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } } contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } contract RBAC { using Roles for Roles.Role; mapping (string => Roles.Role) private roles; event RoleAdded(address addr, string roleName); event RoleRemoved(address addr, string roleName); /** * @dev reverts if addr does not have role * @param addr address * @param roleName the name of the role * // reverts */ function checkRole(address addr, string roleName) view public { roles[roleName].check(addr); } /** * @dev determine if addr has role * @param addr address * @param roleName the name of the role * @return bool */ function hasRole(address addr, string roleName) view public returns (bool) { return roles[roleName].has(addr); } /** * @dev add a role to an address * @param addr address * @param roleName the name of the role */ function addRole(address addr, string roleName) internal { roles[roleName].add(addr); emit RoleAdded(addr, roleName); } /** * @dev remove a role from an address * @param addr address * @param roleName the name of the role */ function removeRole(address addr, string roleName) internal { roles[roleName].remove(addr); emit RoleRemoved(addr, roleName); } /** * @dev modifier to scope access to a single role (uses msg.sender as addr) * @param roleName the name of the role * // reverts */ modifier onlyRole(string roleName) { checkRole(msg.sender, roleName); _; } /** * @dev modifier to scope access to a set of roles (uses msg.sender as addr) * @param roleNames the names of the roles to scope access to * // reverts * * @TODO - when solidity supports dynamic arrays as arguments to modifiers, provide this * see: https://github.com/ethereum/solidity/issues/2467 */ // modifier onlyRoles(string[] roleNames) { // bool hasAnyRole = false; // for (uint8 i = 0; i < roleNames.length; i++) { // if (hasRole(msg.sender, roleNames[i])) { // hasAnyRole = true; // break; // } // } // require(hasAnyRole); // _; // } } library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an address access to this role */ function add(Role storage role, address addr) internal { role.bearer[addr] = true; } /** * @dev remove an address' access to this role */ function remove(Role storage role, address addr) internal { role.bearer[addr] = false; } /** * @dev check if an address has this role * // reverts */ function check(Role storage role, address addr) view internal { require(has(role, addr)); } /** * @dev check if an address has this role * @return bool */ function has(Role storage role, address addr) view internal returns (bool) { return role.bearer[addr]; } } 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); } contract BasicToken is ERC20Basic { using SafeMath for uint256; mapping(address => uint256) balances; uint256 totalSupply_; /** * @dev total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public returns (bool) { require(_to != address(0)); require(_value <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; } } contract BurnableToken is BasicToken { event Burn(address indexed burner, uint256 value); /** * @dev Burns a specific amount of tokens. * @param _value The amount of token to be burned. */ function burn(uint256 _value) public { _burn(msg.sender, _value); } function _burn(address _who, uint256 _value) internal { require(_value <= balances[_who]); // no need to require value <= totalSupply, since that would imply the // sender's balance is greater than the totalSupply, which *should* be an assertion failure balances[_who] = balances[_who].sub(_value); totalSupply_ = totalSupply_.sub(_value); emit Burn(_who, _value); emit Transfer(_who, address(0), _value); } } contract Staff is Ownable, RBAC { string public constant ROLE_STAFF = "staff"; function addStaff(address _staff) public onlyOwner { addRole(_staff, ROLE_STAFF); } function removeStaff(address _staff) public onlyOwner { removeRole(_staff, ROLE_STAFF); } function isStaff(address _staff) view public returns (bool) { return hasRole(_staff, ROLE_STAFF); } } contract StaffUtil { Staff public staffContract; constructor (Staff _staffContract) public { require(msg.sender == _staffContract.owner()); staffContract = _staffContract; } modifier onlyOwner() { require(msg.sender == staffContract.owner()); _; } modifier onlyOwnerOrStaff() { require(msg.sender == staffContract.owner() || staffContract.isStaff(msg.sender)); _; } } contract Crowdsale is StaffUtil { using SafeMath for uint256; Token tokenContract; PromoCodes promoCodesContract; DiscountPhases discountPhasesContract; DiscountStructs discountStructsContract; address ethFundsWallet; uint256 referralBonusPercent; uint256 startDate; uint256 crowdsaleStartDate; uint256 endDate; uint256 tokenDecimals; uint256 tokenRate; uint256 tokensForSaleCap; uint256 minPurchaseInWei; uint256 maxInvestorContributionInWei; bool paused; bool finalized; uint256 weiRaised; uint256 soldTokens; uint256 bonusTokens; uint256 sentTokens; uint256 claimedSoldTokens; uint256 claimedBonusTokens; uint256 claimedSentTokens; uint256 purchasedTokensClaimDate; uint256 bonusTokensClaimDate; mapping(address => Investor) public investors; enum InvestorStatus {UNDEFINED, WHITELISTED, BLOCKED} struct Investor { InvestorStatus status; uint256 contributionInWei; uint256 purchasedTokens; uint256 bonusTokens; uint256 referralTokens; uint256 receivedTokens; TokensPurchase[] tokensPurchases; bool isBlockpass; } struct TokensPurchase { uint256 value; uint256 amount; uint256 bonus; address referrer; uint256 referrerSentAmount; } event InvestorWhitelisted(address indexed investor, uint timestamp, address byStaff); event InvestorBlocked(address indexed investor, uint timestamp, address byStaff); event TokensPurchased( address indexed investor, uint indexed purchaseId, uint256 value, uint256 purchasedAmount, uint256 promoCodeAmount, uint256 discountPhaseAmount, uint256 discountStructAmount, address indexed referrer, uint256 referrerSentAmount, uint timestamp ); event TokensPurchaseRefunded( address indexed investor, uint indexed purchaseId, uint256 value, uint256 amount, uint256 bonus, uint timestamp, address byStaff ); event Paused(uint timestamp, address byStaff); event Resumed(uint timestamp, address byStaff); event Finalized(uint timestamp, address byStaff); event TokensSent(address indexed investor, uint256 amount, uint timestamp, address byStaff); event PurchasedTokensClaimLocked(uint date, uint timestamp, address byStaff); event PurchasedTokensClaimUnlocked(uint timestamp, address byStaff); event BonusTokensClaimLocked(uint date, uint timestamp, address byStaff); event BonusTokensClaimUnlocked(uint timestamp, address byStaff); event CrowdsaleStartDateUpdated(uint date, uint timestamp, address byStaff); event EndDateUpdated(uint date, uint timestamp, address byStaff); event MinPurchaseChanged(uint256 minPurchaseInWei, uint timestamp, address byStaff); event MaxInvestorContributionChanged(uint256 maxInvestorContributionInWei, uint timestamp, address byStaff); event TokenRateChanged(uint newRate, uint timestamp, address byStaff); event TokensClaimed( address indexed investor, uint256 purchased, uint256 bonus, uint256 referral, uint256 received, uint timestamp, address byStaff ); event TokensBurned(uint256 amount, uint timestamp, address byStaff); constructor ( uint256[11] uint256Args, address[5] addressArgs ) StaffUtil(Staff(addressArgs[4])) public { // uint256 args startDate = uint256Args[0]; crowdsaleStartDate = uint256Args[1]; endDate = uint256Args[2]; tokenDecimals = uint256Args[3]; tokenRate = uint256Args[4]; tokensForSaleCap = uint256Args[5]; minPurchaseInWei = uint256Args[6]; maxInvestorContributionInWei = uint256Args[7]; purchasedTokensClaimDate = uint256Args[8]; bonusTokensClaimDate = uint256Args[9]; referralBonusPercent = uint256Args[10]; // address args ethFundsWallet = addressArgs[0]; promoCodesContract = PromoCodes(addressArgs[1]); discountPhasesContract = DiscountPhases(addressArgs[2]); discountStructsContract = DiscountStructs(addressArgs[3]); require(startDate < crowdsaleStartDate); require(crowdsaleStartDate < endDate); require(tokenDecimals > 0); require(tokenRate > 0); require(tokensForSaleCap > 0); require(minPurchaseInWei <= maxInvestorContributionInWei); require(ethFundsWallet != address(0)); } function getState() external view returns (bool[2] boolArgs, uint256[18] uint256Args, address[6] addressArgs) { boolArgs[0] = paused; boolArgs[1] = finalized; uint256Args[0] = weiRaised; uint256Args[1] = soldTokens; uint256Args[2] = bonusTokens; uint256Args[3] = sentTokens; uint256Args[4] = claimedSoldTokens; uint256Args[5] = claimedBonusTokens; uint256Args[6] = claimedSentTokens; uint256Args[7] = purchasedTokensClaimDate; uint256Args[8] = bonusTokensClaimDate; uint256Args[9] = startDate; uint256Args[10] = crowdsaleStartDate; uint256Args[11] = endDate; uint256Args[12] = tokenRate; uint256Args[13] = tokenDecimals; uint256Args[14] = minPurchaseInWei; uint256Args[15] = maxInvestorContributionInWei; uint256Args[16] = referralBonusPercent; uint256Args[17] = getTokensForSaleCap(); addressArgs[0] = staffContract; addressArgs[1] = ethFundsWallet; addressArgs[2] = promoCodesContract; addressArgs[3] = discountPhasesContract; addressArgs[4] = discountStructsContract; addressArgs[5] = tokenContract; } function fitsTokensForSaleCap(uint256 _amount) public view returns (bool) { return getDistributedTokens().add(_amount) <= getTokensForSaleCap(); } function getTokensForSaleCap() public view returns (uint256) { if (tokenContract != address(0)) { return tokenContract.balanceOf(this); } return tokensForSaleCap; } function getDistributedTokens() public view returns (uint256) { return soldTokens.sub(claimedSoldTokens).add(bonusTokens.sub(claimedBonusTokens)).add(sentTokens.sub(claimedSentTokens)); } function setTokenContract(Token token) external onlyOwner { require(token.balanceOf(this) >= 0); require(tokenContract == address(0)); require(token != address(0)); tokenContract = token; } function getInvestorClaimedTokens(address _investor) external view returns (uint256) { if (tokenContract != address(0)) { return tokenContract.balanceOf(_investor); } return 0; } function isBlockpassInvestor(address _investor) external constant returns (bool) { return investors[_investor].status == InvestorStatus.WHITELISTED && investors[_investor].isBlockpass; } function whitelistInvestor(address _investor, bool _isBlockpass) external onlyOwnerOrStaff { require(_investor != address(0)); require(investors[_investor].status != InvestorStatus.WHITELISTED); investors[_investor].status = InvestorStatus.WHITELISTED; investors[_investor].isBlockpass = _isBlockpass; emit InvestorWhitelisted(_investor, now, msg.sender); } function bulkWhitelistInvestor(address[] _investors) external onlyOwnerOrStaff { for (uint256 i = 0; i < _investors.length; i++) { if (_investors[i] != address(0) && investors[_investors[i]].status != InvestorStatus.WHITELISTED) { investors[_investors[i]].status = InvestorStatus.WHITELISTED; emit InvestorWhitelisted(_investors[i], now, msg.sender); } } } function blockInvestor(address _investor) external onlyOwnerOrStaff { require(_investor != address(0)); require(investors[_investor].status != InvestorStatus.BLOCKED); investors[_investor].status = InvestorStatus.BLOCKED; emit InvestorBlocked(_investor, now, msg.sender); } function lockPurchasedTokensClaim(uint256 _date) external onlyOwner { require(_date > now); purchasedTokensClaimDate = _date; emit PurchasedTokensClaimLocked(_date, now, msg.sender); } function unlockPurchasedTokensClaim() external onlyOwner { purchasedTokensClaimDate = now; emit PurchasedTokensClaimUnlocked(now, msg.sender); } function lockBonusTokensClaim(uint256 _date) external onlyOwner { require(_date > now); bonusTokensClaimDate = _date; emit BonusTokensClaimLocked(_date, now, msg.sender); } function unlockBonusTokensClaim() external onlyOwner { bonusTokensClaimDate = now; emit BonusTokensClaimUnlocked(now, msg.sender); } function setCrowdsaleStartDate(uint256 _date) external onlyOwner { crowdsaleStartDate = _date; emit CrowdsaleStartDateUpdated(_date, now, msg.sender); } function setEndDate(uint256 _date) external onlyOwner { endDate = _date; emit EndDateUpdated(_date, now, msg.sender); } function setMinPurchaseInWei(uint256 _minPurchaseInWei) external onlyOwner { minPurchaseInWei = _minPurchaseInWei; emit MinPurchaseChanged(_minPurchaseInWei, now, msg.sender); } function setMaxInvestorContributionInWei(uint256 _maxInvestorContributionInWei) external onlyOwner { require(minPurchaseInWei <= _maxInvestorContributionInWei); maxInvestorContributionInWei = _maxInvestorContributionInWei; emit MaxInvestorContributionChanged(_maxInvestorContributionInWei, now, msg.sender); } function changeTokenRate(uint256 _tokenRate) external onlyOwner { require(_tokenRate > 0); tokenRate = _tokenRate; emit TokenRateChanged(_tokenRate, now, msg.sender); } function buyTokens(bytes32 _promoCode, address _referrer) external payable { require(!finalized); require(!paused); require(startDate < now); require(investors[msg.sender].status == InvestorStatus.WHITELISTED); require(msg.value > 0); require(msg.value >= minPurchaseInWei); require(investors[msg.sender].contributionInWei.add(msg.value) <= maxInvestorContributionInWei); // calculate purchased amount uint256 purchasedAmount; if (tokenDecimals > 18) { purchasedAmount = msg.value.mul(tokenRate).mul(10 ** (tokenDecimals - 18)); } else if (tokenDecimals < 18) { purchasedAmount = msg.value.mul(tokenRate).div(10 ** (18 - tokenDecimals)); } else { purchasedAmount = msg.value.mul(tokenRate); } // calculate total amount, this includes promo code amount or discount phase amount uint256 promoCodeBonusAmount = promoCodesContract.applyBonusAmount(msg.sender, purchasedAmount, _promoCode); uint256 discountPhaseBonusAmount = discountPhasesContract.calculateBonusAmount(purchasedAmount); uint256 discountStructBonusAmount = discountStructsContract.getBonus(msg.sender, purchasedAmount, msg.value); uint256 bonusAmount = promoCodeBonusAmount.add(discountPhaseBonusAmount).add(discountStructBonusAmount); // update referrer's referral tokens uint256 referrerBonusAmount; address referrerAddr; if ( _referrer != address(0) && msg.sender != _referrer && investors[_referrer].status == InvestorStatus.WHITELISTED ) { referrerBonusAmount = purchasedAmount * referralBonusPercent / 100; referrerAddr = _referrer; } // check that calculated tokens will not exceed tokens for sale cap require(fitsTokensForSaleCap(purchasedAmount.add(bonusAmount).add(referrerBonusAmount))); // update crowdsale total amount of capital raised weiRaised = weiRaised.add(msg.value); soldTokens = soldTokens.add(purchasedAmount); bonusTokens = bonusTokens.add(bonusAmount).add(referrerBonusAmount); // update referrer's bonus tokens investors[referrerAddr].referralTokens = investors[referrerAddr].referralTokens.add(referrerBonusAmount); // update investor's purchased tokens investors[msg.sender].purchasedTokens = investors[msg.sender].purchasedTokens.add(purchasedAmount); // update investor's bonus tokens investors[msg.sender].bonusTokens = investors[msg.sender].bonusTokens.add(bonusAmount); // update investor's tokens eth value investors[msg.sender].contributionInWei = investors[msg.sender].contributionInWei.add(msg.value); // update investor's tokens purchases uint tokensPurchasesLength = investors[msg.sender].tokensPurchases.push(TokensPurchase({ value : msg.value, amount : purchasedAmount, bonus : bonusAmount, referrer : referrerAddr, referrerSentAmount : referrerBonusAmount }) ); // log investor's tokens purchase emit TokensPurchased( msg.sender, tokensPurchasesLength - 1, msg.value, purchasedAmount, promoCodeBonusAmount, discountPhaseBonusAmount, discountStructBonusAmount, referrerAddr, referrerBonusAmount, now ); // forward eth to funds wallet require(ethFundsWallet.call.gas(300000).value(msg.value)()); } function sendTokens(address _investor, uint256 _amount) external onlyOwner { require(investors[_investor].status == InvestorStatus.WHITELISTED); require(_amount > 0); require(fitsTokensForSaleCap(_amount)); // update crowdsale total amount of capital raised sentTokens = sentTokens.add(_amount); // update investor's received tokens balance investors[_investor].receivedTokens = investors[_investor].receivedTokens.add(_amount); // log tokens sent action emit TokensSent( _investor, _amount, now, msg.sender ); } function burnUnsoldTokens() external onlyOwner { require(tokenContract != address(0)); require(finalized); uint256 tokensToBurn = tokenContract.balanceOf(this).sub(getDistributedTokens()); require(tokensToBurn > 0); tokenContract.burn(tokensToBurn); // log tokens burned action emit TokensBurned(tokensToBurn, now, msg.sender); } function claimTokens() external { require(tokenContract != address(0)); require(!paused); require(investors[msg.sender].status == InvestorStatus.WHITELISTED); uint256 clPurchasedTokens; uint256 clReceivedTokens; uint256 clBonusTokens_; uint256 clRefTokens; require(purchasedTokensClaimDate < now || bonusTokensClaimDate < now); { uint256 purchasedTokens = investors[msg.sender].purchasedTokens; uint256 receivedTokens = investors[msg.sender].receivedTokens; if (purchasedTokensClaimDate < now && (purchasedTokens > 0 || receivedTokens > 0)) { investors[msg.sender].contributionInWei = 0; investors[msg.sender].purchasedTokens = 0; investors[msg.sender].receivedTokens = 0; claimedSoldTokens = claimedSoldTokens.add(purchasedTokens); claimedSentTokens = claimedSentTokens.add(receivedTokens); // free up storage used by transaction delete (investors[msg.sender].tokensPurchases); clPurchasedTokens = purchasedTokens; clReceivedTokens = receivedTokens; tokenContract.transfer(msg.sender, purchasedTokens.add(receivedTokens)); } } { uint256 bonusTokens_ = investors[msg.sender].bonusTokens; uint256 refTokens = investors[msg.sender].referralTokens; if (bonusTokensClaimDate < now && (bonusTokens_ > 0 || refTokens > 0)) { investors[msg.sender].bonusTokens = 0; investors[msg.sender].referralTokens = 0; claimedBonusTokens = claimedBonusTokens.add(bonusTokens_).add(refTokens); clBonusTokens_ = bonusTokens_; clRefTokens = refTokens; tokenContract.transfer(msg.sender, bonusTokens_.add(refTokens)); } } require(clPurchasedTokens > 0 || clBonusTokens_ > 0 || clRefTokens > 0 || clReceivedTokens > 0); emit TokensClaimed(msg.sender, clPurchasedTokens, clBonusTokens_, clRefTokens, clReceivedTokens, now, msg.sender); } function refundTokensPurchase(address _investor, uint _purchaseId) external payable onlyOwner { require(msg.value > 0); require(investors[_investor].tokensPurchases[_purchaseId].value == msg.value); _refundTokensPurchase(_investor, _purchaseId); // forward eth to investor's wallet address _investor.transfer(msg.value); } function refundAllInvestorTokensPurchases(address _investor) external payable onlyOwner { require(msg.value > 0); require(investors[_investor].contributionInWei == msg.value); for (uint i = 0; i < investors[_investor].tokensPurchases.length; i++) { if (investors[_investor].tokensPurchases[i].value == 0) { continue; } _refundTokensPurchase(_investor, i); } // forward eth to investor's wallet address _investor.transfer(msg.value); } function _refundTokensPurchase(address _investor, uint _purchaseId) private { // update referrer's referral tokens address referrer = investors[_investor].tokensPurchases[_purchaseId].referrer; if (referrer != address(0)) { uint256 sentAmount = investors[_investor].tokensPurchases[_purchaseId].referrerSentAmount; investors[referrer].referralTokens = investors[referrer].referralTokens.sub(sentAmount); bonusTokens = bonusTokens.sub(sentAmount); } // update investor's eth amount uint256 purchaseValue = investors[_investor].tokensPurchases[_purchaseId].value; investors[_investor].contributionInWei = investors[_investor].contributionInWei.sub(purchaseValue); // update investor's purchased tokens uint256 purchaseAmount = investors[_investor].tokensPurchases[_purchaseId].amount; investors[_investor].purchasedTokens = investors[_investor].purchasedTokens.sub(purchaseAmount); // update investor's bonus tokens uint256 bonusAmount = investors[_investor].tokensPurchases[_purchaseId].bonus; investors[_investor].bonusTokens = investors[_investor].bonusTokens.sub(bonusAmount); // update crowdsale total amount of capital raised weiRaised = weiRaised.sub(purchaseValue); soldTokens = soldTokens.sub(purchaseAmount); bonusTokens = bonusTokens.sub(bonusAmount); // free up storage used by transaction delete (investors[_investor].tokensPurchases[_purchaseId]); // log investor's tokens purchase refund emit TokensPurchaseRefunded(_investor, _purchaseId, purchaseValue, purchaseAmount, bonusAmount, now, msg.sender); } function getInvestorTokensPurchasesLength(address _investor) public constant returns (uint) { return investors[_investor].tokensPurchases.length; } function getInvestorTokensPurchase( address _investor, uint _purchaseId ) external constant returns ( uint256 value, uint256 amount, uint256 bonus, address referrer, uint256 referrerSentAmount ) { value = investors[_investor].tokensPurchases[_purchaseId].value; amount = investors[_investor].tokensPurchases[_purchaseId].amount; bonus = investors[_investor].tokensPurchases[_purchaseId].bonus; referrer = investors[_investor].tokensPurchases[_purchaseId].referrer; referrerSentAmount = investors[_investor].tokensPurchases[_purchaseId].referrerSentAmount; } function pause() external onlyOwner { require(!paused); paused = true; emit Paused(now, msg.sender); } function resume() external onlyOwner { require(paused); paused = false; emit Resumed(now, msg.sender); } function finalize() external onlyOwner { require(!finalized); finalized = true; emit Finalized(now, msg.sender); } } contract DiscountPhases is StaffUtil { using SafeMath for uint256; event DiscountPhaseAdded(uint index, string name, uint8 percent, uint fromDate, uint toDate, uint timestamp, address byStaff); event DiscountPhaseRemoved(uint index, uint timestamp, address byStaff); struct DiscountPhase { uint8 percent; uint fromDate; uint toDate; } DiscountPhase[] public discountPhases; constructor(Staff _staffContract) StaffUtil(_staffContract) public { } function calculateBonusAmount(uint256 _purchasedAmount) public constant returns (uint256) { for (uint i = 0; i < discountPhases.length; i++) { if (now >= discountPhases[i].fromDate && now <= discountPhases[i].toDate) { return _purchasedAmount.mul(discountPhases[i].percent).div(100); } } } function addDiscountPhase(string _name, uint8 _percent, uint _fromDate, uint _toDate) public onlyOwnerOrStaff { require(bytes(_name).length > 0); require(_percent > 0 && _percent <= 100); if (now > _fromDate) { _fromDate = now; } require(_fromDate < _toDate); for (uint i = 0; i < discountPhases.length; i++) { require(_fromDate > discountPhases[i].toDate || _toDate < discountPhases[i].fromDate); } uint index = discountPhases.push(DiscountPhase({percent : _percent, fromDate : _fromDate, toDate : _toDate})) - 1; emit DiscountPhaseAdded(index, _name, _percent, _fromDate, _toDate, now, msg.sender); } function removeDiscountPhase(uint _index) public onlyOwnerOrStaff { require(now < discountPhases[_index].toDate); delete discountPhases[_index]; emit DiscountPhaseRemoved(_index, now, msg.sender); } } contract DiscountStructs is StaffUtil { using SafeMath for uint256; address public crowdsale; event DiscountStructAdded( uint index, bytes32 name, uint256 tokens, uint[2] dates, uint256[] fromWei, uint256[] toWei, uint256[] percent, uint timestamp, address byStaff ); event DiscountStructRemoved( uint index, uint timestamp, address byStaff ); event DiscountStructUsed( uint index, uint step, address investor, uint256 tokens, uint timestamp ); struct DiscountStruct { uint256 availableTokens; uint256 distributedTokens; uint fromDate; uint toDate; } struct DiscountStep { uint256 fromWei; uint256 toWei; uint256 percent; } DiscountStruct[] public discountStructs; mapping(uint => DiscountStep[]) public discountSteps; constructor(Staff _staffContract) StaffUtil(_staffContract) public { } modifier onlyCrowdsale() { require(msg.sender == crowdsale); _; } function setCrowdsale(Crowdsale _crowdsale) external onlyOwner { require(crowdsale == address(0)); require(_crowdsale.staffContract() == staffContract); crowdsale = _crowdsale; } function getBonus(address _investor, uint256 _purchasedAmount, uint256 _purchasedValue) public onlyCrowdsale returns (uint256) { for (uint i = 0; i < discountStructs.length; i++) { if (now >= discountStructs[i].fromDate && now <= discountStructs[i].toDate) { if (discountStructs[i].distributedTokens >= discountStructs[i].availableTokens) { return; } for (uint j = 0; j < discountSteps[i].length; j++) { if (_purchasedValue >= discountSteps[i][j].fromWei && (_purchasedValue < discountSteps[i][j].toWei || discountSteps[i][j].toWei == 0)) { uint256 bonus = _purchasedAmount.mul(discountSteps[i][j].percent).div(100); if (discountStructs[i].distributedTokens.add(bonus) > discountStructs[i].availableTokens) { return; } discountStructs[i].distributedTokens = discountStructs[i].distributedTokens.add(bonus); emit DiscountStructUsed(i, j, _investor, bonus, now); return bonus; } } return; } } } function calculateBonus(uint256 _purchasedAmount, uint256 _purchasedValue) public constant returns (uint256) { for (uint i = 0; i < discountStructs.length; i++) { if (now >= discountStructs[i].fromDate && now <= discountStructs[i].toDate) { if (discountStructs[i].distributedTokens >= discountStructs[i].availableTokens) { return; } for (uint j = 0; j < discountSteps[i].length; j++) { if (_purchasedValue >= discountSteps[i][j].fromWei && (_purchasedValue < discountSteps[i][j].toWei || discountSteps[i][j].toWei == 0)) { uint256 bonus = _purchasedAmount.mul(discountSteps[i][j].percent).div(100); if (discountStructs[i].distributedTokens.add(bonus) > discountStructs[i].availableTokens) { return; } return bonus; } } return; } } } function addDiscountStruct(bytes32 _name, uint256 _tokens, uint[2] _dates, uint256[] _fromWei, uint256[] _toWei, uint256[] _percent) external onlyOwnerOrStaff { require(_name.length > 0); require(_tokens > 0); require(_dates[0] < _dates[1]); require(_fromWei.length > 0 && _fromWei.length == _toWei.length && _fromWei.length == _percent.length); for (uint j = 0; j < discountStructs.length; j++) { require(_dates[0] > discountStructs[j].fromDate || _dates[1] < discountStructs[j].toDate); } DiscountStruct memory ds = DiscountStruct(_tokens, 0, _dates[0], _dates[1]); uint index = discountStructs.push(ds) - 1; for (uint i = 0; i < _fromWei.length; i++) { require(_fromWei[i] > 0 || _toWei[i] > 0); if (_fromWei[i] > 0 && _toWei[i] > 0) { require(_fromWei[i] < _toWei[i]); } require(_percent[i] > 0 && _percent[i] <= 100); discountSteps[index].push(DiscountStep(_fromWei[i], _toWei[i], _percent[i])); } emit DiscountStructAdded(index, _name, _tokens, _dates, _fromWei, _toWei, _percent, now, msg.sender); } function removeDiscountStruct(uint _index) public onlyOwnerOrStaff { require(now < discountStructs[_index].toDate); delete discountStructs[_index]; delete discountSteps[_index]; emit DiscountStructRemoved(_index, now, msg.sender); } } contract PromoCodes is StaffUtil { using SafeMath for uint256; address public crowdsale; event PromoCodeAdded(bytes32 indexed code, string name, uint8 percent, uint256 maxUses, uint timestamp, address byStaff); event PromoCodeRemoved(bytes32 indexed code, uint timestamp, address byStaff); event PromoCodeUsed(bytes32 indexed code, address investor, uint timestamp); struct PromoCode { uint8 percent; uint256 uses; uint256 maxUses; mapping(address => bool) investors; } mapping(bytes32 => PromoCode) public promoCodes; constructor(Staff _staffContract) StaffUtil(_staffContract) public { } modifier onlyCrowdsale() { require(msg.sender == crowdsale); _; } function setCrowdsale(Crowdsale _crowdsale) external onlyOwner { require(crowdsale == address(0)); require(_crowdsale.staffContract() == staffContract); crowdsale = _crowdsale; } function applyBonusAmount(address _investor, uint256 _purchasedAmount, bytes32 _promoCode) public onlyCrowdsale returns (uint256) { if (promoCodes[_promoCode].percent == 0 || promoCodes[_promoCode].investors[_investor] || promoCodes[_promoCode].uses == promoCodes[_promoCode].maxUses) { return 0; } promoCodes[_promoCode].investors[_investor] = true; promoCodes[_promoCode].uses = promoCodes[_promoCode].uses + 1; emit PromoCodeUsed(_promoCode, _investor, now); return _purchasedAmount.mul(promoCodes[_promoCode].percent).div(100); } function calculateBonusAmount(address _investor, uint256 _purchasedAmount, bytes32 _promoCode) public constant returns (uint256) { if (promoCodes[_promoCode].percent == 0 || promoCodes[_promoCode].investors[_investor] || promoCodes[_promoCode].uses == promoCodes[_promoCode].maxUses) { return 0; } return _purchasedAmount.mul(promoCodes[_promoCode].percent).div(100); } function addPromoCode(string _name, bytes32 _code, uint256 _maxUses, uint8 _percent) public onlyOwnerOrStaff { require(bytes(_name).length > 0); require(_code[0] != 0); require(_percent > 0 && _percent <= 100); require(_maxUses > 0); require(promoCodes[_code].percent == 0); promoCodes[_code].percent = _percent; promoCodes[_code].maxUses = _maxUses; emit PromoCodeAdded(_code, _name, _percent, _maxUses, now, msg.sender); } function removePromoCode(bytes32 _code) public onlyOwnerOrStaff { delete promoCodes[_code]; emit PromoCodeRemoved(_code, now, msg.sender); } } contract Token is BurnableToken { }
File 2 of 2: Staff
pragma solidity ^0.4.13; contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } contract RBAC { using Roles for Roles.Role; mapping (string => Roles.Role) private roles; event RoleAdded(address addr, string roleName); event RoleRemoved(address addr, string roleName); /** * @dev reverts if addr does not have role * @param addr address * @param roleName the name of the role * // reverts */ function checkRole(address addr, string roleName) view public { roles[roleName].check(addr); } /** * @dev determine if addr has role * @param addr address * @param roleName the name of the role * @return bool */ function hasRole(address addr, string roleName) view public returns (bool) { return roles[roleName].has(addr); } /** * @dev add a role to an address * @param addr address * @param roleName the name of the role */ function addRole(address addr, string roleName) internal { roles[roleName].add(addr); emit RoleAdded(addr, roleName); } /** * @dev remove a role from an address * @param addr address * @param roleName the name of the role */ function removeRole(address addr, string roleName) internal { roles[roleName].remove(addr); emit RoleRemoved(addr, roleName); } /** * @dev modifier to scope access to a single role (uses msg.sender as addr) * @param roleName the name of the role * // reverts */ modifier onlyRole(string roleName) { checkRole(msg.sender, roleName); _; } /** * @dev modifier to scope access to a set of roles (uses msg.sender as addr) * @param roleNames the names of the roles to scope access to * // reverts * * @TODO - when solidity supports dynamic arrays as arguments to modifiers, provide this * see: https://github.com/ethereum/solidity/issues/2467 */ // modifier onlyRoles(string[] roleNames) { // bool hasAnyRole = false; // for (uint8 i = 0; i < roleNames.length; i++) { // if (hasRole(msg.sender, roleNames[i])) { // hasAnyRole = true; // break; // } // } // require(hasAnyRole); // _; // } } library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an address access to this role */ function add(Role storage role, address addr) internal { role.bearer[addr] = true; } /** * @dev remove an address' access to this role */ function remove(Role storage role, address addr) internal { role.bearer[addr] = false; } /** * @dev check if an address has this role * // reverts */ function check(Role storage role, address addr) view internal { require(has(role, addr)); } /** * @dev check if an address has this role * @return bool */ function has(Role storage role, address addr) view internal returns (bool) { return role.bearer[addr]; } } contract Staff is Ownable, RBAC { string public constant ROLE_STAFF = "staff"; function addStaff(address _staff) public onlyOwner { addRole(_staff, ROLE_STAFF); } function removeStaff(address _staff) public onlyOwner { removeRole(_staff, ROLE_STAFF); } function isStaff(address _staff) view public returns (bool) { return hasRole(_staff, ROLE_STAFF); } }