create account

🧩 Peake DEX β€” Full Architecture & Deployment Guide (Docs + Tutorial) by paulmoon410

View this thread on: hive.blogpeakd.comecency.com
· @paulmoon410 ·
$0.30
🧩 Peake DEX β€” Full Architecture & Deployment Guide (Docs + Tutorial)
<center>![](https://images.ecency.com/DQmX4VujQ4StCLPYjUqLXHZKBhGo6G4ERHARDHJAwLntAgd/image.png)<br>*http://geocities.ws/peakecoin/pekdex*</center>




## 1) Complete Architecture Analysis

### File structure (at a glance)

    Peake_Dex/
    β”œβ”€ frontend/                 # Static site (Geocities-friendly)
    β”‚  β”œβ”€ index.html             # Landing + balances/auth state
    β”‚  β”œβ”€ config.js              # Backend base URL
    β”‚  └─ pairs/
    β”‚     └─ pair.html           # Per-pair trading UI
    β”‚
    β”œβ”€ backend/                  # Flask API
    β”‚  β”œβ”€ app.py                 # Endpoints + matching loop
    β”‚  β”œβ”€ peake-dex.service      # systemd unit
    β”‚  β”œβ”€ deploy.sh              # Production deploy script
    β”‚  β”œβ”€ deploy-simple.sh       # Minimal deploy script
    β”‚  β”œβ”€ requirements.txt       # Python dependencies
    β”‚  └─ README.md              # Backend setup/how-to
    β”‚
    β”œβ”€ README.md                 # Top-level docs
    └─ LICENSE                   # MIT

**Separation Strategy**

- Frontend = static HTML/JS. No private keys; signing is delegated to **Hive Keychain** in the browser. Hosted on free/static hosting (e.g., Geocities).
- Backend = **Flask** API with SQLite storage, background **order-matching** loop, and optional Hive integration (e.g., account validation, on-chain transfers, or audit trails) using **beem**.

**Deployment Shape**

- Recommended split: Frontend on Geocities (static), Backend on a VPS (Python/Flask). This keeps costs low, avoids storing keys server-side, and keeps the API controlled.

---

## 2) Frontend Deep Dive

### `index.html` β€” User auth & balance management (+ CORS awareness)

**What it does**

- Detects **Hive Keychain** and toggles UI accordingly.
- Fetches and renders: supported pairs, and the user’s balances (via backend).
- Relies on the backend to be CORS-enabled (or you use an allowed-origin policy).

**Example (minimal pattern)**

    <!-- index.html core behavior (conceptual) -->
    <script src="./config.js"></script>
    <script>
      const hasKeychain = typeof window.hive_keychain !== 'undefined';
      const username = localStorage.getItem('pek_user') || '';

      // Fetch available pairs for the UI
      fetch(`${CONFIG.API_BASE_URL}/api/pairs`)
        .then(r => r.json())
        .then(pairs => renderPairs(pairs))
        .catch(console.error);

      // Fetch balances (if we have a username)
      async function loadBalances() {
        if (!username) return;
        const r = await fetch(`${CONFIG.API_BASE_URL}/api/balances?user=${encodeURIComponent(username)}`);
        if (r.ok) {
          const data = await r.json();
          renderBalances(data);
        }
      }

      // CORS note:
      // The backend enables CORS. If you reverse-proxy or use HTTPS,
      // configure Access-Control-Allow-Origin to your Geocities domain.
    </script>

### `pairs/pair.html` β€” Trading interface, order execution, Keychain integration

**What it does**

- Parses `?base=PEK&quote=SWAP.HIVE` from the URL.
- Renders order form (side/price/amount).
- Pulls live orderbook + trade history from the backend.
- Uses **Hive Keychain** to sign the user’s intent (no private keys in JS).

**Example (placing an order with Keychain)**

    <script src="../config.js"></script>
    <script>
      const params = new URLSearchParams(location.search);
      const base  = params.get('base')  || 'PEK';
      const quote = params.get('quote') || 'SWAP.HIVE';

      async function placeOrder(side, price, amount, username) {
        if (!window.hive_keychain) {
          alert('Hive Keychain not detected. Please install/enable it.');
          return;
        }

        // Build an order intent payload (signed by Keychain).
        const intent = { base, quote, side, price, amount, username, ts: Date.now() };

        // Example: request a custom_json signature for audit/authorization
        // (You can adapt to your preferred on-chain pattern.)
        hive_keychain.requestCustomJson(
          username,
          'ssc-mainnet-hive',         // Hive-Engine domain tag
          'Active',                   // needs active for market ops
          JSON.stringify(intent),     // your order intent payload
          'Place PEK DEX order',
          async (res) => {
            if (!res || !res.success) {
              alert(`Keychain rejected: ${res && res.message ? res.message : 'unknown error'}`);
              return;
            }
            // Send the signed order to backend for validation + matching
            const r = await fetch(`${CONFIG.API_BASE_URL}/api/order`, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ intent, keychain_result: res })
            });
            const out = await r.json();
            out.ok ? notify('Order accepted!') : alert(out.error || 'Order failed.');
          }
        );
      }
    </script>

