Transparency Series Part One: Diving Into Composable Smart Contracts
By Sarah Imran
Functioning as more than just a smart contract auditing firm, BlockApex works with clients to ensure their products are as secure as possible. We pride ourselves on maintaining the utmost quality in our audits all while delivering exceptional service.
A big part of BlockApex’s philosophy is transparency. We want to make sure our auditing process is as open and fulfilling as possible to both parties involved in any discussion. In an effort to ensure trustlessness, we have decided to launch a series of publications highlighting the ways in which BlockApex functions as an entity. This document can be seen as the first part of that series, where we will discuss the procedure we follow when working on composable smart contracts in particular.
An Overview on Composability
Composability can be understood as the interdependence between different blocks in a system. Composable smart contracts are commonly seen in the Defi space in particular, due to the advantages brought forth from the Ethereum network. Ethereum is regarded as the host of the DeFi ecosystem, allowing anyone to build decentralized apps on top of its network.
The integration between multiple applications and protocols deployed on Ethereum is where composability comes into play. Anyone can build on top of already existing programs to fulfill newer use cases- creating grounds for greater innovation. Though right now this is mostly limited to protocols operating on Ethereum, composability can one day stretch to include applications on other blockchains as well. This goal of blockchain interoperability is something many in the field are already working towards making a reality, as the benefits offered by such a phenomenon being commonplace are tenfold.
To learn more about blockchain interoperability and its importance, you can read this piece titled Blockchain Bridges: A Security Perspective.
At BlockApex, we have been working on the composable smart contracts of Unipilot, an automated investment manager for liquidity providers in Uniswap V3. The Unipilot contract ULM composes over Uniswap V3 core smart contracts to ensure its concurrent functionality along with some other helper libraries.
The Train And Hotel Problem
The train and hotel problem is an example that is often cited in discussions about composability.
Let’s say there is a man who wishes to book a train ticket and reserve a hotel room in preparation for a trip. However, he wants to make sure that this operation is atomic- meaning that either he successfully books both the hotel room and the train ticket or does not book anything at all. He should not be able to book a ticket if a hotel room is unavailable and vice versa.
The graphic above displays the different messaging connections that a developer must establish if he were to create a single dApp for catering these reservations. As booking a train ticket, reserving a hotel room, and making a payment are all operations within separate smart contracts, composability is seen when trying to use them to construct a single transaction.
Problems Faced When Auditing Composable Smart Contracts
Composable smart contracts bring about certain problems in particular during the auditing phase. One of these is the hindering of end-to-end (E2E) testing. Often it is the case that for calling even just one function of a composable smart contract, multiple other contracts are required to be deployed. As the purpose of end-to-end testing is to ensure all the integrated pieces are working the way they are expected to, each of these dependencies must be catered to separately- sometimes requiring multiple iterations to carry out successfully. Hence this can be a grueling process.
Similarly, fuzzing E2E properties is also a difficult task to perform on composable smart contracts. Since the purpose of fuzzing is to stress the system by feeding it random stochastic inputs, it is already more difficult than simply unit-testing. The dependency brought by composable smart contracts makes this procedure even more difficult as now there is a hierarchy that must be considered as well. As the number of contracts increases, the number of reachable states also increases, making everything from testing to formal verification even more difficult.
Testing Tools Used by BlockApex
Our audit team uses a combination of several different tools throughout our auditing process. Among these were the testing frameworks Truffle and Ganache. These were primarily used to run automated tests, migrate, and interact with the contract so troubleshooting can be done without connecting to any public testnet or mainnet. However, both of these have now been largely replaced by Hardhat, another testing framework which has much simpler syntax and is also faster.
Solidity-coverage and Slither
Solidity-coverage is an npm package which is used to record code coverage while testing Solidity. This was a tool used by us during our audit to check the number of times a function calls different interdependent contracts, outlining both the path taken and the result of that function. Slither was also used at this time, printing details about the contract like the number of functions, imports and exports, internal and external calls, access modifiers, etc.
Hardhat is an Ethereum tool which was beneficial during the audit process as it allowed for many debugging functionalities. This helped our team carry out tests from different angles to check for any vulnerabilities at a deeper level. It is mostly used alongside development but comes in handy for manual testing and behavioral testing as well, and is used for executing, testing, and running edge cases and pin-pointing solidity to produce a desired result.
Echidna was a tool used by our team extensively and its main purpose was for fuzzing. It works on user-defined properties for custom property-based testing, assertion checking, and an estimation of maximum gas usage. By feeding a set of random stochastic inputs to the tool, we were able to check for any malicious behaviour within the smart contracts we were auditing.
Etheno is a JSON RPC multiplexer, analysis tool wrapper, and test integration tool. In layman’s terms, it takes an at-the-moment snapshot of the current state of the blockchain and saves it into a JSON file which can then be used during testing.
During our audit with Unipilot, we used Echidna as a fuzzer based on the integration of Etheno- a setup that detected a potential bug in the system. This unique combination was used as the solution of an interesting problem we encountered.
The basic feature of any fuzzer, including Echidna, is that it works on one state at a time. If you fuzz on one state, the results of that test are not considered when the state changes. However, our requirement was such that we needed to test on multiple achievable states. Although this was manageable with unit tests, we realized it would not work with standalone fuzzing with Echidna, as it did not consider changing states and their previous results when executing and would instead restart its test as soon as a state concluded. To solve this dilemma, we introduced Etheno as a helper tool.
Etheno was used as a chain manager and RPC multiplexer that listened to the transactions and calls made on the local blockchain then saved them in a file. This JSON file was provided to Echidna to fuzz upon as a seeder capturing a set of bugs in multiple scenarios. In this way, we were able to effectively fuzz the given scenario of changing states, making sure we covered all possible angles of exploit during our audit.
DappTools also provides comprehensive tools for fuzzing, which led to us straying away from using Echidna separately. The setup for Echidna was also proving to be time-consuming during our audit, as the software took some time to provide proof of any property. We concluded that Echidna was becoming costly in terms of resources, so DappTools was more preferred, though Echidna is used sparingly depending on the requirement of a project.
DappTools is a suite of Ethereum focused command-line-interface tools following the Unix design philosophy and favoring composability, configurability and extensibility. The set includes several tools to build, test, fuzz, formally verify, debug and deploy solidity contracts These include dapp, seth, hevm, and ethsign. Dapp uses hevm under the hood and seth is a tool for sending transactions to a state of the blockchain which comes in handy while identifying errors on a live mainnet deployed contract.
Right now, there is a whole lot of area to explore with the Dapphub’s hevm. This is an implementation of the EVM made specifically for unit testing and debugging smart contracts. It can run unit tests and property tests, as well as interactively debug contracts while showing the Solidity source, the byte codes of an evm executable, and memory consumption and allocation according to the lines of code. It can even run arbitrary EVM code as well.
Currently, we are using DappTools primarily with help from Hardhat where DappTools restricts.
Plugins For Visualization
Our audit team employed the help of various plugins for the purpose of visualization of the smart contract and its code. This included Solidity Visual Auditor, Solhint, and Prettier extensions. These were used to improve workflow by optimizing the code to create a clearer picture while auditing.
Developing An Audit Mindset
Our team prides itself on our focus towards consistent personal growth. To assist new and experienced auditors with developing an audit mindset, fun and educational challenges are encouraged. CTFs like Ethernaut, DamnVulnerableDeFi, and Capture-the-Ether as well as Bug Bounty programs sponsored in the space all assist in honing skills between audits.
Enjoyed reading this piece? Subscribe to our newsletter to get instantly notified of new stories directly in your inbox at https://blockapex.medium.com/subscribe.