# Fun with Ethereum smart contracts

## 1. Prerequisites and foreword

This post will go through topics that are related to the blockchain such as zero-knowledge proofs (PoW, PoS etc.) or assymetric cryptography. I will also write some smart contracts in Solidity; hence programming-related jargon might appear. There are, however, several levels of difficulty at which this post can be read, which makes it a good resource to refer to if you later gain knowledge in this area. And don't worry, I'll make a brief introduction in layman's terms for each concept that might require one. I am delighted to share my experience and knowledge in such a domain, as it is quite unique and at the intersection of several niche interests. To illustrate that, let me simply state that I do not personally know anybody who codes smart contracts. If you do though, don't hesitate to reach out !

## 2. The heck is a smart contract ?

I find the definition given on Wikipedia clear enough :

A smart contract is a computer program or a transaction protocol which is intended to automatically execute, control or document legally relevant events and actions according to the terms of a contract or an agreement.

An example of a basic smart contract (outside of the blockchain) that is often taken as a case study is a vending machine. Indeed, the software inside the machine checks which conditions are met and acts accordingly. Did you put the required amount for the snack you selected ? Let it drop then. Are they out of stock ? Return the money and display an error message. Is the amount not high enough ? Display the remaining amount to be paid.

Ethereum takes this concept and extends its capabilities to almost anything. One of its advantage is its decentralized nature and removing the need for a trusted party. In Eth's case, the program is usually written using the contract-oriented programming language called Solidity. Since the Ethereum Virtual Machine (the runtime environment) is Turing-complete, there is no way to determine if the code will come to a halt or not. To circumvent that, each transaction costs gas, an amount expressed in Gwei, a subdivision of Ether (1 Gwei = 10-9 ETH), hence why there are fees associated with smart contract interactions (view-only functions excluded). These are quite high now but it should change with Eth 2.0 coming.

## 3. Examples of applications

When solving a problem, one should first seek the appropriate tools to use. In a lot of startups or businesses, that is not done correctly. Instead, an emphasis is put on buzz-words, such as machine learning, blockchain, military-grade encryption and much more. That is why I'm putting a little disclaimer here: the following examples might be more on the side of "How to use Ethereum smart contracts to do X ?" rather than "Which tools are the most suited to do X ?" which is a very bad practice. However, I still see a lot of advantages on putting those applications on the blockchain, the main one being the absence of a third-party or central authority, removing the need for trust.

My examples might inspire you to code your own program or even develop a dapp (decentralized application). Should this be the case, you can contact me for business inquiries, tip me if my work has been helpful to you, but most importantly give credit & let me know about it. I'm curious to see where this post will take you. Also, these examples are not ready for production so make sure you know what you are doing!

There pieces of code that will be required in any Solidity contract (More info in the documentation: Layout of a Solidity Source File). For instance, the first line is a specifically formatted comment that indicates the license. Being formatted that way, it is easily readable by a machine. I chose the MIT license but I didn't research this enough to have a strong opinion on it. The second line specifies the version of the compiler to be used. I still have to understand the implications of using different ranges (The ^ ensures that it is compatible with versions from 0.8.0 to 0.9.0) but I consider this a detail since I'm just a beginner. I'll look into it more extensively once I have the opportunity to write production code. To begin the contract, we add a line specifying its name and opening curly braces. We now have a place to start :


pragma solidity ^0.8.0;

contract ContractName {

}



They will often contain a constructor() { } which is a piece of code that is executed once; when the contract is deployed. Also, the variable types need to be defined, that is why you will encounter keywords such as uint, which is an unsigned integer ($$\in \mathbb{N}$$), another one specifying a sequence of characters called string, an Ethereum address, and much more.

### 3.1 Shared ownership

When thinking about blockchain applications, I like to try to come up with real-world use cases that require the least amount of technical knowledge from the user. For this one, I thought about a group of friends (let's say 4) owning a property and renting it to someone. It would be annoying for that person to make four transactions each month. Moreover, we may want to future-proof the agreement in case there is a discrepancy in said group. We can solve that with smart contracts. Instead of the renter paying to one of the friends, they can pay to the smart contract, which will automatically divide (equally or not, depending on the decision taken) and transfer the amount to each co-owner. In it simplest form, it looks like this:


pragma solidity ^0.8.0;

contract SharedProperty {

constructor() {
owners = [payable(0x704A1bFD15c629E08EC6824470c37a9aA81558c7),
payable(0xa62E10cD675A847E15399ea473AcC91f3BF3775a),
payable(0xC6C4187d9ca7585Df74E4df56F64A48bb3976aA4)];
}

uint rent = msg.value;
for (uint i=0; i<owners.length; i++) {
co_owner.transfer(rent/owners.length);
}
}
}



This contract has been deployed on the ropsten network and, as the first transaction of 0.4 ETH shows, it works as intended. You can give it a try, and make sure you have some test Ether. Don't send real Ether though, I won't have access to it. If you want to spend real ETH on something, please consider supporting my work by donating !

