Deploy & Work with an Escrow Contract
Follow the steps exactly. Use Ganache as your local blockchain and Truffle for compile/deploy. Keep screenshots of the key actions and outputs.
Reference material
GitHub: Chapter 1 (Escrow)
Part 1: Setting Up the Development Environment
Step 1: Install Node.js
- Go to the Node.js website and download the LTS version for your OS.
- Install following the instructions for your platform.
- Verify installation:
node -v npm -v
Step 2: Install Truffle
- Install globally:
npm install -g truffle
- Verify:
truffle version
Step 3: Install Ganache
- Download & install from the Ganache website.
- Start Ganache → Quickstart Ethereum to launch a local blockchain.
Step 4: Set Up a New Truffle Project
- Create a project directory and init:
mkdir Escrow cd Escrow truffle init
- Configure Truffle to connect to Ganache. Edit
truffle-config.js
to include:
module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost
port: 7545, // Ganache port
network_id: "*", // Match any network id
},
},
compilers: {
solc: {
version: "0.8.0", // Specify the Solidity compiler version
},
},
};
Part 2: Creating the Escrow Smart Contract
Step 1: Create the Smart Contract
- In
contracts/
, createEscrow.sol
. - Paste the following code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Escrow {
address public buyer;
address public seller;
uint public deposit;
uint public timeToExpiry;
uint public startTime;
// Buyer sets up the escrow contract and pays the deposit
constructor(address _seller, uint _timeToExpiry) payable {
require(msg.value > 0, "Deposit must be greater than zero");
buyer = msg.sender;
seller = _seller;
deposit = msg.value;
timeToExpiry = _timeToExpiry;
startTime = block.timestamp;
}
// Buyer releases deposit to seller
function releaseToSeller() public {
require(msg.sender == buyer, "Only buyer can release funds");
require(!isExpired(), "Escrow is expired");
payable(seller).transfer(deposit);
deposit = 0; // Set deposit to zero to prevent re-entry
}
// Buyer can withdraw deposit if escrow is expired
function withdraw() public {
require(isExpired(), "Escrow is not expired yet");
require(msg.sender == buyer, "Only buyer can withdraw funds");
uint amount = deposit;
deposit = 0; // Set deposit to zero to prevent re-entry
payable(buyer).transfer(amount);
}
// Seller can cancel escrow and return all funds to buyer
function cancel() public {
require(msg.sender == seller, "Only seller can cancel the escrow");
uint amount = deposit;
deposit = 0; // Set deposit to zero to prevent re-entry
payable(buyer).transfer(amount);
}
// Check if the escrow is expired
function isExpired() public view returns (bool) {
return block.timestamp > startTime + timeToExpiry;
}
}
Step 2: Compile
truffle compile
Write a Migration Script & Deploy
Create migrations/2_deploy_contracts.js
const Escrow = artifacts.require("Escrow");
module.exports = function (deployer, network, accounts) {
const sellerAddress = accounts[1]; // Example seller address
const timeToExpiry = 3600; // 1 hour in seconds
deployer.deploy(Escrow, sellerAddress, timeToExpiry, {
value: web3.utils.toWei("1", "ether"), // 1 ETH deposit
from: accounts[0], // Example buyer address
});
};
Deploy
truffle migrate --reset
Expected: Contract address appears in the migration output. Ganache shows the deployment transaction and 1 ETH deducted from buyer (accounts[0]).
Part 3: Interacting with the Escrow Contract
Step 1: Start the Truffle Console
truffle console
Step 2: Get the Deployed Instance & Inspect State
const Escrow = artifacts.require("Escrow");
const escrowInstance = await Escrow.deployed();
const buyer = await escrowInstance.buyer();
const seller = await escrowInstance.seller();
const deposit = await escrowInstance.deposit();
const timeToExpiry = await escrowInstance.timeToExpiry();
const startTime = await escrowInstance.startTime();
console.log("Buyer:", buyer);
console.log("Seller:", seller);
console.log("Deposit (in wei):", deposit.toString());
console.log("Time to Expiry (seconds):", timeToExpiry.toString());
console.log("Start Time (timestamp):", startTime.toString());
Step 3: Call Functions
Use the correct roles. Buyer = accounts[0]
, Seller = accounts[1]
as per migration.
const accounts = await web3.eth.getAccounts();
// Release funds to seller (only before expiry)
await escrowInstance.releaseToSeller({ from: buyer });
// If expired, buyer can withdraw
await escrowInstance.withdraw({ from: buyer });
// Seller can cancel (returns funds to buyer)
await escrowInstance.cancel({ from: seller });
// Check expiry status
const expired = await escrowInstance.isExpired();
console.log("Is the escrow expired?", expired);
Tip: If you need to test expiry quickly, wait a few seconds or redeploy with a smaller
timeToExpiry
(e.g., 10
). Advanced (optional): use Ganache's time travel (evm_increaseTime
, evm_mine
).Monitoring in Ganache
As you interact, watch Ganache: transactions, gas, and balances change. You should see the deployment, and then function calls that move the 1 ETH deposit depending on the path taken.
Submission Checklist
- Screenshot: successful
truffle compile
. - Screenshot:
truffle migrate --reset
with deployed contract address. - Screenshot(s): Truffle console showing initial state (buyer, seller, deposit).
- Screenshot(s): one function path executed (either
releaseToSeller
,withdraw
after expiry, orcancel
). - Screenshot: Ganache UI showing transactions.
Name your file:
HW4_FirstnameLastname.pdf
with screenshots in order.