Skip to content

Commit cc96bfa

Browse files
committed
Make Modify orders set the new quantity and remove Cancel orders
1 parent ea10181 commit cc96bfa

File tree

5 files changed

+59
-166
lines changed

5 files changed

+59
-166
lines changed

examples/matching-engine/src/contract.rs

Lines changed: 30 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use linera_sdk::{
1414
use matching_engine::{
1515
MatchingEngineAbi, Message, Operation, Order, OrderNature, Parameters, PendingOrderInfo, Price,
1616
};
17-
use state::{MatchingEngineState, ModifyQuantity, Transfer};
17+
use state::{MatchingEngineState, Transfer};
1818

1919
pub struct MatchingEngineContract {
2020
state: MatchingEngineState,
@@ -77,7 +77,7 @@ impl Contract for MatchingEngineContract {
7777
.await
7878
.expect("Failed to read existing order IDs");
7979
for order_id in order_ids {
80-
match self.state.modify_order(order_id, ModifyQuantity::All).await {
80+
match self.state.modify_order(order_id, Amount::ZERO).await {
8181
Some(transfer) => self.send_to(transfer),
8282
// Orders with amount zero may have been cleared in an earlier iteration.
8383
None => continue,
@@ -138,24 +138,16 @@ impl Contract for MatchingEngineContract {
138138
.expect("Failed to load pending orders")
139139
.expect("Account should have pending orders");
140140

141-
// Update the pending order
142-
if let Some(order) = pending_orders.get_mut(&order_id) {
143-
order.quantity = new_quantity;
141+
if new_quantity == Amount::ZERO {
142+
// Remove the pending order
143+
pending_orders.remove(&order_id);
144+
} else {
145+
// Update the pending order
146+
if let Some(order) = pending_orders.get_mut(&order_id) {
147+
order.quantity = new_quantity;
148+
}
144149
}
145150
}
146-
Message::OrderRemoved { owner, order_id } => {
147-
// This message is received on the sender chain from the matching engine
148-
let pending_orders = self
149-
.state
150-
.pending_orders
151-
.get_mut(&owner)
152-
.await
153-
.expect("Failed to load pending orders")
154-
.expect("Account should have pending orders");
155-
156-
// Remove the pending order
157-
pending_orders.remove(&order_id);
158-
}
159151
}
160152
}
161153

@@ -239,9 +231,10 @@ impl MatchingEngineContract {
239231
let matching_engine_chain = self.runtime.chain_id();
240232
for (filled_chain_id, filled_owner, filled_order_id) in filled_orders {
241233
if filled_chain_id != matching_engine_chain {
242-
let removal_message = Message::OrderRemoved {
234+
let removal_message = Message::OrderUpdated {
243235
owner: filled_owner,
244236
order_id: filled_order_id,
237+
new_quantity: Amount::ZERO,
245238
};
246239
self.runtime
247240
.prepare_message(removal_message)
@@ -278,7 +271,11 @@ impl MatchingEngineContract {
278271
}
279272
}
280273
}
281-
Order::Cancel { owner, order_id } => {
274+
Order::Modify {
275+
owner,
276+
order_id,
277+
new_quantity,
278+
} => {
282279
self.state.check_order_id(&order_id, &owner).await;
283280
let key_book = self
284281
.state
@@ -291,16 +288,21 @@ impl MatchingEngineContract {
291288

292289
let transfer = self
293290
.state
294-
.modify_order(order_id, ModifyQuantity::All)
291+
.modify_order(order_id, new_quantity)
295292
.await
296-
.expect("Order is not present therefore cannot be cancelled");
293+
.expect("Failed to modify order");
297294
self.send_to(transfer);
298295

299-
// Send removal notification to the sender chain (if different from matching engine chain)
296+
// Order still exists with reduced amount
300297
if sender_chain_id != self.runtime.chain_id() {
301-
let removal_message = Message::OrderRemoved { owner, order_id };
298+
// Send update notification to the sender chain (if different from matching engine chain)
299+
let update_message = Message::OrderUpdated {
300+
owner,
301+
order_id,
302+
new_quantity,
303+
};
302304
self.runtime
303-
.prepare_message(removal_message)
305+
.prepare_message(update_message)
304306
.with_authentication()
305307
.send_to(sender_chain_id);
306308
} else {
@@ -311,77 +313,9 @@ impl MatchingEngineContract {
311313
.await
312314
.expect("Failed to load pending orders")
313315
.expect("Account should have pending orders");
314-
// Remove the pending order
315-
pending_orders.remove(&order_id);
316-
}
317-
}
318-
Order::Modify {
319-
owner,
320-
order_id,
321-
reduce_quantity,
322-
} => {
323-
self.state.check_order_id(&order_id, &owner).await;
324-
let key_book = self
325-
.state
326-
.orders
327-
.get(&order_id)
328-
.await
329-
.expect("Failed to load order")
330-
.expect("Order should exist");
331-
let sender_chain_id = key_book.account.chain_id;
332-
333-
let transfer = self
334-
.state
335-
.modify_order(order_id, ModifyQuantity::Partial(reduce_quantity))
336-
.await
337-
.expect("Order is not present therefore cannot be cancelled");
338-
self.send_to(transfer);
339-
340-
// Get the remaining quantity after modification
341-
if let Some(new_quantity) = self.state.get_order_quantity(&order_id).await {
342-
// Order still exists with reduced amount
343-
if sender_chain_id != self.runtime.chain_id() {
344-
// Send update notification to the sender chain (if different from matching engine chain)
345-
let update_message = Message::OrderUpdated {
346-
owner,
347-
order_id,
348-
new_quantity,
349-
};
350-
self.runtime
351-
.prepare_message(update_message)
352-
.with_authentication()
353-
.send_to(sender_chain_id);
354-
} else {
355-
let pending_orders = self
356-
.state
357-
.pending_orders
358-
.get_mut(&owner)
359-
.await
360-
.expect("Failed to load pending orders")
361-
.expect("Account should have pending orders");
362-
// Update the pending order
363-
if let Some(order) = pending_orders.get_mut(&order_id) {
364-
order.quantity = new_quantity;
365-
}
366-
}
367-
} else {
368-
// Order was fully cancelled
369-
if sender_chain_id != self.runtime.chain_id() {
370-
let removal_message = Message::OrderRemoved { owner, order_id };
371-
self.runtime
372-
.prepare_message(removal_message)
373-
.with_authentication()
374-
.send_to(sender_chain_id);
375-
} else {
376-
let pending_orders = self
377-
.state
378-
.pending_orders
379-
.get_mut(&owner)
380-
.await
381-
.expect("Failed to load pending orders")
382-
.expect("Account should have pending orders");
383-
// Remove the pending order
384-
pending_orders.remove(&order_id);
316+
// Update the pending order
317+
if let Some(order) = pending_orders.get_mut(&order_id) {
318+
order.quantity = new_quantity;
385319
}
386320
}
387321
}

examples/matching-engine/src/lib.rs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -136,23 +136,19 @@ scalar!(OrderNature);
136136

137137
#[derive(Clone, Debug, Serialize, Deserialize)]
138138
pub enum Order {
139-
/// Insertion of an order
139+
/// Insert an order
140140
Insert {
141141
owner: AccountOwner,
142142
quantity: Amount,
143143
nature: OrderNature,
144144
price: Price,
145145
},
146-
/// Cancelling of an order
147-
Cancel {
148-
owner: AccountOwner,
149-
order_id: OrderId,
150-
},
151-
/// Modifying order (only decreasing is allowed)
146+
/// Modify an order. The quantity can only be decreased. If nul, the order is
147+
/// canceled.
152148
Modify {
153149
owner: AccountOwner,
154150
order_id: OrderId,
155-
reduce_quantity: Amount,
151+
new_quantity: Amount,
156152
},
157153
}
158154

@@ -163,7 +159,6 @@ impl Order {
163159
pub fn owner(&self) -> AccountOwner {
164160
match self {
165161
Order::Insert { owner, .. } => *owner,
166-
Order::Cancel { owner, .. } => *owner,
167162
Order::Modify { owner, .. } => *owner,
168163
}
169164
}
@@ -177,10 +172,7 @@ impl Order {
177172
// Get the quantity/amount to check based on the order type
178173
let quantity = match self {
179174
Order::Insert { quantity, .. } => *quantity,
180-
Order::Modify {
181-
reduce_quantity, ..
182-
} => *reduce_quantity,
183-
Order::Cancel { .. } => return Ok(()), // No quantity to check
175+
Order::Modify { new_quantity, .. } => *new_quantity,
184176
};
185177

186178
// Calculate the minimum precision unit allowed
@@ -284,17 +276,12 @@ pub enum Message {
284276
order_id: OrderId,
285277
order_info: PendingOrderInfo,
286278
},
287-
/// Notification sent from the matching engine when an order is modified or cancelled.
279+
/// Notification sent from the matching engine when an order is modified, filled, or cancelled.
288280
OrderUpdated {
289281
owner: AccountOwner,
290282
order_id: OrderId,
291283
new_quantity: Amount,
292284
},
293-
/// Notification sent from the matching engine when an order is fully cancelled or filled.
294-
OrderRemoved {
295-
owner: AccountOwner,
296-
order_id: OrderId,
297-
},
298285
}
299286

300287
#[cfg(test)]
@@ -333,19 +320,23 @@ mod tests {
333320
let modify_valid = Order::Modify {
334321
owner,
335322
order_id: 1,
336-
reduce_quantity: Amount::from_attos(200),
323+
new_quantity: Amount::from_attos(200),
337324
};
338325
assert!(modify_valid.check_precision(price_decimals).is_ok());
339326

340327
let modify_invalid = Order::Modify {
341328
owner,
342329
order_id: 1,
343-
reduce_quantity: Amount::from_attos(199),
330+
new_quantity: Amount::from_attos(199),
344331
};
345332
assert!(modify_invalid.check_precision(price_decimals).is_err());
346333

347-
// Test Cancel order (should always succeed)
348-
let cancel_order = Order::Cancel { owner, order_id: 1 };
334+
// Test order cancellation.
335+
let cancel_order = Order::Modify {
336+
owner,
337+
order_id: 1,
338+
new_quantity: Amount::ZERO,
339+
};
349340
assert!(cancel_order.check_precision(price_decimals).is_ok());
350341

351342
// Test with price_decimals = 0 (any quantity should be valid)

examples/matching-engine/src/state.rs

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -140,35 +140,13 @@ impl MatchingEngineState {
140140
}
141141
}
142142

143-
/// Gets the current amount of an order from the order book.
144-
/// Returns None if the order doesn't exist or has been fully filled/cancelled.
145-
pub async fn get_order_quantity(&mut self, order_id: &OrderId) -> Option<Amount> {
146-
let key_book = self.orders.get(order_id).await.ok()??;
147-
148-
// Find the order in the appropriate price level
149-
match key_book.nature {
150-
OrderNature::Bid => {
151-
let view = self.bid_level(&key_book.price.to_bid()).await;
152-
let mut iter = view.queue.iter_mut().await.expect("Failed to iterate");
153-
iter.find(|order| order.order_id == *order_id)
154-
.map(|order| order.quantity)
155-
}
156-
OrderNature::Ask => {
157-
let view = self.ask_level(&key_book.price.to_ask()).await;
158-
let mut iter = view.queue.iter_mut().await.expect("Failed to iterate");
159-
iter.find(|order| order.order_id == *order_id)
160-
.map(|order| order.quantity)
161-
}
162-
}
163-
}
164-
165143
/// Modifies the order from the order_id.
166144
/// This means that some transfers have to be done and the size depends
167145
/// whether ask or bid.
168146
pub async fn modify_order(
169147
&mut self,
170148
order_id: OrderId,
171-
cancel_quantity: ModifyQuantity,
149+
new_quantity: Amount,
172150
) -> Option<Transfer> {
173151
let key_book = self
174152
.orders
@@ -179,7 +157,7 @@ impl MatchingEngineState {
179157
OrderNature::Bid => {
180158
let view = self.bid_level(&key_book.price.to_bid()).await;
181159
let (cancel_quantity, remove_order_id) =
182-
view.modify_order_level(order_id, cancel_quantity).await?;
160+
view.modify_order_level(order_id, new_quantity).await?;
183161
if remove_order_id {
184162
self.remove_order_id((key_book.account.owner, order_id))
185163
.await;
@@ -196,7 +174,7 @@ impl MatchingEngineState {
196174
OrderNature::Ask => {
197175
let view = self.ask_level(&key_book.price.to_ask()).await;
198176
let (cancel_quantity, remove_order_id) =
199-
view.modify_order_level(order_id, cancel_quantity).await?;
177+
view.modify_order_level(order_id, new_quantity).await?;
200178
if remove_order_id {
201179
self.remove_order_id((key_book.account.owner, order_id))
202180
.await;
@@ -427,15 +405,6 @@ pub struct Transfer {
427405
pub token_idx: u32,
428406
}
429407

430-
/// An order can be cancelled which removes it totally or
431-
/// modified which is a partial cancellation. The size of the
432-
/// order can never increase.
433-
#[derive(Clone, Debug)]
434-
pub enum ModifyQuantity {
435-
All,
436-
Partial(Amount),
437-
}
438-
439408
impl LevelView {
440409
fn parameters(&self) -> Parameters {
441410
*self.context().extra()
@@ -610,24 +579,20 @@ impl LevelView {
610579
pub async fn modify_order_level(
611580
&mut self,
612581
order_id: OrderId,
613-
reduce_quantity: ModifyQuantity,
582+
new_quantity: Amount,
614583
) -> Option<(Amount, bool)> {
615584
let mut iter = self
616585
.queue
617586
.iter_mut()
618587
.await
619588
.expect("Failed to load iterator over level queue");
620589
let state_order = iter.find(|order| order.order_id == order_id)?;
621-
let new_quantity = match reduce_quantity {
622-
ModifyQuantity::All => Amount::ZERO,
623-
ModifyQuantity::Partial(reduce_quantity) => state_order
624-
.quantity
625-
.try_sub(reduce_quantity)
626-
.expect("Attempt to cancel a larger quantity than available"),
627-
};
628-
let corr_reduce_quantity = state_order.quantity.try_sub(new_quantity).unwrap();
590+
let cancel_quantity = state_order
591+
.quantity
592+
.try_sub(new_quantity)
593+
.expect("Attempt to increase quantity");
629594
state_order.quantity = new_quantity;
630595
self.remove_zero_orders_from_level().await;
631-
Some((corr_reduce_quantity, new_quantity == Amount::ZERO))
596+
Some((cancel_quantity, new_quantity == Amount::ZERO))
632597
}
633598
}

examples/matching-engine/tests/transaction.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ async fn single_transaction() {
246246
}
247247

248248
// Cancel A's order.
249-
let order = Order::Cancel {
249+
let order = Order::Modify {
250250
owner: owner_a,
251251
order_id: order_ids_a[0],
252+
new_quantity: Amount::ZERO,
252253
};
253254
let operation = Operation::ExecuteOrder { order };
254255
let order_certificate = user_chain_a

0 commit comments

Comments
 (0)