The line just before the constructor defines an array (by using []) of payable addresses, named owners. This allows us to add the addresses of the four co-owners in the constructor. Note that they are hardcoded, so we cannot change them but we can easily think about a function that would achieve that. For example, we could create one that would replace an address if and only if the sender is the owner of that address. Here it is in Solidity:


for (uint i=0; i<owners.length; i++) {
if(msg.sender == owners[i]) {
}
}
}


And to view the current owners array:


function getOwners() public view returns (address payable[] memory) {
return owners;
}



This improved contract has also been deployed on the testnet.

While writing this post, I stumbled upon a StackExchange answer which mentioned that the use of a mapping instead of an address[] array was a better practice since it should be more efficient and thus save gas. In the case of this contract, this is not conceivable because we still need to loop in order to make a transaction to each owner. However, using a mapping may come in handy in a lot of cases. Also, note that at the time of writing, there is no way to use an in keyword, such as :


for (owner in owners) {}



because it is not supported in Solidity.

### 3.2 Money games

Games are, in general, based on a precise set of rules, which makes them easy to code. That is why they are often used in programming tutorials; they mainly incorporate several if-then-else statements. The Ethereum blockchain, mixing monetary value and programming in a trustless way, is an ideal platform on which to develop gambling/money games. Additionnaly, they incorporate a problem that I wanted to address: the generation of random numbers on the blockchain.

#### 3.2.1 Random number generation on the blockchain

As you may know, the generation of random numbers is a specific domain of computer science. The main techniques currently employed actually make use of PRNGs, pseudorandom number generators. Those generators are not truly random, as, for example, they migh use the clock time of your computer (modulo the adequate number, in order to have an integer in a specific range), which isn't. PRNGs often implement the linear congruential generator, a standard in the field. We can afford to use PRNGs in a majority of applications (simulations, shuffling, statistical sampling …) because for the most part, trying to find the pattern or predicting the next number is just not worth it. On the other hand, for sensitive subjects potentially involving big amounts of money or anonymity, it is needed to have true RNGs. For that, we can link real world data that, although not random, are extremely hard -nay impossible- to predict. Such data are often coming from chaotic systems. We could even deal with truly random physical phenomenona such as quantum fluctuations or radioactive decay.

In the blockchain space, the will to have a trustless or decentralized source of randomness adds complexity to this task. We cannot trust a single lab to send us its quantum data because it comes from a centralized source, nor can we request a user to submit a hardware-generated RN because they could manipulate it. It is no wonder that "How can I securely generate a random number in my smart contract?" is currently the second most upvoted question on the Ethereum stackexchange.

It seems like Chainlink's VRF provides a solution for this. I still need to dig deeper into the underlying mathematics but as I understand it now, Chainlink is a decentralized oracle and when you request a random number, you need to provide a (pseudo-random) seed which, after being mixed with other data, is used by the chainlink network to return a random number. A lot of Ethereum's cryptographic functions are used, in order to perform actions such as recursive hashing. To be used, some LINK must be sent to the contract address beforehand, because requesting a random number costs a LINK fee.

Using the minimal working example provided by Chainlink, we can add a function that will take a number $$n$$ as a parameter and output a random number between 0 and $$n-1$$:


contract RandomNumberInRange is RandomNumberConsumer {
function getRandomInRange(uint userSeed, uint _range) public returns (uint) {
getRandomNumber(userSeed);
uint number = randomResult % _range;
return number;
}
}


You may want to check the link I just gave in order to understand it better. Using the is keyword, we are creating a contract that inherits from the one provided by Chainlink. Many other functions can be built this way, but I'm not going to implement them in this article.

#### 3.2.2 Choice of game

At first, I wanted to code Blackjack but then I faced some issues:

1. Although it is one of the simplest casino games, the rules are quite convoluted. Using it as an example would not have been a wise choice; not only does the reader have to know the rules of the game but reading the code would also be confusing.
2. A lot of casino games require the users not to display their cards. It isn't much for anonymity but rather to prevent cheating and to avoid giving an advantage to the other players. Since the blockchain is public, keeping track of card ownership without revealing it to others is complex. Each solution to hide that data has a degree of tolerance attached to it. For example, setting a variable as private might be enough for some applications but its name is a bit misleading. Indeed, as stated by the Solidity docs :
Everything that is inside a contract is visible to all observers external to the blockchain. Making something private only prevents other contracts from reading or modifying the information, but it will still be visible to the whole world outside of the blockchain.
We could also think about building a part of the game on a website, which would create a partially decentralized application. I'm not sure on how exactly this would be achieved though, maybe by using cryptographic signatures and encrypting some data on the client's machine.
3. I am not qualified enough -yet- to quickly setup a frontend and UI for a dapp, which prevents me from adding the card designs and link them to their ID (a number between 0 and 51). I could have skipped this step and have the users deal with the IDs but considering the previous points and that such a decision would make the user experience less smooth, it was smarter to choose another game.

#### 3.2.3 Lottery

