Contract Address Details

0x285a8c43a3165B21D19B51D7B27f11207a5E9D2d

EduLottery Last Balance Update: Block #6328230
Created by 0x1010–80a4b2 at 0x895c–65ab49

Balance

0 ATS

Fetching tokens...

Contract name:
EduLottery




Optimization enabled
true
Compiler version
v0.5.7+commit.6da8b019





Contract source code

/**
* Submitted for verification at blockscout.com on 2019-07-08 11:06:43.917694Z
*/
pragma solidity >=0.5.7 <0.6.0;
/// @title A simple blocktime based lottery for educational purposes
/// @author Thomas Haller <tha@lab10.coop>, Dietmar Hofer <dho@lab10.coop>
///
/// @notice
/// This contract implements a reusable lottery which can be played by sending simple transactions,
/// thus doesn't depend on the players using a specific DApp.
/// The duration of rounds and pauses in between are configured on deployment, expressed in number of blocks.
/// A new round is started by the first incoming transaction after deployment or after a pause.
/// The bid amount is constant per round and either configured for all rounds on deployment or - if not done so -
/// set per round by the transaction starting that round.
/// Once a round is over, the first following transaction triggers payout of the collected funds to the winner,
/// the winner being determined in a raffle based on a simple block hash based RNG (see inline comments).
contract EduLottery {
// positive number indicates the block number when the current round got started.
// negative number indicates the minimal block number at which the next round can be started (implying that the lottery is currently paused).
int256 public roundStartBlock;
// timeframe in blocks a round will take.
uint256 public roundRuntimeInBlocks;
// minimum timeframe in blocks the lottery is paused after a round.
// During that timeframe, incoming transactions are rejected.
uint256 public minPauseRuntimeInBlocks;
// indicates if the bidAmount is set per round (defined by the opening tx) or not.
bool public dynamicBidAmount;
// the bid amount in wei to be transferred by participants of the current round.
uint256 public bidAmount;
// stores a list of all players who participated in the current round by transferring <bidAmount>.
// those sending multiple transactions will be listed in this array multiple times.
// due to gas economics, the array is not cleared between rounds, thus <nrPlayers> holds the current length.
address payable[] public players;
// current size of the <players> array. Is always less or equal to players.length.
uint256 nrPlayers;
event RoundStarted(uint256 bidAmount);
event Bid(address player);
// the RoundPayout event also indicates the end of a lottery round and the start of a pause period.
event RoundPayout(address winner, uint256 amount);
/// create a new instance EduLottery
/// @param _bidAmount amount in wei required for bids, 0 (zero) means the bid amount is not pre-set, but set per round
/// @param _roundRuntimeInBlocks Lottery runtime in blocks for a round.
/// @param _minPauseRuntimeInBlocks pause time after a lottery round.
constructor(uint256 _bidAmount, uint256 _roundRuntimeInBlocks, uint256 _minPauseRuntimeInBlocks) public {
require(_roundRuntimeInBlocks > 0, 'roundRuntimeInBlocks must not bet null.');
roundRuntimeInBlocks = _roundRuntimeInBlocks;
bidAmount = _bidAmount;
minPauseRuntimeInBlocks = _minPauseRuntimeInBlocks;
dynamicBidAmount = (_bidAmount == 0);
//this allows the first round to be started immediately after deployment.
roundStartBlock = (int256)(block.number) * -1;
}
modifier lotteryIsNotPaused {
require(roundStartBlock > 0 || - roundStartBlock < (int256)(block.number), 'Lottery is currently paused');
_;
}
/// Fallback function handling all interaction, triggered by simple send transactions.
/// starts a new lottery round if there is currently none running.
/// if a round is running, it places a bid if the correct amount is provided.
/// if a round is over, selects a winner and pays out collected funds.
/// if paused (mininum pause timeframe after a round not reached), rejects all transactions.
/// @notice senders need to provide enough gas. 21000 - as often set by default - will not suffice.
function () external payable lotteryIsNotPaused {
if(roundStartBlock < 0) {
// a new round is started
roundStartBlock = (int256)(block.number);
if (dynamicBidAmount) {
bidAmount = msg.value;
}
emit RoundStarted(bidAmount);
}
// roundStartBlock cannot be negative when getting here
if(block.number >= uint256(roundStartBlock) + roundRuntimeInBlocks) {
// a round is over. Pick a winner, pay out, end the round (transitioning to paused state)
if (msg.value > 0) {
// since the round was already over before this tx, sender funds (if any) are immediately returned
msg.sender.transfer(msg.value);
}
roundStartBlock = -int256(block.number + minPauseRuntimeInBlocks);
payout(getRandomNumber(nrPlayers));
nrPlayers = 0;
} else {
// a round is running. Register the bid if the amount is correct
require(msg.value == bidAmount, "wrong amount!");
if(nrPlayers == players.length) {
// array needs to grow in order to accomodate this bid, thus using push()
players.push(msg.sender);
} else {
players[nrPlayers] = msg.sender;
}
nrPlayers++;
emit Bid(msg.sender);
}
}
/// Pays out the collected funds to the given player
/// @param luckyPlayerIndex valid index in the players array
/// @dev Re-entrancy issues (see https://solidity.readthedocs.io/en/latest/security-considerations.html#re-entrancy)
/// should be avoided by the caller (should call this method after having done everything else). However even in case of
/// re-entrancy, not much could go wrong as the winner gets all funds on first transfer anyway.
function payout(uint256 luckyPlayerIndex) internal {
address payable luckyPlayer = players[luckyPlayerIndex];
uint256 amount = address(this).balance;
luckyPlayer.transfer(amount);
emit RoundPayout(luckyPlayer, amount);
}
/// get a random number.
/// @param range upper range bound for the retrieved random number.
/// @return a number calculated out of the last blockhash.
/// @notice Assumption for this RNG to have any meaning: block author doesn't care about it.
/// Context: https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract
/// (PS: in a PoA network, it's much easier for the block author to manipulate the block hash than it is in a PoW network)
/// Also, if nobody else triggers payout, smart players could wait for a "favourable" block...
function getRandomNumber(uint256 range) public view returns(uint256) {
// hash of last block is the randomness source (hash of current block not yet known at the time of execution)
return uint256(blockhash(block.number-1)) % range;
}
}

