Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-gambler - Lab 02 submission by Sangbin Cho, Sanjay Goel and Jai Kumar #4

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Betting Contract/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Lab submission by team - Sangbin Cho, Sanjay Goel and Jai Kumar
# Betting Contract
Build a simple betting contract that rewards correct guesses of outcomes. This contract utilizes aspects of game theory; you may want to use a paper and pen to make a note of possible game states that may arise.

Expand Down
249 changes: 203 additions & 46 deletions Betting Contract/contracts/betting.sol
Original file line number Diff line number Diff line change
@@ -1,70 +1,227 @@
pragma solidity 0.4.19;
/*
* @title Betting contract
* @authors: Sangbin Cho, Sanjay Goel and Jai Kumar
*
* @supports multiple gamblers
*/

pragma solidity 0.4.20;

contract Betting {
/* Constructor function, where owner and outcomes are set */
function Betting(uint[] _outcomes) public {
}

/* Fallback function */
function() public payable {
revert();
// Representing a single gambler.
struct Gambler {
bool hasBetted; // if true, that gambler already betted to prevent multiple betting
uint expectedOutcome; // index of the selected outcome
uint bettingWei; // wei amount putting for this bet
}

/* Standard state variables */
address public owner;
address public gamblerA;
address public gamblerB;
// Representing an outcome
uint[] public outcomes;

// Keeping balances information
mapping (address => uint256) public balances;

//Events
event oracleSelected();
event bettingStarted();
event invalidBettingOutcomeEntered(address gAddr);
event bettingEnded();
event winnerAnnounced(uint winningOutcomeNumber);
event gamblerBetted(address gambler);
event rewardDispered();
event LogTransfer(address sender, address to, uint amount);

// Flag to know if betting is open yet
bool public bettingIsOpen = false;

// Winning outcome
uint winningOutcome;

// Oracle
address public oracle;

/* Structs are custom data structures with self-defined parameters */
struct Bet {
uint outcome;
uint amount;
bool initialized;
// Owner
address public contractOwner;

// This declares a state variable that
// stores a `Gambler` struct for each possible address.
mapping(address => Gambler) public gamblers;

//Bet counter for gamblers
mapping (address => uint) public betCounter;

//Number of total bets
uint public numberOfBets;

//Total money poured in
uint totalBetAmount;

// Mapping for (bets outcome => gambler address)
mapping (uint => address[]) public bets;

// Mapping for (bets outcome => total amount poured in for particular outcomes)
mapping (uint => uint) public outcomesAmount;

modifier onlyOwner {
require(msg.sender == contractOwner);
_;
}

modifier onlyOracle {
require(msg.sender == oracle);
_;
}

modifier gamblerOnly() {
require(msg.sender != oracle);
require(msg.sender != contractOwner);
_;
}

modifier canBetOnlyOnce() {
require(betCounter[msg.sender] == 0);
_;
}

/* Keep track of every gambler's bet */
mapping (address => Bet) bets;
/* Keep track of every player's winnings (if any) */
mapping (address => uint) winnings;
/* Keep track of all outcomes (maps index to numerical outcome) */
mapping (uint => uint) public outcomes;
// Create a new betting to choose one of `outcomeNumbers`.
function Betting(uint[] outcomeNumbers) public {
contractOwner = msg.sender;
//Make sure there is at least two outcomes, else betting has no point
require(outcomeNumbers.length > 1);

/* Add any events you think are necessary */
event BetMade(address gambler);
event BetClosed();
for(uint i = 0; i < outcomeNumbers.length; i++) {
// Make sure these outcome numbers are unique - no duplicates
if (!_check_uint_item_exists_in_array(outcomeNumbers[i], outcomes)) {
outcomes.push(outcomeNumbers[i]);
}
}
}

// Choose oracle
function chooseOracle(address oracleAddress) public onlyOwner {
//Make sure bet has not started yet
require(!bettingIsOpen && winningOutcome == 0 && oracle == 0X0);

/* Uh Oh, what are these? */
modifier ownerOnly() {_;}
modifier oracleOnly() {_;}
modifier outcomeExists(uint outcome) {_;}
// Make sure owner cannot select himself/herself as the oracle
require(oracleAddress != contractOwner);

/* Owner chooses their trusted Oracle */
function chooseOracle(address _oracle) public ownerOnly() returns (address) {
oracle = oracleAddress;
oracleSelected();
bettingIsOpen = true; // Open betting for gambler
bettingStarted();
}

/* Gamblers place their bets, preferably after calling checkOutcomes */
function makeBet(uint _outcome) public payable returns (bool) {
// End betting
function endBetting() public onlyOwner {
bettingIsOpen = false;
bettingEnded();
}

/* The oracle chooses which outcome wins */
function makeDecision(uint _outcome) public oracleOnly() outcomeExists(_outcome) {
// Make a bet
function makeABet(uint bettedOutcome) public payable gamblerOnly canBetOnlyOnce {
address gamblerAddress = msg.sender;

// Make sure gambling is open first
require(bettingIsOpen);

// weiAmount must be greater than 0
require(msg.value > 0);

// Make sure betted Outcome is among the defined outcomes
if(!_check_uint_item_exists_in_array(bettedOutcome, outcomes)){
invalidBettingOutcomeEntered(gamblerAddress);
revert();
}

//Finally record the betting
gamblers[gamblerAddress] = Gambler({
hasBetted: true,
expectedOutcome: bettedOutcome,
bettingWei: msg.value
});

//Increment bet amount
totalBetAmount += msg.value;

// Save it for iterating at money dispering time
bets[bettedOutcome].push(gamblerAddress);
outcomesAmount[bettedOutcome] += msg.value;

numberOfBets += 1;
gamblerBetted(gamblerAddress);
}

/* Allow anyone to withdraw their winnings safely (if they have enough) */
function withdraw(uint withdrawAmount) public returns (uint) {
// A utility function to find if an uint element exists in an array
function _check_uint_item_exists_in_array(uint needle, uint[] haystack) public pure returns(bool decision) {
for (uint i = 0; i < haystack.length; i++) {
if (needle == haystack[i]) {
return true;
}
}

return false;
}

/* Allow anyone to check the outcomes they can bet on */
function checkOutcomes(uint outcome) public view returns (uint) {

// Select winnining outcome
function selectWinningOutcome(uint selectedOutcome) public onlyOracle {
require(bettingIsOpen); // Betting is still open
require(numberOfBets > 1); // Must have more than one gamblers participated till now

for (uint i =0; i < outcomes.length; i++) {
if (outcomes[i] == selectedOutcome) {
winningOutcome = outcomes[i];
break;
}
}
// Make sure the selectedOutcome is in the list of winningOutcome, else bail out
require(winningOutcome != 0);
bettingIsOpen = false;

bettingEnded();
winnerAnnounced(winningOutcome);

// Now disperse the reward
disperseReward(winningOutcome);
}

/* Allow anyone to check if they won any bets */
function checkWinnings() public view returns(uint) {

//Allocate the reward
function disperseReward(uint selectedOutcome) public onlyOracle payable {
require(!bettingIsOpen); //Betting must be closed by now
require(selectedOutcome != 0); // Winning outcome must have bene selected

// If no gambler betted to the winning outcome, the oracle wins the sum of the funds
if (bets[selectedOutcome].length == 0) {
//send money to oracle
if(!transfer(oracle, totalBetAmount)){
revert();
}
} else {
// First find winning gamblers
address[] storage winning_gamblers = bets[selectedOutcome];
uint winning_gamblers_total_betted_amount = outcomesAmount[selectedOutcome];

// The winners receive a propotional share of the total funds at stake if they all bet on the correct outcome
for (uint i = 0; i < winning_gamblers.length; i++) {
// TODO - use safemath here for the integer division
uint amount_to_transfer = ((gamblers[winning_gamblers[i]].bettingWei) / winning_gamblers_total_betted_amount )*totalBetAmount;
transfer(winning_gamblers[i], amount_to_transfer);
}
}

rewardDispered();
}

/* Call delete() to reset certain state variables. Which ones? That's upto you to decide */
function contractReset() public ownerOnly() {
// Tranfser the reward
function transfer(address to, uint value) public returns(bool success) {
if(balances[msg.sender] < value) revert();
balances[msg.sender] -= value;
to.transfer(value);
LogTransfer(msg.sender, to, value);
return true;
}
}

// Fall back funtion
function () public payable {}
}
2 changes: 1 addition & 1 deletion Betting Contract/migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var Betting = artifacts.require("./Betting.sol");

module.exports = function(deployer) {
deployer.deploy(Betting, ["A", "B", "C"]);
deployer.deploy(Betting, [1, 3, 4, 5, 8]);
};