Transaction Hash:
Block:
7362667 at Mar-13-2019 07:53:56 PM +UTC
Transaction Fee:
0.000191019 ETH
$0.49
Gas Used:
63,673 Gas / 3 Gwei
Emitted Events:
36 |
CrowdsaleToken.Transfer( from=[Receiver] TokenStore, to=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, value=10753700000000000000 )
|
37 |
TokenStore.Withdraw( token=CrowdsaleToken, user=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, amount=10753700000000000000, balance=38783649052841 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0Cf0Ee63...E122cC023 | |||||
0x1cE7AE55...ee6Ee33D8 | (Token.Store) | ||||
0x343a3C7F...F643678d9 |
0.42390716 Eth
Nonce: 22
|
0.423716141 Eth
Nonce: 23
| 0.000191019 | ||
0xEEa5B82B...2d0D25Bfb
Miner
| (BTC.com Pool) | 269.809674248641468852 Eth | 269.809865267641468852 Eth | 0.000191019 |
Execution Trace
TokenStore.withdrawToken( _token=0x0Cf0Ee63788A0849fE5297F3407f701E122cC023, _amount=10753700000000000000 )
-
CrowdsaleToken.transfer( _to=0x343a3C7F789335C9EA60932D34bE258F643678d9, _value=10753700000000000000 ) => ( success=True )
withdrawToken[TokenStore (ln:194)]
safeSub[TokenStore (ln:197)]
transfer[TokenStore (ln:198)]
revert[TokenStore (ln:199)]
Withdraw[TokenStore (ln:201)]
File 1 of 2: TokenStore
File 2 of 2: CrowdsaleToken
pragma solidity ^0.4.11; // ERC20 token protocol, see more details at // https://theethereum.wiki/w/index.php/ERC20_Token_Standard // And also https://github.com/ethereum/eips/issues/20 contract Token { function totalSupply() constant returns (uint256 supply); function balanceOf(address _owner) constant returns (uint256 balance); function transfer(address _to, uint256 _value) returns (bool success); function transferFrom(address _from, address _to, uint256 _value) returns (bool success); function approve(address _spender, uint256 _value) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint256 remaining); event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); } // Safe mathematics to make the code more readable contract SafeMath { function safeMul(uint a, uint b) internal returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function safeSub(uint a, uint b) internal returns (uint) { assert(b <= a); return a - b; } function safeAdd(uint a, uint b) internal returns (uint) { uint c = a + b; assert(c>=a && c>=b); return c; } } // Ownable interface to simplify owner checks contract Ownable { address public owner; function Ownable() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner); _; } function transferOwnership(address _newOwner) onlyOwner { require(_newOwner != address(0)); owner = _newOwner; } } // Interface for trading discounts and rebates for specific accounts contract AccountModifiersInterface { function accountModifiers(address _user) constant returns(uint takeFeeDiscount, uint rebatePercentage); function tradeModifiers(address _maker, address _taker) constant returns(uint takeFeeDiscount, uint rebatePercentage); } // Interface for trade tacker contract TradeTrackerInterface { function tradeComplete(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, address _get, address _give, uint _takerFee, uint _makerRebate); } // Exchange contract contract TokenStore is SafeMath, Ownable { // The account that will receive fees address feeAccount; // The account that stores fee discounts/rebates address accountModifiers; // Trade tracker account address tradeTracker; // We charge only the takers and this is the fee, percentage times 1 ether uint public fee; // Mapping of token addresses to mapping of account balances (token 0 means Ether) mapping (address => mapping (address => uint)) public tokens; // Mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled) mapping (address => mapping (bytes32 => uint)) public orderFills; // Address of a next and previous versions of the contract, also status of the contract // can be used for user-triggered fund migrations address public successor; address public predecessor; bool public deprecated; uint16 public version; // Logging events // Note: Order creation is handled off-chain, see explanation further below event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s); event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give, uint nonce); event Deposit(address token, address user, uint amount, uint balance); event Withdraw(address token, address user, uint amount, uint balance); event FundsMigrated(address user); function TokenStore(uint _fee, address _predecessor) { feeAccount = owner; fee = _fee; predecessor = _predecessor; deprecated = false; if (predecessor != address(0)) { version = TokenStore(predecessor).version() + 1; } else { version = 1; } } // Throw on default handler to prevent direct transactions of Ether function() { revert(); } modifier deprecable() { require(!deprecated); _; } function deprecate(bool _deprecated, address _successor) onlyOwner { deprecated = _deprecated; successor = _successor; } function changeFeeAccount(address _feeAccount) onlyOwner { require(_feeAccount != address(0)); feeAccount = _feeAccount; } function changeAccountModifiers(address _accountModifiers) onlyOwner { accountModifiers = _accountModifiers; } function changeTradeTracker(address _tradeTracker) onlyOwner { tradeTracker = _tradeTracker; } // Fee can only be decreased! function changeFee(uint _fee) onlyOwner { require(_fee <= fee); fee = _fee; } // Allows a user to get her current discount/rebate function getAccountModifiers() constant returns(uint takeFeeDiscount, uint rebatePercentage) { if (accountModifiers != address(0)) { return AccountModifiersInterface(accountModifiers).accountModifiers(msg.sender); } else { return (0, 0); } } //////////////////////////////////////////////////////////////////////////////// // Deposits, withdrawals, balances //////////////////////////////////////////////////////////////////////////////// function deposit() payable deprecable { tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value); Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]); } function withdraw(uint _amount) { require(tokens[0][msg.sender] >= _amount); tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], _amount); if (!msg.sender.call.value(_amount)()) { revert(); } Withdraw(0, msg.sender, _amount, tokens[0][msg.sender]); } function depositToken(address _token, uint _amount) deprecable { // Note that Token(_token).approve(this, _amount) needs to be called // first or this contract will not be able to do the transfer. require(_token != 0); if (!Token(_token).transferFrom(msg.sender, this, _amount)) { revert(); } tokens[_token][msg.sender] = safeAdd(tokens[_token][msg.sender], _amount); Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]); } function withdrawToken(address _token, uint _amount) { require(_token != 0); require(tokens[_token][msg.sender] >= _amount); tokens[_token][msg.sender] = safeSub(tokens[_token][msg.sender], _amount); if (!Token(_token).transfer(msg.sender, _amount)) { revert(); } Withdraw(_token, msg.sender, _amount, tokens[_token][msg.sender]); } function balanceOf(address _token, address _user) constant returns (uint) { return tokens[_token][_user]; } //////////////////////////////////////////////////////////////////////////////// // Trading //////////////////////////////////////////////////////////////////////////////// // Note: Order creation happens off-chain but the orders are signed by creators, // we validate the contents and the creator address in the logic below function trade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires, uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount) { bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce); // Check order signatures and expiration, also check if not fulfilled yet if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user || block.number > _expires || safeAdd(orderFills[_user][hash], _amount) > _amountGet) { revert(); } tradeBalances(_tokenGet, _amountGet, _tokenGive, _amountGive, _user, msg.sender, _amount); orderFills[_user][hash] = safeAdd(orderFills[_user][hash], _amount); Trade(_tokenGet, _amount, _tokenGive, _amountGive * _amount / _amountGet, _user, msg.sender, _nonce); } function tradeBalances(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, address _user, address _caller, uint _amount) private { uint feeTakeValue = safeMul(_amount, fee) / (1 ether); uint rebateValue = 0; uint tokenGiveValue = safeMul(_amountGive, _amount) / _amountGet; // Proportionate to request ratio // Apply modifiers if (accountModifiers != address(0)) { var (feeTakeDiscount, rebatePercentage) = AccountModifiersInterface(accountModifiers).tradeModifiers(_user, _caller); // Check that the discounts/rebates are never higher then 100% if (feeTakeDiscount > 100) { feeTakeDiscount = 0; } if (rebatePercentage > 100) { rebatePercentage = 0; } feeTakeValue = safeMul(feeTakeValue, 100 - feeTakeDiscount) / 100; // discounted fee rebateValue = safeMul(rebatePercentage, feeTakeValue) / 100; // % of actual taker fee } tokens[_tokenGet][_user] = safeAdd(tokens[_tokenGet][_user], safeAdd(_amount, rebateValue)); tokens[_tokenGet][_caller] = safeSub(tokens[_tokenGet][_caller], safeAdd(_amount, feeTakeValue)); tokens[_tokenGive][_user] = safeSub(tokens[_tokenGive][_user], tokenGiveValue); tokens[_tokenGive][_caller] = safeAdd(tokens[_tokenGive][_caller], tokenGiveValue); tokens[_tokenGet][feeAccount] = safeAdd(tokens[_tokenGet][feeAccount], safeSub(feeTakeValue, rebateValue)); if (tradeTracker != address(0)) { TradeTrackerInterface(tradeTracker).tradeComplete(_tokenGet, _amount, _tokenGive, tokenGiveValue, _user, _caller, feeTakeValue, rebateValue); } } function testTrade(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires, uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s, uint _amount, address _sender) constant returns(bool) { if (tokens[_tokenGet][_sender] < _amount || availableVolume(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, _user, _v, _r, _s) < _amount) { return false; } return true; } function availableVolume(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires, uint _nonce, address _user, uint8 _v, bytes32 _r, bytes32 _s) constant returns(uint) { bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce); if (ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) != _user || block.number > _expires) { return 0; } uint available1 = safeSub(_amountGet, orderFills[_user][hash]); uint available2 = safeMul(tokens[_tokenGive][_user], _amountGet) / _amountGive; if (available1 < available2) return available1; return available2; } function amountFilled(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires, uint _nonce, address _user) constant returns(uint) { bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce); return orderFills[_user][hash]; } function cancelOrder(address _tokenGet, uint _amountGet, address _tokenGive, uint _amountGive, uint _expires, uint _nonce, uint8 _v, bytes32 _r, bytes32 _s) { bytes32 hash = sha256(this, _tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce); if (!(ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) == msg.sender)) { revert(); } orderFills[msg.sender][hash] = _amountGet; Cancel(_tokenGet, _amountGet, _tokenGive, _amountGive, _expires, _nonce, msg.sender, _v, _r, _s); } //////////////////////////////////////////////////////////////////////////////// // Migrations //////////////////////////////////////////////////////////////////////////////// // User-triggered (!) fund migrations in case contract got updated // Similar to withdraw but we use a successor account instead // As we don't store user tokens list on chain, it has to be passed from the outside function migrateFunds(address[] _tokens) { // Get the latest successor in the chain require(successor != address(0)); TokenStore newExchange = TokenStore(successor); for (uint16 n = 0; n < 20; n++) { // We will look past 20 contracts in the future address nextSuccessor = newExchange.successor(); if (nextSuccessor == address(this)) { // Circular succession revert(); } if (nextSuccessor == address(0)) { // We reached the newest, stop break; } newExchange = TokenStore(nextSuccessor); } // Ether uint etherAmount = tokens[0][msg.sender]; if (etherAmount > 0) { tokens[0][msg.sender] = 0; newExchange.depositForUser.value(etherAmount)(msg.sender); } // Tokens for (n = 0; n < _tokens.length; n++) { address token = _tokens[n]; require(token != address(0)); // 0 = Ether, we handle it above uint tokenAmount = tokens[token][msg.sender]; if (tokenAmount == 0) { continue; } if (!Token(token).approve(newExchange, tokenAmount)) { revert(); } tokens[token][msg.sender] = 0; newExchange.depositTokenForUser(token, tokenAmount, msg.sender); } FundsMigrated(msg.sender); } // This is used for migrations only. To be called by previous exchange only, // user-triggered, on behalf of the user called the migrateFunds method. // Note that it does exactly the same as depositToken, but as this is called // by a previous generation of exchange itself, we credit internally not the // previous exchange, but the user it was called for. function depositForUser(address _user) payable deprecable { require(_user != address(0)); require(msg.value > 0); TokenStore caller = TokenStore(msg.sender); require(caller.version() > 0); // Make sure it's an exchange account tokens[0][_user] = safeAdd(tokens[0][_user], msg.value); } function depositTokenForUser(address _token, uint _amount, address _user) deprecable { require(_token != address(0)); require(_user != address(0)); require(_amount > 0); TokenStore caller = TokenStore(msg.sender); require(caller.version() > 0); // Make sure it's an exchange account if (!Token(_token).transferFrom(msg.sender, this, _amount)) { revert(); } tokens[_token][_user] = safeAdd(tokens[_token][_user], _amount); } }
File 2 of 2: CrowdsaleToken
/* * ERC20 interface * see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 { uint public totalSupply; function balanceOf(address who) constant returns (uint); function allowance(address owner, address spender) constant returns (uint); function transfer(address to, uint value) returns (bool ok); function transferFrom(address from, address to, uint value) returns (bool ok); function approve(address spender, uint value) returns (bool ok); event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); } /** * Math operations with safety checks */ contract SafeMath { function safeMul(uint a, uint b) internal returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function safeDiv(uint a, uint b) internal returns (uint) { assert(b > 0); uint c = a / b; assert(a == b * c + a % b); return c; } function safeSub(uint a, uint b) internal returns (uint) { assert(b <= a); return a - b; } function safeAdd(uint a, uint b) internal returns (uint) { uint c = a + b; assert(c>=a && c>=b); return c; } function max64(uint64 a, uint64 b) internal constant returns (uint64) { return a >= b ? a : b; } function min64(uint64 a, uint64 b) internal constant returns (uint64) { return a < b ? a : b; } function max256(uint256 a, uint256 b) internal constant returns (uint256) { return a >= b ? a : b; } function min256(uint256 a, uint256 b) internal constant returns (uint256) { return a < b ? a : b; } function assert(bool assertion) internal { if (!assertion) { throw; } } } /** * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation. * * Based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is ERC20, SafeMath { /* Token supply got increased and a new owner received these tokens */ event Minted(address receiver, uint amount); /* Actual balances of token holders */ mapping(address => uint) balances; /* approve() allowances */ mapping (address => mapping (address => uint)) allowed; /* Interface declaration */ function isToken() public constant returns (bool weAre) { return true; } function transfer(address _to, uint _value) returns (bool success) { balances[msg.sender] = safeSub(balances[msg.sender], _value); balances[_to] = safeAdd(balances[_to], _value); Transfer(msg.sender, _to, _value); return true; } function transferFrom(address _from, address _to, uint _value) returns (bool success) { uint _allowance = allowed[_from][msg.sender]; balances[_to] = safeAdd(balances[_to], _value); balances[_from] = safeSub(balances[_from], _value); allowed[_from][msg.sender] = safeSub(_allowance, _value); Transfer(_from, _to, _value); return true; } function balanceOf(address _owner) constant returns (uint balance) { return balances[_owner]; } function approve(address _spender, uint _value) returns (bool success) { // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw; allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) constant returns (uint remaining) { return allowed[_owner][_spender]; } } /** * Upgrade agent interface inspired by Lunyr. * * Upgrade agent transfers tokens to a new contract. * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting. */ contract UpgradeAgent { uint public originalSupply; /** Interface marker */ function isUpgradeAgent() public constant returns (bool) { return true; } function upgradeFrom(address _from, uint256 _value) public; } /** * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision. * * First envisioned by Golem and Lunyr projects. */ contract UpgradeableToken is StandardToken { /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */ address public upgradeMaster; /** The next contract where the tokens will be migrated. */ UpgradeAgent public upgradeAgent; /** How many tokens we have upgraded by now. */ uint256 public totalUpgraded; /** * Upgrade states. * * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens * */ enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading} /** * Somebody has upgraded some of his tokens. */ event Upgrade(address indexed _from, address indexed _to, uint256 _value); /** * New upgrade agent available. */ event UpgradeAgentSet(address agent); /** * Do not allow construction without upgrade master set. */ function UpgradeableToken(address _upgradeMaster) { upgradeMaster = _upgradeMaster; } /** * Allow the token holder to upgrade some of their tokens to a new contract. */ function upgrade(uint256 value) public { UpgradeState state = getUpgradeState(); if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) { // Called in a bad state throw; } // Validate input value. if (value == 0) throw; balances[msg.sender] = safeSub(balances[msg.sender], value); // Take tokens out from circulation totalSupply = safeSub(totalSupply, value); totalUpgraded = safeAdd(totalUpgraded, value); // Upgrade agent reissues the tokens upgradeAgent.upgradeFrom(msg.sender, value); Upgrade(msg.sender, upgradeAgent, value); } /** * Set an upgrade agent that handles */ function setUpgradeAgent(address agent) external { if(!canUpgrade()) { // The token is not yet in a state that we could think upgrading throw; } if (agent == 0x0) throw; // Only a master can designate the next agent if (msg.sender != upgradeMaster) throw; // Upgrade has already begun for an agent if (getUpgradeState() == UpgradeState.Upgrading) throw; upgradeAgent = UpgradeAgent(agent); // Bad interface if(!upgradeAgent.isUpgradeAgent()) throw; // Make sure that token supplies match in source and target if (upgradeAgent.originalSupply() != totalSupply) throw; UpgradeAgentSet(upgradeAgent); } /** * Get the state of the token upgrade. */ function getUpgradeState() public constant returns(UpgradeState) { if(!canUpgrade()) return UpgradeState.NotAllowed; else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent; else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade; else return UpgradeState.Upgrading; } /** * Change the upgrade master. * * This allows us to set a new owner for the upgrade mechanism. */ function setUpgradeMaster(address master) public { if (master == 0x0) throw; if (msg.sender != upgradeMaster) throw; upgradeMaster = master; } /** * Child contract can enable to provide the condition when the upgrade can begun. */ function canUpgrade() public constant returns(bool) { return true; } } /* * Ownable * * Base contract with an owner. * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. */ contract Ownable { address public owner; function Ownable() { owner = msg.sender; } modifier onlyOwner() { if (msg.sender != owner) { throw; } _; } function transferOwnership(address newOwner) onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } } /** * Define interface for releasing the token transfer after a successful crowdsale. */ contract ReleasableToken is ERC20, Ownable { /* The finalizer contract that allows unlift the transfer limits on this token */ address public releaseAgent; /** A crowdsale contract can release us to the wild if ICO success. If false we are are in transfer lock up period.*/ bool public released = false; /** Map of agents that are allowed to transfer tokens regardless of the lock down period. These are crowdsale contracts and possible the team multisig itself. */ mapping (address => bool) public transferAgents; /** * Limit token transfer until the crowdsale is over. * */ modifier canTransfer(address _sender) { if(!released) { if(!transferAgents[_sender]) { throw; } } _; } /** * Set the contract that can call release and make the token transferable. * * Design choice. Allow reset the release agent to fix fat finger mistakes. */ function setReleaseAgent(address addr) onlyOwner inReleaseState(false) public { // We don't do interface check here as we might want to a normal wallet address to act as a release agent releaseAgent = addr; } /** * Owner can allow a particular address (a crowdsale contract) to transfer tokens despite the lock up period. */ function setTransferAgent(address addr, bool state) onlyOwner inReleaseState(false) public { transferAgents[addr] = state; } /** * One way function to release the tokens to the wild. * * Can be called only from the release agent that is the final ICO contract. It is only called if the crowdsale has been success (first milestone reached). */ function releaseTokenTransfer() public onlyReleaseAgent { released = true; } /** The function can be called only before or after the tokens have been releasesd */ modifier inReleaseState(bool releaseState) { if(releaseState != released) { throw; } _; } /** The function can be called only by a whitelisted release agent. */ modifier onlyReleaseAgent() { if(msg.sender != releaseAgent) { throw; } _; } function transfer(address _to, uint _value) canTransfer(msg.sender) returns (bool success) { // Call StandardToken.transfer() return super.transfer(_to, _value); } function transferFrom(address _from, address _to, uint _value) canTransfer(_from) returns (bool success) { // Call StandardToken.transferForm() return super.transferFrom(_from, _to, _value); } } /** * Safe unsigned safe math. * * https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736#.750gwtwli * * Originally from https://raw.githubusercontent.com/AragonOne/zeppelin-solidity/master/contracts/SafeMathLib.sol * * Maintained here until merged to mainline zeppelin-solidity. * */ library SafeMathLib { function times(uint a, uint b) returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function minus(uint a, uint b) returns (uint) { assert(b <= a); return a - b; } function plus(uint a, uint b) returns (uint) { uint c = a + b; assert(c>=a); return c; } function assert(bool assertion) private { if (!assertion) throw; } } /** * A token that can increase its supply by another contract. * * This allows uncapped crowdsale by dynamically increasing the supply when money pours in. * Only mint agents, contracts whitelisted by owner, can mint new tokens. * */ contract MintableToken is StandardToken, Ownable { using SafeMathLib for uint; bool public mintingFinished = false; /** List of agents that are allowed to create new tokens */ mapping (address => bool) public mintAgents; event MintingAgentChanged(address addr, bool state ); /** * Create new tokens and allocate them to an address.. * * Only callably by a crowdsale contract (mint agent). */ function mint(address receiver, uint amount) onlyMintAgent canMint public { totalSupply = totalSupply.plus(amount); balances[receiver] = balances[receiver].plus(amount); // This will make the mint transaction apper in EtherScan.io // We can remove this after there is a standardized minting event Transfer(0, receiver, amount); } /** * Owner can allow a crowdsale contract to mint new tokens. */ function setMintAgent(address addr, bool state) onlyOwner canMint public { mintAgents[addr] = state; MintingAgentChanged(addr, state); } modifier onlyMintAgent() { // Only crowdsale contracts are allowed to mint new tokens if(!mintAgents[msg.sender]) { throw; } _; } /** Make sure we are not done yet. */ modifier canMint() { if(mintingFinished) throw; _; } } /** * A crowdsaled token. * * An ERC-20 token designed specifically for crowdsales with investor protection and further development path. * * - The token transfer() is disabled until the crowdsale is over * - The token contract gives an opt-in upgrade path to a new contract * - The same token can be part of several crowdsales through approve() mechanism * - The token can be capped (supply set in the constructor) or uncapped (crowdsale contract can mint new tokens) * */ contract CrowdsaleToken is ReleasableToken, MintableToken, UpgradeableToken { /** Name and symbol were updated. */ event UpdatedTokenInformation(string newName, string newSymbol); string public name; string public symbol; uint public decimals; /** * Construct the token. * * This token must be created through a team multisig wallet, so that it is owned by that wallet. * * @param _name Token name * @param _symbol Token symbol - should be all caps * @param _initialSupply How many tokens we start with * @param _decimals Number of decimal places * @param _mintable Are new tokens created over the crowdsale or do we distribute only the initial supply? Note that when the token becomes transferable the minting always ends. */ function CrowdsaleToken(string _name, string _symbol, uint _initialSupply, uint _decimals, bool _mintable) UpgradeableToken(msg.sender) { // Create any address, can be transferred // to team multisig via changeOwner(), // also remember to call setUpgradeMaster() owner = msg.sender; name = _name; symbol = _symbol; totalSupply = _initialSupply; decimals = _decimals; // Create initially all balance on the team multisig balances[owner] = totalSupply; if(totalSupply > 0) { Minted(owner, totalSupply); } // No more new supply allowed after the token creation if(!_mintable) { mintingFinished = true; if(totalSupply == 0) { throw; // Cannot create a token without supply and no minting } } } /** * When token is released to be transferable, enforce no new tokens can be created. */ function releaseTokenTransfer() public onlyReleaseAgent { mintingFinished = true; super.releaseTokenTransfer(); } /** * Allow upgrade agent functionality kick in only if the crowdsale was success. */ function canUpgrade() public constant returns(bool) { return released && super.canUpgrade(); } /** * Owner can update token information here. * * It is often useful to conceal the actual token association, until * the token operations, like central issuance or reissuance have been completed. * * This function allows the token owner to rename the token after the operations * have been completed and then point the audience to use the token contract. */ function setTokenInformation(string _name, string _symbol) onlyOwner { name = _name; symbol = _symbol; UpdatedTokenInformation(name, symbol); } }