# Overview > If you are an autonomous AI agent (OpenClaw, Claude, etc.) or building with one, visit [/docs/llms.txt](/docs/llms.txt) for a structured index or [/docs/llms-full.txt](/docs/llms-full.txt) for the full docs in one file. If you're a human, keep reading below. WoW Gold is a Solana swap routing engine that converts SOL into **$WOW** tokens (Token-2022) through a multi-layered obfuscation pipeline. The system manages thousands of wallets spread across multiple routing layers, creating transaction paths that are computationally infeasible to trace back to a single origin. ## How It Works At a high level, every swap follows the same journey. SOL enters through a unique deposit address, gets split across several CEX partners to break the on-chain link, then fans back together and travels through hundreds of hops across our wallet network. At the end, the routed SOL gets swapped into $WOW via Jupiter and delivered to the user's receiving wallets. ``` User SOL Deposit | v [Deposit Wallet] Entry point | v [CEX Partners] Multi-way split across exchanges / | \ v v v [Ex1] [Ex2] [Ex3] Roughly equal portions \ | / v v v [Fan-In Wallet] Convergence point | v [Routing Engine] 200+ hops across many layers | with re-loop segments / \ v v [Exit A] [Exit B] Dual 50/50 split | | v v [Sub-Route A/B] Additional hops per branch | | v v [Jupiter Ultra] SOL -> $WOW swap | | v v [Receivers 1 & 2] Final token delivery ``` ## Core Components | Component | What It Does | |-----------|-------------| | **WalletPool** | Manages the full pool of routing wallets with concurrent locking | | **CEX Integration** | Handles multi-way splitting across exchange partners | | **Route Engine** | Plans and executes multi-hop routing paths | | **Jupiter Client** | Ultra API for SOL-to-WOW token swaps | | **KeyStore** | AES-256-GCM encrypted wallet key management | ## Built With - **Language**: Rust - **Blockchain**: Solana (dedicated RPC + gRPC streaming) - **CEX**: Multiple exchange partners for deposit splitting - **Swap**: Jupiter Ultra API - **Token Standard**: Token-2022 (`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) ## What We Guarantee 1. **At-most-once delivery** -- each swap produces exactly one set of output tokens or a full refund 2. **Minimal lock footprint** -- only 2 wallets are locked per active swap at any given moment 3. **Crash recovery** -- every phase transition is persisted; the system picks up right where it left off 4. **Orphan protection** -- wallets holding stale funds are automatically detected and excluded from routing 5. **Dual confirmation** -- both streaming and polling confirm every transaction on-chain --- # Quick Start Get up and running in minutes. This guide walks through generating receiver wallets, creating a swap, and polling for completion. ## Install Dependencies **Python**: ```bash pip install requests solders ``` **JavaScript** (Node.js 18+): ```bash npm install @solana/web3.js ``` ## Step 1: Generate Receiver Wallets Every swap delivers tokens to two Solana wallets. Generate a keypair for each and save the secret key locally so you can actually access your tokens later. **Python**: ```python from solders.keypair import Keypair import json w1, w2 = Keypair(), Keypair() data = { "receiver_1": {"pubkey": str(w1.pubkey()), "secret": list(bytes(w1))}, "receiver_2": {"pubkey": str(w2.pubkey()), "secret": list(bytes(w2))}, } with open("wallets.json", "w") as f: json.dump(data, f, indent=2) print(f"Wallet 1: {w1.pubkey()}") print(f"Wallet 2: {w2.pubkey()}") ``` **JavaScript**: ```javascript import { Keypair } from "@solana/web3.js"; import { writeFileSync } from "fs"; const w1 = Keypair.generate(); const w2 = Keypair.generate(); writeFileSync("wallets.json", JSON.stringify({ receiver_1: { pubkey: w1.publicKey.toBase58(), secret: [...w1.secretKey] }, receiver_2: { pubkey: w2.publicKey.toBase58(), secret: [...w2.secretKey] }, }, null, 2)); console.log("Wallet 1:", w1.publicKey.toBase58()); console.log("Wallet 2:", w2.publicKey.toBase58()); ``` Both scripts create a `wallets.json` with your public and secret keys. Keep this file safe -- anyone with the secret key controls the wallet. ## Step 2: Create a Swap Pass your two receiver addresses to the API. You get back a deposit address and a swap ID. **Python**: ```python import requests, json wallets = json.load(open("wallets.json")) API = "https://your-api-url.com" resp = requests.post(f"{API}/api/swap", json={ "receiver_1": wallets["receiver_1"]["pubkey"], "receiver_2": wallets["receiver_2"]["pubkey"], "output_mode": "wow_token", }) swap = resp.json() print(f"Swap ID: {swap['swap_id']}") print(f"Send SOL to: {swap['deposit_address']}") ``` **JavaScript**: ```javascript import { readFileSync } from "fs"; const wallets = JSON.parse(readFileSync("wallets.json", "utf-8")); const API = "https://your-api-url.com"; const resp = await fetch(`${API}/api/swap`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ receiver_1: wallets.receiver_1.pubkey, receiver_2: wallets.receiver_2.pubkey, output_mode: "wow_token", }), }); const swap = await resp.json(); console.log("Swap ID:", swap.swap_id); console.log("Send SOL to:", swap.deposit_address); ``` Send SOL to the deposit address. The system picks up your deposit automatically and starts processing. ## Step 3: Poll for Status Use the swap ID to check progress. Poll every few seconds until it reaches a terminal state. **Python**: ```python import requests, time API = "https://your-api-url.com" SWAP_ID = "your-swap-id" while True: s = requests.get(f"{API}/api/swap/{SWAP_ID}").json() print(f"{s['status']} -- {s.get('progress_pct', 0)}%") if s["status"] in ("completed", "failed", "refunded"): break time.sleep(5) ``` **JavaScript**: ```javascript const API = "https://your-api-url.com"; const SWAP_ID = "your-swap-id"; while (true) { const s = await fetch(`${API}/api/swap/${SWAP_ID}`).then(r => r.json()); console.log(`${s.status} -- ${s.progress_pct ?? 0}%`); if (["completed", "failed", "refunded"].includes(s.status)) break; await new Promise(r => setTimeout(r, 5000)); } ``` ## About Swap IDs When you create a swap, the API hands back a UUID v4 like `a1b2c3d4-e5f6-7890-abcd-ef1234567890`. This is generated server-side -- you never need to create one yourself. The swap ID doubles as your authentication secret for checking status. Anyone who has it can see the swap's progress, so treat it like a password and don't share it publicly. --- # Swap Lifecycle Every swap walks through a deterministic state machine. Each transition gets persisted as a checkpoint, so if anything goes wrong the system can pick right back up from where it left off. ## Status Flow ``` awaiting_deposit | v (deposit detected) pending | v (CEX order created) cex_processing | v (all exchange legs complete, fan-in done) routing | v (route complete, Jupiter swap submitted) swapping | v (tokens transferred to receivers) delivering | v (confirmed on-chain) completed ``` ## When Things Go Wrong ``` any status ──> failed ──> refunding ──> refunded | v recovery_pending ──> (auto-retries) ──> resumes from checkpoint ``` The system distinguishes between pre-CEX and post-CEX failures. If something goes wrong before funds leave the deposit wallet, the swap simply resets to `awaiting_deposit` so the user can try again automatically. Post-CEX failures trigger the recovery pipeline to track down where the funds ended up and resume from there. ## Status Reference | Status | What's Happening | |--------|-----------------| | `awaiting_deposit` | Deposit address is ready, waiting for the user to send SOL | | `pending` | Deposit received, preparing the CEX order | | `cex_processing` | Funds are being split across exchange partners | | `routing` | Multi-hop routing is in progress | | `swapping` | Jupiter swap from SOL to $WOW | | `delivering` | Transferring $WOW tokens to receiver wallets | | `completed` | All tokens delivered and confirmed on-chain | | `failed` | Something went wrong that couldn't be auto-recovered | | `refunding` | Refund transaction in progress | | `refunded` | SOL has been returned to the deposit address | | `recovery_pending` | Automatic recovery is working on it | ## How Phase Transitions Work Each step in the pipeline follows the same pattern: 1. **Validate** -- check balances, wallet availability, preconditions 2. **Execute** -- perform the transfer, swap, or API call 3. **Confirm** -- verify on-chain via dual-path confirmation (streaming + polling) 4. **Persist** -- save the new status and checkpoint 5. **Release** -- unlock wallet locks from the previous phase This approach means the system always knows exactly where a swap stands, even after a restart. --- # Data Layer All swap state, wallet metadata, routing history, and transaction records are stored in a persistent database with compile-time query validation. This means query bugs get caught at build time rather than at runtime -- no surprises in production. ## What Gets Stored The data model revolves around four core entities: ### Wallets Every routing wallet in the pool is tracked with its public key, encrypted private key, layer assignment, and current status. The system knows at a glance which wallets are available, which are in use, and which have been flagged as orphaned. ### Swaps The main swap table tracks the full lifecycle of each swap -- from the moment a deposit address is assigned through to final delivery. It holds the current status, checkpoint, receiver addresses, amounts, and any error information needed for recovery. ### Route Steps Each individual hop in a swap's routing path gets its own record. This creates a complete audit trail of every transfer: source wallet, destination wallet, layer numbers, amounts, transaction signatures, and confirmation timestamps. This is what makes checkpoint recovery possible -- the system can look at the last confirmed hop and pick up from there. ### Transactions A full audit log of every on-chain transaction the system submits. Every transfer, swap, refund, and burn gets recorded with its Solana signature, slot number, and status. This serves as the ground truth for reconciliation. ## Key Design Decisions - **UUIDs everywhere** -- all primary keys are UUIDs, making them safe to expose in APIs without leaking ordering information - **Compile-time queries** -- every database query is validated at build time against the actual schema - **Timezone-aware timestamps** -- all timestamps include timezone info to avoid ambiguity - **Indexed for recovery** -- the schema is optimized for the queries the recovery system needs: finding swaps by status, looking up wallets by layer, and scanning route steps in order --- # API Endpoints The bot exposes a handful of HTTP endpoints. Public endpoints are open to anyone with the swap ID. Admin endpoints require an `x-api-key` header. --- ## GET /api/health A simple health check. Returns whether the service is running and what the current minimum deposit is. **Auth**: None **Response** `200 OK`: ```json { "status": "ok", "min_deposit_sol": 0.01, "min_deposit_lamports": 10000000 } ``` --- ## POST /api/swap Creates a new swap and returns a unique deposit address. Send SOL to that address to kick things off. The swap ID doubles as the secret for checking status -- keep it safe. **Auth**: None **Request Body**: ```json { "receiver_1": "So1ana...Addr1", "receiver_2": "So1ana...Addr2", "output_mode": "wow_token" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `receiver_1` | `string` | Yes | First receiver Solana address | | `receiver_2` | `string` | Yes | Second receiver Solana address | | `output_mode` | `string` | Yes | `wow_token` or `sol_direct` | **Response** `201 Created`: ```json { "swap_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "deposit_address": "DepositWa11etPubkeyBase58Here", "status": "awaiting_deposit" } ``` **Response** `409 Conflict` (active swap already exists for same receiver pair): ```json { "error": "Active swap already exists for this receiver pair", "existing_swap_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "deposit_address": "DepositWa11etPubkeyBase58Here" } ``` The deposit address is deterministic -- the same receiver pair will always get the same deposit wallet. This is computed via a hash of the sorted receiver addresses. --- ## GET /api/swap/{id} Check how a swap is doing. The swap UUID itself acts as the authentication secret, so don't share it publicly. **Auth**: None (UUID-as-secret) **Path Parameter**: `id` -- the swap UUID from POST /api/swap **Response** `200 OK`: ```json { "swap_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "routing", "deposit_address": "DepositWa11etPubkeyBase58Here", "amount_lamports": 500000000, "burn_amount": 12500000, "output_mode": "wow_token", "error_message": null, "created_at": "2025-01-15T10:30:00Z", "progress_pct": 45, "phase": "routing", "completed_hops": 112, "total_hops": 248 } ``` **Response** `404 Not Found`: ```json { "error": "Swap not found" } ``` --- ## GET /api/swaps Lists all swaps. This is an admin-only endpoint meant for monitoring and debugging. **Auth**: `x-api-key` header (required) **Response** `200 OK`: ```json { "swaps": [ { "swap_id": "a1b2c3d4-...", "status": "completed", "deposit_address": "...", "amount_lamports": 500000000, "burn_amount": 12500000, "output_mode": "wow_token", "error_message": null, "created_at": "2025-01-15T10:30:00Z" } ] } ``` **Response** `401 Unauthorized`: ```json { "error": "Invalid or missing API key" } ``` --- ## POST /api/swap/{id}/refund Trigger a refund for a swap that's either failed or still waiting for a deposit. Returns SOL back to the deposit address. **Auth**: `x-api-key` header (required) **Path Parameter**: `id` -- the swap UUID **Response** `200 OK`: ```json { "status": "refunded", "refund_amount_lamports": 495000000, "refund_to": "DepositWa11etPubkeyBase58Here", "tx_signature": "5KtR...sig" } ``` **Response** `400 Bad Request`: ```json { "error": "Swap is not in a refundable state. Current status: routing" } ``` Only swaps with status `failed` or `awaiting_deposit` can be refunded. --- # Wallet System The wallet system sits at the heart of the routing engine. It manages thousands of wallets distributed across multiple layers in a diamond-shaped pattern, plus a smaller set of exchange wallets for the CEX integration. ## Diamond Distribution Wallets aren't evenly distributed -- they follow a diamond shape. The outer layers (entry and exit points) have fewer wallets, while the middle layers have the highest density. This makes sense because the middle is where most of the routing action happens, so you want maximum wallet diversity there. ``` Outer layers: ████ Fewer wallets ██████ █████████ ... Middle layers: ████████████████████████ Peak density ... █████████ ██████ Outer layers: ████ Fewer wallets Exchange: ██ Small dedicated set ``` ## How Wallet Selection Works When the routing engine needs to pick a wallet for the next hop, it draws randomly from the available wallets in the target layer. The selection automatically skips any wallets that are currently locked by another swap or flagged as orphaned. For deposit wallets, selection is deterministic -- we hash the sorted receiver addresses and use that to pick a consistent entry point. Same receivers always get the same deposit address. ## Concurrent Locking Each wallet can only be used by one swap at a time. The locking system tracks which swap holds which wallet, and at any given moment each active swap only locks **two wallets**: the current source and destination. As soon as a hop confirms, the source gets released immediately. This means even with many concurrent swaps running, the vast majority of the wallet pool stays available. ## Orphan Protection Sometimes a wallet ends up holding SOL that doesn't belong to any active swap. This can happen if a swap fails mid-route and recovery can't cleanly trace the funds, or if a transaction confirms after a swap has already been refunded. The system detects these orphaned wallets automatically and excludes them from the routing pool. They get flagged for manual review rather than risking mixing those funds into a new swap. ## KeyStore Private keys never sit around in plaintext. Every wallet's key is encrypted at rest using AES-256-GCM with a unique nonce. The system uses separate encryption keys for routing wallets versus exchange wallets, adding another layer of isolation. When a transaction needs to be signed, the key is decrypted just for that moment and discarded immediately after. Keys are never held in memory longer than it takes to sign a single transaction. --- # CEX Layer The CEX layer is what breaks the on-chain link between a user's deposit and the routing network. Instead of moving SOL directly from the deposit wallet into routing, we split it across multiple cryptocurrency exchanges first. ## How the Split Works When a deposit confirms, the system creates an order that divides the SOL into roughly equal portions and sends each one through a different exchange. Each portion withdraws to a separate entry-layer wallet, creating multiple independent on-chain origins that have no visible connection to each other. ``` Deposit: 1.0 SOL | +---> Exchange A: ~0.33 SOL -> Entry Wallet A +---> Exchange B: ~0.33 SOL -> Entry Wallet B +---> Exchange C: ~0.33 SOL -> Entry Wallet C ``` ## Order Flow The process is straightforward: 1. **Get a quote** -- the system requests current rates and fees for splitting the deposit amount 2. **Submit the order** -- specifies which exchanges to use and where each portion should withdraw to 3. **Poll for completion** -- checks order status periodically until all legs finish their withdrawals Exchange withdrawals aren't instant -- they can take anywhere from a few seconds to several minutes depending on the exchange's processing queue. The system is patient about this and uses exponential backoff to avoid hammering the API. ## Fan-In Once all exchange legs complete their withdrawals, the entry-layer wallets each hold a portion of the original deposit. The fan-in phase consolidates these back into a single wallet that becomes the starting point for the main routing path. ``` [Entry Wallet A] --\ +--> [Fan-In Wallet] --> [Main Route Begins] [Entry Wallet B] --/ / [Entry Wallet C] / ``` Each fan-in transfer is a separate on-chain transaction with a few intermediate hops for additional obfuscation. ## Retry Strategy CEX operations use exponential backoff with jitter. Since exchange operations are inherently slower than on-chain transfers, the retry limits are more generous -- the system will keep trying for much longer before giving up on a CEX order compared to a routing hop. --- # Routing Engine The routing engine is where the magic happens. It plans and executes multi-hop SOL transfers across the wallet network, creating paths complex enough that tracing them back to a single origin is computationally infeasible. ## Route Planning Every route is generated fresh with randomized parameters. The planner takes a configuration that defines the target hop count, maximum layer depth, number of re-loop segments, and how many lateral hops to include. Within those bounds, it builds a unique path every time. ### What a Route Looks Like A typical route follows this general shape: ``` Fan-In Phase: Entry wallets -> intermediate hops -> Fan-In wallet Main Route: Layer 1 -> Layer 2 -> Layer 3 -> ... -> deeper layers -> re-loop back a few layers (surprise!) -> resume forward -> continue deeper -> another re-loop -> push through to exit layers Exit Split (50/50): Exit -> [Branch A] -> many hops -> [Jupiter Swap] Exit -> [Branch B] -> many hops -> [Jupiter Swap] ``` ### Re-Loop Segments This is one of the more clever parts of the routing. Instead of just going forward through the layers in a straight line, the route periodically loops back to layers it has already passed through. This means the same layer shows up multiple times in the path, making automated analysis significantly harder -- an observer can't just follow the "forward" direction to trace the flow. ### Intra-Layer Hops At certain points, the route makes lateral transfers between wallets within the same layer before moving on. This increases the fan-out at each layer and makes it ambiguous which wallet is actually continuing the route. ## How Execution Works The executor processes each hop one at a time: 1. Pick a random available wallet in the target layer 2. Lock both the source and destination wallets 3. Submit the SOL transfer 4. Wait for on-chain confirmation (dual-path: streaming + polling) 5. Unlock the source wallet 6. Record the completed hop 7. Brief random delay before the next hop The delay between hops is randomized to avoid creating a predictable timing pattern that could be used for analysis. ## Lock Footprint At any given moment, each active swap only holds locks on **2 wallets** -- the current source and destination. After each hop confirms, the source is released and a new destination is picked. This keeps the lock footprint tiny relative to the total pool, so many swaps can run concurrently without stepping on each other. ## Dual Exit Split At the end of the main route, the funds split 50/50 into two branches. Each branch runs its own independent sub-route with additional hops before arriving at Jupiter for the final swap. This means even the exit phase doesn't create a single traceable path. --- # Jupiter & Token Swaps The final phase of the pipeline converts routed SOL into **$WOW** tokens using the Jupiter Ultra API, with a 2.5% burn built into every swap. ## $WOW Token | Property | Value | |----------|-------| | Standard | Token-2022 (Token Extensions) | | Program | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | | Burn Rate | 2.5% of swap output | | Delivery | Split across 2 receiver wallets | Worth noting: $WOW uses Token-2022, not the legacy SPL Token program. All token operations -- transfers, account creation, burns -- go through the Token-2022 program ID. ## Jupiter Ultra Flow Jupiter Ultra gives us a clean three-step process: 1. **Create Order** -- request a swap quote for the SOL amount, specifying the $WOW mint as the output 2. **Sign Transaction** -- sign the swap transaction with the source wallet's key 3. **Execute** -- submit the signed transaction and wait for confirmation ## Output Modes ### wow_token (default) SOL gets swapped to $WOW tokens. 2.5% of the output is burned, and the rest goes to the receiver wallet. ``` SOL Amount (per exit branch) | v [Jupiter Ultra Swap] | v $WOW Tokens Received | +---> Burn: 2.5% +---> Deliver: 97.5% -> Receiver Wallet ``` ### sol_direct Alternative mode. The 2.5% burn is computed on the SOL amount, a small portion gets swapped to $WOW just for burning, and the rest of the SOL goes directly to receivers without a token swap. ``` SOL Amount (per exit branch) | +---> 2.5% -> Jupiter Swap -> $WOW -> Burn +---> 97.5% -> Direct SOL transfer -> Receiver ``` ## Token Account Handling Before delivering $WOW tokens, the system checks whether the receiver has a Token-2022 Associated Token Account for the $WOW mint. If not, it creates one. The rent cost for ATA creation (~0.002 SOL) gets deducted from the swap amount. If the receiver already has an ATA, this step is skipped entirely. ## Error Handling | Scenario | What Happens | |----------|-------------| | Jupiter order creation fails | Retry with backoff (up to 5 attempts) | | Transaction signing fails | Swap marked as failed (likely a key issue) | | Execution times out | Check on-chain status, retry if unconfirmed | | Slippage too high | Retry with a fresh quote | | ATA creation fails | One retry, then fail the swap | --- # Reliability & Recovery The system is designed to handle failures gracefully. Crashes, network hiccups, RPC timeouts, exchange delays -- none of these should result in lost funds. ## Deposit Detection Incoming deposits are detected through two independent channels that run in parallel: a real-time transaction stream for immediate detection and periodic RPC polling as a fallback. Both channels need to agree before a deposit is considered confirmed. This dual-path approach means deposits get picked up quickly under normal conditions and reliably even when one channel has issues. ## Automatic Recovery The system continuously monitors for swaps that seem stuck -- anything that's been sitting in a non-terminal status for too long gets flagged and the system attempts to resume it automatically. How recovery works depends on where the swap got stuck: - **Before CEX processing**: Safe to simply reset the swap. The funds are still sitting in the deposit wallet, so the user doesn't need to do anything -- the swap will retry on its own. - **During or after CEX processing**: The system needs to figure out where the funds actually ended up. It checks the last known position, scans backwards through confirmed route steps, and verifies on-chain balances. Once it locates the funds, it resumes the route from that point. ## Finding Lost Funds When recovery can't immediately tell where funds are, it uses a multi-strategy approach: 1. Check the last wallet the system recorded as holding funds 2. Walk backwards through the routing history, checking each wallet's actual on-chain balance 3. Check exchange wallets for unexpected balances that match the swap amount 4. Trace forward from the deposit wallet through on-chain transaction history In practice, strategy 1 or 2 almost always finds the funds. The later strategies are there as safety nets for edge cases. ## Timeout Thresholds Each phase has its own timeout. If a swap exceeds the threshold for its current status, automatic recovery kicks in. The timeouts are tuned to be generous enough that normal processing never triggers them, but tight enough that genuinely stuck swaps get caught within minutes. ## When Auto-Recovery Fails If automated recovery exhausts its retry limit, the swap is marked as `failed` and flagged for manual review. At that point, an admin can either investigate and manually resume the swap or trigger a refund via the API. No funds are ever lost -- they're always sitting in a wallet the system controls. --- # Deployment All configuration is managed through environment variables. The system validates everything it needs at startup and will refuse to start if any required variable is missing. ## Configuration The bot needs the following to run: - **Database connection** -- where to store swap state and wallet data - **Solana RPC endpoint** -- for submitting transactions and checking balances - **gRPC streaming endpoint** -- for real-time transaction monitoring - **CEX API credentials** -- for the exchange splitting integration - **Encryption keys** -- separate AES-256 keys for routing wallets and exchange wallets - **Admin API key** -- for protecting admin-only endpoints - **$WOW token mint** -- the mint address for the target token There are also optional settings for tuning things like hop delays, retry limits, burn rate, minimum deposit threshold, and server port. Sensible defaults are built in for all of these. ## Startup Sequence When the bot starts up, it goes through a predictable sequence: 1. Parse and validate all environment variables 2. Connect to the database and run any pending migrations 3. Load all wallets into the in-memory pool 4. Initialize the keystore with encryption keys 5. Connect to the real-time transaction stream 6. Start background monitoring tasks 7. Resume any swaps that were mid-recovery when the bot last shut down 8. Start the HTTP server and begin accepting requests The whole process is logged so you can see exactly where things are at during startup. If anything fails, you'll know which step went wrong. ## Logging The bot uses structured logging with configurable verbosity. In production, `info` level gives you a good picture of what's happening without drowning you in noise. Bump it to `debug` or `trace` when you need to dig into specific issues.