Ethereum ÐApps Programming Distilled — Part 2

What’s so special about Ethereum, in comparison to Bitcoin? How to write a Smart Contract with Solidity, and what are its constraints and workarounds? How much do transactions cost, and what are gas and gas price? And finally…how can a developer make money with this?

Marco Bellinaso
13 min readMar 6, 2018

This is a multi-part article. Here are the links to the other parts:

What’s so special about Ethereum?

Bitcoin has, by design and because of safety concerns, limited scripting capabilities that makes it unsuitable for applications more complex than basically transferring digital money from an address to another. This is why Bitcoin is mostly “just digital dollars”, not a platform to build upon.

On the other hand Ethereum was designed from the beginning to be a platform to run custom scripts and logic (these are called “Smart Contracts”), with a developer-friendly Turing-complete language named Solidity that allows quite a decent flexibility (its compiler generates assembly-like code behind the scene, but you don’t need to see that.). Let’s be clear, it’s still a primitive language in comparison to other modern languages (not just Swift, Kotlin or C#…but even JavaScript is so much more powerful), and it has many constraints; but it will probably be extended, and even now it can be used for some non-negligible logic.

Ethereum has two types of accounts: 1) “external accounts” that are owned by humans and controlled by public/key pairs (the public key is the account’s address, and the private key is the key to digitally sign a transactions). 2) “contract accounts”, ie Smart Contracts, which are controlled by the contract’s code. The runtime environment for Smart Contracts is called Ethereum Virtual Machine (EVM).

Read more about all this in the official documentation, it’s surprisingly very clear.

Some examples?

  1. A voting system or poll, in which every account can cast a vote and only one, until a specific date, after which the Smart Contract declares the winning option. Votes will be kept permanently and it won’t be possible to change them. (There’s a question of how to validate the accounts that can vote, since creating an account is free and you can create as many as you want, but that’s another story :)
  2. An escrow service that automatically transfers assets (eg: some cryptocurrency amount) to a specific address after some conditions are met (eg: all parties mentioned in a contract digitally sign the contract).
  3. Betting/gambling games, run on top of public rules that you can trust (how do you know if the online gambling games running on a closed centralised system cheat?)
  4. An insurance policy that accepts payments for the premium, and automatically refund you if some condition happens (eg: your flight is cancelled).
  5. A crowd-funding system, where people send some amount of ETH to the contract, and the contract transfers the sum to a recipient if some conditions are met (eg: the minimum sum is reached, so that the project can start).
  6. A Smart Contract can also create new types of sub-tokens/coins, and a crowd-funding system could give you back some of these tokens when you transfer ETH to it. This is basically what an ICO (initial coin offering) is: you pay ETH (that will fund a project or startup) and you get back some of those new coins — later on (as long as the ICO is not a scam but an actual serious project that moves forward and is implemented successfully…) you will be able to spend those coins to use the service being built, or will be able to sell or convert the coins to some other currency (this is what “exchanges” do) potentially for more than what you initially spent (if the project becomes successful and the price of its coins increases). Because this is a very popular use case, there are a number of “standard” contracts that you can use as a starting point to derive your own custom tokens: ERC20 for example is a base contract that defines a public interface (with functions like totalSupply, balanceOf, transfer etc.) that you can inherit from, and implement in your own token contract rather than coming up with something completely new (you could technically, but why, if many people have already spent plenty of time to determine how to do it properly?). In addition to ERC20 there’s ERC223, ERC721 and others. Refer to this article by Lukas K and this other one by Shaan Ray to know more about this topic and see actual examples. Even the official Ethereum website has a full example for this.

Constraints / limitations

In reality, some of the examples above aren’t really doable in a fully decentralised way, because what you can do in practice at the moment is limited, and for example you can’t call external resources such as a 3rd party external API call. And that would be necessary for a thing as simple as generating a random number (you can create one from Solidity but it wouldn’t really be random and malicious nodes could cheat), or for retrieving external data such as a flight’s landing time, a currency conversion exchange rate, the weather conditions etc…all things that could determine something happening in your Smart Contract. At the moment the solution is to use “oracles”.

