A Decentralized Exchange (DEX) Aggregator built in Go, designed to optimize token swaps across Ethereum (L1) and zkSync (L2) by fetching quotes from UniswapV2 and SyncSwap, selecting the best trade route, and tracking analytics. This project demonstrates clean architecture, SOLID principles, and L2 scaling for efficient DeFi applications, using Ethereum JSON RPC calls (e.g., eth_call) to interact with blockchain networks.
- Overview
- Features
- Key Components
- Project Structure
- Prerequisites
- Installation
- Usage
- Testing
- Contributing
- License
The DEX Aggregator enables users to find the best token swap routes by comparing prices across DEXes on Ethereum and zkSync. It fetches quotes via simplified eth_call requests, optimizes trades based on amount out and gas costs, and records trade analytics in memory. Built with Go, the project emphasizes clean architecture, separating business logic, presentation, and infrastructure layers. It uses table-driven unit tests with manual mocks for high test coverage, following the naming convention Test_[Function]_[Outcome]_[Condition].
This project is ideal for developers learning to build high-performance Web3 applications with L2 scaling, Ethereum internals, and SOLID design principles.
- Fetch token swap quotes from UniswapV2 (Ethereum) and SyncSwap (zkSync).
- Optimize trade routes based on amount out and gas costs.
- Track trade count and total volume in memory.
- Expose REST API endpoints for trade execution and analytics.
- Support L2 scaling with zkSync for lower gas fees.
- Implement clean architecture with SOLID principles.
- Comprehensive table-driven unit tests with manual mocks.
- TradeAggregator: Orchestrates quote fetching, route optimization, and analytics recording (
internal/application/trade_aggregator.go). - APIHandler: Exposes REST API endpoints using Gin (
internal/presentation/api_handler.go). - InMemoryAnalytics: Tracks trade count and volume in memory, thread-safe with mutexes (
internal/infrastructure/analytics/in_memory_analytics.go). - ClientFactory: Creates blockchain clients for Ethereum and zkSync (
internal/infrastructure/blockchain/client_factory.go). - EthereumClient/ZkSyncClient: Fetch quotes via
ethclient.Client(internal/infrastructure/blockchain/ethereum_client.go,zksync_client.go). - SimpleRouteOptimizer: selects the trade with the highest output amount (
cmd/main.go). - Entities/Interfaces: Define
Trade,Quote, and interfaces (internal/domain/entities,internal/domain/interfaces).
dex-aggregator/
├── cmd/
│ ├── main_test.go
│ └── main.go # Application entry point
├── internal/
│ ├── application/
│ │ ├── trade_aggregator.go # Core business logic
│ │ ├── trade_aggregator_test.go
│ ├── domain/
│ │ ├── entities/
│ │ │ ├── quote.go
│ │ │ └── trade.go
│ │ ├── interfaces/
│ │ │ ├── route_optimizer.go
│ │ │ ├── price_fetcher.go
│ │ │ └── analytics_service.go
│ ├── infrastructure/
│ │ ├── analytics/
│ │ │ ├── in_memory_analytics.go
│ │ │ ├── in_memory_analytics_test.go
│ │ ├── blockchain/
│ │ │ ├── client_factory.go
│ │ │ ├── client_factory_test.go
│ │ │ ├── ethereum_client.go
│ │ │ ├── zksync_client.go
│ │ ├── logging/
│ │ │ └── logger.go # Zerolog-based logger
│ │ ├── metrics/
│ │ │ └── metrics.go
│ ├── presentation/
│ │ ├── api_handler.go
│ │ ├── api_handler_test.go
├── README.md
├── go.mod
- Go 1.22 or later
- Access to Ethereum and zkSync RPC endpoints (e.g., Infura for Sepolia, zkSync Era Sepolia public RPC)
- Sepolia UniswapV2 Router (
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D) and zkSync SyncSwap Router (0xB3b7fCbb8Db6b605490cC976F65E1F3B3C46deB3) addresses for integration
- Clone the repository:
git clone https://github.com/your-repo/dex-aggregator.git cd dex-aggregator - Install dependencies:
go mod tidy go get github.com/ethereum/go-ethereum go get github.com/gin-gonic/gin go get github.com/rs/zerolog go get github.com/stretchr/testify
- Replace your environment variables:
replace in the main.go file ETHEREUM_RPC_URL="https://sepolia.infura.io/v3/YOUR_KEY" ZKSYNC_RPC_URL="https://sepolia.era.zksync.dev"
-
Run de application:
go run cmd/main.go
The API will start on http://localhost:8080.
-
API Endpoints:
-
Get Best Trade:
curl "http://localhost:8080/trade?token_in=0xWETH&token_out=0xUSDC&amount_in=1000000000000000000" Response: { "TokenIn": "0xWETH", "TokenOut": "0xUSDC", "AmountIn": "1000000000000000000", "AmountOut": "100000000", "Dex": "UniswapV2", "Chain": "Ethereum", "GasCost": "5000000000000" }
-
Get Trade Count:
curl "http://localhost:8080/analytics/trade-count" Response: {"trade_count": 5}
-
Get Total Volume:
curl "http://localhost:8080/analytics/volume" Response: {"total_volume": "200000000"}
-
The project uses table-driven unit tests with manual mocks for high coverage, following the naming convention Test_[Function][Outcome][Condition].
- Run all tests:
go test ./... -v- Check test coverage:
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.outContributions are welcome! To contribute:
- Fork the repository.
- Create a feature branch (git checkout -b feature/your-feature).
- Commit changes (git commit -m 'Add your feature').
- Push to the branch (git push origin feature/your-feature).
- Open a Pull Request.
Please ensure your code follows SOLID principles, includes table-driven tests, and adheres to the project's clean architecture.
This project is licensed under the MIT License.