### `config.js` β€” Configuration management

**Shape**

    // frontend/config.js
    const CONFIG = {
      API_BASE_URL: 'http://YOUR_SERVER_IP:8080'
    };

- Change `API_BASE_URL` to your Flask server’s address/port.
- Keep this file under `frontend/` so it’s easy to switch environments.

---

## 3) Backend Implementation

### `app.py` β€” All API endpoints, matching, optional blockchain ops

**What it exposes (typical set)**

- `GET /api/pairs` β€” list supported pairs
- `GET /api/orderbook?base=PEK&quote=SWAP.HIVE` β€” current bids/asks
- `GET /api/history?base=PEK&quote=SWAP.HIVE` β€” recent trades
- `GET /api/balances?user=<name>` β€” simplified balance view
- `POST /api/order` β€” place a signed order intent
- FTP admin utilities (optional, controlled): `/api/ftp/...` for backup/import

**Skeleton (conceptual)**

    # backend/app.py
    import os, sqlite3, threading, time, json
    from flask import Flask, request, jsonify
    from flask_cors import CORS

    app = Flask(__name__)
    CORS(app)  # allow your Geocities origin

    DB_PATH = os.getenv('PEK_DB_PATH', 'dex.db')
    def _db():
      return sqlite3.connect(DB_PATH, check_same_thread=False)

    @app.get('/api/pairs')
    def pairs():
      # Could also be dynamic from a table
      return jsonify(["PEK/SWAP.HIVE", "PEK/SWAP.BTC", "PEK/SWAP.LTC", "PEK/SWAP.ETH", "PEK/SWAP.DOGE"])

    @app.get('/api/orderbook')
    def orderbook():
      base  = request.args.get('base', 'PEK')
      quote = request.args.get('quote', 'SWAP.HIVE')
      with _db() as cx:
        bids = cx.execute("SELECT price, amount FROM orders WHERE base=? AND quote=? AND side='buy'  AND status='open' ORDER BY price DESC", (base, quote)).fetchall()
        asks = cx.execute("SELECT price, amount FROM orders WHERE base=? AND quote=? AND side='sell' AND status='open' ORDER BY price ASC",  (base, quote)).fetchall()
      return jsonify({ "bids": [dict(price=p, amount=a) for (p,a) in bids],
                       "asks": [dict(price=p, amount=a) for (p,a) in asks] })

    @app.get('/api/history')
    def history():
      base  = request.args.get('base', 'PEK')
      quote = request.args.get('quote', 'SWAP.HIVE')
      with _db() as cx:
        rows = cx.execute("SELECT ts, price, amount, taker_side FROM trades WHERE base=? AND quote=? ORDER BY id DESC LIMIT 200", (base, quote)).fetchall()
      return jsonify([dict(ts=ts, price=pr, amount=am, taker_side=side) for (ts, pr, am, side) in rows])

    @app.get('/api/balances')
    def balances():
      user = request.args.get('user', '')
      if not user:
        return jsonify([])

      with _db() as cx:
        # Simplified: internal ledger (off-chain) balances from fills/deposits
        rows = cx.execute("SELECT token, amount FROM balances WHERE username=?", (user,)).fetchall()
      return jsonify([dict(token=t, amount=a) for (t,a) in rows])

    @app.post('/api/order')
    def place_order():
      data = request.get_json(force=True) or {}
      intent = data.get('intent', {})
      kcres  = data.get('keychain_result', {})

      # 1) Validate inputs
      ok, msg = validate_intent(intent)
      if not ok:
        return jsonify({"ok": False, "error": msg}), 400

      # 2) Validate Keychain signature/format (shape, username matches, etc.)
      ok, msg = validate_keychain_result(kcres, intent)
      if not ok:
        return jsonify({"ok": False, "error": msg}), 400

      # 3) Insert order; matcher thread will execute fills
      with _db() as cx:
        cx.execute("""INSERT INTO orders (username, base, quote, side, price, amount, status, ts)
                      VALUES (?,?,?,?,?,?, 'open', strftime('%s','now'))""",
                   (intent['username'], intent['base'], intent['quote'], intent['side'],
                    float(intent['price']), float(intent['amount'])))
        cx.commit()
      return jsonify({"ok": True})

    # --- Matching loop (very simple example) ---
    def match_loop():
      while True:
        try:
          with _db() as cx:
            # Find best bid/ask and cross if possible
            best_bid = cx.execute("SELECT id, price, amount FROM orders WHERE side='buy'  AND status='open' ORDER BY price DESC, id ASC LIMIT 1").fetchone()
            best_ask = cx.execute("SELECT id, price, amount FROM orders WHERE side='sell' AND status='open' ORDER BY price ASC,  id ASC LIMIT 1").fetchone()
            if best_bid and best_ask and best_bid[1] >= best_ask[1]:
              # Execute at midpoint or ask price (policy choice)
              exec_price = best_ask[1]
              filled_amt = min(best_bid[2], best_ask[2])
              # ... update orders/trades/balances ...
              # (Omitted for brevity; see DB schema below)
              pass
        except Exception as e:
          print("matcher error:", e)
        time.sleep(1)

    threading.Thread(target=match_loop, daemon=True).start()

    if __name__ == "__main__":
      app.run(host="0.0.0.0", port=8080)

