Order Book DEX Development: Smart Contracts and Architecture

Traders are used to limit orders and order books on CEX, but on DeFi exchanges with AMM this is unavailable — only market orders with slippage. We build an order book DEX that provides CEX liquidity without sacrificing decentralization. Our team delivers the project turnkey — from smart contract architecture to matching engine integration and ongoing support.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1335
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1293
  • B2B Advance company logo design
    B2B Advance company logo design
    738
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1031
  • AIDER company logo development
    AIDER company logo development
    978
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1087

Traders are used to limit orders and order books on CEX. But on DeFi exchanges with AMM, this is unavailable—only market orders with slippage. We build an order book DEX that provides CEX-like liquidity without sacrificing decentralization. Our experience: 10+ years in blockchain and over 50 projects, including integrations with AMM and L2.

Problems we solve

  • Gas costs of on-chain storage: each order is a transaction, each match is a transaction; at trading frequency, this is economically unviable—solution: off-chain matching engine + on-chain settlement.
  • Front-running and MEV: in the public mempool, orders are visible to all; bots can front-run your transaction—we use EIP-712 signatures with non-standard nonces, integration with Flashbots, and transaction batching.
  • Liquidity at launch: an empty order book scares traders—solutions: integration with AMM pools as fallback, RFQ service for institutional orders, market maker program with reduced fees.

Which order book model to choose?

Fully on-chain order book: order book stored and matched directly in a smart contract. Each order is a transaction. Gas cost high: placing, canceling, filling—all paid by trader. Latency ~12 seconds (Ethereum block). Front-running inevitable. Suitable only for auctions and batch settlement.

Off-chain order book + on-chain settlement: dominant model. Matching—off-chain (fast, free). Only the final trade is settled on-chain. Examples: dYdX v3 (Starkware), Serum (Solana). Off-chain matching outperforms fully on-chain by 10-100x in gas cost and 10x in speed.

Hybrid: off-chain orders + on-chain matching: orders signed off-chain (EIP-712), stored in off-chain order book, but matching executed on-chain at settlement. Example: 0x Protocol, Hashflow. Maker pays no gas for placement—only for execution.

Criteria On-chain Off-chain + on-chain settlement Hybrid
Gas cost High Low Medium
Latency ~12 sec <1 sec <1 sec
Complexity Low Medium High
Applicability Auctions Professional trading Market making

Smart contracts for settlement

EIP-712 order signatures:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract OrderBookSettlement {
    bytes32 public constant ORDER_TYPEHASH = keccak256(
        "Order(address maker,address taker,address makerToken,address takerToken,"
        "uint256 makerAmount,uint256 takerAmount,uint256 nonce,uint256 expiry)"
    );

    struct Order {
        address maker;
        address taker; // address(0) = any taker
        address makerToken;
        address takerToken;
        uint256 makerAmount;
        uint256 takerAmount;
        uint256 nonce;
        uint256 expiry;
    }

    mapping(address => mapping(uint256 => bool)) public usedNonces;
    mapping(bytes32 => uint256) public filledAmounts; // partial fills

    function fillOrder(
        Order calldata order,
        bytes calldata signature,
        uint256 takerFillAmount // for partial fills
    ) external {
        require(block.timestamp < order.expiry, "ORDER_EXPIRED");
        require(
            order.taker == address(0) || order.taker == msg.sender,
            "INVALID_TAKER"
        );

        bytes32 orderHash = getOrderHash(order);
        require(
            filledAmounts[orderHash] + takerFillAmount <= order.takerAmount,
            "OVERFILL"
        );

        // Verify EIP-712 signature
        address recovered = recoverSigner(orderHash, signature);
        require(recovered == order.maker, "INVALID_SIGNATURE");

        // Calculate maker amount proportional to partial fill
        uint256 makerFillAmount = (order.makerAmount * takerFillAmount) / order.takerAmount;
        filledAmounts[orderHash] += takerFillAmount;

        // Atomic swap
        IERC20(order.takerToken).transferFrom(msg.sender, order.maker, takerFillAmount);
        IERC20(order.makerToken).transferFrom(order.maker, msg.sender, makerFillAmount);

        emit OrderFilled(orderHash, order.maker, msg.sender, makerFillAmount, takerFillAmount);
    }
}

The EIP-712 specification defines the signature structure. Key security aspects: filledAmounts for partial fills, usedNonces against replay, mandatory expiry. For standard approve, a separate transaction is needed—Permit2 by Uniswap Labs solves this with a single signature.

How does the off-chain matching engine work?

