Skip to content

Instantly share code, notes, and snippets.

@brunobuddy
Created June 21, 2018 11:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brunobuddy/551346e025f0b85e521057e654c7fd9f to your computer and use it in GitHub Desktop.
Save brunobuddy/551346e025f0b85e521057e654c7fd9f to your computer and use it in GitHub Desktop.
BetDemocracy V 0.1 Contract
pragma solidity 0.4.17;
import "./JsmnSolLib.sol";
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
import "github.com/Arachnid/solidity-stringutils/src/strings.sol";
contract BetDemocracy is usingOraclize {
using strings for *;
enum Result { Unknown, HomeWin, AwayWin, Draw }
enum Prediction { Home, HomeOrDraw, Draw, AwayOrDraw, Away, HomeOrAway }
struct Match {
Result result;
string apiResult;
mapping(uint => Bet) bets;
uint betCount;
uint startDate;
bool exists; // explicit bool to test resource existence
}
struct Bet {
address initiator;
address taker;
uint16 initiatorOdds; // odds * 10**2
uint256 initiatorStake; // stake in wei
Prediction initiatorPrediction;
address winner;
bool withdrawn;
bool exists; // explicit bool to test resource existence
}
mapping (uint32 => Match) public matches;
mapping (bytes32 => uint32) public pendingQueries;
event Log(string _description);
event Log(address _address);
event Log(uint _amount);
event NewOraclizeQuery(string description);
// accepts sending value to contract
function acceptValue() public payable {}
// Opens a new bet for a defined game
function createBet(uint32 _matchId, uint16 _odds, Prediction _prediction) public payable {
require(msg.value >= 0.0001 ether);
require(_odds >= 105 && _odds <= 2100); // Odds between 1.05 and 21
require(matches[_matchId].exists);
require(now < matches[_matchId].startDate);
// Add bet to "bet" mapping and increment count
matches[_matchId].bets[matches[_matchId].betCount] =
Bet(msg.sender, address(0x0), _odds, msg.value, _prediction, address(0x0), false, true);
matches[_matchId].betCount++;
}
// Take an open bet
function takeBet(uint32 _matchId, uint _betIndex) public payable returns (bool succeeded) {
require(matches[_matchId].exists);
require(now < matches[_matchId].startDate);
Bet memory bet = matches[_matchId].bets[_betIndex];
require(bet.exists);
require(bet.taker == address(0x0));
require(matches[_matchId].result == Result.Unknown);
// Ensure that taker paid enough money to take the bet
uint takerStake = (bet.initiatorStake * bet.initiatorOdds - bet.initiatorStake * 10 ** 2) / 10 ** 2;
require(msg.value == takerStake);
matches[_matchId].bets[_betIndex].taker = msg.sender;
Log("Bet has been taken");
return true;
}
// Withdraw bet pot (winner only)
function withdrawBet(uint32 _matchId, uint _betIndex) public returns (bool succeeded) {
Match memory betMatch = matches[_matchId];
Bet memory bet = matches[_matchId].bets[_betIndex];
require(now > betMatch.startDate + 4 hours);
require(!bet.withdrawn);
require(betMatch.result != Result.Unknown);
// Determine winner and transfer prize
if (betMatch.result == Result.HomeWin) {
matches[_matchId].bets[_betIndex].winner = (bet.initiatorPrediction == Prediction.Home
|| bet.initiatorPrediction == Prediction.HomeOrDraw || bet.initiatorPrediction == Prediction.HomeOrAway) ?
bet.initiator : bet.taker;
} else if (betMatch.result == Result.AwayWin) {
matches[_matchId].bets[_betIndex].winner = (bet.initiatorPrediction == Prediction.Away
|| bet.initiatorPrediction == Prediction.AwayOrDraw || bet.initiatorPrediction == Prediction.HomeOrAway) ?
bet.initiator : bet.taker;
} else {
matches[_matchId].bets[_betIndex].winner = (bet.initiatorPrediction == Prediction.HomeOrDraw
|| bet.initiatorPrediction == Prediction.AwayOrDraw || bet.initiatorPrediction == Prediction.Draw) ?
bet.initiator : bet.taker;
}
uint256 prize = bet.initiatorStake * bet.initiatorOdds / 100;
Log(prize);
matches[_matchId].bets[_betIndex].winner.transfer(prize);
matches[_matchId].bets[_betIndex].withdrawn = true;
return true;
}
// Refund initiator if nobody took his or her bet
function refundBet(uint32 _matchId, uint _betIndex) public returns (bool _refunded) {
Bet memory bet = matches[_matchId].bets[_betIndex];
require(bet.exists);
require(!bet.withdrawn);
require(bet.initiator == msg.sender);
require(bet.taker == address(0x0));
require(now > matches[_matchId].startDate);
msg.sender.transfer(bet.initiatorStake);
matches[_matchId].bets[_betIndex].withdrawn = true;
return true;
}
// Orcale response (scheduled to arrive after match ending)
function __callback(bytes32 _queryId, string _apiResult) {
require(msg.sender == oraclize_cbAddress());
// find matchId
uint32 matchId = pendingQueries[_queryId];
// Store API result into Match (for debug purposes)
matches[matchId].apiResult = _apiResult;
matches[matchId].result = getResult(_apiResult);
delete pendingQueries[_queryId];
}
// Returns enum Result based on JSON string received from api
function getResult(string json) public returns (Result result) {
uint returnValue;
JsmnSolLib.Token[] memory tokens;
uint tokenCount;
int goalsHomeTeam;
int goalsAwayTeam;
(returnValue, tokens, tokenCount) = JsmnSolLib.parse(json, 11);
require(returnValue == 0 && (tokenCount == 5 || tokenCount == 11));
// API returns halftime data
if (tokenCount == 11) {
JsmnSolLib.Token memory t = tokens[10];
goalsHomeTeam = JsmnSolLib.parseInt(JsmnSolLib.getBytes(json, t.start, t.end));
t = tokens[8];
goalsAwayTeam = JsmnSolLib.parseInt(JsmnSolLib.getBytes(json, t.start, t.end));
// API does not have/return halftime Data
} else if (tokenCount == 5) {
t = tokens[4];
goalsHomeTeam = JsmnSolLib.parseInt(JsmnSolLib.getBytes(json, t.start, t.end));
t = tokens[2];
goalsAwayTeam = JsmnSolLib.parseInt(JsmnSolLib.getBytes(json, t.start, t.end));
}
// Update result based on goals
if (goalsHomeTeam > goalsAwayTeam) {
return Result.HomeWin;
} else if (goalsHomeTeam == goalsAwayTeam) {
return Result.Draw;
} else if (goalsHomeTeam < goalsAwayTeam) {
return Result.AwayWin;
} else {
return Result.Unknown;
}
}
/*
Read functions
*/
function readBet(uint32 _matchId, uint _betIndex) public constant returns (address initiator,
address taker, uint16 initiatorOdds, uint256 initiatorStake, Prediction initiatorPrediction,
address winner, bool withdrawn, bool exists) {
Bet memory bet = matches[_matchId].bets[_betIndex];
return (bet.initiator, bet.taker, bet.initiatorOdds, bet.initiatorStake,
bet.initiatorPrediction, bet.winner, bet.withdrawn, bet.exists);
}
function getBalance() public constant returns (uint) {
return address(this).balance;
}
// Helper function : TODO: move to library
function uintToString(uint i) internal pure returns (string) {
if (i == 0) return "0";
uint j = i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (i != 0) {
bstr[k--] = byte(48 + i % 10);
i /= 10;
}
return string(bstr);
}
// Adds one match and schedule Oraclize call
function addMatch(uint32 _matchId, uint _startDate) public payable {
bytes32 queryId;
NewOraclizeQuery("Oraclize query were scheduled...");
// Group matches
matches[_matchId] = Match({
result: Result.Unknown,
apiResult: "",
betCount: 0,
startDate: _startDate,
exists: true
});
queryId = oraclize_query(matches[_matchId].startDate + 4 * 3600, "URL", getApiPath(_matchId), 500000);
pendingQueries[queryId] = _matchId;
}
// Returns API path for defined match
function getApiPath(uint32 _matchId) internal pure returns (string path) {
return path = "json(http://api.football-data.org/v1/fixtures/".toSlice().concat(uintToString(_matchId).toSlice()).toSlice().concat("?head2head=0).fixture.result".toSlice());
}
function BetDemocracy() public payable {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment