Term Project 1 (Option 1) – KYC DApp
Truffle • Ganache • Web3 Front‑End • Console

Deploy & Interact with a KYC DApp

Students will compile, deploy, and interact with a KYC smart contract, then build a tiny front‑end using Web3.

Only use your local Ganache network (development). Do not deploy on public networks.

Instructional Videos

  • 🎥 Video 1: Smart Contract (KYC.sol)
  • 🎥 Video 2: Truffle Console (optional)
  • 🎥 Video 3: Web3 Front‑End (part 1)
  • 🎥 Video 4: Web3 Front‑End (part 2)

(Instructor: paste links here.)

1) Set Up Your Environment

  1. Install Node.js (LTS) → verify:
    node -v
    npm -v
  2. Install Truffle (global) → verify:
    npm install -g truffle
    truffle version
  3. Install Ganache (GUI or CLI) and start a fresh workspace (RPC http://127.0.0.1:7545).

2) Initialize a Truffle Project

mkdir my-project
cd my-project
truffle init

Set truffle-config.js for Ganache and Solidity 0.8.x:

module.exports = {
  networks: {
    development: { host: "127.0.0.1", port: 7545, network_id: "*" },
  },
  compilers: { solc: { version: "0.8.20" } }
};

3) Write Your KYC Smart Contract

Create contracts/KYC.sol and paste:

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

contract KYC {
    struct Bank { string name; address bankAddress; }
    struct Customer { string name; address customerAddress; bool isVerified; }

    mapping(address => Bank) public banks;
    mapping(address => Customer) public customers;

    address[] public bankList;
    address[] public customerList;

    event BankAdded(string name, address bankAddress);
    event CustomerAdded(string name, address customerAddress);
    event CustomerVerified(address customerAddress);

    function addNewBank(string memory name, address bankAddress) public {
        require(bankAddress != address(0), "Invalid address");
        require(bytes(name).length > 0, "Bank name required");
        banks[bankAddress] = Bank(name, bankAddress);
        bankList.push(bankAddress);
        emit BankAdded(name, bankAddress);
    }

    function addNewCustomer(string memory name, address customerAddress) public {
        require(customerAddress != address(0), "Invalid address");
        require(bytes(name).length > 0, "Customer name required");
        customers[customerAddress] = Customer(name, customerAddress, false);
        customerList.push(customerAddress);
        emit CustomerAdded(name, customerAddress);
    }

    function verifyCustomer(address customerAddress) public {
        require(customerAddress != address(0), "Invalid address");
        require(customers[customerAddress].customerAddress != address(0), "Customer missing");
        customers[customerAddress].isVerified = true;
        emit CustomerVerified(customerAddress);
    }

    function getBanks() public view returns (Bank[] memory) {
        Bank[] memory allBanks = new Bank[](bankList.length);
        for (uint i = 0; i < bankList.length; i++) {
            allBanks[i] = banks[bankList[i]];
        }
        return allBanks;
    }

    function getCustomers() public view returns (Customer[] memory) {
        Customer[] memory allCustomers = new Customer[](customerList.length);
        for (uint i = 0; i < customerList.length; i++) {
            allCustomers[i] = customers[customerList[i]];
        }
        return allCustomers;
    }
}

Compile:

truffle compile

4) Migration Script & Deploy

Create migrations/2_deploy_contracts.js:

const KYC = artifacts.require("KYC");
module.exports = function(deployer){ deployer.deploy(KYC); };

Start Ganache, then deploy:

truffle migrate --network development
Copy the deployed contract address. The ABI + address also live in build/contracts/KYC.json.

5) Interact via Truffle Console (optional but helpful)

truffle console --network development

const KYC = artifacts.require("KYC");
const kyc = await KYC.deployed();
const accounts = await web3.eth.getAccounts();

// Add banks
await kyc.addNewBank("Bank1", accounts[1]);
await kyc.addNewBank("Bank2", accounts[2]);

// Add customers
await kyc.addNewCustomer("Customer1", accounts[3]);
await kyc.addNewCustomer("Customer2", accounts[4]);

// Verify
await kyc.verifyCustomer(accounts[3]);

// Read back
const banks = await kyc.getBanks();
const customers = await kyc.getCustomers();
console.log(banks);
console.log(customers);

6) Minimal Front‑End (Vanilla Web3, no React)

We’ll serve a simple HTML page with lite-server and auto‑load ABI + address from Truffle’s build JSON.

6.1 Create front‑end folder & install tools

mkdir frontend
cd frontend
npm init -y
npm install web3 lite-server --save-dev

6.2 Add package.json script

{
  "name": "kyc-dapp",
  "version": "1.0.0",
  "private": true,
  "scripts": { "start": "lite-server" },
  "devDependencies": { "lite-server": "^2.6.1" },
  "dependencies": { "web3": "^1.10.4" }
}

6.3 Add bs-config.json (so the browser can fetch ../build/)

{ "server": { "baseDir": ["./", "../"] } }

