Back
Featured image of post How to install truffle, compile and run our first smart contract with Solidity

How to install truffle, compile and run our first smart contract with Solidity

Learn how to install truffle and compile our first smart contract with Solidity

Table of Content

In this article I will guide you through a complete step by step guide about how to configure your machine and setup to have your first smart contract running and installed on a Blockchain.

Warning

This article was developed using Truffle v5.5.3 (core: 5.5.3), Ganache v7.0.1, Solidity - 0.8.12 (solc-js), Node v17.7.0 and Web3.js v1.5.3

Steps

The steps we are going to make are listed below

  1. Configure our environment and install required tools: truffle, ganache.
  2. Design a simple smart contract with Solidity and compile it.
  3. Deploy our smart contract bytecode.

Configure our environment and install required tools

This is the basic step required before starting. We need to install some tools to make our development easier and faster.

Installing the tools

The first thing is to install the tools called truffle and ganache that allow us to have a easy development environment in our local computer. However, as requirements, we need to have NodeJS v12 or later installed. To do so, we will use nvm (a Node Version Manager) to setup the specific version.

Install NVM

To install or update nvm, you should run the install script. To do that, you may either download and run the script manually, or use the following cURL or Wget command:

1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

After successful download, add required completion script to your bashrc

1
2
3
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Download NodeJS v12+ with NVM

First, run nvm list to get the list of available node versions

1
nvm list
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
        v8.10.0
        v8.17.0
       v10.24.1
        v13.7.0
->      v17.0.1
default -> node (-> v17.0.1)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v17.0.1) (default)
stable -> 17.0 (-> v17.0.1) (default)
lts/* -> lts/gallium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0
lts/dubnium -> v10.24.1
lts/erbium -> v12.22.9 (-> N/A)
lts/fermium -> v14.18.3 (-> N/A)
lts/gallium -> v16.13.2 (-> N/A)

Select the default version or any other greater than v12

1
nvm install stable

A successful installation will report

1
2
3
4
5
6
Downloading and installing node v17.7.0...
Downloading https://nodejs.org/dist/v17.7.0/node-v17.7.0-linux-x64.tar.xz...
###################################################################################################################### 100,0%
Computing checksum with sha256sum
Checksums matched!
Now using node v17.7.0 (npm v8.5.2)

Set the downloaded Node JS version

At this point, the nvm tool already downloaded the requested version. The last step, is to make it active

This is required since nvm tool allow us to have multiple NodeJS version installed at the same time in our local environment.

1
2
nvm use stable
Now using node v17.7.0 (npm v8.5.2)

Install Truffle

After installing NodeJS, next we require to install truffle tool. The installation process is as simple as running

1
npm install -g truffle

Wait for download to finish, and you should have truffle installed globally in your system.

To make sure truffle was completely installed, we check the installed tool version

1
truffle version
1
2
3
4
5
Truffle v5.5.3 (core: 5.5.3)
Ganache v7.0.1
Solidity - 0.8.12 (solc-js)
Node v17.7.0
Web3.js v1.5.3

Install Ganache

To install ganache globally, run:

1
npm install ganache --global

Once installed globally, you can start ganache right from your command line:

1
ganache

Design a simple smart contract with Solidity and compile it

Now that we have some basic tools installed, lets code our smart contract!

Creating a Truffle project

To use most Truffle commands, you need to run them against an existing Truffle project. So the first step is to create a Truffle project.

In order to create an empty project, you need to run init command

1
truffle init  
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Starting init...
================

> Copying project files to /home/sergio/Downloads/proyectos/zuretzat-sc/new

Init successful, sweet!

Try our scaffold commands to get started:
  $ truffle create contract YourContractName # scaffold a contract
  $ truffle create test YourTestName         # scaffold a test

http://trufflesuite.com/docs

Now, pick a contract name, for example Demo and scafold your new project

1
truffle create contract Demo

Once this operation is completed, you’ll now have a project structure with the following items:

  • contracts/: Directory for Solidity contracts
  • migrations/: Directory for scriptable deployment files
  • test/: Directory for test files for testing your application and contracts
  • truffle.js: Truffle configuration file

Apart from previous directories, the content of a bare minimum project is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.
├── contracts
│   ├── Demo.sol
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── package.json
├── test
└── truffle-config.js

3 directories, 5 files

Next thing we need to focus on: Solidity programming and contract design.

Building a contract with Solidity

For article purposes, we are going to design and build a simple example of Solidity contract that just writes arbitrary information to the ledger and reads data from it.

For this purpose, we assume following requirements:

  • The contract must be installable and must keep a reference to contract owner
  • The contract must allow anyone to register the specified data model
  • The contract must allow anyone to fetch the previously registered information. The lookup will be done by key.

So, truffle created a sample file called Demo.sol with some basic code

1
cat contracts/Demo.sol  
1
2
3
4
5
6
7
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract Demo {
  constructor() public {
  }
}

We need to update this Demo.sol file with our own code

Change 1: setup latest Solidity compiler version

We set the version of solidity v0.8.12. The modified code is

1
2
3
4
5
6
7
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

contract Demo {
  constructor() public {
  }
}

Change 2: we set the contract owner

In order to set the contract owner, we need to modify constructor function so that the caller of the contract get stored in the Blockchain. To do so, we need to save this information as part of the contract state data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

contract Demo {

  // owner keeps the address of which installed the contract
  address private owner;

  constructor() public {
    // we store the address who installed this contract as owner
    owner = msg.sender;
  }
}

Change 3: create the data model

Next step, is to defined the data model we need to store the information in the ledger. This is just a tiny example to show you that Solidity can hold more information that what usually is been saved in an ERC20, ERC721, etc.

Our data model, will be called Record and will contain following attributes:

  • Id: numeric value that works as a key. This data is provided by the user.
  • Data: string data. This data is provided by the user.

Knowing previous information, we can model our structure as

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
Record is our structure definition for our data model
*/
struct Record {
    /**
    id is the unique identifier of the record
    */
    uint id;

