Web3 Blockchain Development Fundamentals: Smart Contracts, DApps & DeFi
This article is a comprehensive introduction to Web3 blockchain development fundamentals – covering smart contracts, DApps, and DeFi with practical examples.
In a Nutshell
Web3 is the next generation of the internet, built on blockchain technology and enabling decentralized applications (DApps) that operate without central intermediaries.
Core Technical Overview
Web3 is a decentralized internet protocol built on blockchain technology that gives users control over their data and digital identities.
Key Components:
Blockchain Fundamentals
- Distributed Ledger: Decentralized data storage
- Consensus Mechanisms: Proof of Work, Proof of Stake
- Cryptography: Hash functions, Digital signatures
- Smart Contracts: Self-executing contracts
- Gas Fees: Transaction costs
Ethereum Ecosystem
- EVM: Ethereum Virtual Machine
- Solidity: Smart contract programming language
- Web3.js: JavaScript library for Web3
- Hardhat: Development environment
- OpenZeppelin: Secure smart contract libraries
Decentralized Finance (DeFi)
- DEX: Decentralized Exchanges
- Lending: Decentralized lending platforms
- Yield Farming: Liquidity provision
- Staking: Token validation
- Governance: Decentralized decision-making
Key Concepts
- Web3: Decentralized internet based on blockchain
- Blockchain: Decentralized database with cryptographic security
- Smart Contracts: Self-executing contracts on the blockchain
- DApps: Decentralized applications without central authority
- DeFi: Decentralized financial services
- Ethereum: Leading smart contract platform
- Solidity: Programming language for smart contracts
- Gas: Transaction costs on the blockchain
- Modern Development: Blockchain development and architecture patterns
Core Components
- Blockchain Technology: Distributed ledger, consensus mechanisms
- Smart Contracts: Self-executing contracts with business logic
- DApps: Decentralized frontend applications
- DeFi Protocols: Financial services on the blockchain
- Wallet Integration: MetaMask, WalletConnect
- Web3 Libraries: Web3.js, Ethers.js
- Development Tools: Hardhat, Truffle, Remix
- Security: Audits, best practices, vulnerability scanning
Practical Examples
1. Smart Contract with Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
/**
* @title DeFiToken
* @dev ERC20 Token with advanced features for DeFi applications
* @author Web3 Developer
*/
contract DeFiToken is ERC20, ERC20Burnable, ReentrancyGuard, Ownable {
using SafeMath for uint256;
// Events
event TokensMinted(address indexed to, uint256 amount);
event TokensBurned(address indexed from, uint256 amount);
event LiquidityAdded(address indexed provider, uint256 tokenAmount, uint256 ethAmount);
event LiquidityRemoved(address indexed provider, uint256 tokenAmount, uint256 ethAmount);
event RewardClaimed(address indexed claimant, uint256 rewardAmount);
// State variables
uint256 public constant MAX_SUPPLY = 1000000000 * 10**18; // 1 Billion tokens
uint256 public constant INITIAL_SUPPLY = 100000000 * 10**18; // 100 Million tokens
uint256 public constant REWARD_RATE = 1 * 10**16; // 0.01 tokens per block
uint256 public totalLiquidity;
uint256 public totalRewardsDistributed;
uint256 public lastRewardBlock;
uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
mapping(address => uint256) public liquidityProvided;
mapping(address => uint256) public depositTimestamp;
// Staking pool arrays
struct Pool {
uint256 totalStaked;
uint256 rewardRate;
uint256 lockPeriod;
uint256 apr;
bool active;
}
mapping(uint256 => Pool) public pools;
mapping(address => mapping(uint256 => uint256)) public userStakes;
mapping(address => mapping(uint256 => uint256)) public userStakeTimestamp;
uint256 public poolCount;
// Governance
struct Proposal {
uint256 id;
address proposer;
string description;
uint256 voteCount;
uint256 quorum;
uint256 deadline;
bool executed;
mapping(address => bool) hasVoted;
}
mapping(uint256 => Proposal) public proposals;
uint256 public proposalCount;
mapping(address => uint256) public votingPower;
// Modifiers
modifier validAddress(address _addr) {
require(_addr != address(0), "Invalid address");
_;
}
modifier sufficientBalance(address _account, uint256 _amount) {
require(balanceOf(_account) >= _amount, "Insufficient balance");
_;
}
modifier sufficientLiquidity(uint256 _amount) {
require(address(this).balance >= _amount, "Insufficient ETH liquidity");
_;
}
modifier onlyWhenPaused() {
require(!paused(), "Contract is paused");
_;
}
// Constructor
constructor(
string memory _name,
string memory _symbol,
uint256 _initialMint
) ERC20(_name, _symbol) {
require(_initialMint <= INITIAL_SUPPLY, "Initial mint exceeds limit");
_mint(msg.sender, _initialMint);
lastRewardBlock = block.number;
// Create initial staking pool
_createPool(100, 30 days, 10); // 100% APR, 30 day lock
}
/**
* @dev Mint new tokens (owner only)
*/
function mint(address _to, uint256 _amount)
external
onlyOwner
validAddress(_to)
returns (bool)
{
require(totalSupply() + _amount <= MAX_SUPPLY, "Exceeds max supply");
_mint(_to, _amount);
emit TokensMinted(_to, _amount);
return true;
}
/**
* @dev Add liquidity to the pool
*/
function addLiquidity(uint256 _tokenAmount)
external
payable
nonReentrant
sufficientBalance(msg.sender, _tokenAmount)
returns (uint256 liquidityTokens)
{
require(_tokenAmount > 0 && msg.value > 0, "Invalid amounts");
// Calculate liquidity tokens
uint256 ethReserve = address(this).balance - msg.value;
uint256 tokenReserve = totalSupply() - _tokenAmount;
if (ethReserve == 0 || tokenReserve == 0) {
liquidityTokens = _tokenAmount;
} else {
liquidityTokens = min(
(msg.value * tokenReserve) / ethReserve,
_tokenAmount
);
}
require(liquidityTokens > 0, "Insufficient liquidity minted");
// Transfer tokens and ETH
_transfer(msg.sender, address(this), _tokenAmount);
// Update user liquidity
liquidityProvided[msg.sender] += liquidityTokens;
depositTimestamp[msg.sender] = block.timestamp;
// Update total liquidity
totalLiquidity += liquidityTokens;
// Update rewards
_updateReward(msg.sender);
emit LiquidityAdded(msg.sender, _tokenAmount, msg.value);
return liquidityTokens;
}
/**
* @dev Remove liquidity from the pool
*/
function removeLiquidity(uint256 _liquidityAmount)
external
nonReentrant
returns (uint256 ethAmount, uint256 tokenAmount)
{
require(_liquidityAmount > 0, "Invalid amount");
require(liquidityProvided[msg.sender] >= _liquidityAmount, "Insufficient liquidity");
// Calculate share amounts
uint256 totalLiquidityTokens = totalLiquidity;
uint256 ethReserve = address(this).balance;
uint256 tokenReserve = balanceOf(address(this));
ethAmount = (ethReserve * _liquidityAmount) / totalLiquidityTokens;
tokenAmount = (tokenReserve * _liquidityAmount) / totalLiquidityTokens;
require(ethAmount > 0 && tokenAmount > 0, "Insufficient withdraw amount");
// Update user liquidity
liquidityProvided[msg.sender] -= _liquidityAmount;
totalLiquidity -= _liquidityAmount;
// Update rewards
_updateReward(msg.sender);
_claimRewards(msg.sender);
// Transfer ETH and tokens
payable(msg.sender).transfer(ethAmount);
_transfer(address(this), msg.sender, tokenAmount);
emit LiquidityRemoved(msg.sender, tokenAmount, ethAmount);
return (ethAmount, tokenAmount);
}
/**
* @dev Create new staking pool
*/
function createPool(uint256 _apr, uint256 _lockPeriod) external onlyOwner {
_createPool(_apr, _lockPeriod, 0);
}
function _createPool(uint256 _apr, uint256 _lockPeriod, uint256 _rewardRate) internal {
pools[poolCount] = Pool({
totalStaked: 0,
rewardRate: _rewardRate > 0 ? _rewardRate : REWARD_RATE,
lockPeriod: _lockPeriod,
apr: _apr,
active: true
});
poolCount++;
}
/**
* @dev Stake tokens in pool
*/
function stake(uint256 _poolId, uint256 _amount)
external
nonReentrant
sufficientBalance(msg.sender, _amount)
returns (bool)
{
require(_poolId < poolCount, "Invalid pool");
require(pools[_poolId].active, "Pool not active");
require(_amount > 0, "Invalid amount");
// Update rewards before staking
_updatePoolRewards(_poolId);
_updateUserPoolRewards(_poolId, msg.sender);
// Transfer tokens to contract
_transfer(msg.sender, address(this), _amount);
// Update staking
userStakes[msg.sender][_poolId] += _amount;
userStakeTimestamp[msg.sender][_poolId] = block.timestamp;
pools[_poolId].totalStaked += _amount;
// Update voting power
votingPower[msg.sender] += _amount;
return true;
}
/**
* @dev Unstake tokens from pool
*/
function unstake(uint256 _poolId, uint256 _amount)
external
nonReentrant
returns (bool)
{
require(_poolId < poolCount, "Invalid pool");
require(userStakes[msg.sender][_poolId] >= _amount, "Insufficient stake");
Pool storage pool = pools[_poolId];
// Check lock period
uint256 stakeTime = userStakeTimestamp[msg.sender][_poolId];
require(
block.timestamp >= stakeTime + pool.lockPeriod,
"Tokens still locked"
);
// Update rewards before unstaking
_updatePoolRewards(_poolId);
_updateUserPoolRewards(_poolId, msg.sender);
// Update staking
userStakes[msg.sender][_poolId] -= _amount;
pool.totalStaked -= _amount;
// Update voting power
votingPower[msg.sender] -= _amount;
// Transfer tokens back
_transfer(address(this), msg.sender, _amount);
return true;
}
/**
* @dev Claim rewards from pool
*/
function claimRewards(uint256 _poolId) external nonReentrant returns (uint256) {
require(_poolId < poolCount, "Invalid pool");
require(userStakes[msg.sender][_poolId] > 0, "No stake in pool");
// Update rewards
_updatePoolRewards(_poolId);
_updateUserPoolRewards(_poolId, msg.sender);
uint256 reward = rewards[msg.sender];
require(reward > 0, "No rewards to claim");
// Reset rewards
rewards[msg.sender] = 0;
// Mint new tokens as reward
_mint(msg.sender, reward);
emit RewardClaimed(msg.sender, reward);
return reward;
}
/**
* @dev Create governance proposal
*/
function createProposal(string memory _description, uint256 _quorum)
external
returns (uint256)
{
require(votingPower[msg.sender] >= 1000 * 10**18, "Insufficient voting power");
require(bytes(_description).length > 0, "Empty description");
Proposal storage proposal = proposals[proposalCount];
proposal.id = proposalCount;
proposal.proposer = msg.sender;
proposal.description = _description;
proposal.voteCount = 0;
proposal.quorum = _quorum;
proposal.deadline = block.timestamp + 7 days;
proposal.executed = false;
proposalCount++;
return proposalCount - 1;
}
/**
* @dev Vote on proposal
*/
function vote(uint256 _proposalId) external {
require(_proposalId < proposalCount, "Invalid proposal");
require(block.timestamp <= proposals[_proposalId].deadline, "Voting ended");
require(!proposals[_proposalId].hasVoted[msg.sender], "Already voted");
require(votingPower[msg.sender] > 0, "No voting power");
proposals[_proposalId].hasVoted[msg.sender] = true;
proposals[_proposalId].voteCount += votingPower[msg.sender];
}
/**
* @dev Execute proposal
*/
function executeProposal(uint256 _proposalId) external onlyOwner {
require(_proposalId < proposalCount, "Invalid proposal");
require(block.timestamp > proposals[_proposalId].deadline, "Voting not ended");
require(!proposals[_proposalId].executed, "Already executed");
require(
proposals[_proposalId].voteCount >= proposals[_proposalId].quorum,
"Quorum not reached"
);
proposals[_proposalId].executed = true;
}
/**
* @dev Update reward functions
*/
function _updateReward(address _account) internal {
if (_account == address(0)) return;
rewardPerTokenStored = rewardPerToken();
lastRewardBlock = block.number;
rewards[_account] = earned(_account);
userRewardPerTokenPaid[_account] = rewardPerTokenStored;
}
function _updatePoolRewards(uint256 _poolId) internal {
// Implement pool-specific reward logic
// This is a simplified version
}
function _updateUserPoolRewards(uint256 _poolId, address _account) internal {
// Implement user-specific pool rewards
}
/**
* @dev Helper functions
*/
function rewardPerToken() public view returns (uint256) {
if (totalLiquidity == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored.add(
((block.number.sub(lastRewardBlock)).mul(REWARD_RATE)).mul(1e18).div(totalLiquidity)
);
}
function earned(address _account) public view returns (uint256) {
return liquidityProvided[_account]
.mul(rewardPerToken().sub(userRewardPerTokenPaid[_account]))
.div(1e18)
.add(rewards[_account]);
}
function getPoolInfo(uint256 _poolId)
external
view
returns (
uint256 totalStaked,
uint256 rewardRate,
uint256 lockPeriod,
uint256 apr,
bool active
)
{
require(_poolId < poolCount, "Invalid pool");
Pool storage pool = pools[_poolId];
return (pool.totalStaked, pool.rewardRate, pool.lockPeriod, pool.apr, pool.active);
}
function getUserStake(uint256 _poolId, address _user)
external
view
returns (uint256 amount, uint256 timestamp)
{
require(_poolId < poolCount, "Invalid pool");
return (userStakes[_user][_poolId], userStakeTimestamp[_user][_poolId]);
}
function getProposalInfo(uint256 _proposalId)
external
view
returns (
address proposer,
string memory description,
uint256 voteCount,
uint256 quorum,
uint256 deadline,
bool executed,
bool hasVoted
)
{
require(_proposalId < proposalCount, "Invalid proposal");
Proposal storage proposal = proposals[_proposalId];
return (
proposal.proposer,
proposal.description,
proposal.voteCount,
proposal.quorum,
proposal.deadline,
proposal.executed,
proposal.hasVoted[msg.sender]
);
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Emergency functions
*/
function emergencyPause() external onlyOwner {
_pause();
}
function emergencyUnpause() external onlyOwner {
_unpause();
}
function emergencyWithdraw() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
/**
* @dev Fallback for ETH transfers
*/
receive() external payable {
// Accept ETH for liquidity
}
fallback() external payable {
// Accept ETH for liquidity
}
}
2. DApp Frontend with React and Web3.js
// src/components/Web3Provider.jsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import Web3 from 'web3';
import { Contract } from 'web3-eth-contract';
import DeFiTokenABI from '../contracts/DeFiToken.json';
const Web3Context = createContext();
export const useWeb3 = () => {
const context = useContext(Web3Context);
if (!context) {
throw new Error('useWeb3 must be used within a Web3Provider');
}
return context;
};
const Web3Provider = ({ children }) => {
const [web3, setWeb3] = useState(null);
const [account, setAccount] = useState(null);
const [contract, setContract] = useState(null);
const [networkId, setNetworkId] = useState(null);
const [balance, setBalance] = useState('0');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const CONTRACT_ADDRESS = process.env.REACT_APP_CONTRACT_ADDRESS;
const SUPPORTED_NETWORKS = {
1: 'Ethereum Mainnet',
3: 'Ropsten Testnet',
4: 'Rinkeby Testnet',
5: 'Goerli Testnet',
42: 'Kovan Testnet',
137: 'Polygon Mainnet',
80001: 'Polygon Mumbai'
};
// Initialize Web3
useEffect(() => {
const initWeb3 = async () => {
try {
// Check if MetaMask is installed
if (typeof window.ethereum !== 'undefined') {
const web3Instance = new Web3(window.ethereum);
setWeb3(web3Instance);
// Request account access
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
setAccount(accounts[0]);
await setupContract(web3Instance);
await getNetworkInfo(web3Instance);
await updateBalance(web3Instance, accounts[0]);
}
// Setup event listeners
setupEventListeners(web3Instance);
} else {
setError('MetaMask is not installed. Please install MetaMask to use this DApp.');
}
} catch (error) {
console.error('Web3 initialization error:', error);
setError('Failed to initialize Web3. Please check your wallet connection.');
} finally {
setLoading(false);
}
};
initWeb3();
}, []);
const setupEventListeners = (web3Instance) => {
// Account change
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
setAccount(null);
setBalance('0');
} else {
setAccount(accounts[0]);
updateBalance(web3Instance, accounts[0]);
}
});
// Network change
window.ethereum.on('chainChanged', () => {
window.location.reload();
});
// Connect/Disconnect
window.ethereum.on('connect', (connectInfo) => {
console.log('Connected to blockchain:', connectInfo);
});
window.ethereum.on('disconnect', (error) => {
console.error('Disconnected from blockchain:', error);
setAccount(null);
setBalance('0');
});
};
const setupContract = async (web3Instance) => {
try {
const contractInstance = new web3Instance.eth.Contract(
DeFiTokenABI.abi,
CONTRACT_ADDRESS
);
setContract(contractInstance);
} catch (error) {
console.error('Contract setup error:', error);
setError('Failed to setup smart contract.');
}
};
const getNetworkInfo = async (web3Instance) => {
try {
const networkId = await web3Instance.eth.net.getId();
setNetworkId(networkId);
if (!SUPPORTED_NETWORKS[networkId]) {
setError(`Unsupported network. Please switch to a supported network.`);
}
} catch (error) {
console.error('Network info error:', error);
}
};
const updateBalance = async (web3Instance, accountAddress) => {
try {
const balanceWei = await web3Instance.eth.getBalance(accountAddress);
const balanceEth = web3Instance.utils.fromWei(balanceWei, 'ether');
setBalance(balanceEth);
} catch (error) {
console.error('Balance update error:', error);
}
};
const connectWallet = async () => {
try {
if (typeof window.ethereum !== 'undefined') {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
setAccount(accounts[0]);
await updateBalance(web3, accounts[0]);
}
}
} catch (error) {
console.error('Wallet connection error:', error);
setError('Failed to connect wallet.');
}
};
const disconnectWallet = () => {
setAccount(null);
setBalance('0');
};
const switchNetwork = async (networkId) => {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${networkId.toString(16)}` }]
});
} catch (error) {
console.error('Network switch error:', error);
setError('Failed to switch network.');
}
};
const value = {
web3,
account,
contract,
networkId,
balance,
loading,
error,
supportedNetworks: SUPPORTED_NETWORKS,
connectWallet,
disconnectWallet,
switchNetwork,
refreshBalance: () => updateBalance(web3, account)
};
return (
<Web3Context.Provider value={value}>
{children}
</Web3Context.Provider>
);
};
export default Web3Provider;
// src/components/TokenDashboard.jsx
import React, { useState, useEffect } from 'react';
import { useWeb3 } from './Web3Provider';
import { Card, CardHeader, CardContent, CardActions } from './UI/Card';
import Button from './UI/Button';
import Input from './UI/Input';
import Alert from './UI/Alert';
import LoadingSpinner from './UI/LoadingSpinner';
const TokenDashboard = () => {
const { web3, account, contract, balance, refreshBalance } = useWeb3();
const [tokenBalance, setTokenBalance] = useState('0');
const [totalSupply, setTotalSupply] = useState('0');
const [stakingData, setStakingData] = useState([]);
const [liquidityData, setLiquidityData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(null);
// Form states
const [stakeAmount, setStakeAmount] = useState('');
const [liquidityTokenAmount, setLiquidityTokenAmount] = useState('');
const [liquidityEthAmount, setLiquidityEthAmount] = useState('');
// Load token data
useEffect(() => {
if (contract && account) {
loadTokenData();
}
}, [contract, account]);
const loadTokenData = async () => {
try {
setLoading(true);
setError(null);
const [
tokenBalanceWei,
totalSupplyWei,
poolCount,
userLiquidity,
userRewards
] = await Promise.all([
contract.methods.balanceOf(account).call(),
contract.methods.totalSupply().call(),
contract.methods.poolCount().call(),
contract.methods.liquidityProvided(account).call(),
contract.methods.earned(account).call()
]);
setTokenBalance(web3.utils.fromWei(tokenBalanceWei, 'ether'));
setTotalSupply(web3.utils.fromWei(totalSupplyWei, 'ether'));
setLiquidityData({
provided: web3.utils.fromWei(userLiquidity, 'ether'),
rewards: web3.utils.fromWei(userRewards, 'ether')
});
// Load staking data
const pools = [];
for (let i = 0; i < poolCount; i++) {
const [poolInfo, userStake] = await Promise.all([
contract.methods.getPoolInfo(i).call(),
contract.methods.getUserStake(i, account).call()
]);
pools.push({
id: i,
...poolInfo,
userStake: {
amount: web3.utils.fromWei(userStake.amount, 'ether'),
timestamp: userStake.timestamp
}
});
}
setStakingData(pools);
} catch (error) {
console.error('Token data load error:', error);
setError('Failed to load token data.');
} finally {
setLoading(false);
}
};
const handleStake = async (poolId, amount) => {
try {
setLoading(true);
setError(null);
const amountWei = web3.utils.toWei(amount, 'ether');
const tx = await contract.methods.stake(poolId, amountWei).send({
from: account,
gas: '300000'
});
setSuccess(`Successfully staked ${amount} tokens! Transaction: ${tx.transactionHash}`);
setStakeAmount('');
await loadTokenData();
await refreshBalance();
} catch (error) {
console.error('Stake error:', error);
setError('Failed to stake tokens.');
} finally {
setLoading(false);
}
};
const handleUnstake = async (poolId, amount) => {
try {
setLoading(true);
setError(null);
const amountWei = web3.utils.toWei(amount, 'ether');
const tx = await contract.methods.unstake(poolId, amountWei).send({
from: account,
gas: '300000'
});
setSuccess(`Successfully unstaked ${amount} tokens! Transaction: ${tx.transactionHash}`);
await loadTokenData();
await refreshBalance();
} catch (error) {
console.error('Unstake error:', error);
setError('Failed to unstake tokens.');
} finally {
setLoading(false);
}
};
const handleClaimRewards = async (poolId) => {
try {
setLoading(true);
setError(null);
const tx = await contract.methods.claimRewards(poolId).send({
from: account,
gas: '200000'
});
const rewards = await contract.methods.claimRewards(poolId).call({ from: account });
setSuccess(`Successfully claimed ${web3.utils.fromWei(rewards, 'ether')} tokens!`);
await loadTokenData();
} catch (error) {
console.error('Claim rewards error:', error);
setError('Failed to claim rewards.');
} finally {
setLoading(false);
}
};
const handleAddLiquidity = async () => {
try {
setLoading(true);
setError(null);
const tokenAmountWei = web3.utils.toWei(liquidityTokenAmount, 'ether');
const ethAmountWei = web3.utils.toWei(liquidityEthAmount, 'ether');
// Approve token transfer
await contract.methods.approve(CONTRACT_ADDRESS, tokenAmountWei).send({
from: account,
gas: '100000'
});
// Add liquidity
const tx = await contract.methods.addLiquidity(tokenAmountWei).send({
from: account,
value: ethAmountWei,
gas: '300000'
});
setSuccess(`Successfully added liquidity! Transaction: ${tx.transactionHash}`);
setLiquidityTokenAmount('');
setLiquidityEthAmount('');
await loadTokenData();
await refreshBalance();
} catch (error) {
console.error('Add liquidity error:', error);
setError('Failed to add liquidity.');
} finally {
setLoading(false);
}
};
const handleRemoveLiquidity = async (amount) => {
try {
setLoading(true);
setError(null);
const amountWei = web3.utils.toWei(amount, 'ether');
const tx = await contract.methods.removeLiquidity(amountWei).send({
from: account,
gas: '300000'
});
setSuccess(`Successfully removed liquidity! Transaction: ${tx.transactionHash}`);
await loadTokenData();
await refreshBalance();
} catch (error) {
console.error('Remove liquidity error:', error);
setError('Failed to remove liquidity.');
} finally {
setLoading(false);
}
};
if (loading && !tokenBalance) {
return <LoadingSpinner />;
}
return (
<div className="token-dashboard">
<div className="dashboard-header">
<h1>DeFi Token Dashboard</h1>
<div className="account-info">
<p><strong>Account:</strong> {account}</p>
<p><strong>ETH Balance:</strong> {balance} ETH</p>
<p><strong>Token Balance:</strong> {tokenBalance} DEFI</p>
</div>
</div>
{error && <Alert type="error" message={error} />}
{success && <Alert type="success" message={success} />}
{/* Token Info */}
<Card className="token-info-card">
<CardHeader>
<h2>Token Information</h2>
</CardHeader>
<CardContent>
<div className="info-grid">
<div className="info-item">
<label>Total Supply</label>
<span>{totalSupply} DEFI</span>
</div>
<div className="info-item">
<label>Your Balance</label>
<span>{tokenBalance} DEFI</span>
</div>
<div className="info-item">
<label>Liquidity Provided</label>
<span>{liquidityData?.provided || '0'} DEFI</span>
</div>
<div className="info-item">
<label>Pending Rewards</label>
<span>{liquidityData?.rewards || '0'} DEFI</span>
</div>
</div>
</CardContent>
</Card>
{/* Staking Pools */}
<Card className="staking-card">
<CardHeader>
<h2>Staking Pools</h2>
</CardHeader>
<CardContent>
{stakingData.map((pool) => (
<div key={pool.id} className="staking-pool">
<div className="pool-header">
<h3>Pool #{pool.id + 1}</h3>
<span className={`pool-status ${pool.active ? 'active' : 'inactive'}`}>
{pool.active ? 'Active' : 'Inactive'}
</span>
</div>
<div className="pool-info">
<div className="pool-stat">
<label>APR</label>
<span>{pool.apr}%</span>
</div>
<div className="pool-stat">
<label>Lock Period</label>
<span>{pool.lockPeriod / 86400} days</span>
</div>
<div className="pool-stat">
<label>Total Staked</label>
<span>{web3.utils.fromWei(pool.totalStaked, 'ether')} DEFI</span>
</div>
<div className="pool-stat">
<label>Your Stake</label>
<span>{pool.userStake.amount} DEFI</span>
</div>
</div>
<div className="pool-actions">
{pool.active && (
<>
<div className="stake-form">
<Input
type="number"
placeholder="Amount to stake"
value={stakeAmount}
onChange={(e) => setStakeAmount(e.target.value)}
min="0"
step="0.01"
/>
<Button
onClick={() => handleStake(pool.id, stakeAmount)}
disabled={!stakeAmount || parseFloat(stakeAmount) <= 0}
>
Stake
</Button>
</div>
{pool.userStake.amount > '0' && (
<>
<Button
variant="secondary"
onClick={() => handleUnstake(pool.id, pool.userStake.amount)}
>
Unstake All
</Button>
<Button
variant="outline"
onClick={() => handleClaimRewards(pool.id)}
>
Claim Rewards
</Button>
</>
)}
</>
)}
</div>
</div>
))}
</CardContent>
</Card>
{/* Liquidity Management */}
<Card className="liquidity-card">
<CardHeader>
<h2>Liquidity Management</h2>
</CardHeader>
<CardContent>
<div className="liquidity-form">
<h3>Add Liquidity</h3>
<div className="form-row">
<Input
type="number"
placeholder="Token amount"
value={liquidityTokenAmount}
onChange={(e) => setLiquidityTokenAmount(e.target.value)}
min="0"
step="0.01"
/>
<Input
type="number"
placeholder="ETH amount"
value={liquidityEthAmount}
onChange={(e) => setLiquidityEthAmount(e.target.value)}
min="0"
step="0.01"
/>
</div>
<Button
onClick={handleAddLiquidity}
disabled={!liquidityTokenAmount || !liquidityEthAmount || loading}
>
Add Liquidity
</Button>
</div>
{liquidityData?.provided > '0' && (
<div className="remove-liquidity">
<h3>Remove Liquidity</h3>
<div className="form-row">
<Input
type="number"
placeholder="Amount to remove"
min="0"
max={liquidityData.provided}
step="0.01"
/>
<Button
variant="danger"
onClick={() => handleRemoveLiquidity(liquidityData.provided)}
>
Remove All
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>
);
};
export default TokenDashboard;
// src/components/GovernancePanel.jsx
import React, { useState, useEffect } from 'react';
import { useWeb3 } from './Web3Provider';
import { Card, CardHeader, CardContent, CardActions } from './UI/Card';
import Button from './UI/Button';
import Input from './UI/Input';
import Alert from './UI/Alert';
const GovernancePanel = () => {
const { web3, account, contract } = useWeb3();
const [proposals, setProposals] = useState([]);
const [votingPower, setVotingPower] = useState('0');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(null);
// Form states
const [proposalDescription, setProposalDescription] = useState('');
const [proposalQuorum, setProposalQuorum] = useState('');
useEffect(() => {
if (contract && account) {
loadGovernanceData();
}
}, [contract, account]);
const loadGovernanceData = async () => {
try {
setLoading(true);
setError(null);
const [proposalCount, userVotingPower] = await Promise.all([
contract.methods.proposalCount().call(),
contract.methods.votingPower(account).call()
]);
setVotingPower(web3.utils.fromWei(userVotingPower, 'ether'));
// Load proposals
const proposalList = [];
for (let i = 0; i < proposalCount; i++) {
const proposalInfo = await contract.methods.getProposalInfo(i).call();
proposalList.push({
id: i,
proposer: proposalInfo[0],
description: proposalInfo[1],
voteCount: web3.utils.fromWei(proposalInfo[2], 'ether'),
quorum: web3.utils.fromWei(proposalInfo[3], 'ether'),
deadline: new Date(proposalInfo[4] * 1000),
executed: proposalInfo[5],
hasVoted: proposalInfo[6]
});
}
setProposals(proposalList.reverse()); // Newest first
} catch (error) {
console.error('Governance data load error:', error);
setError('Failed to load governance data.');
} finally {
setLoading(false);
}
};
const handleCreateProposal = async () => {
try {
setLoading(true);
setError(null);
const quorumWei = web3.utils.toWei(proposalQuorum, 'ether');
const tx = await contract.methods.createProposal(
proposalDescription,
quorumWei
).send({
from: account,
gas: '200000'
});
setSuccess(`Proposal created successfully! Transaction: ${tx.transactionHash}`);
setProposalDescription('');
setProposalQuorum('');
await loadGovernanceData();
} catch (error) {
console.error('Create proposal error:', error);
setError('Failed to create proposal.');
} finally {
setLoading(false);
}
};
const handleVote = async (proposalId) => {
try {
setLoading(true);
setError(null);
const tx = await contract.methods.vote(proposalId).send({
from: account,
gas: '100000'
});
setSuccess(`Vote cast successfully! Transaction: ${tx.transactionHash}`);
await loadGovernanceData();
} catch (error) {
console.error('Vote error:', error);
setError('Failed to cast vote.');
} finally {
setLoading(false);
}
};
const handleExecuteProposal = async (proposalId) => {
try {
setLoading(true);
setError(null);
const tx = await contract.methods.executeProposal(proposalId).send({
from: account,
gas: '100000'
});
setSuccess(`Proposal executed successfully! Transaction: ${tx.transactionHash}`);
await loadGovernanceData();
} catch (error) {
console.error('Execute proposal error:', error);
setError('Failed to execute proposal.');
} finally {
setLoading(false);
}
};
const canVote = (proposal) => {
return !proposal.hasVoted &&
votingPower > '0' &&
new Date() < proposal.deadline &&
!proposal.executed;
};
const canExecute = (proposal) => {
return !proposal.executed &&
new Date() > proposal.deadline &&
proposal.voteCount >= proposal.quorum;
};
return (
<div className="governance-panel">
<div className="governance-header">
<h1>Governance</h1>
<div className="voting-info">
<p><strong>Your Voting Power:</strong> {votingPower} DEFI</p>
</div>
</div>
{error && <Alert type="error" message={error} />}
{success && <Alert type="success" message={success} />}
{/* Create Proposal */}
<Card className="create-proposal-card">
<CardHeader>
<h2>Create Proposal</h2>
</CardHeader>
<CardContent>
<div className="proposal-form">
<div className="form-group">
<label>Proposal Description</label>
<textarea
value={proposalDescription}
onChange={(e) => setProposalDescription(e.target.value)}
placeholder="Describe your proposal..."
rows={4}
/>
</div>
<div className="form-group">
<label>Quorum (tokens required)</label>
<Input
type="number"
value={proposalQuorum}
onChange={(e) => setProposalQuorum(e.target.value)}
placeholder="1000000"
min="0"
step="0.01"
/>
</div>
</div>
</CardContent>
<CardActions>
<Button
onClick={handleCreateProposal}
disabled={!proposalDescription || !proposalQuorum || votingPower < '1000'}
>
Create Proposal
</Button>
</CardActions>
</Card>
{/* Proposals List */}
<Card className="proposals-card">
<CardHeader>
<h2>Proposals</h2>
</CardHeader>
<CardContent>
{proposals.length === 0 ? (
<p>No proposals yet.</p>
) : (
<div className="proposals-list">
{proposals.map((proposal) => (
<div key={proposal.id} className="proposal-item">
<div className="proposal-header">
<h3>Proposal #{proposal.id + 1}</h3>
<div className="proposal-status">
{proposal.executed ? (
<span className="status-executed">Executed</span>
) : new Date() > proposal.deadline ? (
<span className="status-expired">Expired</span>
) : (
<span className="status-active">Active</span>
)}
</div>
</div>
<div className="proposal-content">
<p className="proposal-description">{proposal.description}</p>
<div className="proposal-meta">
<div className="meta-item">
<label>Proposer:</label>
<span>{proposal.proposer}</span>
</div>
<div className="meta-item">
<label>Deadline:</label>
<span>{proposal.deadline.toLocaleDateString()}</span>
</div>
<div className="meta-item">
<label>Vote Count:</label>
<span>{proposal.voteCount} / {proposal.quorum}</span>
</div>
</div>
<div className="vote-progress">
<div className="progress-bar">
<div
className="progress-fill"
style={{
width: `${Math.min((parseFloat(proposal.voteCount) / parseFloat(proposal.quorum)) * 100, 100)}%`
}}
/>
</div>
<span className="progress-text">
{Math.round((parseFloat(proposal.voteCount) / parseFloat(proposal.quorum)) * 100)}%
</span>
</div>
</div>
<div className="proposal-actions">
{canVote(proposal) && (
<Button onClick={() => handleVote(proposal.id)}>
Vote
</Button>
)}
{canExecute(proposal) && (
<Button variant="success" onClick={() => handleExecuteProposal(proposal.id)}>
Execute
</Button>
)}
{proposal.hasVoted && (
<span className="voted-indicator">✓ Voted</span>
)}
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</div>
);
};
export default GovernancePanel;
// src/App.jsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Web3Provider from './components/Web3Provider';
import TokenDashboard from './components/TokenDashboard';
import GovernancePanel from './components/GovernancePanel';
import Navbar from './components/Navbar';
import './App.css';
function App() {
return (
<Web3Provider>
<Router>
<div className="App">
<Navbar />
<main className="main-content">
<Routes>
<Route path="/" element={<TokenDashboard />} />
<Route path="/governance" element={<GovernancePanel />} />
</Routes>
</main>
</div>
</Router>
</Web3Provider>
);
}
export default App;
This section builds out the React frontend for your DApp with three key components. The Web3Provider sets up wallet connectivity, handles MetaMask integration, and manages network switching. It exposes a custom useWeb3 hook so other components can easily access the Web3 instance, account, and contract without prop drilling.
The TokenDashboard serves as the main interface where users interact with staking pools and liquidity management. It fetches token balances, displays active staking pools with their APR and lock periods, and allows users to stake, unstake, and claim rewards. The liquidity section lets users contribute tokens and ETH to pools in exchange for rewards.
The GovernancePanel implements on-chain voting. Users with sufficient voting power can create proposals, vote on existing ones, and execute proposals once the quorum is reached. The interface tracks voting progress with visual indicators and prevents users from voting twice on the same proposal.
The top-level App component wires everything together using React Router, wrapping the application in the Web3Provider to ensure wallet state is available throughout.
3. DeFi Protocol with Python and Web3.py
# defi_protocol.py
import json
import time
from web3 import Web3
from web3.contract import Contract
from web3.middleware import geth_poa_middleware
from eth_account import Account
from eth_utils import to_checksum_address
from decimal import Decimal
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DeFiProtocol:
"""
DeFi Protocol implementation for automated trading and liquidity management
"""
def __init__(self, provider_url, private_key=None):
"""
Initialize DeFi Protocol
Args:
provider_url: Web3 provider URL
private_key: Private key for transactions (optional)
"""
self.w3 = Web3(Web3.HTTPProvider(provider_url))
# Inject POA middleware for testnets
if self.w3.eth.chain_id in [3, 4, 5, 42, 80001]:
self.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
self.account = None
if private_key:
self.account = Account.from_key(private_key)
self.w3.eth.default_account = self.account.address
# Contract addresses and ABIs
self.contracts = {}
self.load_contracts()
# Gas settings
self.gas_price = self.w3.eth.gas_price
self.gas_limit = 300000
def load_contracts(self):
"""Load contract ABIs and addresses"""
# Example contract ABIs (would normally load from files)
erc20_abi = [
{
"constant": True,
"inputs": [{"name": "_owner", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "balance", "type": "uint256"}],
"type": "function"
},
{
"constant": False,
"inputs": [
{"name": "_to", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"type": "function"
},
{
"constant": False,
"inputs": [
{"name": "_spender", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "approve",
"outputs": [{"name": "", "type": "bool"}],
"type": "function"
}
]
uniswap_router_abi = [
{
"constant": False,
"inputs": [
{"name": "amountOutMin", "type": "uint256"},
{"name": "path", "type": "address[]"},
{"name": "to", "type": "address"},
{"name": "deadline", "type": "uint256"}
],
"name": "swapExactETHForTokens",
"outputs": [{"name": "amounts", "type": "uint256[]"}],
"type": "function"
},
{
"constant": False,
"inputs": [
{"name": "amountIn", "type": "uint256"},
{"name": "amountOutMin", "type": "uint256"},
{"name": "path", "type": "address[]"},
{"name": "to", "type": "address"},
{"name": "deadline", "type": "uint256"}
],
"name": "swapExactTokensForTokens",
"outputs": [{"name": "amounts", "type": "uint256[]"}],
"type": "function"
},
{
"constant": True,
"inputs": [
{"name": "amountIn", "type": "uint256"},
{"name": "reserveIn", "type": "uint256"},
{"name": "reserveOut", "type": "uint256"}
],
"name": "getAmountOut",
"outputs": [{"name": "amountOut", "type": "uint256"}],
"type": "function"
}
]
# Contract addresses (example for Ethereum Mainnet)
self.contract_addresses = {
'WETH': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
'USDC': '0xA0b86a33E6441E7C1A785D1A97b7327c2c0c0c0c',
'DAI': '0x6B175474E89094C44Da98b954EedeAC495271d0F',
'UNISWAP_ROUTER': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
'UNISWAP_FACTORY': '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'
}
# Initialize contracts
for name, address in self.contract_addresses.items():
if name in ['WETH', 'USDC', 'DAI']:
self.contracts[name] = self.w3.eth.contract(
address=address,
abi=erc20_abi
)
elif name == 'UNISWAP_ROUTER':
self.contracts[name] = self.w3.eth.contract(
address=address,
abi=uniswap_router_abi
)
def get_balance(self, token_symbol, address=None):
"""
Get token balance for address
Args:
token_symbol: Token symbol (e.g., 'ETH', 'USDC', 'DAI')
address: Address to check (default: current account)
Returns:
Token balance as Decimal
"""
if address is None:
if not self.account:
raise ValueError("No account specified")
address = self.account.address
if token_symbol == 'ETH':
balance_wei = self.w3.eth.get_balance(address)
return Decimal(balance_wei) / Decimal(10**18)
if token_symbol not in self.contracts:
raise ValueError(f"Unknown token: {token_symbol}")
contract = self.contracts[token_symbol]
balance_wei = contract.functions.balanceOf(address).call()
decimals = self.get_token_decimals(token_symbol)
return Decimal(balance_wei) / Decimal(10**decimals)
def get_token_decimals(self, token_symbol):
"""Get token decimals"""
decimals_map = {
'WETH': 18,
'USDC': 6,
'DAI': 18
}
return decimals_map.get(token_symbol, 18)
def approve_token(self, token_symbol, spender_address, amount):
"""
Approve token spending
Args:
token_symbol: Token symbol to approve
spender_address: Address to approve for
amount: Amount to approve (in human-readable format)
Returns:
Transaction hash
"""
if not self.account:
raise ValueError("No account configured")
if token_symbol not in self.contracts:
raise ValueError(f"Unknown token: {token_symbol}")
contract = self.contracts[token_symbol]
decimals = self.get_token_decimals(token_symbol)
amount_wei = int(Decimal(amount) * Decimal(10**decimals))
# Build transaction
transaction = contract.functions.approve(
spender_address,
amount_wei
).build_transaction({
'from': self.account.address,
'gas': self.gas_limit,
'gasPrice': self.gas_price,
'nonce': self.w3.eth.get_transaction_count(self.account.address)
})
# Sign and send transaction
signed_txn = self.w3.eth.account.sign_transaction(transaction, self.account.key)
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction)
# Wait for confirmation
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
logger.info(f"Token approved: {tx_hash.hex()}")
return tx_hash.hex()
else:
raise Exception("Transaction failed")
def swap_eth_for_tokens(self, eth_amount, token_symbol, slippage_percent=1):
"""
Swap ETH for tokens on Uniswap
Args:
eth_amount: Amount of ETH to swap
token_symbol: Target token symbol
slippage_percent: Maximum slippage percentage
Returns:
Transaction hash
"""
if not self.account:
raise ValueError("No account configured")
if token_symbol not in self.contract_addresses:
raise ValueError(f"Unknown token: {token_symbol}")
router = self.contracts['UNISWAP_ROUTER']
token_address = self.contract_addresses[token_symbol]
# Calculate minimum amount out with slippage
amount_out = self.get_swap_amount_out(eth_amount, 'ETH', token_symbol)
min_amount_out = int(amount_out * (1 - slippage_percent / 100))
# Build transaction
eth_amount_wei = int(Decimal(eth_amount) * Decimal(10**18))
deadline = int(time.time()) + 300 # 5 minutes
transaction = router.functions.swapExactETHForTokens(
min_amount_out,
[self.contract_addresses['WETH'], token_address],
self.account.address,
deadline
).build_transaction({
'from': self.account.address,
'value': eth_amount_wei,
'gas': self.gas_limit,
'gasPrice': self.gas_price,
'nonce': self.w3.eth.get_transaction_count(self.account.address)
})
# Sign and send transaction
signed_txn = self.w3.eth.account.sign_transaction(transaction, self.account.key)
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction)
# Wait for confirmation
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
logger.info(f"ETH swapped for {token_symbol}: {tx_hash.hex()}")
return tx_hash.hex()
else:
raise Exception("Transaction failed")
def swap_tokens_for_eth(self, token_symbol, token_amount, slippage_percent=1):
"""
Swap tokens for ETH on Uniswap
Args:
token_symbol: Token symbol to swap
token_amount: Amount of tokens to swap
slippage_percent: Maximum slippage percentage
Returns:
Transaction hash
"""
if not self.account:
raise ValueError("No account configured")
if token_symbol not in self.contract_addresses:
raise ValueError(f"Unknown token: {token_symbol}")
router = self.contracts['UNISWAP_ROUTER']
token_address = self.contract_addresses[token_symbol]
# Approve token spending
self.approve_token(token_symbol, self.contract_addresses['UNISWAP_ROUTER'], token_amount)
# Calculate minimum amount out with slippage
amount_out = self.get_swap_amount_out(token_amount, token_symbol, 'ETH')
min_amount_out = int(amount_out * (1 - slippage_percent / 100))
# Build transaction
decimals = self.get_token_decimals(token_symbol)
token_amount_wei = int(Decimal(token_amount) * Decimal(10**decimals))
deadline = int(time.time()) + 300 # 5 minutes
transaction = router.functions.swapExactTokensForTokens(
token_amount_wei,
min_amount_out,
[token_address, self.contract_addresses['WETH']],
self.account.address,
deadline
).build_transaction({
'from': self.account.address,
'gas': self.gas_limit,
'gasPrice': self.gas_price,
'nonce': self.w3.eth.get_transaction_count(self.account.address)
})
# Sign and send transaction
signed_txn = self.w3.eth.account.sign_transaction(transaction, self.account.key)
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction)
# Wait for confirmation
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
logger.info(f"{token_symbol} swapped for ETH: {tx_hash.hex()}")
return tx_hash.hex()
else:
raise Exception("Transaction failed")
def get_swap_amount_out(self, amount_in, token_in, token_out):
"""
Calculate amount out for swap (simplified)
Args:
amount_in: Amount to swap
token_in: Input token symbol
token_out: Output token symbol
Returns:
Expected amount out
"""
# This is a simplified calculation
# In production, you would query the actual pool reserves
# Example exchange rates (would be fetched from DEX)
exchange_rates = {
('ETH', 'USDC'): 2000, # 1 ETH = 2000 USDC
('ETH', 'DAI'): 2000, # 1 ETH = 2000 DAI
('USDC', 'ETH'): 0.0005, # 1 USDC = 0.0005 ETH
('DAI', 'ETH'): 0.0005, # 1 DAI = 0.0005 ETH
}
rate = exchange_rates.get((token_in, token_out))
if rate is None:
raise ValueError(f"No exchange rate for {token_in} -> {token_out}")
return Decimal(amount_in) * Decimal(rate)
def add_liquidity(self, token_symbol, token_amount, eth_amount):
"""
Add liquidity to Uniswap pool
Args:
token_symbol: Token symbol to add
token_amount: Amount of tokens to add
eth_amount: Amount of ETH to add
Returns:
Transaction hash
"""
if not self.account:
raise ValueError("No account configured")
# This would implement actual Uniswap liquidity addition
# For now, it's a placeholder
logger.info(f"Adding liquidity: {token_amount} {token_symbol} + {eth_amount} ETH")
# In production, you would:
# 1. Approve token spending
# 2. Call Uniswap router's addLiquidityETH function
# 3. Handle transaction and receipt
return "0xplaceholder"
def remove_liquidity(self, token_symbol, liquidity_amount):
"""
Remove liquidity from Uniswap pool
Args:
token_symbol: Token symbol of the pool
liquidity_amount: Amount of liquidity tokens to remove
Returns:
Transaction hash
"""
if not self.account:
raise ValueError("No account configured")
# This would implement actual Uniswap liquidity removal
logger.info(f"Removing liquidity: {liquidity_amount} from {token_symbol} pool")
# In production, you would:
# 1. Call Uniswap router's removeLiquidityETH function
# 2. Handle transaction and receipt
return "0xplaceholder"
def get_portfolio_value(self, address=None):
"""
Calculate total portfolio value in USD
Args:
address: Address to calculate for (default: current account)
Returns:
Portfolio value in USD
"""
if address is None:
if not self.account:
raise ValueError("No account specified")
address = self.account.address
# Get balances
eth_balance = self.get_balance('ETH', address)
usdc_balance = self.get_balance('USDC', address)
dai_balance = self.get_balance('DAI', address)
# Calculate USD value (simplified)
# In production, you would fetch real prices from oracles
eth_price_usd = 2000 # Example price
dai_price_usd = 1.0 # DAI is pegged to USD
portfolio_value = (
eth_balance * eth_price_usd +
usdc_balance +
dai_balance * dai_price_usd
)
return {
'total_value_usd': portfolio_value,
'breakdown': {
'ETH': {'amount': eth_balance, 'value_usd': eth_balance * eth_price_usd},
'USDC': {'amount': usdc_balance, 'value_usd': usdc_balance},
'DAI': {'amount': dai_balance, 'value_usd': dai_balance * dai_price_usd}
}
}
def monitor_gas_prices(self):
"""
Monitor current gas prices
Returns:
Current gas price in Gwei
"""
gas_price_wei = self.w3.eth.gas_price
gas_price_gwei = gas_price_wei / 10**9
return {
'gas_price_wei': gas_price_wei,
'gas_price_gwei': gas_price_gwei,
'estimated_eth_cost': gas_price_wei * self.gas_limit / 10**18
}
def execute_arbitrage(self, token_symbol, min_profit_percent=1):
"""
Execute arbitrage opportunity (simplified)
Args:
token_symbol: Token to arbitrage
min_profit_percent: Minimum profit percentage required
Returns:
Transaction hash if arbitrage executed
"""
# This is a simplified arbitrage implementation
# In production, you would:
# 1. Monitor prices across multiple DEXs
# 2. Calculate profit opportunities
# 3. Execute trades when profitable
logger.info(f"Checking arbitrage opportunities for {token_symbol}")
# Example: Check price difference between two DEXs
# This is placeholder logic
price_dex1 = self.get_swap_amount_out(1, 'ETH', token_symbol)
price_dex2 = self.get_swap_amount_out(1, 'ETH', token_symbol) * 1.02 # 2% difference
profit_percent = ((price_dex2 - price_dex1) / price_dex1) * 100
if profit_percent >= min_profit_percent:
logger.info(f"Arbitrage opportunity found: {profit_percent:.2f}% profit")
# Execute arbitrage trades
return "0xarbitrage_placeholder"
return None
# Usage example
def main():
"""Example usage of DeFi Protocol"""
# Initialize protocol
provider_url = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
private_key = "YOUR_PRIVATE_KEY" # Be careful with private keys!
protocol = DeFiProtocol(provider_url, private_key)
# Get portfolio value
portfolio = protocol.get_portfolio_value()
print(f"Portfolio Value: ${portfolio['total_value_usd']:.2f}")
# Check gas prices
gas_info = protocol.monitor_gas_prices()
print(f"Gas Price: {gas_info['gas_price_gwei']:.2f} Gwei")
# Swap ETH for USDC
try:
tx_hash = protocol.swap_eth_for_tokens(0.1, 'USDC', slippage_percent=1)
print(f"Swap transaction: {tx_hash}")
except Exception as e:
print(f"Swap failed: {e}")
# Check for arbitrage opportunities
arbitrage_tx = protocol.execute_arbitrage('USDC', min_profit_percent=1)
if arbitrage_tx:
print(f"Arbitrage executed: {arbitrage_tx}")
if __name__ == "__main__":
main()
Web3 Architecture
DeFi Ecosystem
graph TD
A[User] --> B[Wallet]
B --> C[DApp Frontend]
C --> D[Web3.js/Ethers.js]
D --> E[Smart Contracts]
E --> F[DeFi Protocols]
F --> G[DEX]
F --> H[Lending]
F --> I[Yield Farming]
F --> J[Liquidity Pools]
E --> K[Blockchain]
K --> L[Ethereum]
K --> M[Polygon]
K --> N[BSC]
O[Oracles] --> E
P[Chainlink] --> O
Q[Band Protocol] --> O
R[Governance] --> E
S[DAO] --> R
T[Token Holders] --> R
Smart Contract Security
Common Vulnerabilities
| Vulnerability | Description | Mitigation |
|---|---|---|
| Reentrancy | Recursive calls before state update | ReentrancyGuard, Checks-Effects-Interactions |
| Integer Overflow | Arithmetic overflow/underflow | SafeMath, Solidity 0.8+ |
| Access Control | Unauthorized function calls | Modifiers, Role-based access |
| Front-Running | Transaction order manipulation | Commit-reveal schemes |
| Oracle Manipulation | Price feed manipulation | Decentralized oracles |
DeFi Protocols Comparison
Leading DeFi Categories
| Category | Protocols | Function | TVL (Billion $) |
|---|---|---|---|
| DEX | Uniswap, SushiSwap | Token Swaps | 25+ |
| Lending | Aave, Compound | Loans | 20+ |
| Yield | Yearn, Harvest | Yield Optimization | 10+ |
| Derivatives | Synthetix, Perpetual | Derivatives | 5+ |
| Oracle | Chainlink, Band | Price Feeds | 15+ |
Advantages and Disadvantages
Benefits of Web3/DeFi
- Decentralization: No central authority required
- Transparency: All transactions are publicly visible
- Permissionless: Anyone can participate without approval
- Programmability: Smart contracts enable complex logic
- Composability: Protocols can be combined and stacked
Drawbacks
- Scalability: Limited transaction throughput
- Gas Fees: High transaction costs under network congestion
- User Experience: Complex wallet integration and onboarding
- Security Risks: Smart contract vulnerabilities
- Regulatory Uncertainty: Evolving and unclear regulatory landscape
Common Exam Questions
-
What is the difference between Web2 and Web3? Web2 is centralized, relying on servers controlled by companies. Web3 is decentralized, using blockchain and smart contracts to eliminate intermediaries.
-
Explain smart contracts. Smart contracts are self-executing agreements deployed on the blockchain. They automatically enforce and execute the terms of a transaction without requiring intermediaries.
-
When do you use DeFi protocols? DeFi protocols provide decentralized financial services such as trading, lending, and yield farming without traditional financial institutions.
-
What are the main advantages of blockchain? Decentralization, transparency, immutability, and cryptographic security.



