Use dapp.tools for Ethereum Contract Development

KC Tam
Coinmonks
8 min readMay 16, 2019

--

Introduction

When developing and testing Solidity contract, I believe most people are using Remix, an online IDE (integrated development environment) everyone can use through a browser. Remix comes with editor, terminal, compiler and runtime environment, on either a built-in Ethereum simulator or an external Ethereum network.

Because of a new project I got a chance to use a toolset from dapp.tools, and find almost a command-line equivalent to Remix running in my localhost. I find it very intuitive. Also dapp.tools come with some packages that I can easily incorporate into my contract.

Here in this article I first show some basic functions and operation of dapp.tools, I will use a simple contract code and see how to create unit test on the contract itself, ensuring the logic defined in the contract is exactly what is expected in the test case. After that I deploy the contract and interact it with an Ethereum simulation, testnet, which is also from dapp.tools. In the next article I will create another contract and see how to incorporate pre-built token package into my project.

dapp.tools Overview and Installation

The best introduction for dapp.tools is in their page.

As described by their site, dapp.tools is a set of “command-line-friendly tools for blockchain development”. As a quick overview, dapp.tools come with several handy tools for Ethereum contract development. Again you can see the best description on those tools and the description in their website.

In my demonstration I will use dapp to perform unit test of my contract code and deploy it in the testnet provided by dapp.tools. After that i will use seth to interact with my deployed contract.

The installation is quite straight-forward. As I am using Mac I simply follow their instruction.

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

Initialize a Workspace

The first thing of our demonstration is to initialize a workspace for dapp, using dapp init.

mkdir simplestorage
cd simplestorage
dapp init

From the output we learn that, after dapp init, we have

  • a directory structure created, with directory src keeping our contract code and test code, and directory lib for the packages.
  • inside src, there are two files created: Simplestorage.sol is for our contract code, and Simplestorage.t.sol is for the test code. They are both prepopulated with basic contract structure.
  • inside lib, a package ds-test is installed. This is the package for testing our contract.
  • A sanity test is performed.
  • A git repository is created for this workspace.

Here is the directory structure after dapp init.

Directory structure created by dapp init

Contract Code and Test Code

Now let’s prepare the contract code and the test code.

Here I am using the famous Simple Storage contract code. This contract keeps a stored value when it is first deployed. And we can use the two functions to interact with the contract: get() for the stored value, and set(x) to update the value.

The workspace initialization creates an empty contract using the workspace directory name: src/Simplestorage.sol. We simply copy this contract code in this file.

pragma solidity ^0.5.6;contract Simplestorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() view public returns (uint retVal) {
return storedData;
}
}

The workspace initialization also creates a test contract of the name, with extension t.sol: src/Simplestorage.t.sol. We simply copy the test code in this file. While it comes with two sample tests.

pragma solidity ^0.5.6;import "ds-test/test.sol";import "./Simplestorage.sol";contract SimplestorageTest is DSTest {
Simplestorage simplestorage;
function setUp() public {
simplestorage = new Simplestorage();
}
function testGetInitialValue() public {
assertTrue(simplestorage.get() == 0);
}
function testSetValue() public {
uint x = 300;
simplestorage.set(x);
assertTrue(simplestorage.get() == 300);
}
}

Let’s take a look on this test contract. Simplestorage.t.sol is also a Solidity contract. It first imports the test.sol inds-test package (installed during workspace initialization) and the Simplestorage.sol contract.

We are defining our test cases as functions. Before that we need a setUp() function to deploy this contract.

Functions for test cases are named with prefix test. Each of these functions will be performed as test item. Test is passed only when condition in assertTrue(condition) is true. Function testGetInitialValue() is to get the stored value with get() function after contract deployment. Function testSetValue() is to check whether set(x) can be used for a new value and check the value with get() function.

You may ask where the test is running. Dapp test uses a Hevm, their own EVM implementation good for testing and debugging. Therefore we do not need to run any testnet for the contract testing.

Test Contract Code

As said, we do not need a testnet for contract testing. Here we use command dapp test for unit test.

dapp test

