Lesson 5

The Truffle framework

4

While it's good to know how to manually compile and run Solidity code, it gets tedious for larger apps. Thankfully, there are som great DApp frameworks that take care of this for you – the most popular one being Truffle.

The Truffle website

Throughout the rest of the course, we'll be using Truffle to build our DApp following their best practices.

Creating a Truffle project

To install Truffle globally on your computer, run the following command:

After the installation is done, you should be able to run truffle version to see your installed version.

Make sure that your Truffle version is at least v5.0.0.


Alright, now let's again re-create our HelloWorld example app from scratch (don't worry, we'll soon build something new) – this time using Truffle!

Once you've created a new folder for your project and navigated to it, the truffle init command will automatically generate a boilerplate file structure for your DApp.

What your generated file structure should look like

Let's go through the generated files and folders one by one so that we know what's going on!

  • contracts: As you could've guessed, this is where your app's Solidity contracts (.sol files) should go.

  • migrations: The files in this folder will orchestrate the deployment process, similarly to what we did in the last chapter. Thanks to Truffle however, we get a lot of functionality for free so that the files can be written in a much simpler way.

  • test: Having automated tests for your contracts is crucial! They go in this folder, and can be written either in Solidity or JavaScript.

  • truffle-config.js: A configuration file for your project's settings.

HelloWorld in Truffle

Let's add our HelloWorld.sol contract that we wrote earlier in the contracts folder. Again, here's what it looks like, in case you had forgotten:

contracts/HelloWorld.sol

Next, as we saw in the previous chapter, we need to deploy this contract to some address in order to interact with it. With Truffle, we just add a new file to the migrations folder to take care of this process. It should be prefixed with a 2 (so that it runs after 1_initial_migrations.js), so let's call it 2_deploy_hello.js:

migrations/2_deploy_hello.js

Ah, isn't that nice? The whole process of compiling your contract, connecting to the network, and spending ether to deploy the bytecode is summarized in just a few lines! That certainly saves us some time!

Now, in order to deploy our contract, we need to first specify which network we want to connect to. Remember, there are several Ethereum networks out there (the real one being the "mainnet"), but in our case we simply want to connect to our own local ganache-cli network.

To specify this, we open the file truffle-config.js and add the details of our network in JSON format, as documented on Truffle's website.

truffle.js

We also need to specify what version of solc we want Truffle to use when compiling our contracts. This should match the version specified on the first pragma line of our .sol files (in this case, 0.5.100.5.10).

truffle.js

After this, we simply run truffle migrate in our console (make sure that your ganache-cli instance is still running in the background as usual so that Truffle can connect to it)!

Success!

You should now see the contract address that your contract was deployed to. In my case, the address for the HelloWorld contract is 0xCD461e4B556eF1Aa11DB570809c0D70B7DfF0a09, but yours will obviously be something different.

Using the Truffle console

Finally, if we want to interact with our contract, we can use the Truffle console:


By entering the console, you will have the HelloWorld contract object available as a global variable and the web3 library is automatically included and connected.

When inside the console, you can access your deployed contract instance by typing HelloWorld.deployed(). This will return a promise, so if you want to view the result, you should use the await keyword:

Using that contract instance, we can then call our getMyName function by first saving the returned instance, and then using the syntax instance.getMyName.call().

We use the .call() method whenever we call a function that does not write anything to the contract. In other words, for functions that use the view or pure modifier, just like getMyName.

And there's the expected result of the function!


Testing your Truffle code

As I mentioned before, testing your Solidity code is crucial. You don't want to run into a situation where funds get stolen because of a bug in your contract.

In Truffle, you can write tests for your smart contracts in both JavaScript and Solidity, each with their pros and cons.

I've found that Solidity tests are usually great for simple unit tests that call specific parts of your code, whereas JavaScript tests are great for putting yourself in the user's shoes and testing wider use cases (a.k.a. integration tests).

In this chapter, we'll stick to JavaScript testing. In your test folder, create a new file called helloworld.js (you can name it whatever you want).

Truffle uses the Mocha testing framework behind the scenes, but uses contract() instead of describe() as its main function.

In our code, we'll do exactly what we did in the console: we simply fetch the deployed contract instance using HelloWorld.deployed() and then call the getMyName function and match it against the expected value ("Tristan").

test/helloworld.js

Running our tests is just as simple as you'd expect:

It passes!

To prove that the test is working as it should, you can try changing the expected value in your test to something else (like Bob) and watch the test fail as you run it again:

test/helloworld.js

Well done! Let's write one more test, this time for the writable changeMyName function, so that we get the hang of it.

This time, when we call changeMyName, we cannot use the .call() method, since it doesn't have either the view or pure modifier.

test/helloworld.js

Run truffle test again, and you should now see both tests passing:

Hooray!

We've now gone through the basics of setting up a Truffle project. In the next chapter, we will start working on a more serious application, a decentralised Twitter clone (you can see the final result here), to see what pitfalls to avoid when dealing with more advanced smart contracts.

Comments

profile/avatar/default
SoongChee Gi

Unable to round-trip http request to upstream: dial tcp: lookup www.tweether.io: no such host

profile/avatar/default
Yahya

@tristan at line 11, we can also re-write as const value = await instance.getMyName(); which one do you recommend, please?

Tristan Edwards

@yahya: Since getMyName is a view function, it's good to use call to make sure an Ethereum transaction isn't sent (check out the web3 docs)

profile/avatar/default
Yahya

@tristan is there any way to automatically export the contract address? for example to create a new file which contains the contract name and address.

profile/avatar/default
Yahya

thank you very much, really looking forward!

Tristan Edwards

@yahya: Good question! In the "Contract manager" chapter you can see how to get the address of the recently deployed contract. After that I suppose you could use Node's writeFile function to create a text document.

profile/avatar/default
Yahya

so migration basically compiling and deploying the contract?

profile/avatar/default
Yahya

because I can see a command called 'truffle compile'

Tristan Edwards

@yahya: That's right, the migrate command will automatically figure which contracts need to be compiled before the migration. "truffle compile" simply compiles your contracts into bytecode, but doesn't deploy them anywhere.

profile/avatar/default
Yahya

@tristan thank you very much for your explanation

profile/avatar/default
Yahya

@tristan it would be nice if you say a few words about deployer. It seems an argument of a function, so what parameter we are passing here? thanks

profile/avatar/default
Yahya

truffle create contract HelloWorld try this command to create the contract.