Transaction Hash:
Block:
5313770 at Mar-24-2018 03:00:17 PM +UTC
Transaction Fee:
0.000341642 ETH
$0.86
Gas Used:
341,642 Gas / 1 Gwei
Emitted Events:
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x1AF5C17a...FdEA56461 | |||||
0x4FdDeD11...62D5a3a82 |
0.004574829 Eth
Nonce: 29
|
0.004233187 Eth
Nonce: 30
| 0.000341642 | ||
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 3,002.808810820641580108 Eth | 3,002.809152462641580108 Eth | 0.000341642 |
Execution Trace
HexelErc20Token.multiMint( recipients=[0x88E2eFac3D2ef957FcD82Ec201a506871AD06204, 0xCf40D0d2b44F2b66e07cAcE1372Ca42b73Cf21a3, 0x1d805bC00b8fa3c96aE6C8FA97B2FD24B19a9801, 0x4319C142f7b6cD722Fc3a49289b8a22A7a51cA1e, 0x7Bb0b08587b8a6B8945e09F1Baca426558B0f06a, 0xa353F8be5A6c8BEe2118772Ac14186bdD734A7e0, 0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98, 0x29d38FdF26d64Fa799276e6615759D27dB1F1fcD, 0xEdf28066F227DF32B584fcC892e2c69b0bDa7702, 0x3257Bde8CF067aE6f1DDc0E4b140fe02e3C5e44f], values=[1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000, 1000000000000000000] )
multiMint[HexelErc20Token (ln:314)]
File 1 of 4: HexelErc20Token
File 2 of 4: GNTAllocation
File 3 of 4: DSEasyMultisig
File 4 of 4: MultiSigWalletWithDailyLimit
pragma solidity ^0.4.19; // Made with Hexel at www.onhexel.com /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ 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; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } /** * @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 ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/179 */ contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } /** * @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; uint256 totalSupply_; /** * @dev total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public returns (bool) { require(_to != address(0)); require(_value <= balances[msg.sender]); // 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 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; } } /** * @title Mintable token * @dev Simple ERC20 Token example, with mintable token creation * @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120 * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol */ contract MintableToken is StandardToken, Ownable { event Mint(address indexed to, uint256 amount); event MintFinished(); bool public mintingFinished = false; modifier canMint() { require(!mintingFinished); _; } /** * @dev Function to mint tokens * @param _to The address that will receive the minted tokens. * @param _amount The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) { totalSupply_ = totalSupply_.add(_amount); balances[_to] = balances[_to].add(_amount); Mint(_to, _amount); Transfer(address(0), _to, _amount); return true; } /** * @dev Function to stop minting new tokens. * @return True if the operation was successful. */ function finishMinting() onlyOwner canMint public returns (bool) { mintingFinished = true; MintFinished(); return true; } } contract HexelErc20Token is MintableToken { /* * Token meta data */ string public name; string public symbol; uint8 constant public decimals = 18; function HexelErc20Token(string _name, string _symbol, uint256 _initialSupply) public { name = _name; symbol = _symbol; if (_initialSupply > 0) { mint(msg.sender, _initialSupply); } } function multiMint(address[] recipients, uint256[] values) onlyOwner canMint external { require(recipients.length == values.length); for (uint256 i = 0; i < recipients.length; i++) { mint(recipients[i], values[i]); } } }
File 2 of 4: GNTAllocation
pragma solidity ^0.4.4; /// @title Migration Agent interface contract MigrationAgent { function migrateFrom(address _from, uint256 _value); } /// @title Golem Network Token (GNT) - crowdfunding code for Golem Project contract GolemNetworkToken { string public constant name = "Golem Network Token"; string public constant symbol = "GNT"; uint8 public constant decimals = 18; // 18 decimal places, the same as ETH. uint256 public constant tokenCreationRate = 1000; // The funding cap in weis. uint256 public constant tokenCreationCap = 820000 ether * tokenCreationRate; uint256 public constant tokenCreationMin = 150000 ether * tokenCreationRate; uint256 public fundingStartBlock; uint256 public fundingEndBlock; // The flag indicates if the GNT contract is in Funding state. bool public funding = true; // Receives ETH and its own GNT endowment. address public golemFactory; // Has control over token migration to next version of token. address public migrationMaster; GNTAllocation lockedAllocation; // The current total token supply. uint256 totalTokens; mapping (address => uint256) balances; address public migrationAgent; uint256 public totalMigrated; event Transfer(address indexed _from, address indexed _to, uint256 _value); event Migrate(address indexed _from, address indexed _to, uint256 _value); event Refund(address indexed _from, uint256 _value); function GolemNetworkToken(address _golemFactory, address _migrationMaster, uint256 _fundingStartBlock, uint256 _fundingEndBlock) { if (_golemFactory == 0) throw; if (_migrationMaster == 0) throw; if (_fundingStartBlock <= block.number) throw; if (_fundingEndBlock <= _fundingStartBlock) throw; lockedAllocation = new GNTAllocation(_golemFactory); migrationMaster = _migrationMaster; golemFactory = _golemFactory; fundingStartBlock = _fundingStartBlock; fundingEndBlock = _fundingEndBlock; } /// @notice Transfer `_value` GNT tokens from sender's account /// `msg.sender` to provided account address `_to`. /// @notice This function is disabled during the funding. /// @dev Required state: Operational /// @param _to The address of the tokens recipient /// @param _value The amount of token to be transferred /// @return Whether the transfer was successful or not function transfer(address _to, uint256 _value) returns (bool) { // Abort if not in Operational state. if (funding) throw; var senderBalance = balances[msg.sender]; if (senderBalance >= _value && _value > 0) { senderBalance -= _value; balances[msg.sender] = senderBalance; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } return false; } function totalSupply() external constant returns (uint256) { return totalTokens; } function balanceOf(address _owner) external constant returns (uint256) { return balances[_owner]; } // Token migration support: /// @notice Migrate tokens to the new token contract. /// @dev Required state: Operational Migration /// @param _value The amount of token to be migrated function migrate(uint256 _value) external { // Abort if not in Operational Migration state. if (funding) throw; if (migrationAgent == 0) throw; // Validate input value. if (_value == 0) throw; if (_value > balances[msg.sender]) throw; balances[msg.sender] -= _value; totalTokens -= _value; totalMigrated += _value; MigrationAgent(migrationAgent).migrateFrom(msg.sender, _value); Migrate(msg.sender, migrationAgent, _value); } /// @notice Set address of migration target contract and enable migration /// process. /// @dev Required state: Operational Normal /// @dev State transition: -> Operational Migration /// @param _agent The address of the MigrationAgent contract function setMigrationAgent(address _agent) external { // Abort if not in Operational Normal state. if (funding) throw; if (migrationAgent != 0) throw; if (msg.sender != migrationMaster) throw; migrationAgent = _agent; } function setMigrationMaster(address _master) external { if (msg.sender != migrationMaster) throw; if (_master == 0) throw; migrationMaster = _master; } // Crowdfunding: /// @notice Create tokens when funding is active. /// @dev Required state: Funding Active /// @dev State transition: -> Funding Success (only if cap reached) function create() payable external { // Abort if not in Funding Active state. // The checks are split (instead of using or operator) because it is // cheaper this way. if (!funding) throw; if (block.number < fundingStartBlock) throw; if (block.number > fundingEndBlock) throw; // Do not allow creating 0 or more than the cap tokens. if (msg.value == 0) throw; if (msg.value > (tokenCreationCap - totalTokens) / tokenCreationRate) throw; var numTokens = msg.value * tokenCreationRate; totalTokens += numTokens; // Assign new tokens to the sender balances[msg.sender] += numTokens; // Log token creation event Transfer(0, msg.sender, numTokens); } /// @notice Finalize crowdfunding /// @dev If cap was reached or crowdfunding has ended then: /// create GNT for the Golem Factory and developer, /// transfer ETH to the Golem Factory address. /// @dev Required state: Funding Success /// @dev State transition: -> Operational Normal function finalize() external { // Abort if not in Funding Success state. if (!funding) throw; if ((block.number <= fundingEndBlock || totalTokens < tokenCreationMin) && totalTokens < tokenCreationCap) throw; // Switch to Operational state. This is the only place this can happen. funding = false; // Create additional GNT for the Golem Factory and developers as // the 18% of total number of tokens. // All additional tokens are transfered to the account controller by // GNTAllocation contract which will not allow using them for 6 months. uint256 percentOfTotal = 18; uint256 additionalTokens = totalTokens * percentOfTotal / (100 - percentOfTotal); totalTokens += additionalTokens; balances[lockedAllocation] += additionalTokens; Transfer(0, lockedAllocation, additionalTokens); // Transfer ETH to the Golem Factory address. if (!golemFactory.send(this.balance)) throw; } /// @notice Get back the ether sent during the funding in case the funding /// has not reached the minimum level. /// @dev Required state: Funding Failure function refund() external { // Abort if not in Funding Failure state. if (!funding) throw; if (block.number <= fundingEndBlock) throw; if (totalTokens >= tokenCreationMin) throw; var gntValue = balances[msg.sender]; if (gntValue == 0) throw; balances[msg.sender] = 0; totalTokens -= gntValue; var ethValue = gntValue / tokenCreationRate; Refund(msg.sender, ethValue); if (!msg.sender.send(ethValue)) throw; } } /// @title GNT Allocation - Time-locked vault of tokens allocated /// to developers and Golem Factory contract GNTAllocation { // Total number of allocations to distribute additional tokens among // developers and the Golem Factory. The Golem Factory has right to 20000 // allocations, developers to 10000 allocations, divides among individual // developers by numbers specified in `allocations` table. uint256 constant totalAllocations = 30000; // Addresses of developer and the Golem Factory to allocations mapping. mapping (address => uint256) allocations; GolemNetworkToken gnt; uint256 unlockedAt; uint256 tokensCreated = 0; function GNTAllocation(address _golemFactory) internal { gnt = GolemNetworkToken(msg.sender); unlockedAt = now + 6 * 30 days; // For the Golem Factory: allocations[_golemFactory] = 20000; // 12/18 pp of 30000 allocations. // For developers: allocations[0x9d3F257827B17161a098d380822fa2614FF540c8] = 2500; // 25.0% of developers' allocations (10000). allocations[0xd7406E50b73972Fa4aa533a881af68B623Ba3F66] = 730; // 7.3% of developers' allocations. allocations[0xd15356D05A7990dE7eC94304B0fD538e550c09C0] = 730; allocations[0x3971D17B62b825b151760E2451F818BfB64489A7] = 730; allocations[0x95e337d09f1bc67681b1cab7ed1125ea2bae5ca8] = 730; allocations[0x0025C58dB686b8CEce05CB8c50C1858b63Aa396E] = 730; allocations[0xB127FC62dE6ca30aAc9D551591daEDdeBB2eFD7A] = 630; // 6.3% of developers' allocations. allocations[0x21AF2E2c240a71E9fB84e90d71c2B2AddE0D0e81] = 630; allocations[0x682AA1C3b3E102ACB9c97B861d595F9fbfF0f1B8] = 630; allocations[0x6edd429c77803606cBd6Bb501CC701a6CAD6be01] = 630; allocations[0x5E455624372FE11b39464e93d41D1F6578c3D9f6] = 310; // 3.1% of developers' allocations. allocations[0xB7c7EaD515Ca275d53e30B39D8EBEdb3F19dA244] = 138; // 1.38% of developers' allocations. allocations[0xD513b1c3fe31F3Fe0b1E42aa8F55e903F19f1730] = 135; // 1.35% of developers' allocations. allocations[0x70cac7f8E404EEFce6526823452e428b5Ab09b00] = 100; // 1.0% of developers' allocations. allocations[0xe0d5861e7be0fac6c85ecde6e8bf76b046a96149] = 100; allocations[0x17488694D2feE4377Ec718836bb9d4910E81D9Cf] = 100; allocations[0xb481372086dEc3ca2FCCD3EB2f462c9C893Ef3C5] = 100; allocations[0xFB6D91E69CD7990651f26a3aa9f8d5a89159fC92] = 70; // 0.7% of developers' allocations. allocations[0xE2ABdAe2980a1447F445cb962f9c0bef1B63EE13] = 70; allocations[0x729A5c0232712caAf365fDd03c39cb361Bd41b1C] = 70; allocations[0x12FBD8fef4903f62e30dD79AC7F439F573E02697] = 70; allocations[0x657013005e5cFAF76f75d03b465cE085d402469A] = 42; // 0.42% of developers' allocations. allocations[0xD0AF9f75EA618163944585bF56aCA98204d0AB66] = 25; // 0.25% of developers' allocations. } /// @notice Allow developer to unlock allocated tokens by transferring them /// from GNTAllocation to developer's address. function unlock() external { if (now < unlockedAt) throw; // During first unlock attempt fetch total number of locked tokens. if (tokensCreated == 0) tokensCreated = gnt.balanceOf(this); var allocation = allocations[msg.sender]; allocations[msg.sender] = 0; var toTransfer = tokensCreated * allocation / totalAllocations; // Will fail if allocation (and therefore toTransfer) is 0. if (!gnt.transfer(msg.sender, toTransfer)) throw; } }
File 3 of 4: DSEasyMultisig
contract DSFalseFallback { function() returns (bool) { return false; } } contract DSTrueFallback { function() returns (bool) { return true; } } contract DSAuthModesEnum { enum DSAuthModes { Owner, Authority } } contract DSAuthUtils is DSAuthModesEnum { function setOwner( DSAuthorized what, address owner ) internal { what.updateAuthority( owner, DSAuthModes.Owner ); } function setAuthority( DSAuthorized what, DSAuthority authority ) internal { what.updateAuthority( authority, DSAuthModes.Authority ); } } contract DSAuthorizedEvents is DSAuthModesEnum { event DSAuthUpdate( address indexed auth, DSAuthModes indexed mode ); } // `DSAuthority` is the interface which `DSAuthorized` (`DSAuth`) contracts expect // their authority to be when they are in the remote auth mode. contract DSAuthority { // `can_call` will be called with these arguments in the caller's // scope if it is coming from an `auth()` call: // `DSAuthority(_ds_authority).can_call(msg.sender, address(this), msg.sig);` function canCall( address caller , address callee , bytes4 sig ) constant returns (bool); } contract AcceptingAuthority is DSTrueFallback {} contract RejectingAuthority is DSFalseFallback {} // `DSAuthorized` is a mixin contract which enables standard authorization patterns. // It has a shorter alias `auth/auth.sol: DSAuth` because it is so common. contract DSAuthorized is DSAuthModesEnum, DSAuthorizedEvents { // There are two "modes": // * "owner mode", where `auth()` simply checks if the sender is `_authority`. // This is the default mode, when `_auth_mode` is false. // * "authority mode", where `auth()` makes a call to // `DSAuthority(_authority).canCall(sender, this, sig)` to ask if the // call should be allowed. DSAuthModes public _auth_mode; DSAuthority public _authority; function DSAuthorized() { _authority = DSAuthority(msg.sender); _auth_mode = DSAuthModes.Owner; DSAuthUpdate( msg.sender, DSAuthModes.Owner ); } // Attach the `auth()` modifier to functions to protect them. modifier auth() { if( isAuthorized() ) { _ } else { throw; } } // A version of `auth()` which implicitly returns garbage instead of throwing. modifier try_auth() { if( isAuthorized() ) { _ } } // An internal helper function for if you want to use the `auth()` logic // someplace other than the modifier (like in a fallback function). function isAuthorized() internal returns (bool is_authorized) { if( _auth_mode == DSAuthModes.Owner ) { return msg.sender == address(_authority); } if( _auth_mode == DSAuthModes.Authority ) { // use `canCall` in "authority" mode return _authority.canCall( msg.sender, address(this), msg.sig ); } throw; } // This function is used to both transfer the authority and update the mode. // Be extra careful about setting *both* correctly every time. function updateAuthority( address new_authority, DSAuthModes mode ) auth() { _authority = DSAuthority(new_authority); _auth_mode = mode; DSAuthUpdate( new_authority, mode ); } } contract DSAuth is DSAuthorized {} //, is DSAuthorizedEvents, DSAuthModesEnum contract DSAuthUser is DSAuthUtils {} //, is DSAuthModesEnum {} contract DSActionStructUser { struct Action { address target; uint value; bytes calldata; // bool triggered; } // todo store call_ret by default? } // A base contract used by governance contracts in `gov` and by the generic `DSController`. contract DSBaseActor is DSActionStructUser { // todo gas??? function tryExec(Action a) internal returns (bool call_ret) { return a.target.call.value(a.value)(a.calldata); } function exec(Action a) internal { if(!tryExec(a)) { throw; } } function tryExec( address target, bytes calldata, uint value) internal returns (bool call_ret) { return target.call.value(value)(calldata); } function exec( address target, bytes calldata, uint value) internal { if(!tryExec(target, calldata, value)) { throw; } } } contract DSEasyMultisigEvents { event MemberAdded(address who); event Proposed(uint indexed action_id, bytes calldata); event Confirmed(uint indexed action_id, address who); event Triggered(uint indexed action_id); } /* A multisig actor optimized for ease of use. * The user never has to pack their own calldata. Instead, use `easyPropose`. * This eliminates the need for UI support or helper contracts. * * To set up the multisig, specify the arguments, then add members * * First, call the multisig contract itself as if it were your target contract, * with the correct calldata. You can make Solidity and web3.js to do this for * you very easily by casting the multisig address as the target type. * Then, you call `easyPropose` with the missing arguments. This calls `propose`. * * "last calldata" is "local" to the `msg.sender`. This makes it usable directly * from keys (but still not as secure as if it were atomic using a helper contract). * * In Soldity: * 1) `TargetType(address(multisig)).doAction(arg1, arg2);` * 2) `multisig.easyPropose(address(target), value);` * * This is equivalent to `propose(address(my_target), <calldata>, value);`, * where calldata is correctly formatted for `TargetType(target).doAction(arg1, arg2)` */ contract DSEasyMultisig is DSBaseActor , DSEasyMultisigEvents , DSAuthUser , DSAuth { // How many confirmations an action needs to execute. uint _required; // How many members this multisig has. Members must be distinct addresses. uint _member_count; // Auto-locks once this reaches zero - easy setup phase. uint _members_remaining; // Maximum time between proposal time and trigger time. uint _expiration; // Action counter uint _last_action_id; struct action { address target; bytes calldata; uint value; uint confirmations; // If this number reaches `required`, you can trigger uint expiration; // Last timestamp this action can execute bool triggered; // Was this action successfully triggered (multisig does not catch exceptions) } mapping( uint => action ) actions; // action_id -> member -> confirmed mapping( uint => mapping( address => bool ) ) confirmations; // A record of the last fallback calldata recorded for this sender. // This is an easy way to create proposals for most actions. mapping( address => bytes ) easy_calldata; // Only these addresses can add confirmations mapping( address => bool ) is_member; function DSEasyMultisig( uint required, uint member_count, uint expiration ) { _required = required; _member_count = member_count; _members_remaining = member_count; _expiration = expiration; } // The authority can add members until they reach `member_count`, and then // the contract is finalized (`updateAuthority(0, DSAuthModes.Owner)`), // meaning addMember will always throw. function addMember( address who ) auth() { if( is_member[who] ) { throw; } is_member[who] = true; MemberAdded(who); _members_remaining--; if( _members_remaining == 0 ) { updateAuthority( address(0x0), DSAuthModes.Owner ); } } function isMember( address who ) constant returns (bool) { return is_member[who]; } // Some constant getters function getInfo() constant returns ( uint required, uint members, uint expiration, uint last_proposed_action) { return (_required, _member_count, _expiration, _last_action_id); } // Public getter for the action mapping doesn't work in web3.js yet function getActionStatus(uint action_id) constant returns (uint confirmations, uint expiration, bool triggered, address target, uint eth_value) { var a = actions[action_id]; return (a.confirmations, a.expiration, a.triggered, a.target, a.value); } // `propose` an action using the calldata from this sender's last call. function easyPropose( address target, uint value ) returns (uint action_id) { return propose( target, easy_calldata[msg.sender], value ); } function() { easy_calldata[msg.sender] = msg.data; } // Propose an action. // Anyone can propose an action. // Only members can confirm actions. function propose( address target, bytes calldata, uint value ) returns (uint action_id) { action memory a; a.target = target; a.calldata = calldata; a.value = value; a.expiration = block.timestamp + _expiration; // Increment first because, 0 is not a valid ID. _last_action_id++; actions[_last_action_id] = a; Proposed(_last_action_id, calldata); return _last_action_id; } // Attempts to confirm the action. // Only members can confirm actions. function confirm( uint action_id ) returns (bool confirmed) { if( !is_member[msg.sender] ) { throw; } if( confirmations[action_id][msg.sender] ) { throw; } if( action_id > _last_action_id ) { throw; } var a = actions[action_id]; if( block.timestamp > a.expiration ) { throw; } if( a.triggered ) { throw; } confirmations[action_id][msg.sender] = true; a.confirmations = a.confirmations + 1; actions[action_id] = a; Confirmed(action_id, msg.sender); } // Attempts to trigger the action. // Fails if there are not enough confirmations or if the action has expired. function trigger( uint action_id ) { var a = actions[action_id]; if( a.confirmations < _required ) { throw; } if( block.timestamp > a.expiration ) { throw; } if( a.triggered ) { throw; } if( this.balance < a.value ) { throw; } a.triggered = true; exec( a.target, a.calldata, a.value ); actions[action_id] = a; Triggered(action_id); } }
File 4 of 4: MultiSigWalletWithDailyLimit
pragma solidity 0.4.4; /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. /// @author Stefan George - <[email protected]> contract MultiSigWallet { uint constant public MAX_OWNER_COUNT = 50; 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); 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; } modifier onlyWallet() { if (msg.sender != address(this)) throw; _; } modifier ownerDoesNotExist(address owner) { if (isOwner[owner]) throw; _; } modifier ownerExists(address owner) { if (!isOwner[owner]) throw; _; } modifier transactionExists(uint transactionId) { if (transactions[transactionId].destination == 0) throw; _; } modifier confirmed(uint transactionId, address owner) { if (!confirmations[transactionId][owner]) throw; _; } modifier notConfirmed(uint transactionId, address owner) { if (confirmations[transactionId][owner]) throw; _; } modifier notExecuted(uint transactionId) { if (transactions[transactionId].executed) throw; _; } modifier notNull(address _address) { if (_address == 0) throw; _; } modifier validRequirement(uint ownerCount, uint _required) { if ( ownerCount > MAX_OWNER_COUNT || _required > ownerCount || _required == 0 || ownerCount == 0) throw; _; } /// @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++) { if (isOwner[_owners[i]] || _owners[i] == 0) throw; 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 owner 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 notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction tx = transactions[transactionId]; tx.executed = true; if (tx.destination.call.value(tx.value)(tx.data)) Execution(transactionId); else { ExecutionFailure(transactionId); tx.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 - <[email protected]> contract MultiSigWalletWithDailyLimit is MultiSigWallet { event DailyLimitChange(uint dailyLimit); 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 notExecuted(transactionId) { Transaction tx = transactions[transactionId]; bool confirmed = isConfirmed(transactionId); if (confirmed || tx.data.length == 0 && isUnderLimit(tx.value)) { tx.executed = true; if (!confirmed) spentToday += tx.value; if (tx.destination.call.value(tx.value)(tx.data)) Execution(transactionId); else { ExecutionFailure(transactionId); tx.executed = false; if (!confirmed) spentToday -= tx.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; return dailyLimit - spentToday; } }