exchange.delta
1 // Copyright (c) 2025 ALPHA/DELTA Network 2 // This file is part of the DeltaVM library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /**********************************************************************************************************************/ 17 // DELTA Exchange Program 18 // 19 // Provides spot trading functionality for the DELTA exchange, including: 20 // - Order book management with price-time priority (FIFO) 21 // - Order types: Limit, Market, IOC (Immediate or Cancel), FOK (Fill or Kill), Post-Only 22 // - Fee collection based on volume tiers 23 // - Trade execution and settlement 24 // 25 // Fee Tiers (volume-based): 26 // - Tier 1 (<100K sAX/month): 0.10% 27 // - Tier 2 (100K-1M sAX/month): 0.05% 28 // - Tier 3 (1M-10M sAX/month): 0.025% 29 // - Tier 4 (>10M sAX/month): 0.01% 30 // 31 // Base fee: 10 microcredits (0.001 AX equivalent) 32 /**********************************************************************************************************************/ 33 34 program delta.exchange; 35 36 /**********************************************************************************************************************/ 37 // STRUCTS 38 /**********************************************************************************************************************/ 39 40 // Order side: 0 = Buy, 1 = Sell 41 // Order type: 0 = Limit, 1 = Market, 2 = IOC, 3 = FOK, 4 = PostOnly 42 43 // An order in the order book. 44 struct order: 45 // Order ID (unique). 46 order_id as u128; 47 // Trader's address. 48 trader as address; 49 // Market ID (e.g., hash of "BTC/sAX"). 50 market_id as field; 51 // Order side: 0 = Buy, 1 = Sell. 52 side as u8; 53 // Order type: 0 = Limit, 1 = Market, 2 = IOC, 3 = FOK, 4 = PostOnly. 54 order_type as u8; 55 // Price in quote currency (8 decimals). 0 for market orders. 56 price as u128; 57 // Original quantity in base currency. 58 quantity as u128; 59 // Filled quantity so far. 60 filled as u128; 61 // Block height when order was placed. 62 timestamp as u32; 63 // Status: 0 = Open, 1 = Filled, 2 = Cancelled, 3 = Expired. 64 status as u8; 65 66 // A completed trade. 67 struct trade: 68 // Trade ID. 69 trade_id as u128; 70 // Market ID. 71 market_id as field; 72 // Maker order ID. 73 maker_order_id as u128; 74 // Taker order ID. 75 taker_order_id as u128; 76 // Maker address. 77 maker as address; 78 // Taker address. 79 taker as address; 80 // Trade price. 81 price as u128; 82 // Trade quantity. 83 quantity as u128; 84 // Maker fee in quote currency. 85 maker_fee as u64; 86 // Taker fee in quote currency. 87 taker_fee as u64; 88 // Block height when trade occurred. 89 timestamp as u32; 90 91 // Market configuration. 92 struct market_config: 93 // Base asset ID (e.g., BTC). 94 base_asset as field; 95 // Quote asset ID (e.g., sAX). 96 quote_asset as field; 97 // Minimum order size in base currency. 98 min_order_size as u128; 99 // Tick size (minimum price increment). 100 tick_size as u128; 101 // Whether market is active. 102 is_active as boolean; 103 104 // User's monthly trading volume for fee tier calculation. 105 struct user_volume: 106 // Volume in quote currency (sAX) for current month. 107 volume as u128; 108 // Month identifier (year * 12 + month). 109 month_id as u32; 110 111 /**********************************************************************************************************************/ 112 // MAPPINGS 113 /**********************************************************************************************************************/ 114 115 // Orders by order ID. 116 mapping orders: 117 key as u128.public; 118 value as order.public; 119 120 // Order book: market_id + side + price_level -> list of order IDs (linked list head). 121 // Uses a composite key for efficient lookup. 122 mapping order_book: 123 key as field.public; 124 value as u128.public; 125 126 // Trades by trade ID. 127 mapping trades: 128 key as u128.public; 129 value as trade.public; 130 131 // Market configurations by market ID. 132 mapping markets: 133 key as field.public; 134 value as market_config.public; 135 136 // User trading volume by address. 137 mapping user_volumes: 138 key as address.public; 139 value as user_volume.public; 140 141 // Next order ID counter. 142 mapping counters: 143 key as u8.public; 144 value as u128.public; 145 146 // User balances: composite key (user_hash + asset_hash) -> balance. 147 // This is a simplified balance tracking - in production would integrate with credits.delta. 148 mapping balances: 149 key as field.public; 150 value as u128.public; 151 152 // Best bid price per market. 153 mapping best_bid: 154 key as field.public; 155 value as u128.public; 156 157 // Best ask price per market. 158 mapping best_ask: 159 key as field.public; 160 value as u128.public; 161 162 // 24-hour volume per market. 163 mapping market_volume_24h: 164 key as field.public; 165 value as u128.public; 166 167 // Last trade price per market. 168 mapping last_price: 169 key as field.public; 170 value as u128.public; 171 172 // Fee collection pool. 173 mapping fee_pool: 174 key as u8.public; 175 value as u128.public; 176 177 // Admin address for market management. 178 mapping admin: 179 key as u8.public; 180 value as address.public; 181 182 /**********************************************************************************************************************/ 183 // FUNCTIONS 184 /**********************************************************************************************************************/ 185 186 // Place a new order. 187 function place_order: 188 // Input the market ID. 189 input r0 as field.public; 190 // Input the side (0 = Buy, 1 = Sell). 191 input r1 as u8.public; 192 // Input the order type (0 = Limit, 1 = Market, 2 = IOC, 3 = FOK, 4 = PostOnly). 193 input r2 as u8.public; 194 // Input the price (0 for market orders). 195 input r3 as u128.public; 196 // Input the quantity. 197 input r4 as u128.public; 198 199 // Validate side (0 or 1). 200 lte r1 1u8 into r5; 201 assert.eq r5 true; 202 203 // Validate order type (0-4). 204 lte r2 4u8 into r6; 205 assert.eq r6 true; 206 207 // Validate quantity is positive. 208 gt r4 0u128 into r7; 209 assert.eq r7 true; 210 211 // For limit orders, price must be positive. 212 is.eq r2 1u8 into r8; // Is market order? 213 ternary r8 1u128 r3 into r9; // Use 1 for market orders in check 214 gt r9 0u128 into r10; 215 assert.eq r10 true; 216 217 // Execute order placement. 218 async place_order self.caller r0 r1 r2 r3 r4 into r11; 219 output r11 as delta.exchange/place_order.future; 220 221 finalize place_order: 222 // Input the caller (trader). 223 input r0 as address.public; 224 // Input the market ID. 225 input r1 as field.public; 226 // Input the side. 227 input r2 as u8.public; 228 // Input the order type. 229 input r3 as u8.public; 230 // Input the price. 231 input r4 as u128.public; 232 // Input the quantity. 233 input r5 as u128.public; 234 235 // Get market config (fails if market doesn't exist). 236 get markets[r1] into r6; 237 238 // Ensure market is active. 239 assert.eq r6.is_active true; 240 241 // Check minimum order size. 242 gte r5 r6.min_order_size into r7; 243 assert.eq r7 true; 244 245 // Get next order ID. 246 get.or_use counters[0u8] 1u128 into r8; 247 248 // Construct the order. 249 cast r8 r0 r1 r2 r3 r4 r5 0u128 block.height 0u8 into r9 as order; 250 251 // Store the order. 252 set r9 into orders[r8]; 253 254 // Increment order counter. 255 add r8 1u128 into r10; 256 set r10 into counters[0u8]; 257 258 // Update best bid/ask based on side. 259 is.eq r2 0u8 into r11; // Is buy order? 260 branch.eq r11 true to update_bid; 261 262 // Update ask. 263 get.or_use best_ask[r1] 18446744073709551615u128 into r12; // Max u128 as default 264 lt r4 r12 into r13; // Is price lower than current best ask? 265 ternary r13 r4 r12 into r14; 266 set r14 into best_ask[r1]; 267 branch.eq true true to end; 268 269 position update_bid; 270 get.or_use best_bid[r1] 0u128 into r15; 271 gt r4 r15 into r16; // Is price higher than current best bid? 272 ternary r16 r4 r15 into r17; 273 set r17 into best_bid[r1]; 274 275 position end; 276 277 /**********************************************************************************************************************/ 278 279 // Cancel an existing order. 280 function cancel_order: 281 // Input the order ID. 282 input r0 as u128.public; 283 284 // Execute cancellation. 285 async cancel_order self.caller r0 into r1; 286 output r1 as delta.exchange/cancel_order.future; 287 288 finalize cancel_order: 289 // Input the caller. 290 input r0 as address.public; 291 // Input the order ID. 292 input r1 as u128.public; 293 294 // Get the order. 295 get orders[r1] into r2; 296 297 // Verify caller owns the order. 298 assert.eq r0 r2.trader; 299 300 // Verify order is open. 301 assert.eq r2.status 0u8; 302 303 // Calculate unfilled quantity. 304 sub r2.quantity r2.filled into r3; 305 306 // Update order status to cancelled. 307 cast r2.order_id r2.trader r2.market_id r2.side r2.order_type r2.price r2.quantity r2.filled r2.timestamp 2u8 into r4 as order; 308 set r4 into orders[r1]; 309 310 // TODO: Return locked funds to user. 311 312 /**********************************************************************************************************************/ 313 314 // Execute a match between two orders (called by matching engine). 315 function execute_match: 316 // Input the maker order ID. 317 input r0 as u128.public; 318 // Input the taker order ID. 319 input r1 as u128.public; 320 // Input the match quantity. 321 input r2 as u128.public; 322 // Input the match price. 323 input r3 as u128.public; 324 325 // Validate quantity is positive. 326 gt r2 0u128 into r4; 327 assert.eq r4 true; 328 329 // Execute the match. 330 async execute_match r0 r1 r2 r3 into r5; 331 output r5 as delta.exchange/execute_match.future; 332 333 finalize execute_match: 334 // Input the maker order ID. 335 input r0 as u128.public; 336 // Input the taker order ID. 337 input r1 as u128.public; 338 // Input the match quantity. 339 input r2 as u128.public; 340 // Input the match price. 341 input r3 as u128.public; 342 343 // Get both orders. 344 get orders[r0] into r4; // Maker 345 get orders[r1] into r5; // Taker 346 347 // Verify orders are compatible (opposite sides). 348 add r4.side r5.side into r6; 349 assert.eq r6 1u8; // 0 + 1 = 1 (buy + sell) 350 351 // Verify same market. 352 assert.eq r4.market_id r5.market_id; 353 354 // Verify both orders are open. 355 assert.eq r4.status 0u8; 356 assert.eq r5.status 0u8; 357 358 // Calculate remaining quantities. 359 sub r4.quantity r4.filled into r7; // Maker remaining 360 sub r5.quantity r5.filled into r8; // Taker remaining 361 362 // Match quantity cannot exceed remaining. 363 lte r2 r7 into r9; 364 assert.eq r9 true; 365 lte r2 r8 into r10; 366 assert.eq r10 true; 367 368 // Calculate fees (simplified: 0.1% = 10 bps). 369 // notional = quantity * price / 10^8 (adjust for decimals) 370 mul r2 r3 into r11; 371 div r11 100000000u128 into r12; // Notional in quote currency 372 373 // Maker fee: 5 bps (0.05%) 374 mul r12 5u128 into r13; 375 div r13 10000u128 into r14; 376 cast r14 into r15 as u64; 377 378 // Taker fee: 10 bps (0.1%) 379 mul r12 10u128 into r16; 380 div r16 10000u128 into r17; 381 cast r17 into r18 as u64; 382 383 // Get next trade ID. 384 get.or_use counters[1u8] 1u128 into r19; 385 386 // Create trade record. 387 cast r19 r4.market_id r0 r1 r4.trader r5.trader r3 r2 r15 r18 block.height into r20 as trade; 388 set r20 into trades[r19]; 389 390 // Increment trade counter. 391 add r19 1u128 into r21; 392 set r21 into counters[1u8]; 393 394 // Update maker order filled amount. 395 add r4.filled r2 into r22; 396 is.eq r22 r4.quantity into r23; // Is fully filled? 397 ternary r23 1u8 0u8 into r24; // 1 = Filled, 0 = Open 398 cast r4.order_id r4.trader r4.market_id r4.side r4.order_type r4.price r4.quantity r22 r4.timestamp r24 into r25 as order; 399 set r25 into orders[r0]; 400 401 // Update taker order filled amount. 402 add r5.filled r2 into r26; 403 is.eq r26 r5.quantity into r27; 404 ternary r27 1u8 0u8 into r28; 405 cast r5.order_id r5.trader r5.market_id r5.side r5.order_type r5.price r5.quantity r26 r5.timestamp r28 into r29 as order; 406 set r29 into orders[r1]; 407 408 // Update last price. 409 set r3 into last_price[r4.market_id]; 410 411 // Update 24h volume. 412 get.or_use market_volume_24h[r4.market_id] 0u128 into r30; 413 add r30 r12 into r31; 414 set r31 into market_volume_24h[r4.market_id]; 415 416 // Collect fees. 417 get.or_use fee_pool[0u8] 0u128 into r32; 418 cast r15 into r33 as u128; 419 cast r18 into r34 as u128; 420 add r33 r34 into r35; 421 add r32 r35 into r36; 422 set r36 into fee_pool[0u8]; 423 424 /**********************************************************************************************************************/ 425 426 // Register a new market. Only admin can call. 427 function register_market: 428 // Input the market ID. 429 input r0 as field.public; 430 // Input the base asset ID. 431 input r1 as field.public; 432 // Input the quote asset ID. 433 input r2 as field.public; 434 // Input the minimum order size. 435 input r3 as u128.public; 436 // Input the tick size. 437 input r4 as u128.public; 438 439 async register_market self.caller r0 r1 r2 r3 r4 into r5; 440 output r5 as delta.exchange/register_market.future; 441 442 finalize register_market: 443 // Input the caller. 444 input r0 as address.public; 445 // Input the market ID. 446 input r1 as field.public; 447 // Input the base asset. 448 input r2 as field.public; 449 // Input the quote asset. 450 input r3 as field.public; 451 // Input the min order size. 452 input r4 as u128.public; 453 // Input the tick size. 454 input r5 as u128.public; 455 456 // Verify caller is admin. 457 get admin[0u8] into r6; 458 assert.eq r0 r6; 459 460 // Construct market config. 461 cast r2 r3 r4 r5 true into r7 as market_config; 462 463 // Store market. 464 set r7 into markets[r1]; 465 466 /**********************************************************************************************************************/ 467 468 // Get order book depth (view function). 469 function get_orderbook: 470 // Input the market ID. 471 input r0 as field.public; 472 // Input the depth (number of levels). 473 input r1 as u8.public; 474 475 async get_orderbook r0 r1 into r2; 476 output r2 as delta.exchange/get_orderbook.future; 477 478 finalize get_orderbook: 479 // Input the market ID. 480 input r0 as field.public; 481 // Input the depth. 482 input r1 as u8.public; 483 484 // Get best bid and ask. 485 get.or_use best_bid[r0] 0u128 into r2; 486 get.or_use best_ask[r0] 18446744073709551615u128 into r3; 487 488 // Note: In a real implementation, we would iterate through 489 // the order book and return aggregated levels. 490 // ADL doesn't support complex data structures for returns, 491 // so clients would query individual orders or use events. 492 493 /**********************************************************************************************************************/ 494 495 // Initialize the exchange with an admin address. 496 function initialize: 497 // Input the admin address. 498 input r0 as address.public; 499 500 async initialize self.caller r0 into r1; 501 output r1 as delta.exchange/initialize.future; 502 503 finalize initialize: 504 // Input the caller. 505 input r0 as address.public; 506 // Input the admin address. 507 input r1 as address.public; 508 509 // Check if already initialized. 510 contains admin[0u8] into r2; 511 assert.eq r2 false; 512 513 // Set the admin. 514 set r1 into admin[0u8]; 515 516 // Initialize counters. 517 set 1u128 into counters[0u8]; // Order ID counter 518 set 1u128 into counters[1u8]; // Trade ID counter 519 520 /**********************************************************************************************************************/ 521 522 // Collect accumulated fees (admin only). 523 function collect_fees: 524 // Input the recipient address. 525 input r0 as address.public; 526 527 async collect_fees self.caller r0 into r1; 528 output r1 as delta.exchange/collect_fees.future; 529 530 finalize collect_fees: 531 // Input the caller. 532 input r0 as address.public; 533 // Input the recipient. 534 input r1 as address.public; 535 536 // Verify caller is admin. 537 get admin[0u8] into r2; 538 assert.eq r0 r2; 539 540 // Get fee pool balance. 541 get.or_use fee_pool[0u8] 0u128 into r3; 542 543 // Clear fee pool. 544 set 0u128 into fee_pool[0u8]; 545 546 // TODO: Transfer fees to recipient via credits.delta. 547 // For now, fees are tracked but transfer happens externally. 548 549 /**********************************************************************************************************************/