Firstblood is a “Decentralized eSports Platform Based on Smart Contracts”. This is just a quick look into the smart contract behind the crowdfunding.
Update 00:08 Sep 26 2016 AEST – The crowdsale hit the maximum cap limit of raising 465,313 ETH within the first 8 minutes of commencement of the crowdsale.
Update 22:10 Oct 09 2016 UTC – The crowd sale was over in 9 minutes (a few < 1 ETH transactions shortly after) - See Error sending transaction to the First Blood crowd sale contract. Trading of First Blood 1ST tokens will be enabled around Dec 23 2016 – see When can First Blood 1ST tokens be transferred?.
Resources
- firstblood.io
- https://firstblood.io/sale/#
- Firstblood’s github repository with the main FirstBloodToken.sol smart contract
- Verified contract on etherscan.io – 0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7
- Decentralized eSports Platform FirstBlood Raises $5.5M in Minutes
Verified Smart Contract From Etherscan.io
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
/** * Overflow aware uint math functions. * * Inspired by https://github.com/MakerDAO/maker-otc/blob/master/contracts/simple_market.sol */ contract SafeMath { //internals function safeMul(uint a, uint b) internal returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function safeSub(uint a, uint b) internal returns (uint) { assert(b <= a); return a - b; } function safeAdd(uint a, uint b) internal returns (uint) { uint c = a + b; assert(c>=a && c>=b); return c; } function assert(bool assertion) internal { if (!assertion) throw; } } /** * ERC 20 token * * https://github.com/ethereum/EIPs/issues/20 */ contract Token { /// @return total amount of tokens function totalSupply() constant returns (uint256 supply) {} /// @param _owner The address from which the balance will be retrieved /// @return The balance function balanceOf(address _owner) constant returns (uint256 balance) {} /// @notice send `_value` token to `_to` from `msg.sender` /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return Whether the transfer was successful or not function transfer(address _to, uint256 _value) returns (bool success) {} /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` /// @param _from The address of the sender /// @param _to The address of the recipient /// @param _value The amount of token to be transferred /// @return Whether the transfer was successful or not function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {} /// @notice `msg.sender` approves `_addr` to spend `_value` tokens /// @param _spender The address of the account able to transfer the tokens /// @param _value The amount of wei to be approved for transfer /// @return Whether the approval was successful or not function approve(address _spender, uint256 _value) returns (bool success) {} /// @param _owner The address of the account owning tokens /// @param _spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens allowed to spent function allowance(address _owner, address _spender) constant returns (uint256 remaining) {} event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); } /** * ERC 20 token * * https://github.com/ethereum/EIPs/issues/20 */ contract StandardToken is Token { /** * Reviewed: * - Interger overflow = OK, checked */ function transfer(address _to, uint256 _value) returns (bool success) { //Default assumes totalSupply can't be over max (2^256 - 1). //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. //Replace the if with this one instead. if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { //if (balances[msg.sender] >= _value && _value > 0) { balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { return false; } } function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { //same as above. Replace this line with the following if you want to protect against wrapping uints. if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } else { return false; } } function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } function approve(address _spender, uint256 _value) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } mapping(address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; uint256 public totalSupply; } /** * First blood crowdsale crowdsale contract. * * Security criteria evaluated against http://ethereum.stackexchange.com/questions/8551/methodological-security-review-of-a-smart-contract * * */ contract FirstBloodToken is StandardToken, SafeMath { string public name = "FirstBlood Token"; string public symbol = "1ST"; uint public decimals = 18; uint public startBlock; //crowdsale start block (set in constructor) uint public endBlock; //crowdsale end block (set in constructor) // Initial founder address (set in constructor) // All deposited ETH will be instantly forwarded to this address. // Address is a multisig wallet. address public founder = 0x0; // signer address (for clickwrap agreement) // see function() {} for comments address public signer = 0x0; uint public etherCap = 465313 * 10**18; //max amount raised during crowdsale (5.5M USD worth of ether will be measured with a moving average market price at beginning of the crowdsale) uint public transferLockup = 370285; //transfers are locked for this many blocks after endBlock (assuming 14 second blocks, this is 2 months) uint public founderLockup = 2252571; //founder allocation cannot be created until this many blocks after endBlock (assuming 14 second blocks, this is 1 year) uint public bountyAllocation = 2500000 * 10**18; //2.5M tokens allocated post-crowdsale for the bounty fund uint public ecosystemAllocation = 5 * 10**16; //5% of token supply allocated post-crowdsale for the ecosystem fund uint public founderAllocation = 10 * 10**16; //10% of token supply allocated post-crowdsale for the founder allocation bool public bountyAllocated = false; //this will change to true when the bounty fund is allocated bool public ecosystemAllocated = false; //this will change to true when the ecosystem fund is allocated bool public founderAllocated = false; //this will change to true when the founder fund is allocated uint public presaleTokenSupply = 0; //this will keep track of the token supply created during the crowdsale uint public presaleEtherRaised = 0; //this will keep track of the Ether raised during the crowdsale bool public halted = false; //the founder address can set this to true to halt the crowdsale due to emergency event Buy(address indexed sender, uint eth, uint fbt); event Withdraw(address indexed sender, address to, uint eth); event AllocateFounderTokens(address indexed sender); event AllocateBountyAndEcosystemTokens(address indexed sender); function FirstBloodToken(address founderInput, address signerInput, uint startBlockInput, uint endBlockInput) { founder = founderInput; signer = signerInput; startBlock = startBlockInput; endBlock = endBlockInput; } /** * Security review * * - Integer overflow: does not apply, blocknumber can't grow that high * - Division is the last operation and constant, should not cause issues * - Price function plotted https://github.com/Firstbloodio/token/issues/2 */ function price() constant returns(uint) { if (block.number>=startBlock && block.number<startBlock+250) return 170; //power hour if (block.number<startBlock || block.number>endBlock) return 100; //default price return 100 + 4*(endBlock - block.number)/(endBlock - startBlock + 1)*67/4; //crowdsale price } // price() exposed for unit tests function testPrice(uint blockNumber) constant returns(uint) { if (blockNumber>=startBlock && blockNumber<startBlock+250) return 170; //power hour if (blockNumber<startBlock || blockNumber>endBlock) return 100; //default price return 100 + 4*(endBlock - blockNumber)/(endBlock - startBlock + 1)*67/4; //crowdsale price } // Buy entry point function buy(uint8 v, bytes32 r, bytes32 s) { buyRecipient(msg.sender, v, r, s); } /** * Main token buy function. * * Security review * * - Integer math: ok - using SafeMath * * - halt flag added - ok * * Applicable tests: * * - Test halting, buying, and failing * - Test buying on behalf of a recipient * - Test buy * - Test unhalting, buying, and succeeding * - Test buying after the sale ends * */ function buyRecipient(address recipient, uint8 v, bytes32 r, bytes32 s) { bytes32 hash = sha256(msg.sender); if (ecrecover(hash,v,r,s) != signer) throw; if (block.number<startBlock || block.number>endBlock || safeAdd(presaleEtherRaised,msg.value)>etherCap || halted) throw; uint tokens = safeMul(msg.value, price()); balances[recipient] = safeAdd(balances[recipient], tokens); totalSupply = safeAdd(totalSupply, tokens); presaleEtherRaised = safeAdd(presaleEtherRaised, msg.value); if (!founder.call.value(msg.value)()) throw; //immediately send Ether to founder address Buy(recipient, msg.value, tokens); } /** * Set up founder address token balance. * * allocateBountyAndEcosystemTokens() must be calld first. * * Security review * * - Integer math: ok - only called once with fixed parameters * * Applicable tests: * * - Test bounty and ecosystem allocation * - Test bounty and ecosystem allocation twice * */ function allocateFounderTokens() { if (msg.sender!=founder) throw; if (block.number <= endBlock + founderLockup) throw; if (founderAllocated) throw; if (!bountyAllocated || !ecosystemAllocated) throw; balances[founder] = safeAdd(balances[founder], presaleTokenSupply * founderAllocation / (1 ether)); totalSupply = safeAdd(totalSupply, presaleTokenSupply * founderAllocation / (1 ether)); founderAllocated = true; AllocateFounderTokens(msg.sender); } /** * Set up founder address token balance. * * Set up bounty pool. * * Security review * * - Integer math: ok - only called once with fixed parameters * * Applicable tests: * * - Test founder token allocation too early * - Test founder token allocation on time * - Test founder token allocation twice * */ function allocateBountyAndEcosystemTokens() { if (msg.sender!=founder) throw; if (block.number <= endBlock) throw; if (bountyAllocated || ecosystemAllocated) throw; presaleTokenSupply = totalSupply; balances[founder] = safeAdd(balances[founder], presaleTokenSupply * ecosystemAllocation / (1 ether)); totalSupply = safeAdd(totalSupply, presaleTokenSupply * ecosystemAllocation / (1 ether)); balances[founder] = safeAdd(balances[founder], bountyAllocation); totalSupply = safeAdd(totalSupply, bountyAllocation); bountyAllocated = true; ecosystemAllocated = true; AllocateBountyAndEcosystemTokens(msg.sender); } /** * Emergency Stop crowdsale. * * Applicable tests: * * - Test unhalting, buying, and succeeding */ function halt() { if (msg.sender!=founder) throw; halted = true; } function unhalt() { if (msg.sender!=founder) throw; halted = false; } /** * Change founder address (where crowdsale ETH is being forwarded). * * Applicable tests: * * - Test founder change by hacker * - Test founder change * - Test founder token allocation twice * */ function changeFounder(address newFounder) { if (msg.sender!=founder) throw; founder = newFounder; } /** * ERC 20 Standard Token interface transfer function * * Prevent transfers until freeze period is over. * * Applicable tests: * * - Test restricted early transfer * - Test transfer after restricted period */ function transfer(address _to, uint256 _value) returns (bool success) { if (block.number <= endBlock + transferLockup && msg.sender!=founder) throw; return super.transfer(_to, _value); } /** * ERC 20 Standard Token interface transfer function * * Prevent transfers until freeze period is over. */ function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (block.number <= endBlock + transferLockup && msg.sender!=founder) throw; return super.transferFrom(_from, _to, _value); } /** * Do not allow direct deposits. * * All crowdsale depositors must have read the legal agreement. * This is confirmed by having them signing the terms of service on the website. * They give their crowdsale Ethereum source address on the website. * Website signs this address using crowdsale private key (different from founders key). * buy() takes this signature as input and rejects all deposits that do not have * signature you receive after reading terms of service. * */ function() { throw; } } |