A decentralized exchange (DEX) implementation built on Ethereum that enables seamless token swapping and liquidity provision using an Automated Market Maker (AMM) with constant product formula.
| Component | Links |
|---|---|
| Live Frontend | https://simpleswap-dex-dapp.vercel.app/ |
| SimpleSwap Contract | 0x957d727337297b649AE8df9Cad0b157cf04C3224 |
| Token A Contract | 0xCDBDDA06C8b9dF27a8502e57C8614d4EfAc5ED76 |
| Token B Contract | 0x11d6A5B61eE830b97C88b4Fc26849274517DfF94 |
- Project Overview
- How to Use
- Deployment Instructions
- Smart Contract Documentation
- Frontend Documentation
- Test Documentation
- Coverage Analysis
- Error Handling
SimpleSwap is a decentralized exchange (DEX) implementation built on Ethereum that provides:
- Automated Market Maker (AMM) functionality using constant product formula (
x * y = k) - Liquidity Provision with LP token rewards for liquidity providers
- Token Swapping with real-time price calculations and slippage protection
- ERC20 Token Management with minting capabilities for testing
- Modern Web3 Frontend with MetaMask integration and responsive design
- Event-Driven Architecture with real-time blockchain event monitoring
- Advanced Interaction Tracking with duplicate prevention and localStorage persistence
- Constant Product Formula: Implements
x * y = kfor automatic price discovery - LP Token System: Mint/burn LP tokens representing liquidity pool shares
- Slippage Protection: Minimum amount guarantees for all operations
- Deadline Protection: Transaction expiration timestamps prevent stale trades
- Token Sorting: Consistent token pair ordering prevents duplicate pools
- Comprehensive Testing: Function coverage with edge cases and access control
- Real-Time Event Monitoring: Automatic detection of swaps and liquidity operations
- Smart Interaction Management: Duplicate prevention and wallet-specific history
- Click "Connect MetaMask" in the top navigation
- Approve the connection request in your wallet
- Ensure you're on Sepolia testnet
- Click "Mint 1000 Token A"
- Confirm the transaction in MetaMask
- "Mint 1000 Token B"
- Confirm the transaction in MetaMask
- Your balance will update automatically
- Click "Approve All Tokens" to enable add liquidity and swapping
- This allows the DEX to spend your tokens
- Enter amounts for both Token A and Token B
- Click "Add Liquidity" to receive LP tokens
- Enter the amount you want to swap
- Select source and destination tokens
- Review the exchange rate and expected output
- Click "Execute Swap" and confirm transaction
- Enter the amount of LP tokens to remove
- Preview shows the tokens you'll receive
- Click "Remove Liquidity" to get back your tokens
- Real-Time Tracking: All transactions are automatically detected and displayed
- Transaction Details: View amounts, timestamps, and Etherscan links
- Duplicate Prevention: System automatically prevents duplicate entries
git clone git@github.com:IvanSarapura/SimpleSwapDApp.gitnpm installnpx hardhat compilenpx hardhat test
npx hardhat coverageThe project consists of three main smart contracts:
- SimpleSwap.sol - Main DEX contract with AMM functionality
- TokenA.sol - ERC20 token for testing and liquidity provision
- TokenB.sol - ERC20 token for testing and liquidity provision
Network: Sepolia Testnet | Compiler: Solidity ^0.8.28 | License: MIT
| Function | Description | Gas Usage |
|---|---|---|
addLiquidity() |
Adds liquidity to token pair | ~179,282 |
removeLiquidity() |
Removes liquidity from token pair | ~64,513 |
swapExactTokensForTokens() |
Swaps exact input tokens | ~69,405 |
getAmountOut() |
Calculates output amount | View Function |
getReserves() |
Returns current reserves | View Function |
getPrice() |
Returns current token price | View Function |
| Event | Parameters | Description |
|---|---|---|
Swap |
tokenIn, tokenOut, amountIn, amountOut, to |
Emitted when tokens are swapped |
LiquidityAdded |
tokenA, tokenB, amountA, amountB, liquidity |
Emitted when liquidity is added |
LiquidityRemoved |
tokenA, tokenB, amountA, amountB, liquidity |
Emitted when liquidity is removed |
- Liquidity Calculation:
sqrt(amountA * amountB)for initial liquidity - Swap Calculation:
amountOut = (amountIn * reserveOut) / (reserveIn + amountIn) - Price Calculation:
price = (reserveB * 10^18) / reserveA
Security Features:
- Zero address validation
- Same token prevention
- Deadline validation
- Slippage protection with minimum amounts
- Token sorting for consistent pair ordering
Properties:
- Initial Supply: 1,000,000 tokens each
- Symbols: "TACC" and "TBCC"
- Decimals: 18
- Mintable: Yes (owner only)
Key Functions:
mint(address to, uint256 amount)- Owner-only minting- Standard ERC20 functions (transfer, approve, etc.)
| Component | Technology | Version |
|---|---|---|
| Frontend | Vanilla JavaScript | ES6+ |
| Blockchain | Ethers.js | v5.7.2 |
| Styling | Custom CSS | - |
| Wallet | MetaMask | Latest |
├── index.html # Main HTML structure
├── styles.css # Complete styling system
├── script.js # Core application logic
└── contracts/ # Smart contract files
- Wallet Integration: MetaMask connection and account management
- Token Operations: Minting, approval, and balance tracking
- DEX Operations: Swapping and liquidity management
- Price Display: Real-time price feeds and reserve information
- Responsive Design: Mobile-first approach with modern UI
- Event-Driven Architecture: Real-time blockchain event monitoring
- Advanced Interaction Management: Smart duplicate prevention and localStorage persistence
- Data Management: Fresh blockchain reads with cache clearing for accurate data
- Loading States: Visual feedback during transactions
- Error Handling: Comprehensive error messages and recovery guidance
- Real-time Updates: Automatic balance and price updates
- Transaction History: Complete wallet interaction tracking with Etherscan links
- Data Persistence: Interactions saved per wallet address in localStorage
- Cache Management: Fresh blockchain reads to ensure data accuracy
- 24-Hour Time Format: All timestamps displayed in 24-hour format
The application implements an event-driven architecture that automatically detects and tracks all blockchain interactions:
// Automatic event listener setup on wallet connection
setupContractEventListeners();
// Event listener cleanup on wallet disconnect
cleanupContractEventListeners();
// Fetch historical events from blockchain
fetchRecentEvents(fromBlock, toBlock);| Event Type | Contract Event | Description |
|---|---|---|
| Swap | Swap |
Token swap operations |
| Add Liquidity | LiquidityAdded |
Liquidity provision |
| Remove Liquidity | LiquidityRemoved |
Liquidity removal |
Duplicate Prevention:
- Transaction hash-based duplicate detection
- Wallet connection verification before processing
- Automatic cleanup on disconnect
Real-Time Features:
- Automatic interaction recording
- Real-time UI updates
- Balance and data refresh on events
- Notification system for user transactions
// Load recent events from blockchain
fetchRecentEvents(fromBlock, toBlock);
// Manual event history loading via UI
// Click "Load Event History" buttonFeatures:
- Configurable block range (default: last 1000 blocks)
- Automatic interaction population
- Error handling and user feedback
- Loading states and progress indicators
The system displays interactions in a structured format with proper token names:
Swap Operations:
From: 1,000.00 Token A
To: 1,050.00 Token B
Add Liquidity:
From: 1,000.00 Token A
1,000.00 Token B
To: LP
Remove Liquidity:
From: 1,638.96 LP
To: 1,678.59 Token A
1,600.26 Token B
All timestamps are displayed in 24-hour format for better readability:
- Format:
MM/DD/YYYY, HH:MM:SS - Example:
12/15/2023, 14:30:45
connect(): Initializes Web3Provider with MetaMask, creates contract instances, sets up event listenersdisconnect(): Resets global variables, clears UI state, disconnects from blockchain, cleans up event listenersupdateBalances(): Fetches token balances (TokenA, TokenB, LP) from contracts and updates UI display
calculateSwap(): Real-time price calculation usinggetReserves()andgetAmountOut()from SimpleSwap contractcheckApproval(): Validates token allowance against SimpleSwap contract before swapsapproveToken(): Approves specific token amount for SimpleSwap contract spendingexecuteSwap(): ExecutesswapExactTokensForTokens()with⚠️ No slippage protection (usesamountOutMin = 0)swapAddress(): UI function to reverse token selection direction
addLiquidity(): CallsaddLiquidity()contract function with 5% slippage protection, validates positive amountsremoveLiquidity(): CallsremoveLiquidity()contract function with 5% slippage protection, validates LP token balancecalculateRemoveLiquidityPreview(): Estimates withdrawal amounts using pool reserves and total supply (view only)autofillLiquidityFields(): Automatically calculates optimal token ratios based on current pool reserves
mintTokenA()/mintTokenB(): Callsmint()function to mint 1000 tokens (owner only), updates UI with loading statesapproveAllTokens(): Sets maximum allowance (MaxUint256) for both tokens to SimpleSwap contract
addWalletInteraction(): Records wallet interactions with timestamps, transaction hashes, and formatted amounts with duplicate preventionupdateInteractionsDisplay(): Renders interaction history with action badges, wallet addresses, and Etherscan linkssaveInteractionsToStorage(): Persists interactions in localStorage per wallet addressloadInteractionsFromStorage(): Loads interaction history when wallet connectsfetchRecentEvents(): Loads historical blockchain events to populate interaction history- Supports up to 50 interactions with automatic cleanup and localStorage persistence
updateData(): Fetches current reserves usinggetReserves(), calculates and displays exchange rates, callsupdateLPSupply()for LP token supplyupdateLPSupply(): Fetches LP token supply usingtotalSupply()function with fresh blockchain reads to avoid cache issuesforceUpdateLPSupply(): Forces complete refresh of LP supply data with cache clearingcalculateRealPrices(): Calculates accurate prices usinggetAmountOut()for 1 token standard amounts- Real-time data updates in navigation bar showing Token A to Token B ratio
Core Contract Calls:
SimpleSwap:getReserves(),getAmountOut(),swapExactTokensForTokens(),addLiquidity(),removeLiquidity(),balanceOf(),totalSupply()TokenA/TokenB:balanceOf(),allowance(),approve(),mint()
Event Monitoring:
Swap: Real-time swap event detection with automatic interaction recordingLiquidityAdded: Automatic liquidity addition trackingLiquidityRemoved: Automatic liquidity removal tracking
Utility Functions:
showLoading()/hideLoading(): Visual feedback during transactionsshowNotification(): Toast notifications with auto-hide and multiple types (success, error, info, warning)closeNotification(): Manual notification dismissal
Transaction Flow:
- User Input → Basic validation (amount > 0, sufficient balance)
- Approval Check →
allowance()vs required amount - Contract Call → Direct smart contract interaction with proper parameters
- Transaction Wait →
await tx.wait()for confirmation - Event Detection → Automatic event listener processing
- UI Update → Balance refresh, notification display, loading state management
Error Handling:
- MetaMask detection and connection errors
- Transaction rejection handling
- Contract revert messages display
- Network validation and switching
- Insufficient balance and allowance errors
- Event listener cleanup and error recovery
- Framework: Hardhat + Chai
- Language: JavaScript
- Coverage: solidity-coverage
- Network: Hardhat Network
test/
├── SimpleSwapTest.js # Core DEX functionality tests
└── TokensTest.js # Token contract access control tests
Primary test file covering complete SimpleSwap contract functionality
Test Categories:
-
Input Validation Tests (3 tests)
- Zero amount validation in
getAmountOut() - Invalid reserves handling
- Same token prevention in
addLiquidity()
- Zero amount validation in
-
Core Functionality Tests (5 tests)
- LP token minting via
addLiquidity() - Price calculations using
getPrice() - Token swapping with
swapExactTokensForTokens() - Liquidity removal with
removeLiquidity() - Reserve tracking via
getReserves()
- LP token minting via
-
Edge Case Tests (15 tests)
- No liquidity scenarios
- Insufficient amount validations
- Deadline expiration (3 scenarios)
- Zero address validation (4 scenarios)
- Slippage protection testing
- Invalid path validation
- Zero liquidity prevention
Access control tests for token contracts
Test Categories:
- Access Control Tests (2 tests)
- TokenA mint function - owner-only access with
OwnableUnauthorizedAccounterror - TokenB mint function - owner-only access with
OwnableUnauthorizedAccounterror
- TokenA mint function - owner-only access with
| Test Category | Count | Description |
|---|---|---|
| Input Validation | 3 | Zero amounts, invalid reserves, same tokens |
| Core Functionality | 5 | Add/remove liquidity, swap, price calculation |
| Edge Cases | 15 | Insufficient amounts, expired deadlines, slippage |
| Access Control | 2 | Owner-only functions, permission validation |
| Total | 25 | Complete test coverage |
Core Functionality:
- ✓ Add liquidity and mint LP tokens
- ✓ Remove liquidity and burn LP tokens
- ✓ Execute token swaps with proper price calculation
- ✓ Calculate exchange rates and reserves
Edge Cases:
- ✓ Handle insufficient liquidity scenarios
- ✓ Validate slippage protection mechanisms
- ✓ Test deadline expiration handling
- ✓ Verify zero address validation
Security Tests:
- ✓ Owner-only mint function access control
- ✓ Proper error handling for invalid inputs
File | % Stmts | % Branch | % Funcs | % Lines |
-----------------|----------|----------|----------|----------|
contracts/ | 98.65 | 87.88 | 100 | 95.83 |
SimpleSwap.sol | 98.57 | 87.10 | 100 | 95.65 |
TokenA.sol | 100 | 100 | 100 | 100 |
TokenB.sol | 100 | 100 | 100 | 100 |
-----------------|----------|----------|----------|----------|
All files | 98.65 | 87.88 | 100 | 95.83 |
-----------------|----------|----------|----------|----------|| Contract / Method | Min Gas | Max Gas | Avg Gas | # Calls | Description |
| -------------------------- | ------- | ------- | ------- | ------- | ---------------------------- |
| **SimpleSwap** | | | | | |
| `addLiquidity` | 82,480 | 196,896 | 179,282 | 13 | Add liquidity to token pairs |
| `approve` | - | - | 46,012 | 1 | Approve LP token spending |
| `removeLiquidity` | - | - | 64,513 | 1 | Remove liquidity from pairs |
| `swapExactTokensForTokens` | - | - | 69,405 | 1 | Execute token swaps |
| **TokenA** | | | | | |
| `approve` | 45,962 | 45,974 | 45,970 | 20 | Approve token spending |
| `mint` | - | - | 36,086 | 27 | Mint new tokens (owner only) |
| **TokenB** | | | | | |
| `approve` | 45,962 | 45,974 | 45,973 | 15 | Approve token spending |
| `mint` | - | - | 36,086 | 27 | Mint new tokens (owner only) || Contract | Gas Cost | % of Block Limit | Description |
| ---------- | --------- | ---------------- | ----------------- |
| SimpleSwap | 1,419,056 | 4.7% | Main DEX contract |
| TokenA | 644,865 | 2.1% | Test token A |
| TokenB | 644,865 | 2.1% | Test token B |Common errors that may occur when interacting with the contracts:
| Error Message | Cause | Solution |
|---|---|---|
"Same token" |
Identical token addresses | Use different token addresses |
"Zero address" |
Using address(0) | Provide valid token addresses |
"Expired" |
Transaction deadline passed | Use current timestamp + buffer |
"Low amountA" / "Low amountB" |
Slippage protection triggered | Increase slippage tolerance |
"No liquidity" |
Empty liquidity pool | Add liquidity first |
"Low output" |
Swap output below minimum | Adjust amountOutMin parameter |
"Invalid path" |
Wrong path length | Use exactly 2 addresses in path |
"Low liquidity" |
Trying to mint zero LP tokens | Provide non-zero amounts |
"Invalid reserves" |
Pool reserves are zero | Ensure pool is properly initialized |
"Zero amount" |
Input amount is zero | Enter valid amount > 0 |
| Error Type | Cause | Solution |
|---|---|---|
| Event Listener Errors | Contract events not found in ABI | Ensure events are included in ABI |
| Duplicate Interactions | Same transaction processed multiple times | Duplicate prevention system active |
| Wallet Connection Issues | MetaMask not installed or wrong network | Install MetaMask, switch to Sepolia |
| Cache Issues | Stale blockchain data | Use "Update Data" button |
Developed as part of ETH KIPU Module 4