This is Part II of a two-part article. In Part I, we analyzed what a smart contract is at a high level and how it will enable the future of P2P internet interactions absent centralized intermediaries. We also analyzed early use cases for smart contracts today ranging from DeFi to tokenizing real-world assets.
Part II goes deeper into smart contracts. In this article, we evaluate features of smart contracts, namely immutability, and report a mechanism to work around editing and amending contracts. The second half of the article lists some of the most common vulnerabilities of smart contracts across the “deployment stack.” There’s also a fun appendix that outlines the smart contract lifecycle and how it is compiled into the Ethereum running environment.
With that, let’s check out Part II.
Do we want immutability?
“The best thing about us is often the worst, too.” This applies well to the features that accompany smart contracts: transparency, permissionlessness, and immutability. Choosing to focus on the final point, it’s hard to see why anyone would want to engage in an agreement that doesn’t allow for last-minute amendments or changes. This is a critical shortcoming of smart contracts. Modifying a smart contract after deployment can be more costly and technically challenging than leveraging traditional lawyers to write a text-based contract.
While addressing this concern is critical, it’s worth noting that there are mechanisms in place to change smart contracts after deployment. This is particularly valuable in the case of a hack, which for whatever reason, crypto has no shortage of :)
Immutability becomes less so through a concept explained by Preethi Kasireddy. It’s duly called a backdoor approach. It was first proposed by OpenZeppelin, one of the premier smart contract auditors.
To create a backdoor in a smart contract, a programmer needs to create two separate smart contracts: a proxy contract and a logic contract. In this setup, a user would only interact with the proxy contract. When a user initiates a smart contract, a function (set of instructions) is “called” by the user, passing along the expected and required inputs. In turn, the proxy contract would then delegate, or pass along, those parameters to the logic contract. The logic contract is what contains the executable instructions that update the state of the blockchain. The data from the logic contract would then make its way back to the proxy contract, which would then return some output or outcome to the users as desired.
How is this a backdoor? Since you can call a smart contract from another, you can always change which logic contract is called in the proxy contract. The only change is which logic contract is being called (e.g., logic contract v1 vs. logic contract v2).
With this functionality in place, one would have the ability to “update” and amend a seemingly immutable smart contract since only the proxy contract is the interface with which a set of users or participants in an agreement engage. Additional details for how a proxy contract can call upon different logic contracts are beyond the scope of this essay but can be found here.
A smart contract is a bit of a misnomer: a smart contract is only as smart as those who code it. To give you a quick example of how smart our smart contract coders are today, it would only be fair that I show some major hacks courtesy of our friends from REKT. Some quick examples are here, here, here, here, and here, too.
When approaching smart contracts through a first-principles lens, we need to be transparent about the vulnerabilities. I tried to explain some of these vulnerabilities in plain English, along with links to a more thorough analysis, in the hopes that the next time a hack occurs, you can have some idea of why and how it happened.
From a high level, though, naive builders will always be a target for fraud. There’s not much you can do to get around it. Assuming a developer didn’t implement a backdoor approach as discussed in Part III, there’s often no recourse. Vulnerabilities and behavior testing must be a necessity for these groups to build businesses on the backs of smart contracts. Given the lack of standardization around vulnerability testing, it is difficult for the industry to identify, categorize, and analyze these vulnerabilities before they happen.
With that, let’s dive in.
What are the major smart contract vulnerabilities?
We will analyze smart contract vulnerabilities across three different levels:
Solidity (how code is written)
Ethereum Virtual Machine (where code is compiled)
Blockchain (how code changes the state of a distributed ledger)
The list below is by no means exhaustive, but it provides proper context as to just how complex identifying these vulnerabilities can be.
A reentrancy attack occurs between two smart contracts, A and B, where an attacking smart contract exploits the code in a vulnerable contract to drain it of its funds. This is accomplished by having the attacking smart contract repeatedly call the withdraw function before the vulnerable smart contract has had time to update the balance.
An example is a bank teller who doesn’t update your balance until you are finished with all the money requests you make. You consistently withdraw $1,000 repeatedly, but your account balance never updates until you finish all your requests.
Example: The DAO Attack
Due to the lack of floating-point support (think long decimals after a whole number), smart contracts generally represent values as integers. Using whole numbers to represent values requires reducing the value to a smaller unit to allow sufficient precision.
An example is an integer overflow. Computers, for example, have a maximum value of integers that can be calculated. When the max value is reached, the computer returns to the starting point, typically the minimum value. This opens up a vulnerability as follows: if a person (or program) tries to subtract 5 from 3 in an unsigned integer (these a positive, whole numbers), it will cause an overflow error. The outcome is an output of a very large number outside the scope of a program’s range, leading to a potential exploit.
An example is a token holder with 5 ether but attempts to spend 6 ether. If the contract doesn’t check for this, the attack might be allowed to spend more tokens than originally possessed.
Example: The PoWH Coin
This function is used to delete a smart contract from a blockchain. The manner is by removing all bytecode (the instructions for the virtual machine to execute) from the contract address, then sending all the ether stored in the contract to a different designated address. This operation saves on gas and is an excellent mechanism to stop a deployed smart contract, especially when utilized with multisig authorization.
An example is when an attacker changes the deployed self-destruct address to their own address, usually through a vulnerability where the smart contract library is stored, then triggers the self-destruct function. This allows them to take full custody of ether still in the contract.
Example: Parity Bug
External Contract Referencing
Because smart contracts are open-source and public on the blockchain, other contracts can call them. This is part of a smart contract’s composability, which is often described as programming Lego pieces. This tends to be safe from a code audit perspective, however, if a malicious actor were to change an address from a smart contract’s library, a security hole is created. A malicious actor can then create a new smart contract, dubbed a honey pot, and use that same address to mask malicious code. This malicious code could then exploit others that call the original contract.
Example: Honey Pot – Reddit Post
Ethereum Virtual Machine
Short address/parameter issues
Freezing ether (greedy contract/locked money)
Transaction order dependence
Transactions change the blockchain state from one to another. The state of contracts depends on the order that transactions are executed. An Ethereum miner typically decides the execution order. This non-deterministic feature of execution can make it difficult for an actor to forecast the state before transactions are submitted. With limited knowledge on how a state change can occur between blocks, submitted transactions with shifted transaction orders can cause buying or selling items at unexpected prices. An example of this is frontrunning, in which observers of pending transactions (viewable in the mempool) can see and react to an action before it is included in a block.
Note: an equivalent in traditional finance markets is Payment For Order Flow (PFOF), which isn’t technically a vulnerability.
Example: Bancor Frontrunning
Generating randomness is difficult for many programming languages. The current practice is usually leveraged by the function block.timestamp. If a historical blocks’ timestamp is used, attackers can use the same random number-generation process to obtain the same result because historical blocks never change,
Example: Predicting Random Numbers
As we move away from a centralized internet to a decentralized one, smart contracts will be one of the most important tools for humans to be at the crux of peer-to-peer interaction. The internet will reform itself to one of human interaction creating the highest value, as opposed to machine interaction. This two-part article analyzed why smart contracts are important to this shift, how they are behind used today, how immutability as a feature is as much a strength as it is a weakness and provided a keen understanding of some of the most common vulnerabilities in smart contracts.
The appendix below contains a deeper dive into the details for smart contract components, known as Ethereum accounts, and reviews the life cycle of a smart contract’s development. If those topics interest you, I welcome you to read further.
The basic element behind Ethereum are accounts, which is otherwise known as account states. Each account typically has four data fields:
Nonce – the transaction counter
Balance – amount of ether the account possesses
Storage – memory space for code and its execution
Code – where a smart contract is stored
Within accounts, there are two types: external accounts and contract accounts. External accounts are controlled by private-key pairs. These accounts can alter the state of the blockchain through a transaction, which after execution, is broadcasted to the entire blockchain. These transactions are not free and require a gas fee paid in ether. Assuming this gas fee is paid, a miner would need to choose to pick up a transaction from the mempool (the pending transactions), execute it, then propagate that state change to the rest of the network. The gas fee compensates the miner for this work.
The second type is contract accounts. These are smart contracts deployed to the Ethereum network and are controlled by the code, which is why there is no need for a private key pair. Some key differences between these accounts are that external accounts are free to create, whereas control accounts require a gas fee because the code takes up network storage. Contract accounts are also input-only contracts, meaning that they can only send transactions after having received an initiated transaction.
Life Cycle of Smart Contracts
Ethereum Running Environment
Blockchain blocks, the Ethereum Virtual Machine, and smart contract codes together make up the running environment for Ethereum. The block mining process identifies legitimate transactions and combines the corresponding state’s change into a new block. A miner will pick the transactions that they want to include onto their block from a mempool, execute the codes within the smart contracts, update the state, calculate the nonce (proof of work), and attach the newly minted block onto the previous block on the blockchain before broadcasting the state change to the rest of the network.
Special thanks to Zoe Enright and Sam Wheeler for their help on this post.
“Aave and Flash Loans.” Gemini, https://www.gemini.com/cryptopedia/aave-flashloans. Accessed 25 Sept. 2022.
Konstantopoulos, Georgios. “How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and How to Avoid Them (Part 1).” Loom Network, 6 Feb. 2020, https://medium.com/loom-network/how-to-secure-your-smart-contracts-6-solidity-vulnerabilities-and-how-to-avoid-them-part-1-c33048d4d17d.
“Learn Hub.” Ethereum.Org, https://ethereum.org. Accessed 25 Sept. 2022.
Lipton, Alex, and Stuart Levi. “An Introduction to Smart Contracts and Their Potential and Inherent Limitations.” The Harvard Law School Forum on Corporate Governance, 26 May 2018, https://corpgov.law.harvard.edu/2018/05/26/an-introduction-to-smart-contracts-and-their-potential-and-inherent-limitations/.
Monolith. “TKN and Short Address Attack Mitigation:” Monolith, 31 Oct. 2018, https://medium.com/monolith/tkn-and-short-address-attack-mitigation-88cc895734ba.
“Real World Examples of Smart Contracts.” Gemini, https://www.gemini.com/cryptopedia/smart-contract-examples-smart-contract-use-cases. Accessed 25 Sept. 2022.
Smart Contracts 101 - What to Know & Where to Start - Wharton | Wharton - Economics of Blockchain and Digital Assets. https://www.web3.wharton.upenn.edu/blog/smart-contracts-101-what-to-know-where-to-start-wharton. Accessed 25 Sept. 2022.
Valaitis, Alex. “A Deep Dive on Chainlink.” Web3 Pills by Alex Valaitis, 28 July 2022, https://web3pills.substack.com/p/a-deep-dive-on-chainlink.
Voshmgir, Shermin. Token Economy: How Blockchains and Smart Contracts Revolutionize the Economy. 1st edition, 2nd amended printing, BlockchainHub, 2019.
Zhou, Haozhe, et al. “The State of Ethereum Smart Contracts Security: Vulnerabilities, Countermeasures, and Tool Support.” Journal of Cybersecurity and Privacy, vol. 2, no. 2, May 2022, pp. 358–78. DOI.org (Crossref), https://doi.org/10.3390/jcp2020019.