Supercharge with DappTools

DappTools: Supercharge Your Smart Contracts

Rahul Ravindran

DappTools is a suite of Ethereum focused CLI tools following the Unix design philosophy, favoring composability, configurability and extensibility.

But so is hardhat, right?

Yeah, but hardhat is not perfect and the JavaScript based testing workflow is a nightmare and has following issues:

  • So much boilerplate
  • Context switching: The mental gymnastics of switching between two different languages to test the same functionality is like having a penguin as your co worker, you might think it’s exotic and adds value. But it doesn’t.
  • It gets complicated real fast.
  • Its painfully Slow for complex tests, you are still making RPC calls behind the scenes And no, adding typescript to the equation doesn’t help.

Simply put, working with JavaScript to test solidity is like trying to put out a fire by blowing at it, it works for a small fire, it gets increasingly difficult to put out as it gets bigger. And before you know it, you’re out of breath and in tears wishing there was a fire extinguisher all along. DappTools is the fire extinguisher. It can help you fix bugs in your smart contracts that can cost you millions of dollars.

If you want devs to write more secure code, give them better tools

Still not sold?

Eth2 Deposit Contract and Wrapped Ether are the top 2 accounts by ETH Balance. Together they hold around $60 billion in value or about 13% of all ETH in existence.

Top 2 accounts by ETH Balance
Top 2 accounts by ETH Balance

Do you want to guess what they have in common?

DappTools being used
DappTools being used

They were both built / tested using DappTools. It’s also used by: – The ETH2 Deposit Contract – MakerDAO – Fractional – Reflexer – Maple – Pickle – And countless others

If the top smart contracts use DappTools for their development workflow, you bet there are great reasons why DappTools is simply awesome.

Tell Me More

So what’s this DappTools thing?

I’m glad you asked, DappTools is a suite of tools for writing, testing, fuzzing, and deploying Solidity smart contracts. Let’s explore with an example:

Get started by installing DappTools:

Install Nix if you haven’t already: curl -L https://nixos.org/nix/install | sh

Run this or login again to use Nix

. "$HOME/.nix-profile/etc/profile.d/nix.sh"

Then install dapptools: curl https://dapp.tools/install | sh

Create a directory and run dapp init

Dapp Init

You should have a batteries included project in under 10s. That brings me to another feature of DappTools. It’s fast. Very very fast.

I am Speed

Project Structure

Project Structure

The project structure is fairly straight forward. The src folder contains your source file and a corresponding “.t.sol” file. That’s right – you can now test solidity files in solidity.

Building Files

You can build your solidity files using the “dapp build” command.

Let’s look at the following code:

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.6;
contract Dapptools {
   function add(uint256 a, uint256 b) external pure  returns (uint256){
      return undfinedVariable;
   }
}

DappTools will give you clear build errors during compilation.

Build Error

Let’s fix our function and actually add the parameters.

contract  Dapptools {
  function  add(uint256 a, uint256 b) external  pure  returns (uint256){
    return a+b;
 }
}

Let’s rebuild our contract.

This time our contract compiles without any error. The contract ABI is stored in the out directory. So far so good.

Testing Files

DappTools ship with a sample test file “Dapptools.t.sol”. Let’s understand how this file is constructed.

The setup function is cached under the hood, which significantly speeds up unit tests.

Let’s write our first test

contract  DapptoolsTest  is DSTest {
 Dapptools dapptools;

 function  setUp() public {
    dapptools = new  Dapptools();
 }

 function testAddFunction() public { 
    assertTrue(dapptools.add(1, 2)==3); 
 } 
}

You can run your test using the “dapp test” command:

Our test passed. You might also see the gas it took for our function to run. DappTools gives you the sweet sweet gas computation out of the box. You see what I mean by batteries included framework?

Let try failing our test

function  testAddFunction() public {
    assertTrue(dapptools.add(1, 2)==4);
}

And re-run our test

It’s not clear what exactly is going on. To get more info we can run dapp test --verbosity 3

