| title | Technical Specs - Architecture, Components, Smart Contracts |
|---|---|
| release | Alpha |
| type | Meta |
| created | 2018-05-24 |
In order to understand the implementation aspects, we would recommend you to consider the pre-reads in the following areas:
- authentication in ethereum
- simple staking games, voting games
- ERC20 token and gensis
- Ethereum APIs & testnet deployments
Starterem Dapp can be abstracted under following core components and their interactons as shown in the architecture diagram below:
- App Server (serving web3 app)
- Auth Server
- GraphQL Server
- Smart Contracts
We don't need a pure blockchain based authentication. It is slow and it costs gas. We want to register the user with their public ethereum address and authenticate by proof of ownership of respective registered address.
assumption: we need the users to have metamask browser extension installed.
Our User model consist of following attributes:
- username
- publicAddress
- nonce
- password
publicAddress should be automatically fetched from web3.eth.coinbase which will get the current Metamask account's public address
nonce is a random string generated at the time of sign up and each subsequent verification. It's purpose is for validating the signatures as proof of ownership of the publicAddress
username will be used to identify the user in the app and leaderboard
email will be used to send notifications and system messages
password is an added security layer.
Please look at the workflow below:
helpful reads:
One-click Login with Blockchain: A MetaMask Tutorial
Startereum is an ERC20 token used for staking in the project matches. The game is described in the TWR Concept in the root directory.
- A
waveis a tournament style format for creating the ranked list of projects and consists ofmatches. The number of matches depends on the new projects participating in the wave. Generally for a one-to-one comparison, for N projects in a wave there will be N*(N-1)/2 number of matches. - A
matchis a staking game between two project on a single dimension. A match starts with default parameters such asminStake,maxStake,threshold,endTimebut can be changed if desired. - In order to create a new match, owner must provide
waveID, hashes of the two competing projects astags, and time when the game will expire and reward the winning players. - The game contract implements call scheduling with Ethereum Alarm Clock service to execute the set of functions on reaching the
endTimeof the game. More information on that can be found here: http://blog.ethereum-alarm-clock.com/blog/2016/1/16/the-alarm-service-is-now-available-on-the-testnet - Players should be able to subscribe to the wave of matches and they can only play the matches that are part of the subscribed wave.
- Players can play the match by calling the
stakewithvalueand staking preferencetagon the smart contract. - Players can
unstakeanytime before theendTimeof the match. - If the staking value is below the
minStakeor above themaxStakeof the respective match, then the staking request is rejected and the value is returned (adjusting the gas price) - If the staking preference
tagis not set, then the staking request is rejected and the value is returned. - Staking preference is done by setting
tagto one of the endorsed hashes of the match.
-
The contract implements the following functions to trigger rewards:
find_winner,calc_rewards,distribute_rewards. -
find_winnerfigures out which of the endorsed hashes won the majority of tokens -
This is how we define the reward formula:
- lets suppose a playerA sent
ntokens to the later concluded winning side - lets suppose win:lose is W:L in token volume
- lets suppose
ntoken staked by playerA isp%of the total winning token, i.en/W*100 - then playerA is supposed to win
p%ofL, i.ep/100*L - the reward (
R) for each winning participant will ben(i) * L / W, whereirepresents each unique participant - the total tokens (stake + reward,
T) to be sent back to a winning participant will ben(i) + n(i) * L / W
- lets suppose a playerA sent
-
calc_rewardsworks in the following way:- iterates through all the addresses that staked to the contract.
- In each iteration,
- it checks if the stake is in favour of winning token determined earlier using
find_winneror not - if it is not a winning address, then it skips to the next iteration
- else, it calculates the reward: R using the above formulation
- It then calls
distribute_rewardwith the following value ofT:n(i) + n(i) * L / W
- it checks if the stake is in favour of winning token determined earlier using
-
distribute_rewardssends the desired tokens back to the originating staking addresses with the above calculations
further considerations:
- The user experience of sending the votes to ethereum blockchain can be improved by optimistic voting confirmation and then batching the requests of all the games played in a session together at the end of the game session.
- The house fee needs to be factored before rewarding the winners
- The ethereum gas fee needs to be factored in rewarding the winners
Below are the high level functions that are part of the Startereum smart contract. I have also proposed some design patterns inspired by Numerai, StakeBank and EIP 900: Simple Staking Interface.
player functions
transfer(address _to, uint256 _value)
_tothe address where to send the tokens_valuethe amount to send
stake(uint256 _value, bytes32 _tag, uint256 _waveID, uint256 _matchID)
_valuethe amount to stake_tagthe preference hash of the project to stake on_waveIDthe ID that represent the wave_matchIDthe ID that represent the match being staked into
unstake(uint256 _value, uint256 _waveID, uint256 _matchID)
_valuethe amount from the previously staked value to unstake_waveIDthe ID that represent the wave_matchIDthe ID that represent the match being staked into
Owner functions
createWave(uint256 _waveID, uint256[] _tags)
_waveIDthe ID to create the wave_tagsthe array of all the participating projects in this wave
createMatch(uint256 _waveID, uint256 _matchID, uint256 _endTime, uint256 _tagA, uint256 _tagB, uint256 _minStake, uint256 _maxStake, uint256 _threshold)
_waveIDthe ID that represent the wave_matchIDthe ID to create the match_endtimethe future block time when the game will expire and reward will be triggered_tagA, _tagBthe hashes of the two projects that constitute this game_minStakethe minimum staking value required to play the game. (default is set to 3 STR)_maxStakethe maximum staking value required to play the game. (default is set to 100 STR)_thresholdthe minimum value of total staked value to finalise the game. (default is set to 1000 STR)
stopMatch(uint256 _waveID, uint256 _matchID)
_waveIDthe ID that represent the wave_matchIDthe ID that represent the match
resumeMatch(uint256 _waveID, uint256 _matchID)
_waveIDthe ID that represent the wave_matchIDthe ID that represent the match
disableStoppingMatch(uint256 _waveID, uint256 _matchID)
_waveIDthe ID that represent the wave_matchIDthe ID that represent the match
Inspection functions
- Returns the data about wave: Number of matches, List of matches, List of project tags
getWave(uint256 _waveID)
- Returns the data about match: participating project tags, ending time of the match, status of the match
getMatch(uint256 _waveID, uint256 _matchID)
- Returns the status of the match: 0 ended, 1 live
getMatchStatus(uint256 _waveID, uint256 _matchID)
- Returns the result of the match: tokens staked for both project tags (tokensForTagA, tokensForTagB), Winner tag value
getMatchResults(uint256 _waveID, uint256 _matchID)
- Returns the staked value corresponding to an address in a match: stakedValue
getStakeFor(uint256 _waveID, uint256 _matchID, address _staker, bytes32 _tag)
- Returns the reward value corresponding to an address in a match: rewardValue
getRewardFor(uint256 _waveID, uint256 _matchID, address _staker, bytes32 _tag)
Events
The following events are emitted from the contracts. The events is a good way to generate the data layer for the game interfaces. By storing the events in our centralised data store, we can improve the user experience of the app by offering them live statuses of the matches and their results.
- Staked(stakerAddress, tag, totalAmountStaked, waveID, matchID)
- WaveCreated(waveID)
- MatchCreated(waveID, matchID, endTime, minStake, maxStake, threshold)
- MatchStopped(waveID, matchID)
- MatchResumed(waveID, matchID)
- MatchEnded(waveID, matchID)
Design pattern
Contracts
- StartereumBackend.sol
- StartereumDelegate.sol
- StartereumShared.sol
StartereumShared implement data structures that will be used in the other smart contracts mainly StartereumBackend and StartereumDelegate
StartereumShared extends an OpenZepplin interface called Shareable, StoppableShareable that implements owner control features such stopping and resuming the contract functions.
StartereumBackend implements basic ERC20 functionalities such as transfer, minting, withdraw etc, and also creates interfaces for staking match operations via StartereumDelegate contract methods. The delegate contract can be upgraded but the backend contract is immutable.
Since, we are writing directly on contracts, we will use the contract events to populate data models. It is recommended to use graphQL as the data serving layer as the project will be evolving fast to update existing models or add new models.
We have following data models and querying requirements:
User model
- Account of balances
- how many/what games a user has played
- what is the current balance of the user wallet
- how many/what games a user has won
- how many/what games a user has lost
- User management
- what is my publicAddress
- what is my latest nonce (protected method)
- what is my email
- what is my password
- update functions
Game Model
- Game status
- Is the game live?
- Is the game finished?
- When is the game ending?
- What is the result of the game?
- What are the endorsed projects of the game?
- What is the game type?
- Game collection and session
- create a new game
- list of active games
- list of inactive games
- create a new session of games for a user A
- commit a session on chain