### Database schema & SQLite usage

**Core tables (example DDL)**

    -- Pairs you enable
    CREATE TABLE IF NOT EXISTS pairs (
      id INTEGER PRIMARY KEY,
      base TEXT NOT NULL,
      quote TEXT NOT NULL,
      UNIQUE(base, quote)
    );

    -- Orders
    CREATE TABLE IF NOT EXISTS orders (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT NOT NULL,
      base TEXT NOT NULL,
      quote TEXT NOT NULL,
      side TEXT CHECK(side IN ('buy','sell')) NOT NULL,
      price REAL NOT NULL,
      amount REAL NOT NULL,
      status TEXT CHECK(status IN ('open','filled','canceled','partial')) NOT NULL DEFAULT 'open',
      ts INTEGER NOT NULL
    );

    -- Trades (executions)
    CREATE TABLE IF NOT EXISTS trades (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      base TEXT NOT NULL,
      quote TEXT NOT NULL,
      price REAL NOT NULL,
      amount REAL NOT NULL,
      taker_side TEXT CHECK(taker_side IN ('buy','sell')) NOT NULL,
      ts INTEGER NOT NULL
    );

    -- Ledger balances (off-chain internal accounting)
    CREATE TABLE IF NOT EXISTS balances (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT NOT NULL,
      token TEXT NOT NULL,
      amount REAL NOT NULL,
      UNIQUE(username, token)
    );

    -- Optional: config for FTP backups
    CREATE TABLE IF NOT EXISTS ftp_config (
      id INTEGER PRIMARY KEY,
      host TEXT, user TEXT, password TEXT, path TEXT
    );

**Multi-account strategy per trading pair**

- Export different env vars for each quote asset. The backend chooses the correct key/account per pair.

    export PEK_SWAP_HIVE_KEY="your_active_key"
    export PEK_SWAP_BTC_KEY="your_active_key"
    export PEK_SWAP_LTC_KEY="your_active_key"
    export PEK_SWAP_ETH_KEY="your_active_key"
    export PEK_SWAP_DOGE_KEY="your_active_key"

In `app.py`, map `(base, quote)` β†’ the proper environment variable. This keeps operational separation across markets.

**Security features & validation (server-side)**

- Validate user/account name shape (regex), allowed charset, max lengths.
- Enforce numeric bounds for `price`/`amount`.
- Verify Keychain result username matches `intent.username`.
- Enforce per-IP / per-account rate limits (optional).
- Strict CORS (see below).

---

## 4) Blockchain Integration

**Hive Keychain implementation (frontend)**

- The UI asks Keychain to sign a **custom_json** intent (or specific market action), keeping private keys in the browser extension, not in your app.
- The signed response (`res`) includes a transaction id/metadata you can store alongside the order for auditing.

**Hive Engine API / beem (backend)**

- Use **beem** where needed (e.g., account existence checks, simple transfers, or logging). Keep all sensitive keys in **env vars**β€”never in code or the repo.
- If you later choose on-chain settlement (e.g., Hive-Engine `market` contract actions), adapt the payload to the canonical HE JSON format and verify with dry-runs in a sandbox/test account first.

**Balance tracking across multiple tokens**

- Internal (`balances` table) reflects fills and on/off-ramp events.
- If you credit/debit on-chain tokens, reconcile by recording tx ids (audit fields) to prove solvency.

---

## 5) Security Features

**Account validation**

- Check `intent.username` exists on Hive (via beem).
- Match Keychain-returned account to `intent.username`.

**Private key management**

- Never store private keys client-side or server-side.
- Server-only keys (for house ops) live in environment variables (per pair).

**Input sanitization**

- Validate everything (types, ranges, enums).
- Use parameterized SQL (as shown) to prevent injection.

**CORS protection**

- Enable CORS in Flask, but restrict `Access-Control-Allow-Origin` to your Geocities domain in production.
- Block credentials if not explicitly required.

---

## 6) Deployment Strategy

**Frontend β†’ Geocities**

- Upload all files from `frontend/`.
- Set `index.html` as the landing page.
- Edit `frontend/config.js` with your backend API URL.

**Backend server setup (Ubuntu example)**

    # 1) System packages
    sudo apt update && sudo apt install -y python3 python3-venv python3-pip

    # 2) Project
    git clone https://github.com/PaulMoon410/Peake_Dex.git
    cd Peake_Dex/backend
    python3 -m venv .venv && source .venv/bin/activate
    pip install -r requirements.txt

    # 3) Environment
    export PEK_DB_PATH="/var/lib/peke_dex/dex.db"
    export PEK_SWAP_HIVE_KEY="your_active_key"
    # ... set other pair keys as needed

    # 4) First run
    python app.py      # Ensure it starts locally (port 8080)

**systemd service**

    # /etc/systemd/system/peake-dex.service
    [Unit]
    Description=Peake DEX Flask API
    After=network.target

    [Service]
    User=www-data
    WorkingDirectory=/opt/Peake_Dex/backend
    Environment="PEK_DB_PATH=/var/lib/peke_dex/dex.db"
    Environment="PEK_SWAP_HIVE_KEY=your_active_key"
    # ...more keys per market...
    ExecStart=/opt/Peake_Dex/backend/.venv/bin/python /opt/Peake_Dex/backend/app.py
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target

    # Enable and start:
    sudo systemctl daemon-reload
    sudo systemctl enable peake-dex
    sudo systemctl start peake-dex
    sudo systemctl status peake-dex

**Optional nginx (reverse proxy)**

- Proxy `https://api.yourdomain.com` β†’ `http://127.0.0.1:8080`.
- Add strict `Access-Control-Allow-Origin` for your Geocities domain.

**FTP backup system (simple pattern)**

    # backend/ftp_backup.py (conceptual)
    from ftplib import FTP
    import os

    def ftp_upload(db_path, host, user, password, dest):
      with FTP(host) as ftp:
        ftp.login(user, password)
        with open(db_path, 'rb') as f:
          ftp.storbinary(f'STOR {dest}', f)

    # Use from a cronjob or a /api/ftp/upload admin endpoint.

