Taking a Hackathon project to production

Key takeaways from our first code audit

Robson Silva
Pods
Published in
9 min readJan 29, 2021

--

If you are a developer that just came out from a hackathon with your first project or just got to the Ethereum ecosystem, this article is for you.

Ethereum is a vivid and nascent ecosystem that has taught me so many things since I first started coding in Solidity, back in 2017. I look back to the people that started before me and appreciate them for sharing their learnings and experiences. I still have so much to learn and improve. But one thing that I can share is what we have learned so far and give back what was given to me in the past. I hope that in the article you will learn more about how does going through an audit process works and how to be prepared for it.

Hackers and musicians…

When I became a developer, my mentor shared with me Pauls Graham's article about Hackers and Painters. Later that would become one of my favorite pieces. Paul finished grad school in computer science and went to art school to study painting and in this article, he compares coding with painting and highlights the similarities between the two.

Sometime later, I incorporated that analogy and transformed it into my own reality. Instead of painting, I used music. Whenever I had to talk about the difference between being able to code some Python and calling yourself a Python developer. In that case, I explain that there is an important difference between playing few songs and understanding all the other elements of being a musician, such as composition, improvisation, and stage presence.

In this text, I will discuss the other elements of being a Solidity developer, outside of just writing code. Some topics that we will cover:

  • Mentality (or the vibe)
  • Architecture
  • Development environment
  • Tests
  • Documentation

You need first to feel the vibe…

Have you ever tried to play a Rock song like if it was a Samba? If you reproduce the same notes, same lyrics, the music will be much more similar to a happy party song (like Samba) than a heavy feeling (like Rock). If you are curious, you can check a Linkin Park Samba version or even Sunday Bloody Sunday Samba.

The same will happen if you try to apply the “build fast and break things” mentality to Smart Contracts development. If you came out of an early-stage startup (just like I did), you’ll naturally apply that mentally here as well.

In our first meeting with Solidified, Fabio Hildebrand, a developer that I deeply respect, told me something that stuck in my mind and if there is anything you need to take away from this post this is the most important:

Writing smart contracts is more similar to building hardware than writing software.

Once you have shipped your code, it is very hard to add a missing feature or fix a bug. Most contracts are immutable and even with the upgradable ones, it is possible that someone could find a way to exploit your contract before you have time to update it, and the damage could be huge.

Thinking of writing smart contracts as if you were building hardware has direct implications in elements such as Architecture, Testing, Security, Documentation.

1. Architecture

Inheritance, interfaces, libraries, external contracts, upgradable contracts. You have many different approaches to your system design. Although that is almost always a very case-specific decision, here are some guidelines that you should have in mind:

A code audit is probably the biggest initial cost for your project, so use it wisely.

- Isolate responsibilities and think in modules

Imagine that if every time you need to add a new feature to your system, you have to re-audit the system entirely. That will cost you time and a lot of money! If you break it down into smaller pieces, making each have a very well-defined scope, can help you change parts of the system in the future. For example, in our new Options AMM, we have a specific contract that only deals with the spot price. If we want to change the logic or source for the spot price using other oracles, we can use just do an audit of this new specific part.

- Encapsulate shared logic

Less code means less code to be audited, less space for failure, and less complexity. The simpler your code can be, the better. Using Pods as an example again, we created the PodOption abstract contract to deal with shared logic between Puts and Calls.

2. Development Environment

Using a development environment speeds up recurring tasks such as compiling, deploying, debugging, and setting up a local environment to test your contracts.

Since Hackmoney last year, we started to use Buidler (now HardHat), and we totally recommend it. Being able to insert logs directly on the contracts saved us a lot of debugging time. We also use a lot of the tasks, it’s very easy to coordinate deploy orchestration. Also. it is friendly enough for team members outside of the solidity context to use the CLI.

Also on this topic, you will need to choose between web3 libraries like Ethers.js and Web3.js and the way that you are going to deal with BigNumber in your tests and scripts. We picked Ethers.js because the way that you interact with the contracts is similar to the way you write on Solidity. We also use the native BigNumber library of Ethers.js.

Hardhat console.log example

3. Security Framework

Coding programmable money is exciting and terrifying at the same time. We have the power of building financial products that impact people’s lives, but also the huge responsibility of doing everything we can to ensure the user’s funds are secure.

For this, I believe security should be a priority across the entire DeFi ecosystem. There is a large spectrum of security levels that you can choose depending on the stage of your project. You need to choose which level of security best fits your situation, however, there always remains risk no matter how hard you try.

Risk protection ranges from 0% to 100%. 0% means the protocol has done nothing to protect itself, while 100% would mean the protocol has done code audits with all the firms, formal verification, economic stress testing, and it has 100% of testing coverage, a bug bounty… and everything else one can imagine.