Okay now we see where exactly the test is failing but the error message could be improved. Lets update our assertion statement.

function  testAddFunction() public {
    assertEq(dapptools.add(1, 2),4);
}

The assertEq function explicitly tests equality and provides appropriate error messages when the test fails. Let’s run the test.

Beautiful, isn’t it?

Fuzzing

Now you cannot be 100% sure if your function really works because 1 unit test passed. You could technically add more assertions:

function  testAddFunction() public {
    assertEq(dapptools.add(1, 2),3);
    assertEq(dapptools.add(1, 3),4);
    assertEq(dapptools.add(1, 4),5);
    assertEq(dapptools.add(1, 5),6);
}

But there will always be inputs that you may not be able to cover manually. Behold – here comes fuzzing to the rescue.

Fuzzing might be new to you if you come from a traditional unit testing framework. Fuzzing is the idea of supplying type hinted parameters to test function and letting the framework supply the value during runtime.

Lets update our test include fuzzing:

function  testAddFunction(uint256 x, uint256 y) public {
    assertEq(dapptools.add(x, y),x+y);
}

Our test function can take arguments – DappTools will supply random values during runtime. Lets try it:

Our test failed: we were unable to add two very large numbers because these numbers are out of bounds for unit256 data type. This brings me to another important point – unit tests should always expose limitations of our code which is exactly what happened here. Lets change our test function to add 2 uint128 since adding two unit128 numbers will not cause this overflow.

function  testAddFunction(uint128 x, uint128 y) public {
    assertEq(dapptools.add(x, y),uint256(x)+unit256(y));
}

Re run the test:

Our test passes for 100 runs. Sweet. We can customize the number of runs with “dapp test –fuzz-runs 1000”

Symbolic Execution

But we are still not trying all possible inputs. How can we really be sure if our function works in all cases?

Symbolic execution is a unique way of testing code for correctness. Under the hood, instead of spamming your function with random inputs, it turns your code into a mathematical expression and uses a solver to find all possible paths the function could take and which are reachable.

If there are any reachable paths which violate your assertions, DappTools will tell you!

Alright, how do we turn our boring old fuzz test into a futuristic symbolically executed test that can tell us for sure that our function is correct?

We just change the function name so it starts with the word prove. That’s it! DappTools will behind the scenes convert the fuzz test to a symbolic test. Can it get any simpler than this?

function  prooveAddFunction(uint128 x, uint128 y) public {
 assertEq(dapptools.add(x, y),uint256(x)+uint256(y));
}

Lets run the test

Now, assuming there aren’t any big bugs in DappTools or the mathematical solver they use, we should have 100% certainty that our assertion holds over all possible inputs.

So why ever use a fuzz test?

If symbolic execution tries every input and is still fast, can we just throw away “test” and prefix every test with “prove”?

The amount of possible paths explodes as the code’s complexity increases which makes symbolic execution infeasible beyond a moderate level of complexity.

Use symbolic tests wherever you can for simpler functions and other isolated parts of your codebase.

Use fuzz tests to test larger parts of your codebase that are too complex to symbolically execute.

Here is a table that can guide you while making this decision.

Closing Thoughts

We only just scratched the surface of DappTools and what it has to offer. Learn more about DappTools on the project homepage: https://dapp.tools/ and let it supercharge your workflow.

Special Thanks

I’m thankful to @Transmissions11 for his work on DappTools and allowing me to use his work in this post. Go follow him on twitter where he is building a vibrant community around this exceptional tool.

0 Comments

Leave a Reply

More great articles

Code your first DAO

DAOs are an effective and safe way to work with like-minded folks around the globe. DAOs are like digital organizations…

Read Story

Set up your own Ethereum Node using Geth

Geth (short for Go Ethereum) is one of the three original implementations (along with C++ and Python) of the Ethereum…

Read Story

10x Solidity Development using Foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. Foundry is consistently 1.5x-11x…

Read Story

Never miss a minute

Get great content to your inbox every week. No spam.
[contact-form-7 id="6" title="Footer CTA Subscribe Form"]
Arrow-up