---

## 7) Advanced Features

**Automated order matching**

- Background thread scans best bid/ask and executes crosses.
- Policy choices:
  - Match at ask, bid, or midpoint.
  - Partial fills: decrement remaining `amount` and keep order `open`.
  - Fees: add maker/taker fee logic if needed.

**Real-time price discovery**

- Simple approach: poll `/api/orderbook` and diff client-side.
- Advanced: server-sent events or WebSocket gateway for events.

**Database backup**

- Nightly cron to FTP/S3; keep rolling 7/30-day snapshots.
- Optionally export a CSV dump of orders/trades for transparency.

**Error handling strategies**

- Structured JSON errors: `{ ok: false, error: "message", code: "E_INPUT" }`
- Centralized `try/except` in the matcher; exponential backoff on DB locks.
- Health endpoint `/api/health` returning version, DB status, and last match tick.

---

## 🎯 Why This Matters (Key Benefits)

- **Real code patterns** you can paste and adapt now.
- **Security-first**: Keychain signing, no key storage, strict validation.
- **Scalable deployment**: Static frontend + slim Python backend.
- **DEX best practices**: matching loop, separate markets, auditability.
- **Complete path**: dev β†’ systemd β†’ backups β†’ (optional) reverse-proxy.

**Who this helps**

- **Developers**: understand and extend the codebase quickly.
- **Users**: see how balances and orders flow through the system.
- **Contributors**: where to add features (pairs, fees, sockets).
- **Ops/Deploy**: step-by-step service, env vars, and backups.

---

## πŸ”§ Quick Checklist Before Production

- [ ] Restrict CORS to your frontend origin.
- [ ] Enforce strict input validation + rate limits.
- [ ] Run the matcher with tests for partial fills & edge cases.
- [ ] Enable nightly FTP/S3 backups (DB + logs).
- [ ] Monitor with `systemd` + journald, and add `/api/health`.
- [ ] Keep your pair keys in env vars only (never in code/repo).



## Appendix: Minimal Local Smoke Test

    # Frontend: open frontend/index.html locally and set CONFIG.API_BASE_URL to:
    #   http://127.0.0.1:8080

    # Backend:
    cd Peake_Dex/backend
    python3 -m venv .venv && source .venv/bin/activate
    pip install -r requirements.txt
    export PEK_DB_PATH="./dex.db"
    python app.py

    # Now visit your local frontend, place a tiny test order,
    # and confirm it appears in /api/orderbook and /api/history.

πŸ‘  , , , , , , , , , , , , , , , , , , , , , , , ,
properties (23)
authorpaulmoon410
permlinkpeake-dex-full-architecture-deployment
categoryhive-186392
json_metadata"{"app":"ecency/4.2.2-vision","tags":["hive-186392","pimp","neoxian","waivio","proofofbrain","palnet","bee","leo","ctp","creativecoin"],"format":"markdown+html","image":["https://images.ecency.com/DQmX4VujQ4StCLPYjUqLXHZKBhGo6G4ERHARDHJAwLntAgd/image.png"],"thumbnails":["https://images.ecency.com/DQmX4VujQ4StCLPYjUqLXHZKBhGo6G4ERHARDHJAwLntAgd/image.png"],"description":"Complete Architecture Analysis File structure (at a glance) Peake_Dex/ β”œβ”€ frontend/ # Static site (Geocities-friendly) β”‚ β”œβ”€ index.html # Landing + balances/auth state β”‚ β”œβ”€ config.js # Backend base URL","image_ratios":["1.5006"]}"
created2025-08-10 03:46:21
last_update2025-08-10 03:46:21
depth0
children0
last_payout1969-12-31 23:59:59
cashout_time2025-08-17 03:46:21
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.302 HBD
promoted0.000 HBD
body_length18,122
author_reputation41,705,424,817,301
root_title"🧩 Peake DEX β€” Full Architecture & Deployment Guide (Docs + Tutorial)"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id144,849,943
net_rshares959,493,124,470
author_curate_reward""
vote details (25)