pragma solidity ^0.4.18;
contract DataSourceInterface {
function isDataSource() public pure returns (bool);
function getGroupResult(uint matchId) external;
function getRoundOfSixteenTeams(uint index) external;
function getRoundOfSixteenResult(uint matchId) external;
function getQuarterResult(uint matchId) external;
function getSemiResult(uint matchId) external;
function getFinalTeams() external;
function getYellowCards() external;
function getRedCards() external;
}
/**
* @title DataLayer.
* @author CryptoCup Team (https://cryptocup.io/about)
*/
contract DataLayer{
uint256 constant WCCTOKEN_CREATION_LIMIT = 5000000;
uint256 constant STARTING_PRICE = 45 finney;
/// Epoch times based on when the prices change.
uint256 constant FIRST_PHASE = 1527476400;
uint256 constant SECOND_PHASE = 1528081200;
uint256 constant THIRD_PHASE = 1528686000;
uint256 constant WORLD_CUP_START = 1528945200;
DataSourceInterface public dataSource;
address public dataSourceAddress;
address public adminAddress;
uint256 public deploymentTime = 0;
uint256 public gameFinishedTime = 0; //set this to now when oraclize was called.
uint32 public lastCalculatedToken = 0;
uint256 public pointsLimit = 0;
uint32 public lastCheckedToken = 0;
uint32 public winnerCounter = 0;
uint32 public lastAssigned = 0;
uint256 public auxWorstPoints = 500000000;
uint32 public payoutRange = 0;
uint32 public lastPrizeGiven = 0;
uint256 public prizePool = 0;
uint256 public adminPool = 0;
uint256 public finalizedTime = 0;
enum teamState { None, ROS, QUARTERS, SEMIS, FINAL }
enum pointsValidationState { Unstarted, LimitSet, LimitCalculated, OrderChecked, TopWinnersAssigned, WinnersAssigned, Finished }
/**
* groups1 scores of the first half of matches (8 bits each)
* groups2 scores of the second half of matches (8 bits each)
* brackets winner's team ids of each round (5 bits each)
* timeStamp creation timestamp
* extra number of yellow and red cards (16 bits each)
*/
struct Token {
uint192 groups1;
uint192 groups2;
uint160 brackets;
uint64 timeStamp;
uint32 extra;
}
struct GroupResult{
uint8 teamOneGoals;
uint8 teamTwoGoals;
}
struct BracketPhase{
uint8[16] roundOfSixteenTeamsIds;
mapping (uint8 => bool) teamExists;
mapping (uint8 => teamState) middlePhaseTeamsIds;
uint8[4] finalsTeamsIds;
}
struct Extras {
uint16 yellowCards;
uint16 redCards;
}
// List of all tokens
Token[] tokens;
GroupResult[48] groupsResults;
BracketPhase bracketsResults;
Extras extraResults;
// List of all tokens that won
uint256[] sortedWinners;
// List of the worst tokens (they also win)
uint256[] worstTokens;
pointsValidationState public pValidationState = pointsValidationState.Unstarted;
mapping (address => uint256[]) public tokensOfOwnerMap;
mapping (uint256 => address) public ownerOfTokenMap;
mapping (uint256 => address) public tokensApprovedMap;
mapping (uint256 => uint256) public tokenToPayoutMap;
mapping (uint256 => uint16) public tokenToPointsMap;
event LogTokenBuilt(address creatorAddress, uint256 tokenId, Token token);
event LogDataSourceCallbackList(uint8[] result);
event LogDataSourceCallbackInt(uint8 result);
event LogDataSourceCallbackTwoInt(uint8 result, uint8 result2);
}
///Author Dieter Shirley (https://github.com/dete)
contract ERC721 {
event LogTransfer(address from, address to, uint256 tokenId);
event LogApproval(address owner, address approved, uint256 tokenId);
function name() public view returns (string);
function symbol() public view returns (string);
function totalSupply() public view returns (uint256 total);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) external view returns (address owner);
function approve(address _to, uint256 _tokenId) external;
function transfer(address _to, uint256 _tokenId) external;
function transferFrom(address _from, address _to, uint256 _tokenId) external;
function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
}
/**
* @title AccessControlLayer
* @author CryptoCup Team (https://cryptocup.io/about)
* @dev Containes basic admin modifiers to restrict access to some functions. Allows
* for pauseing, and setting emergency stops.
*/
contract AccessControlLayer is DataLayer{
bool public paused = false;
bool public finalized = false;
bool public saleOpen = true;
/**
* @dev Main modifier to limit access to delicate functions.
*/
modifier onlyAdmin() {
require(msg.sender == adminAddress);
_;
}
/**
* @dev Modifier that checks that the contract is not paused
*/
modifier isNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier that checks that the contract is paused
*/
modifier isPaused() {
require(paused);
_;
}
/**
* @dev Modifier that checks that the contract has finished successfully
*/
modifier hasFinished() {
require((gameFinishedTime != 0) && now >= (gameFinishedTime + (15 days)));
_;
}
/**
* @dev Modifier that checks that the contract has finalized
*/
modifier hasFinalized() {
require(finalized);
_;
}
/**
* @dev Checks if pValidationState is in the provided stats
* @param state State required to run
*/
modifier checkState(pointsValidationState state){
require(pValidationState == state);
_;
}
/**
* @dev Transfer contract's ownership
* @param _newAdmin Address to be set
*/
function setAdmin(address _newAdmin) external onlyAdmin {
require(_newAdmin != address(0));
adminAddress = _newAdmin;
}
/**
* @dev Sets the contract pause state
* @param state True to pause
*/
function setPauseState(bool state) external onlyAdmin {
paused = state;
}
/**
* @dev Sets the contract to finalized
* @param state True to finalize
*/
function setFinalized(bool state) external onlyAdmin {
paused = state;
finalized = state;
if(finalized == true)
finalizedTime = now;
}
}
/**
* @title CryptoCupToken, main implemantations of the ERC721 standard
* @author CryptoCup Team (https://cryptocup.io/about)
*/
contract CryptocupToken is AccessControlLayer, ERC721 {
//FUNCTIONALTIY
/**
* @notice checks if a user owns a token
* @param userAddress - The address to check.
* @param tokenId - ID of the token that needs to be verified.
* @return true if the userAddress provided owns the token.
*/
function _userOwnsToken(address userAddress, uint256 tokenId) internal view returns (bool){
return ownerOfTokenMap[tokenId] == userAddress;
}
/**
* @notice checks if the address provided is approved for a given token
* @param userAddress
* @param tokenId
* @return true if it is aproved
*/
function _tokenIsApproved(address userAddress, uint256 tokenId) internal view returns (bool) {
return tokensApprovedMap[tokenId] == userAddress;
}
/**
* @notice transfers the token specified from sneder address to receiver address.
* @param fromAddress the sender address that initially holds the token.
* @param toAddress the receipient of the token.
* @param tokenId ID of the token that will be sent.
*/
function _transfer(address fromAddress, address toAddress, uint256 tokenId) internal {
require(tokensOfOwnerMap[toAddress].length < 100);
require(pValidationState == pointsValidationState.Unstarted);
tokensOfOwnerMap[toAddress].push(tokenId);
ownerOfTokenMap[tokenId] = toAddress;
uint256[] storage tokenArray = tokensOfOwnerMap[fromAddress];
for (uint256 i = 0; i < tokenArray.length; i++){
if(tokenArray[i] == tokenId){
tokenArray[i] = tokenArray[tokenArray.length-1];
}
}
delete tokenArray[tokenArray.length-1];
tokenArray.length--;
delete tokensApprovedMap[tokenId];
}
/**
* @notice Approve the address for a given token
* @param tokenId - ID of token to be approved
* @param userAddress - Address that will be approved
*/
function _approve(uint256 tokenId, address userAddress) internal {
tokensApprovedMap[tokenId] = userAddress;
}
/**
* @notice set token owner to an address
* @dev sets token owner on the contract data structures
* @param ownerAddress address to be set
* @param tokenId Id of token to be used
*/
function _setTokenOwner(address ownerAddress, uint256 tokenId) internal{
tokensOfOwnerMap[ownerAddress].push(tokenId);
ownerOfTokenMap[tokenId] = ownerAddress;
}
//ERC721 INTERFACE
function name() public view returns (string){
return "Cryptocup";
}
function symbol() public view returns (string){
return "CC";
}
function balanceOf(address userAddress) public view returns (uint256 count) {
return tokensOfOwnerMap[userAddress].length;
}
function transfer(address toAddress,uint256 tokenId) external isNotPaused {
require(toAddress != address(0));
require(toAddress != address(this));
require(_userOwnsToken(msg.sender, tokenId));
_transfer(msg.sender, toAddress, tokenId);
LogTransfer(msg.sender, toAddress, tokenId);
}
function transferFrom(address fromAddress, address toAddress, uint256 tokenId) external isNotPaused {
require(toAddress != address(0));
require(toAddress != address(this));
require(_tokenIsApproved(msg.sender, tokenId));
require(_userOwnsToken(fromAddress, tokenId));
_transfer(fromAddress, toAddress, tokenId);
LogTransfer(fromAddress, toAddress, tokenId);
}
function approve( address toAddress, uint256 tokenId) external isNotPaused {
require(toAddress != address(0));
require(_userOwnsToken(msg.sender, tokenId));
_approve(tokenId, toAddress);
LogApproval(msg.sender, toAddress, tokenId);
}
function totalSupply() public view returns (uint) {
return tokens.length;
}
function ownerOf(uint256 tokenId) external view returns (address ownerAddress) {
ownerAddress = ownerOfTokenMap[tokenId];
require(ownerAddress != address(0));
}
function tokensOfOwner(address ownerAddress) external view returns(uint256[] tokenIds) {
tokenIds = tokensOfOwnerMap[ownerAddress];
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title GameLogicLayer, contract in charge of everything related to calculating points, asigning
* winners, and distributing prizes.
* @author CryptoCup Team (https://cryptocup.io/about)
*/
contract GameLogicLayer is CryptocupToken{
using SafeMath for *;
uint8 TEAM_RESULT_MASK_GROUPS = 15;
uint160 RESULT_MASK_BRACKETS = 31;
uint16 EXTRA_MASK_BRACKETS = 65535;
uint16 private lastPosition;
uint16 private superiorQuota;
uint16[] private payDistributionAmount = [1,1,1,1,1,1,1,1,1,1,5,5,10,20,50,100,100,200,500,1500,2500];
uint32[] private payoutDistribution;
event LogGroupDataArrived(uint matchId, uint8 result, uint8 result2);
event LogRoundOfSixteenArrived(uint id, uint8 result);
event LogMiddlePhaseArrived(uint matchId, uint8 result);
event LogFinalsArrived(uint id, uint8[4] result);
event LogExtrasArrived(uint id, uint16 result);
//ORACLIZE
function dataSourceGetGroupResult(uint matchId) external onlyAdmin{
dataSource.getGroupResult(matchId);
}
function dataSourceGetRoundOfSixteen(uint index) external onlyAdmin{
dataSource.getRoundOfSixteenTeams(index);
}
function dataSourceGetRoundOfSixteenResult(uint matchId) external onlyAdmin{
dataSource.getRoundOfSixteenResult(matchId);
}
function dataSourceGetQuarterResult(uint matchId) external onlyAdmin{
dataSource.getQuarterResult(matchId);
}
function dataSourceGetSemiResult(uint matchId) external onlyAdmin{
dataSource.getSemiResult(matchId);
}
function dataSourceGetFinals() external onlyAdmin{
dataSource.getFinalTeams();
}
function dataSourceGetYellowCards() external onlyAdmin{
dataSource.getYellowCards();
}
function dataSourceGetRedCards() external onlyAdmin{
dataSource.getRedCards();
}
/**
* @notice sets a match result to the contract storage
* @param matchId id of match to check
* @param result number of goals the first team scored
* @param result2 number of goals the second team scored
*/
function dataSourceCallbackGroup(uint matchId, uint8 result, uint8 result2) public {
require (msg.sender == dataSourceAddress);
require (matchId >= 0 && matchId <= 47);
groupsResults[matchId].teamOneGoals = result;
groupsResults[matchId].teamTwoGoals = result2;
LogGroupDataArrived(matchId, result, result2);
}
/**
* @notice sets the sixteen teams that made it through groups to the contract storage
* @param id index of sixteen teams
* @param result results to be set
*/
function dataSourceCallbackRoundOfSixteen(uint id, uint8 result) public {
require (msg.sender == dataSourceAddress);
bracketsResults.roundOfSixteenTeamsIds[id] = result;
bracketsResults.teamExists[result] = true;
LogRoundOfSixteenArrived(id, result);
}
function dataSourceCallbackTeamId(uint matchId, uint8 result) public {
require (msg.sender == dataSourceAddress);
teamState state = bracketsResults.middlePhaseTeamsIds[result];
if (matchId >= 48 && matchId <= 55){
if (state < teamState.ROS)
bracketsResults.middlePhaseTeamsIds[result] = teamState.ROS;
} else if (matchId >= 56 && matchId <= 59){
if (state < teamState.QUARTERS)
bracketsResults.middlePhaseTeamsIds[result] = teamState.QUARTERS;
} else if (matchId == 60 || matchId == 61){
if (state < teamState.SEMIS)
bracketsResults.middlePhaseTeamsIds[result] = teamState.SEMIS;
}
LogMiddlePhaseArrived(matchId, result);
}
/**
* @notice sets the champion, second, third and fourth teams to the contract storage
* @param id
* @param result ids of the four teams
*/
function dataSourceCallbackFinals(uint id, uint8[4] result) public {
require (msg.sender == dataSourceAddress);
uint256 i;
for(i = 0; i < 4; i++){
bracketsResults.finalsTeamsIds[i] = result[i];
}
LogFinalsArrived(id, result);
}
/**
* @notice sets the number of cards to the contract storage
* @param id 101 for yellow cards, 102 for red cards
* @param result amount of cards
*/
function dataSourceCallbackExtras(uint id, uint16 result) public {
require (msg.sender == dataSourceAddress);
if (id == 101){
extraResults.yellowCards = result;
} else if (id == 102){
extraResults.redCards = result;
}
LogExtrasArrived(id, result);
}
/**
* @notice check if prediction for a match winner is correct
* @param realResultOne amount of goals team one scored
* @param realResultTwo amount of goals team two scored
* @param tokenResultOne amount of goals team one was predicted to score
* @param tokenResultTwo amount of goals team two was predicted to score
* @return
*/
function matchWinnerOk(uint8 realResultOne, uint8 realResultTwo, uint8 tokenResultOne, uint8 tokenResultTwo) internal pure returns(bool){
int8 realR = int8(realResultOne - realResultTwo);
int8 tokenR = int8(tokenResultOne - tokenResultTwo);
return (realR > 0 && tokenR > 0) || (realR < 0 && tokenR < 0) || (realR == 0 && tokenR == 0);
}
/**
* @notice get points from a single match
* @param matchIndex
* @param groupsPhase token predictions
* @return 10 if predicted score correctly, 3 if predicted only who would win
* and 0 if otherwise
*/
function getMatchPointsGroups (uint256 matchIndex, uint192 groupsPhase) internal view returns(uint16 matchPoints) {
uint8 tokenResultOne = uint8(groupsPhase & TEAM_RESULT_MASK_GROUPS);
uint8 tokenResultTwo = uint8((groupsPhase >> 4) & TEAM_RESULT_MASK_GROUPS);
uint8 teamOneGoals = groupsResults[matchIndex].teamOneGoals;
uint8 teamTwoGoals = groupsResults[matchIndex].teamTwoGoals;
if (teamOneGoals == tokenResultOne && teamTwoGoals == tokenResultTwo){
matchPoints += 10;
} else {
if (matchWinnerOk(teamOneGoals, teamTwoGoals, tokenResultOne, tokenResultTwo)){
matchPoints += 3;
}
}
}
/**
* @notice calculates points from the last two matches
* @param brackets token predictions
* @return amount of points gained from the last two matches
*/
function getFinalRoundPoints (uint160 brackets) internal view returns(uint16 finalRoundPoints) {
uint8[3] memory teamsIds;
for (uint i = 0; i <= 2; i++){
brackets = brackets >> 5; //discard 4th place
teamsIds[2-i] = uint8(brackets & RESULT_MASK_BRACKETS);
}
if (teamsIds[0] == bracketsResults.finalsTeamsIds[0]){
finalRoundPoints += 100;
}
if (teamsIds[2] == bracketsResults.finalsTeamsIds[2]){
finalRoundPoints += 25;
}
if (teamsIds[0] == bracketsResults.finalsTeamsIds[1]){
finalRoundPoints += 50;
}
if (teamsIds[1] == bracketsResults.finalsTeamsIds[0] || teamsIds[1] == bracketsResults.finalsTeamsIds[1]){
finalRoundPoints += 50;
}
}
/**
* @notice calculates points for round of sixteen, quarter-finals and semifinals
* @param size amount of matches in round
* @param round ros, qf, sf or f
* @param brackets predictions
* @return amount of points
*/
function getMiddleRoundPoints(uint8 size, teamState round, uint160 brackets) internal view returns(uint16 middleRoundResults){
uint8 teamId;
for (uint i = 0; i < size; i++){
teamId = uint8(brackets & RESULT_MASK_BRACKETS);
if (uint(bracketsResults.middlePhaseTeamsIds[teamId]) >= uint(round) ) {
middleRoundResults+=60;
}
brackets = brackets >> 5;
}
}
/**
* @notice calculates points for correct predictions of group winners
* @param brackets token predictions
* @return amount of points
*/
function getQualifiersPoints(uint160 brackets) internal view returns(uint16 qualifiersPoints){
uint8 teamId;
for (uint256 i = 0; i <= 15; i++){
teamId = uint8(brackets & RESULT_MASK_BRACKETS);
if (teamId == bracketsResults.roundOfSixteenTeamsIds[15-i]){
qualifiersPoints+=30;
} else if (bracketsResults.teamExists[teamId]){
qualifiersPoints+=25;
}
brackets = brackets >> 5;
}
}
/**
* @notice calculates points won by yellow and red cards predictions
* @param extras token predictions
* @return amount of points
*/
function getExtraPoints(uint32 extras) internal view returns(uint16 extraPoints){
uint16 redCards = uint16(extras & EXTRA_MASK_BRACKETS);
extras = extras >> 16;
uint16 yellowCards = uint16(extras);
if (redCards == extraResults.redCards){
extraPoints+=20;
}
if (yellowCards == extraResults.yellowCards){
extraPoints+=20;
}
}
/**
* @notice calculates total amount of points for a token
* @param t token to calculate points for
* @return total amount of points
*/
function calculateTokenPoints (Token memory t) internal view returns(uint16 points){
//Groups phase 1
uint192 g1 = t.groups1;
for (uint256 i = 0; i <= 23; i++){
points+=getMatchPointsGroups(23-i, g1);
g1 = g1 >> 8;
}
//Groups phase 2
uint192 g2 = t.groups2;
for (i = 0; i <= 23; i++){
points+=getMatchPointsGroups(47-i, g2);
g2 = g2 >> 8;
}
uint160 bracketsLocal = t.brackets;
//Brackets phase 1
points+=getFinalRoundPoints(bracketsLocal);
bracketsLocal = bracketsLocal >> 20;
//Brackets phase 2
points+=getMiddleRoundPoints(4, teamState.QUARTERS, bracketsLocal);
bracketsLocal = bracketsLocal >> 20;
//Brackets phase 3
points+=getMiddleRoundPoints(8, teamState.ROS, bracketsLocal);
bracketsLocal = bracketsLocal >> 40;
//Brackets phase 4
points+=getQualifiersPoints(bracketsLocal);
//Extras
points+=getExtraPoints(t.extra);
}
/**
* @notice Sets the points of all the tokens between the last chunk set and the amount given.
* @dev This function uses all the data collected earlier by oraclize to calculate points.
* @param amount The amount of tokens that should be analyzed.
*/
function calculatePointsBlock(uint32 amount) external{
require (gameFinishedTime == 0);
require(amount + lastCheckedToken <= tokens.length);
for (uint256 i = lastCalculatedToken; i < (lastCalculatedToken + amount); i++) {
uint16 points = calculateTokenPoints(tokens[i]);
tokenToPointsMap[i] = points;
if(worstTokens.length == 0 || points <= auxWorstPoints){
if(worstTokens.length != 0 && points < auxWorstPoints){
worstTokens.length = 0;
}
if(worstTokens.length < 100){
auxWorstPoints = points;
worstTokens.push(i);
}
}
}
lastCalculatedToken += amount;
}
/**
* @notice Sets the structures for payout distribution, last position and superior quota. Payout distribution is the
* percentage of the pot each position gets, last position is the percentage of the pot the last position gets,
* and superior quota is the total amount OF winners that are given a prize.
* @dev Each of this structures is dynamic and is assigned depending on the total amount of tokens in the game
*/
function setPayoutDistributionId () internal {
if(tokens.length < 101){
payoutDistribution = [289700, 189700, 120000, 92500, 75000, 62500, 52500, 42500, 40000, 35600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
lastPosition = 0;
superiorQuota = 10;
}else if(tokens.length < 201){
payoutDistribution = [265500, 165500, 105500, 75500, 63000, 48000, 35500, 20500, 20000, 19500, 18500, 17800, 0, 0, 0, 0, 0, 0, 0, 0, 0];
lastPosition = 0;
superiorQuota = 20;
}else if(tokens.length < 301){
payoutDistribution = [260700, 155700, 100700, 70900, 60700, 45700, 35500, 20500, 17900, 12500, 11500, 11000, 10670, 0, 0, 0, 0, 0, 0, 0, 0];
lastPosition = 0;
superiorQuota = 30;
}else if(tokens.length < 501){
payoutDistribution = [238600, 138600, 88800, 63800, 53800, 43800, 33800, 18800, 17500, 12500, 9500, 7500, 7100, 6700, 0, 0, 0, 0, 0, 0, 0];
lastPosition = 0;
superiorQuota = 50;
}else if(tokens.length < 1001){
payoutDistribution = [218300, 122300, 72300, 52400, 43900, 33900, 23900, 16000, 13000, 10000, 9000, 7000, 5000, 4000, 3600, 0, 0, 0, 0, 0, 0];
lastPosition = 4000;
superiorQuota = 100;
}else if(tokens.length < 2001){
payoutDistribution = [204500, 114000, 64000, 44100, 35700, 26700, 22000, 15000, 11000, 9500, 8500, 6500, 4600, 2500, 2000, 1800, 0, 0, 0, 0, 0];
lastPosition = 2500;
superiorQuota = 200;
}else if(tokens.length < 3001){
payoutDistribution = [189200, 104800, 53900, 34900, 29300, 19300, 15300, 14000, 10500, 8300, 8000, 6000, 3800, 2500, 2000, 1500, 1100, 0, 0, 0, 0];
lastPosition = 2500;
superiorQuota = 300;
}else if(tokens.length < 5001){
payoutDistribution = [178000, 100500, 47400, 30400, 24700, 15500, 15000, 12000, 10200, 7800, 7400, 5500, 3300, 2000, 1500, 1200, 900, 670, 0, 0, 0];
lastPosition = 2000;
superiorQuota = 500;
}else if(tokens.length < 10001){
payoutDistribution = [157600, 86500, 39000, 23100, 18900, 15000, 14000, 11000, 9300, 6100, 6000, 5000, 3800, 1500, 1100, 900, 700, 500, 360, 0, 0];
lastPosition = 1500;
superiorQuota = 1000;
}else if(tokens.length < 25001){
payoutDistribution = [132500, 70200, 31300, 18500, 17500, 14000, 13500, 10500, 7500, 5500, 5000, 4000, 3000, 1000, 900, 700, 600, 400, 200, 152, 0];
lastPosition = 1000;
superiorQuota = 2500;
} else {
payoutDistribution = [120000, 63000, 27000, 18800, 17300, 13700, 13000, 10000, 6300, 5000, 4500, 3900, 2500, 900, 800, 600, 500, 350, 150, 100, 70];
lastPosition = 900;
superiorQuota = 5000;
}
}
/**
* @notice Sets the id of the last token that will be given a prize.
* @dev This is done to offload some of the calculations needed for sorting, and to cap the number of sorts
* needed to just the winners and not the whole array of tokens.
* @param tokenId last token id
*/
function setLimit(uint256 tokenId) external onlyAdmin{
require(tokenId < tokens.length);
require(pValidationState == pointsValidationState.Unstarted || pValidationState == pointsValidationState.LimitSet);
pointsLimit = tokenId;
pValidationState = pointsValidationState.LimitSet;
lastCheckedToken = 0;
lastCalculatedToken = 0;
winnerCounter = 0;
setPayoutDistributionId();
}
/**
* @notice Sets the 10th percentile of the sorted array of points
* @param amount tokens in a chunk
*/
function calculateWinners(uint32 amount) external onlyAdmin checkState(pointsValidationState.LimitSet){
require(amount + lastCheckedToken <= tokens.length);
uint256 points = tokenToPointsMap[pointsLimit];
for(uint256 i = lastCheckedToken; i < lastCheckedToken + amount; i++){
if(tokenToPointsMap[i] > points ||
(tokenToPointsMap[i] == points && i <= pointsLimit)){
winnerCounter++;
}
}
lastCheckedToken += amount;
if(lastCheckedToken == tokens.length){
require(superiorQuota == winnerCounter);
pValidationState = pointsValidationState.LimitCalculated;
}
}
/**
* @notice Checks if the order given offchain coincides with the order of the actual previously calculated points
* in the smart contract.
* @dev the token sorting is done offchain so as to save on the huge amount of gas and complications that
* could occur from doing all the sorting onchain.
* @param sortedChunk chunk sorted by points
*/
function checkOrder(uint32[] sortedChunk) external onlyAdmin checkState(pointsValidationState.LimitCalculated){
require(sortedChunk.length + sortedWinners.length <= winnerCounter);
for(uint256 i=0;i < sortedChunk.length-1;i++){
uint256 id = sortedChunk[i];
uint256 sigId = sortedChunk[i+1];
require(tokenToPointsMap[id] > tokenToPointsMap[sigId] ||
(tokenToPointsMap[id] == tokenToPointsMap[sigId] && id < sigId));
}
if(sortedWinners.length != 0){
uint256 id2 = sortedWinners[sortedWinners.length-1];
uint256 sigId2 = sortedChunk[0];
require(tokenToPointsMap[id2] > tokenToPointsMap[sigId2] ||
(tokenToPointsMap[id2] == tokenToPointsMap[sigId2] && id2 < sigId2));
}
for(uint256 j=0;j < sortedChunk.length;j++){
sortedWinners.push(sortedChunk[j]);
}
if(sortedWinners.length == winnerCounter){
require(sortedWinners[sortedWinners.length-1] == pointsLimit);
pValidationState = pointsValidationState.OrderChecked;
}
}
/**
* @notice If anything during the point calculation and sorting part should fail, this function can reset
* data structures to their initial position, so as to
*/
function resetWinners(uint256 newLength) external onlyAdmin checkState(pointsValidationState.LimitCalculated){
sortedWinners.length = newLength;
}
/**
* @notice Assigns prize percentage for the lucky top 30 winners. Each token will be assigned a uint256 inside
* tokenToPayoutMap structure that represents the size of the pot that belongs to that token. If any tokens
* tie inside of the first 30 tokens, the prize will be summed and divided equally.
*/
function setTopWinnerPrizes() external onlyAdmin checkState(pointsValidationState.OrderChecked){
uint256 percent = 0;
uint[] memory tokensEquals = new uint[](30);
uint16 tokenEqualsCounter = 0;
uint256 currentTokenId;
uint256 currentTokenPoints;
uint256 lastTokenPoints;
uint32 counter = 0;
uint256 maxRange = 13;
if(tokens.length < 201){
maxRange = 10;
}
while(payoutRange < maxRange){
uint256 inRangecounter = payDistributionAmount[payoutRange];
while(inRangecounter > 0){
currentTokenId = sortedWinners[counter];
currentTokenPoints = tokenToPointsMap[currentTokenId];
inRangecounter--;
//Special case for the last one
if(inRangecounter == 0 && payoutRange == maxRange - 1){
if(currentTokenPoints == lastTokenPoints){
percent += payoutDistribution[payoutRange];
tokensEquals[tokenEqualsCounter] = currentTokenId;
tokenEqualsCounter++;
}else{
tokenToPayoutMap[currentTokenId] = payoutDistribution[payoutRange];
}
}
if(counter != 0 && (currentTokenPoints != lastTokenPoints || (inRangecounter == 0 && payoutRange == maxRange - 1))){ //Fix second condition
for(uint256 i=0;i < tokenEqualsCounter;i++){
tokenToPayoutMap[tokensEquals[i]] = percent.div(tokenEqualsCounter);
}
percent = 0;
tokensEquals = new uint[](30);
tokenEqualsCounter = 0;
}
percent += payoutDistribution[payoutRange];
tokensEquals[tokenEqualsCounter] = currentTokenId;
tokenEqualsCounter++;
counter++;
lastTokenPoints = currentTokenPoints;
}
payoutRange++;
}
pValidationState = pointsValidationState.TopWinnersAssigned;
lastPrizeGiven = counter;
}
/**
* @notice Sets prize percentage to every address that wins from the position 30th onwards
* @dev If there are less than 300 tokens playing, then this function will set nothing.
* @param amount tokens in a chunk
*/
function setWinnerPrizes(uint32 amount) external onlyAdmin checkState(pointsValidationState.TopWinnersAssigned){
require(lastPrizeGiven + amount <= winnerCounter);
uint16 inRangeCounter = payDistributionAmount[payoutRange];
for(uint256 i = 0; i < amount; i++){
if (inRangeCounter == 0){
payoutRange++;
inRangeCounter = payDistributionAmount[payoutRange];
}
uint256 tokenId = sortedWinners[i + lastPrizeGiven];
tokenToPayoutMap[tokenId] = payoutDistribution[payoutRange];
inRangeCounter--;
}
//i + amount prize was not given yet, so amount -1
lastPrizeGiven += amount;
payDistributionAmount[payoutRange] = inRangeCounter;
if(lastPrizeGiven == winnerCounter){
pValidationState = pointsValidationState.WinnersAssigned;
return;
}
}
/**
* @notice Sets prizes for last tokens and sets prize pool amount
*/
function setLastPositions() external onlyAdmin checkState(pointsValidationState.WinnersAssigned){
for(uint256 j = 0;j < worstTokens.length;j++){
uint256 tokenId = worstTokens[j];
tokenToPayoutMap[tokenId] += lastPosition.div(worstTokens.length);
}
uint256 balance = address(this).balance;
adminPool = balance.mul(25).div(100);
prizePool = balance.mul(75).div(100);
pValidationState = pointsValidationState.Finished;
gameFinishedTime = now;
}
}
/**
* @title CoreLayer
* @author CryptoCup Team (https://cryptocup.io/about)
* @notice Main contract
*/
contract CoreLayer is GameLogicLayer {
function CoreLayer() public {
adminAddress = msg.sender;
deploymentTime = now;
}
/**
* @dev Only accept eth from the admin
*/
function() external payable {
require(msg.sender == adminAddress);
}
function isDataSourceCallback() public pure returns (bool){
return true;
}
/**
* @notice Builds ERC721 token with the predictions provided by the user.
* @param groups1 - First half of the group matches scores encoded in a uint192.
* @param groups2 - Second half of the groups matches scores encoded in a uint192.
* @param brackets - Bracket information encoded in a uint160.
* @param extra - Extra information (number of red cards and yellow cards) encoded in a uint32.
* @dev An automatic timestamp is added for internal use.
*/
function buildToken(uint192 groups1, uint192 groups2, uint160 brackets, uint32 extra) external payable isNotPaused {
Token memory token = Token({
groups1: groups1,
groups2: groups2,
brackets: brackets,
timeStamp: uint64(now),
extra: extra
});
require(msg.value >= _getTokenPrice());
require(msg.sender != address(0));
require(tokens.length < WCCTOKEN_CREATION_LIMIT);
require(tokensOfOwnerMap[msg.sender].length < 100);
require(now < WORLD_CUP_START); //World cup Start
uint256 tokenId = tokens.push(token) - 1;
require(tokenId == uint256(uint32(tokenId)));
_setTokenOwner(msg.sender, tokenId);
LogTokenBuilt(msg.sender, tokenId, token);
}
/**
* @param tokenId - ID of token to get.
* @return Returns all the valuable information about a specific token.
*/
function getToken(uint256 tokenId) external view returns (uint192 groups1, uint192 groups2, uint160 brackets, uint64 timeStamp, uint32 extra) {
Token storage token = tokens[tokenId];
groups1 = token.groups1;
groups2 = token.groups2;
brackets = token.brackets;
timeStamp = token.timeStamp;
extra = token.extra;
}
/**
* @notice Called by the development team once the World Cup has ended (adminPool is set)
* @dev Allows dev team to retrieve adminPool
*/
function adminWithdrawBalance() external onlyAdmin {
adminAddress.transfer(adminPool);
adminPool = 0;
}
/**
* @notice Allows any user to retrieve their asigned prize. This would be the sum of the price of all the tokens
* owned by the caller of this function.
* @dev If the caller has no prize, the function will revert costing no gas to the caller.
*/
function withdrawPrize() external checkState(pointsValidationState.Finished){
uint256 prize = 0;
uint256[] memory tokenList = tokensOfOwnerMap[msg.sender];
for(uint256 i = 0;i < tokenList.length; i++){
prize += tokenToPayoutMap[tokenList[i]];
tokenToPayoutMap[tokenList[i]] = 0;
}
require(prize > 0);
msg.sender.transfer((prizePool.mul(prize)).div(1000000));
}
/**
* @notice Gets current token price
*/
function _getTokenPrice() internal view returns(uint256 tokenPrice){
if ( now >= THIRD_PHASE){
tokenPrice = (150 finney);
} else if (now >= SECOND_PHASE) {
tokenPrice = (110 finney);
} else if (now >= FIRST_PHASE) {
tokenPrice = (75 finney);
} else {
tokenPrice = STARTING_PRICE;
}
require(tokenPrice >= STARTING_PRICE && tokenPrice <= (200 finney));
}
/**
* @dev Sets the data source contract address
* @param _address Address to be set
*/
function setDataSourceAddress(address _address) external onlyAdmin {
DataSourceInterface c = DataSourceInterface(_address);
require(c.isDataSource());
dataSource = c;
dataSourceAddress = _address;
}
/**
* @notice Testing function to corroborate group data from oraclize call
* @param x Id of the match to get
* @return uint8 Team 1 goals
* @return uint8 Team 2 goals
*/
function getGroupData(uint x) external view returns(uint8 a, uint8 b){
a = groupsResults[x].teamOneGoals;
b = groupsResults[x].teamTwoGoals;
}
/**
* @notice Testing function to corroborate round of sixteen data from oraclize call
* @return An array with the ids of the round of sixteen teams
*/
function getBracketData() external view returns(uint8[16] a){
a = bracketsResults.roundOfSixteenTeamsIds;
}
/**
* @notice Testing function to corroborate brackets data from oraclize call
* @param x Team id
* @return The place the team reached
*/
function getBracketDataMiddleTeamIds(uint8 x) external view returns(teamState a){
a = bracketsResults.middlePhaseTeamsIds[x];
}
/**
* @notice Testing function to corroborate finals data from oraclize call
* @return the 4 (four) final teams ids
*/
function getBracketDataFinals() external view returns(uint8[4] a){
a = bracketsResults.finalsTeamsIds;
}
/**
* @notice Testing function to corroborate extra data from oraclize call
* @return amount of yellow and red cards
*/
function getExtrasData() external view returns(uint16 a, uint16 b){
a = extraResults.yellowCards;
b = extraResults.redCards;
}
//EMERGENCY CALLS
//If something goes wrong or fails, these functions will allow retribution for token holders
/**
* @notice if there is an unresolvable problem, users can call to this function to get a refund.
*/
function emergencyWithdraw() external hasFinalized{
uint256 balance = STARTING_PRICE * tokensOfOwnerMap[msg.sender].length;
delete tokensOfOwnerMap[msg.sender];
msg.sender.transfer(balance);
}
/**
* @notice Let the admin cash-out the entire contract balance 10 days after game has finished.
*/
function finishedGameWithdraw() external onlyAdmin hasFinished{
uint256 balance = address(this).balance;
adminAddress.transfer(balance);
}
/**
* @notice Let the admin cash-out the entire contract balance 10 days after game has finished.
*/
function emergencyWithdrawAdmin() external hasFinalized onlyAdmin{
require(finalizedTime != 0 && now >= finalizedTime + 10 days );
msg.sender.transfer(address(this).balance);
}
}
{
"compilationTarget": {
"CoreLayer.sol": "CoreLayer"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"dataSourceGetFinals","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"toAddress","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dataSource","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"state","type":"bool"}],"name":"setFinalized","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"fromAddress","type":"address"},{"name":"toAddress","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"}],"name":"dataSourceGetRoundOfSixteenResult","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"setLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint32"}],"name":"calculateWinners","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokenToPayoutMap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"ownerOfTokenMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pointsLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setDataSourceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"result","type":"uint8[4]"}],"name":"dataSourceCallbackFinals","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"x","type":"uint256"}],"name":"getGroupData","outputs":[{"name":"a","type":"uint8"},{"name":"b","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastCheckedToken","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokenToPointsMap","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"index","type":"uint256"}],"name":"dataSourceGetRoundOfSixteen","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawPrize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"}],"name":"dataSourceGetSemiResult","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"lastCalculatedToken","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isDataSourceCallback","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"groups1","type":"uint192"},{"name":"groups2","type":"uint192"},{"name":"brackets","type":"uint160"},{"name":"extra","type":"uint32"}],"name":"buildToken","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"setTopWinnerPrizes","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newLength","type":"uint256"}],"name":"resetWinners","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"ownerAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sortedChunk","type":"uint32[]"}],"name":"checkOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"setLastPositions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"lastAssigned","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBracketData","outputs":[{"name":"a","type":"uint8[16]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"}],"name":"dataSourceGetQuarterResult","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"userAddress","type":"address"}],"name":"balanceOf","outputs":[{"name":"count","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"prizePool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"},{"name":"result","type":"uint8"},{"name":"result2","type":"uint8"}],"name":"dataSourceCallbackGroup","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"payoutRange","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pValidationState","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"ownerAddress","type":"address"}],"name":"tokensOfOwner","outputs":[{"name":"tokenIds","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"auxWorstPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"result","type":"uint8"}],"name":"dataSourceCallbackRoundOfSixteen","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"dataSourceGetRedCards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint32"}],"name":"setWinnerPrizes","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"emergencyWithdrawAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"},{"name":"result","type":"uint8"}],"name":"dataSourceCallbackTeamId","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"saleOpen","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"winnerCounter","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"tokensOfOwnerMap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"toAddress","type":"address"},{"name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"finalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishedGameWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint32"}],"name":"calculatePointsBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getExtrasData","outputs":[{"name":"a","type":"uint16"},{"name":"b","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"adminPool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"dataSourceGetYellowCards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"state","type":"bool"}],"name":"setPauseState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"lastPrizeGiven","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dataSourceAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"emergencyWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenId","type":"uint256"}],"name":"getToken","outputs":[{"name":"groups1","type":"uint192"},{"name":"groups2","type":"uint192"},{"name":"brackets","type":"uint160"},{"name":"timeStamp","type":"uint64"},{"name":"extra","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"result","type":"uint16"}],"name":"dataSourceCallbackExtras","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"deploymentTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokensApprovedMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"matchId","type":"uint256"}],"name":"dataSourceGetGroupResult","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"x","type":"uint8"}],"name":"getBracketDataMiddleTeamIds","outputs":[{"name":"a","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBracketDataFinals","outputs":[{"name":"a","type":"uint8[4]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"adminAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"adminWithdrawBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"finalizedTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gameFinishedTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"matchId","type":"uint256"},{"indexed":false,"name":"result","type":"uint8"},{"indexed":false,"name":"result2","type":"uint8"}],"name":"LogGroupDataArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"result","type":"uint8"}],"name":"LogRoundOfSixteenArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"matchId","type":"uint256"},{"indexed":false,"name":"result","type":"uint8"}],"name":"LogMiddlePhaseArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"result","type":"uint8[4]"}],"name":"LogFinalsArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"result","type":"uint16"}],"name":"LogExtrasArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"LogTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"approved","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"LogApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"creatorAddress","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"components":[{"name":"groups1","type":"uint192"},{"name":"groups2","type":"uint192"},{"name":"brackets","type":"uint160"},{"name":"timeStamp","type":"uint64"},{"name":"extra","type":"uint32"}],"indexed":false,"name":"token","type":"tuple"}],"name":"LogTokenBuilt","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"result","type":"uint8[]"}],"name":"LogDataSourceCallbackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"result","type":"uint8"}],"name":"LogDataSourceCallbackInt","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"result","type":"uint8"},{"indexed":false,"name":"result2","type":"uint8"}],"name":"LogDataSourceCallbackTwoInt","type":"event"}]