/ BLOCKCHAIN, ETHEREUM, SMART CONTRACT

Starting with Ethereum - Smart contracts

This post is the 4th in a serie dedicated on starting development with the Ethereum blockchain. Previous ones are the following:

  1. Setup
  2. Writing a contract
  3. Deploying and running a contract

Let’s face it. For a smart contract, implementing an addition of two integers is not very "smart". In this post, I’ll try to go beyond this former trivial example, and propose a simple but real use-case.

As an example, let’s consider the business domain of a referendum.

A referendum use-case

A referendum is a direct vote in which an entire electorate is invited to vote on a particular proposal.
— Wikipedia
https://en.wikipedia.org/wiki/Referendum

To keep things simple for a blog post, there shouldn’t be too many requirements. Here are some very minimal ones:

  • There are x votes per referendum. Let’s name such a vote a "voting token".
  • An account can submit multiple voting token. In other words, there’s no check whether an account submitted one or more tokens.
  • Votes are public. Third-party can check which account submitted which tokens. In other words, anyone can check transactions and who created them - which is the benefit of the blockchain.
    However, it’s not trivial to bind an account to a specific identity.
  • Answers can be either "yes", "no", or "blank".
  • The account that creates the referendum also gets credited with all initial voting tokens. It’s up to the creating account to dispatch the tokens to other accounts so they can submit them.
  • At anytime, the state of the submitted voting tokens can be queried.
  • When all voting tokens have been cast, the result should be computed.
  • Any account can subscribe to the final result.

The model

Here’s a proposal for the conceptual model:

  • There’s a limited amount of available voting tokens associated with a referendum.
  • A vote is cast with a specific answer.
conceptual model proposal
Figure 1. Referendum conceptual model proposal

And the sequence diagram:

voting sequence
Figure 2. Voting sequence

How do features, the conceptual model and the sequence translate into a Solidity implementation model?

The first step is to design the voting token object itself. There’s a need for a registry, one that references which account owns which amount. Ethereum is made by design to create custom crypto-currencies. They offer a solid documentation and some code templates for that. This translates very well into a voting token. I just updated the code a bit to better fit the requirements:

  • Removal of the name, a symbol is more than enough
  • Transferring voting tokens can only originate from the token owner - the function caller. This will be used after contract creating, to give them to voter accounts. It can also be used afterwards, to pass the voting token to another account.
  • On the other hand, burning a token i.e. using it, will be called from the referendum contract and needs a reference to the original caller (see below).
implementation model proposal
Figure 3. Referendum implementation model proposal

The code

The full source code is available here.

Let’s highlight some interesting functions. At the hear of the matter is the castVotes() function.

function castVotes(Answer _answer, uint256 _count) public {
    require(referendum.open);                              (1)
    VotingToken tokenContract = VotingToken(tokenAddress); (2)
    tokenContract.burn(msg.sender, _count);                (3)
    handleVotes(_answer, _count);                          (4)
    Vote(msg.sender, _answer, _count);                     (5)
    if (hasAllVotes()) {
        close();
    }
}
1require is a Solidity keyword to manage pre-conditions in Design by Contract Programming. A failed require will rollback any state passed during the current call. Here, we disallow voting on closed contract instances.
2Creates a reference to an existing contract! This is one of the most important feature of Ethereum: a contract instance can call the method of another contract instance, thus chaining method calls across contracts.
3Effectively calls the burn() method of the contract instance. Notice the caller account is passed as an argument - in order to remove the voting toking from the account.
4Increment the vote count.
5Send an event. Events are essentially blockchain logs, but they also allow to be subscribed to from JavaScript clients.

Another interesting bit is the following function:

function getResults() public view returns (uint256 yesCount, uint256 noCount, uint256 blankCount) {
    return (referendum.votes.yesCount, referendum.votes.noCount, referendum.votes.blankCount);
}

For Java developers, it might be a surprise, but Solidity can not only return multiple values but also allows to name them for better maintainability.

Also, note the view modifier. In writing the first contract, we used the pure modifier. pure means the return value only depends on the input parameters - hence, there’s no write to nor read from the blockchain. The view modifier means there’s no write, but there might be a read.

If there’s no read, then the modifier shouldn’t be view but pure

Usage

Let’s deploy both contracts on the Remix network:

Then start interacting with the deployed contract:

var referendumContract = web3.eth.contract(<content/of/ABI>);
var referendumContractInstance = referendumContract.at('0x48dbcdd695fb6a7728dc656abd8baa460d7ff081');
referendumContractInstance.getQuestion();

This yields the question I initialized the vote with - check if you want.

At this point, voting tokens should be given to other accounts. However, it’s easier to directly cast the votes from the main account.

referendumContractInstance.castVote(0);
referendumContractInstance.getResults();
Interestingly enough, I get an exception when calling castVote() from the console, but it works from the Wallet. Probably because I’m not signed in in the console.

As stated above, Solidity events write into the blockchain. But, they can also be listened to from web3.js, via events. The following code will trigger every time a Finish event is written into the blockchain (from the close() method).

var finishEvent = referendumContractInstance.Finish();

var consoleLog = function(error, result) {
  if (error) {
    console.log(error);
  } else {
    console.log("Referendum " + result.args._referendum + " finished");
  }
}

finishEvent.watch(consoleLog);

Conclusion

At this point, we really got into smart contracts. The contracts designed have some degree of business logic. We saw some of Solidity features: events, require, view and how to call a contract from another one.

On your side, you can re-use the Solidity source, build upon it and deploy your own referendum to the blockchain.

Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with narrower interests like Software Quality, Build Processes and Rich Internet Applications. Currently working for Exoscale. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Read More