DEV Community

Cover image for Smart Contract Dev : Hands-On with Truffle, Ganache and Solidity
Yassine Kosrani
Yassine Kosrani

Posted on

Smart Contract Dev : Hands-On with Truffle, Ganache and Solidity

Best guide for Developing, Deploying and Testing Smart Contracts using truffle and ganache .

Overview:
In this guide, we’ll set up an environment that simulates the real Ethereum network without using the usual testnets provided by Ethereum, such as Sepolia or Goerli. Instead, our testnet will be local, using the Ganache client, which automatically mines blocks instantly. This simulates a real blockchain environment without waiting for actual block times, allowing for rapid testing and development. I prefer this method over traditional testnets because it eliminates the need to collect test Ether from faucets, saving time. However, this method has some drawbacks, which we’ll discuss later.

Truffle

Truffle is a comprehensive development framework for building, testing, and deploying smart contracts on Ethereum, streamlining the entire blockchain development process, we’ll link it to the ganache client for our tests rather than the actual Ethereum network ‘mainnet’.

Introduction:
In this guide, we’re not using any IDE. The primary tools are Windows PowerShell and Node Package Manager (npm)."

It might be unusual for beginners to work without any GUI but this method really helped me getting hand on hand with smart contract development using only CLI , so first of all you should install Node.js on your system here’s a link for downloading it :

Preparing Your Development Environment:**

After installing Node.js on your system, we proceed to install the required libraries and frameworks. We will install the Ganache client, the Truffle framework, the Solidity compiler, and the Web3 library. Open the command prompt and type:

npm install -g solc web3 truffle ganache

Make sure, after executing that command, to check if they are installed. For example, try checking the Truffle version as shown in the following screenshot:

npm truffle --version

Truffle version

you could see all versions with this command :
npm list

Following step

Now after setting all requirements lets start developing , Create your project folder and navigate to it as cited in the following screenshot

create project

commands :

mkdir ProjectName

cd ProjectName
Enter fullscreen mode Exit fullscreen mode

now lets initialize a truffle project :

truffle init

Initialize Truffle project

Truffle will create a new project folder in the specified folder with standard project folders :

New project folder

Now lets start configuring our truffle project , Open the truffle-config.js file using Notepad to coordinate it with the Ganache client later when we start the service .

uncomment the network section and set it as this

