Now that the 2018 Russia world cup is over I can talk about my world cup experience. My world cup experience this time around was probably a bit different from most persons because for the duration of the world cup, I ran a world cup betting site.
EtherWorldCup - https://etherworldcup.com (I don't like to alias links unless I have to)
I wanted to talk a bit about my motivations behind this projects, how I implemented it, and the lessons I learned from it.
So I'm not new to crypto, I've been around a little while and I've been working in the crypto space for some time now, and I absolutely love it. But for the past six months or so I've wanted to create a DApp on the Ethereum platform to really put my skills to the test, and I thought what better way to do this than to create something that I think had value to individuals, what I sought was a way I could combine my passion for Ethereum and Football to create something that everyone could have fun with this past world cup season, after all it is the world's greatest sports event and only comes around once every 4 years.
I'm gonna breakdown this explanation of the code, tools and technologies over a few posts. Explaining the SmartContract, the supporting services, the front end, and the production infrastructure of it all so that maybe after I've finished writing this "mini-series" anyone new to solidity out there can potentially learn something from my experience to implement their own DApp.
Before getting into all of that, I should explain what the project is supposed to do, what it's composed off, and just outline why I chose the technology that I did.
EtherWorldCup was intended to be a betting site that users could use to bet on the 2018 Russia World Cup matches. They could place a bet on any either of the teams playing or bet on a draw. The system was a pool betting system where only one pool (home team, away team, draw) could win, and the winners would be payed fraction of the losing pools proportional to their contribution to the winning pool after a commission fee was deducted.
The system used 2 independent sources of truth to verify the results of individual matches. Two separate services were used for increased fault tolerance and increased protection against the compromise of a single source of truth. The two services are football-data.org (https://www.football-data.org) and sportsmonks (https://sportmonks.com/). Both sources of truth were required to have the same winner for the match result to be determined. These sources of truth were chosen because of the simplicity in parsing their API JSON responses, and customer service, and security. Can you believe 80% of the football livescore sites I visited didn't have HTTPS!!
The project could be divided into 3 major components:
- The SmartContract
- The Backend services that serves data from the contract
- The Web UI Frontend application that users use to interact with the contract
Additionally I setup Graphite and Statsd so I'd be able to record metrics. You never know when you'll need them metrics for debugging!
Furthermore the backend services could be subdivided into:
- The Observer which listened for events that occurred on the SmartContract and queried live score APIs and persisted all data in data stores.
The API which read data from the data stores persisted to by the Observer and served it to the frontend.
The entirety of the backend services and the frontend were hosted on AWS infrastructure. While the SmartContract was deployed to the Ethereum mainnet.
For the most part the motivation behind choosing each of the languages used to develop the application was relatively simple:
The SmartContract was written in Solidity because Solidity is the language supported by the EVM. While I'm looking forward to the upcoming SolidityX by Loom (https://solidityx.org) that is still not open for public beta. I chose to write the contract in the latest solidity version (0.4.4) at the time of writing because it patched many known vulnerabilities.
The API was written in Go because Go is simple, lightweight, blazing fast, and has a ton of documentation, setting up a Go API was the simplest part of the entire project. Go being statically typed is easier to debug when bugs appear, with proper logging finding issues in your go code should be fairly simple. The API pushed data to Graphite and Statsd to record time series data for charting metrics.
Finally the Web UI was written in React.js because stateful components made updating the DOM very easy since the frontend application also actively listened to the contract and updated it's DOM on bet events.
Before designing any SmartContract as with any application it is very important that you consider what goals you are aiming to achieve before coding. I think this should be even more heavily emphasized when working with SmartConntracts because once they're deployed, that's it, there's no ammending them to fix any issues, you'll just have to redeploy. This could be detrimental depending on the functionality of your contract.
The EtherWorldCup smart contract "WorldCupBroker" was intended to do the following:
- Allow the contract owner, me, to add any match for the upcoming world cup.
- Allow any address to wager ether on a world cup match
- Independently determine the winner of the match
- Payout winners from the pool of losing bets proportional to the amount they bet, after subtracting a commission
- Allow me the owner to withdraw commission fees and any balance remaining on the contract, a month after the world cup has ended.
As you can see this contract isn't particularly complex, but I don't think it's completely trivial either. For now here's a list of all the tech, tools, libraries, and frameworks that were used in developing the contract:
- Truffle (https://truffleframework.com)
- Ganache (https://truffleframework.com/ganache), ganache is a part of the truffle suite
- Remix (https://remix.ethereum.org)
- Oraclize (https://oraclize.it)
- Solidity-stringutils (https://github.com/Arachnid/solidity-stringutils)
- Metamask (https://metamask.io)
The truffle framework was used to initiate the project, and the truffle-cli used to compile, deploy, and interact with the contract in testing.
Ganache was used to run a local Ethereum blockchain which was used for controlled testing purposes since it makes it easy to configure block times, gas limits, inspect logs, and supplies Ether for testing.
However in my experimentation, I had some issues with Ganache's websocket support (https://github.com/trufflesuite/ganache-cli/issues/257). So if you intend to listen to events on your contract like I did, I'll discuss this in the next post, then you might have eventually have to migrate to deploying to some testnet such as Rinkeby (https://www.rinkeby.io/). While this removed a lot of the control that I had with Ganache, I had the advantage of an environment that closer mimicked that of the actual Ethereum mainnet. Think of Ganache as a dev's local env, and Rinkeby as Staging before going into production. On the plus side if you're using Rinkeby you won't have to use an Ethereum bridge to get your SmartContract deployed to Ganache to communicate with Oraclize (https://github.com/oraclize/ethereum-bridge).
Remix is a web IDE for solidity that includes an optimizer, debugger, and static analysis tool. It also has a built in interface for creating transactions and making calls on deployed contracts as long as the contract ABI is available.
Oraclize is a third party Oraclze service for Ethereum that provides access to Web APIs. We're going to use Oraclize to get the match scores from our sources of truth to determine the results of the match.
Solidity-stringutils is a solidity library for onchain string parsing. Thinking back this wasn't the greatest decision and it would have been smarter to parse this off chain using the Oraclize
computation option. Onchain parsing is far more expensive and as you can see this will impact the gas cost of the contract.
Metamask is a browser extension for interacting with the Ethereum chain through your browser.
This post is already pretty long, so I'll cotinue in the next post which I'll link below once I've posted it:
"ex nihilo nihil fit"