pragma solidity ^0.4.20;
/*
Author : RNDM (Discord RNDM#3033)
Write me if you need coding service
My Ethereum address : 0x13373FEdb7f8dF156E5718303897Fae2d363Cc96
Description tl;dr :
Simple trustless lottery with entries
After the contract reaches a certain amount of ethereum or when the owner calls "payWinnerManually()"
a winner gets calculated/drawed and paid out (100%, no Dev or Owner fee).
*/
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
}
contract Lottery is Ownable {
// The tokens can never be stolen
modifier secCheck(address aContract) {
require(aContract != address(contractCall));
_;
}
// When this is active no one is able to participate
modifier restriction() {
require(!_restriction);
_;
}
/**
* Events
*/
event BoughtTicket(uint256 amount, address customer, uint yourEntry);
event WinnerPaid(uint256 amount, address winner);
/**
* Data
*/
_Contract contractCall; // a reference to the contract
address[] public entries; // array with entries
uint256 entryCounter; // counter for the entries
uint256 public automaticThreshold; // automatic Threshold to close the lottery and pay the winner
uint256 public ticketPrice = 10 finney; // the price per lottery ticket (0.01 eth)
bool public _restriction; // check restriction modifier
constructor() public {
contractCall = _Contract(0x05215FCE25902366480696F38C3093e31DBCE69A);
_restriction = true;
automaticThreshold = 100; // 100 tickets
ticketPrice = 10 finney; // 10 finney = 0.01 eth
entryCounter = 0;
}
// If you send money directly to the contract its like a donation
function() payable public {
}
function buyTickets() restriction() payable public {
//You have to send at least ticketPrice to get one entry
require(msg.value >= ticketPrice);
address customerAddress = msg.sender;
//Use deposit to purchase _Contract tokens
contractCall.buy.value(msg.value)(customerAddress);
// add customer to the entry list
if (entryCounter == (entries.length)) {
entries.push(customerAddress);
}
else {
entries[entryCounter] = customerAddress;
}
// increment the entry counter
entryCounter++;
//fire event
emit BoughtTicket(msg.value, msg.sender, entryCounter);
//Automatic Treshhold, checks if the always incremented entryCounter reached the threshold
if(entryCounter >= automaticThreshold) {
// withdraw + sell all tokens.
contractCall.exit();
//payout winner & start from beginning
payWinner();
}
}
// Other functions
function PRNG() internal view returns (uint256) {
uint256 initialize1 = block.timestamp;
uint256 initialize2 = uint256(block.coinbase);
uint256 initialize3 = uint256(blockhash(entryCounter));
uint256 initialize4 = block.number;
uint256 initialize5 = block.gaslimit;
uint256 initialize6 = block.difficulty;
uint256 calc1 = uint256(keccak256(abi.encodePacked((initialize1 * 5),initialize5,initialize6)));
uint256 calc2 = 1-calc1;
int256 ov = int8(calc2);
uint256 calc3 = uint256(sha256(abi.encodePacked(initialize1,ov,initialize3,initialize4)));
uint256 PRN = uint256(keccak256(abi.encodePacked(initialize1,calc1,initialize2,initialize3,calc3)))%(entryCounter);
return PRN;
}
// Choose a winner and pay him
function payWinner() internal returns (address) {
uint256 balance = address(this).balance;
uint256 number = PRNG(); // generates a pseudorandom number
address winner = entries[number]; // choose the winner with the pseudorandom number
winner.transfer(balance); // payout winner
entryCounter = 0; // Zero entries again => Lottery resetted
emit WinnerPaid(balance, winner);
return winner;
}
/*
If you plan to use this contract for your projects
be a man of honor and do not change or delete this function
*/
function donateToDev() payable public {
address developer = 0x13373FEdb7f8dF156E5718303897Fae2d363Cc96;
developer.transfer(msg.value);
}
//Number of tokens currently in the Lottery pool
function myTokens() public view returns(uint256) {
return contractCall.myTokens();
}
//Amount of dividends currently in the Lottery pool
function myDividends() public view returns(uint256) {
return contractCall.myDividends(true);
}
/**
* Administrator functions
*/
//Disable the buy restriction
function disableRestriction() onlyOwner() public {
_restriction = false;
}
// change the Threshold
function changeThreshold(uint newThreshold) onlyOwner() public {
// Owner is only able to change the threshold when no one bought (otherwise it would be unfair)
require(entryCounter == 0);
automaticThreshold = newThreshold;
}
function changeTicketPrice(uint newticketPrice) onlyOwner() public {
// Owner is only able to change the ticket price when no one bought (otherwise it would be unfair)
require(entryCounter == 0);
ticketPrice = newticketPrice;
}
// Admin can call the payWinner (ends lottery round & starts a new one) if it takes too long to reach the threshold
function payWinnerManually() public onlyOwner() returns (address) {
address winner = payWinner();
return winner;
}
// check special functions
function imAlive() public onlyOwner() {
inactivity = 1;
}
/**
* Special functions
*/
/*
* In case the threshold is way too high and the owner/admin disappeared (inactive for 30days)
* Everyone can call this function then the timestamp gets saved
* after 30 days of owner-inactivity someone can call the function again and calls payWinner with it
*/
uint inactivity = 1;
function adminIsDead() public {
if (inactivity == 1) {
inactivity == block.timestamp;
}
else {
uint256 inactivityThreshold = (block.timestamp - (30 days));
assert(inactivityThreshold < block.timestamp);
if (inactivity < inactivityThreshold) {
inactivity = 1;
payWinnerManually2();
}
}
}
function payWinnerManually2() internal {
payWinner();
}
/* A trap door for when someone sends tokens other than the intended ones so the overseers
can decide where to send them. (credit: Doublr Contract) */
function returnAnyERC20Token(address tokenAddress, address tokenOwner, uint tokens) public onlyOwner() secCheck(tokenAddress) returns (bool success) {
return ERC20Interface(tokenAddress).transfer(tokenOwner, tokens);
}
}
//Need to ensure this contract can send tokens to people
contract ERC20Interface
{
function transfer(address to, uint256 tokens) public returns (bool success);
}
// Interface to actually call contract functions of e.g. REV1
contract _Contract
{
function buy(address) public payable returns(uint256);
function exit() public;
function myTokens() public view returns(uint256);
function myDividends(bool) public view returns(uint256);
}
{
"compilationTarget": {
"Lottery.sol": "Lottery"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"_restriction","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"adminIsDead","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ticketPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"imAlive","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"myDividends","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newticketPrice","type":"uint256"}],"name":"changeTicketPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newThreshold","type":"uint256"}],"name":"changeThreshold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"automaticThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"myTokens","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokenOwner","type":"address"},{"name":"tokens","type":"uint256"}],"name":"returnAnyERC20Token","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"entries","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"buyTickets","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"disableRestriction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"payWinnerManually","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"donateToDev","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"customer","type":"address"},{"indexed":false,"name":"yourEntry","type":"uint256"}],"name":"BoughtTicket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"winner","type":"address"}],"name":"WinnerPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]