Qatar 2022 matches betting in the blockchain

I imagine that by 2022 cryptocurrency and blockchain will be used by many in Qatar, and in 2022 Qatar will be hosting the world cup. So I had this idea of implementing a football match betting in the blockchain. I decided to write a contract in Solidity for Ethereum to learn a little about it.

The idea of the contract is as follows :

  • Every match represent a new contract
  • Owner create the contract before the match starts
  • The contract represent two teams A and B each with its details set at time of contract creation
  • People start betting Ether
  • After the match ends the owner declare a winning team and betting is locked
  • Money is distributed to winning bets

So let's take it a step by step to write the solidity code that does this.

A bet represent an address and an amount

struct Bets {
   address addr;
   uint amount;
}

A team consist of a locked state, name, image (ipfs), a number of bets, and a total balance.

struct Team {
   bool locked;
   string name;
   string image;
   Bets[] bets;
   uint balance;
}

Now we can represent teams and the bets inside them, we need some functionality such as the ability to bet on a team, and payWinners. bet is a function of payable type that can receive Ether. This function is called with an index by the participant choosing a team to bet for, and money will be added to the contract.

function bet(uint choice) public payable {
	require(choice == 0 || choice == 1);
	require(msg.value > 0);
	require(A.locked == false);
	require(B.locked == false);

	uint amount = msg.value;
	uint idx;
	if (choice == 0) { // 0=A
		A.balance += msg.value;
		idx = A.bets.length;
		A.bets.length += 1;
		A.bets[idx].addr = msg.sender;
		A.bets[idx].amount = amount;
		NewBetA(msg.sender, msg.value);
	} else {          // 1=B
		B.balance += amount;
		idx = B.bets.length;
		B.bets.length += 1;
		B.bets[idx].addr = msg.sender;
		B.bets[idx].amount = amount;
		NewBetB(msg.sender, msg.value);
	}
}

NewBetA and NewBetB are Events that are triggered when people bet and can be seen by anyone watching for those events in the contract. This is good for light clients, and can be used by the UI to make life easier.

The declaration of a winning team must be only possible by the owner of the contract so no one can cheat. Ownership and function protection can be implemented using function modifiers

modifier onlyowner {
   require(msg.sender == owner);
   _;
}

Each winning bet gets his money back with extra money from the lost balance that is calculated as amountBet * (lostBalance/WonBalance).

function payWinners(uint choice) public onlyowner {
  require(choice == 0 || choice == 1);
  uint collected = 0;
  var winBalance = (choice == 0) ? A.balance : B.balance;
  var lostBalance = (choice == 0) ? B.balance : A.balance;
  var winnerBets = (choice == 0) ? A.bets : B.bets;

  for (uint i = 0; i < winnerBets.length; i++) {
    uint extra = winnerBets[i].amount * (lostBalance/winBalance);
    uint amount = winnerBets[i].amount + extra;
    if (!winnerBets[i].addr.send(amount)) {
      collected += amount;
    } else {
      NewWinner(winnerBets[i].addr, amount);
    }
  }

  if (collected != 0) {
    owner.transfer(collected);
  }

  // lock the teams
  lock();
}

Any amount that fails to be sent to an address is collected and sent back to the owner of the contract.

The full contract code. Now improve this, add percentage cut for contract owner, build mobile dapps and make some money in 2022 :-D.