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-like liquidity without sacrificing decentralization. Our experience: 10+ years in blockchain and over 50 project

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

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.