Blockchain 14 min read

Build a Complete Ethereum Pet Shop DApp in Three Simple Steps

This step‑by‑step guide shows how to set up a local development environment, write and deploy Solidity smart contracts with Truffle, configure MetaMask, and create a JavaScript front‑end to launch a functional pet‑shop DApp on an Ethereum private chain.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
Build a Complete Ethereum Pet Shop DApp in Three Simple Steps

Prepare Development Environment

Install the required tools on macOS:

Node.js v9.11.1 and npm v5.6.0

Truffle v4.1.5 (Solidity compiler 0.4.21)

Ganache CLI or GUI v1.1.0 (private Ethereum network)

If Homebrew reports a linking error for an old Node version, run the following commands to clean and reinstall:

brew uninstall node --force
brew uninstall npm --force
brew link node
brew install node

Install Truffle globally: npm install -g truffle Download and start Ganache (it will listen on http://127.0.0.1:7545).

Create Project with Truffle Box

Generate a new directory and unbox the official pet‑shop template:

mkdir pet-shop-tutorial
cd pet-shop-tutorial
truffle unbox pet-shop

The scaffold contains the following important folders: contracts/ – Solidity source files (e.g., Adoption.sol) migrations/ – Deployment scripts test/ – JavaScript/Solidity test files truffle.js – Truffle configuration

Write the Smart Contract

Create contracts/Adoption.sol with this source:

pragma solidity ^0.4.17;

contract Adoption {
    address[16] public adopters;

    // Adopt a pet
    function adopt(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);
        adopters[petId] = msg.sender;
        return petId;
    }

    // Retrieve the list of adopters
    function getAdopters() public view returns (address[16]) {
        return adopters;
    }
}

Compile and Deploy the Contract

Compile the contracts with Truffle: truffle compile Ignore the visibility warnings that appear in Migrations.sol (they do not affect functionality).

Create a migration script migrations/2_deploy_contracts.js:

var Adoption = artifacts.require("Adoption");

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

Deploy to the Ganache network: truffle migrate The console output shows two migration transactions and the deployed contract address (e.g., 0x104ba492f5d8f4e0df6971ae09ca0c9b496ff15b).

Test the Smart Contract

Add a Solidity test file test/TestAdoption.sol:

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {
    Adoption adoption = Adoption(DeployedAddresses.Adoption());

    function testUserCanAdoptPet() public {
        uint returnedId = adoption.adopt(8);
        uint expected = 8;
        Assert.equal(returnedId, expected, "Adoption of Pet ID 8 should be recorded.");
    }

    function testGetAdopterAddressByPetId() public {
        address expected = this;
        address adopter = adoption.adopters(8);
        Assert.equal(adopter, expected, "Owner of pet id 8 should be recorded.");
    }

    function testGetAdopterAddressByPetIdInArray() public {
        address expected = this;
        address[16] memory adopters = adoption.getAdopters();
        Assert.equal(adopters[8], expected, "Owner of pet id 8 should be recorded.");
    }
}

Run the tests: truffle test All three tests pass, confirming that adoption logic and data retrieval work as expected.

Front‑End UI

The DApp’s front‑end is a JavaScript application located in src/js/app.js. The key functions are: init() – Loads pet data from pets.json and calls initWeb3(). initWeb3() – Detects an injected Web3 provider (MetaMask) or falls back to Ganache’s HTTP provider. initContract() – Loads the compiled Adoption.json artifact, creates a TruffleContract instance, and sets its provider. bindEvents() – Registers a click handler for the “Adopt” button. handleAdopt(event) – Sends a transaction to adopt(petId) using the selected account. markAdopted() – Queries the contract for adopted pets and updates the UI.

Below is a condensed version of app.js:

App = {
    web3Provider: null,
    contracts: {},

    init: function() {
        $.getJSON('../pets.json', function(data) {
            var petsRow = $('#petsRow');
            var petTemplate = $('#petTemplate');
            for (i = 0; i < data.length; i++) {
                petTemplate.find('.panel-title').text(data[i].name);
                petTemplate.find('img').attr('src', data[i].picture);
                petTemplate.find('.pet-breed').text(data[i].breed);
                petTemplate.find('.pet-age').text(data[i].age);
                petTemplate.find('.pet-location').text(data[i].location);
                petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
                petsRow.append(petTemplate.html());
            }
        });
        return App.initWeb3();
    },

    initWeb3: function() {
        if (typeof web3 !== 'undefined') {
            App.web3Provider = web3.currentProvider;
        } else {
            App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
        }
        web3 = new Web3(App.web3Provider);
        return App.initContract();
    },

    initContract: function() {
        $.getJSON('Adoption.json', function(data) {
            var AdoptionArtifact = data;
            App.contracts.Adoption = TruffleContract(AdoptionArtifact);
            App.contracts.Adoption.setProvider(App.web3Provider);
            return App.markAdopted();
        });
        return App.bindEvents();
    },

    bindEvents: function() {
        $(document).on('click', '.btn-adopt', App.handleAdopt);
    },

    markAdopted: function() {
        App.contracts.Adoption.deployed().then(function(instance) {
            return instance.getAdopters.call();
        }).then(function(adopters) {
            for (i = 0; i < adopters.length; i++) {
                if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
                    $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
                }
            }
        }).catch(function(err) {
            console.log(err.message);
        });
    },

    handleAdopt: function(event) {
        event.preventDefault();
        var petId = parseInt($(event.target).data('id'));
        web3.eth.getAccounts(function(error, accounts) {
            if (error) { console.log(error); }
            var account = accounts[0];
            App.contracts.Adoption.deployed().then(function(instance) {
                return instance.adopt(petId, {from: account});
            }).then(function() {
                return App.markAdopted();
            }).catch(function(err) {
                console.log(err.message);
            });
        });
    }
};

$(function() {
    $(window).load(function() { App.init(); });
});

Configure MetaMask

Install the MetaMask browser extension and import the 12‑word mnemonic displayed by Ganache (found in the Ganache UI under “Accounts”). Set a password, then add a custom RPC network with URL http://127.0.0.1:7545. This connects MetaMask to the local private chain, allowing the DApp to sign transactions.

Run the Lite‑Server

The project includes a lite-server configuration (defined in bs-config.json and package.json). Start the development server with: npm run dev The server serves files from ./src and ./build/contracts at http://localhost:3000. Opening this URL loads the pet‑shop UI. When a user clicks “Adopt”, MetaMask prompts for transaction confirmation; after approval the UI updates to reflect the adopted pet.

Reference

Original Truffle pet‑shop tutorial: http://truffleframework.com/tutorials/pet-shop.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Blockchainsmart contractsDAppEthereumTruffleMetaMask
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.