Transaction Hash:
Block:
5013427 at Feb-01-2018 08:45:50 PM +UTC
Transaction Fee:
0.002299766 ETH
$5.32
Gas Used:
74,186 Gas / 31 Gwei
Emitted Events:
| 101 |
BeeToken.Transfer( from=0x77856D50941e1b5b29e291B083740e05E800883A, to=[Sender] 0xd8bef84daf322b79e5c325a46ce1c4d7edf4759b, value=445000000000000000000 )
|
| 102 |
MultiSigWalletWithDailyLimit.Deposit( sender=[Receiver] BeeTokenOffering, value=100000000000000000 )
|
| 103 |
BeeTokenOffering.TokenPurchase( purchaser=[Sender] 0xd8bef84daf322b79e5c325a46ce1c4d7edf4759b, value=100000000000000000, amount=445000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x4D8fc145...80d996FbF | |||||
| 0x62CAf75a...0C7434B7F | (Bee Token: Token Sale) | ||||
| 0x7C4b4BB0...d456726c6 | (Bee Token: MultiSig) | 3,671.443689104883906904 Eth | 3,671.543689104883906904 Eth | 0.1 | |
| 0xD8BEf84d...7edF4759b |
0.982486705 Eth
Nonce: 5
|
0.880186939 Eth
Nonce: 6
| 0.102299766 | ||
|
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 851.787741999953466948 Eth | 851.790041765953466948 Eth | 0.002299766 |
Execution Trace
ETH 0.1
BeeTokenOffering.CALL( )
-
BeeToken.CALL( )
-
BeeToken.transferFrom( from=0x77856D50941e1b5b29e291B083740e05E800883A, to=0xD8BEf84daf322B79e5C325A46cE1C4D7edF4759b, value=445000000000000000000 ) => ( True )
- ETH 0.1
MultiSigWalletWithDailyLimit.CALL( )
[BeeTokenOffering (ln:573)]
buy[BeeTokenOffering (ln:574)]buyTokensTier[BeeTokenOffering (ln:632)]mul[BeeTokenOffering (ln:663)]transferFrom[BeeTokenOffering (ln:665)]owner[BeeTokenOffering (ln:665)]revert[BeeTokenOffering (ln:666)]add[BeeTokenOffering (ln:669)]add[BeeTokenOffering (ln:670)]endOfferingImpl[BeeTokenOffering (ln:673)]OfferingCloses[BeeTokenOffering (ln:687)]
transfer[BeeTokenOffering (ln:677)]TokenPurchase[BeeTokenOffering (ln:678)]
revert[BeeTokenOffering (ln:636)]
File 1 of 3: BeeTokenOffering
File 2 of 3: BeeToken
File 3 of 3: MultiSigWalletWithDailyLimit
pragma solidity ^0.4.18;
/**
* @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;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() 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 transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
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 c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
uint256 public totalSupply;
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);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @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]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
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 balance) {
return balances[_owner];
}
}
/**
* @title Burnable Token
* @dev Token that can be irreversibly burned (destroyed).
*/
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 {
require(_value <= balances[msg.sender]);
// 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
address burner = msg.sender;
balances[burner] = balances[burner].sub(_value);
totalSupply = totalSupply.sub(_value);
Burn(burner, _value);
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
/*
* BeeToken is a standard ERC20 token with some additional functionalities:
* - Transfers are only enabled after contract owner enables it (after the ICO)
* - Contract sets 30% of the total supply as allowance for ICO contract
*
* Note: Token Offering == Initial Coin Offering(ICO)
*/
contract BeeToken is StandardToken, BurnableToken, Ownable {
string public constant symbol = "BEE";
string public constant name = "Bee Token";
uint8 public constant decimals = 18;
uint256 public constant INITIAL_SUPPLY = 500000000 * (10 ** uint256(decimals));
uint256 public constant TOKEN_OFFERING_ALLOWANCE = 150000000 * (10 ** uint256(decimals));
uint256 public constant ADMIN_ALLOWANCE = INITIAL_SUPPLY - TOKEN_OFFERING_ALLOWANCE;
// Address of token admin
address public adminAddr;
// Address of token offering
address public tokenOfferingAddr;
// Enable transfers after conclusion of token offering
bool public transferEnabled = false;
/**
* Check if transfer is allowed
*
* Permissions:
* Owner Admin OffeirngContract Others
* transfer (before transferEnabled is true) x x x x
* transferFrom (before transferEnabled is true) x v v x
* transfer/transferFrom after transferEnabled is true v x x v
*/
modifier onlyWhenTransferAllowed() {
require(transferEnabled || msg.sender == adminAddr || msg.sender == tokenOfferingAddr);
_;
}
/**
* Check if token offering address is set or not
*/
modifier onlyTokenOfferingAddrNotSet() {
require(tokenOfferingAddr == address(0x0));
_;
}
/**
* Check if address is a valid destination to transfer tokens to
* - must not be zero address
* - must not be the token address
* - must not be the owner's address
* - must not be the admin's address
* - must not be the token offering contract address
*/
modifier validDestination(address to) {
require(to != address(0x0));
require(to != address(this));
require(to != owner);
require(to != address(adminAddr));
require(to != address(tokenOfferingAddr));
_;
}
/**
* Token contract constructor
*
* @param admin Address of admin account
*/
function BeeToken(address admin) public {
totalSupply = INITIAL_SUPPLY;
// Mint tokens
balances[msg.sender] = totalSupply;
Transfer(address(0x0), msg.sender, totalSupply);
// Approve allowance for admin account
adminAddr = admin;
approve(adminAddr, ADMIN_ALLOWANCE);
}
/**
* Set token offering to approve allowance for offering contract to distribute tokens
*
* @param offeringAddr Address of token offerng contract
* @param amountForSale Amount of tokens for sale, set 0 to max out
*/
function setTokenOffering(address offeringAddr, uint256 amountForSale) external onlyOwner onlyTokenOfferingAddrNotSet {
require(!transferEnabled);
uint256 amount = (amountForSale == 0) ? TOKEN_OFFERING_ALLOWANCE : amountForSale;
require(amount <= TOKEN_OFFERING_ALLOWANCE);
approve(offeringAddr, amount);
tokenOfferingAddr = offeringAddr;
}
/**
* Enable transfers
*/
function enableTransfer() external onlyOwner {
transferEnabled = true;
// End the offering
approve(tokenOfferingAddr, 0);
}
/**
* Transfer from sender to another account
*
* @param to Destination address
* @param value Amount of beetokens to send
*/
function transfer(address to, uint256 value) public onlyWhenTransferAllowed validDestination(to) returns (bool) {
return super.transfer(to, value);
}
/**
* Transfer from `from` account to `to` account using allowance in `from` account to the sender
*
* @param from Origin address
* @param to Destination address
* @param value Amount of beetokens to send
*/
function transferFrom(address from, address to, uint256 value) public onlyWhenTransferAllowed validDestination(to) returns (bool) {
return super.transferFrom(from, to, value);
}
/**
* Burn token, only owner is allowed to do this
*
* @param value Amount of tokens to burn
*/
function burn(uint256 value) public {
require(transferEnabled || msg.sender == owner);
super.burn(value);
}
}
contract BeeTokenOffering is Pausable {
using SafeMath for uint256;
// Start and end timestamps where contributions are allowed (both inclusive)
uint256 public startTime;
uint256 public endTime;
// Address where funds are collected
address public beneficiary;
// Token to be sold
BeeToken public token;
// Price of the tokens as in tokens per ether
uint256 public rate;
// Amount of raised in Wei (1 ether)
uint256 public weiRaised;
// Timelines for different contribution limit policy
uint256 public capDoublingTimestamp;
uint256 public capReleaseTimestamp;
// Individual contribution limits in Wei per tier
uint256[3] public tierCaps;
// Whitelists of participant address for each tier
mapping(uint8 => mapping(address => bool)) public whitelists;
// Contributions in Wei for each participant
mapping(address => uint256) public contributions;
// Funding cap in ETH. Change to equal $5M at time of token offering
uint256 public constant FUNDING_ETH_HARD_CAP = 5000 * 1 ether;
// Min contribution is 0.1 ether
uint256 public constant MINIMUM_CONTRIBUTION = 10**17;
// The current stage of the offering
Stages public stage;
enum Stages {
Setup,
OfferingStarted,
OfferingEnded
}
event OfferingOpens(uint256 startTime, uint256 endTime);
event OfferingCloses(uint256 endTime, uint256 totalWeiRaised);
/**
* Event for token purchase logging
*
* @param purchaser Who paid for the tokens
* @param value Weis paid for purchase
* @return amount Amount of tokens purchased
*/
event TokenPurchase(address indexed purchaser, uint256 value, uint256 amount);
/**
* Modifier that requires certain stage before executing the main function body
*
* @param expectedStage Value that the current stage is required to match
*/
modifier atStage(Stages expectedStage) {
require(stage == expectedStage);
_;
}
/**
* Modifier that validates a purchase at a tier
* All the following has to be met:
* - current time within the offering period
* - valid sender address and ether value greater than 0.1
* - total Wei raised not greater than FUNDING_ETH_HARD_CAP
* - contribution per perticipant within contribution limit
*
* @param tier Index of the tier
*/
modifier validPurchase(uint8 tier) {
require(tier < tierCaps.length);
require(now >= startTime && now <= endTime && stage == Stages.OfferingStarted);
uint256 contributionInWei = msg.value;
address participant = msg.sender;
require(participant != address(0) && contributionInWei >= MINIMUM_CONTRIBUTION);
require(weiRaised.add(contributionInWei) <= FUNDING_ETH_HARD_CAP);
uint256 initialCapInWei = tierCaps[tier];
if (now < capDoublingTimestamp) {
require(contributions[participant].add(contributionInWei) <= initialCapInWei);
} else if (now < capReleaseTimestamp) {
require(contributions[participant].add(contributionInWei) <= initialCapInWei.mul(2));
}
_;
}
/**
* The constructor of the contract.
* Note: tierCaps[tier] define the individual contribution limits in Wei of each address
* per tier within the first tranche of the sale (sale start ~ capDoublingTimestamp)
* these limits are doubled between capDoublingTimestamp ~ capReleaseTimestamp
* and are lifted completely between capReleaseTimestamp ~ end time
*
* @param beeToEtherRate Number of beetokens per ether
* @param beneficiaryAddr Address where funds are collected
* @param baseContributionCapInWei Base contribution limit in Wei per address
*/
function BeeTokenOffering(
uint256 beeToEtherRate,
address beneficiaryAddr,
uint256 baseContributionCapInWei,
address tokenAddress
) public {
require(beeToEtherRate > 0);
require(beneficiaryAddr != address(0));
require(tokenAddress != address(0));
require(baseContributionCapInWei >= MINIMUM_CONTRIBUTION);
token = BeeToken(tokenAddress);
rate = beeToEtherRate;
beneficiary = beneficiaryAddr;
stage = Stages.Setup;
// Contribution cap per tier in Wei
tierCaps[0] = baseContributionCapInWei.mul(3);
tierCaps[1] = baseContributionCapInWei.mul(2);
tierCaps[2] = baseContributionCapInWei;
}
/**
* Fallback function can be used to buy tokens
*/
function () public payable {
buy();
}
/**
* Withdraw available ethers into beneficiary account, serves as a safety, should never be needed
*/
function ownerSafeWithdrawal() external onlyOwner {
beneficiary.transfer(this.balance);
}
function updateRate(uint256 beeToEtherRate) public onlyOwner atStage(Stages.Setup) {
rate = beeToEtherRate;
}
/**
* Whitelist participant address per tier
*
* @param tiers Array of indices of tier, each value should be less than tierCaps.length
* @param users Array of addresses to be whitelisted
*/
function whitelist(uint8[] tiers, address[] users) public onlyOwner {
require(tiers.length == users.length);
for (uint32 i = 0; i < users.length; i++) {
require(tiers[i] < tierCaps.length);
whitelists[tiers[i]][users[i]] = true;
}
}
/**
* Start the offering
*
* @param durationInSeconds Extra duration of the offering on top of the minimum 48 hours
*/
function startOffering(uint256 durationInSeconds) public onlyOwner atStage(Stages.Setup) {
stage = Stages.OfferingStarted;
startTime = now;
capDoublingTimestamp = startTime + 24 hours;
capReleaseTimestamp = startTime + 48 hours;
endTime = capReleaseTimestamp.add(durationInSeconds);
OfferingOpens(startTime, endTime);
}
/**
* End the offering
*/
function endOffering() public onlyOwner atStage(Stages.OfferingStarted) {
endOfferingImpl();
}
/**
* Function to invest ether to buy tokens, can be called directly or called by the fallback function
* Only whitelisted users can buy tokens.
*
* @return bool Return true if purchase succeeds, false otherwise
*/
function buy() public payable whenNotPaused atStage(Stages.OfferingStarted) returns (bool) {
for (uint8 i = 0; i < tierCaps.length; ++i) {
if (whitelists[i][msg.sender]) {
buyTokensTier(i);
return true;
}
}
revert();
}
/**
* Function that returns whether offering has ended
*
* @return bool Return true if token offering has ended
*/
function hasEnded() public view returns (bool) {
return now > endTime || stage == Stages.OfferingEnded;
}
/**
* Internal function that buys token per tier
*
* Investiment limit changes over time:
* 1) [offering starts ~ capDoublingTimestamp]: 1x of contribution limit per tier (1 * tierCaps[tier])
* 2) [capDoublingTimestamp ~ capReleaseTimestamp]: limit per participant is raised to 2x of contribution limit per tier (2 * tierCaps[tier])
* 3) [capReleaseTimestamp ~ offering ends]: no limit per participant as along as total Wei raised is within FUNDING_ETH_HARD_CAP
*
* @param tier Index of tier of whitelisted participant
*/
function buyTokensTier(uint8 tier) internal validPurchase(tier) {
address participant = msg.sender;
uint256 contributionInWei = msg.value;
// Calculate token amount to be distributed
uint256 tokens = contributionInWei.mul(rate);
if (!token.transferFrom(token.owner(), participant, tokens)) {
revert();
}
weiRaised = weiRaised.add(contributionInWei);
contributions[participant] = contributions[participant].add(contributionInWei);
// Check if the funding cap has been reached, end the offering if so
if (weiRaised >= FUNDING_ETH_HARD_CAP) {
endOfferingImpl();
}
// Transfer funds to beneficiary
beneficiary.transfer(contributionInWei);
TokenPurchase(msg.sender, contributionInWei, tokens);
}
/**
* End token offering by set the stage and endTime
*/
function endOfferingImpl() internal {
endTime = now;
stage = Stages.OfferingEnded;
OfferingCloses(endTime, weiRaised);
}
/**
* Allocate tokens for presale participants before public offering, can only be executed at Stages.Setup stage.
*
* @param to Participant address to send beetokens to
* @param tokens Amount of beetokens to be sent to parcitipant
*/
function allocateTokensBeforeOffering(address to, uint256 tokens)
public
onlyOwner
atStage(Stages.Setup)
returns (bool)
{
if (!token.transferFrom(token.owner(), to, tokens)) {
revert();
}
return true;
}
/**
* Bulk version of allocateTokensBeforeOffering
*/
function batchAllocateTokensBeforeOffering(address[] toList, uint256[] tokensList)
external
onlyOwner
atStage(Stages.Setup)
returns (bool)
{
require(toList.length == tokensList.length);
for (uint32 i = 0; i < toList.length; i++) {
allocateTokensBeforeOffering(toList[i], tokensList[i]);
}
return true;
}
}File 2 of 3: BeeToken
pragma solidity ^0.4.18;
/**
* @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;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() 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 transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
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 c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
uint256 public totalSupply;
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);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @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]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
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 balance) {
return balances[_owner];
}
}
/**
* @title Burnable Token
* @dev Token that can be irreversibly burned (destroyed).
*/
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 {
require(_value <= balances[msg.sender]);
// 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
address burner = msg.sender;
balances[burner] = balances[burner].sub(_value);
totalSupply = totalSupply.sub(_value);
Burn(burner, _value);
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
/*
* BeeToken is a standard ERC20 token with some additional functionalities:
* - Transfers are only enabled after contract owner enables it (after the ICO)
* - Contract sets 30% of the total supply as allowance for ICO contract
*
* Note: Token Offering == Initial Coin Offering(ICO)
*/
contract BeeToken is StandardToken, BurnableToken, Ownable {
string public constant symbol = "BEE";
string public constant name = "Bee Token";
uint8 public constant decimals = 18;
uint256 public constant INITIAL_SUPPLY = 500000000 * (10 ** uint256(decimals));
uint256 public constant TOKEN_OFFERING_ALLOWANCE = 150000000 * (10 ** uint256(decimals));
uint256 public constant ADMIN_ALLOWANCE = INITIAL_SUPPLY - TOKEN_OFFERING_ALLOWANCE;
// Address of token admin
address public adminAddr;
// Address of token offering
address public tokenOfferingAddr;
// Enable transfers after conclusion of token offering
bool public transferEnabled = false;
/**
* Check if transfer is allowed
*
* Permissions:
* Owner Admin OffeirngContract Others
* transfer (before transferEnabled is true) x x x x
* transferFrom (before transferEnabled is true) x v v x
* transfer/transferFrom after transferEnabled is true v x x v
*/
modifier onlyWhenTransferAllowed() {
require(transferEnabled || msg.sender == adminAddr || msg.sender == tokenOfferingAddr);
_;
}
/**
* Check if token offering address is set or not
*/
modifier onlyTokenOfferingAddrNotSet() {
require(tokenOfferingAddr == address(0x0));
_;
}
/**
* Check if address is a valid destination to transfer tokens to
* - must not be zero address
* - must not be the token address
* - must not be the owner's address
* - must not be the admin's address
* - must not be the token offering contract address
*/
modifier validDestination(address to) {
require(to != address(0x0));
require(to != address(this));
require(to != owner);
require(to != address(adminAddr));
require(to != address(tokenOfferingAddr));
_;
}
/**
* Token contract constructor
*
* @param admin Address of admin account
*/
function BeeToken(address admin) public {
totalSupply = INITIAL_SUPPLY;
// Mint tokens
balances[msg.sender] = totalSupply;
Transfer(address(0x0), msg.sender, totalSupply);
// Approve allowance for admin account
adminAddr = admin;
approve(adminAddr, ADMIN_ALLOWANCE);
}
/**
* Set token offering to approve allowance for offering contract to distribute tokens
*
* @param offeringAddr Address of token offerng contract
* @param amountForSale Amount of tokens for sale, set 0 to max out
*/
function setTokenOffering(address offeringAddr, uint256 amountForSale) external onlyOwner onlyTokenOfferingAddrNotSet {
require(!transferEnabled);
uint256 amount = (amountForSale == 0) ? TOKEN_OFFERING_ALLOWANCE : amountForSale;
require(amount <= TOKEN_OFFERING_ALLOWANCE);
approve(offeringAddr, amount);
tokenOfferingAddr = offeringAddr;
}
/**
* Enable transfers
*/
function enableTransfer() external onlyOwner {
transferEnabled = true;
// End the offering
approve(tokenOfferingAddr, 0);
}
/**
* Transfer from sender to another account
*
* @param to Destination address
* @param value Amount of beetokens to send
*/
function transfer(address to, uint256 value) public onlyWhenTransferAllowed validDestination(to) returns (bool) {
return super.transfer(to, value);
}
/**
* Transfer from `from` account to `to` account using allowance in `from` account to the sender
*
* @param from Origin address
* @param to Destination address
* @param value Amount of beetokens to send
*/
function transferFrom(address from, address to, uint256 value) public onlyWhenTransferAllowed validDestination(to) returns (bool) {
return super.transferFrom(from, to, value);
}
/**
* Burn token, only owner is allowed to do this
*
* @param value Amount of tokens to burn
*/
function burn(uint256 value) public {
require(transferEnabled || msg.sender == owner);
super.burn(value);
}
}
contract BeeTokenOffering is Pausable {
using SafeMath for uint256;
// Start and end timestamps where contributions are allowed (both inclusive)
uint256 public startTime;
uint256 public endTime;
// Address where funds are collected
address public beneficiary;
// Token to be sold
BeeToken public token;
// Price of the tokens as in tokens per ether
uint256 public rate;
// Amount of raised in Wei (1 ether)
uint256 public weiRaised;
// Timelines for different contribution limit policy
uint256 public capDoublingTimestamp;
uint256 public capReleaseTimestamp;
// Individual contribution limits in Wei per tier
uint256[3] public tierCaps;
// Whitelists of participant address for each tier
mapping(uint8 => mapping(address => bool)) public whitelists;
// Contributions in Wei for each participant
mapping(address => uint256) public contributions;
// Funding cap in ETH. Change to equal $5M at time of token offering
uint256 public constant FUNDING_ETH_HARD_CAP = 5000 * 1 ether;
// The current stage of the offering
Stages public stage;
enum Stages {
Setup,
OfferingStarted,
OfferingEnded
}
event OfferingOpens(uint256 startTime, uint256 endTime);
event OfferingCloses(uint256 endTime, uint256 totalWeiRaised);
/**
* Event for token purchase logging
*
* @param purchaser Who paid for the tokens
* @param value Weis paid for purchase
* @return amount Amount of tokens purchased
*/
event TokenPurchase(address indexed purchaser, uint256 value, uint256 amount);
/**
* Modifier that requires certain stage before executing the main function body
*
* @param expectedStage Value that the current stage is required to match
*/
modifier atStage(Stages expectedStage) {
require(stage == expectedStage);
_;
}
/**
* Modifier that validates a purchase at a tier
* All the following has to be met:
* - current time within the offering period
* - valid sender address and ether value greater than 0.1
* - total Wei raised not greater than FUNDING_ETH_HARD_CAP
* - contribution per perticipant within contribution limit
*
* @param tier Index of the tier
*/
modifier validPurchase(uint8 tier) {
require(tier < tierCaps.length);
require(now >= startTime && now <= endTime && stage == Stages.OfferingStarted);
uint256 contributionInWei = msg.value;
address participant = msg.sender;
require(participant != address(0) && contributionInWei > 100000000000000000);
require(weiRaised.add(contributionInWei) <= FUNDING_ETH_HARD_CAP);
uint256 initialCapInWei = tierCaps[tier];
if (now < capDoublingTimestamp) {
require(contributions[participant].add(contributionInWei) <= initialCapInWei);
} else if (now < capReleaseTimestamp) {
require(contributions[participant].add(contributionInWei) <= initialCapInWei.mul(2));
}
_;
}
/**
* The constructor of the contract.
* Note: tierCaps[tier] define the individual contribution limits in Wei of each address
* per tier within the first tranche of the sale (sale start ~ capDoublingTimestamp)
* these limits are doubled between capDoublingTimestamp ~ capReleaseTimestamp
* and are lifted completely between capReleaseTimestamp ~ end time
*
* @param beeToEtherRate Number of beetokens per ether
* @param beneficiaryAddr Address where funds are collected
* @param baseContributionCapInWei Base contribution limit in Wei per address
*/
function BeeTokenOffering(
uint256 beeToEtherRate,
address beneficiaryAddr,
uint256 baseContributionCapInWei,
address tokenAddress
) public {
require(beeToEtherRate > 0);
require(beneficiaryAddr != address(0));
require(tokenAddress != address(0));
token = BeeToken(tokenAddress);
rate = beeToEtherRate;
beneficiary = beneficiaryAddr;
stage = Stages.Setup;
// Contribution cap per tier in Wei
tierCaps[0] = baseContributionCapInWei.mul(3);
tierCaps[1] = baseContributionCapInWei.mul(2);
tierCaps[2] = baseContributionCapInWei;
}
/**
* Fallback function can be used to buy tokens
*/
function () public payable {
buy();
}
/**
* Withdraw available ethers into beneficiary account, serves as a safety, should never be needed
*/
function ownerSafeWithdrawal() external onlyOwner {
beneficiary.transfer(this.balance);
}
function updateRate(uint256 beeToEtherRate) public onlyOwner atStage(Stages.Setup) {
rate = beeToEtherRate;
}
/**
* Whitelist participant address per tier
*
* @param tiers Array of indices of tier, each value should be less than tierCaps.length
* @param users Array of addresses to be whitelisted
*/
function whitelist(uint8[] tiers, address[] users) public onlyOwner {
require(tiers.length == users.length);
for (uint32 i = 0; i < users.length; i++) {
require(tiers[i] < tierCaps.length);
whitelists[tiers[i]][users[i]] = true;
}
}
/**
* Start the offering
*
* @param durationInSeconds Extra duration of the offering on top of the minimum 48 hours
*/
function startOffering(uint256 durationInSeconds) public onlyOwner atStage(Stages.Setup) {
stage = Stages.OfferingStarted;
startTime = now;
capDoublingTimestamp = startTime + 24 hours;
capReleaseTimestamp = startTime + 48 hours;
endTime = capReleaseTimestamp.add(durationInSeconds);
OfferingOpens(startTime, endTime);
}
/**
* End the offering
*/
function endOffering() public onlyOwner atStage(Stages.OfferingStarted) {
endOfferingImpl();
}
/**
* Function to invest ether to buy tokens, can be called directly or called by the fallback function
* Only whitelisted users can buy tokens.
*
* @return bool Return true if purchase succeeds, false otherwise
*/
function buy() public payable whenNotPaused atStage(Stages.OfferingStarted) returns (bool) {
for (uint8 i = 0; i < tierCaps.length; ++i) {
if (whitelists[i][msg.sender]) {
buyTokensTier(i);
return true;
}
}
revert();
}
/**
* Function that returns whether offering has ended
*
* @return bool Return true if token offering has ended
*/
function hasEnded() public view returns (bool) {
return now > endTime || stage == Stages.OfferingEnded;
}
/**
* Internal function that buys token per tier
*
* Investiment limit changes over time:
* 1) [offering starts ~ capDoublingTimestamp]: 1x of contribution limit per tier (1 * tierCaps[tier])
* 2) [capDoublingTimestamp ~ capReleaseTimestamp]: limit per participant is raised to 2x of contribution limit per tier (2 * tierCaps[tier])
* 3) [capReleaseTimestamp ~ offering ends]: no limit per participant as along as total Wei raised is within FUNDING_ETH_HARD_CAP
*
* @param tier Index of tier of whitelisted participant
*/
function buyTokensTier(uint8 tier) internal validPurchase(tier) {
address participant = msg.sender;
uint256 contributionInWei = msg.value;
// Calculate token amount to be distributed
uint256 tokens = contributionInWei.mul(rate);
if (!token.transferFrom(token.owner(), participant, tokens)) {
revert();
}
weiRaised = weiRaised.add(contributionInWei);
contributions[participant] = contributions[participant].add(contributionInWei);
// Check if the funding cap has been reached, end the offering if so
if (weiRaised >= FUNDING_ETH_HARD_CAP) {
endOfferingImpl();
}
// Transfer funds to beneficiary
beneficiary.transfer(contributionInWei);
TokenPurchase(msg.sender, contributionInWei, tokens);
}
/**
* End token offering by set the stage and endTime
*/
function endOfferingImpl() internal {
endTime = now;
stage = Stages.OfferingEnded;
OfferingCloses(endTime, weiRaised);
}
/**
* Allocate tokens for presale participants before public offering, can only be executed at Stages.Setup stage.
*
* @param to Participant address to send beetokens to
* @param tokens Amount of beetokens to be sent to parcitipant
*/
function allocateTokensBeforeOffering(address to, uint256 tokens)
public
onlyOwner
atStage(Stages.Setup)
returns (bool)
{
if (!token.transferFrom(token.owner(), to, tokens)) {
revert();
}
return true;
}
/**
* Bulk version of allocateTokensBeforeOffering
*/
function batchAllocateTokensBeforeOffering(address[] toList, uint256[] tokensList)
external
onlyOwner
atStage(Stages.Setup)
returns (bool)
{
require(toList.length == tokensList.length);
for (uint32 i = 0; i < toList.length; i++) {
allocateTokensBeforeOffering(toList[i], tokensList[i]);
}
return true;
}
}File 3 of 3: MultiSigWalletWithDailyLimit
contract Factory {
/*
* Events
*/
event ContractInstantiation(address sender, address instantiation);
/*
* Storage
*/
mapping(address => bool) public isInstantiation;
mapping(address => address[]) public instantiations;
/*
* Public functions
*/
/// @dev Returns number of instantiations by creator.
/// @param creator Contract creator.
/// @return Returns number of instantiations by creator.
function getInstantiationCount(address creator)
public
constant
returns (uint)
{
return instantiations[creator].length;
}
/*
* Internal functions
*/
/// @dev Registers contract in factory registry.
/// @param instantiation Address of contract instantiation.
function register(address instantiation)
internal
{
isInstantiation[instantiation] = true;
instantiations[msg.sender].push(instantiation);
ContractInstantiation(msg.sender, instantiation);
}
}
/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWallet {
/*
* Events
*/
event Confirmation(address indexed sender, uint indexed transactionId);
event Revocation(address indexed sender, uint indexed transactionId);
event Submission(uint indexed transactionId);
event Execution(uint indexed transactionId);
event ExecutionFailure(uint indexed transactionId);
event Deposit(address indexed sender, uint value);
event OwnerAddition(address indexed owner);
event OwnerRemoval(address indexed owner);
event RequirementChange(uint required);
/*
* Constants
*/
uint constant public MAX_OWNER_COUNT = 50;
/*
* Storage
*/
mapping (uint => Transaction) public transactions;
mapping (uint => mapping (address => bool)) public confirmations;
mapping (address => bool) public isOwner;
address[] public owners;
uint public required;
uint public transactionCount;
struct Transaction {
address destination;
uint value;
bytes data;
bool executed;
}
/*
* Modifiers
*/
modifier onlyWallet() {
require(msg.sender == address(this));
_;
}
modifier ownerDoesNotExist(address owner) {
require(!isOwner[owner]);
_;
}
modifier ownerExists(address owner) {
require(isOwner[owner]);
_;
}
modifier transactionExists(uint transactionId) {
require(transactions[transactionId].destination != 0);
_;
}
modifier confirmed(uint transactionId, address owner) {
require(confirmations[transactionId][owner]);
_;
}
modifier notConfirmed(uint transactionId, address owner) {
require(!confirmations[transactionId][owner]);
_;
}
modifier notExecuted(uint transactionId) {
require(!transactions[transactionId].executed);
_;
}
modifier notNull(address _address) {
require(_address != 0);
_;
}
modifier validRequirement(uint ownerCount, uint _required) {
require(ownerCount <= MAX_OWNER_COUNT
&& _required <= ownerCount
&& _required != 0
&& ownerCount != 0);
_;
}
/// @dev Fallback function allows to deposit ether.
function()
payable
{
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners and required number of confirmations.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
function MultiSigWallet(address[] _owners, uint _required)
public
validRequirement(_owners.length, _required)
{
for (uint i=0; i<_owners.length; i++) {
require(!isOwner[_owners[i]] && _owners[i] != 0);
isOwner[_owners[i]] = true;
}
owners = _owners;
required = _required;
}
/// @dev Allows to add a new owner. Transaction has to be sent by wallet.
/// @param owner Address of new owner.
function addOwner(address owner)
public
onlyWallet
ownerDoesNotExist(owner)
notNull(owner)
validRequirement(owners.length + 1, required)
{
isOwner[owner] = true;
owners.push(owner);
OwnerAddition(owner);
}
/// @dev Allows to remove an owner. Transaction has to be sent by wallet.
/// @param owner Address of owner.
function removeOwner(address owner)
public
onlyWallet
ownerExists(owner)
{
isOwner[owner] = false;
for (uint i=0; i<owners.length - 1; i++)
if (owners[i] == owner) {
owners[i] = owners[owners.length - 1];
break;
}
owners.length -= 1;
if (required > owners.length)
changeRequirement(owners.length);
OwnerRemoval(owner);
}
/// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
/// @param owner Address of owner to be replaced.
/// @param newOwner Address of new owner.
function replaceOwner(address owner, address newOwner)
public
onlyWallet
ownerExists(owner)
ownerDoesNotExist(newOwner)
{
for (uint i=0; i<owners.length; i++)
if (owners[i] == owner) {
owners[i] = newOwner;
break;
}
isOwner[owner] = false;
isOwner[newOwner] = true;
OwnerRemoval(owner);
OwnerAddition(newOwner);
}
/// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
/// @param _required Number of required confirmations.
function changeRequirement(uint _required)
public
onlyWallet
validRequirement(owners.length, _required)
{
required = _required;
RequirementChange(_required);
}
/// @dev Allows an owner to submit and confirm a transaction.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function submitTransaction(address destination, uint value, bytes data)
public
returns (uint transactionId)
{
transactionId = addTransaction(destination, value, data);
confirmTransaction(transactionId);
}
/// @dev Allows an owner to confirm a transaction.
/// @param transactionId Transaction ID.
function confirmTransaction(uint transactionId)
public
ownerExists(msg.sender)
transactionExists(transactionId)
notConfirmed(transactionId, msg.sender)
{
confirmations[transactionId][msg.sender] = true;
Confirmation(msg.sender, transactionId);
executeTransaction(transactionId);
}
/// @dev Allows an owner to revoke a confirmation for a transaction.
/// @param transactionId Transaction ID.
function revokeConfirmation(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
confirmations[transactionId][msg.sender] = false;
Revocation(msg.sender, transactionId);
}
/// @dev Allows anyone to execute a confirmed transaction.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
if (isConfirmed(transactionId)) {
Transaction storage txn = transactions[transactionId];
txn.executed = true;
if (txn.destination.call.value(txn.value)(txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
}
}
}
/// @dev Returns the confirmation status of a transaction.
/// @param transactionId Transaction ID.
/// @return Confirmation status.
function isConfirmed(uint transactionId)
public
constant
returns (bool)
{
uint count = 0;
for (uint i=0; i<owners.length; i++) {
if (confirmations[transactionId][owners[i]])
count += 1;
if (count == required)
return true;
}
}
/*
* Internal functions
*/
/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function addTransaction(address destination, uint value, bytes data)
internal
notNull(destination)
returns (uint transactionId)
{
transactionId = transactionCount;
transactions[transactionId] = Transaction({
destination: destination,
value: value,
data: data,
executed: false
});
transactionCount += 1;
Submission(transactionId);
}
/*
* Web3 call functions
*/
/// @dev Returns number of confirmations of a transaction.
/// @param transactionId Transaction ID.
/// @return Number of confirmations.
function getConfirmationCount(uint transactionId)
public
constant
returns (uint count)
{
for (uint i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]])
count += 1;
}
/// @dev Returns total number of transactions after filers are applied.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Total number of transactions after filters are applied.
function getTransactionCount(bool pending, bool executed)
public
constant
returns (uint count)
{
for (uint i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
count += 1;
}
/// @dev Returns list of owners.
/// @return List of owner addresses.
function getOwners()
public
constant
returns (address[])
{
return owners;
}
/// @dev Returns array with owner addresses, which confirmed transaction.
/// @param transactionId Transaction ID.
/// @return Returns array of owner addresses.
function getConfirmations(uint transactionId)
public
constant
returns (address[] _confirmations)
{
address[] memory confirmationsTemp = new address[](owners.length);
uint count = 0;
uint i;
for (i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]]) {
confirmationsTemp[count] = owners[i];
count += 1;
}
_confirmations = new address[](count);
for (i=0; i<count; i++)
_confirmations[i] = confirmationsTemp[i];
}
/// @dev Returns list of transaction IDs in defined range.
/// @param from Index start position of transaction array.
/// @param to Index end position of transaction array.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Returns array of transaction IDs.
function getTransactionIds(uint from, uint to, bool pending, bool executed)
public
constant
returns (uint[] _transactionIds)
{
uint[] memory transactionIdsTemp = new uint[](transactionCount);
uint count = 0;
uint i;
for (i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
{
transactionIdsTemp[count] = i;
count += 1;
}
_transactionIds = new uint[](to - from);
for (i=from; i<to; i++)
_transactionIds[i - from] = transactionIdsTemp[i];
}
}
/// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWalletWithDailyLimit is MultiSigWallet {
/*
* Events
*/
event DailyLimitChange(uint dailyLimit);
/*
* Storage
*/
uint public dailyLimit;
uint public lastDay;
uint public spentToday;
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
/// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis.
function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit)
public
MultiSigWallet(_owners, _required)
{
dailyLimit = _dailyLimit;
}
/// @dev Allows to change the daily limit. Transaction has to be sent by wallet.
/// @param _dailyLimit Amount in wei.
function changeDailyLimit(uint _dailyLimit)
public
onlyWallet
{
dailyLimit = _dailyLimit;
DailyLimitChange(_dailyLimit);
}
/// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
Transaction storage txn = transactions[transactionId];
bool _confirmed = isConfirmed(transactionId);
if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) {
txn.executed = true;
if (!_confirmed)
spentToday += txn.value;
if (txn.destination.call.value(txn.value)(txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
if (!_confirmed)
spentToday -= txn.value;
}
}
}
/*
* Internal functions
*/
/// @dev Returns if amount is within daily limit and resets spentToday after one day.
/// @param amount Amount to withdraw.
/// @return Returns if amount is under daily limit.
function isUnderLimit(uint amount)
internal
returns (bool)
{
if (now > lastDay + 24 hours) {
lastDay = now;
spentToday = 0;
}
if (spentToday + amount > dailyLimit || spentToday + amount < spentToday)
return false;
return true;
}
/*
* Web3 call functions
*/
/// @dev Returns maximum withdraw amount.
/// @return Returns amount.
function calcMaxWithdraw()
public
constant
returns (uint)
{
if (now > lastDay + 24 hours)
return dailyLimit;
if (dailyLimit < spentToday)
return 0;
return dailyLimit - spentToday;
}
}