    /**
    content is the arbitrary content of the record
    since this is an example, we do not focus on gas optimization
    */
    string content;
}
  • You can define your own type by creating a struct.
  • They are useful for grouping together related data.
  • Structs can be declared outside of a contract and imported in another contract.

Add our new struct Record to our contract and your code will look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

contract Demo {

  // owner keeps the address of which installed the contract
  address private owner;

  /**
  Record is our structure definition for our data model
  */
  struct Record {
      /**
      id is the unique identifier of the record
      */
      uint id;

      /**
      content is the arbitrary content of the record
      since this is an example, we do not focus on gas optimization
      */
      string content;
  }

  constructor() public {
    // we store the address who installed this contract as owner
    owner = msg.sender;
  }
}

Change 4: create a function to read data from Blockchain

According to our self assigned requirement:

  • The contract must allow anyone to fetch the previously registered information. The lookup will be done by key.

we can model the Solidity function as

1
2
3
4
function readRecord() external view returns(Record memory) {
    Record memory r;
    return r;
}

After populating the function with requested code it will look like:

1
2
3
4
5
6
7
function readRecord(uint key) external view returns(Record memory) {
    // lookup requested record by given key
    Record memory r = recordData[key];
    // if r is not found by key, returned r object will be populated with default values
    // default data Record { id: 0, content: "" } will works as NULL
    return r;
}

As you can see, there is a new variable called recordData that is new, and is defined as

1
mapping(uint => Record) private recordData;

Change 5: create a function to write data to Blockchain

According to our self assigned requirement:

  • The contract must allow anyone to register the specified data model

we can model the Solidity function as

1
2
3
function writeRecord(uint key, string memory content) external returns(bool) {
    return true;
}

After populating the function with requested code it will look like:

1
2
3
4
5
6
7
function writeRecord(uint key, string memory data) external returns(bool) {
    // create a new record item from user input parameters
    Record memory r = Record({id:key, content:data});
    // store item into the blockchain
    recordData[key]=r;
    return true;
}

Deploy our solidity smart contract

After designing and building our Solidity contract, we get the following code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

contract Demo {

  // owner keeps the address which installed the contract
  address private owner;

  /**
  Record is our structure definition for our data model
  */
  struct Record {
      /**
      id is the unique identifier of the record
      */
      uint id;

      /**
      content is the arbitrary content of the record
      since this is an example, we do not focus on gas optimization
      */
      string content;
  }

  mapping(uint => Record) private recordData;

  constructor() public {
    // we store the address who installed this contract as owner
    owner = msg.sender;
  }

  function writeRecord(uint key, string memory data) external returns(bool) {
    // create a new record item from user input parameters
    Record memory r = Record({id:key, content:data});
    // store item into the blockchain
    recordData[key]=r;
    return true;
  }

  function readRecord(uint key) external view returns(Record memory) {
    // lookup requested record by given key
    Record memory r = recordData[key];
    // if r is not found by key, returned r object will be populated with default values
    // default data Record { id: 0, content: "" } will works as NULL
    return r;
  }
}

Compiling the contract

Now that our Demo contract is designed, our next step it to compile it. We will use truffle compile to do it.

1
truffle compile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Compiling your contracts...
===========================
✔ Fetching solc version list from solc-bin. Attempt #1
✔ Downloading compiler. Attempt #1.
> Compiling ./contracts/Demo.sol
> Compiling ./contracts/Migrations.sol
> Compilation warnings encountered:

    Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
  --> project:/contracts/Demo.sol:27:3:
   |
