My experience with Solidity, the language of the Ethereum blockchain

Julia Wu
6 min readMar 24, 2018

--

As a fresh college graduate, I come from a relatively mainstream programming background. For the past few months, I have been using Solidity to build smart contract-based dApps with a web UI. I learned a bit about object representation, mapping, event logging, interacting with the smart contract via the command line and querying external data from the contract through Solidity. In this post, I will highlight some of Solidity's noteworthy properties while reflecting on my own experience learning it.

Solidity is a programming language that runs on the Ethereum Virtual Machine (EVM). It is a turing-complete language born out of Ethereum’s motivation to provide an alternative protocol for distributed applications. It is contract-oriented as opposed to Bitcoin’s assembly-like and limited scripting language, and was inspired by C++, Python and JavaScript — it can also work with JavaScript.

The Smart Contract

Unlike Python, variables are statically typed instead of dynamically typed in Solidity. Statically typed languages have variables type-checked (enforced) at compile time: the compiler can detect when there is a type error and throw an exception before the code can run. In dynamically typed languages, a program will execute even if there is a type error and would only throw an exception or exit when a specific function/line with the type error gets executed.

State Variables

It is expensive to copy complex types such as arrays and structs, so they need to be handled differently to avoid taking up too much space. We have the options of storing them in memory, which is not persistent, or keeping them in storage — where state variables are located.

The default is memory for function parameters and storage for local variables.

Sometimes, we explicitly define the state when declaring a variable to enforce their location:

uint[] memory a = new uint[](7);

Gas

Gas is an indispensable part of activity on the Ethereum blockchain — it is attached to every transaction on the contract, not just in the form of value transfer. Executing a function in the contract is a transaction, and there is a cost attached to this execution (expressed in a unit of ether). Gas also protects against malicious attacks, making it unreasonably expensive to try to slow down or freeze the network with a large amount of transactions. So how is the gas cost of a transaction calculated?

transaction cost = units of gas needed for atransaction * price per unit of gas

The price per unit of gas can vary, because it needs to adjust to the price movement of Ether. It wouldn’t be feasible for the cost of a computational transaction to be higher just because Ether has gained in value through trading volume.

We can also compare the transaction cost to the economic concept of cost of labor. If you have studied economics, then you must have looked at a firm’s costs of production. There are fixed costs and variable costs, where variable costs are the cost of the variable input:

VC = w*L

In this case, w is the wage rate and and L is the total labor employed. We can take the same framework to interpret transaction costs in Ethereum:

The gas cost of a transaction is similar to the variable cost, and the wage rate would be the price per unit of gas (expressed in a unit of Ether), which is variable. L is the amount of gas units (labor) necessary for a transaction to be performed. So when we multiply gas price per unit*gas units, we get the gas cost of a transaction.

Events

In my experience so far, events are the closest thing to print statements in Solidity. Events let us use EVM logging facilities which can be used to trigger JavaScript callbacks in the UI of a dApp, which can listen for these events. For example, when something happens in the smart contract itself, the frontend has a way to know that it took place.

When I was implementing the a blockchain version of the red envelope given out during Chinese New Years, I defined an event in the contract that gets triggered after some gift money is received:

event RecordingGift();function recordGift(uint _amount, string giftPurpose) {    RecordingGift();
// do something to add funds to a data structure that tracks gift money
}

And in my JS code, I set up a listener:

var recordGiftEvent;RedEnvelope.deployed().then(function(instance){    envelope = instance;    recordGiftEvent = envelope.RecordingGift();    recordGiftEvent.watch(function(error, result){        if(!error){            console.log("Verifying merchant in contract! ", result);        } else {            console.log(error);        }   });}

Events can store information in the transaction’s log — the log is a special data structure in the blockchain that will stay in the blockchain as long as a block is accessible.

Function Modifiers

Function modifiers in solidity allow us to assign and modify a function’s behavior. This could mean declaring a specific condition, such as: the owner is the only one that can execute a certain function, or a contract needs to contain a specific balance in order to perform an action:

modifier onlyOwner {
require(msg.sender == owner);
}
function addToCart(string _productName, uint _productId) onlyOwner public {}

Modifiers are also inheritable — these properties can be used by any contract that implements the contract containing the modifier:

contract parent{    modifier onlyOwner{        require(msg.sender == owner);        _; // only continue executing a function if the requirement is met    }}contract child is parent {    function doSomething() public onlyOwner {
// function body here
}
}

As long as a function includes the modifier in its declaration, it will adopt the condition in the modifier. The modifiers can also be overridden by the contracts that inherit them.

Mappings

Mappings behave similarly to hash tables — they have key-value pairs and the keys can be pretty much any type except for a mapping, a dynamically sized array, a contract, an enum and a struct. The value can be any type, including mappings.

Mappings in Solidity are similar to defaultdicts in Python: every possible key exists and is by default mapped to a value that is all 0s.

The keys of the mapping are not actually stored in a mapping — only the keccak256 hash used to look up the value is stored in the mapping.

If you want to have a list of all the keys in a mapping or iterate through the keys, you actually need a separate list keeping track of those keys. Often times, we have setups like:

Mapping (string => string) mapName;string[] public keyList;

So when adding to or removing from the map, you have to update the key list as well.

Structs

Structs are a widely-used type not just in Solidity — it’s great for defining objects with multiple properties. For example, you can have a Product struct whose properties are price, name and id.

A struct cannot contain a member of its own type, and the size of the struct has to be finite. But it can contain mappings and arrays.

I have used structs pretty recently to represent objects in my smart contracts. In the past, I’ve run into issues with space when structs had too many fields. Struct types are usually assigned to a local variable in the smart contract (default storage data location) so that the struct is not copied — only a reference to it is stored.

Inheritance

This is a concept most developers are familiar with, and just like Structs, it comes in handy when developing smart contracts in Solidity. For a contract to inherit another contract, we add is <name of parent contract> to the declaration of the inheriting contract:

contract Dog {    function Dog() public {        // constructor here    }    modifier onlyOwner {        require(msg.sender == owner);    }}contract Shibainu is Dog {
// contract inherits Dog
}

If contract Dog has function modifiers like onlyOwner, then contract Shibainu can use them. For example, a function in the Shibainu contract can use the onlyOwner modifier. This inheritance system is very similar to Python’s.

When a contract inherits from multiple contracts, only a single contract is created on the blockchain.

Closing thoughts

In general, I have found Solidity to be less verbose and “flexible” than other languages like Python, meaning that there is a wider gap between the goal I have in my mind and the code actually performing that goal without errors. However, it is still the best language for building smart contracts and Ethereum and has various people maintaining/developing.

Pros:

  • Good object- and contract-oriented properties
  • Similar to Python’s interactive mode, we can also test contracts through the command line with tools like Truffle

Cons:

  • Hard to debug and use print statements
  • Hard to import libraries — often have to clone the entire repo from Github
  • String parsing is the opposite of what it is in Python — limited and inflexible (cannot join 3 string slices at a time)
  • Still very early stages for making API calls

Relevant sources

https://en.wikipedia.org/wiki/Marginal_product_of_labor

http://solidity.readthedocs.io/en/develop/index.html

https://coursetro.com/courses/20/Developing-Ethereum-Smart-Contracts-for-Beginners

--

--

Julia Wu

Building something new. Prev. eng at Brex, Apple, MSFT. More at juliawu.me