Local AMM simulation designed as a portfolio-grade demo for a Solutions Engineer in crypto/blockchain. It wires Solidity contracts to a Go execution engine and a Next.js dashboard so you can explain protocol mechanics, real-time data pipelines, and system design tradeoffs in one cohesive project.
This repo is intentionally built for demoability:
- Clear, inspectable end-to-end flow (contracts -> backend -> UI).
- Real-time WebSocket streaming and REST snapshots.
- Bots that stress the pool and generate meaningful metrics.
- A small but realistic system that is easy to run in interviews or live demos.
- Protocol mechanics: Constant-product AMM, fees, LP economics, price impact.
- System integration: go-ethereum bindings, on-chain execution, backend orchestration.
- Data pipeline: on-chain reads -> metrics -> WebSocket -> visualization.
- Operational thinking: session lifecycle, safe defaults, deterministic local infra.
- Customer-facing clarity: explorable UI and explicit data provenance.
Next.js (localhost:3000)
- Dashboard (charts + controls)
- REST fetch for snapshots
- WebSocket for live updates
|
v
Go backend (localhost:8080)
- Session + bot orchestration
- On-chain executor (go-ethereum)
- In-memory store + metrics
v
Anvil (localhost:8545)
- AppleToken + AppleAMM contracts
- REST: snapshot endpoints for candles, trades, account performance, and session state.
- WebSocket:
ws://localhost:8080/streamfor real-time trades, prices, and events. - Chain RPC:
http://localhost:8545(Anvil local chain).
- Deploy contracts via Foundry (Deploy.s.sol writes broadcast JSON).
- Backend starts and loads contract addresses from broadcast output.
- Bots are created based on
backend/internal/config/accounts.go. - On-chain trades emit updates -> in-memory store -> metrics pipeline.
- WebSocket broadcasts push live updates to the dashboard.
- AppleToken (ERC20) + AppleAMM (constant product, 0.30% fee) on Anvil.
- Go executor that submits swaps and computes trade details.
- Trading bots:
- Retail (15) small random trades
- Whale (3) large random trades
- MeanRev (3) EWMA mean reversion on trade-flow events
- Session control (start/stop/reset) with per-session bot lifecycle.
- Metrics computed in Go:
- 5s OHLC candles, 60s TWAP, volatility from observed returns
- LP metrics (IL, fees earned, net PnL)
- Account performance (equity curve, Sharpe, drawdown, win rate)
- Dashboard components: Price, TWAP, Impact Curve, Blotter, LP Stats, Key Events, Account Metrics.
- Performance analytics page at
/performance.
The LP metrics are calculated in backend/internal/metrics/lp.go from on-chain reserves and fee totals. The goal is to clearly separate price-only IL from fees and the LP’s realized vs HODL performance.
Core inputs:
- Reserves: current APPL + ETH reserves from the AMM.
- Initial state: reserves and price at session start.
- Fees: cumulative fees from the contract, tracked as “current minus initial”.
- Spot price:
ETH reserve / APPL reserve(ETH per APPL).
Key outputs:
- LP Value:
currentApples * price + currentETH. - HODL Value: value of the initial deposit at current price
initialApples * price + initialETH. - Theoretical IL (price-only):
IL% = (2 * sqrt(r) / (1 + r)) - 1, wherer = currentPrice / initialPrice.
Converted to ETH terms:HODLValue * IL%. This is always ≤ 0 and ignores fees. - LP vs HODL PnL:
LPValue - HODLValue(can be +/-). - Fees Earned:
feesApple = currentFeesApple - initialFeesApple
feesETH = currentFeesETH - initialFeesETH
Convert apples to ETH at the current price and sum. - Net PnL:
LPvsHODL + feesEarned. - Net PnL %:
NetPnL / HODLValue.
These values are snapshotted over time for charting (history includes reserves, price, LP value, HODL value, IL, fees, and net PnL).
This project is intentionally concurrent: many bots trade simultaneously, the backend polls on-chain state, and the UI consumes real-time updates. Go’s goroutines are a superior fit for this type of system because they are lightweight, cheap to spawn, and provide clear coordination primitives for many parallel workflows.
Where goroutines are used:
- Bot execution: each bot runs in its own goroutine with context cancellation (start/stop/reset).
- Price polling: a dedicated goroutine polls reserves every few seconds and updates metrics.
- WebSocket broadcasting: non-blocking fan-out of updates to clients.
- Trade callbacks + analytics: async callbacks and trade-flow notifications to avoid blocking execution.
Why Go is especially well-suited here:
- Low overhead concurrency: dozens of bots and background tasks without heavy threads.
- Simple coordination:
context+errgroupfor lifecycle management and clean shutdown. - Safe parallelism: mutex-protected metrics + a nonce manager to prevent tx collisions.
- Great IO fit: RPC calls, timers, and WebSocket writes are naturally concurrent.
contracts/Solidity contracts + Foundry scriptsbackend/Go engine, bots, metrics, REST + WebSocket serverfrontend/Next.js dashboardscripts/deployment, bindings, and dev toolingMakefileorchestration targets
- Foundry (forge, anvil)
- Go 1.21+
- Node.js 18+ (npm)
- tmux (optional but recommended for
make up) - abigen (from go-ethereum) for Go contract bindings
- jq or python3 for ABI extraction in
scripts/generate-bindings.sh
macOS (Homebrew):
brew install tmuxUbuntu/Debian:
sudo apt-get update
sudo apt-get install -y tmuxFedora:
sudo dnf install -y tmuxArch:
sudo pacman -S tmuxWindows:
- Use WSL and install with
sudo apt-get install -y tmux.
This launches Anvil, deploys contracts, generates bindings, and starts backend + frontend.
make setup # installs deps (Foundry/solidity deps + frontend deps + bindings)
make up # tmux session: anvil, deploy, bindings, backend, frontendTo stop everything:
make downOpen http://localhost:3000, click Start, and watch metrics stream in.
If you prefer to run each service yourself:
make anvil
make deploy
make bindings
make backend
make frontendmake setup- install deps and generate bindingsmake anvil- start local chain on :8545make deploy- deploy contracts and write broadcast JSONmake bindings- generate Go bindings from contract ABIsmake backend- run Go simulator on :8080make frontend- run Next.js on :3000make kill-all- free ports 8545, 8080, 3000-3004make test-contracts- run Foundry tests
- Accounts & bot params:
backend/internal/config/accounts.go - AMM constants & thresholds:
backend/internal/config/amm.go - Chain + session defaults:
backend/internal/config/config.go
Common knobs:
- Session duration
- Bot frequency and max trade size
- Pool seed reserves
- Volatility and LP metrics behavior
- POST
/session/start(optional body:{ "duration": 300 }) - POST
/session/stop - POST
/session/reset(query:?hard=truefor hard reset) - GET
/session/state - GET
/candles - GET
/trades(query:?limit=...) - GET
/impact-curve - GET
/lp/metrics - GET
/events(query:?limit=...) - GET
/accounts - GET
/accounts/{nickname}/performance - POST
/trade/buy(body:{ "ethAmount": "1.0" }) - POST
/trade/sell(body:{ "appleAmount": "1.0" }) - GET
/user/balance
ws://localhost:8080/stream- Message types:
trade,price,lp_metrics,key_event,session_state,account_update,trades,candles,events.
make upand openhttp://localhost:3000.- Click Start and highlight the session timer + live trade blotter.
- Point out price impact from whale trades on the chart.
- Open LP Stats to explain impermanent loss vs. fees earned.
- Open
/performanceto show account equity curves and Sharpe.
- Ports in use: run
make kill-allthen retry. - No data in UI: confirm backend is running on
:8080and contracts were deployed. - Deploy failed: delete old
contracts/broadcast/Deploy.s.sol/31337/run-latest.jsonand redeploy. - Bindings errors: ensure
abigenis installed andjqorpython3is available. - tmux not found: install tmux or run the manual workflow.
- Accounts: 30 Anvil addresses; active 23
- 1 LP (index 0)
- 1 User (index 1)
- 15 Retail (indices 2-16)
- 3 Whale (indices 17-19)
- 3 MeanRev (indices 20-22)
- 7 Reserved (indices 23-29)
- Session duration: 180s default (UI-configurable)
- Retail: 10-100% of 15 ETH, every 1-4s
- Whales: up to 600 ETH, every 45-90s
- MeanRev: 50/75/150 ETH notional, EWMA half-life 25/50/75 trades
- Expose trade-flow diagnostics (rTilde/flow) in the UI for debugging.
- Multi-pool or multi-token simulations.
- Persist metrics to disk for replay.
- Compare against an external reference price feed (read-only).
This project is for local simulation and educational/demo purposes. It is not audited and is not intended for production or mainnet usage.