An oracle is a Smart Contract that requests data to the world outside of the blockchain, by raising an “even”. A listener running on an external server will intercept this event and understand that the oracle wants some data (eg: the USD=>ETH conversion rate), will retrieve this data somehow (eg: by making an API call to some service that provide this info), and will call back into the Smart Contract to pass the requested info. The Smart Contract saves the data as part of its internal state, and makes it available for other contracts running on the blockchain. Of course, only a “certified” listener can call the oracle’s Smart Contract with the requested info….and that is its owner, i.e. the account that originally created and deployed the Smart Contract. This strategy ensures that the data offered by the oracle really comes from that specific external source (that you must trust in the first place, of course), and it can’t be tampered with.

What about the Solidity language?

Solidity is the de-facto standard programming language for Ethereum (previously there were Serpent and Viper, but they lost the battle). It:

  • has classes, structs, enums, inheritance, state variables, functions, function modifiers (for visibility, like public/external/internal, and others with custom meanings)
  • supports many control structures similar to JS (if, else, while, do, for, break, continue, return)
  • has a few data types: bool, int, uint, bytes, string, address. They should all make sense to you already except for “address”, which just stores an account’s address. A notable missing type is non-integer numbers (like float or double). The types “fixed” and “ufixed” (for fixed point numbers) are present in the specs but not fully supported yet.
  • supports arrays (of both static and dynamic size) and “mappings” (dictionaries/hashmaps in other languages)

Not bad, right? However, it’s still quite primitive and limited. For example:

  • “string” is mostly just for storing string literals, but it doesn’t have functions to manipulate them (eg: no append, trim, subString, indexOf etc.). Check out this utility library for an implementation of those functions that work directly at bytes level.
  • While dynamic arrays are supported, a function can only return static arrays (i.e. arrays for which the size is determined at creation-time).
  • While structs are supported, a public function cannot return them. A (partial and quite ugly) workaround is to return a tuple, or an array of tuples if you wanted to return an array of structs, and then re-build the original struct in the frontend. Bryn Bellomy explains it well and with a clear example in this article.
  • A function can’t have too many variables, otherwise at compile-time you’ll get this error: “Error: Stack too deep, try removing local variables”. Variables defined inside of the function + input parameters + output parameters are all counted against the limit (which I think is 12). Now…if you take 2 or 3 parameters in input, and need to return a tuple with 5–6 parameters to simulate returning a struct…that doesn’t leave many variables to be used inside of the function itself! I struggled against this in my own app, and in the end I had to pack multiple return strings into just one (with some separator string that the frontend then uses to split it back to the individual parts), remove some variables from the body’s functions and just recalculate some values every time I needed it, or split the function into multiple simpler and smaller ones.

Soooo…after all this you probably want to see some actual code, right? Follows an excerpt of my app’s Smart Contract, contained in a MessageStorage.sol file, which has functions to store a message (with a number of attributes), load and return a message by id, and retrieve all message ids for a specific user account:

contract MessageStorage is Ownable {
using strings for *;
struct Message {
address sender;
string senderName;
string content;
bool isPwdProtected;
bool allowsReplies;
uint parentId;
uint blockNumber;
}

Message[] internal messages;
mapping (address => uint[]) internal senderMessageIds;
event MessageStored(address indexed from, uint msgId);function postMessage(string _senderName, string _content,
bool _isPwdProtected, bool _allowsReplies, uint _parentId)
public payable {

// process fee...
...
// save message to blockchain
uint id = messages.push(
Message(msg.sender, _senderName,
_content, _isPwdProtected,
_allowsReplies, _parentId, block.number)
) - 1;
// add the new msg id to the list of messages owned by sender
senderMessageIds[msg.sender].push(id);
// notify frontend
MessageStored(msg.sender, id);
}
function getMessage(uint _id) public view returns
(address sender, string senderName, string content,
bool isPwdProtected, bool allowsReplies, uint parentId,
uint blockNumber) {

Message storage m = messages[_id];
return (m.sender, m.senderName, m.content, m.isPwdProtected,
m.allowsReplies, m.parentId, m.blockNumber);
}
function getMessageIdsBySender(address _sender) public view
returns (uint[]) {

return senderMessageIds[_sender];
}
function getMessagesBySender(address _sender,
uint _page, uint _limit) public view
returns (uint[], string, uint) {
...returns a paginated result...
}
...other functions removed for brevity...
}

