Created
June 29, 2016 14:45
-
-
Save Pascal66/3433d65c824f18f7e791140541c76487 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
contract TheBarCounter { | |
address private owner; | |
//Stored variables | |
uint private balance = 0; | |
uint private uniqueUsers = 0; | |
uint private usersProfits = 0; | |
uint private rescues = 0; | |
uint private collectedFees = 0; | |
uint private jumpFee = 10; | |
uint private baseMultiplier = 110; | |
uint private maxMultiplier = 200; | |
uint private payoutOrder = 0; | |
uint private rescueRecord = 0; | |
uint timeOfLastDeposit = now; | |
address private hero = 0x0; | |
mapping (address => User) private users; | |
Entry[] private entries; | |
event Jump(address who, uint deposit, uint payout); | |
event Rescue(address who, address saviour, uint payout); | |
event NewHero(address who); | |
//Set owner on contract creation | |
function TheBarCounter() { | |
owner = msg.sender; | |
} | |
modifier onlyowner { if (msg.sender == owner) _ } | |
struct User { | |
uint id; | |
address addr; | |
string nickname; | |
uint rescueCount; | |
uint rescueTokens; | |
} | |
struct Entry { | |
address entryAddress; | |
uint deposit; | |
uint payout; | |
uint tokens; | |
} | |
//Fallback function | |
function() { | |
init(); | |
} | |
function init() private{ | |
//Only deposits >0.1ETH are allowed to join | |
if (msg.value < 100 finney) { | |
return; | |
} | |
jumpIn(); | |
//Prevent cheap trolls from reviving the pit if it dies (death = ~3months without deposits) | |
if (msg.value > 5) | |
timeOfLastDeposit = now; | |
} | |
//Join the pit | |
function jumpIn() private { | |
//Limit deposits to 50ETH | |
uint dValue = 100 finney; | |
if (msg.value > 50 ether) { | |
//Make sure we received the money before refunding the surplus | |
if (this.balance >= balance + collectedFees + msg.value) | |
msg.sender.send(msg.value - 50 ether); | |
dValue = 50 ether; | |
} | |
else { dValue = msg.value; } | |
//Add new users to the users array if he's a new player | |
addNewUser(msg.sender); | |
//Make sure that only up to 5 rescue tokens are spent at a time | |
uint tokensToUse = users[msg.sender].rescueTokens >= 5 ? 5 : users[msg.sender].rescueTokens; | |
uint tokensUsed = 0; | |
//Enforce lower payouts if too many people stuck in the pit | |
uint randMultiplier = rand(50); | |
uint currentEntries = entries.length - payoutOrder; | |
randMultiplier = currentEntries > 15 ? (randMultiplier / 2) : randMultiplier; | |
randMultiplier = currentEntries > 25 ? 0 : randMultiplier; | |
//Incentive to join if the pit is nearly empty (+50% random multiplier) | |
randMultiplier = currentEntries <= 5 && dValue <= 20 ? randMultiplier * 3 / 2 : randMultiplier; | |
//Calculate the optimal amount of rescue tokens to spend | |
while (tokensToUse > 0 && (baseMultiplier + randMultiplier + tokensUsed*10) < maxMultiplier) | |
{ | |
tokensToUse--; | |
tokensUsed++; | |
} | |
uint finalMultiplier = (baseMultiplier + randMultiplier + tokensUsed*10); | |
if (finalMultiplier > maxMultiplier) | |
finalMultiplier = maxMultiplier; | |
//Add new entry to the entries array | |
if (msg.value < 50 ether) | |
entries.push(Entry(msg.sender, msg.value, (msg.value * (finalMultiplier) / 100), tokensUsed)); | |
else | |
entries.push(Entry(msg.sender, 50 ether,((50 ether) * (finalMultiplier) / 100), tokensUsed)); | |
//Trigger jump event | |
if (msg.value < 50 ether) | |
Jump(msg.sender, msg.value, (msg.value * (finalMultiplier) / 100)); | |
else | |
Jump(msg.sender, 50 ether, ((50 ether) * (finalMultiplier) / 100)); | |
users[msg.sender].rescueTokens -= tokensUsed; | |
//Collect fees and update contract balance | |
balance += (dValue * (100 - jumpFee)) / 100; | |
collectedFees += (dValue * jumpFee) / 100; | |
bool saviour = false; | |
//Pay pending entries if the new balance allows for it | |
while (balance > entries[payoutOrder].payout) { | |
saviour = false; | |
uint entryPayout = entries[payoutOrder].payout; | |
uint entryDeposit = entries[payoutOrder].deposit; | |
uint profit = entryPayout - entryDeposit; | |
uint saviourShare = 0; | |
//Give credit & reward for the rescue if the user saved someone else | |
if (users[msg.sender].addr != entries[payoutOrder].entryAddress) | |
{ | |
users[msg.sender].rescueCount++; | |
//Double or triple token bonus if the user is taking a moderate/high risk to help those trapped | |
if (entryDeposit >= 1 ether) { | |
users[msg.sender].rescueTokens += dValue < 20 || currentEntries < 15 ? 1 : 2; | |
users[msg.sender].rescueTokens += dValue < 40 || currentEntries < 25 ? 0 : 1; | |
} | |
saviour = true; | |
} | |
bool isHero = false; | |
isHero = entries[payoutOrder].entryAddress == hero; | |
//Share profit with saviour if the gain is substantial enough and the saviour invested enough (hero exempt) | |
if (saviour && !isHero && profit > 20 * entryDeposit / 100 && profit > 100 finney && dValue >= 5 ether) | |
{ | |
if (dValue < 10 ether) | |
saviourShare = 3 + rand(5); | |
else if (dValue >= 10 ether && dValue < 25 ether) | |
saviourShare = 7 + rand(8); | |
else if (dValue >= 25 ether && dValue < 40 ether) | |
saviourShare = 12 + rand(13); | |
else if (dValue >= 40 ether) | |
saviourShare = rand(50); | |
saviourShare *= profit / 100; | |
msg.sender.send(saviourShare); | |
} | |
uint payout = entryPayout - saviourShare; | |
entries[payoutOrder].entryAddress.send(payout); | |
//Trigger rescue event | |
Rescue(entries[payoutOrder].entryAddress, msg.sender, payout); | |
balance -= entryPayout; | |
usersProfits += entryPayout; | |
rescues++; | |
payoutOrder++; | |
} | |
//Check for new Hero of the Pit | |
if (saviour && users[msg.sender].rescueCount > rescueRecord) | |
{ | |
rescueRecord = users[msg.sender].rescueCount; | |
hero = msg.sender; | |
//Trigger new hero event | |
NewHero(msg.sender); | |
} | |
} | |
//Generate random number between 1 & max | |
uint256 constant private FACTOR = 1157920892373161954235709850086879078532699846656405640394575840079131296399; | |
function rand(uint max) constant private returns (uint256 result){ | |
uint256 factor = FACTOR * 100 / max; | |
uint256 lastBlockNumber = block.number - 1; | |
uint256 hashVal = uint256(block.blockhash(lastBlockNumber)); | |
return uint256((uint256(hashVal) / factor)) % max + 1; | |
} | |
function addNewUser(address Address) private | |
{ | |
if (users[Address].addr == address(0)) | |
{ | |
users[Address].id = ++uniqueUsers; | |
users[Address].addr = Address; | |
users[Address].nickname = 'UnnamedPlayer'; | |
users[Address].rescueCount = 0; | |
users[Address].rescueTokens = 0; | |
} | |
} | |
//Transfer earnings from fees to the owner | |
function collectFees() onlyowner { | |
if (collectedFees == 0) throw; | |
owner.send(collectedFees); | |
collectedFees = 0; | |
} | |
//Contract management | |
function changeOwner(address newOwner) onlyowner { | |
owner = newOwner; | |
} | |
function changeBaseMultiplier(uint multi) onlyowner { | |
if (multi < 110 || multi > 150) throw; | |
baseMultiplier = multi; | |
} | |
function changeMaxMultiplier(uint multi) onlyowner { | |
if (multi < 200 || multi > 300) throw; | |
maxMultiplier = multi; | |
} | |
function changeFee(uint fee) onlyowner { | |
if (fee < 0 || fee > 10) throw; | |
jumpFee = fee; | |
} | |
//JSON functions | |
function setNickname(string name) { | |
addNewUser(msg.sender); | |
if (bytes(name).length >= 2 && bytes(name).length <= 16) | |
users[msg.sender].nickname = name; | |
} | |
function currentBalance() constant returns (uint pitBalance, string info) { | |
pitBalance = balance / 1 finney; | |
info = 'The balance of the pit in Finneys (contract balance minus fees).'; | |
} | |
function heroOfThePit() constant returns (address theHero, string nickname, uint peopleSaved, string info) { | |
theHero = hero; | |
nickname = users[theHero].nickname; | |
peopleSaved = rescueRecord; | |
info = 'The current rescue record holder. All hail!'; | |
} | |
function userName(address Address) constant returns (string nickname) { | |
nickname = users[Address].nickname; | |
} | |
function totalRescues() constant returns (uint rescueCount, string info) { | |
rescueCount = rescues; | |
info = 'The number of times that people have been rescued from the pit (aka the number of times people made a profit).'; | |
} | |
function multipliers() constant returns (uint BaseMultiplier, uint MaxMultiplier, string info) { | |
BaseMultiplier = baseMultiplier; | |
MaxMultiplier = maxMultiplier; | |
info = 'The multipliers applied to all deposits: the final multiplier is a random number between the multpliers shown divided by 100. By default x1.1~x1.5 (up to x2 if rescue tokens are used, granting +0.1 per token). It determines the amount of money you will get when rescued (a saviour share might be deducted).'; | |
} | |
function pitFee() constant returns (uint feePercentage, string info) { | |
feePercentage = jumpFee; | |
info = 'The fee percentage applied to all deposits. It can change to speed payouts (max 10%).'; | |
} | |
function nextPayoutGoal() constant returns (uint finneys, string info) { | |
finneys = (entries[payoutOrder].payout - balance) / 1 finney; | |
info = 'The amount of Finneys (Ethers * 1000) that need to be deposited for the next payout to be executed.'; | |
} | |
function unclaimedFees() constant returns (uint ethers, string info) { | |
ethers = collectedFees / 1 ether; | |
info = 'The amount of Ethers obtained through fees that have not yet been collected by the owner.'; | |
} | |
function totalEntries() constant returns (uint count, string info) { | |
count = entries.length; | |
info = 'The number of times that people have jumped into the pit.'; | |
} | |
function totalUsers() constant returns (uint users, string info) { | |
users = uniqueUsers; | |
info = 'The number of unique users that have joined the pit.'; | |
} | |
function awaitingPayout() constant returns (uint count, string info) { | |
count = entries.length - payoutOrder; | |
info = 'The number of people waiting to be saved.'; | |
} | |
function entryDetails(uint index) constant returns (address user, string nickName, uint deposit, uint payout, uint tokensUsed, string info) | |
{ | |
if (index <= entries.length) { | |
user = entries[index].entryAddress; | |
nickName = users[entries[index].entryAddress].nickname; | |
deposit = entries[index].deposit / 1 finney; | |
payout = entries[index].payout / 1 finney; | |
tokensUsed = entries[index].tokens; | |
info = 'Entry info: user address, name, expected payout in Finneys (approximate), rescue tokens used.'; | |
} | |
} | |
function userId(address user) constant returns (uint id, string info) { | |
id = users[user].id; | |
info = 'The id of the user, represents the order in which he first joined the pit.'; | |
} | |
function userTokens(address user) constant returns (uint tokens, string info) { | |
tokens = users[user].addr != address(0x0) ? users[user].rescueTokens : 0; | |
info = 'The number of Rescue Tokens the user has. Tokens are awarded when your deposits save people, and used automatically on your next deposit. They provide a 0.1 multiplier increase per token. (+0.5 max)'; | |
} | |
function userRescues(address user) constant returns(uint rescueCount, string info) { | |
rescueCount = users[user].addr != address(0x0) ? users[user].rescueCount : 0; | |
info = 'The number of times the user has rescued someone from the pit.'; | |
} | |
function userProfits() constant returns(uint profits, string info) { | |
profits = usersProfits / 1 finney; | |
info = 'The combined earnings of all users in Finney.'; | |
} | |
//Destroy the contract after ~3 months of inactivity at the owner's discretion | |
function recycle() onlyowner | |
{ | |
if (now >= timeOfLastDeposit + 10 weeks) | |
{ | |
//Refund the current balance | |
if (balance > 0) | |
{ | |
entries[0].entryAddress.send(balance); | |
} | |
//Destroy the contract | |
selfdestruct(owner); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment