I’ve been working on a rock, paper, scissors Ethereum DApp using Solidity, Web3 and the Truffle framework. I hit a few difficulties trying to replicate functionality that would normally be trivial in a non blockchain world so I thought I’d share what I learned.
My first thoughts for the DApp was to display a list of existing games that people had created. Normally if I were doing something like this in Django I’d create a game model and save any new games in the database. To display a list of existing games on the front end I’d query the db and iterate over the returned collection. (I realise storage is expensive when using the Ethereum blockchain but I thought trying to replicate this functionality would make sense and would be a good place to start.)
Solidity
Structures
While investigating the various data types that could be used I found the Typing and Your Contracts Storage page from Ethereum useful. I settled on using a struct, a grouping of variables, stored under one reference.
struct Game {
string name;
uint move;
bool isFinished;
address ownerAddress;
uint stake;
uint index;
}
That handles one game but I want to store all games. I attempted to do this in a number of different ways but settled on mapping using the games index as the key. Every time a new game is added the index is incremented so I also use gameCount to keep count of the total games.
mapping (uint => Game) games;
uint gameCount;
struct Game {
string name;
uint move;
bool isFinished;
address ownerAddress;
uint stake;
uint index;
}
To add a new game I used this function:
function StartGame(uint moveId, string gameName) payable {
require(moveId >= 0 && moveId <= 2);
games[gameCount].name = gameName;
games[gameCount].move = moveId;
games[gameCount].isFinished = false;
games[gameCount].ownerAddress = msg.sender;
games[gameCount].stake = msg.value;
games[gameCount].index = gameCount;
gameCount++;
}
I also added a function that returns the total number of games:
function GetGamesLength() public returns (uint){
return gameCount;
}
Returning A Structure
Next I want to be able to get information about a game using it’s index. In Solidity a structure can only be returned by a function from an internal call so for the front end to get the data I had to find another way. I went with the suggestion here — return the fields of the struct as separate return variables.
function GetGame(uint Index) public returns (string, bool, address, uint, uint) {
return (games[Index].name, games[Index].isFinished, games[Index].ownerAddress, games[Index].stake, games[Index].index);
}
Front End
On the front end I use Web3 to iterate over each game and display it. To begin I call the GetGamesLength() function. As we saw previously this gives the total number of games. Then I can iterate the index from 0->NoGames to get the data for each game using the GetGame(uint Index) function.
When my page first loads it calls:
getGames: function() {
var rpsInstance;
App.contracts.RpsFirst.deployed().then(function(instance) {
rpsInstance = instance;
return rpsInstance.GetGamesLength.call();
}).then(function(gameCount) {
App.getAllGames(web3.toDecimal(gameCount), rpsInstance);
}).catch(function(err) {
console.log(err.message);
});
},
Web3 – Promises, Promises & more Promises…
The getAllGames function calls GetGame(uint Index) for each game. To do this I created a sequence of promises using the method described here:
getAllGames: function(NoGames, Instance){
var sequence = Promise.resolve()
for (var i=0; i < NoGames; i++){(function(){
var capturedindex = i
sequence = sequence.then(function(){
return Instance.GetGame.call(capturedindex);
}).then(function(Game){
console.log(Game + ' fetched!'
// Do something with game data.
console.log(Game[0]); // Name
console.log(Game[1]); // isFinished
}).catch(function(err){
console.log('Error loading ' + err)
})
}())
}
}
Conclusion
Looking back at this now it’s all pretty easy looking but it took me a while to get there! I’m still not even sure if it’s the best way to do it. Any advice would be awesome and if it helps someone even better.