Decentralized Exchange (DEX)
Building a simple decentralized exchange for token trading.
Advanced⏱️ 60 min📚 Prerequisites: 1
Decentralized Exchange (DEX)
A Decentralized Exchange allows users to trade tokens without a central authority. Let's build a simple order book DEX.
DEX Components
- Order Book: Buy and sell orders
- Matching Engine: Matches orders
- Liquidity Pool: Alternative AMM model
- Trade Execution: Execute matched trades
Order Structure
RUSTenum OrderType { Buy, Sell, } struct Order { id: String, trader: String, order_type: OrderType, token_pair: (String, String), // (base, quote) price: u64, amount: u64, filled: u64, }
Order Book
RUSTstruct OrderBook { buy_orders: Vec<Order>, // Sorted by price (highest first) sell_orders: Vec<Order>, // Sorted by price (lowest first) } impl OrderBook { fn add_order(&mut self, order: Order) { match order.order_type { OrderType::Buy => self.buy_orders.push(order), OrderType::Sell => self.sell_orders.push(order), } self.match_orders(); } fn match_orders(&mut self) { // Match highest buy with lowest sell // Execute trades when prices overlap } }
Automated Market Maker (AMM)
Alternative to order books:
RUSTstruct LiquidityPool { token_a: u64, token_b: u64, total_supply: u64, } impl LiquidityPool { fn swap(&mut self, token_in: String, amount_in: u64) -> u64 { // Constant product formula: x * y = k // Calculate amount_out based on pool reserves } }
Trade Execution
RUSTfn execute_trade(buy_order: &mut Order, sell_order: &mut Order) -> Trade { let trade_amount = buy_order.amount.min(sell_order.amount); let trade_price = sell_order.price; // Use sell price // Update orders buy_order.filled += trade_amount; sell_order.filled += trade_amount; Trade { buy_order_id: buy_order.id.clone(), sell_order_id: sell_order.id.clone(), amount: trade_amount, price: trade_price, } }
DEX Features
- No KYC: Permissionless trading
- Non-custodial: Users control funds
- Transparent: All trades on-chain
- Liquidity: Provided by users
Implementation Considerations
- Slippage: Price impact of large trades
- Front-running: MEV (Maximal Extractable Value)
- Gas optimization: Minimize on-chain operations
- Liquidity incentives: Reward liquidity providers
Code Examples
Order Book
Basic order book implementation
RUST
enum OrderType {
Buy,
Sell,
}
struct Order {
id: String,
trader: String,
order_type: OrderType,
price: u64,
amount: u64,
}
struct OrderBook {
buy_orders: Vec<Order>,
sell_orders: Vec<Order>,
}
impl OrderBook {
fn new() -> Self {
OrderBook {
buy_orders: Vec::new(),
sell_orders: Vec::new(),
}
}
fn add_order(&mut self, order: Order) {
match order.order_type {
OrderType::Buy => {
self.buy_orders.push(order);
self.buy_orders.sort_by(|a, b| b.price.cmp(&a.price));
},
OrderType::Sell => {
self.sell_orders.push(order);
self.sell_orders.sort_by(|a, b| a.price.cmp(&b.price));
},
}
}
fn get_best_buy(&self) -> Option<&Order> {
self.buy_orders.first()
}
fn get_best_sell(&self) -> Option<&Order> {
self.sell_orders.first()
}
}
fn main() {
let mut book = OrderBook::new();
book.add_order(Order {
id: String::from("buy1"),
trader: String::from("0xAlice"),
order_type: OrderType::Buy,
price: 100,
amount: 10,
});
book.add_order(Order {
id: String::from("sell1"),
trader: String::from("0xBob"),
order_type: OrderType::Sell,
price: 95,
amount: 5,
});
if let Some(best_buy) = book.get_best_buy() {
println!("Best buy: {} at price {}", best_buy.amount, best_buy.price);
}
if let Some(best_sell) = book.get_best_sell() {
println!("Best sell: {} at price {}", best_sell.amount, best_sell.price);
}
}Explanation:
An order book maintains buy and sell orders sorted by price. The best buy (highest price) and best sell (lowest price) can be matched when prices overlap.
AMM Liquidity Pool
Automated Market Maker pool
RUST
struct LiquidityPool {
token_a_reserve: u64,
token_b_reserve: u64,
}
impl LiquidityPool {
fn new(amount_a: u64, amount_b: u64) -> Self {
LiquidityPool {
token_a_reserve: amount_a,
token_b_reserve: amount_b,
}
}
fn swap_a_for_b(&mut self, amount_a_in: u64) -> u64 {
let k = self.token_a_reserve * self.token_b_reserve;
self.token_a_reserve += amount_a_in;
let new_b_reserve = k / self.token_a_reserve;
let amount_b_out = self.token_b_reserve - new_b_reserve;
self.token_b_reserve = new_b_reserve;
amount_b_out
}
fn get_price(&self) -> f64 {
if self.token_a_reserve > 0 {
self.token_b_reserve as f64 / self.token_a_reserve as f64
} else {
0.0
}
}
}
fn main() {
let mut pool = LiquidityPool::new(1000, 2000);
println!("Initial price: {:.2}", pool.get_price());
let b_received = pool.swap_a_for_b(100);
println!("Swapped 100 A for {} B", b_received);
println!("New price: {:.2}", pool.get_price());
}Explanation:
AMM pools use constant product formula (x * y = k) to determine swap rates. Prices change based on pool reserves, providing automatic price discovery.
Exercises
Order Book
Create an order book that can add buy and sell orders!
Starter Code:
RUST
enum OrderType {
Buy,
Sell,
}
struct Order {
price: u64,
amount: u64,
}
struct OrderBook {
buy_orders: Vec<Order>,
sell_orders: Vec<Order>,
}
fn main() {
let mut book = OrderBook::new();
book.add_buy_order(Order { price: 100, amount: 10 });
book.add_sell_order(Order { price: 95, amount: 5 });
println!("Buy orders: {}, Sell orders: {}",
book.buy_orders.len(), book.sell_orders.len());
}