Contract ABI

[{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"int256","name":""}],"name":"roundStartBlock","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"roundRuntimeInBlocks","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"minPauseRuntimeInBlocks","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"bidAmount","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getRandomNumber","inputs":[{"type":"uint256","name":"range"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"dynamicBidAmount","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"players","inputs":[{"type":"uint256","name":""}],"constant":true},{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[{"type":"uint256","name":"_bidAmount"},{"type":"uint256","name":"_roundRuntimeInBlocks"},{"type":"uint256","name":"_minPauseRuntimeInBlocks"}]},{"type":"fallback","stateMutability":"payable","payable":true},{"type":"event","name":"RoundStarted","inputs":[{"type":"uint256","name":"bidAmount","indexed":false}],"anonymous":false},{"type":"event","name":"Bid","inputs":[{"type":"address","name":"player","indexed":false}],"anonymous":false},{"type":"event","name":"RoundPayout","inputs":[{"type":"address","name":"winner","indexed":false},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false}]
            

Contract Byte Code

0x60806040526004361061008d576000357c010000000000000000000000000000000000000000000000000000000090048063aec2393b1161006b578063aec2393b14610385578063b37217a41461039a578063b3a64887146103c4578063f71d96cb146103ed5761008d565b80630be19a04146103345780630f2075281461035b57806319f0606614610370575b6000805413806100a1575043600054600003125b61010c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4c6f74746572792069732063757272656e746c79207061757365640000000000604482015290519081900360640190fd5b600080541215610160574360005560035460ff161561012a57346004555b60045460408051918252517f33a701182892fd888ed152ca2ac23771a32e814469b7cd255965471e1af3a6599181900360200190a15b6001546000540143106101cc5734156101a15760405133903480156108fc02916000818181858888f1935050505015801561019f573d6000803e3d6000fd5b505b60025443016000036000819055506101c26101bd600654610440565b610457565b6000600655610332565b600454341461023c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f77726f6e6720616d6f756e742100000000000000000000000000000000000000604482015290519081900360640190fd5b600554600654141561029c57600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001805473ffffffffffffffffffffffffffffffffffffffff1916331790556102f5565b336005600654815481106102ac57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6006805460010190556040805133815290517f417bd604b82c3fa277680a27ba0a50c43772789bdafa274544181c0a2b1e8ab59181900360200190a15b005b34801561034057600080fd5b50610349610511565b60408051918252519081900360200190f35b34801561036757600080fd5b50610349610517565b34801561037c57600080fd5b5061034961051d565b34801561039157600080fd5b50610349610523565b3480156103a657600080fd5b50610349600480360360208110156103bd57600080fd5b5035610440565b3480156103d057600080fd5b506103d9610529565b604080519115158252519081900360200190f35b3480156103f957600080fd5b506104176004803603602081101561041057600080fd5b5035610532565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6000816000194301408161045057fe5b0692915050565b60006005828154811061046657fe5b600091825260208220015460405173ffffffffffffffffffffffffffffffffffffffff9091169250303191839183156108fc0291849190818181858888f193505050501580156104ba573d6000803e3d6000fd5b506040805173ffffffffffffffffffffffffffffffffffffffff841681526020810183905281517f991a69517cbbe726a496d7c8c30177326e67558de2ef235c5762ea4b2a9601d9929181900390910190a1505050565b60005481565b60015481565b60025481565b60045481565b60035460ff1681565b6005818154811061053f57fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690508156fea165627a7a72305820f9c3be21bb9df629001b29117f1a22a4338c772b672444854b0e9a0ae705e25c0029