Skip to content

Truffle and Ganache: Local Blockchain Development Suite

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn Truffle and Ganache for local Ethereum development including project setup, migrations, testing, and contract deployment. Why it matters: Truffle and Ganache provide a complete Ethereum development suite with visual blockchain inspection, interactive debugging, and automated contract testing since 2016. By the end, you'll create a full Truffle project from scratch.

Truffle is a development framework for Ethereum that handles compilation, deployment, testing, and asset management, while Ganache provides a personal local blockchain for development and testing.

Setting Up Truffle and Ganache

Install Truffle globally and Ganache (CLI or UI) to start building.

npm install -g truffle
npm install -g ganache-cli

# Verify installation
truffle version
ganache-cli --version

Expected output:

Truffle v5.11.5 (core: 5.11.5)
Ganache v7.9.0 (@ganache/cli: 7.9.0)
Solidity v0.8.21 (solc-js)
Node v18.17.0
Web3.js v1.10.0

Now create a Truffle project:

mkdir my-truffle-project && cd my-truffle-project
truffle init

Expected output:

Starting init...
================

> Copying project files to /path/to/my-truffle-project
  - truffle-config.js
  - contracts/
  - migrations/
  - test/

Init successful, sweet!
flowchart LR
  A[truffle init] --> B[contracts/]
  A --> C[migrations/]
  A --> D[test/]
  A --> E[truffle-config.js]
  B --> F[Write Solidity Contract]
  F --> G[truffle compile]
  G --> H[Write Migration]
  H --> I[Start Ganache]
  I --> J[truffle migrate]
  J --> K[Write Tests]
  K --> L[truffle test]

Writing Contracts and Migrations

Create a SimpleStorage contract and a migration script to deploy it.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract SimpleStorage {
    uint256 private storedData;
    
    event DataStored(uint256 data);
    
    function set(uint256 x) external {
        storedData = x;
        emit DataStored(x);
    }
    
    function get() external view returns (uint256) {
        return storedData;
    }
}

Create migration file migrations/2_deploy_contracts.js:

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

module.exports = function (deployer) {
  deployer.deploy(SimpleStorage);
};

Compile and migrate:

truffle compile
truffle migrate --network development

Expected output from compile:

Compiling your contracts...
===========================
> Compiling ./contracts/SimpleStorage.sol
> Artifacts written to /path/to/build/contracts
> Compiled successfully using:
   - solc: 0.8.21+commit.d5de6c1e.Emscripten.clang

Expected output from migrate:

Starting migration...
=====================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 6721975

2_deploy_contracts.js
=====================
   Deploying 'SimpleStorage'
   ------------------------
   > transaction hash:    0x7a3f...
   > contract address:    0x8Cda...
   > block number:        1
   > block timestamp:     1700000000
   > account:             0x90F8...
   > balance:             99.9994583
   > gas used:            254169
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00508338 ETH

Testing with Truffle

Truffle tests use Mocha and Chai with special artifacts for contract interaction.

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

contract("SimpleStorage", (accounts) => {
  let instance;
  
  before(async () => {
    instance = await SimpleStorage.deployed();
  });
  
  it("should store a value", async () => {
    await instance.set(42, { from: accounts[0] });
    const storedData = await instance.get();
    assert.equal(storedData.toNumber(), 42, "Value was not stored correctly");
  });
  
  it("should emit DataStored event", async () => {
    const result = await instance.set(100);
    const event = result.logs[0];
    assert.equal(event.event, "DataStored");
    assert.equal(event.args.data.toNumber(), 100);
  });
  
  it("should update existing value", async () => {
    await instance.set(200);
    const value = await instance.get();
    assert.equal(value.toNumber(), 200);
  });
  
  it("should handle multiple accounts", async () => {
    await instance.set(300, { from: accounts[1] });
    const value = await instance.get();
    assert.equal(value.toNumber(), 300);
  });
});

Run tests:

truffle test

Expected output:

Contract: SimpleStorage
  ✔ should store a value (48ms)
  ✔ should emit DataStored event (72ms)
  ✔ should update existing value (35ms)
  ✔ should handle multiple accounts (41ms)

4 passing (452ms)

Using Ganache for Visual Debugging

Ganache UI provides a graphical interface to inspect blocks, transactions, accounts, and logs. Start Ganache CLI:

ganache-cli --port 7545 --networkId 5777 --deterministic

Expected output:

ganache-cli v7.9.0 (@ganache/cli: 7.9.0)
Available Accounts
==================
(0) 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 (1000 ETH)
(1) 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 (1000 ETH)
(2) 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b (1000 ETH)

Private Keys
==================
(0) 0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d

Gas Price
==================
20 gwei

Gas Limit
==================
6721975

Listening on 127.0.0.1:7545