You see how functions, arrays, mappings and tuples are used, and hopefully most of it should be quite clear if you’ve written any code before. A few things are worth some explanation though, as they are specific to Solidity and Smart Contracts:

  1. MessageStored is an “event”, raised at the end of the postMessage function. This is used to notify a frontend/client when the transaction has been completed, optionally with parameters (like the id of the new message being stored in the array). In fact, after a transaction to execute the function is broadcasted to the network, it might take several seconds or potentially minutes to be picked up and executed by a miner, and this event is what we use to pass a transaction’s result back to the client (back to any client listening for an event with that name in reality — more about this in the frontend section).
  2. The postMessage is marked with the payable modifier, which allows a user to send Ether to it (logic inside the function can make it mandatory). More about this below.
  3. After postMessage creates and adds a new message to the messages array, it also adds the new message’s id to the senderMessageIds dictionary, where the key is the sender’s address and the value is an array of message IDs. There’s then a separate function, getMessageIdsBySender, that just returns the array of ids for the input account. Why do we need the senderMessageIds dictionary at all, rather than just looping through the messages array and returning all objects with item.sender==_sender? The thing is that even if read-only functions are free to execute, they can’t require too much computational effort, otherwise they would still run out of gas and just terminate without returning anything (gas is explained in the next section). Saving the message references against their owner consumes a little bit more storage, but makes the getMessageIdsBySenderfunction as quick as possible.
  4. Once the frontend knows the list of IDs for the messages to be loaded, it could just call getMessage for each of them. However, that would mean 1 call to getMessageIdsBySender + N additional calls according to how many IDs are returned. Not ideal, if you want a quick frontend. A solution is to implement some form of pagination, in which you retrieve/display N items at a time, with a single call. So what I did in my app was a getMessagesBySender function with the additional _page and _limit, parameters (the former being the page index and the latter being how many items it should return). This worked fine…as long as _limit is small enough (just 2 or 3!) and as long as I only returned the minimum amount of data (for the list of past user messages I just need the ID, content, and parent ID if the message is a reply to another message). Solidity really teaches you to write optimised code!
  5. The MessageStorage contract inherits from Ownable, which defines logic and an onlyOwner modifier that you apply to functions only callable by the contract’s owner (which is the account that deploys the contract to the blockchain). The Ownable base class is part of Open Zeppelin, an open source framework of reusable and secure smart contracts.

NOTE: if you’ve paid attention so far, and looked at the code above, you might now be asking “Hey, the messages are stored in an array, local to the contract. Can’t I just delete or reset one item in the array? But didn’t you say that something stored in the blockchain will be there forever? Did you lie?”. Good question, but no, I didn’t lie. Yes, you could delete a message from the array, and that would delete it from the contract’s current state. However, nobody can delete past transactions…and if you know the hash of the transaction that originally wrote the message, you’ll be forever able to use a blockchain explorer (like Etherscan.io) to load up all its details and content/data.

Gas, gas price, transaction costs

Reading from the blockchain is free…but writing to it (i.e. executing a transaction) has a cost, because after all that consumes hard-disk space on all the nodes. In addition to paying for the actual storage space, a transaction also costs according to the computation effort it requires to be executed — looping through an array and doing some calculation with every item it contains before generating and storing a result is more complex (and therefore more expensive) than just taking something in input and storing it.

Just like a car consumes fuel, operations and storage in Ethereum cost “gas” (this spreadsheet shows how much, operation by operation: for example, summing 2 numbers costs 3 gas, and multiplying them costs 5 gas), and just like fuel costs money, gas costs ETH. How much ETH it costs depends on the gas price (ETH cost = gas * gasPrice): this is not fixed though, you can decide how much you’re willing to pay for a unit of gas. However, if your gas price is too low, your transaction will stay in the pool of pending transactions longer, because miners are less incentivised to include it in the next block. A higher price will instead speed it up. Refer to ETH Gas Station to see how what is the average, low and high gas prices. (What that final price means in USD or another fiat currency then depends on the current valuation for ETH of course.)

When you execute a transaction, you specify the maximum amount of gas you’re willing to consume (this is called gasLimit or startGas), and specify the gas price. When the transaction is executed, if it requires more gas than what you supplied, it will “run out of gas” and fail. But you still pay the fee in this case, because after all the miner did their work and it wasn’t their fault if the transaction failed. If instead the transaction consumes less than your maximum amount, what’s left if refunded to you.

The actual gas needed depends on the Smart Contract’s function being called, and on the inputs that you pass…so it’s hard to determine how much gas will actually be spent before executing it. The gasLimit is important because it allows to put a cap on what you can spend, and avoid that a bugged (or malicious) contract can run an infinite loop that costs so much gas that it completely depletes your account!

NOTE: just to be clear (because this is something many people are initially confused about), you don’t buy or own gas. You have Ether in your account, and use it to pay for the gas consumed by your transaction.

Read this article by ConsenSys, or this other one by Danny Ryan, for more.

Payable functions — how do developers make money?

So far I mentioned how you pay for a transaction according to storage and computational effort…but all that fee goes to the miner. What about the poor developer that spent blood and tears to build the contract/application? Well, turns out that you can/must send ETH to functions marked with the payablemodifier (check again the postMessage function from the code sample above). The user can decide how much to send (in case of a crow-funding campaign or ICO for example), or it could be a fixed amount enforced by the contract (eg: you must send 0.005 ETH to execute a function, or send 0.1 ETH to buy a digital asset). That amount is basically transferred to the contract and stays there until the owner withdraws it to his own account or moves it somewhere else. Check out this article on the official docs to see an example of how this is done.

With msg.sender you know the address of the sender account, and with msg.value you know the ETH that they sent. With these two values (accessible from anywhere in the contract, msg is like a global object) you can easily write some checks to ensure enough ETH was sent, and give something to the sender (eg: save their message to the array and link it to their account’s address)

From a developer’s point of view, this bit is super cool. Think about what it takes today, with a traditional website, to receive money from a user in return for a service: you’d need to integrate with Paypal, Square or one of the many payments services…which means signing up and configuring an account, write a decent amount of code to integrate with the service’s API or SDK, etc. With a Smart Contract instead you mark a function as payable, write one line to validate that the correct amount was sent and…it’s done! (we’ll see how that works from the customer’s point of view, on the frontend, in a bit).

Who am I / what do I do? I proudly work as a Solutions Architect in the Mobile Team @ ASOS.com (iOS app | Android app), and we’re always looking for strong, friendly and talented developers that want to have an impact on how tens of millions of customers shop online. ASOS is the biggest online-only retailer in UK and, let’s be real, the best tech+fashion company in the world. Some of the technologies we use are Swift for iOS, Kotlin for Android, React and Node on the web frontend, .NET and Azure on the backend. If that sounds interesting to you, and you happen to live in beautiful London (or are willing to move here…after all it’s the best city in Europe except for some in Italy!), do get in touch with me!

--

--

Marco Bellinaso

Principal Architect @ASOS.com (and iOS / full-stack dev for fun)