/ synthesizer / program / src / resources / exchange.delta
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  /**********************************************************************************************************************/