Originally posted Sep 15 2016. Updated Oct 29 2016 with The DAO hacker successfully converting ~ USD 100,000 of booty into bitcoins
The DAO, the first-of-a-kind decentralised autonomous venture capital fund on the Ethereum virtual currency blockchain started it’s 28 day crowdfunding phase on Apr 30 2016. It raised the equivalent of USD 150 million from over 20,000 separate accounts. The DAO consists of 1236 lines of source code running at account 0xbb9bc244d798123fde783fcc1c72d3bb8c189413. Click here to view the source code in the Online Solidity Editor.
The DAO Bug And The Hack
The DAO was hacked to the tune of USD 50 million (~ 30%) on Jun 17 2016 by exploiting a bug in the highlighted lines within the withdrawRewardFor(...)
function below. Moving line 9 before line 7 would have prevented this attack (but it is easy to spot now after the event):
1 2 3 4 5 6 7 8 9 10 11 |
function withdrawRewardFor(address _account) noEther internal returns (bool _success) { if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account]) throw; uint reward = (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account]; if (!rewardAccount.payOut(_account, reward)) throw; paidOut[_account] += reward; return true; } |
Here is the bug fix in The DAO’s github repository.
What the hacker did was to withdraw their ethers into an Ethereum smart contract that recursively called the splitDAO(..)
function which calls the withdrawRewardFor(...)
, up to a depth of 29 recursive calls. So the transfers were executed 29 times without paidout[_account]
being incremented with the amount already paid out. The attacking code also transferred the account’s The DAO tokens between different contracts to prevent these token amounts being zeroed out.
For further details of the hack see my Ethereum.StackExchange.com Q&As:
- What is a recursive calling vulnerability?
- Which split proposal was used to mount the recursive call vulnerability attack on The DAO?
- Which accounts are involved in mounting the recursive call vulnerability attacks on The DAO?
- How was the recursive call vulnerability attack conducted via proposal #59 when there were no Yes votes?
- What was the second vulnerability used in The DAO attack on 17 June 2016?
- How many The DAO recursive call vulnerability attacks have occurred to date?
- How many ethers have been drained through the recursive call attacks on The DAO?
- Give a summary of the fork state changes in block 1920000
Accounts
- The DAO 0xbb9bc244d798123fde783fcc1c72d3bb8c189413
- The DAO extraBalance 0x807640a13483f8ac783c557fcdf27be11ea4ac7a
- The DAO Refund – DAO to ETH Withdrawal contract 0xbf4ed7b27f1d666546e30d74d50d173d20bca754
- The DAO Refund – DAO to ETH extraBalance Withdrawal contract 0x755cdba6ae4f479f7164792b318b2a06c759833b
- The DAO Refund – DAO to ETC Withdrawal contract on the Classic chain 0x9f5304da62a5408416ea58a17a92611019bd5ce3
History
- The DAO contract deployment in tx 0xe9eb…30f9 in #1,428,757 at Apr-30-2016 01:42:58 AM +UTC
- First crowdfunding transaction at 100 DAO = 1 ETH in tx 0x18aa…8c61 in #1,429,008 at Apr-30-2016 02:44:37 AM +UTC
- First crowdfunding transaction at 100 DAO > 1 ETH in tx 0xb989…6003 in #1,520,861 at May-15-2016 09:00:16 AM +UTC
- Last crowdfunding transaction at 100 DAO = 1.5 ETH in tx 0x39e8…a7da in #1,599,205 at May-28-2016 08:59:47 AM +UTC
- Baddie’s first recursive vulnerability attack in tx 0x0ec3…790b in #1,718,497 at Jun-17-2016 03:34:48 AM +UTC
- Baddie’s last recursive vulnerability attack in tx 0xa348…f6e3 in #1,720,245 at Jun-17-2016 11:00:23 AM +UTC
- The DAO hard fork in #1,920,000 at Jul-20-2016 01:20:40 PM +UTC
The DAO Refunds
The Ethereum node client software was modified to hard-fork the Ethereum blockchain at block 1,920,000 on Jul 20 2016. Ethers (ETH) in The DAO and it’s child DAOs were swept up into the curator’s multisig contract. These ETHs are being refunded fairly to The DAO investors in full – see the base refund, the extraBalance refund and the split DAO refund.
See also One Month After Ethereum Fork, Millions in DAO Funds Unclaimed where yours truly from New Zealand is mentioned.
Poloniex unexpectedly listed the non-forked Ethereum blockchain (called Ethereum Classic with ETC coins) on Jul 24 2017. ETCs are currently trading at about 12% of the original ETH price (1 ETC = 1.430 USD or 1.894 AUD while 1 ETH = 12.000 USD or 15.895 AUD). The Goodies used the same hack as the Baddies to drain the remaining ETCs on the Ethereum Classic chain and are expecting to return 71% of ETCs to The DAO investors. 60% of the funds is already available for The DAO investors to withdraw, with the remaining available in a few weeks.
And the Baddies will have access to 3641694.241898506992612606 ETCs from Mon, 05 Sep 2016 21:56:22 UTC. The current value for this stash is around USD 5.5 million. But it may be difficult for the Baddies to convert these funds into cash or other untraceable currencies.
The DAO Refund Procedures
- Convert DAO to ETH
- Convert DAO extraBalance to ETH
- Convert Split DAO to ETH
- Convert DAO to ETC – withdraw before Feb 13 2017
- Prevent replays on ETH or ETC chain
Update 22:51 Sep 5 2016 UTC – The Hacker Has Just Withdrawn Their Booty From The DAO On The ETC Chain.
Update Oct 29 2016 01:26:11 UTC – The DAO Hacker’s Booty Is On The Move, The Plot Thickens As DAO Attacker Trades Stolen Funds for Bitcoin and The DAO Hacker Cashes Out.
The Source Code
Table of contents
- The Source Code
- The Application Binary Interface – Full Version
- The Application Binary Interface – Short Version
For easier reading, I have stripped out all but the first GPL licence text, separated the individual contracts and interfaces and removed unnecessary blank lines.
GPL v3 Copyright
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* - Bytecode Verification performed was compared on second iteration - This file is part of the DAO. The DAO is free software: you can redistribute it and/or modify it under the terms of the GNU lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The DAO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lesser General Public License for more details. You should have received a copy of the GNU lesser General Public License along with the DAO. If not, see <http://www.gnu.org/licenses/>. */ |
Contract TokenInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/* Basic, standardized Token contract with no "premine". Defines the functions to check token balances, send tokens, send tokens on behalf of a 3rd party and the corresponding approval process. Tokens need to be created by a derived contract (e.g. TokenCreation.sol). Thank you ConsenSys, this contract originated from: https://github.com/ConsenSys/Tokens/blob/master/Token_Contracts/contracts/Standard_Token.sol Which is itself based on the Ethereum standardized contract APIs: https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs */ /// @title Standard Token Contract. contract TokenInterface { mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; /// Total amount of tokens uint256 public totalSupply; /// @param _owner The address from which the balance will be retrieved /// @return The balance function balanceOf(address _owner) constant returns (uint256 balance); /// @notice Send `_amount` tokens to `_to` from `msg.sender` /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transferred /// @return Whether the transfer was successful or not function transfer(address _to, uint256 _amount) returns (bool success); /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it /// is approved by `_from` /// @param _from The address of the origin of the transfer /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transferred /// @return Whether the transfer was successful or not function transferFrom(address _from, address _to, uint256 _amount) returns (bool success); /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on /// its behalf /// @param _spender The address of the account able to transfer the tokens /// @param _amount The amount of tokens to be approved for transfer /// @return Whether the approval was successful or not function approve(address _spender, uint256 _amount) returns (bool success); /// @param _owner The address of the account owning tokens /// @param _spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens of _owner that _spender is allowed /// to spend function allowance( address _owner, address _spender ) constant returns (uint256 remaining); event Transfer(address indexed _from, address indexed _to, uint256 _amount); event Approval( address indexed _owner, address indexed _spender, uint256 _amount ); } |
Contract Token (is TokenInterface)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
contract Token is TokenInterface { // Protects users by preventing the execution of method calls that // inadvertently also transferred ether modifier noEther() {if (msg.value > 0) throw; _} function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } function transfer(address _to, uint256 _amount) noEther returns (bool success) { if (balances[msg.sender] >= _amount && _amount > 0) { balances[msg.sender] -= _amount; balances[_to] += _amount; Transfer(msg.sender, _to, _amount); return true; } else { return false; } } function transferFrom( address _from, address _to, uint256 _amount ) noEther returns (bool success) { if (balances[_from] >= _amount && allowed[_from][msg.sender] >= _amount && _amount > 0) { balances[_to] += _amount; balances[_from] -= _amount; allowed[_from][msg.sender] -= _amount; Transfer(_from, _to, _amount); return true; } else { return false; } } function approve(address _spender, uint256 _amount) returns (bool success) { allowed[msg.sender][_spender] = _amount; Approval(msg.sender, _spender, _amount); return true; } function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } } |
Contract ManagedAccountInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* Basic account, used by the DAO contract to separately manage both the rewards and the extraBalance accounts. */ contract ManagedAccountInterface { // The only address with permission to withdraw from this account address public owner; // If true, only the owner of the account can receive ether from it bool public payOwnerOnly; // The sum of ether (in wei) which has been sent to this contract uint public accumulatedInput; /// @notice Sends `_amount` of wei to _recipient /// @param _amount The amount of wei to send to `_recipient` /// @param _recipient The address to receive `_amount` of wei /// @return True if the send completed function payOut(address _recipient, uint _amount) returns (bool); event PayOut(address indexed _recipient, uint _amount); } |
Contract ManagedAccount (is ManagedAccountInterface)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
contract ManagedAccount is ManagedAccountInterface{ // The constructor sets the owner of the account function ManagedAccount(address _owner, bool _payOwnerOnly) { owner = _owner; payOwnerOnly = _payOwnerOnly; } // When the contract receives a transaction without data this is called. // It counts the amount of ether it receives and stores it in // accumulatedInput. function() { accumulatedInput += msg.value; } function payOut(address _recipient, uint _amount) returns (bool) { if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner)) throw; if (_recipient.call.value(_amount)()) { PayOut(_recipient, _amount); return true; } else { return false; } } } |
Contract TokenCreationInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/* * Token Creation contract, used by the DAO to create its tokens and initialize * its ether. Feel free to modify the divisor method to implement different * Token Creation parameters */ contract TokenCreationInterface { // End of token creation, in Unix time uint public closingTime; // Minimum fueling goal of the token creation, denominated in tokens to // be created uint public minTokensToCreate; // True if the DAO reached its minimum fueling goal, false otherwise bool public isFueled; // For DAO splits - if privateCreation is 0, then it is a public token // creation, otherwise only the address stored in privateCreation is // allowed to create tokens address public privateCreation; // hold extra ether which has been sent after the DAO token // creation rate has increased ManagedAccount public extraBalance; // tracks the amount of wei given from each contributor (used for refund) mapping (address => uint256) weiGiven; /// @dev Constructor setting the minimum fueling goal and the /// end of the Token Creation /// @param _minTokensToCreate Minimum fueling goal in number of /// Tokens to be created /// @param _closingTime Date (in Unix time) of the end of the Token Creation /// @param _privateCreation Zero means that the creation is public. A /// non-zero address represents the only address that can create Tokens /// (the address can also create Tokens on behalf of other accounts) // This is the constructor: it can not be overloaded so it is commented out // function TokenCreation( // uint _minTokensTocreate, // uint _closingTime, // address _privateCreation // ); /// @notice Create Token with `_tokenHolder` as the initial owner of the Token /// @param _tokenHolder The address of the Tokens's recipient /// @return Whether the token creation was successful function createTokenProxy(address _tokenHolder) returns (bool success); /// @notice Refund `msg.sender` in the case the Token Creation did /// not reach its minimum fueling goal function refund(); /// @return The divisor used to calculate the token creation rate during /// the creation phase function divisor() constant returns (uint divisor); event FuelingToDate(uint value); event CreatedToken(address indexed to, uint amount); event Refund(address indexed to, uint value); } |
Contract TokenCreation (is TokenCreationInterface, Token)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
contract TokenCreation is TokenCreationInterface, Token { function TokenCreation( uint _minTokensToCreate, uint _closingTime, address _privateCreation) { closingTime = _closingTime; minTokensToCreate = _minTokensToCreate; privateCreation = _privateCreation; extraBalance = new ManagedAccount(address(this), true); } function createTokenProxy(address _tokenHolder) returns (bool success) { if (now < closingTime && msg.value > 0 && (privateCreation == 0 || privateCreation == msg.sender)) { uint token = (msg.value * 20) / divisor(); extraBalance.call.value(msg.value - token)(); balances[_tokenHolder] += token; totalSupply += token; weiGiven[_tokenHolder] += msg.value; CreatedToken(_tokenHolder, token); if (totalSupply >= minTokensToCreate && !isFueled) { isFueled = true; FuelingToDate(totalSupply); } return true; } throw; } function refund() noEther { if (now > closingTime && !isFueled) { // Get extraBalance - will only succeed when called for the first time if (extraBalance.balance >= extraBalance.accumulatedInput()) extraBalance.payOut(address(this), extraBalance.accumulatedInput()); // Execute refund if (msg.sender.call.value(weiGiven[msg.sender])()) { Refund(msg.sender, weiGiven[msg.sender]); totalSupply -= balances[msg.sender]; balances[msg.sender] = 0; weiGiven[msg.sender] = 0; } } } function divisor() constant returns (uint divisor) { // The number of (base unit) tokens per wei is calculated // as `msg.value` * 20 / `divisor` // The fueling period starts with a 1:1 ratio if (closingTime - 2 weeks > now) { return 20; // Followed by 10 days with a daily creation rate increase of 5% } else if (closingTime - 4 days > now) { return (20 + (now - (closingTime - 2 weeks)) / (1 days)); // The last 4 days there is a constant creation rate ratio of 1:1.5 } else { return 30; } } } |
Contract DAOInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
/* Standard smart contract for a Decentralized Autonomous Organization (DAO) to automate organizational governance and decision-making. */ contract DAOInterface { // The amount of days for which people who try to participate in the // creation by calling the fallback function will still get their ether back uint constant creationGracePeriod = 40 days; // The minimum debate period that a generic proposal can have uint constant minProposalDebatePeriod = 2 weeks; // The minimum debate period that a split proposal can have uint constant minSplitDebatePeriod = 1 weeks; // Period of days inside which it's possible to execute a DAO split uint constant splitExecutionPeriod = 27 days; // Period of time after which the minimum Quorum is halved uint constant quorumHalvingPeriod = 25 weeks; // Period after which a proposal is closed // (used in the case `executeProposal` fails because it throws) uint constant executeProposalPeriod = 10 days; // Denotes the maximum proposal deposit that can be given. It is given as // a fraction of total Ether spent plus balance of the DAO uint constant maxDepositDivisor = 100; // Proposals to spend the DAO's ether or to choose a new Curator Proposal[] public proposals; // The quorum needed for each proposal is partially calculated by // totalSupply / minQuorumDivisor uint public minQuorumDivisor; // The unix time of the last time quorum was reached on a proposal uint public lastTimeMinQuorumMet; // Address of the curator address public curator; // The whitelist: List of addresses the DAO is allowed to send ether to mapping (address => bool) public allowedRecipients; // Tracks the addresses that own Reward Tokens. Those addresses can only be // DAOs that have split from the original DAO. Conceptually, Reward Tokens // represent the proportion of the rewards that the DAO has the right to // receive. These Reward Tokens are generated when the DAO spends ether. mapping (address => uint) public rewardToken; // Total supply of rewardToken uint public totalRewardToken; // The account used to manage the rewards which are to be distributed to the // DAO Token Holders of this DAO ManagedAccount public rewardAccount; // The account used to manage the rewards which are to be distributed to // any DAO that holds Reward Tokens ManagedAccount public DAOrewardAccount; // Amount of rewards (in wei) already paid out to a certain DAO mapping (address => uint) public DAOpaidOut; // Amount of rewards (in wei) already paid out to a certain address mapping (address => uint) public paidOut; // Map of addresses blocked during a vote (not allowed to transfer DAO // tokens). The address points to the proposal ID. mapping (address => uint) public blocked; // The minimum deposit (in wei) required to submit any proposal that is not // requesting a new Curator (no deposit is required for splits) uint public proposalDeposit; // the accumulated sum of all current proposal deposits uint sumOfProposalDeposits; // Contract that is able to create a new DAO (with the same code as // this one), used for splits DAO_Creator public daoCreator; // A proposal with `newCurator == false` represents a transaction // to be issued by this DAO // A proposal with `newCurator == true` represents a DAO split struct Proposal { // The address where the `amount` will go to if the proposal is accepted // or if `newCurator` is true, the proposed Curator of // the new DAO). address recipient; // The amount to transfer to `recipient` if the proposal is accepted. uint amount; // A plain text description of the proposal string description; // A unix timestamp, denoting the end of the voting period uint votingDeadline; // True if the proposal's votes have yet to be counted, otherwise False bool open; // True if quorum has been reached, the votes have been counted, and // the majority said yes bool proposalPassed; // A hash to check validity of a proposal bytes32 proposalHash; // Deposit in wei the creator added when submitting their proposal. It // is taken from the msg.value of a newProposal call. uint proposalDeposit; // True if this proposal is to assign a new Curator bool newCurator; // Data needed for splitting the DAO SplitData[] splitData; // Number of Tokens in favor of the proposal uint yea; // Number of Tokens opposed to the proposal uint nay; // Simple mapping to check if a shareholder has voted for it mapping (address => bool) votedYes; // Simple mapping to check if a shareholder has voted against it mapping (address => bool) votedNo; // Address of the shareholder who created the proposal address creator; } // Used only in the case of a newCurator proposal. struct SplitData { // The balance of the current DAO minus the deposit at the time of split uint splitBalance; // The total amount of DAO Tokens in existence at the time of split. uint totalSupply; // Amount of Reward Tokens owned by the DAO at the time of split. uint rewardToken; // The new DAO contract created at the time of split. DAO newDAO; } // Used to restrict access to certain functions to only DAO Token Holders modifier onlyTokenholders {} /// @dev Constructor setting the Curator and the address /// for the contract able to create another DAO as well as the parameters /// for the DAO Token Creation /// @param _curator The Curator /// @param _daoCreator The contract able to (re)create this DAO /// @param _proposalDeposit The deposit to be paid for a regular proposal /// @param _minTokensToCreate Minimum required wei-equivalent tokens /// to be created for a successful DAO Token Creation /// @param _closingTime Date (in Unix time) of the end of the DAO Token Creation /// @param _privateCreation If zero the DAO Token Creation is open to public, a /// non-zero address means that the DAO Token Creation is only for the address // This is the constructor: it can not be overloaded so it is commented out // function DAO( // address _curator, // DAO_Creator _daoCreator, // uint _proposalDeposit, // uint _minTokensToCreate, // uint _closingTime, // address _privateCreation // ); /// @notice Create Token with `msg.sender` as the beneficiary /// @return Whether the token creation was successful function () returns (bool success); /// @dev This function is used to send ether back /// to the DAO, it can also be used to receive payments that should not be /// counted as rewards (donations, grants, etc.) /// @return Whether the DAO received the ether successfully function receiveEther() returns(bool); /// @notice `msg.sender` creates a proposal to send `_amount` Wei to /// `_recipient` with the transaction data `_transactionData`. If /// `_newCurator` is true, then this is a proposal that splits the /// DAO and sets `_recipient` as the new DAO's Curator. /// @param _recipient Address of the recipient of the proposed transaction /// @param _amount Amount of wei to be sent with the proposed transaction /// @param _description String describing the proposal /// @param _transactionData Data of the proposed transaction /// @param _debatingPeriod Time used for debating a proposal, at least 2 /// weeks for a regular proposal, 10 days for new Curator proposal /// @param _newCurator Bool defining whether this proposal is about /// a new Curator or not /// @return The proposal ID. Needed for voting on the proposal function newProposal( address _recipient, uint _amount, string _description, bytes _transactionData, uint _debatingPeriod, bool _newCurator ) onlyTokenholders returns (uint _proposalID); /// @notice Check that the proposal with the ID `_proposalID` matches the /// transaction which sends `_amount` with data `_transactionData` /// to `_recipient` /// @param _proposalID The proposal ID /// @param _recipient The recipient of the proposed transaction /// @param _amount The amount of wei to be sent in the proposed transaction /// @param _transactionData The data of the proposed transaction /// @return Whether the proposal ID matches the transaction data or not function checkProposalCode( uint _proposalID, address _recipient, uint _amount, bytes _transactionData ) constant returns (bool _codeChecksOut); /// @notice Vote on proposal `_proposalID` with `_supportsProposal` /// @param _proposalID The proposal ID /// @param _supportsProposal Yes/No - support of the proposal /// @return The vote ID. function vote( uint _proposalID, bool _supportsProposal ) onlyTokenholders returns (uint _voteID); /// @notice Checks whether proposal `_proposalID` with transaction data /// `_transactionData` has been voted for or rejected, and executes the /// transaction in the case it has been voted for. /// @param _proposalID The proposal ID /// @param _transactionData The data of the proposed transaction /// @return Whether the proposed transaction has been executed or not function executeProposal( uint _proposalID, bytes _transactionData ) returns (bool _success); /// @notice ATTENTION! I confirm to move my remaining ether to a new DAO /// with `_newCurator` as the new Curator, as has been /// proposed in proposal `_proposalID`. This will burn my tokens. This can /// not be undone and will split the DAO into two DAO's, with two /// different underlying tokens. /// @param _proposalID The proposal ID /// @param _newCurator The new Curator of the new DAO /// @dev This function, when called for the first time for this proposal, /// will create a new DAO and send the sender's portion of the remaining /// ether and Reward Tokens to the new DAO. It will also burn the DAO Tokens /// of the sender. function splitDAO( uint _proposalID, address _newCurator ) returns (bool _success); /// @dev can only be called by the DAO itself through a proposal /// updates the contract of the DAO by sending all ether and rewardTokens /// to the new DAO. The new DAO needs to be approved by the Curator /// @param _newContract the address of the new contract function newContract(address _newContract); /// @notice Add a new possible recipient `_recipient` to the whitelist so /// that the DAO can send transactions to them (using proposals) /// @param _recipient New recipient address /// @dev Can only be called by the current Curator /// @return Whether successful or not function changeAllowedRecipients(address _recipient, bool _allowed) external returns (bool _success); /// @notice Change the minimum deposit required to submit a proposal /// @param _proposalDeposit The new proposal deposit /// @dev Can only be called by this DAO (through proposals with the /// recipient being this DAO itself) function changeProposalDeposit(uint _proposalDeposit) external; /// @notice Move rewards from the DAORewards managed account /// @param _toMembers If true rewards are moved to the actual reward account /// for the DAO. If not then it's moved to the DAO itself /// @return Whether the call was successful function retrieveDAOReward(bool _toMembers) external returns (bool _success); /// @notice Get my portion of the reward that was sent to `rewardAccount` /// @return Whether the call was successful function getMyReward() returns(bool _success); /// @notice Withdraw `_account`'s portion of the reward from `rewardAccount` /// to `_account`'s balance /// @return Whether the call was successful function withdrawRewardFor(address _account) internal returns (bool _success); /// @notice Send `_amount` tokens to `_to` from `msg.sender`. Prior to this /// getMyReward() is called. /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transfered /// @return Whether the transfer was successful or not function transferWithoutReward(address _to, uint256 _amount) returns (bool success); /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it /// is approved by `_from`. Prior to this getMyReward() is called. /// @param _from The address of the sender /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transfered /// @return Whether the transfer was successful or not function transferFromWithoutReward( address _from, address _to, uint256 _amount ) returns (bool success); /// @notice Doubles the 'minQuorumDivisor' in the case quorum has not been /// achieved in 52 weeks /// @return Whether the change was successful or not function halveMinQuorum() returns (bool _success); /// @return total number of proposals ever created function numberOfProposals() constant returns (uint _numberOfProposals); /// @param _proposalID Id of the new curator proposal /// @return Address of the new DAO function getNewDAOAddress(uint _proposalID) constant returns (address _newDAO); /// @param _account The address of the account which is checked. /// @return Whether the account is blocked (not allowed to transfer tokens) or not. function isBlocked(address _account) internal returns (bool); /// @notice If the caller is blocked by a proposal whose voting deadline /// has exprired then unblock him. /// @return Whether the account is blocked (not allowed to transfer tokens) or not. function unblockMe() returns (bool); event ProposalAdded( uint indexed proposalID, address recipient, uint amount, bool newCurator, string description ); event Voted(uint indexed proposalID, bool position, address indexed voter); event ProposalTallied(uint indexed proposalID, bool result, uint quorum); event NewCurator(address indexed _newCurator); event AllowedRecipientChanged(address indexed _recipient, bool _allowed); } |
Contract DAO (is DAOInterface, Token, TokenCreation)
Note that the withdrawRewardFor(..)
code below has a USD 50 million bug.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 |
// The DAO contract itself contract DAO is DAOInterface, Token, TokenCreation { // Modifier that allows only shareholders to vote and create new proposals modifier onlyTokenholders { if (balanceOf(msg.sender) == 0) throw; _ } function DAO( address _curator, DAO_Creator _daoCreator, uint _proposalDeposit, uint _minTokensToCreate, uint _closingTime, address _privateCreation ) TokenCreation(_minTokensToCreate, _closingTime, _privateCreation) { curator = _curator; daoCreator = _daoCreator; proposalDeposit = _proposalDeposit; rewardAccount = new ManagedAccount(address(this), false); DAOrewardAccount = new ManagedAccount(address(this), false); if (address(rewardAccount) == 0) throw; if (address(DAOrewardAccount) == 0) throw; lastTimeMinQuorumMet = now; minQuorumDivisor = 5; // sets the minimal quorum to 20% proposals.length = 1; // avoids a proposal with ID 0 because it is used allowedRecipients[address(this)] = true; allowedRecipients[curator] = true; } function () returns (bool success) { if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance)) return createTokenProxy(msg.sender); else return receiveEther(); } function receiveEther() returns (bool) { return true; } function newProposal( address _recipient, uint _amount, string _description, bytes _transactionData, uint _debatingPeriod, bool _newCurator ) onlyTokenholders returns (uint _proposalID) { // Sanity check if (_newCurator && ( _amount != 0 || _transactionData.length != 0 || _recipient == curator || msg.value > 0 || _debatingPeriod < minSplitDebatePeriod)) { throw; } else if ( !_newCurator && (!isRecipientAllowed(_recipient) || (_debatingPeriod < minProposalDebatePeriod)) ) { throw; } if (_debatingPeriod > 8 weeks) throw; if (!isFueled || now < closingTime || (msg.value < proposalDeposit && !_newCurator)) { throw; } if (now + _debatingPeriod < now) // prevents overflow throw; // to prevent a 51% attacker to convert the ether into deposit if (msg.sender == address(this)) throw; _proposalID = proposals.length++; Proposal p = proposals[_proposalID]; p.recipient = _recipient; p.amount = _amount; p.description = _description; p.proposalHash = sha3(_recipient, _amount, _transactionData); p.votingDeadline = now + _debatingPeriod; p.open = true; //p.proposalPassed = False; // that's default p.newCurator = _newCurator; if (_newCurator) p.splitData.length++; p.creator = msg.sender; p.proposalDeposit = msg.value; sumOfProposalDeposits += msg.value; ProposalAdded( _proposalID, _recipient, _amount, _newCurator, _description ); } function checkProposalCode( uint _proposalID, address _recipient, uint _amount, bytes _transactionData ) noEther constant returns (bool _codeChecksOut) { Proposal p = proposals[_proposalID]; return p.proposalHash == sha3(_recipient, _amount, _transactionData); } function vote( uint _proposalID, bool _supportsProposal ) onlyTokenholders noEther returns (uint _voteID) { Proposal p = proposals[_proposalID]; if (p.votedYes[msg.sender] || p.votedNo[msg.sender] || now >= p.votingDeadline) { throw; } if (_supportsProposal) { p.yea += balances[msg.sender]; p.votedYes[msg.sender] = true; } else { p.nay += balances[msg.sender]; p.votedNo[msg.sender] = true; } if (blocked[msg.sender] == 0) { blocked[msg.sender] = _proposalID; } else if (p.votingDeadline > proposals[blocked[msg.sender]].votingDeadline) { // this proposal's voting deadline is further into the future than // the proposal that blocks the sender so make it the blocker blocked[msg.sender] = _proposalID; } Voted(_proposalID, _supportsProposal, msg.sender); } function executeProposal( uint _proposalID, bytes _transactionData ) noEther returns (bool _success) { Proposal p = proposals[_proposalID]; uint waitPeriod = p.newCurator ? splitExecutionPeriod : executeProposalPeriod; // If we are over deadline and waiting period, assert proposal is closed if (p.open && now > p.votingDeadline + waitPeriod) { closeProposal(_proposalID); return; } // Check if the proposal can be executed if (now < p.votingDeadline // has the voting deadline arrived? // Have the votes been counted? || !p.open // Does the transaction code match the proposal? || p.proposalHash != sha3(p.recipient, p.amount, _transactionData)) { throw; } // If the curator removed the recipient from the whitelist, close the proposal // in order to free the deposit and allow unblocking of voters if (!isRecipientAllowed(p.recipient)) { closeProposal(_proposalID); p.creator.send(p.proposalDeposit); return; } bool proposalCheck = true; if (p.amount > actualBalance()) proposalCheck = false; uint quorum = p.yea + p.nay; // require 53% for calling newContract() if (_transactionData.length >= 4 && _transactionData[0] == 0x68 && _transactionData[1] == 0x37 && _transactionData[2] == 0xff && _transactionData[3] == 0x1e && quorum < minQuorum(actualBalance() + rewardToken[address(this)])) { proposalCheck = false; } if (quorum >= minQuorum(p.amount)) { if (!p.creator.send(p.proposalDeposit)) throw; lastTimeMinQuorumMet = now; // set the minQuorum to 20% again, in the case it has been reached if (quorum > totalSupply / 5) minQuorumDivisor = 5; } // Execute result if (quorum >= minQuorum(p.amount) && p.yea > p.nay && proposalCheck) { if (!p.recipient.call.value(p.amount)(_transactionData)) throw; p.proposalPassed = true; _success = true; // only create reward tokens when ether is not sent to the DAO itself and // related addresses. Proxy addresses should be forbidden by the curator. if (p.recipient != address(this) && p.recipient != address(rewardAccount) && p.recipient != address(DAOrewardAccount) && p.recipient != address(extraBalance) && p.recipient != address(curator)) { rewardToken[address(this)] += p.amount; totalRewardToken += p.amount; } } closeProposal(_proposalID); // Initiate event ProposalTallied(_proposalID, _success, quorum); } function closeProposal(uint _proposalID) internal { Proposal p = proposals[_proposalID]; if (p.open) sumOfProposalDeposits -= p.proposalDeposit; p.open = false; } function splitDAO( uint _proposalID, address _newCurator ) noEther onlyTokenholders returns (bool _success) { Proposal p = proposals[_proposalID]; // Sanity check if (now < p.votingDeadline // has the voting deadline arrived? //The request for a split expires XX days after the voting deadline || now > p.votingDeadline + splitExecutionPeriod // Does the new Curator address match? || p.recipient != _newCurator // Is it a new curator proposal? || !p.newCurator // Have you voted for this split? || !p.votedYes[msg.sender] // Did you already vote on another proposal? || (blocked[msg.sender] != _proposalID && blocked[msg.sender] != 0) ) { throw; } // If the new DAO doesn't exist yet, create the new DAO and store the // current split data if (address(p.splitData[0].newDAO) == 0) { p.splitData[0].newDAO = createNewDAO(_newCurator); // Call depth limit reached, etc. if (address(p.splitData[0].newDAO) == 0) throw; // should never happen if (this.balance < sumOfProposalDeposits) throw; p.splitData[0].splitBalance = actualBalance(); p.splitData[0].rewardToken = rewardToken[address(this)]; p.splitData[0].totalSupply = totalSupply; p.proposalPassed = true; } // Move ether and assign new Tokens uint fundsToBeMoved = (balances[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply; if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false) throw; // Assign reward rights to new DAO uint rewardTokenToBeMoved = (balances[msg.sender] * p.splitData[0].rewardToken) / p.splitData[0].totalSupply; uint paidOutToBeMoved = DAOpaidOut[address(this)] * rewardTokenToBeMoved / rewardToken[address(this)]; rewardToken[address(p.splitData[0].newDAO)] += rewardTokenToBeMoved; if (rewardToken[address(this)] < rewardTokenToBeMoved) throw; rewardToken[address(this)] -= rewardTokenToBeMoved; DAOpaidOut[address(p.splitData[0].newDAO)] += paidOutToBeMoved; if (DAOpaidOut[address(this)] < paidOutToBeMoved) throw; DAOpaidOut[address(this)] -= paidOutToBeMoved; // Burn DAO Tokens Transfer(msg.sender, 0, balances[msg.sender]); withdrawRewardFor(msg.sender); // be nice, and get his rewards totalSupply -= balances[msg.sender]; balances[msg.sender] = 0; paidOut[msg.sender] = 0; return true; } function newContract(address _newContract){ if (msg.sender != address(this) || !allowedRecipients[_newContract]) return; // move all ether if (!_newContract.call.value(address(this).balance)()) { throw; } //move all reward tokens rewardToken[_newContract] += rewardToken[address(this)]; rewardToken[address(this)] = 0; DAOpaidOut[_newContract] += DAOpaidOut[address(this)]; DAOpaidOut[address(this)] = 0; } function retrieveDAOReward(bool _toMembers) external noEther returns (bool _success) { DAO dao = DAO(msg.sender); if ((rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) / totalRewardToken < DAOpaidOut[msg.sender]) throw; uint reward = (rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) / totalRewardToken - DAOpaidOut[msg.sender]; if(_toMembers) { if (!DAOrewardAccount.payOut(dao.rewardAccount(), reward)) throw; } else { if (!DAOrewardAccount.payOut(dao, reward)) throw; } DAOpaidOut[msg.sender] += reward; return true; } function getMyReward() noEther returns (bool _success) { return withdrawRewardFor(msg.sender); } function withdrawRewardFor(address _account) noEther internal returns (bool _success) { if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account]) throw; uint reward = (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account]; if (!rewardAccount.payOut(_account, reward)) throw; paidOut[_account] += reward; return true; } function transfer(address _to, uint256 _value) returns (bool success) { if (isFueled && now > closingTime && !isBlocked(msg.sender) && transferPaidOut(msg.sender, _to, _value) && super.transfer(_to, _value)) { return true; } else { throw; } } function transferWithoutReward(address _to, uint256 _value) returns (bool success) { if (!getMyReward()) throw; return transfer(_to, _value); } function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (isFueled && now > closingTime && !isBlocked(_from) && transferPaidOut(_from, _to, _value) && super.transferFrom(_from, _to, _value)) { return true; } else { throw; } } function transferFromWithoutReward( address _from, address _to, uint256 _value ) returns (bool success) { if (!withdrawRewardFor(_from)) throw; return transferFrom(_from, _to, _value); } function transferPaidOut( address _from, address _to, uint256 _value ) internal returns (bool success) { uint transferPaidOut = paidOut[_from] * _value / balanceOf(_from); if (transferPaidOut > paidOut[_from]) throw; paidOut[_from] -= transferPaidOut; paidOut[_to] += transferPaidOut; return true; } function changeProposalDeposit(uint _proposalDeposit) noEther external { if (msg.sender != address(this) || _proposalDeposit > (actualBalance() + rewardToken[address(this)]) / maxDepositDivisor) { throw; } proposalDeposit = _proposalDeposit; } function changeAllowedRecipients(address _recipient, bool _allowed) noEther external returns (bool _success) { if (msg.sender != curator) throw; allowedRecipients[_recipient] = _allowed; AllowedRecipientChanged(_recipient, _allowed); return true; } function isRecipientAllowed(address _recipient) internal returns (bool _isAllowed) { if (allowedRecipients[_recipient] || (_recipient == address(extraBalance) // only allowed when at least the amount held in the // extraBalance account has been spent from the DAO && totalRewardToken > extraBalance.accumulatedInput())) return true; else return false; } function actualBalance() constant returns (uint _actualBalance) { return this.balance - sumOfProposalDeposits; } function minQuorum(uint _value) internal constant returns (uint _minQuorum) { // minimum of 20% and maximum of 53.33% return totalSupply / minQuorumDivisor + (_value * totalSupply) / (3 * (actualBalance() + rewardToken[address(this)])); } function halveMinQuorum() returns (bool _success) { // this can only be called after `quorumHalvingPeriod` has passed or at anytime // by the curator with a delay of at least `minProposalDebatePeriod` between the calls if ((lastTimeMinQuorumMet < (now - quorumHalvingPeriod) || msg.sender == curator) && lastTimeMinQuorumMet < (now - minProposalDebatePeriod)) { lastTimeMinQuorumMet = now; minQuorumDivisor *= 2; return true; } else { return false; } } function createNewDAO(address _newCurator) internal returns (DAO _newDAO) { NewCurator(_newCurator); return daoCreator.createDAO(_newCurator, 0, 0, now + splitExecutionPeriod); } function numberOfProposals() constant returns (uint _numberOfProposals) { // Don't count index 0. It's used by isBlocked() and exists from start return proposals.length - 1; } function getNewDAOAddress(uint _proposalID) constant returns (address _newDAO) { return proposals[_proposalID].splitData[0].newDAO; } function isBlocked(address _account) internal returns (bool) { if (blocked[_account] == 0) return false; Proposal p = proposals[blocked[_account]]; if (now > p.votingDeadline) { blocked[_account] = 0; return false; } else { return true; } } function unblockMe() returns (bool) { return isBlocked(msg.sender); } } |
Contract DAO_Creator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
contract DAO_Creator { function createDAO( address _curator, uint _proposalDeposit, uint _minTokensToCreate, uint _closingTime ) returns (DAO _newDAO) { return new DAO( _curator, DAO_Creator(this), _proposalDeposit, _minTokensToCreate, _closingTime, msg.sender ); } } |
The Application Binary Interface – Full Version
Following is the Application Binary Interface (ABI) for The DAO listed at account 0xbb9bc244d798123fde783fcc1c72d3bb8c189413 on etherscan.io.
1 |
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"open","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"proposalHash","type":"bytes32"},{"name":"proposalDeposit","type":"uint256"},{"name":"newCurator","type":"bool"},{"name":"yea","type":"uint256"},{"name":"nay","type":"uint256"},{"name":"creator","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"minTokensToCreate","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"rewardAccount","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"daoCreator","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"divisor","outputs":[{"name":"divisor","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"extraBalance","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_proposalID","type":"uint256"},{"name":"_transactionData","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"unblockMe","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"totalRewardToken","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"actualBalance","outputs":[{"name":"_actualBalance","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"closingTime","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"allowedRecipients","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferWithoutReward","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_description","type":"string"},{"name":"_transactionData","type":"bytes"},{"name":"_debatingPeriod","type":"uint256"},{"name":"_newCurator","type":"bool"}],"name":"newProposal","outputs":[{"name":"_proposalID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"DAOpaidOut","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minQuorumDivisor","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_newContract","type":"address"}],"name":"newContract","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_allowed","type":"bool"}],"name":"changeAllowedRecipients","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"halveMinQuorum","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"paidOut","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_proposalID","type":"uint256"},{"name":"_newCurator","type":"address"}],"name":"splitDAO","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"DAOrewardAccount","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"proposalDeposit","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numberOfProposals","outputs":[{"name":"_numberOfProposals","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"lastTimeMinQuorumMet","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_toMembers","type":"bool"}],"name":"retrieveDAOReward","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"receiveEther","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"isFueled","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_tokenHolder","type":"address"}],"name":"createTokenProxy","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_proposalID","type":"uint256"}],"name":"getNewDAOAddress","outputs":[{"name":"_newDAO","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_proposalID","type":"uint256"},{"name":"_supportsProposal","type":"bool"}],"name":"vote","outputs":[{"name":"_voteID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"getMyReward","outputs":[{"name":"_success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"rewardToken","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFromWithoutReward","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_proposalDeposit","type":"uint256"}],"name":"changeProposalDeposit","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"blocked","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"curator","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_proposalID","type":"uint256"},{"name":"_recipient","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_transactionData","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"_codeChecksOut","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"privateCreation","outputs":[{"name":"","type":"address"}],"type":"function"},{"inputs":[{"name":"_curator","type":"address"},{"name":"_daoCreator","type":"address"},{"name":"_proposalDeposit","type":"uint256"},{"name":"_minTokensToCreate","type":"uint256"},{"name":"_closingTime","type":"uint256"},{"name":"_privateCreation","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"FuelingToDate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"CreatedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"newCurator","type":"bool"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":true,"name":"voter","type":"address"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"bool"},{"indexed":false,"name":"quorum","type":"uint256"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_newCurator","type":"address"}],"name":"NewCurator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_recipient","type":"address"},{"indexed":false,"name":"_allowed","type":"bool"}],"name":"AllowedRecipientChanged","type":"event"}] |
The Application Binary Interface – Short Version
Following is the ABI for The DAO listed from the page The DAO – Voting.
1 |
[{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"minTokensToCreate","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"closingTime","inputs":[],"constant":true},{"type":"function","outputs":[],"name":"refund","inputs":[],"constant":false},{"type":"function","outputs":[{"type":"address","name":""}],"name":"curator","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":"balance"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner"}],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":"_numberOfProposals"}],"name":"numberOfProposals","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"address","name":""}],"name":"extraBalance","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":""}],"name":"isFueled","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"createTokenProxy","inputs":[{"type":"address","name":"_tokenHolder"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"_voteID"}],"name":"vote","inputs":[{"type":"uint256","name":"_proposalID"},{"type":"bool","name":"_supportsProposal"}],"constant":false},{"type":"event","name":"FuelingToDate","inputs":[{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalAdded","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"address","name":"recipient","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"bool","name":"newCurator","indexed":false},{"type":"string","name":"description","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalTallied","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"bool","name":"result","indexed":false},{"type":"uint256","name":"quorum","indexed":false}],"anonymous":false},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"open","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"proposalHash","type":"bytes32"},{"name":"proposalDeposit","type":"uint256"},{"name":"newCurator","type":"bool"},{"name":"yea","type":"uint256"},{"name":"nay","type":"uint256"},{"name":"creator","type":"address"}],"type":"function"}] |