This is a high-performance service similar to CEX matching, but with specifics:

  1. Orders are signed messages, not on-chain.
  2. Partial fills tracked off-chain and on-chain (mapping filledAmounts).
  3. Order cancellation: on-chain nonce or off-chain cancel with maker confirmation.
from dataclasses import dataclass
from decimal import Decimal
from typing import Optional
import asyncio

@dataclass
class SignedOrder:
    maker: str
    taker_token: str
    maker_token: str
    taker_amount: Decimal
    maker_amount: Decimal
    nonce: int
    expiry: int
    signature: str

    @property
    def price(self) -> Decimal:
        """Price in units of maker_token per taker_token"""
        return self.maker_amount / self.taker_amount

    @property
    def is_expired(self) -> bool:
        import time
        return time.time() > self.expiry

class OffChainOrderBook:
    def __init__(self, pair: str):
        self.pair = pair
        self.bids: list[SignedOrder] = []  # buy orders, sorted by price DESC
        self.asks: list[SignedOrder] = []  # sell orders, sorted by price ASC
        self._settlement_queue = asyncio.Queue()

    async def add_order(self, order: SignedOrder, side: str):
        if side == 'bid':
            self.bids.append(order)
            self.bids.sort(key=lambda x: x.price, reverse=True)
        else:
            self.asks.append(order)
            self.asks.sort(key=lambda x: x.price)
        await self.try_match()

    async def try_match(self):
        while self.bids and self.asks:
            best_bid = self.bids[0]
            best_ask = self.asks[0]
            if best_bid.is_expired:
                self.bids.pop(0)
                continue
            if best_ask.is_expired:
                self.asks.pop(0)
                continue
            if best_bid.price >= best_ask.price:
                # Match found
                fill_amount = min(best_bid.taker_amount, best_ask.taker_amount)
                await self._settlement_queue.put({
                    'bid': best_bid,
                    'ask': best_ask,
                    'fill_amount': fill_amount
                })
                # Update or remove filled orders
                best_bid.taker_amount -= fill_amount
                best_ask.taker_amount -= fill_amount
                if best_bid.taker_amount == 0:
                    self.bids.pop(0)
                if best_ask.taker_amount == 0:
                    self.asks.pop(0)
            else:
                break
"}

How much does batching save?

A batch of 10 fills in one transaction saves ~70% gas compared to 10 separate ones. The fillOrderBatch function makes this possible.

function fillOrderBatch(
    Order[] calldata orders,
    bytes[] calldata signatures,
    uint256[] calldata fillAmounts
) external {
    require(orders.length == signatures.length, "LENGTH_MISMATCH");
    for (uint256 i = 0; i < orders.length; i++) {
        fillOrder(orders[i], signatures[i], fillAmounts[i]);
    }
}

What do L2 and appchain offer?

Deploy: dYdX v4 on Cosmos appchain gives zero fees for traders and <1ms latency. Arbitrum / zkSync L2 reduce gas cost by 10-100x—on-chain orders become economical for volumes >$100. Starkware with validity proofs scales to 10,000+ trades/sec.

How to ensure liquidity at launch?

  • Integration with AMM: if no market maker, the router directs to an AMM pool (e.g., Uniswap V3).
  • Market maker program: reduced fees, credit line, priority matching.
  • RFQ: institutional traders request quotes directly from registered MMs.

Comparison with AMM

Criteria Order Book DEX AMM DEX
Capital efficiency High (no idle liquidity) Low (V2) / High (V3)
UX for traders Familiar, limit orders Simpler, market only
Market making Requires professionals Accessible to all LPs
Front-running risk High (without protection) Medium (sandwich)
Latency Depends on architecture One block
Complexity High Medium

Development process

  1. Analytics: choose blockchain, L2, matching model (on-chain/off-chain/hybrid).
  2. Design: architecture of smart contracts, off-chain services, API.
  3. Implementation: develop settlement contracts, matching engine, wallet integration.
  4. Testing: unit tests, integration tests, fuzzing (Echidna), testnet simulation.
  5. Audit: automated (Slither, Mythril) and manual code review.
  6. Deployment: phased rollout with monitoring.

What is included in the work

  • Architecture and API documentation
  • Access to source code of contracts and matching engine
  • Deployment and monitoring instructions
  • Team training on administration
  • Support for one month after deployment

Timelines: MVP in 2–3 months, a full system with L2 and RFQ from 6 months. Cost is calculated individually. Contact us for a consultation and get an estimate within 2 days. Order development to accelerate time-to-market.