6.4 index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>KYC DApp</title>
  <style> body{font-family:Arial,sans-serif;background:#f4f4f4;padding:20px} .card{max-width:720px;margin:auto;background:#fff;padding:20px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,.1)} input,button{padding:10px;margin:6px 0;border-radius:6px;border:1px solid #ccc} button{background:#2563eb;color:#fff;border:none;cursor:pointer} button:hover{opacity:.9} table{width:100%;border-collapse:collapse;margin-top:10px} td,th{border-bottom:1px solid #e5e7eb;padding:8px;text-align:left} .muted{color:#6b7280} </style>
</head>
<body>
  <div class="card">
    <h1>KYC DApp</h1>
    <button id="connect">Connect MetaMask</button>
    <div id="who" class="muted"></div>

    <h2>Add New Bank</h2>
    <input id="bankName" placeholder="Bank Name"/>
    <input id="bankAddr" placeholder="Bank Address 0x..."/>
    <button id="addBank">Add Bank</button>

    <h2>Add New Customer</h2>
    <input id="custName" placeholder="Customer Name"/>
    <input id="custAddr" placeholder="Customer Address 0x..."/>
    <button id="addCust">Add Customer</button>

    <h2>Verify Customer</h2>
    <input id="verifyAddr" placeholder="Customer Address 0x..."/>
    <button id="verifyBtn">Verify</button>

    <p id="status" class="muted"></p>

    <h3>Banks</h3>
    <table id="banksTbl"><thead><tr><th>Name</th><th>Address</th></tr></thead><tbody></tbody></table>
    <h3>Customers</h3>
    <table id="custTbl"><thead><tr><th>Name</th><th>Address</th><th>Verified</th></tr></thead><tbody></tbody></table>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/web3@1.10.4/dist/web3.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

6.5 app.js

let web3, accounts, kyc;
const ABI_URL = '../build/contracts/KYC.json';

const el = id => document.getElementById(id);
const setStatus = m => el('status').textContent = m;

async function connect(){
  if(!window.ethereum){ alert('Please install MetaMask'); return; }
  web3 = new Web3(window.ethereum);
  accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
  el('who').textContent = `Connected as ${accounts[0]}`;
  await loadContract();
}

async function loadContract(){
  const res = await fetch(ABI_URL); const json = await res.json();
  const abi = json.abi;
  let addr = '';
  const nets = json.networks || {}; const ids = Object.keys(nets);
  if(ids.length){ addr = nets[ids[ids.length-1]].address; }
  if(!addr){ setStatus('❗ Set contract address in app.js or redeploy.'); return; }
  kyc = new web3.eth.Contract(abi, addr);
  setStatus('Contract loaded: ' + addr);
  await refreshTables();
}

async function refreshTables(){
  const banks = await kyc.methods.getBanks().call();
  const custs = await kyc.methods.getCustomers().call();
  el('banksTbl').querySelector('tbody').innerHTML = banks.map(b=>`${b.name}${b.bankAddress}`).join('');
  el('custTbl').querySelector('tbody').innerHTML = custs.map(c=>`${c.name}${c.customerAddress}${c.isVerified}`).join('');
}

async function addBank(){
  const name = el('bankName').value.trim(); const addr = el('bankAddr').value.trim();
  if(!name || !web3.utils.isAddress(addr)) return setStatus('Enter bank name and valid address');
  try{
    const gas = await kyc.methods.addNewBank(name, addr).estimateGas({ from: accounts[0] });
    await kyc.methods.addNewBank(name, addr).send({ from: accounts[0], gas });
    setStatus('✅ Bank added'); await refreshTables();
  }catch(e){ setStatus('Error: ' + e.message); }
}

async function addCustomer(){
  const name = el('custName').value.trim(); const addr = el('custAddr').value.trim();
  if(!name || !web3.utils.isAddress(addr)) return setStatus('Enter customer name and valid address');
  try{
    const gas = await kyc.methods.addNewCustomer(name, addr).estimateGas({ from: accounts[0] });
    await kyc.methods.addNewCustomer(name, addr).send({ from: accounts[0], gas });
    setStatus('✅ Customer added'); await refreshTables();
  }catch(e){ setStatus('Error: ' + e.message); }
}

async function verify(){
  const addr = el('verifyAddr').value.trim();
  if(!web3.utils.isAddress(addr)) return setStatus('Enter a valid address');
  try{
    const gas = await kyc.methods.verifyCustomer(addr).estimateGas({ from: accounts[0] });
    await kyc.methods.verifyCustomer(addr).send({ from: accounts[0], gas });
    setStatus('✅ Customer verified'); await refreshTables();
  }catch(e){ setStatus('Error: ' + e.message); }
}

el('connect').addEventListener('click', connect);
el('addBank').addEventListener('click', addBank);
el('addCust').addEventListener('click', addCustomer);
el('verifyBtn').addEventListener('click', verify);
Prefer React instead?

Use Create React App and port the same three calls (addNewBank, addNewCustomer, verifyCustomer) into event handlers. If you see a Babel warning, add @babel/plugin-proposal-private-property-in-object as a dev dependency.

7) Run & Test

  1. Start Ganache (GUI quickstart, RPC 7545).
  2. In my-project deploy the contract: truffle migrate --network development.
  3. In my-project/frontend: npm start → open http://localhost:3000.
  4. MetaMask → switch to the Ganache network (host 127.0.0.1, port 7545, chain ID 1337/5777).
  5. Connect, add a bank, add a customer, verify customer → watch updates in the tables.
Sanity check: Truffle console should show the same data as your web UI.

Troubleshooting (Quick Fixes)

Advanced Version (Admin + Permissions)

The appendix in your spec mentions an admin (kycadmin) and bank permissions (canAddCustomers/canPerformKYC). That’s a stricter model. For class, start with the beginner contract above. As extra credit, evolve it by adding an owner, modifiers, and permission bits on the Bank struct, then gate the functions accordingly.

Submission Checklist

Name your PDF/zip: TP1_KYC_FirstnameLastname