That is the game I went for. Here, we'll make a self-filling pool. That is, the prize will start at zero and will be increased for each ticket bought. After a predetermined amount of time (stored in the gameDuration variable) has passed, the ticket sales will close and the function pickWinner() (that will select a winner and transfer the funds to it) will be callable. I initially thought about restricting the access to that function to an administrator (the creator of the contract) but one problem is that if the owner didn't call it, the funds would be locked. There are a lot of ways in which that can be avoided (adding a time limit, change the owner based on a consensus …) but I wanted to demonstrate the power of good smart contract design. That is why I wrote it in such a way that anyone could call the function that picks a winner! Because a timelock was put, and that -supposedly- a good RNG is used, alea iacta est and let the winner be picked!

We will first define the price of a ticket. For that, I'll go with a fixed price in Ether, let's say 0.01 ETH. We define the following variables :


uint public ticketPrice = 0.01 ether;
uint public gameDuration = 1 days;
uint public startTime;


Where, in the first two lines, we used Solidity units such that the preceding value is automatically converted to the appropriate format. Here, putting ether after 0.01 multiplies the value by 1018, essentially expressing the amount in wei, the smallest denomination of ether, which can then be stored as an unsigned integer and thus avoid problems originating from floating-point numbers . Similarly, days is a unit that converts the preceding value in seconds, which is also stored as an uint. The players variable is an array of addresses and the indexes represent the ticket numbers. In order to apply a time lock, we need to keep track of when the game started. We'll use startTime and overwrite it everytime a new game is started. However, we need to initialise it, that is why we add a line in the constructor:


constructor() {
startTime = block.timestamp;
}


By doing so, the value of startTime is first set to the contract deployment time.

We now need to define the two main functions of our contract: buyTicket() and pickWinner(). The former is payable and requires the ticket sales to be opened, while the latter cannot be called if tickets are still being sold. Here is how I wrote the buying function:


require(block.timestamp <= startTime + gameDuration, "The ticket sales are closed");
require(msg.value == ticketPrice, "The amount sent is different from the price of the ticket");
players.push(msg.sender);
}


The second requirement ensures that the correct amount is paid. I could've written it in such a way that if a player sends too much funds, a ticket would still be bought and the excess funds would be sent back but it would cost (the user) additionnal gas, and it makes it clearer if only the ticket price can be sent as an amount. The last line adds the address of the sender to the array of players. That will link an index (that can be seen as the ticket number) to its buyer.

The following function picks and awards a winner. It first ensures that the ticket sales are closed, then I added a statement to handle the case where no one played. This will directly start a new game and save some more gas. In the case where people have played, a winning ID in the range of the tickets sold is picked at random and the person who bought that ticket is the winner. After that, the funds are transferred, the variables are deleted (or overwritten) and a new startTime is set.


function pickWinner() public {
require(block.timestamp > startTime + gameDuration, "The ticket sales are still open");
if (players.length != 0) {
uint ticketsSold = players.length;
//get a random number using the function I defined earlier
uint winningID = getRandomInRange(block.timestamp, ticketsSold);
delete players;
delete winningID;
delete winner;
}
startTime = block.timestamp;
}


Please note that Chainlink's VRF is not very mature yet and it requires the deployment of several other contracts, which may confuse you. If you want to keep some simplicity, you can deploy a (more insecure) version on a testnet by using


uint winningID = block.timestamp % ticketsSold;




uint winningID = getRandomInRange(block.timestamp, ticketsSold);



One improvement of this contract is the addition of two functions callable by the owner only. For this, I can add a modifier and add it to the functions. I will let the reader understand how they work.

Here is the final contract:


pragma solidity ^0.8.0;

contract Lottery {

uint public ticketPrice = 0.01 ether;
uint public gameDuration = 1 days;
uint public startTime;

constructor() {
owner = msg.sender;
startTime = block.timestamp;
}

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

require(block.timestamp <= startTime + gameDuration, "The ticket sales are closed");
require(msg.value == ticketPrice, "The amount sent is different from the price of the ticket");
players.push(msg.sender);
}

function pickWinner() public {
require(block.timestamp > startTime + gameDuration, "The ticket sales are still open");
if (players.length != 0) {
uint ticketsSold = players.length;
uint winningID = getRandomInRange(block.timestamp, ticketsSold);
delete players;
delete winningID;
delete winner;
}
startTime = block.timestamp;
}

function changeTicketPrice(uint _newPrice) public onlyOwner {
require(players.length == 0, "In order not to disadvantage players, the price cannot be changed if the current game already has players");
ticketPrice = _newPrice;
}

function changeGameDuration(uint _newGameDuration) public onlyOwner {
require(players.length == 0, "In order not to disadvantage players, the duration cannot be changed if the current game already has players");
gameDuration = _newGameDuration;
}

}



## 4. Closing thoughts

I initially wanted to provide 4 or 5 examples (I'm not out of ideas at all) but during the redaction of this post, I noticed that I had been more verbose than I thought I was going to be. That is not an issue because it may have helped some readers to get a better understanding on the potential of Ethereum. Of course, there are improvements that can be made, especially in regards to gas cost optimisation but I have already provided some hints here and there. Smart contract redaction is something that I like more and more, their applications are enormous, as decentralized finance (aka DeFi) could compete with traditionnal finance in the near future. I will write other posts related to Ethereum, and I'll probably look into other smart contract capable blockchains but I've got my heart set on Ethereum for the moment.