Transaction Hash:
Block:
3557596 at Apr-18-2017 01:45:16 PM +UTC
Transaction Fee:
0.02141152 ETH
$34.26
Gas Used:
1,070,576 Gas / 20 Gwei
Emitted Events:
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 2,030.148649423189335809 Eth | 2,030.170060943189335809 Eth | 0.02141152 | |
0x6810e776...8e5386b96 |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 1
| |||
0xbe4e2544...dD838f901 | (Gnosis: Deployer) |
0.28272484 Eth
Nonce: 53
|
0.26131332 Eth
Nonce: 54
| 0.02141152 |
Execution Trace
GnosisToken.60606040( )
File 1 of 3: DutchAuction
File 2 of 3: GnosisToken
File 3 of 3: MultiSigWalletWithDailyLimit
pragma solidity 0.4.10; /// @title Abstract token contract - Functions to be implemented by token contracts. contract Token { function transfer(address to, uint256 value) returns (bool success); function transferFrom(address from, address to, uint256 value) returns (bool success); function approve(address spender, uint256 value) returns (bool success); // This is not an abstract function, because solc won't recognize generated getter functions for public variables as functions. function totalSupply() constant returns (uint256 supply) {} function balanceOf(address owner) constant returns (uint256 balance); function allowance(address owner, address spender) constant returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } /// @title Dutch auction contract - distribution of Gnosis tokens using an auction. /// @author Stefan George - <[email protected]> contract DutchAuction { /* * Events */ event BidSubmission(address indexed sender, uint256 amount); /* * Constants */ uint constant public MAX_TOKENS_SOLD = 9000000 * 10**18; // 9M uint constant public WAITING_PERIOD = 7 days; /* * Storage */ Token public gnosisToken; address public wallet; address public owner; uint public ceiling; uint public priceFactor; uint public startBlock; uint public endTime; uint public totalReceived; uint public finalPrice; mapping (address => uint) public bids; Stages public stage; /* * Enums */ enum Stages { AuctionDeployed, AuctionSetUp, AuctionStarted, AuctionEnded, TradingStarted } /* * Modifiers */ modifier atStage(Stages _stage) { if (stage != _stage) // Contract not in expected state throw; _; } modifier isOwner() { if (msg.sender != owner) // Only owner is allowed to proceed throw; _; } modifier isWallet() { if (msg.sender != wallet) // Only wallet is allowed to proceed throw; _; } modifier isValidPayload() { if (msg.data.length != 4 && msg.data.length != 36) throw; _; } modifier timedTransitions() { if (stage == Stages.AuctionStarted && calcTokenPrice() <= calcStopPrice()) finalizeAuction(); if (stage == Stages.AuctionEnded && now > endTime + WAITING_PERIOD) stage = Stages.TradingStarted; _; } /* * Public functions */ /// @dev Contract constructor function sets owner. /// @param _wallet Gnosis wallet. /// @param _ceiling Auction ceiling. /// @param _priceFactor Auction price factor. function DutchAuction(address _wallet, uint _ceiling, uint _priceFactor) public { if (_wallet == 0 || _ceiling == 0 || _priceFactor == 0) // Arguments are null. throw; owner = msg.sender; wallet = _wallet; ceiling = _ceiling; priceFactor = _priceFactor; stage = Stages.AuctionDeployed; } /// @dev Setup function sets external contracts' addresses. /// @param _gnosisToken Gnosis token address. function setup(address _gnosisToken) public isOwner atStage(Stages.AuctionDeployed) { if (_gnosisToken == 0) // Argument is null. throw; gnosisToken = Token(_gnosisToken); // Validate token balance if (gnosisToken.balanceOf(this) != MAX_TOKENS_SOLD) throw; stage = Stages.AuctionSetUp; } /// @dev Starts auction and sets startBlock. function startAuction() public isWallet atStage(Stages.AuctionSetUp) { stage = Stages.AuctionStarted; startBlock = block.number; } /// @dev Changes auction ceiling and start price factor before auction is started. /// @param _ceiling Updated auction ceiling. /// @param _priceFactor Updated start price factor. function changeSettings(uint _ceiling, uint _priceFactor) public isWallet atStage(Stages.AuctionSetUp) { ceiling = _ceiling; priceFactor = _priceFactor; } /// @dev Calculates current token price. /// @return Returns token price. function calcCurrentTokenPrice() public timedTransitions returns (uint) { if (stage == Stages.AuctionEnded || stage == Stages.TradingStarted) return finalPrice; return calcTokenPrice(); } /// @dev Returns correct stage, even if a function with timedTransitions modifier has not yet been called yet. /// @return Returns current auction stage. function updateStage() public timedTransitions returns (Stages) { return stage; } /// @dev Allows to send a bid to the auction. /// @param receiver Bid will be assigned to this address if set. function bid(address receiver) public payable isValidPayload timedTransitions atStage(Stages.AuctionStarted) returns (uint amount) { // If a bid is done on behalf of a user via ShapeShift, the receiver address is set. if (receiver == 0) receiver = msg.sender; amount = msg.value; // Prevent that more than 90% of tokens are sold. Only relevant if cap not reached. uint maxWei = (MAX_TOKENS_SOLD / 10**18) * calcTokenPrice() - totalReceived; uint maxWeiBasedOnTotalReceived = ceiling - totalReceived; if (maxWeiBasedOnTotalReceived < maxWei) maxWei = maxWeiBasedOnTotalReceived; // Only invest maximum possible amount. if (amount > maxWei) { amount = maxWei; // Send change back to receiver address. In case of a ShapeShift bid the user receives the change back directly. if (!receiver.send(msg.value - amount)) // Sending failed throw; } // Forward funding to ether wallet if (amount == 0 || !wallet.send(amount)) // No amount sent or sending failed throw; bids[receiver] += amount; totalReceived += amount; if (maxWei == amount) // When maxWei is equal to the big amount the auction is ended and finalizeAuction is triggered. finalizeAuction(); BidSubmission(receiver, amount); } /// @dev Claims tokens for bidder after auction. /// @param receiver Tokens will be assigned to this address if set. function claimTokens(address receiver) public isValidPayload timedTransitions atStage(Stages.TradingStarted) { if (receiver == 0) receiver = msg.sender; uint tokenCount = bids[receiver] * 10**18 / finalPrice; bids[receiver] = 0; gnosisToken.transfer(receiver, tokenCount); } /// @dev Calculates stop price. /// @return Returns stop price. function calcStopPrice() constant public returns (uint) { return totalReceived * 10**18 / MAX_TOKENS_SOLD + 1; } /// @dev Calculates token price. /// @return Returns token price. function calcTokenPrice() constant public returns (uint) { return priceFactor * 10**18 / (block.number - startBlock + 7500) + 1; } /* * Private functions */ function finalizeAuction() private { stage = Stages.AuctionEnded; if (totalReceived == ceiling) finalPrice = calcTokenPrice(); else finalPrice = calcStopPrice(); uint soldTokens = totalReceived * 10**18 / finalPrice; // Auction contract transfers all unsold tokens to Gnosis inventory multisig gnosisToken.transfer(wallet, MAX_TOKENS_SOLD - soldTokens); endTime = now; } }
File 2 of 3: GnosisToken
pragma solidity 0.4.10; /// @title Abstract token contract - Functions to be implemented by token contracts. contract Token { function transfer(address to, uint256 value) returns (bool success); function transferFrom(address from, address to, uint256 value) returns (bool success); function approve(address spender, uint256 value) returns (bool success); // This is not an abstract function, because solc won't recognize generated getter functions for public variables as functions. function totalSupply() constant returns (uint256 supply) {} function balanceOf(address owner) constant returns (uint256 balance); function allowance(address owner, address spender) constant returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } /// @title Standard token contract - Standard token interface implementation. contract StandardToken is Token { /* * Data structures */ mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; uint256 public totalSupply; /* * Public functions */ /// @dev Transfers sender's tokens to a given address. Returns success. /// @param _to Address of token receiver. /// @param _value Number of tokens to transfer. /// @return Returns success of function call. function transfer(address _to, uint256 _value) public returns (bool) { if (balances[msg.sender] < _value) { // Balance too low throw; } balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success. /// @param _from Address from where tokens are withdrawn. /// @param _to Address to where tokens are sent. /// @param _value Number of tokens to transfer. /// @return Returns success of function call. function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { if (balances[_from] < _value || allowed[_from][msg.sender] < _value) { // Balance or allowance too low throw; } balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } /// @dev Sets approved amount of tokens for spender. Returns success. /// @param _spender Address of allowed account. /// @param _value Number of approved tokens. /// @return Returns success of function call. function approve(address _spender, uint256 _value) public returns (bool) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } /* * Read functions */ /// @dev Returns number of allowed tokens for given address. /// @param _owner Address of token owner. /// @param _spender Address of token spender. /// @return Returns remaining allowance for spender. function allowance(address _owner, address _spender) constant public returns (uint256) { return allowed[_owner][_spender]; } /// @dev Returns number of tokens owned by given address. /// @param _owner Address of token owner. /// @return Returns balance of owner. function balanceOf(address _owner) constant public returns (uint256) { return balances[_owner]; } } /// @title Gnosis token contract /// @author Stefan George - <[email protected]> contract GnosisToken is StandardToken { /* * Token meta data */ string constant public name = "Gnosis Token"; string constant public symbol = "GNO"; uint8 constant public decimals = 18; /* * Public functions */ /// @dev Contract constructor function sets dutch auction contract address and assigns all tokens to dutch auction. /// @param dutchAuction Address of dutch auction contract. /// @param owners Array of addresses receiving preassigned tokens. /// @param tokens Array of preassigned token amounts. function GnosisToken(address dutchAuction, address[] owners, uint[] tokens) public { if (dutchAuction == 0) // Address should not be null. throw; totalSupply = 10000000 * 10**18; balances[dutchAuction] = 9000000 * 10**18; Transfer(0, dutchAuction, balances[dutchAuction]); uint assignedTokens = balances[dutchAuction]; for (uint i=0; i<owners.length; i++) { if (owners[i] == 0) // Address should not be null. throw; balances[owners[i]] += tokens[i]; Transfer(0, owners[i], tokens[i]); assignedTokens += tokens[i]; } if (assignedTokens != totalSupply) throw; } }
File 3 of 3: 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; } }