Maybe adopting a 100% level of security before launching an MVP is not feasible nor viable. I would like to propose to teams to define their comfort level based on what they can do before the launch and stick to it as a framework. The framework should be consistent over time, creating a boundary at the minimum the team commits to before any launch.

I would suggest a minimum boundary include one code audit, over 90% test coverage, a guided launch, and a bug bounty. This should be enough to find any initial severe bugs.

4. Testing

Testing different scenarios through your protocol helps you identify edge cases and potentially prevent an error that would not have seen otherwise.

Here are the layers of test that we started to implement during those months at Pods:

  • Unit testing
  • Test Coverage
  • Integration tests
  • Local blockchain environment + Dapp
  • Public Testnet deploy
  • Static Analyzers
  • Mainnet forking test
  • Fuzzy testing (WIP)

Unit Tests

Let’s start with basic tooling. At Pods, we use Mocha, Chai, Ethers.js, and Waffle. Waffle is a good tool to add some custom matchers to your unit tests, for example:

expect (transaction).to.be.reverted

Ok, so I started adding some tests to our contracts, and now what? How do I know that I am covering enough edge cases? Test Coverage!

Waffle Testing tool

Test Coverage

Here we are using a solidity-coverage Hardhat plugin along with coveralls, which is a service to keep track of your code coverage. They even have a nice UI where you can see the untested branches more easily. Our auditors said that ideally, the minimum should be 90%. You can check on Defi Safety what other projects are doing as a reference.

solidity-coverage example

Integration / Scenario Tests

For the unit testing we usually mock external contracts that interact with the tested contract. But in this case, we pre-deploy all our contracts (exception for the ones that are outside projects, like Chainlink or AAVE for instance).

Most times, in this phase we test some “economic” scenarios. One of our tests for example is called ATPR, meaning: “Add liquidity, Trade happens, Price moves, Remove liquidity”.

Local blockchain environment + Dapp

For this kind of test, we created a HardHat task that basically deploys all our systems from the scratch. You can check the setupLocal.js file in our repo.

After that you need to run a local node, using npx hardhat node. And then finally deploy all your system pointing to the local network.

Why do we do that step instead of going directly to the public testnet? Debugging! It is much easier to be able to console.log your entire flow in detail. Most times is hard to track error returned from the transaction, especially if you had an error on SafeMath or any kind of library that you use in more than one place.

At this level, we deploy mock/dumb contracts in place of other external contracts such as oracles.

Static Analyzer

Security analysis tools identify smart contract vulnerabilities. These tools run a suite of vulnerability detectors and prints out a summary of any issues found. We used Slither at Pods. You can also use Manticore

Public Testnet deploy

Now it is a good moment to test all the external contracts with their respective testnet versions. So, the most important part here is to choose correctly which testnet you will pick. Just make sure that the external contract that you are interacting with has the contracts deployed in the same testnetwork. It is also important to use the same ERC20 version of the external contracts.

Having your contracts on the test net allows your team to open start beta testing with your users and start getting feedback.

Mainnet forking test

By the time that we were doing our internal testing, HardHat old version didn't have the Mainnet Forking tool available.

This is also an alternative for the local environment. Since you can test easily the interaction with the Mainnet version of the external contracts (Oracles, other protocols, for example). On the other hand, it is harder if you have any timestamp functionality and you want to move in time. HardHat team implemented this feature recently and you can learn how to do it here.

Fuzzy testing

Fuzzing or fuzz testing is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program (Wiki). The three main fuzzer tools available on Ethereum are MythX, Echidna, and Mythril classic. We decided to use Mythril classic since is the open-source version of Mythril. Although, you can use MythX that is faster, but it runs only for 3 minutes in his free version. Echidna is supported by the Trail of Bit, and seems pretty complete but is harder to configure compared to Mythril.

5. Documentation

When we started, we thought that the documentation could be postponed to just before the launch. We were very wrong. But we’ve learned that one of the first users of the documentation is actually the audit company. Here I will highlight some important elements that you have to have in mind while working on yours:

  • High-level context
    Before going directly to the code or to the details, give a big picture context of your project.
  • System Diagram
    Having a diagram to visualize all contract interactions and even showing the functions and their types can be really helpful.
  • API Reference
    API Reference document the technical details of your smart contracts, functions, and parameters.
  • Tutorials
    Show some concrete examples of how to do certain tasks and what should be the expected results.

That is all for now folks!

If you have done most of the elements of this post, congrats!! Your project is ready to be audited!

I’m sure that you have some interesting ideas about how to improve the security of our ecosystem. I’d love to hear them and implement them at Pods.

About Pods

Pods is a decentralized non-custodial options protocol. Users can create options and trade them through an Options AMM on the Ethereum Blockchain. Pods is the easiest way to hedge crypto in DeFi.

We invite you to take the first step in your new mission: start testing the app on app.pods.finance

Join the Pods community

app | website | documentation | blog | twitter | youtube | telegram | discord

--

--