networks: {

    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 8545,            // Standard CLI Ethereum port
     network_id: "*"     
     }
},
compilers: {
    solc: {
      version: "0.8.26" ,   // Fetch exact version from solc-bin (default: truffle's version)
      settings: {          
       optimizer: {
         enabled: true,
          runs: 200
       }

    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Do not forget to fetch the exact version of the solidity compiler try: npm solc — version

Solidity

Now, let’s create our smart contract, which we’ll deploy later. I prefer writing my contracts from scratch in Notepad and then compiling them using Truffle directly. This method has helped me get comfortable with the Solidity programming language. However, if you’re familiar with using an IDE like Remix, that would work well too. The following code is a sample of the contract I created: it’s an ERC20 token named Skaltuchet, using the OpenZeppelin library

Do not forget to install Openzepplin’s library , try :

npm install openzeppelin/contracts@5.0.2

Add this code to the contracts folder as solidity code (.sol)

pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Skaltuchet is ERC20, Ownable {
    uint256 public constant TOKENS_PER_ETH = 1000;
    uint256 public constant INITIAL_SUPPLY = 1000000 * 10**18; // 1 million tokens, considering 18 decimals

    // Event to track token purchases
    event TokensPurchased(address indexed buyer, uint256 amountOfETH, uint256 amountOfTokens);

    constructor(address initialOwner) ERC20("Skaltuchet", "SKT") Ownable(initialOwner) {
        _mint(initialOwner, INITIAL_SUPPLY / 2);       // Mint 500,000 tokens to the deployer
        _mint(address(this), INITIAL_SUPPLY / 2);    // Mint 500,000 tokens to the contract itself
    }

    function buyTokens() public payable {
        require(msg.value > 0, "Send ETH to buy tokens");

        uint256 tokensToBuy = (msg.value * TOKENS_PER_ETH * 10**decimals()) / 1 ether;
        require(balanceOf(address(this)) >= tokensToBuy, "Not enough tokens in the reserve");

        _transfer(address(this), msg.sender, tokensToBuy);

        // Emit the TokensPurchased event
        emit TokensPurchased(msg.sender, msg.value, tokensToBuy);
    }
}
Enter fullscreen mode Exit fullscreen mode

After saving the contract’s code in the contracts folder, let’s proceed to the migration.js file to ensure a successful deployment. In the migrations folder, add a new file (2_Deploy.js) that provides a script to deploy smart contracts to Ethereum networks (such as development, testnets, or mainnet). In our case, it’s the Ganache client. The deployment is as follows:

const Skaltuchet = artifacts.require("Skaltuchet");

module.exports = function (deployer, network, accounts) {
    // Use the first account from the ganache client accounts as the owner 
    const initialOwner = accounts[0];

    deployer.deploy(Skaltuchet, initialOwner)
        .then(() => {
            console.log("Skaltuchet contract deployed successfully!");
        })
        .catch(error => {
            console.error("Deployment failed:", error);
        });
};
Enter fullscreen mode Exit fullscreen mode

In our case probably all things are good we settled truffle-configuration, smart contract, and the deployment file as well, Now let’s start the ganache client, and run this command in the command prompt.

ganache

Starting the ganache client

Normally the ganache client should start RPC Listening on 127.0.0.1:8545(local host as defined in the truffle config)and you’ll have 10 accounts with 1000 ETH in each one of them as in the following screenshot.

Ganache starts mining

In the following steps, we’ll compile our contract, deploy it to the test network, interact with it, and test its features.

keep the ganache running and open another command prompt window ,Run these command :
truffle compile

Contract compiled successfully

when compiled successfully lets deploy it by running :

truffle migrate

Contract deployed successfully

we could clearly see in the window where ganache is running the details of deployment as the transaction hash and and block number as shown in the following screenshot .

Contract Deployment Tracking

we managed successfully to deploy the contract, Lets try now interacting with it to see if its working as intended or not ,In this concept truffle offer us a tool for interacting with the deployed contract , Its the truffle console.

Interacting with the deployed contract
To summarize, we deployed our contract successfully lets now interact with it , by running this command:
truffle console
you will enter in a JavaScript environment to interact with the deployed contract .here’s a code example to interact with the contract.

const Skaltuchet = await artifacts.require("Skaltuchet").deployed()

const contractAddress = await Skaltuchet.address
const contractBalance = await Skaltuchet.balanceOf(contractAddress)
console.log("Contract SKT balance:", web3.utils.fromWei(contractBalance.toString(), 'ether'), "SKT")

const accounts = await web3.eth.getAccounts()
const ownerAddress = accounts[0]
const ownerBalance = await Skaltuchet.balanceOf(ownerAddress)
console.log("Owner SKT balance:", web3.utils.fromWei(ownerBalance.toString(), 'ether'), "SKT")

const ownerEthBalance = await web3.eth.getBalance(ownerAddress)
console.log("Owner ETH balance:", web3.utils.fromWei(ownerEthBalance.toString(), 'ether'), "ETH")
Enter fullscreen mode Exit fullscreen mode

You can save this code to file.js, then navigate to its directory and run: node file.js. Alternatively, you can execute it command by command in the Truffle console, as shown in the following screenshot.

Interacting with the deployed Contract

lets try performing a buy transaction now from another account from accounts

and then check its balance, Here’s a js code example :

const buyer = accounts[1]// choose acount one as the buyer 
let buyerBalance = await Skaltuchet.balanceOf(buyer)
console.log("Buyer's initial SKT balance:", web3.utils.fromWei(buyerBalance.toString(), 'ether'), "SKT")

//Check the contract's initial SKT balance:
const ethAmount = web3.utils.toWei('1', 'ether')
const result = await Skaltuchet.buyTokens({ from: buyer, value: ethAmount })
console.log("Buy transaction hash:", result.tx)

//perform a buy transaction
const ethAmount = web3.utils.toWei('1', 'ether')
const result = await Skaltuchet.buyTokens({ from: buyer, value: ethAmount })
console.log("Buy transaction hash:", result.tx)

//check buyer's new balance 
buyerBalance = await Skaltuchet.balanceOf(buyer)
console.log("Buyer's new SKT balance:", web3.utils.fromWei(buyerBalance.toString(), 'ether'), "SKT")
Enter fullscreen mode Exit fullscreen mode

As shown in the screenshot the buy transaction was succesfull and account1 got his ‘SKT’ tokens.

Successful Buy transaction

SUMMARY:

In the previous part we handled interacting with the deployed smart contract and testing it via Truffle console, You can see Truffle with Ganache is quite a good tool for creating deploying, and testing smart contracts, But it has some challenging steps when you want to use deployed contracts on the real Ethereum network (mainnet 1) like using Uniswap router, for example, you would probably need to fork the deployed contract on the ganache client and use an infura provider.

Top comments (0)