Transaction Hash:
Block:
9847358 at Apr-10-2020 11:01:31 PM +UTC
Transaction Fee:
0.0007241900224 ETH
$1.56
Gas Used:
138,944 Gas / 5.2121 Gwei
Emitted Events:
| 187 |
CrowdsaleToken.Transfer( from=[Receiver] CommunityProduct, to=0xF7c5c790Dd34154177C1b7A0D0603f095996C48E, value=2111121733676600486 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x0Cf0Ee63...E122cC023 | |||||
| 0x3F2dA479...B965fe987 | (Swash: Deployer) |
0.873591682302752132 Eth
Nonce: 764
|
0.872867492280352132 Eth
Nonce: 765
| 0.0007241900224 | |
|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 4.22559965138848464 Eth | 4.22632384141088464 Eth | 0.0007241900224 | |
| 0xF24197f7...f7C91FD23 | (Swash: Contract) |
Execution Trace
CommunityProduct.withdrawAllFor( )
withdrawAllFor[Monoplasma (ln:360)]
prove[Monoplasma (ln:361)]sub[Monoplasma (ln:362)]withdrawTo[Monoplasma (ln:363)]
File 1 of 2: CommunityProduct
File 2 of 2: CrowdsaleToken
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring '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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts 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 c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender)
external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value)
external returns (bool);
function transferFrom(address from, address to, uint256 value)
external returns (bool);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
/**
* Abstract contract, requires implementation to specify who can commit blocks and what
* happens when a successful proof is presented
* Verifies Merkle-tree inclusion proofs that show that certain address has
* certain earnings balance, according to hash published ("signed") by a
* sidechain operator or similar authority
*
* ABOUT Merkle-tree inclusion proof: Merkle-tree inclusion proof is an algorithm to prove memebership
* in a set using minimal [ie log(N)] inputs. The hashes of the items are arranged by hash value in a binary Merkle tree where
* each node contains a hash of the hashes of nodes below. The root node (ie "root hash") contains hash information
* about the entire set, and that is the data that BalanceVerifier posts to the blockchain. To prove membership, you walk up the
* tree from the node in question, and use the supplied hashes (the "proof") to fill in the hashes from the adjacent nodes. The proof
* succeeds iff you end up with the known root hash when you get to the top of the tree.
* See https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5
*
* Merkle-tree inclusion proof is a RELATED concept to the blockchain Merkle tree, but a somewhat DIFFERENT application.
* BalanceVerifier posts the root hash of the CURRENT ledger only, and this does NOT depend on the hash of previous ledgers.
* This is different from the blockchain, where each block contains the hash of the previous block.
*
* TODO: see if it could be turned into a library, so many contracts could use it
*/
contract BalanceVerifier {
event BlockCreated(uint blockNumber, bytes32 rootHash, string ipfsHash);
/**
* Sidechain "blocks" are simply root hashes of merkle-trees constructed from its balances
* @param uint root-chain block number after which the balances were recorded
* @return bytes32 root of the balances merkle-tree at that time
*/
mapping (uint => bytes32) public blockHash;
/**
* Handler for proof of sidechain balances
* It is up to the implementing contract to actually distribute out the balances
* @param blockNumber the block whose hash was used for verification
* @param account whose balances were successfully verified
* @param balance the side-chain account balance
*/
function onVerifySuccess(uint blockNumber, address account, uint balance) internal;
/**
* Implementing contract should should do access checks for committing
*/
function onCommit(uint blockNumber, bytes32 rootHash, string ipfsHash) internal;
/**
* Side-chain operator submits commitments to main chain. These
* For convenience, also publish the ipfsHash of the balance book JSON object
* @param blockNumber the block after which the balances were recorded
* @param rootHash root of the balances merkle-tree
* @param ipfsHash where the whole balances object can be retrieved in JSON format
*/
function commit(uint blockNumber, bytes32 rootHash, string ipfsHash) external {
require(blockHash[blockNumber] == 0, "error_overwrite");
string memory _hash = ipfsHash;
onCommit(blockNumber, rootHash, _hash);
blockHash[blockNumber] = rootHash;
emit BlockCreated(blockNumber, rootHash, _hash);
}
/**
* Proving can be used to record the sidechain balances permanently into root chain
* @param blockNumber the block after which the balances were recorded
* @param account whose balances will be verified
* @param balance side-chain account balance
* @param proof list of hashes to prove the totalEarnings
*/
function prove(uint blockNumber, address account, uint balance, bytes32[] memory proof) public {
require(proofIsCorrect(blockNumber, account, balance, proof), "error_proof");
onVerifySuccess(blockNumber, account, balance);
}
/**
* Check the merkle proof of balance in the given side-chain block for given account
*/
function proofIsCorrect(uint blockNumber, address account, uint balance, bytes32[] memory proof) public view returns(bool) {
bytes32 hash = keccak256(abi.encodePacked(account, balance));
bytes32 rootHash = blockHash[blockNumber];
require(rootHash != 0x0, "error_blockNotFound");
return rootHash == calculateRootHash(hash, proof);
}
/**
* Calculate root hash of a Merkle tree, given
* @param hash of the leaf to verify
* @param others list of hashes of "other" branches
*/
function calculateRootHash(bytes32 hash, bytes32[] memory others) public pure returns (bytes32 root) {
root = hash;
for (uint8 i = 0; i < others.length; i++) {
bytes32 other = others[i];
if (other == 0x0) continue; // odd branch, no need to hash
if (root < other) {
root = keccak256(abi.encodePacked(root, other));
} else {
root = keccak256(abi.encodePacked(other, root));
}
}
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
address public pendingOwner;
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, "onlyOwner");
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public {
require(msg.sender == pendingOwner, "onlyPendingOwner");
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
/**
* Monoplasma that is managed by an owner, likely the side-chain operator
* Owner can add and remove recipients.
*/
contract Monoplasma is BalanceVerifier, Ownable {
using SafeMath for uint256;
event OperatorChanged(address indexed newOperator);
event AdminFeeChanged(uint adminFee);
/**
* Freeze period during which all side-chain participants should be able to
* acquire the whole balance book from IPFS (or HTTP server, or elsewhere)
* and validate that the published rootHash is correct
* In case of incorrect rootHash, all members should issue withdrawals from the
* latest block they have validated (that is older than blockFreezeSeconds)
* So: too short freeze period + bad availability => ether (needlessly) spent withdrawing earnings
* long freeze period == lag between purchase and withdrawal => bad UX
* Blocks older than blockFreezeSeconds can be used to withdraw funds
*/
uint public blockFreezeSeconds;
/**
* Block number => timestamp
* Publish time of a block, where the block freeze period starts from.
* Note that block number points to the block after which the root hash is calculated,
* not the block where BlockCreated was emitted (event must come later)
*/
mapping (uint => uint) public blockTimestamp;
address public operator;
//fee fraction = adminFee/10^18
uint public adminFee;
IERC20 public token;
mapping (address => uint) public earnings;
mapping (address => uint) public withdrawn;
uint public totalWithdrawn;
uint public totalProven;
constructor(address tokenAddress, uint blockFreezePeriodSeconds, uint _adminFee) public {
blockFreezeSeconds = blockFreezePeriodSeconds;
token = IERC20(tokenAddress);
operator = msg.sender;
setAdminFee(_adminFee);
}
function setOperator(address newOperator) public onlyOwner {
operator = newOperator;
emit OperatorChanged(newOperator);
}
/**
* Admin fee as a fraction of revenue
* Fixed-point decimal in the same way as ether: 50% === 0.5 ether
* Smart contract doesn't use it, it's here just for storing purposes
*/
function setAdminFee(uint _adminFee) public onlyOwner {
require(adminFee <= 1 ether, "Admin fee cannot be greater than 1");
adminFee = _adminFee;
emit AdminFeeChanged(_adminFee);
}
/**
* Operator creates the side-chain blocks
*/
function onCommit(uint blockNumber, bytes32, string) internal {
require(msg.sender == operator, "error_notPermitted");
blockTimestamp[blockNumber] = now;
}
/**
* Called from BalanceVerifier.prove
* Prove can be called directly to withdraw less than the whole share,
* or just "cement" the earnings so far into root chain even without withdrawing
*/
function onVerifySuccess(uint blockNumber, address account, uint newEarnings) internal {
uint blockFreezeStart = blockTimestamp[blockNumber];
require(now > blockFreezeStart + blockFreezeSeconds, "error_frozen");
require(earnings[account] < newEarnings, "error_oldEarnings");
totalProven = totalProven.add(newEarnings).sub(earnings[account]);
require(totalProven.sub(totalWithdrawn) <= token.balanceOf(this), "error_missingBalance");
earnings[account] = newEarnings;
}
/**
* Prove and withdraw the whole revenue share from sidechain in one transaction
* @param blockNumber of the leaf to verify
* @param totalEarnings in the side-chain
* @param proof list of hashes to prove the totalEarnings
*/
function withdrawAll(uint blockNumber, uint totalEarnings, bytes32[] proof) external {
withdrawAllFor(msg.sender, blockNumber, totalEarnings, proof);
}
/**
* Prove and withdraw the whole revenue share for someone else
* Validator needs to exit those it's watching out for, in case
* it detects Operator malfunctioning
* @param recipient the address we're proving and withdrawing
* @param blockNumber of the leaf to verify
* @param totalEarnings in the side-chain
* @param proof list of hashes to prove the totalEarnings
*/
function withdrawAllFor(address recipient, uint blockNumber, uint totalEarnings, bytes32[] proof) public {
prove(blockNumber, recipient, totalEarnings, proof);
uint withdrawable = totalEarnings.sub(withdrawn[recipient]);
withdrawTo(recipient, recipient, withdrawable);
}
/**
* "Donate withdraw" function that allows you to prove and transfer
* your earnings to a another address in one transaction
* @param recipient the address the tokens will be sent to (instead of msg.sender)
* @param blockNumber of the leaf to verify
* @param totalEarnings in the side-chain
* @param proof list of hashes to prove the totalEarnings
*/
function withdrawAllTo(address recipient, uint blockNumber, uint totalEarnings, bytes32[] proof) external {
prove(blockNumber, msg.sender, totalEarnings, proof);
uint withdrawable = totalEarnings.sub(withdrawn[msg.sender]);
withdrawTo(recipient, msg.sender, withdrawable);
}
/**
* Withdraw a specified amount of your own proven earnings (see `function prove`)
*/
function withdraw(uint amount) public {
withdrawTo(msg.sender, msg.sender, amount);
}
/**
* Do the withdrawal on behalf of someone else
* Validator needs to exit those it's watching out for, in case
* it detects Operator malfunctioning
*/
function withdrawFor(address recipient, uint amount) public {
withdrawTo(recipient, recipient, amount);
}
/**
* Execute token withdrawal into specified recipient address from specified member account
* @dev It is up to the sidechain implementation to make sure
* @dev always token balance >= sum of earnings - sum of withdrawn
*/
function withdrawTo(address recipient, address account, uint amount) public {
require(amount > 0, "error_zeroWithdraw");
uint w = withdrawn[account].add(amount);
require(w <= earnings[account], "error_overdraft");
withdrawn[account] = w;
totalWithdrawn = totalWithdrawn.add(amount);
require(token.transfer(recipient, amount), "error_transfer");
}
}
contract CommunityProduct is Monoplasma {
string public joinPartStream;
constructor(address operator, string joinPartStreamId, address tokenAddress, uint blockFreezePeriodSeconds, uint adminFeeFraction)
Monoplasma(tokenAddress, blockFreezePeriodSeconds, adminFeeFraction) public {
setOperator(operator);
joinPartStream = joinPartStreamId;
}
}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);
}
}