Ethereum Development - Build Pet Shop
Truffle’s pet shop is a very good example to understand Ethereum development. Following official provided demo and tutorial, can completely experience entire development process.
Ganache
For environment needs, and quickly set up a private chain environment locally. truffle launched a visual private chain client: Ganache Download Address After downloading and running, you’ll see an interface like this: 
Initialize Your Project
First we create a new directory, and initialize the project
$ mkdir pet-shop
$ cd pet-shop
$ truffle unbox pet-shop
Project Directory Structure
Here only lists important directories and files
├── bs-config.json
├── contracts //Contract directory
│ └── Migrations.sol //Contract file
├── migrations // Deployment scripts
│ └── 1_initial_migration.js
├── package-lock.json
├── package.json
├── src // Front-end code directory
├── test // Test code directory
└── truffle.js // truffle configuration file
Write Smart Contract
In contracts/ directory, create an Adoption.sol file File content:
pragma solidity ^0.4.17;
contract Adoption {
address[16] public adopters; // Declare an address variable to save adopter addresses
// Adopt pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15); // Ensure pet id is correct, between 0 and 15,
// If doesn't meet condition will rollback
//msg.sender is address of person calling this function
adopters[petId] = msg.sender; // Save caller address
return petId;
}
// Return adopters
function getAdopters() public view returns (address[16]) {
return adopters;
}
}
Compile Your Smart Contract
$ truffle compile
//Output
Compiling ./contracts/Adoption.sol...
Writing artifacts to ./build/contracts
This way you’ll find your project has a build folder, Inside stores json files of smart contract just written compiled
Deploy Your Smart Contract
Your smart contract is written, you can temporarily understand as your backend code is written. Need to deploy for front-end to call.
In your migrations/ directory create a deployment script: 2_deploy_contracts.js
var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};
Open Ganache just downloaded, Ganache will start a private chain. Our smart contract will be deployed on this private chain.
Confirm project root directory’s truffle.js
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545, //Ensure port address, whether consistent with private chain address, if not consistent please keep consistent
network_id: "*" // Match any network id
}
}
};
Execute following command, deploy contract:
truffle migrate
# Output
Using network 'develop'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying Adoption...
... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4
Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...
Reopen Ganache, you’ll find blockchain state has changed, added 4 blocks. OK, deployment complete 
Front-end and Smart Contract Interaction
Alright, smart contract we’ve deployed. Next we’ll start our javascript part Open src/js/app.js
Initialize web3
web3 is a library for front-end to communicate with Ethereum, all operations calling smart contracts, we implement based on web3.
We find initWeb3 method in app.js, add following code:
initWeb3: function() {
// Check if there's global web3 object, generally if installed MetaMask wallet will have global web3 object
if (typeof web3 !== 'undefined') {
// If exists, use directly
App.web3Provider = web3.currentProvider;
} else {
// If no global web3 object, directly initialize one locally
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
web3 = new Web3(App.web3Provider);
return App.initContract();
}
Instantiate Smart Contract
Find initContract method part, add following code:
initContract: function() {
//Load our previously written smart contract
$.getJSON('Adoption.json', function(data) {
// Use Adoption.json data to create an interactive TruffleContract contract instance.
var AdoptionArtifact = data;
//TruffleContract is this project's global method, uses truffle-contract library
//If in other projects, you can directly npm i truffle-contract --save to install this library
//Used to instantiate contract
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
// Set the provider for our contract
App.contracts.Adoption.setProvider(App.web3Provider);
// Use our contract to retrieve and mark the adopted pets
return App.markAdopted();
});
return App.bindEvents();
}
Adoption Part
handleAdopt method part, add following code:
handleAdopt: function(event) {
event.preventDefault();
//Get pet id from dom
var petId = parseInt($(event.target).data('id'));
var adoptionInstance;
// Get user account
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
//Get user, if installed wallet will only return array of length 1, if no wallet installed, will return all user addresses
var account = accounts[0];
//Call smart contract
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
// Send transaction to adopt pet
return adoptionInstance.adopt(petId, {from: account});
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
}
Find markAdopted method, add following code:
markAdopted: function(adopters, account) {
var adoptionInstance;
//Call contract method
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
// Call contract's getAdopters(), using call to read information doesn't consume gas
return adoptionInstance.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);
});
}
Start Service
Alright, all code we’ve filled in, start our project, start adopting pets you like
$ npm run dev