Dapp will search those contract with t.sol extension, and execute setUp() and all functions with prefix test: in our test code there are two functions: testGetInitialValue() and testSetValue().

Here are the result.

From the result we see dapp test first compile the two contracts. After that, the two test cases inside Simplestorage.t.sol are run and successfully passed.

Meanwhile the compiled result is stored in directory out. We can find all the artifacts after compilation in this directory.

Run Testnet

Now our contract is ready for deployment. We will first run the testnet, an Ethereum network provided by dapp.

Use a different terminal to run the testnet

dapp testnet
Use another terminal to run testnet

Some observation on running dapp testnet

  • All the testnet setup is in my home directory, under ~/.dapp/testnet/8545.
  • If we take a look on this directory, we see the geth component and other setup.
  • Inside keystore directory we will have the coinbase account setup. The address is also shown in the testnet screen (here it is 0x62a8…c362, and this address is different every time when we run dapp testnet.)
  • The testnet is in fact a running geth with genesis block and coinbase key kept in this directory.

Here is the directory structure of the testnet.

Deploy Contract

With a terminal with dapp testnet running, we are ready to deploy the contract.

We first specify the coinbase address we are using for contract deployment and tell where to get the private key. It is done through setting environment variables ETH_FROM and ETH_KEYSTORE.

export ETH_FROM=0x62a872619c4072f73724d0fb5905354fc6ecc362
export ETH_KEYSTORE=~/.dapp/testnet/8545/keystore

We use dapp create to deploy the contract. When prompted for passphrase, it is empty for testnet.

dapp create Simplestorage

As we see a transaction is executed. This is the contract deployment.

Note we will get back an address (0x9fa2…7fde). It is the contract ID (or contract address) after deployment. For easy access we will define another environment variable SIMSTO to keep this value.

export SIMSTO=0x9fa245ee3daa5314fdfe33124b9215029c157fde

Now the contract is deployed, and we are ready to interact with it.

Interacting with Contract

To interact with contract we are using seth. Seth is the Ethereum client made in command line. We can perform many tasks with seth. Here we just use it for executing functions in the deployed contract.

First we will use seth call for function get() for those functions without updating the blockchain. What we need is the deployed contract ID (the environment variables we set in previous step).

seth call $SIMSTO “get()”

As expected, the value is initially zero.

We will set a new value. For functions requiring arguments, we place the argument list after the function. Also seth requires hex input and we will use seth --to-uint256 to convert the number into hex and fitting to 256-bit.

We will use seth send for functions updating blockchain.

seth send $SIMSTO “set(uint)” $(seth --to-uint256 100)
The first command is just to show a conversion of number to uint256

We see a new transaction is created, and the transaction is included in a new block.

Finally we check the stored value again with get().

seth call $SIMSTO “get()”

The result is 0x64 (100), as expected.

Clean-Up

And after we complete our demonstration or testing, we can stop the dapp testnet terminal by control-c. The directory for testnet, i.e. ~/.dapp/testnet/8545 will be emptied. All information in testnet, including the account and the block created during the test, are gone. This helps a lot as we do not need to delete files in various directories.

After testnet is stopped, all files in this directory are gone.

Side Node: Deploy Contract in Public Ethereum

The default setup for seth is going to local RPC, which is the local testnet.

If we wish to deploy our contract in public Ethereum networks, what we need is to let seth knows the target network through environment variables.

The variables we need are

export SETH_CHAIN=<the chain you are deploying, e.g. mainnet, rinkeby, etc>export ETH_KEYSTORE=<your keystore in the Ethereum network>export ETH_FROM=<your coinbase address in this Ethereum network>

Then all commands from seth will go to the Ethereum network you specify. Make sure you have ethers on the network in your account for gas.

Closing

We just show how to perform unit test for an Ethereum contract using dapp tools. We also deploy a testnet and use seth to execute functions for a deployed contract.

This only shows the very basic functionality of dapp tools. In the next article I will create a bigger contract, and see how to incorporate ds-token package to make token useable in my contract.

Get Best Software Deals Directly In Your Inbox

--

--