Introduction

With the rise of blockchain based crypto currencies, smart contracts have become increasingly more popular.

Crypto startups are utilizing smart contracts to create intelligent agreements that can be executed in a “trustless” environment. Smart contracts take out the middleman and execute on contract terms automatically, based on rules put in place. Although smart contracts harbor a lot of promise, we have seen a worrying history of hacked smart contracts, where hundreds of millions of dollars worth of crypto currencies have been lost or stolen.

My growing interest in crypto currencies and blockchain based technology has led me to research and investigate these hacks, to gain a better understanding of how they were hacked.

This article is part 1 of a 3 part series, where we will dive deep into the most infamous hacks. We will study how strokes of genius, or subtle mistakes caused earthquakes in the crypto world.

Aside: This article is based on the amazing lecture of Leonid Beder at Blockchain Academy — How to Not Destroy Millions. I highly recommend watching this talk, and all other materials on the Kin Ecosystem Foundation Youtube channel.

Smart Contracts

A smart contract is a set of promises, specified in digital form, including protocols within which the parties perform on these promises — Nick Szabo, 1996

Let’s break down this definition term by term.

Contract

An example of a common contract is a sales contract.

The seller promises to deliver the goods in exchange for the buyer.

The buyer promises to pay the desired price.

Digital form

Digital form means that the contract has to be programmed in machine readable code. We want the contract to be deterministic in nature, thus providing the same outcome based on the same input, every single time.

Protocols

For example, say the parties agree that the purchased good is to be paid in Bitcoin. The obvious protocol of choice would then be the Bitcoin protocol.

Smart

this just sounds cool.

Popular implementation of smart contract programming languages

Ethereum

Implements a nearly Turing-complete language on its blockchain, a prominent smart contract framework. We say nearly Turing-complete b/c in order to be completely Turing complete we need unlimited computational power.

Bitcoin

Implements a Turing-incomplete scripting language that allows the creation of limited smart contracts, such as:

Solidity by Example

Solidity is an OOP language for writing smart contracts (mostly?) on Ethereum. We’ll start with a simple example of a smart contract named Greeter, which will:

It is best practice at the moment to specify explicitly which version of solidity we are using, because we don’t know which bugs can be introduced in future releases.

Solidity has a similar syntax to JS. We are defining a contract named Greeter. Additionally, we have a public state variable of type string called greeting and two methods (constructor and setGreeting).

Fundamentals: Execution Model

if (condition is true) {

// do something super simple which is very cheap in gas terms

} else {

// do something really complicated which will be very expensive!

}

Let’s upload the contract to the Remix IDE, create and deploy it.

Remix can work against main net and test nets. It’s really convenient to work against an Ethereum simulation. When using the simulation we can pretend to have millions of Ether, and not wait on Ethereum transactions to be processed which make development a lot faster. Running smart contracts via Remix is completely free, so I recommend you give it a try at home :)

Once the contract is deployed we see it in the Deployed Contracts section in the box (see the arrow!). Notice that we can easily change the constructor argument and redeploy to the network.

Fundamentals: Special Variables and Functions

There are special variables and functions which always exist in the global namespace. Here are the ones we’ll discuss today

Fundamentals: Function and State Variables Visibility

In Solidity, there are four types of visibilities:

For functions:

For state variables:

Fundamentals: Function Modifiers

Modifiers can be used to modify the behavior of functions. This lets us add functionality before, after or even around the invocation of the function. (Logging for example)

Modifiers are very similar to before/after/around/hooks in other programming languages.

For example, let’s augment our Greeter smart contract to:

We’re going to see this pattern a lot, in the coming examples as well as in the wild. Additionally, let’s add a condition that says only the owner can change the greeting.

It is important to note that require is a predicate in Solidity. If its evaluation returns false, then an exception is thrown, and all changes done in contract are completely rolled back.

The underscore is equivalent to a yield. This basically means execute in this place the remaining code of the function.

Fundamentals: Fallback Function

A smart contract can have exactly one unnamed function.

This function is invoked when a call to the contract is made and none of the other functions match the given function identifier. A common use for fallback functions is when Ether is being transferred to the contract. This triggers the invocation of the fallback function. So, in order to set up an account to be able to receive Ether the bare minimum (in most cases) is setting this function up.

Fundamentals: Payable Modifier

In order to receive Ether, every function must be marked as payable. This is to protect us from accidentally sending ether to a contract that doesn’t expect it.

If no such function exists, the contract cannot receive Ether through regular transactions.

Events Example

This is a fancy way to do some logging in Ethereum.

Aside: We have a function called Donate that logs returns and event. This could be useful for the case of having an off-chain client that reads the loggings and manages all the donations.

We have a function called donate which is payable (we added the payable modifier). We also have a function called troll which is not payable, so sending Ether should fail in this case (payable modifier is missing).

Let’s deploy this contract to Remix so that we can verify the results of sending money via each of these methods.

In the gif above we can see that invoking the donate method results in a transaction where a value of 5 wei is transferred, and when we invoke the troll method no money is transferred.

Let’s Destroy Some Ether

We can see in Reward that we have a mapping between address and a uint. We use an owner concept here so our owner can call the setReward method, deciding that a specific address is supposed to get some amount of a magic token. Additionally, we have the claimReward method which is publicly accessible by everyone. This method gives you the reward that belongs to your address, and then emit an event for the reward.

Can anyone spot a problem with this?

Notice that when we declare the rewards mapping, it is a map from address to unsigned integer.

We are subtracting here without checking that the necessary funds are available! So any scenario where we reach a negative value in rewards will result in an underflow.

Aside: An underflow, will change what should be a negative value to an extremely large positive one. Check out the link, for a further explanation on how underflows work.

Consider the following example. A reward claim of 500 units is made. Now, our hacker will try and claim 1000. In theory, this shouldn’t work because we only have 500 left. After running this transaction the claimer will have an infinite amount of coins!

Bugs: Overflow / Underflow

Mitigation #1: Test for Correctness

Mitigation #2: SafeMath

Setting Function Visibility

Can you spot an error in the Charity contract? Let’s break it down. We have a payable fallback function and a withdrawal function where the owner can withdraw his funds. You can imagine that at some point the owner may want to withdraw his funds so he can do something with them.

Now, let’s take a look at the setOwner method. It’s using the owner paradigm. It has no privacy. In Solidity functions are public by default! Anyone can change the owner, thus transferring the funds to himself / herself.

Hack Scenario:

Alice deposits 1000 wei for this example. Bob tries to withdraw with but fails because he is not the owner of the charity. Now, Bob calls into setOwner, changing the owner to himself. Bob takes all the funds. All this because someone forgot to define visibility!

Mitigation #3: Always Define Visibility!

Conclusion

This has been a primer on the Solidity language, and we have examined some of the simple, dangerous mistakes that can be made during smart contract development.

The next post in this series will dive into infamous, genius and nuanced hacks, that have so far drained hundreds of millions of dollars worth of crypto in the past several years.

If you’re looking for resources to ramp up on the blockchain world, I highly recommend (again) checking out the Kin Ecosystem Youtube channel, as it has a wonderful inventory of high quality, technical discussions and lectures. Huge thanks to Leonid Beder for building this lecture and teaching it at Blockchain Academy!

If this post was helpful, please subscribe and click the clap 👏 button below to show your support! ⬇⬇

You can follow me on Instagram, Linkedin and Medium.