DeFi Orderbook — DEX Order Book

Repo: syed-ghufran-hassan/orderbook · 1 contract · ~85 lines
Audited: February 21, 2026

Overview

A minimal DeFi orderbook on Stacks allowing users to deposit balances, place orders, and fill orders with partial-fill support. The contract demonstrates basic Clarity map operations but implements a simulated orderbook with no real token transfers — deposits create balance from nothing.

Findings Summary

SeverityCountDescription
CRITICAL2Phantom deposits, no withdrawal
HIGH2Single-dimension trade, no cancel
MEDIUM3Zero amounts, no price, fill edge case
LOW2No events, no cleanup

Critical Findings

C-01 Deposit Creates Tokens From Nothing

The deposit function increments an internal balance map without any stx-transfer? or token transfer. Users can call deposit with any amount to create unlimited fake balance.

(define-public (deposit (amount uint))
    (begin
        (map-set balances {user: tx-sender}
            (+ (default-to u0 (map-get? balances {user: tx-sender})) amount))
        (ok true)))

The entire economic model is meaningless — anyone can mint unlimited balance with zero cost.

Fix: Add (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) before crediting the balance.

C-02 No Withdrawal Function — Funds Permanently Locked

There is no withdraw function. If the contract ever held real STX (after fixing C-01), deposited funds could never be recovered. Users have no exit path.

Fix: Add a withdraw function that debits the balance map and transfers STX back to the user.

High Findings

H-01 Single-Dimension Orderbook — Circular Trade

When a taker fills an order, the contract transfers the taker's balance to the maker. But there's only one token dimension — there is no "quote" asset. Filling an order just moves the same token back to the maker, making trades economically circular (taker pays X of token A, maker receives X of token A).

A real orderbook needs two assets: base-token and quote-token, with orders specifying a price (ratio between them).

H-02 No Cancel-Order Function

Once an order is placed via add-order, the maker's balance is locked in the order with no way to cancel and reclaim it. The only recovery is having someone else fill the order — which just returns the same token type (see H-01).

Fix: Add cancel-order that verifies (is-eq tx-sender (get trader order)), returns locked amount to maker's balance, and sets active: false.

Medium Findings

M-01 Zero-Amount Orders and Fills Allowed

Neither add-order nor fill-order validates that amounts are greater than zero. Users can create zero-amount orders (wasting map storage) and zero-amount fills (no-op transactions that still modify state).

Fix: Add (asserts! (> amount u0) err-invalid-amount) to both functions.

M-02 No Price Discovery Mechanism

Orders contain only {trader, amount, active} — no price field. Without price, there's no bid/ask spread, no matching engine, and no price discovery. This is effectively a FIFO queue, not an orderbook.

A functional orderbook needs at minimum: price: uint, side: (string-ascii 4) ("buy"/"sell"), and a matching algorithm.

M-03 Partial Fill Edge Case With Zero Fills

Since zero fills are allowed (M-01), the fill-order active-status check works correctly but allows no-op fills that still trigger state writes:

(let ((new-amount (- (get amount order) fill-amount))     ;; unchanged
      (new-active (if (>= fill-amount (get amount order))  ;; still true
                      false true)))
    (map-set orders ...)  ;; unnecessary write

Low Findings

L-01 No Events or Print Statements

No print statements for deposits, order creation, fills, or any state changes. Off-chain indexers and UIs cannot track contract activity without polling every block.

L-02 No Order Expiry or Cleanup

Filled orders remain in the map permanently with active: false. No mechanism exists to clean up stale entries or set expiry times on orders. Over time this wastes on-chain storage.

Architecture Assessment

The contract demonstrates basic Clarity patterns (maps, variables, assertions, partial fills) but falls far short of a functional DEX:

FeatureStatus
Real token transfers (STX or SIP-010)❌ Missing — phantom balances
Two-sided market (base/quote)❌ Single dimension
Price / matching engine❌ No price field
Order cancellation❌ No cancel function
Withdrawal❌ No withdraw function
Balance tracking✅ Internal map
Order creation✅ With counter
Partial fills✅ Correct accounting
Read-only queries✅ get-order, get-balance

Verdict: Proof-of-concept / learning exercise demonstrating Clarity fundamentals. The partial-fill logic is correctly implemented, but the contract cannot serve as a real DEX without token transfers, price discovery, and a two-asset model. Not suitable for any value transfer.