27 |   constructor() public {
   |   ^ (Relevant source part starts here and spans across multiple lines).


> Artifacts written to /home/r00t/demo/build/contracts
> Compiled successfully using:
   - solc: 0.8.12+commit.f00d7308.Emscripten.clang

Deploying the contract

A way to see the result of the compilation is to review the file under ./build/contracts/Demo.json. Within that file, the compiled bytecode can be seen. In this example, this is the bytecode i get.

1
0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610666806100606000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063f2e0d5741461003b578063fb891df81461006b575b600080fd5b610055600480360381019061005091906102d4565b61009b565b60405161006291906103e6565b60405180910390f35b6100856004803603810190610080919061053d565b61016a565b60405161009291906105b4565b60405180910390f35b6100a36101cd565b600060016000848152602001908152602001600020604051806040016040529081600082015481526020016001820180546100dd906105fe565b80601f0160208091040260200160405190810160405280929190818152602001828054610109906105fe565b80156101565780601f1061012b57610100808354040283529160200191610156565b820191906000526020600020905b81548152906001019060200180831161013957829003601f168201915b505050505081525050905080915050919050565b600080604051806040016040528085815260200184815250905080600160008681526020019081526020016000206000820151816000015560208201518160010190805190602001906101be9291906101e7565b50905050600191505092915050565b604051806040016040528060008152602001606081525090565b8280546101f3906105fe565b90600052602060002090601f016020900481019282610215576000855561025c565b82601f1061022e57805160ff191683800117855561025c565b8280016001018555821561025c579182015b8281111561025b578251825591602001919060010190610240565b5b509050610269919061026d565b5090565b5b8082111561028657600081600090555060010161026e565b5090565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6102b18161029e565b81146102bc57600080fd5b50565b6000813590506102ce816102a8565b92915050565b6000602082840312156102ea576102e9610294565b5b60006102f8848285016102bf565b91505092915050565b61030a8161029e565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561034a57808201518184015260208101905061032f565b83811115610359576000848401525b50505050565b6000601f19601f8301169050919050565b600061037b82610310565b610385818561031b565b935061039581856020860161032c565b61039e8161035f565b840191505092915050565b60006040830160008301516103c16000860182610301565b50602083015184820360208601526103d98282610370565b9150508091505092915050565b6000602082019050818103600083015261040081846103a9565b905092915050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61044a8261035f565b810181811067ffffffffffffffff8211171561046957610468610412565b5b80604052505050565b600061047c61028a565b90506104888282610441565b919050565b600067ffffffffffffffff8211156104a8576104a7610412565b5b6104b18261035f565b9050602081019050919050565b82818337600083830152505050565b60006104e06104db8461048d565b610472565b9050828152602081018484840111156104fc576104fb61040d565b5b6105078482856104be565b509392505050565b600082601f83011261052457610523610408565b5b81356105348482602086016104cd565b91505092915050565b6000806040838503121561055457610553610294565b5b6000610562858286016102bf565b925050602083013567ffffffffffffffff81111561058357610582610299565b5b61058f8582860161050f565b9150509250929050565b60008115159050919050565b6105ae81610599565b82525050565b60006020820190506105c960008301846105a5565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061061657607f821691505b6020821081141561062a576106296105cf565b5b5091905056fea2646970667358221220e245225e3554a6b6a54cc859d5a0d5d4744ee8e0d13146de68f3caa0f0510f7564736f6c634300080c0033

In order to deploy our contract in a testing environment, we use ganache tool, so that we can have a local EVM with some predefined accounts. To run ganache simply type in your terminal ganache.

1
ganache

And it will start a RPC server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
ganache v7.0.3 (@ganache/cli: 0.1.4, @ganache/core: 0.1.4)
Starting RPC server

Available Accounts
==================
(0) 0xeadcE979B7D8EBb7970417b452D08291c8265521 (1000 ETH)
(1) 0x574537620d65FB8fB1d44dffFA1b6b350503dFdA (1000 ETH)
(2) 0x1093BC537E6BEa991111B30280c737089b20af14 (1000 ETH)
(3) 0x45ff62a6fAbb54020C16728eC9bC46E896BCb88E (1000 ETH)
(4) 0xcabD449394A29f91b92cB790CD50edA32A11C23b (1000 ETH)
(5) 0xA731Cf52A50af2a4C6653ACD4bbFeC72A2BC9A08 (1000 ETH)
(6) 0x7440322BB3CDa08F31d454e710690D44b6b2A9ED (1000 ETH)
(7) 0x83D600090213Bd86ccA983455728C92bFB433DC4 (1000 ETH)
(8) 0xedf61b529F8feB023807C103d3336827BE6D88A7 (1000 ETH)
(9) 0xE4c62D0eAce8C6f258505e24f5e2aD687501537F (1000 ETH)

Private Keys
==================
(0) 0x1677700009b08909aa22b9b37be7bb67b323e3de69dd07d7c771c24d75487950
(1) 0x61c335032c0eaae0c09f73c22355d34a3b7a91737152f4204c2c8141c4a6bbb6
(2) 0xedd34a9bb26c1e4386e8870f67048de1bb5c87c0253c0bca45c71c89bde63691
(3) 0x69a16039ac3a15dc53e0b109a0303f5dc3a744ed3533d6dd6b289fccd8446677
(4) 0x7a3f6ba6705545caecbd41f59784cc0e0c6a7b6f588344977e8a4014767879e7
(5) 0xffc5fa88ff2c879803ada4eb58173016d36172feb3c21fc6e87507022bddfc19
(6) 0xe0a2991cb878fd7ce0716a4aaa6c02c2e58679835c6b40240af20f72755e6c33
(7) 0xd79b1b95bcb3fd472217df57b908943d16ed4bc8cc7fcb067c6c5db77dee7fca
(8) 0xe2f1af569e81be8f2df8a1b31cc755d048aaeb7b28f2172b76f5caf82ae94e5d
(9) 0xb049f424e807571269e84c580b19e997a4ae48ac031ae0ab79c2572d13eeb31b

HD Wallet
==================
Mnemonic:      drip purpose object edit traffic refuse fresh inherit entry length badge violin
Base HD Path:  m/44'/60'/0'/0/{account_index}

Default Gas Price
==================
2000000000

BlockGas Limit
==================
30000000

Call Gas Limit
==================
50000000

Chain Id
==================
1337

RPC Listening on 127.0.0.1:8545

After running ganache it will provide you and endpoint to connect with. In my case the endpoint is located at http://127.0.0.1:8545.

To connect to this endpoint, modify the truffle-config.js file and define your test network as ganacheNet like following snippet

1
2
3
4
5
6
networks: {
    ganacheNet: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*"
    }

Now, to deploy the contract against specified network, use truffle deploy command indicating which target network you want to use.

1
truffle deploy --network ganacheNet

where ganacheNet is just the name given in the truffle-config.js

A successful contract deployment will report to you the contract address and the cost of the operation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.


Starting migrations...
======================
> Network name:    'ganache'
> Network id:      1646908313472
> Block gas limit: 30000000 (0x1c9c380)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xfaa7a45c83ddedd424fa2e55f00e22d1255e032b86d8f93a8e7ec4aa0609f9c5
   > Blocks: 0            Seconds: 0
   > contract address:    0x5EeC2846bf49d445C405b67e59b56e335163a78C
   > block number:        1
   > block timestamp:     1646908603
   > account:             0xeadcE979B7D8EBb7970417b452D08291c8265521
   > balance:             999.99915577075
   > gas used:            250142 (0x3d11e)
   > gas price:           3.375 gwei
   > value sent:          0 ETH
   > total cost:          0.00084422925 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:       0.00084422925 ETH

Summary
=======
> Total deployments:   1
> Final cost:          0.00084422925 ETH
  • Deployed contract addess: 0x5EeC2846bf49d445C405b67e59b56e335163a78C
  • gas used: 250142
  • gas price: 3.375 gwei

It cost about $0.00084422925 \ ETH$ using $250142$ gas. If we run this same deployment in the Ethereum mainnet, the $ETH$ gas cost today is $23$ gwei, so that the deployment cost would be

$$ 250142 \cdot 23 / 10^9 = 0.005753266 \ ETH $$

At current exchange rate of $(1 \ ETH = 2.357,66 \ EUR)$, this would be $13,56 \ €$ for our simple read-write contract.

However, in our ganache TEST environment, all deployments are FREE.

Conclusion

And this is all, the steps to prepare, design, build and deploy an smart contract into Ethereum or any other EVM compatible networks. I just would like to mention two thing:

  1. If you are planning to deploy and run serious DApps on any Blockchain network, make sure you properly TEST all your features.
  2. If you deal with people money, make sure you relay on external security auditors to verify your code quality, errors, vulnerabilities and so on.
  3. Take into consideration some smart contract upgrade patterns.

References



💬 Share this post in social media

Thanks for checking this out and I hope you found the info useful! If you have any questions, don't hesitate to write me a comment below. And remember that if you like to see more content on, just let me know it and share this post with your colleges, co-workers, FFF, etc.

Please, don't try to hack this website servers. Guess why...