Configure truffle-config.js:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "5777",
    },
  },
  compilers: {
    solc: {
      version: "0.8.19",
    },
  },
};
flowchart TD
  A[Ganache CLI/UI] --> B[Local Blockchain]
  B --> C[Accounts
10 pre-funded] B --> D[Blocks
Real-time mining] B --> E[Transactions
Detailed logs] B --> F[Gas Reports] C --> G[Test & Deploy] G --> H[truffle migrate] G --> I[truffle test] G --> J[truffle console]

Interacting with Truffle Console

Truffle Console provides an interactive environment to call contract functions directly.

truffle console --network development

Then interact:

truffle(development)> let instance = await SimpleStorage.deployed()
truffle(development)> await instance.set(42)
truffle(development)> let value = await instance.get()
truffle(development)> value.toNumber()
42

truffle(development)> let accounts = await web3.eth.getAccounts()
truffle(development)> accounts
[
  '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1',
  '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0',
  '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b'
]

truffle(development)> let balance = await web3.eth.getBalance(accounts[0])
truffle(development)> web3.utils.fromWei(balance, 'ether')
'999.9945'

Expected output: Interactive session shows the stored value, available accounts, and account balances.

Deploying to Testnets

Configure testnet or mainnet in truffle-config.js for production deployment.

const HDWalletProvider = require("@truffle/hdwallet-provider");
const fs = require("fs");
const mnemonic = fs.readFileSync(".secret").toString().trim();

module.exports = {
  networks: {
    sepolia: {
      provider: () => new HDWalletProvider(
        mnemonic,
        `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`
      ),
      network_id: 11155111,
      gas: 5500000,
      confirmations: 2,
      timeoutBlocks: 200,
    },
  },
};

Deploy to Sepolia:

truffle migrate --network sepolia

Expected output:

Starting migration...
=====================
> Network name:    'sepolia'
> Network id:      11155111
> Block gas limit: 30000000

2_deploy_contracts.js
=====================
   Deploying 'SimpleStorage'
   ------------------------
   > transaction hash:    0xa1b2...
   > Blocks: 2            Seconds: 24
   > contract address:    0xE3Fd...

Common Errors and Misunderstandings

1. Ganache Not Running

Truffle commands fail if Ganache is not running. Start Ganache before any migrate or test command.

2. Wrong Network ID

truffle-config.js network ID must match Ganache's network ID (default 5777). Mismatch causes connection failures.

3. Out of Gas

Complex contract deployments may exceed the default gas limit. Increase gas in config or optimize contract code.

4. Using Global Truffle with Project Dependencies

Global Truffle may conflict with project-local versions. Use npx truffle or install as a dev dependency.

5. Old Migration State

Running truffle migrate again without --reset skips already-deployed contracts. Use --reset to re-deploy all.

Practice Questions

  1. What does truffle init generate? It creates contracts/, migrations/, test/ directories and a truffle-config.js configuration file.

  2. How do migrations work in Truffle? Migrations are JavaScript files that export a function receiving a deployer object. They deploy contracts in order. Truffle tracks which migrations have run using a Migrations contract.

  3. What is the difference between Ganache CLI and Ganache UI? Ganache CLI is a command-line tool for automated environments. Ganache UI provides a graphical interface with block explorer, transaction log, and account management.

  4. How do you reset a migration in Truffle? Use truffle migrate --reset to re-run all migrations from scratch, redeploying every contract.

  5. What network configuration is needed for testnets? You need an HDWalletProvider with a mnemonic or private key, an RPC URL (Infura/Alchemy), and the correct network ID for the testnet.

Challenge

Build a multi-contract project with Truffle that includes three interdependent contracts, write migrations that deploy them in the correct order passing constructor arguments, write comprehensive tests covering all functions, and deploy to the Sepolia testnet.

Real-World Task

Create a Truffle project for an NFT marketplace contract using Truffle and Ganache. Include migrations, unit tests for minting and trading, and a script to seed the local network with test NFTs. Use Ganache's deterministic accounts for reproducible testing.

Frequently Asked Questions

What is the advantage of Truffle over Hardhat?

Truffle provides a structured project layout, a robust migration system, and Ganache's visual debugging. It has been established longer and has extensive documentation. However, Hardhat has largely surpassed Truffle in adoption due to better debugging and performance.

Can I use Truffle with non-Ethereum blockchains?

Truffle works with any EVM-compatible blockchain. You can configure it for BNB Chain, Polygon, Avalanche, and other EVM networks by changing the network configuration and provider.

How does Truffle handle contract dependencies?

Install dependencies via npm (e.g., @openzeppelin/contracts), import them in your Solidity files, and Truffle resolves them automatically during compilation. Use truffle compile to verify.

Next Steps

After mastering Truffle, explore Hardhat for a modern alternative, then learn Web3.js or ethers.js for frontend dApp integration with your deployed contracts.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro